Descubra quando um arquivo foi excluído no Git

1035

Eu tenho um repositório Git com n commits.

Eu tenho um arquivo que eu preciso, e que costumava estar no repositório, e de repente procuro e penso "Oh! Para onde foi esse arquivo?"

Existe algum (s) comando (s) Git que me diz que "o arquivo really_needed.txt foi excluído no commit n-13"?

Em outras palavras, sem olhar para todos os commit individuais, e sabendo que meu repositório Git tem todas as alterações de todos os arquivos, posso encontrar rapidamente o último commit que possui esse arquivo, para recuperá-lo?

JohnMetta
fonte
2
O link compartilhado por Pedro teve a resposta para minha pergunta: como encontrar um arquivo excluído quando você não se lembra do caminho.
Gordon Feijão

Respostas:

1127

git log --full-history -- [file path] mostra as alterações de um arquivo, funcione mesmo que o arquivo tenha sido excluído.

Exemplo:

git log --full-history  -- myfile

Se você deseja ver apenas o último commit, que excluiu um arquivo, use -1 além disso, por exemplo, git log --full-history -1 -- [file path]

Consulte Qual confirmação excluiu um arquivo

vogella
fonte
16
é possível procurar padrões? Eu esqueci o nome inteiro do arquivo = (talvez seja possível obter um registro de todas as exclusões?
wutzebaer
6
encontrei-o aqui: stackoverflow.com/questions/6017987/…
wutzebaer
6
Observe que, se você estiver usando o PowerShell, os hífens precisam ser escapados: git log '-' [caminho do arquivo]. Espero que isso possa causar a alguém mais um rangido nos dentes.
A. Wilson
68
Consegui pesquisar usando git log -- */<<filename>>.<<file extension>>não sabendo o caminho completo do arquivo.
Tom Howard
2
Os colchetes do @MERose estão lá como um espaço reservado para o caminho do arquivo real.
Emile Bergeron
229

Resposta curta:

git log --full-history -- your_file

mostrará a você todos os commit no histórico de seu repo, incluindo os commit de mesclagem que foram tocados your_file. O último (superior) é o que excluiu o arquivo.

Alguma explicação:

A --full-historybandeira aqui é importante. Sem ele, o Git executa a "simplificação do histórico" quando você solicita o log de um arquivo. Os documentos são detalhados sobre exatamente como isso funciona e eu não tenho a coragem e a coragem necessárias para tentar descobrir isso a partir do código-fonte, mas os documentos do git-log têm muito a dizer:

Modo padrão

Simplifica o histórico para o mais simples, explicando o estado final da árvore. Mais simples porque remove algumas ramificações laterais se o resultado final for o mesmo (ou seja, mesclar ramificações com o mesmo conteúdo)

Isso é obviamente preocupante quando o arquivo cujo histórico queremos é excluído , pois o histórico mais simples que explica o estado final de um arquivo excluído não é histórico . Existe o risco de que, git logsem --full-history, simplesmente afirme que o arquivo nunca foi criado? Infelizmente sim. Aqui está uma demonstração:

mark@lunchbox:~/example$ git init
Initialised empty Git repository in /home/mark/example/.git/
mark@lunchbox:~/example$ touch foo && git add foo && git commit -m "Added foo"
[master (root-commit) ddff7a7] Added foo
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 foo
mark@lunchbox:~/example$ git checkout -b newbranch
Switched to a new branch 'newbranch'
mark@lunchbox:~/example$ touch bar && git add bar && git commit -m "Added bar"
[newbranch 7f9299a] Added bar
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 bar
mark@lunchbox:~/example$ git checkout master
Switched to branch 'master'
mark@lunchbox:~/example$ git rm foo && git commit -m "Deleted foo"
rm 'foo'
[master 7740344] Deleted foo
 1 file changed, 0 insertions(+), 0 deletions(-)
 delete mode 100644 foo
mark@lunchbox:~/example$ git checkout newbranch
Switched to branch 'newbranch'
mark@lunchbox:~/example$ git rm bar && git commit -m "Deleted bar"
rm 'bar'
[newbranch 873ed35] Deleted bar
 1 file changed, 0 insertions(+), 0 deletions(-)
 delete mode 100644 bar
mark@lunchbox:~/example$ git checkout master
Switched to branch 'master'
mark@lunchbox:~/example$ git merge newbranch
Already up-to-date!
Merge made by the 'recursive' strategy.
mark@lunchbox:~/example$ git log -- foo
commit 77403443a13a93073289f95a782307b1ebc21162
Author: Mark Amery 
Date:   Tue Jan 12 22:50:50 2016 +0000

    Deleted foo

commit ddff7a78068aefb7a4d19c82e718099cf57be694
Author: Mark Amery 
Date:   Tue Jan 12 22:50:19 2016 +0000

    Added foo
mark@lunchbox:~/example$ git log -- bar
mark@lunchbox:~/example$ git log --full-history -- foo
commit 2463e56a21e8ee529a59b63f2c6fcc9914a2b37c
Merge: 7740344 873ed35
Author: Mark Amery 
Date:   Tue Jan 12 22:51:36 2016 +0000

    Merge branch 'newbranch'

commit 77403443a13a93073289f95a782307b1ebc21162
Author: Mark Amery 
Date:   Tue Jan 12 22:50:50 2016 +0000

    Deleted foo

commit ddff7a78068aefb7a4d19c82e718099cf57be694
Author: Mark Amery 
Date:   Tue Jan 12 22:50:19 2016 +0000

    Added foo
mark@lunchbox:~/example$ git log --full-history -- bar
commit 873ed352c5e0f296b26d1582b3b0b2d99e40d37c
Author: Mark Amery 
Date:   Tue Jan 12 22:51:29 2016 +0000

    Deleted bar

commit 7f9299a80cc9114bf9f415e1e9a849f5d02f94ec
Author: Mark Amery 
Date:   Tue Jan 12 22:50:38 2016 +0000

    Added bar

Observe como git log -- barno despejo terminal acima resultou literalmente em nenhuma saída; O Git está "simplificando" a história em uma ficção onde barnunca existiu. git log --full-history -- bar, por outro lado, nos fornece o commit que criou bare o commit que o excluiu.

Para ser claro: esta questão não é meramente teórica. Eu só procurei nos documentos e descobri o --full-historysinalizador porque git log -- some_fileestava falhando para mim em um repositório real, onde estava tentando rastrear um arquivo excluído. Às vezes, a simplificação do histórico pode ser útil quando você está tentando entender como um arquivo existente existe no estado atual, mas ao tentar rastrear uma exclusão de arquivo , é mais provável que você estrague tudo ocultando a confirmação com a qual você se preocupa. . Sempre use o --full-historysinalizador para este caso de uso.

Mark Amery
fonte
4
Observe que isso apenas pesquisa o histórico relevante para a ramificação atual (não 'todo o histórico de repo') ... ou seja, se o arquivo ainda não foi excluído na ramificação atual, mas já esteve em outra ramificação, isso não encontrará a confirmação da exclusão. Você precisa estar em qualquer filial em que o arquivo já tenha sido excluído . Talvez seja óbvio ao pensar sobre isso, mas me chamou a atenção de início.
Anentropic
1
Esta resposta funciona. Mas a partir da git logsaída em si, não é de todo óbvio que a última confirmação excluiu o arquivo. Eu também tentei git log --name-status --full-history -- file_namee git log -p --stat --full-history -- file_name, mas nenhum indica explicitamente que o arquivo foi removido na confirmação mais recente. Isso parece um bug.
22718 Martin_W
O @Martin_ATS mkdir somedir && cd somedir && git init && touch foo && git add foo && git commit -m "Added foo" && git checkout -b newbranch && touch bar && git add bar && git commit -m "Added bar" && git checkout master && git rm foo && git commit -m "Deleted foo" && git checkout newbranch && git rm bar && git commit -m "Deleted bar" && git checkout master && git merge newbranch && git log --name-status --full-history -- barinclui D bare A barpara mim na saída de log do Git 2.12.2. Você não vê essas linhas na saída? Que versão você tem?
precisa
git version 2.15.1Sim, sua sequência de comandos informa D bare A bar. Talvez meu problema seja específico ao histórico do meu arquivo. Eu estava rastreando o histórico de um .htaccessarquivo que foi ignorado e removido. Finalmente descobri isso e adicionei o arquivo de volta. Quando incluo --name-statusno git logcomando, vejo duas A .htaccessentradas (desde que a adicionei novamente na confirmação mais recente), mas não D .htaccess. Portanto, parece que em alguns casos, mesmo que um arquivo tenha sido removido do repositório, git lognão mostrará uma D file_nameentrada explícita .
Martin_W
@Martin_ATS Curious. Gostaria de saber se talvez .htaccesstenha sido adicionado em um commit X, mas não incluído no commit de mesclagem que trouxe o X ao master? Essa é a única coisa em que consigo pensar que eu poderia argumentar que deveria parecer que um arquivo foi adicionado e nunca excluído e ainda não está presente. Seria interessante tentar descobrir um MCVE, descobrir se é um bug do Git e, se não, se é possível ajustar minha resposta para lidar com o seu caso.
Mark Amery
84

Git log, mas você precisa prefixar o caminho com --

Por exemplo:

dan-mac:test dani$ git log file1.txt
fatal: ambiguous argument 'file1.txt': unknown revision or path not in the working tree.

dan-mac:test dani$ git log -- file1.txt
 commit 0f7c4e1c36e0b39225d10b26f3dea40ad128b976
 Author: Daniel Palacio <[email protected]>
 Date:   Tue Jul 26 23:32:20 2011 -0500

 foo
daniel
fonte
31

Acabei de adicionar uma solução aqui (existe uma maneira no git de listar todos os arquivos excluídos no repositório?) Para encontrar as confirmações dos arquivos excluídos usando um regexp:

git log --diff-filter=D --summary | sed -n '/^commit/h;/\/some_dir\//{G;s/\ncommit \(.*\)/ \1/gp}'

Isso retorna tudo excluído em um diretório chamado some_dir(em cascata). Qualquer sed regexp lá onde \/some_dir\/está vai fazer.

OSX (graças a @triplee e @keif)

git log --diff-filter=D --summary | sed -n -e '/^commit/h' -e '\:/:{' -e G -e 's/\ncommit \(.*\)/ \1/gp' -e }
estani
fonte
1
Agradável. Alguma incompatibilidade no bash no OS X:sed: 1: "/^commit/h;/\/some_dir\ ...": bad flag in substitute command: '}'
Brent Faust
@BrentFoust É uma pena que eu não possa testar isso ... tente adicionar um espaço no final (depois das chaves, mas antes da aspas simples), a página de manual on-line não é clara sobre isso ...
Estani
Boa sugestão. Mas adicionar um espaço antes da aspas simples não ajudou. Nem um espaço antes da chave de fechamento.
Brent Faust
1
BSD / OSX sedAparentemente, o nem sempre é bom com ponto e vírgula como separadores de comando. Tente transformá-los em novas linhas ou mude parased -n -e '/^commit/h' -e '\:/some_dir/:{' -e G -e 's/\ncommit \(.*\)/ \1/gp' -e }
triplee
1
Eu testei e git log --diff-filter=D --summary | sed -n -e '/^commit/h' -e '\:/:{' -e G -e 's/\ncommit \(.*\)/ \1/gp' -e } trabalhei para mim no OSX.
keif
21

Você pode encontrar o último commit que excluiu o arquivo da seguinte maneira:

git rev-list -n 1 HEAD -- [file_path]

Mais informações estão disponíveis aqui

Akif
fonte
11
A principal solução votada não funcionou para mim, mas esta funcionou.
Nick Heiner
0

Tentar:

git log --stat | grep file
Eric Woodruff
fonte