Comment prévisualiser une fusion dans git?

397

J'ai une branche git (la ligne principale, par exemple) et je veux fusionner dans une autre branche de développement. Ou alors?

Afin de décider si je veux vraiment fusionner cette branche, je voudrais voir une sorte d'aperçu de ce que la fusion fera. De préférence avec la possibilité de voir la liste des commits appliqués.

Jusqu'à présent, le mieux que je puisse trouver est merge --no-ff --no-commit, et puis diff HEAD.

Glenjamin
la source
17
Je voudrais juste git mergeet git reset --keep HEAD@{1}si je n'aime pas le résultat.
Jan Hudec
3
Notez que voir la liste des validations avec leur diff ne raconte pas nécessairement toute l'histoire - si la fusion n'est pas triviale, et surtout s'il y a des conflits, le résultat réel de la fusion peut être un peu intéressant.
Cascabel
2
C'est exactement ce que fait votre méthode d'origine. Le point de mon commentaire est que bien que regarder les différences individuelles soit bien, si vous avez une fusion complexe, vous pouvez vous retrouver avec des résultats surprenants même si tous les commits fusionnés sont indépendamment bons.
Cascabel
2
@Jan: Pour certaines raisons, vous avez git reset --keep HEAD@{1}retourné l' fatal: Cannot do a keep reset in the middle of a merge.aide?
moey
3
Pourquoi n'y a-t-il pas d' --previewoption dans git-merge?
Gaui

Réponses:

285

J'ai trouvé que la solution qui me convenait le mieux était de simplement effectuer la fusion et de l'annuler en cas de conflit . Cette syntaxe particulière me semble propre et simple. Il s'agit de la stratégie 2 ci-dessous.

Cependant, si vous voulez vous assurer de ne pas gâcher votre branche actuelle, ou si vous n'êtes tout simplement pas prêt à fusionner indépendamment de l'existence de conflits, créez simplement une nouvelle sous-branche et fusionnez-la:

Stratégie 1: La manière sûre - fusionner une branche temporaire:

git checkout mybranch
git checkout -b mynew-temporary-branch
git merge some-other-branch

De cette façon, vous pouvez simplement supprimer la branche temporaire si vous voulez simplement voir quels sont les conflits. Vous n'avez pas à vous soucier d '«abandonner» la fusion, et vous pouvez retourner à votre travail - il vous suffit de retirer à nouveau «mybranch» et vous n'aurez aucun code fusionné ni conflit de fusion dans votre branche.

Il s'agit essentiellement d'un essai à sec.

Stratégie 2: quand vous voulez vraiment fusionner, mais seulement s'il n'y a pas de conflits

git checkout mybranch
git merge some-other-branch

Si git signale des conflits (et UNIQUEMENT EN CAS DE CONFLIT ), vous pouvez alors:

git merge --abort

Si la fusion réussit, vous ne pouvez pas l'annuler (seulement réinitialiser).

Si vous n'êtes pas prêt à fusionner, utilisez la méthode plus sûre ci-dessus.

