Git - Merge ou rebase ?

Avec Git, on a plusieurs branches et on souhaite synchroniser ces branches (par exemple intégrer les derniers commits d’une branche à une autre). Ce cas de figure est extensible pour la tâche quotidienne : mise à jour de la branche courante (git pull). La question se pose : Quelle méthode mettre en place ?

Avec Git, on a deux possibilités : rebase et merge. Rappel sur ces deux méthodes :

  • Merge permet d’avancer la branche courante en incorporant le travail d’une autre branche. Cette opération joint et fusionne les deux points de départ de ces deux branches dans un commit de fusion qui est visible dans le graphe de l’historique (point D).

merge

  • Rebase rejoue sur une branche (que l’on appelle ma_branche) les commits d’une autre branche (que l’on appelle ref_branche) dans l’ordre d’apparition à partir du point de séparation. C’est comme si tous les travaux de ref_branche avaient été réalisés sur ma_branche. Rebase est transparent dans l’historique et permet de conserver un graphe linéaire.

rebase

Les commandes sont les suivantes :

git checkout ref_branche
git rebase ma_branche

Ou

git rebase ma_branche ref_branche

Le choix entre ces deux méthodes dépend du nombre de branches à synchroniser et du nombre de corrections traitant sur un même périmètre technique. En effet, suite à un merge de plusieurs branches, le graphe historique serait difficile à comprendre. Dans la mesure où les commits de fusion n’apportent pas d’information utile, ils polluent l’historique [2]. Et si, par exemple, plusieurs corrections sont réalisées sur une même fonction, c’est pénible de rejouer tous les commits de rebase.

  • Exemple 1 : Quand un travail local est considéré comme partant d’une base obsolète. Cela peut arriver plusieurs fois par jour, quand on essaie d’envoyer au remote nos commits locaux, pour découvrir que notre version de la branche distante trackée (par exemple origin/master) date : depuis notre dernière synchro avec le remote, quelqu’un a déjà envoyé des évolutions de notre branche au serveur, du coup notre propre travail part d’une base plus ancienne, et l’envoyer tel quel au serveur reviendrait à écraser le travail récent des copains. C’est pourquoi push nous enverrait promener. Un merge (git pull) n’est pas idéale, car elle ajoute du bruit, des remous, dans le graphe de l’historique, alors qu’en fait c’est juste un coup de pas de bol dans la séquence de travail sur cette branche. Dans un monde idéal, j’aurais fait mon boulot après les autres, sur une base à jour, et la branche serait restée bien linéaire. Dans ce cas, il est préférable d’utiliser une rebase (git pull –rebase). [1]

  • Exemple 2 : La branche ref_branche représente un sprint ou une story en méthodologie agile, ou encore un ticket d’incident (issue ou bug) précis, identifié dans la gestion de tâches [1]. Il est alors préférable d’utiliser la méthode merge.

  • Exemple 3 : La branche ref_branche est dédiée pour la correction des anomalies sur la production. Il faut alors utiliser la méthode merge car il est difficile de faire un rebase si plusieurs corrections sont réalisées sur un même périmètre.

  • Exemple 4 : Rebase et fusion d’une branche sur la branche principale

rebase1

Les commandes pour parvenir à ce résultat sont les suivantes :

git rebase master ref1
git checkout ma_branche
git merge ref1
git branch -d ref1

D’autres exemples pratiques

Après le rebase suivant :

git checkout ref_branche
git rebase ma_branche

On est sur ref_branche avec un point de départ divergé F :

rebase

  • Dans l’objectif de récupérer les derniers commits sur ref_branche faits après le rebase, sans avoir mis à jour le remote, il ne faut pas faire : git pull --rebase car git comprendrait mal ce qu’il faut faire et jouerait une série de commits de ma_branche depuis le point B sur le point de départ F.

  • Afin d’envoyer au remote la branche courante (ref_branche), ce n’est pas possible de faire git push car le point de départ divergé n’est pas la suite du HEAD de la branche courante. Il faut dans ce cas écraser le HEAD de ref_branche pour obtenir une branche différente qui porte le même nom (ref_branche) avec la commande :

git push origin +HEAD:ref_branche

[1] http://www.git-attitude.fr/2014/05/04/bien-utiliser-git-merge-et-rebase
[2] http://www.miximum.fr/git-rebase.html
[3] http://git-scm.com/docs/git-rebase