git diff renomeado arquivo

105

Eu tenho um arquivo a.txt.

cat a.txt
> hello

O conteúdo de a.txté "olá".

Eu me comprometo.

git add a.txt
git commit -m "first commit"

Eu então mudo a.txtpara um testdir.

mkdir test
mv a.txt test

Eu então faço meu segundo commit.

git add -A
git commit -m "second commit"

Finalmente, edito a.txtpara dizer "adeus" em vez disso.

cat a.txt
> goodbye

Eu faço meu último compromisso.

git add a.txt
git commit -m "final commit"

Agora, aqui está a minha pergunta:

Como faço para diferenciar o conteúdo a.txtentre meu último commit e meu primeiro commit?

Eu tentei:, git diff HEAD^^..HEAD -M a.txtmas não funcionou. git log --follow a.txtdetecta corretamente a renomeação, mas não consigo encontrar um equivalente para git diff. Existe um?

Ken Hirakawa
fonte
Suponho que seria o mesmo se você fizesse um 'teste git mv a.txt'? IE, você acabou de renomear o arquivo em vez de movê-lo para um subdiretório.
Max Waterman

Respostas:

107

O problema com a diferença entre HEAD^^e HEADé que você tem um a.txtem ambos os commits, então apenas considerando esses dois commits (que é o que diff faz), não há renomeação, há uma cópia e uma mudança.

Para detectar cópias, você pode usar -C:

git diff -C HEAD^^ HEAD

Resultado:

index ce01362..dd7e1c6 100644
--- a/a.txt
+++ b/a.txt
@@ -1 +1 @@
-hello
+goodbye
diff --git a/a.txt b/test/a.txt
similarity index 100%
copy from a.txt
copy to test/a.txt

A propósito, se você restringir seu diff a apenas um caminho (como você faz em git diff HEAD^^ HEAD a.txtvocê nunca vai ver os renomeamentos ou cópias porque você excluiu tudo, exceto um único caminho e renomeia ou copia - por definição - envolve dois caminhos.

CB Bailey
fonte
2
Vamos dizer que o commit continha várias alterações de arquivo. Como eu restringiria a uma diferença de um único arquivo?
Ken Hirakawa,
3
@KenHirakawa use -- <old-path> <new-path>... veja minha resposta.
Nolan Amy
No meu caso, eu queria mostrar os detalhes de um commit onde o arquivo foi renomeado, mas estava apenas me mostrando que um arquivo foi excluído e um arquivo foi adicionado ... Eu executei git show -C [commit]e ele reconheceu o arquivo renomeado e me mostrou o diff entre os arquivos. Perfeito.
Jason
83

Para diferenciar a renomeação de um arquivo específico, use -M -- <old-path> <new-path>( -Ctambém funciona).

Portanto, se você renomeou e alterou um arquivo no último commit, poderá ver as alterações com:

git diff HEAD^ HEAD -M -- a.txt test/a.txt

Isso produz:

diff --git a/a.txt b/test/a.txt
similarity index 55%
rename from a.txt
rename to test/a.txt
index 3f855b5..949dd15 100644
--- a/a.txt
+++ b/test/a.txt
@@ -1,3 +1,3 @@
 // a.txt

-hello
+goodbye

( // a.txtlinhas adicionadas para ajudar o git a detectar a renomeação)


Se o git não estiver detectando a renomeação, você pode especificar um limite de baixa similaridade com -M[=n], digamos, 1%:

git diff HEAD^ HEAD -M01 -- a.txt test/a.txt

Dos documentos do git diff :

-M [<n>] --encontrar-renomear [= <n>]

Detecte renomeações. Se nfor especificado, é um limite no índice de similaridade (ou seja, quantidade de adições / exclusões em comparação com o tamanho do arquivo). Por exemplo, -M90%significa que o Git deve considerar um par excluir / adicionar para ser renomeado se mais de 90% do arquivo não foi alterado. Sem %sinal, o número deve ser lido como uma fração, com um ponto decimal antes dele. Ou seja, -M5torna-se 0,5 e, portanto, é igual a -M50%. Da mesma forma, -M05é o mesmo que -M5%. Para limitar a detecção a renomeações exatas, use -M100%. O índice de similaridade padrão é 50%.

Nolan Amy
fonte
... Claro, também funciona quando a alteração e renomeação estão em commits separados como no exemplo original:git diff HEAD^^ HEAD -M -- a.txt test/a.txt
Nolan Amy
1
Somente quando o conteúdo dos arquivos está próximo o suficiente para o diff chegar a um índice de similaridade. Usar ambas as opções (-M e -C) mostra a diferença para / dev / null e de / dev / null em um arquivo que foi renomeado e mudou totalmente (incluindo indentação), ele nem mesmo detecta ao ignorar espaço em branco.
DavidG
37

Você também pode fazer:

git diff rev1:file1 rev2:file2

que, para o seu exemplo, seria

git diff HEAD^^:./a.txt HEAD:./test/a.txt

Observe o explícito ./- caso contrário, esse formato assume que os caminhos são relativos à raiz do repo. (Se você estiver na raiz do repo, é claro que pode omitir isso.)

Isso não depende da detecção de renomeação, pois o usuário está explicitamente informando exatamente o que comparar. (Portanto, também é útil em algumas outras circunstâncias, como comparar arquivos entre diferentes ramos svn em um ambiente git-svn.)

benkc
fonte
1
Oooh, isso é bom!
Nolan Amy
Para uma renomeação com alterações de arquivo adicionais trazidas por meio de uma mesclagem, esta é a única resposta que funcionou para mim. Obrigado!
Lane Rettig
1

Se sua renomeação de commit foi testada, mas ainda não foi confirmada, você pode usar:

git diff --cached -M -- file.txt renamed_file.txt
Mr-IDE
fonte