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
Où 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.
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:
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:
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:
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):
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:
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.