Git rebase -- preserve-merges échoue

j'ai un arbre de commit (grand) qui contient plusieurs commits de fusion que je veux rebaser vers un autre commit. Faire un rebase normal amène git à me demander de résoudre les conflits de fusion. Je ne voulais pas revoir chaque Fusion, parce que ce serait beaucoup de travail. Après avoir découvert l'option --preserve-merges, qui est bien expliquée ici , j'ai pensé que j'avais trouvé l'outil parfait pour cette tâche. Cependant, je n'arrive pas à le faire fonctionner correctement. J'ai créé un jouet exemple qui démontre le problème.

à partir d'un dossier vide, nous créons d'abord une branche avec une fusion et une autre branche sur laquelle nous rebasons.

A---B--
      
 ---C---D
 
  ---E

maître se réfère à B , branche se réfère à D et au revoir-branche se réfère à E .

git init
echo Hello > Hello.txt
git add Hello.txt
git commit -m "Create Hello.txt (commit A)"
git tag start

echo World! >> Hello.txt
git commit -am "Change to Hello World (commit B)"

git checkout start
git checkout -b branch
echo Dave >> Hello.txt
git commit -am "Change to Hello Dave (commit C)"

git merge master
echo Hello World, Dave! > Hello.txt
git add Hello.txt
git commit -m "Merge branch master into branch (commit D)"

git checkout start
git checkout -b goodbye-branch
echo Goodbye > Goodbye.txt
git add Goodbye.txt
git commit -m "Add Goodbye.txt (commit E)"

Jusqu'à présent, tout s'est bien passé. Il y avait un conflit de fusion, mais nous avons résolu. Maintenant nous essayons de rebaser branche sur E pour finir avec l'arbre de propagation suivant:

A---E----B'
         
      C'---D'

git checkout branch
git rebase -p goodbye-branch

Cette fin avec l'erreur suivante:

Auto-merging Hello.txt
CONFLICT (content): Merge conflict in Hello.txt
Automatic merge failed; fix conflicts and then commit the result.
Error redoing merge f567809e2cc91244cc7fdac210e1771dc75e4d86

Le fichier contient le contenu suivant:

Hello
<<<<<<< HEAD
Dave
=======
World!
>>>>>>> 0437403c97f33f229e41ec9584ce891a50052e48

Qu'est-ce que je fais de mal? Je m'attendais à ce que git soit capable d'utiliser commettre D pour résoudre le conflit de fusion il rencontre alors que le rebasage.

j'utilise Git 1.9.4.msysgit.1, qui est la version la plus récente dès maintenant.

8
demandé sur Community 2014-09-04 20:24:27

1 réponses

TL; DR

le drapeau --preserve-merges indique simplement git-rebase d'essayer de recréer les propagations de fusion au lieu de les ignorer. Il ne donne pas git rebase la capacité de se rappeler comment les conflits de fusion ont été résolus, c.-à-d. il ne pas enregistrer les résolutions de conflit pour une utilisation future. Ce que vous voulez utiliser pour cela est rerere .

dans votre exemple de jouet, le conflit le rebase est exactement le même que celui que vous avez résolu lors de la fusion précédente. Si vous aviez activé rerere avant la fusion, vous n'auriez pas eu à résoudre ce conflit à nouveau pendant le rebase.

si vous prévoyez que vous allez fusionner, puis reformater une branche, vous devez activer rerere de sorte que, dans le futur, vous n'avez besoin de résoudre un conflit de fusion donné qu'une fois, et non plusieurs fois.

détaillé explication

voyons votre exemple de jouet.

git init
echo Hello > Hello.txt
git add Hello.txt
git commit -m "Create Hello.txt (commit A)"
git tag start

echo World! >> Hello.txt
git commit -am "Change to Hello World (commit B)"

git checkout start
git checkout -b branch
echo Dave >> Hello.txt
git commit -am "Change to Hello Dave (commit C)"

jusqu'ici, tout va bien. Juste avant votre première commande git merge , votre repo ressemble à ceci:

enter image description here

En commettre Un, Hello.text contient

Hello

En commettre B, Hello.text contient

Hello
World!

et dans commit C, Hello.text contient

Hello
Dave

maintenant, quand vous essayez de fusionner master en branch en lançant

git merge master

git signale un conflit de fusion parce qu'il n'a aucun moyen de déterminer, seul, si le contenu de Hello.txt après la fusion devrait être

Hello
World!
Dave

ou

Hello
Dave
World!

ou autre chose...

vous résolvez ce conflit en réécrivant le contenu de Hello.txt avec Hello World, Dave! , la mise en scène de vos modifications, et l'achèvement de la commit de fusion.

echo "Hello World, Dave!" > Hello.txt
git add Hello.txt
git commit -m "Merge branch master into branch (commit D)"

votre pension ressemble maintenant à ceci:

enter image description here

puis vous courez

git checkout start
git checkout -b goodbye-branch
echo Goodbye > Goodbye.txt
git add Goodbye.txt
git commit -m "Add Goodbye.txt (commit E)"

À ce stade, votre pension se présente comme suit:

enter image description here

Maintenant vous l'exécutez

git checkout branch
git rebase -p goodbye-branch

mais faire l'expérience d'un conflit. Avant d'expliquer pourquoi ce conflit survient, regardons à quoi ressemblerait votre pension si cette opération git-rebase était réussie (c.-à-d. sans conflit):

enter image description here

voyons maintenant pourquoi vous rencontrez le même conflit dans Hello.txt que lors de votre première fusion; Goodbye.txt n'est pas problématique en aucune façon, ici. Un rebase peut en fait être décomposé dans une séquence de plus opérations élémentaires ( checkout s et cherry-pick s); pour en savoir plus, lisez http://think-like-a-git.net/sections/rebase-from-the-ground-up.html . Longue histoire courte... Au milieu de votre opération git rebase , votre pension se présente comme suit:

enter image description here

La situation est très similaire à celle juste avant votre première fusion: dans commit B', Hello.text contient

Hello
World!

et dans commit c', Hello.text contient

Hello
Dave

puis Git tente de créer la fusion B' et C', mais un conflit de fusion se produit pour la même raison que le premier conflit de fusion que vous avez connu: Git n'a aucun moyen de comprendre si la ligne Dave devrait aller avant ou après la ligne World! . Par conséquent, l'opération rebase s'arrête, et Git vous demande de résoudre ce conflit de fusion avant qu'il ne puisse compléter le rebase.

ce que vous pouvez faire à ce sujet: utilisez rerere

Git rerere est votre ami, ici.

le nom signifie" reuse recorded resolution " et comme le nom l'indique, il vous permet de demander à Git de se rappeler comment vous avez résolu un conflit hunk afin que la prochaine fois qu'il voit le même conflit, git peut automatiquement le résoudre pour vous.

[...] si vous voulez prendre une branche que vous avez fusionnée et corrigé un tas de conflits et décider ensuite de la rebaser à la place - vous n'aurez probablement pas à faire tous les mêmes conflits à nouveau.

si rerere avait été activé,

git config --global rerere.enabled true

avant la fusion, alors Git aurait enregistré comment vous avez résolu le conflit de fusion lors de la création de commit D, et aurait appliqué la même résolution quand il a rencontré le même conflit lors du rebasé suivant. Le conflit aurait tout de même interrompu l'opération rebase, mais il aurait été résolu automatiquement. Tout ce que vous auriez eu à faire c'est git rebase --continue .

cependant, il semble que rerere n'était pas déjà activé avant la fusion, ce qui signifie que Git ne doit pas avoir gardé de trace de la façon dont vous avez résolu le conflit la première fois. À ce stade, vous pouvez soit activer rerere maintenant et résoudre tous ces mêmes conflits manuellement à nouveau, ou utiliser le rerere-train.sh script (voir aussi ce blog post ) pour utiliser l'histoire existante pour pré-semence le rerere cache.

25
répondu jubobs 2015-11-06 15:18:54