242 votes

Rebaser un commit de fusion Git

Prenons le cas suivant :

J'ai travaillé sur une branche topic et je suis maintenant prêt à fusionner vers master :

* eb3b733 3     [master] [origin/master]
| * b62cae6 2   [topic]
|/  
* 38abeae 1

J'effectue la fusion depuis master, je résous les conflits et j'ai maintenant.. :

*   8101fe3 Merge branch 'topic'  [master]
|\  
| * b62cae6 2                     [topic]
* | eb3b733 3                     [origin/master]
|/  
* 38abeae 1

Maintenant, la fusion m'a pris un certain temps, donc je fais un autre fetch et je remarque que la branche master distante a de nouveaux changements :

*   8101fe3 Merge branch 'topic'  [master]
|\  
| * b62cae6 2                     [topic]
| | * e7affba 4                   [origin/master]
| |/  
|/|   
* | eb3b733 3
|/  
* 38abeae 1

Si j'essaie git rebase origin/master à partir de master, je suis obligé de résoudre tous les conflits à nouveau, et je perds également le commit de fusion :

* d4de423 2       [master]
* e7affba 4       [origin/master]
* eb3b733 3
| * b62cae6 2     [topic]
|/  
* 38abeae 1

Existe-t-il un moyen propre de rebaser le commit de fusion pour que je me retrouve avec un historique comme celui que je montre ci-dessous ?

*   51984c7 Merge branch 'topic'  [master]
|\  
| * b62cae6 2                     [topic]
* | e7affba 4                     [origin/master]
* | eb3b733 3
|/  
* 38abeae 1

88 votes

TL;DR : git rebase --preserve-merges origin/master

6 votes

En ce qui concerne la nécessité de résoudre à nouveau les conflits, vous pouvez consulter les documents suivants git rerere .

1 votes

git config --global pull.rebase preserve pour toujours préserver les commits de fusion lors d'un rebasement

181voto

siride Points 36602

Il y a deux options ici.

L'une d'entre elles consiste à effectuer un rebasement interactif et à modifier le commit de fusion, à refaire la fusion manuellement et à poursuivre le rebasement.

Une autre solution consiste à utiliser le --rebase-merges option sur git rebase qui est décrit comme suit dans le manuel :

Par défaut, un rebasement éliminera simplement les commits de fusion de la liste des tâches, et placera les commits rebasés dans une branche unique et linéaire. Avec --rebase-merges, le rebasement essaiera plutôt de préserver la structure des branches dans les commits qui doivent être rebasés, en recréant les merge commits. Tous les conflits de fusion résolus ou les modifications manuelles dans ces commits de fusion devront être résolus/appliqués manuellement. "

18 votes

J'ai essayé l'option -p, et elle laisse effectivement l'historique des livraisons comme je le voulais, mais elle m'oblige à résoudre à nouveau les conflits, même dans les fichiers qui n'ont pas été modifiés dans origin/master. En ce qui concerne votre première suggestion, quelle serait la séquence précise de commandes ?

3 votes

