web-dev-qa-db-fra.com

git afficher un commit de fusion

quand j'ai un commit de fusion et que j'exécute git show #commit, il n'affiche que le journal de commit, pas le diff pour le vrai changement, comme

commit c0f50178901e09a1237f7b9d9173ec5d1c4936c
Merge: ed234b ded051
Author: abc
Date:   Mon Nov 21 15:56:33 2016 -0800

    Merge branch 'abc'

Je comprends que le vrai commit est dans le journal de fusion, mais je veux enregistrer la saisie, existe-t-il un moyen d'afficher le diff en un?

27
Sohaib Farooqui

TL; DR: utilisez git show -m c05f017 ou git show --first-parent c05f017, ou peut-être git diff c05f017^ c05f017.


Il y a une erreur fondamentale dans votre question: les commits ne sont pas des différences; les commits sont des instantanés. Cela peut sembler être une distinction sans différence - et pour certains commits, c'est c'est. Mais pour les validations de fusion, c'est pas.

Quand git show (ou git log -p) montre un commit comme un diff, il le fait en comparant l'instantané du commit à autre chose. Le git diff commande fait la même chose: elle compare un commit à un autre commit. (Ou il peut comparer une validation à l'arbre de travail, ou au contenu de l'index, ou à quelques autres combinaisons également.)

Pour les validations ordinaires, il est trivial de savoir quoi comparer: comparer ceci l'instantané du commit au instantané du commit précédent (c.-à-d. Parent). Voilà donc ce que git show fait (et git log -p aussi): il exécute un git diff du commit parent, à ce commit.

Cependant, les validations de fusion n'ont pas qu'une seule validation parent. Ils ont deux parents.1 C'est ce qui les rend "merge commits" en premier lieu: la définition d'un merge commit est un commit avec au moins deux parents.


1Un commit de fusion peut avoir trois parents ou plus. Celles-ci sont appelées "fusions de poulpes". Ils ne font rien de spécial, cependant, et sont principalement pour se montrer. :-) Vous pouvez les ignorer ici.


Lorsqu'il y a deux parents, lequel (s) devrait (doivent) git show comparer contre?

Quoi git log -p choisit de faire par défaut n'est pas du tout de comparer. Vous pouvez lui faire afficher quelque chose en ajoutant divers drapeaux (voir ci-dessous).

Quoi git show choisit de faire par défaut est plus compliqué. Puisqu'il y a deux parents, git show compare d'abord avec le "premier parent",2 compare ensuite avec le deuxième parent. Ensuite — cette partie est assez cruciale — elle combine les deux diffs , produisant un soi-disant "diff combiné".

Pour la section suivante, permettez-moi de noter un peu délicat, mais très utile, de la syntaxe Git. Si vous avez un ID de validation comme c05f017, vous pouvez ajouter un caret ou un caractère "chapeau" ^ après cela, pour nommer un commit parent. Vous pouvez éventuellement ajouter un autre numéro pour sélectionner lequel parent. Pour les validations régulières (sans fusion), il n'y en a qu'une, donc c05f017^ est le parent. Pour les validations de fusion, c05f017^ et c05f017^1 les deux signifient le premier parent, tandis que c05f017^2 signifie le deuxième parent.


2Je mets cela entre guillemets parce que l'idée premier parent est particulièrement importante dans Git, comme nous le verrons dans un instant. En d'autres termes, Git se soucie le plus de quel parent est d'abord, tandis que les autres ne sont que "les autres".


Différences combinées

Le format diff combiné est décrit dans la documentation , mais un bit clé est d'abord décrit ici , afin de le rendre particulièrement obscur:3

Notez que diff combiné répertorie uniquement les fichiers qui ont été modifiés à partir de tous les parents.

