Workflows d'équipe Git : Merge ou rebase ?

Nicola Paolucci
Nicola Paolucci
Retour à la liste

La question est simple : pour une équipe de développement qui utilise Git et la création de branche de fonctionnalité, quel est le meilleur moyen d'intégrer le travail fini dans la ligne de développement principale ? Ce débat est sulfureux. Les opinions sont bien tranchées et les conversations sont rarement constructives (pour d'autres exemples de débats sulfureux, voir : Internet).

Should you adopt a rebase policy where the repository history is kept flat and clean? Or a merge policy, which gives you traceability at the expense of readability and clarity (going so far as forbidding fast-forward merges)?

La question fait débat

Le sujet est un peu controversé, mais peut-être pas autant que les anciennes guerres saintes entre vim et Emacs ou entre Linux et BSD. Quoi qu'il en soit, les deux camps font clairement entendre leur voix.

My empirical pulse on all-things-git – scientific, I know! – is that the always merge approach has a slightly bigger mind share. But the always rebase field is also pretty vocal online. For examples see:

To be honest, the split in two camps – always rebase vs. always merge – can be confusing, because rebase as local cleanup is a different thing than rebase as team policy.

De plus : le rebase en tant que nettoyage est génial dans le cycle de vie du code

Rebase as team policy is a different thing than rebase as cleanup. Rebase as cleanup is a healthy part of the coding lifecycle of the git practitioner. Let me detail some example scenarios that show when rebasing is reasonable and effective (and when it's not):

  • You're developing locally. You have not shared your work with anyone else. At this point, you should prefer rebasing over merging to keep history tidy. If you've got your personal fork of the repository and that is not shared with other developers, you're safe to rebase even after you've pushed to your fork.
  • Your code is ready for review. You create a pull request, others are reviewing your work and are potentially fetching it into their fork for local review. At this point you should not rebase your work. You should create 'rework' commits and update your feature branch. This helps with traceability in the pull request, and prevents the accidental history breakage.
  • Review is done and ready to be integrated into the target branch. Congratulations! You're about to delete your feature branch. Given that other developers won't be fetch-merging in these changes from this point on, this is your chance to sanitize history. At this point you can rewrite history and fold the original commits and those pesky 'pr rework' and 'merge' commits into a small set of focussed commits. Creating an explicit merge for these commits is optional, but has value. It records when the feature graduated to master.

With this aside clear we can now talk about policies. I'll try to keep a balanced view on the argument, and will mention how the problem is dealt with inside Atlassian.

Stratégie d'équipe Rebase : Définition, avantages et inconvénients

It's obviously hard to generalize since every team is different, but we have to start from somewhere. Consider this policy as a possible example: When a feature branch's development is complete, rebase/squash all the work down to the minimum number of meaningful commits and avoid creating a merge commit – either making sure the changes fast-forward (or simply cherry-pick those commits into the target branch).

While the work is still in progress and a feature branch needs to be brought up to date with the upstream target branch, use rebase – as opposed to pull or merge – not to pollute the history with spurious merges.

Avantages :

  • L'historique du code reste plat et lisible. Des messages de commit propres et clairs font autant partie de la documentation de votre base de code que les commentaires du code, les commentaires de votre outil de suivi de tickets, etc. Pour cette raison, il est important de ne pas polluer l'historique avec 31 commits d'une seule ligne qui s'annulent partiellement les uns les autres pour une seule correction de fonctionnalité ou de bug. Parcourir l'historique pour déterminer quand un bug ou une fonctionnalité a été introduit(e), et pourquoi, sera difficile dans une situation pareille.
  • La gestion d'un commit unique est facile (par exemple, son revert).

Inconvénients :

  • Compresser la fonctionnalité en une poignée de commits peut masquer le contexte, sauf si vous conservez la branche historique avec tout l'historique de développement.
  • Rebasing doesn't play well with pull requests, because you can't see what minor changes someone made if they rebased (incidentally, the consensus inside the Bitbucket Server development team is to never rebase during a pull request).
  • Rebasing can be dangerous! Rewriting history of shared branches is prone to team work breakage. This can be mitigated by doing the rebase/squash on a copy of the feature branch, but rebase carries the implication that competence and carefulness must be employed.
  • It's more work: Using rebase to keep your feature branch updated requires that you resolve similar conflicts again and again. Yes, you can reuse recorded resolutions (rerere) sometimes, but merges win here: Just solve the conflicts one time, and you're set.
  • Another side effect of rebasing with remote branches is that you need to force push at some point. The biggest problem we've seen at Atlassian is that people force push – which is fine – but haven't set git push.default. This results in updates to all branches having the same name, both locally and remotely, and that is dreadful to deal with.

NOTE: When history is rewritten in a shared branch touched by multiple developers breakage happens.

Stratégie d'équipe Merge : définitions, avantages et inconvénients

Always Merge-based policies instead flow like this: When a feature branch is complete merge it to your target branch (master or develop or next).

Make sure the merge is explicit with --no-ff, which forces git to record a merge commit in all cases, even if the changes could be replayed automatically on top of the target branch.

Avantages :

  • Traçabilité : les informations sur l'existence historique d'une branche de fonctionnalité peuvent être conservées, et tous les commits de la fonctionnalité sont regroupés.

Inconvénients :

  • History can become intensely polluted by lots of merge commits, and visual charts of your repository can have rainbow branch lines that don't add too much information, if not outright obfuscate what's happening. (Now to be fair, confusion is easily solved by knowing how to navigate your history; The trick here is to use, for example, git log --first-parent to make sense of what happened.)
Merge trees
  • Debugging using git bisect can become much harder due to the merge commits.

Décisions, décisions, décisions : qu'est-ce que vous importe le plus ?

Quelle est la meilleure solution ? Que recommandent les experts ?

If you and your team are not familiar with, or don't understand the intricacies of rebase, then you probably shouldn't use it. In this context, always merge is the safest option.

If you and your team are familiar with both options, then the main decision revolves around this: Do you value more a clean, linear history? Or the traceability of your branches? In the first case go for a rebase policy, in the later go for a merge one.

Notez qu'une stratégie de rebase est assortie de petites contre-indications et qu'elle nécessite plus d'efforts.

Chez Atlassian

The policy inside Atlassian's Bitbucket Server team is always to merge feature branches, and require that branches are merged through a pull request for quality and code review. But the team is not too strict around fast-forward.

Conclusions et remerciements

This article is the result of the confluence of insightful exchanges (pun intended!) with the Bitbucket Server team on the topic.

This piece hopefully dispels the doubts on this, and allows you to adopt an approach that works for your team. Follow me @durdn, and the fantastic @AtlDevtools team for more git awesomeness.

Prêt à découvrir Git ?

Essayez ce tutoriel interactif.

Démarrez maintenant