Boire ou coder ... Pourquoi choisir?
Publié le 19 octobre 2010 07:17

Rebase vs. Merge

A partir du moment ou vous avez commencé à travailler à plusieurs sur un projet geré par GIT, vous êtes probablement déjà tombé sur un commit du genre de celui-ci.

Merge branch 'master' of github.com:lyonrb/lyonrb

La question que tout le monde peut se poser au premier abord est "WTF? On est en master, on a mergé depuis master. Qu'est-ce qu'il fout là ce commit".

Pourquoi des merge entre la même branche

Si cette phrase ne veut rien dire, c'est avant tout ... Parce que ce n'est pas la même branche.
Ce qu'il s'est passé dans ce commit, c'est la chose suivante :

  • Franck a committé sur le repository. Puis il a poussé sur github et il est parti faire autre chose.
  • J'ai récupéré ses modifications; j'ai committé sur le repository, poussé sur github et je suis parti faire autre chose.
  • Quand Franck est revenu, il a oublié de récupérer mes modifications. Il ne l'a donc fait que après avoir committé d'autres choses dans son repository.

Lorsque Franck a fait un git pull pour récupérer ses modifications après avoir appliqué les miennes, cela a généré ce commit de merge.

Ce qu'il faut savoir, c'est que du point de vue de GIT, c'est que nous avons deux branches différentes : master et origin/master. La première est notre branche locale, celle sur laquelle nous committons. La seconde est la branche sur github, celle qui nous permet de partager notre travail avec les autres développeurs.

Lorsque vous faites un git push ou un git pull, en interne, GIT ne fait que merge ces deux branches ensemble.
Etant donné qu'elles ont divergées, un nouveau commit se crée afin d'appliquer ces modifications.

Les branches, c'est quoi ?

GIT est super dans les facilités qu'il apporte pour la gestion des branches, notamment comparé à SVN.
Une branche nous permet de continuer à travailler sur notre application. Mais tout en ne mélangeant pas ce que nous faisons avec ce qui existe déjà.
Ainsi, à tout moment, nous pouvons revenir sur la branche principale et ne plus avoir les modifications que nous avions fait précédemment.

Ainsi, il est recommandé de travailler sur une nouvelle branche à chaque fois que vous travaillez sur quelque chose de conséquent (qui prends plus de 30 minutes). Que ce soit une correction de bug ou une nouvelle fonctionnalité.

De cette manière, si lorsque vous êtes en train de travailler sur votre nouveau truc, un bug surgit et doit être corrigé tout de suite, vous pouvez stasher vos modifications; revenir sur la branche principale et ainsi corriger votre bug sans avoir les modifications sur lesquelles vous n'avez pas fini de travailler.

Vous pouvez donc même redéployer votre application sans que vos visiteurs ne voient le début de refonte du design de votre application.

Merge VS. Rebase

Bien évidemment, lorsque vous avez fini de travailler sur votre branche, vous allez devoir fusionner les modifications faites dans celle-ci avec les modifications faites dans votre branche principale.
C'est à cela que servent merge et rebase.
Soyons tout de suite clair cependant : merge est, dans 80% des cas, préférable à rebase.

Merge, c'est quoi ?

Les deux images ci-dessus représentent un repository avec une branche de feature. Et cette branche de feature après qu'elle ait été mergée.

Comme vous pouvez le constater, GIT crée un commit supplémentaire afin de rassembler ces deux branches.
Et l'on voit bien dans l'historique que à un moment, ces deux parties de code ont été séparées.

Rebase, c'est quoi ?

Ici, nous avons pris exactement le même repository. Mais au lieu de merger, nous avons rebasé.
Comme vous pouvez le constater, nous ne voyons plus la branche qui existait précédemment.

En effet, lorsque nous rebasons, GIT effectue le travail suivant :

  • Il mets tous les commits effectués sur la branche à rebaser (ici master) de côté et les annule.
  • Il applique tous les commits effectués sur la seconde branche tel des patches à notre branche master.
  • Puis il réapplique tous les commits effectués sur master.

Ainsi, en rejouant l'historique, c'est comme si nous n'avions jamais eu de branche. Par ailleurs nous n'avons pas de commit signalant la fusion entre les deux branches.

Pour en revenir à notre problème de départ

Si je ne vous ai pas encore perdu, vous devez vous demander "ok, mais l'introduction de son article est hors contexte".

Et non !
Car je vous ai énoncé le seul moment ou (selon moi. Les avis peuvent diverger), on devrait faire un rebase et non un merge.

En effet, lorsque vous créez volontairement une branche pour une nouvelle fonctionnalité, il est important de conserver ses modifications. Et de voir aisément dans l'historique de votre repository "de telle date à telle date, cette fonctionnalité était en cours de développement et non déployée". Avec rebase, ce n'est pas possible.

En revanche, lorsque quelqu'un a fait des modifications dans la branche principale du repository et que vous aussi, ce n'est pas une branche différente. C'est juste deux personnes différentes. On se moque de voir un historique.
Par ailleurs si vous poussez régulièrement, vous risquez d'avoir autant de commits de merge que de commits normaux. Alors, la lisibilité de votre repository en prends un coup.

C'est pourquoi je ne fais jamais de git pull !
Mais uniquement des git pull --rebase.

Ainsi, au lieu de merger ma branche distante avec ma branche locale, GIT va la rebaser. Et je n'ai pas de commit supplémentaire. Y'a plus qu'à pusher et tout est clean.

Vous pouvez même configurer votre repository pour que à chaque fois que vous faire un git pull, cela ne soit pas un merge mais un rebase qui soit fait.

Dans le fichier .git/config, ajoutez la ligne suivante à la configuration de votre branche :

[branch "master"]
    rebase = true

Ainsi, automatiquement, git fera des rebase au lieu des merge lorsque vous pullerez cette branche.

Et en cas de conflit ?

Bah oui, vous êtes déjà tous tombé sur le cas du merge ou GIT trouve un conflit et n'arrive pas à faire ce merge tout seul.
La, dans le cas d'un merge, c'est simple. Vous corrigez manuellement le conflit et committez manuellement. Le merge se fait sur le commit que vous faites alors.

Rebase est tout aussi simple pour vous !
Lors d'un conflit lors d'un merge, vous constaterez que GIT vous sors totalement de toute branche.

Corrigez votre conflit, faites un git add mais ne committez pas (vous ne pourriez pas, vu que vous n'êtes pas dans une branche).

Faites ensuite un simple git rebase --continue. GIT saura alors comment interprétez les modifications à apporter pour chacun des conflits que vous avez corrigé et il pourra continuer son rebase tout seul. Vous n'y verrez que du feu lorsque celui-ci sera terminé.

Conclusion

Comme vous avez pu le constatez dans cet article, travailler avec des branches (et notamment les fusionner lorsque vous avez fini) est particulièrement simple.
Alors pourquoi s'en priver ? Autant en user et en abuser.

D'ailleurs cela vous est fortement recommandé. Et si vous ne voyez pas encore vraiment comment faire, je vous conseiller de jeter un coup d'oeil à git flow, qui vous donnera un excellent exemple de workflow GIT en utilisant les branches.

Commentaires

Guillaume
Guillaume dit: 19 octobre 2010 08:36 Site web

Whaou, ça c'est de l'explication, j'ai enfin compris !

Merci :-)

nilsine
nilsine dit: 06 avril 2012 14:36 Site web

Idem, j'ai enfin compris ce qui semblait qqchose de complexe.

Postez un commentaire

Markdown activé