Autrement dit, supposez que [~ # ~] m [~ # ~] est un commit de fusion, et différent M ^ 1 vs [~ # ~] m [~ # ~] dit le fichier mainline.txt et common.txt ont été modifiés. Supposons en outre que différents M ^ 2 et [~ # ~] m [~ # ~] indique que le fichier sidebranch.txt et common.txt ont été modifiés. Le diff combiné affichera seulement common.txt, en ignorant les deux mainline.txt et sidebranch.txt car ces deux fichiers ont été modifiés uniquement à partir de un parent (chacun). (Même dans ce cas, Git peut ne montrer que quelques différences pour common.txt.)


3Il m'a fallu beaucoup de temps pour trouver cela dans la documentation, alors que je continuais à regarder l'autre section.


Diviser les diffs

Le -m option - m signifie probablement merge ici - indique à Git de "diviser" la fusion. Autrement dit, au lieu d'essayer de combiner les diffs contre chaque parent en un grand diff combiné, montrez simplement le diff contre chaque parent, un diff à la fois.

C'est parfois ce que tu veux. Lorsque ce n'est pas ce que vous voulez, vous pouvez exécuter votre propre git diff juste pour faire la différence contre l'un des deux parents (ou voir ci-dessous).

Contre quel parent devriez-vous vous opposer?

Habituellement, la bonne réponse est "le premier parent".

La clé de la notion de "premier parent" est que lorsque Git effectue un commit de fusion, il enregistre toujours la branche sur laquelle vous vous trouvez à ce moment-là, en tant que premier parent. L'autre branche devient le deuxième parent.

Autrement dit, si vous êtes sur develop et que vous fusionnez topic:

$ git checkout develop
$ git merge topic

Git va faire un nouveau commit - un merge commit, avec deux parents - sur votre branche actuelle, develop. Le parent d'abord du commit de fusion sera le commit qui était la pointe de develop il y a un instant. Le parent seconde sera le commit qui est (toujours) la pointe de topic.

Étant donné que vous êtes généralement préoccupé par ce que la fusion a apporté, la comparaison avec le premier parent vous donnera cela. C'est généralement ce que vous voulez. Pour cette raison, git show vous permet d'exécuter git show --first-parent. Cela "divise" le commit puis git show ne diffère que par rapport au premier parent. (C'est un peu différent de git show -m, qui divise la validation deux fois: le premier fractionnement se compare au premier parent, et le deuxième fractionnement se compare au deuxième parent.)

De même, vous pouvez exécuter git log -p --first-parent ... mais vous devez quand même ajouter -m pour voir le changement comme un patch, car par défaut git log ignore simplement l'affichage des différences pour une fusion. (En interne, l'opération skipping-because-merge remplace la façon dont le fractionnement le fait agir comme not-a-merge.) Ici, le --first-parent flag a un effet encore plus important: l'opération de journalisation ne regarde pas du tout tout des validations de la branche latérale, seulement celles de la ligne principale (premier parent).

76
torek

Comme mentionné ici , ces solutions impliquent d'afficher un diff combiné, comme:

git diff --cc $M $M^1 $M^2 $(git merge-base $M^1 $M^2)

Mais: la sortie de "diff --cc "n'affichait pas les chemins d'origine lorsque la fusion impliquait des renommages .
Une nouvelle option dans Git 2.22 (Q1 2019) ajoute les chemins dans les arborescences d'origine à la sortie.

git diff --cc --combined-all-paths $M $M^1 $M^2 $(git merge-base $M^1 $M^2)

log, diff-tree: ajouter --combined-all-paths option

Le format de différence combiné pour les fusions ne répertoriera qu'un seul nom de fichier, même si la détection de changement de nom ou de copie est active.

Par exemple, avec le format brut, on peut voir:

::100644 100644 100644 fabadb8 cc95eb0 4866510 MM describe.c
::100755 100755 100755 52b7a2d 6d1ac04 d2ac7d7 RM   bar.sh
::100644 100644 100644 e07d6c5 9042e82 ee91881 RR   phooey.c

Cela ne nous permet pas de savoir quel était le nom d'origine de bar.sh était dans le premier parent et ne nous dit pas quel nom d'origine de phooey.c étaient dans l'un ou l'autre des parents.

En revanche, pour les validations non fusionnées, le format brut fournit des noms de fichiers originaux (et un score de changement de nom pour démarrer).
Afin de fournir également des noms de fichiers originaux pour les validations de fusion, ajoutez un --combined-all-paths option (qui doit être utilisée avec -c ou --cc, et n'est probablement utile que si la détection de renommer ou de copier est active) afin que nous puissions imprimer les noms de fichiers séparés par des tabulations lorsque les renommages sont impliqués.

Cela transforme la sortie ci-dessus en:

::100644 100644 100644 fabadb8 cc95eb0 4866510 MM desc.c  desc.c  desc.c
::100755 100755 100755 52b7a2d 6d1ac04 d2ac7d7 RM   foo.sh  bar.sh  bar.sh
::100644 100644 100644 e07d6c5 9042e82 ee91881 RR   fooey.c fuey.c  phooey.c

De plus, au format patch, cela change les en-têtes from/to de sorte qu'au lieu d'avoir juste un en-tête "from", nous obtenons un pour chaque parent.
Par exemple, au lieu d'avoir

--- a/phooey.c
+++ b/phooey.c

nous verrions

--- a/fooey.c
--- a/fuey.c
+++ b/phooey.c
0
VonC