git diff renommé le fichier

105

J'ai un dossier a.txt.

cat a.txt
> hello

Le contenu de a.txtest "bonjour".

Je fais un engagement.

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

Je passe ensuite a.txtdans un testdir.

mkdir test
mv a.txt test

Je fais alors mon deuxième commit.

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

Enfin, je modifie a.txtpour dire "au revoir" à la place.

cat a.txt
> goodbye

Je fais mon dernier engagement.

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

Voici maintenant ma question:

Comment est-ce que je diffère le contenu a.txtentre mon dernier commit et mon premier commit?

J'ai essayé:, git diff HEAD^^..HEAD -M a.txtmais cela n'a pas fonctionné. git log --follow a.txtdétecte correctement le changement de nom, mais je ne trouve pas d'équivalent pour git diff. Est-ce qu'il y a un?

Ken Hirakawa
la source
Je suppose que ce serait la même chose si vous faisiez un «test git mv a.txt»? IE vous venez de renommer le fichier au lieu de le déplacer dans un sous-répertoire.
Max Waterman

Réponses:

107

Le problème avec la différence entre HEAD^^et HEADest que vous avez un a.txtdans les deux commits, donc juste en considérant ces deux commits (ce que fait diff), il n'y a pas de changement de nom, il y a une copie et un changement.

Pour détecter les copies, vous pouvez utiliser -C:

git diff -C HEAD^^ HEAD

Résultat:

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

Incidemment, si vous limitez votre diff à un seul chemin (comme vous le faites dans, git diff HEAD^^ HEAD a.txtvous n'allez jamais voir les renommages ou les copies parce que vous avez tout exclu d'un seul chemin et que les renommages ou les copies - par définition - impliquent deux chemins.

CB Bailey
la source
2
Disons que le commit contenait plusieurs modifications de fichier. Comment pourrais-je le réduire à un diff d'un seul fichier?
Ken Hirakawa
3
@KenHirakawa utilise -- <old-path> <new-path>... voir ma réponse.
Nolan Amy
Dans mon cas, je voulais afficher les détails d'un commit où le fichier a été renommé, mais cela me montrait simplement qu'un fichier a été supprimé et qu'un fichier a été ajouté ... J'ai couru git show -C [commit]et il a reconnu le fichier renommé et m'a montré le diffèrent entre les fichiers. Parfait.
Jason
83

Pour différencier le changement de nom d'un fichier spécifique, utilisez -M -- <old-path> <new-path>( -Cfonctionne également).

Donc, si vous avez à la fois renommé et modifié un fichier lors du dernier commit, vous pouvez voir les changements avec:

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

Cela produit:

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.txtlignes ajoutées pour aider git à détecter le changement de nom)


Si git ne détecte pas le changement de nom, vous pouvez spécifier un seuil de similitude faible avec -M[=n], disons 1%:

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

À partir de la documentation git diff :

-M [<n>] --trouver-renommer [= <n>]

Détectez les renommés. Si nest spécifié, il s'agit d'un seuil sur l'indice de similarité (c'est-à-dire le nombre d'ajout / de suppressions par rapport à la taille du fichier). Par exemple, cela -M90%signifie que Git devrait considérer une paire suppression / ajout comme un changement de nom si plus de 90% du fichier n'a pas changé. Sans %signe, le nombre doit être lu comme une fraction, avec un point décimal devant lui. Ie, -M5devient 0,5, et est donc le même que -M50%. De même, -M05est le même que -M5%. Pour limiter la détection aux noms exacts, utilisez -M100%. L'indice de similarité par défaut est de 50%.

Nolan Amy
la source
... Bien sûr, fonctionne également lorsque le changement et le changement de nom sont dans des commits séparés comme dans l'exemple d'origine:git diff HEAD^^ HEAD -M -- a.txt test/a.txt
Nolan Amy
1
Uniquement lorsque le contenu des fichiers est suffisamment proche pour que diff génère un index de similarité. L'utilisation des deux options (-M et -C) affiche la différence entre / dev / null et from / dev / null dans un fichier qui a été renommé et a entièrement changé (y compris en retrait), il ne l'attrape même pas en ignorant espace blanc.
DavidG
37

Vous pouvez également faire:

git diff rev1:file1 rev2:file2

qui, pour votre exemple, serait

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

Notez l'explicite ./- ce format suppose sinon que les chemins sont relatifs à la racine du dépôt. (Si vous êtes à la racine du repo, vous pouvez bien sûr l'omettre.)

Cela ne dépend pas du tout de la détection de changement de nom, car l'utilisateur indique explicitement ce qu'il faut comparer. (Par conséquent, il est également utile dans d'autres circonstances, telles que la comparaison de fichiers entre différentes branches svn dans un environnement git-svn.)

benkc
la source
1
Oooh, c'est sympa!
Nolan Amy
Pour un changement de nom avec des modifications de fichiers supplémentaires apportées via une fusion, c'est la seule réponse qui a fonctionné pour moi. Merci!
Lane Rettig
1

Si votre validation de changement de nom est préparée mais pas encore validée, vous pouvez utiliser:

git diff --cached -M -- file.txt renamed_file.txt
Mr-IDE
la source