@jipumarino : git rebase -i (dites-lui d'éditer le commit de fusion), quand il arrive au commit de fusion, git reset --hard HEAD^, git merge, fixez les conflits, git commit, git rebase --continue. Vous pouvez aussi regarder git rerere qui est supposé aider avec ce genre de choses (mais je ne l'ai jamais utilisé, donc je ne peux pas offrir de conseils ou d'aide).

3 votes

Merci. J'ai activé rerere et essayé avec rebase -p et ça marche comme ça devrait.

50voto

Ivan Naydonov Points 471

Ok, c'est une vieille question et la réponse a déjà été acceptée par @siride mais cette réponse n'était pas suffisante dans mon cas, car --preserve-merges vous oblige à résoudre tous les conflits une deuxième fois. Ma solution basée sur l'idée de @Tobi B mais avec des commandes exactes étape par étape

Nous allons donc commencer par cet état en nous basant sur l'exemple de la question :

*   8101fe3 Merge branch 'topic'  [HEAD -> master]
|\  
| * b62cae6 2                     [topic]
| |
| | * f5a7ca8 5                   [origin/master]
| | * e7affba 4
| |/  
|/|   
* | eb3b733 3
|/  
* 38abeae 1

Notez que nous avons 2 commits avant master, donc le cherry-pick ne fonctionnerait pas.

  1. Tout d'abord, créons l'historique correct que nous voulons :

    git checkout -b correct-history # create new branch to save master for future
    git rebase --strategy=ours --preserve-merges origin/master

    Nous utilisons --preserve-merges pour sauvegarder notre commit de fusion dans l'historique. Nous utilisons --strategy=ours pour ignorer tous les conflits de fusion car nous ne nous soucions pas du contenu qui sera dans ce commit de fusion, nous avons seulement besoin d'un bel historique maintenant.

    L'histoire ressemblera à cela (en ignorant le maître) :

    *   51984c7 Merge branch 'topic'  [HEAD -> correct-history]
    |\  
    | * b62cae6 2                     [topic]
    * | f5a7ca8 5                     [origin/master]
    * | e7affba 4
    * | eb3b733 3
    |/  
    * 38abeae 1
  2. Obtenons un indice correct maintenant.

    git checkout master # return to our master branch
    git merge origin/master # merge origin/master on top of our master

    Nous pourrions obtenir quelques conflits de fusion supplémentaires ici, mais il ne s'agirait que de conflits provenant de fichiers modifiés entre 8101fe3 et f5a7ca8 mais n'inclut pas les conflits déjà résolus de topic

    L'histoire ressemblera à ceci (en ignorant l'histoire correcte) :

    *   94f1484 Merge branch 'origin/master'  [HEAD -> master]
    |\  
    * | f5a7ca8 5                   [origin/master]
    * | e7affba 4
    | *   8101fe3 Merge branch 'topic'
    | |\  
    | | * b62cae6 2                     [topic]
    |/ /
    * / eb3b733 3
    |/  
    * 38abeae 1
  3. La dernière étape consiste à combiner notre branche avec l'historique correct et la branche avec l'index correct.

    git reset --soft correct-history
    git commit --amend

    Nous utilisons reset --soft pour réinitialiser notre branche (et l'historique) à correct-history, mais laisser l'index et l'arbre de travail tels quels. Ensuite, nous utilisons commit --amend pour réécrire notre commit de fusion, qui avait un index incorrect, avec notre bon index de master.

    A la fin, nous aurons un tel état (notez un autre id du top commit) :

    *   13e6d03 Merge branch 'topic'  [HEAD -> master]
    |\  
    | * b62cae6 2                     [topic]
    * | f5a7ca8 5                     [origin/master]
    * | e7affba 4
    * | eb3b733 3
    |/  
    * 38abeae 1

7voto

Claude Peloquin Points 91

Étant donné que je viens de perdre une journée à essayer de résoudre ce problème et que j'ai trouvé une solution avec l'aide d'un collègue, j'ai pensé que je devais intervenir.

Nous avons une grande base de code et nous devons faire face à 2 branches fortement modifiées en même temps. Il y a une branche principale et une branche secondaire si vous voulez.

Pendant que je fusionne la branche secondaire dans la branche principale, le travail continue dans la branche principale et quand j'ai fini, je ne peux pas pousser mes changements parce qu'ils sont incompatibles.

Je dois donc "rebaser" ma "fusion".

C'est ainsi que nous avons finalement réussi :

1) Notez le SHA, par exemple : c4a924d458ea0629c0d694f1b9e9576a3ecf506b.

git log -1

2) Créez l'historique approprié mais cela brisera la fusion.

git rebase -s ours --preserve-merges origin/master

3) notez le SHA, par exemple : 29dd8101d78.

git log -1

4) Maintenant, revenez à l'endroit où vous étiez avant.

git reset c4a924d458ea0629c0d694f1b9e9576a3ecf506b --hard

5) Maintenant, fusionnez le master actuel dans votre branche de travail

git merge origin/master
git mergetool
git commit -m"correct files

6) Maintenant que vous avez les bons fichiers, mais le mauvais historique, mettez le bon historique en haut de votre liste de modifications. en haut de votre changement avec :

git reset 29dd8101d78 --soft

7) Et ensuite --amender les résultats dans votre commit de fusion original

git commit --amend

Voilà !

1voto

Antoine Pelisse Points 5067

On dirait que ce que vous voulez faire, c'est supprimer votre première fusion. Vous pourriez suivre la procédure suivante :

git checkout master      # Let's make sure we are on master branch
git reset --hard master~ # Let's get back to master before the merge
git pull                 # or git merge remote/master
git merge topic

Cela vous donnerait ce que vous voulez.

4 votes

Avec rerere activé, cela semble donner le même résultat que la solution rebase -p donnée ci-dessus par siride.

-2voto

Tobi B Points 5
  • À partir de votre validation de fusion
  • Sélectionnez les nouveaux changements qui devraient être faciles à réaliser.
  • copier vos affaires
  • refaire la fusion et résoudre les conflits en copiant simplement les fichiers de votre copie locale ;)

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X