Pergunta

eu acidentalmente cometeu um arquivo não desejado (filename.orig ao resolver uma mesclagem) para meu repositório vários commits atrás, sem me notar isso até agora. Eu quero apagar completamente o arquivo do histórico do repositório.

É possível reescrever o histórico de alterações de tal forma que filename.orig nunca mais foi adicionado ao repositório em primeiro lugar?

Foi útil?

Solução

Por favor, não use esta receita se a sua situação não é a descrita na questão. Esta receita é para a fixação de um mau merge, e repetindo seus bons commits em um merge fixo.

Embora filter-branch vai fazer o que quiser, é um comando bastante complexo e eu provavelmente iria escolher para fazer isso com git rebase. É provavelmente uma preferência pessoal. filter-branch pode fazê-lo em um único comando, um pouco mais complexo, enquanto que a solução rebase está realizando o equivalente operações lógicas um passo de cada vez.

Tente o seguinte receita:

(Note que você não precisa realmente um ramo temporário, você pode fazer isso com um "CABEÇA campo isolada, mas você precisa tomar nota do ID de cometer gerado pelo passo git commit --amend a alimentação para o comando git rebase vez de usar o nome de filial temporário.)

Outras dicas

Intro: Você tem 5 Soluções disponíveis

Os estados cartaz original:

eu acidentalmente cometeu um arquivo não desejado ... para meu repositório vários commits atrás ... Eu quero apagar completamente o arquivo do histórico do repositório.

É possível reescrever o histórico de alterações de tal forma que nunca foi filename.orig adicionados ao repositório em primeiro lugar?

Existem muitas maneiras diferentes para remover o histórico de um arquivo completamente do git:

  1. commits de alteração.
  2. redefine rígidos (possivelmente mais um REBASE).
  3. rebase não-interativo.
  4. rebases Interactive.
  5. ramos de filtragem.

No caso do cartaz original, que altera a confirmação não é realmente uma opção por si só, uma vez que ele fez vários commits adicionais depois, mas por uma questão de completude, eu também irá explicar como fazê-lo, para qualquer pessoa que Justs quer alterar a sua submissão anterior.

Note-se que todas estas soluções envolvem alterando / re-escrita História / commits de uma forma de outra, para que qualquer pessoa com cópias antigas dos commits terá que fazer trabalho extra para re-sync sua história com a nova história.


Solução 1: rectificativos Compromete

Se você acidentalmente fez uma mudança (como adicionar um arquivo) no seu anterior cometer, e você não quer que a história de que a mudança de existir mais, em seguida, você pode simplesmente alterar a submissão anterior para remover o arquivo a partir dele:

git rm <file>
git commit --amend --no-edit

Solução 2: Hard Reset (Possivelmente Mais um Rebase)

Como solução # 1, se você quiser apenas para se livrar de sua submissão anterior, então você também tem a opção de simplesmente fazer um hard reset ao seu pai:

git reset --hard HEAD^

Esse comando vai hard-redefinir seu ramo para o st pai anterior 1 cometer.

No entanto , se, como o cartaz original, você fez várias submissões após a cometer você quiser desfazer a alteração para, você ainda pode usar reinicializações para modificá-lo, mas isso também envolve o uso de um rebase. Aqui estão os passos que você pode usar para alterar a comprometer ainda mais para trás na história:


Solução 3: não interativo Rebase

Isto irá funcionar se você apenas deseja remover um commit da história inteiramente:


Solução 4: rebases interativos

Esta solução irá permitir-lhe realizar as mesmas coisas como soluções # 2 e # 3, ou seja, modificar ou remover compromete ainda mais para trás na história do que a sua imediatamente submissão anterior, de modo que solução você optar por usar é uma espécie de até você. rebases interativos não são bem adaptados para rebasing centenas de commits, para motivos de desempenho, então eu usaria rebases não-interativo ou o ramo filtro solução (ver abaixo) na esse tipo de situações.

Para começar o rebase interativo, use o seguinte:

Isto fará com que git para rebobinar a cometer história de volta para o pai do commit que você deseja modificar ou remover. Ela irá então apresentar-lhe uma lista do commits rebobinado na ordem inversa em qualquer git editor está definido para uso (este é Vim por padrão):

pick 00ddaac Add symlinks for executables
pick 03fa071 Set `push.default` to `simple`
pick 7668f34 Modify Bash config to use Homebrew recommended PATH
pick 475593a Add global .gitignore file for OS X
pick 1b7f496 Add alias for Dr Java to Bash config (OS X)

O comprometer que deseja modificar ou remover estará no topo desta lista. Para removê-lo, basta apagar a sua linha na lista. Caso contrário, substitua "pegar" com "Editar" no st Linha 1, assim:

edit 00ddaac Add symlinks for executables
pick 03fa071 Set `push.default` to `simple`

Em seguida, insira git rebase --continue. Se você optar por remover a comprometer totalmente, então que tudo que você precisa fazer (além de verificação, consulte o passo final para esta solução). Se, por outro lado, você queria modificar a cometer, git, em seguida, vai reaplicar o cometer e, em seguida, pausar o rebase.

Stopped at 00ddaacab0a85d9989217dd9fe9e1b317ed069ac... Add symlinks
You can amend the commit now, with

        git commit --amend

Once you are satisfied with your changes, run

        git rebase --continue

Neste ponto, você pode remover o arquivo e alterar a cometer, em seguida, continuar a rebase:

git rm <file>
git commit --amend --no-edit
git rebase --continue

É isso. Como etapa final, se você modacidificado a confirmação ou removido completamente, é sempre uma boa idéia para verificar se há outras mudanças inesperadas foram feitas para o seu ramo por diffing-lo com o seu estado antes do rebase:

git diff master@{1}

Solução 5: Ramos de filtragem

Finalmente, esta solução é melhor se você quiser destruir completamente todos os vestígios de existência de um arquivo de história, e nenhuma das outras soluções são bastante até a tarefa.

Isso irá remover <file> de todos os commits, a partir da raiz cometer. E se em vez disso você só quer reescrever a cometer HEAD~5..HEAD gama, então você pode passar isso como um argumento adicional para filter-branch, como fora apontado em esta resposta :

Mais uma vez, após o filter-branch está completo, é geralmente uma boa idéia para verificar que não há outras mudanças inesperadas por diffing seu ramo com a sua estado anterior antes da operação de filtragem:

git diff master@{1}

filter-branch Alternativa: BFG Repo Cleaner

Eu ouvi que a BFG Repo Cleaner ferramenta é executada mais rapidamente do que git filter-branch, assim você pode querer verificar isso como uma opção também. É ainda mencionado oficialmente no filter-branch documentação como uma alternativa viável :

git-filter-branch permite fazer regravações script-shell complexos de sua história Git, mas você provavelmente não precisa essa flexibilidade se você está simplesmente remover dados indesejados como grandes arquivos ou senhas. Para aquelas operações que você pode querer considerar A BFG Repo-Cleaner , um baseado em JVM alternativa para git-filtro-ramo, tipicamente, pelo menos, mais rápido para 10-50x esses casos de uso, e com características bastante diferentes:

  • Qualquer versão específica de um arquivo for limpo exatamente uma vez . A BFG, ao contrário git-filter-branch, não lhe dá a oportunidade de punho um arquivo de forma diferente dependendo de onde ou em que foi cometida dentro da sua história. Essa restrição dá o benefício de desempenho do núcleo de A BFG, e é bem adequada para a tarefa de limpeza de dados ruins - você não fazer cuidado , onde os dados ruim é, você só quer que gone .

  • Por padrão A BFG tira o máximo partido das máquinas multi-core, limpeza de cometer arquivo-árvores em paralelo. limpa git-filtro-ramo compromete-se sequencialmente (ou seja, de um modo único de rosca), embora é possível filtros de escrita que incluem seu próprio paralelismo, no scripts executados contra cada commit.

  • opções de comando são muito mais restritiva do que git branch-filtro, e dedicada apenas para o tarefas de remoção indesejada data- por exemplo:. --strip-blobs-bigger-than 1M

Recursos adicionais

  1. Pro Git § 6.4 Git Ferramentas - História Rewriting .
  2. git-filter-branch (1) manual de página.
  3. git-commit (1) manual Página .
  4. git-reset (1) manual de página.
  5. git-rebase (1) manual de página.
  6. The Cleaner BFG Repo (ver também this resposta do próprio criador ).

Se você não cometi nada desde, apenas git rm o arquivo e git commit --amend.

Se você tiver

git filter-branch \
--index-filter 'git rm --cached --ignore-unmatch path/to/file/filename.orig' merge-point..HEAD

vai passar por cada mudança de merge-point para HEAD, filename.orig apagar e reescrever a mudança. Usando --ignore-unmatch significa que o comando não falhará se por algum motivo filename.orig está faltando uma mudança. Essa é a maneira recomendada a partir da secção Exemplos no git-filter-branch página homem .

Nota para utilizadores de Windows: O caminho do arquivo deve utilizar barras

Esta é a melhor maneira:
http://github.com/guides/completely-remove-a -file-de-todas-as revisões

Apenas certifique-se de backup cópias dos arquivos primeiro.

Editar

A edição por Neon foi infelizmente rejeitado durante a revisão.
Veja Neons post abaixo, ele pode conter informações úteis!


por exemplo. para remover todos os arquivos *.gz cometido acidentalmente em repositório git:

$ du -sh .git ==> e.g. 100M
$ git filter-branch --index-filter 'git rm --cached --ignore-unmatch *.gz' HEAD
$ git push origin master --force
$ rm -rf .git/refs/original/
$ git reflog expire --expire=now --all
$ git gc --prune=now
$ git gc --aggressive --prune=now

Isso ainda não funcionou para mim? (Eu sou atualmente na versão git 1.7.6.1)

$ du -sh .git ==> e.g. 100M

Não sei por que, desde que eu só tinha um mestre ramo. De qualquer forma, eu finalmente tenho o meu git repo verdadeiramente limpo, empurrando para um novo repositório git vazio e nu, por exemplo.

$ git init --bare /path/to/newcleanrepo.git
$ git push /path/to/newcleanrepo.git master
$ du -sh /path/to/newcleanrepo.git ==> e.g. 5M 

(sim!)

clone Então eu que para um novo diretório e movido sobre pasta .git-lo de em um presente. por exemplo.

$ mv .git ../large_dot_git
$ git clone /path/to/newcleanrepo.git ../tmpdir
$ mv ../tmpdir/.git .
$ du -sh .git ==> e.g. 5M 

(yeah! Finalmente limpo!)

Depois de verificar que tudo está bem, então você pode excluir os diretórios ../large_dot_git e ../tmpdir (talvez em um par de semanas ou meses a partir de agora, apenas no caso ...)

Rewriting Git história novas exigências todos os ids afetadas cometer, e para que todos que está trabalhando no projeto terá de apagar suas antigas cópias do repo, e fazer um clone fresco depois que você limpou a história. Quanto mais as pessoas que inconvenientes, mais você precisa de uma boa razão para fazê-lo - seu arquivo supérfluo não está realmente causando um problema, mas que apenas você estão trabalhando no projeto, você poderia muito bem limpo a história Git se você quiser!

Para torná-lo tão fácil quanto possível, eu recomendo usar o BFG Repo-Cleaner , um mais simples, mais rápido alternativa para git-filter-branch projetado especificamente para a remoção de arquivos da história Git. Uma maneira em que facilita a sua vida aqui é que ele realmente alças todas refs por padrão (todos os tags, ramos, etc), mas também é 10 - 50x mais rápido

.

Você deve seguir cuidadosamente os passos aqui: http://rtyley.github.com / bfg-repo-purificador / # uso - mas o bocado de núcleo é apenas isso: o download do BFG jar (requer Java 6 ou acima) e execute este comando:

$ java -jar bfg.jar --delete-files filename.orig my-repo.git

Toda a sua história repositório será digitalizado e qualquer filename.orig arquivo chamado (que não está em sua mais recente cometer ) serão removidos. Este é consideravelmente mais fácil do que usar git-filter-branch para fazer a mesma coisa!

A divulgação completa:. Eu sou o autor do BFG Repo-Cleaner

You should probably clone your repository first.

Remove your file from all branches history:
git filter-branch --tree-filter 'rm -f filename.orig' -- --all

Remove your file just from the current branch:
git filter-branch --tree-filter 'rm -f filename.orig' -- --HEAD    

Lastly you should run to remove empty commits:
git filter-branch -f --prune-empty -- --all

Só para acrescentar que a solução de Charles Bailey, eu usei apenas um git rebase -i para remover arquivos indesejados de um anterior cometer e funcionou como um encanto. As etapas:

# Pick your commit with 'e'
$ git rebase -i

# Perform as many removes as necessary
$ git rm project/code/file.txt

# amend the commit
$ git commit --amend

# continue with rebase
$ git rebase --continue

A maneira mais simples que eu encontrei foi sugerido por leontalbot (como um comentário), que é um post publicado por Anoopjohn . Eu acho que vale a pena o seu próprio espaço como uma resposta:

(I converteu-o para um script bash)

#!/bin/bash
if [[ $1 == "" ]]; then
    echo "Usage: $0 FILE_OR_DIR [remote]";
    echo "FILE_OR_DIR: the file or directory you want to remove from history"
    echo "if 'remote' argument is set, it will also push to remote repository."
    exit;
fi
FOLDERNAME_OR_FILENAME=$1;

#The important part starts here: ------------------------

git filter-branch -f --index-filter "git rm -rf --cached --ignore-unmatch $FOLDERNAME_OR_FILENAME" -- --all
rm -rf .git/refs/original/
git reflog expire --expire=now --all
git gc --prune=now
git gc --aggressive --prune=now

if [[ $2 == "remote" ]]; then
    git push --all --force
fi
echo "Done."

Todos os créditos vai para Annopjohn, e leontalbot para apontar isso.

NOTA

Esteja ciente de que o roteiro não inclui validações, tenha certeza que você não cometer erros e que você tem um backup no caso de algo correr mal. Ela trabalhou para mim, mas ele pode não funcionar na sua situação. Use-o com CUIDADO (siga o link se você quer saber o que está acontecendo).

Definitivamente, git filter-branch é o caminho a percorrer.

Infelizmente, isso não será suficiente para remover completamente filename.orig do seu repo, pois ele pode ainda ser ser referenciado por tags, entradas reflog, controles remotos e assim por diante.

Eu recomendo remover todas essas referências, bem como, e em seguida, chamar o coletor de lixo. Você pode usar o script git forget-blob de este site para fazer tudo isso em uma única etapa.

git forget-blob filename.orig

Se é a última cometer você deseja limpar, eu tentei com versão git 2.14.3 (Apple Git-98):

touch empty
git init
git add empty
git commit -m init

# 92K   .git
du -hs .git

dd if=/dev/random of=./random bs=1m count=5
git add random
git commit -m mistake

# 5.1M  .git
du -hs .git

git reset --hard HEAD^
git reflog expire --expire=now --all
git gc --prune=now

# 92K   .git
du -hs .git

Isto é o que git filter-branch foi projetado para.

Você também pode usar:

git reset HEAD file/path

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top