[EDIT: 2016-Nov - J'ai échangé la stratégie 1 pour 2, car il semble que la plupart des gens recherchent «la manière sûre». La stratégie 2 est désormais plus une note que vous pouvez simplement annuler la fusion si la fusion a des conflits que vous n'êtes pas prêt à traiter. Gardez à l'esprit si vous lisez des commentaires!]

Kasapo
la source
2
+1 pour la stratégie 2. Branche temporaire qui montre exactement ce qui se passera lors de la fusion. La stratégie 1 est ce que j'essaie d'éviter pour ce cas particulier.
Gordolio
2
Je suggère de changer les stratégies afin que la plus sûre soit la première (ma formation psychologique passe - la plupart des gens supposeraient que la meilleure option serait la première, malgré l'utilisation claire du mot "plus sûr") mais, à part cela, excellent emploi.
paxdiablo
6
Vous pouvez faire un git merge --no-ff --no-commitsi vous ne souhaitez pas valider directement les modifications. Cela élimine le "besoin" d'une branche différente, ce qui facilite un peu plus la révision des modifications, imo.
Gerard van Helden
et si vous décidez que vous ne voulez pas fusionner du tout et qu'en fait, vous préférez écrire un peu plus de code et ensuite vous engager dans votre branche afin de pouvoir déployer JUST votre branche sur un serveur de test, n'auriez-vous pas encore à revenir sur le git merge --no-ff --no-commit? Je suppose que vous pouvez toujours faire un git merge --abortaprès cela si vous n'aimez pas ce que vous voyez? Même si la fusion ne produit pas de conflits?
Kasapo
La plupart des gens aiment copier et coller et ne pensent pas trop. Par conséquent, pour la stratégie 1, ajoutez peut-être également comment annuler la fusion dans la branche locale temporaire git reset --hard HEAD, puis extraire une autre branche git checkout <different branch name>et supprimer la branche temporaire git delete -b <name of temporary branch>.
user3613932
402
  • git log ..otherbranch
    • liste des modifications qui seront fusionnées dans la branche actuelle.
  • git diff ...otherbranch
    • diff de l'ancêtre commun (base de fusion) à la tête de ce qui sera fusionné. Notez les trois points , qui ont une signification particulière par rapport à deux points (voir ci-dessous).
  • gitk ...otherbranch
    • représentation graphique des branches depuis leur dernière fusion.

Une chaîne vide implique HEAD, c'est pourquoi juste ..otherbranchau lieu de HEAD..otherbranch.

Les deux contre trois points ont une signification légèrement différente pour diff que pour les commandes qui listent les révisions (log, gitk etc.). Pour log et autres, deux points ( a..b) signifient tout ce qui est dans bmais pas aet trois points ( a...b) signifient tout ce qui est dans un seul aou b. Mais diff fonctionne avec deux révisions et là le cas le plus simple représenté par deux points ( a..b) est la différence simple de aà bet trois points ( a...b) différence moyenne entre ancêtre commun et b( git diff $(git merge-base a b)..b).

Jan Hudec
la source
7
Le troisième point était la partie qui me manquait, merci! L'approche logarithmique fonctionne bien aussi, log -p --reverse ..otherbranch semble être un bon moyen de voir ce qui serait fusionné.
Glenjamin
1
Je manquais git checkout masteravant d'essayer. Je me demandais pourquoi cela disait que tous mes changements allaient être
écrasés
5
git show ..otherbranchaffichera une liste des modifications et des différences qui seront fusionnées dans la branche actuelle.
Gaui
4
Ce n'est pas tout à fait exact, en particulier pour les médiators.
void.pointer
2
@ void.pointer, git ne traite pas spécialement les sélections de cerises en fusion normale, il ne le fait pas non plus ici. Une stratégie de fusion pourrait être rédigée, mais pour autant que je sache, elle ne l'a jamais été.
Jan Hudec
19

Si vous êtes comme moi, vous cherchez l'équivalent de svn update -n. Ce qui suit semble faire l'affaire. Notez que assurez-vous de faire une git fetchpremière afin que votre référentiel local dispose des mises à jour appropriées pour comparer.

$ git fetch origin
$ git diff --name-status origin/master
D       TableAudit/Step0_DeleteOldFiles.sh
D       TableAudit/Step1_PopulateRawTableList.sh
A       manbuild/staff_companies.sql
M       update-all-slave-dbs.sh

ou si vous voulez un diff de votre tête à la télécommande:

$ git fetch origin
$ git diff origin/master

IMO cette solution est beaucoup plus facile et moins sujette aux erreurs (et donc beaucoup moins risquée) que la solution haut de gamme qui propose de "fusionner puis abandonner".

djschny
la source
1
devrait être $ git checkout target-branchet ensuite $ git diff --name-status ...branch-to-be-merged(trois points sont essentiels, alors saisissez-les tels
quels
Une chose manque: il ne vous montre pas quels fichiers auraient un conflit - ils ont le même marqueur "M" que les fichiers qui ont été modifiés sur la branche mais peuvent être fusionnés sans aucun conflit.
peterflynn
Peut-être que les choses étaient différentes en 2012, mais ces jours-ci, l'annulation d'une fusion semble assez fiable, donc je ne pense pas qu'il soit juste de qualifier cela de "beaucoup plus facile et moins sujet aux erreurs" en ce moment.
David Z
8

La plupart des réponses ici nécessitent soit un répertoire de travail propre et plusieurs étapes interactives (mauvaises pour les scripts), soit ne fonctionnent pas dans tous les cas, par exemple les fusions passées qui apportent déjà certaines des modifications en suspens dans votre branche cible, ou des sélections faisant le même.

Pour vraiment voir ce qui changerait dans la masterbranche si vous y fusionniez, developen ce moment:

git merge-tree $(git merge-base master develop) master develop

Comme c'est une commande de plomberie, elle ne devine pas ce que vous voulez dire, vous devez être explicite. Il ne colore pas non plus la sortie ni n'utilise votre pager, donc la commande complète serait:

git merge-tree $(git merge-base master develop) master develop | colordiff | less -R

- https://git.seveas.net/previewing-a-merge-result.html

(merci à David Normington pour le lien)

PS:

Si vous obtenez des conflits de fusion, ils s'afficheront avec les marqueurs de conflit habituels dans la sortie, par exemple:

$ git merge-tree $(git merge-base a b ) a b 
added in both
  our    100644 78981922613b2afb6025042ff6bd878ac1994e85 a
  their  100644 61780798228d17af2d34fce4cfbdf35556832472 a
@@ -1 +1,5 @@
+<<<<<<< .our
 a
+=======
+b
+>>>>>>> .their

L'utilisateur @dreftymac fait valoir un bon argument: cela le rend impropre à l'écriture de scripts, car vous ne pouvez pas facilement saisir cela dans le code d'état. Les marqueurs de conflit peuvent être très différents selon les circonstances (supprimés vs modifiés, etc.), ce qui rend également difficile la grep. Il faut se méfier.

hraban
la source
1
@hraban Cela ressemble à la bonne réponse, mais il reste apparemment un élément manquant. Cette approche nécessite que l'utilisateur "regarde" la sortie pour voir si les marqueurs de conflit sont présents. Avez-vous une approche qui revient simplement trues'il y a des conflits et falses'il n'y a pas de conflits (par exemple, une valeur booléenne qui ne nécessite pas le test du "globe oculaire" ou toute intervention humaine).
dreftymac
1
@dreftymac ne trouve rien à cet effet. vous pourriez utiliser quelque chose comme git merge-tree ... | grep -q '^[a-z].*in both$' && echo conflict || echo safe to mergemais c'est finnicky; J'oublie probablement un cas. peut-être voulez-vous plutôt vérifier les marqueurs de conflit? Par exemple, cela n'attrape probablement pas les conflits de style «le leur a été supprimé, le nôtre a changé». (Je viens de vérifier et cela ne montre même pas de marqueurs de conflit, donc vous avez besoin d'une expression méticuleuse pour être en sécurité ici)
hraban
1
Utilisez less -Rpour gérer la sortie colorée sans modifier votre configuration.
Giacomo Alzetta
Merci @GiacomoAlzetta, je l'ai mis à jour.
hraban
6

Si vous avez déjà récupéré les modifications, mon préféré est:

git log ...@{u}

Cela nécessite cependant git 1.7.x. La @{u}notation est un "raccourci" pour la branche en amont, donc c'est un peu plus polyvalent que git log ...origin/master.

Remarque: Si vous utilisez zsh et la fonction glog étendue, vous devrez probablement faire quelque chose comme:

git log ...@\{u\}
Pablo Olmos de Aguilera C.
la source
6

En plus des réponses existantes, un alias pourrait être créé pour afficher le diff et / ou le journal avant une fusion. De nombreuses réponses omettent la tâche fetchà effectuer avant de "prévisualiser" la fusion; il s'agit d'un alias qui combine ces deux étapes en une (émulant quelque chose de similaire à hg incoming/ de mercurial outgoing)

Donc, en vous appuyant sur " git log ..otherbranch", vous pouvez ajouter ce qui suit à ~/.gitconfig:

...
[alias]
    # fetch and show what would be merged (use option "-p" to see patch)
    incoming = "!git remote update -p; git log ..@{u}"

Pour la symétrie, l'alias suivant peut être utilisé pour montrer ce qui est validé et serait poussé, avant de pousser:

    # what would be pushed (currently committed)
    outgoing = log @{u}..

Et puis vous pouvez exécuter " git incoming" pour afficher un grand nombre de modifications, ou " git incoming -p" pour afficher le correctif (c'est-à-dire le "diff"), " git incoming --pretty=oneline", pour un résumé concis, etc. Vous pouvez ensuite (facultativement) exécuter " git pull" pour fusionnent réellement. (Cependant, puisque vous avez déjà récupéré, la fusion peut être effectuée directement.)

De même, " git outgoing" montre ce qui serait poussé si vous exécutiez " git push".

Michael
la source
3

git log currentbranch..otherbranchvous donnera la liste des commits qui iront dans la branche courante si vous faites une fusion. Les arguments habituels pour vous connecter qui donnent des détails sur les commits vous donneront plus d'informations.

git diff currentbranch otherbranchvous donnera la différence entre les deux commits qui deviendront un. Ce sera un diff qui vous donnera tout ce qui sera fusionné.

Est-ce que cela aiderait?

Noufal Ibrahim
la source
2
En fait, c'est faux. git log otherbranch..currentbranchdonne la liste des commits sur currentbranch . La git diff otherbranch currentbranchdonne vous diff de la version à fusionner à la pointe actuelle, qui est à peu près aussi inutile que possible, car ce que vous voulez est différent de la base de fusion à la tête de fusion.
Jan Hudec
Merci. J'ai changé les noms des arbres.
Noufal Ibrahim le
3

Pull Request - J'ai utilisé la plupart des idées déjà soumises, mais celle que j'utilise aussi souvent est (surtout si elle provient d'un autre développeur) de faire une Pull Request qui donne un moyen pratique de revoir toutes les modifications d'une fusion avant qu'elle ne prenne endroit. Je sais que GitHub n'est pas git, mais c'est certainement pratique.

G-Man
la source
2

À moins d'effectuer réellement la fusion de manière jetable (voir la réponse de Kasapo), il ne semble pas y avoir de moyen fiable de voir cela.

Cela dit, voici une méthode qui se rapproche légèrement:

git log TARGET_BRANCH...SOURCE_BRANCH --cherry

Cela donne une indication juste des validations qui feront la fusion. Pour voir les différences, ajoutez -p. Pour voir les noms de fichiers, ajouter l' un --raw, --stat, --name-only, --name-status.

Le problème avec l' git diff TARGET_BRANCH...SOURCE_BRANCHapproche (voir la réponse de Jan Hudec) est que vous verrez des différences pour les changements déjà dans votre branche cible si votre branche source contient des fusions croisées.

antak
la source
1

Peut-être que cela peut vous aider ? git-diff-tree - Compare le contenu et le mode des blobs trouvés via deux objets d'arbre

Chugaister
la source
1

Je ne veux pas utiliser la commande git merge comme précurseur de l'examen des fichiers en conflit. Je ne veux pas faire de fusion, je veux identifier les problèmes potentiels avant de fusionner - des problèmes que la fusion automatique pourrait me cacher. La solution que je cherchais est de savoir comment avoir git cracher une liste de fichiers qui ont été modifiés dans les deux branches qui seront fusionnés à l'avenir, par rapport à un ancêtre commun. Une fois que j'ai cette liste, je peux utiliser d'autres outils de comparaison de fichiers pour explorer plus loin les choses. J'ai cherché plusieurs fois et je n'ai toujours pas trouvé ce que je veux dans une commande native git.

Voici ma solution de contournement, au cas où cela aiderait quelqu'un d'autre là-bas:

Dans ce scénario, j'ai une branche appelée QA qui comporte de nombreux changements depuis la dernière version de production. Notre dernière version de production est étiquetée "15.20.1". J'ai une autre branche de développement appelée new_stuff que je veux fusionner avec la branche QA. QA et new_stuff pointent tous les deux sur les commits qui "suivent" (comme rapporté par gitk) la balise 15.20.1.

git checkout QA
git pull
git diff 15.20.1 --name-only > QA_files
git checkout new_stuff
git pull
git diff 15.20.1 --name-only > new_stuff_files
comm -12 QA_files new_stuff_files

Voici quelques discussions qui expliquent pourquoi je souhaite cibler ces fichiers spécifiques:

Comment puis-je faire confiance à Git Merge?

/software/199780/how-far-do-you-trust-automerge

Laura
la source