I’ll describe the so-called “next branch” workflow, used for the development of the Git project and for the Linux kernel, and illustrate how it systematically leads to criss-cross merges. A criss-cross merge happens when the greatest common ancestor of the nodes to be merged is not unique. It’s more of a curiosity; I made this observation and wanted to share it. Feel free to prove me wrong if I am missing things.
If you are in a hurry, you can skip the text and jump from image to image reading the captions; that should give you a fair idea of the content of this post.
The “next branch” workflow
This workflow is described in great detail at the gitworkflows(7) manpage. The main idea behind it is to keep a so called “throw-away integration branch” called “
next” (hence the name), where you merge all the contributions from your developers. The purpose of
next is to see how all those features interact together “in the wild”: some people use to build the software directly from
next, use it and report back to the developers. With
next acting as a “buffer” for features, you can decide which ones are ready from prime time and which ones needs more time, testing, improvements. It’s not “all or nothing”, you can select what you like.
When you’re satisfied by a feature that is sitting in
next you merge that feature alone in your “principal branch” (as Git calls it, “
master“), the one that sooner or later will be tagged, released and distributed to your users; this process is called “graduation”.
Note that some features might graduate from
next for a long time before seeing
master and hit the release.
Let’s see the workflow in action stage by stage.
It all starts from
As it is described in the gitworkflows(7) manpage, this workflow uses two more branches:
pu (which stands for “proposed updates”). The most stable of the four is
maint (the maintainance branch, meant to host fixes to the previous release), then comes
next, and the most experimental of them is
pu. I will not consider
pu, but limit this description to
next for the sake of simplicity.
So, for what matters to this post, “
master” is the principal and most stable branch.
next is an experimental fork of
next, our “throw-away” integration branch, is a fork of
master and hosts features that you aren’t yet sure work well. Since it’s clear to everybody that
next is only for testers and developers, regular users who build from the source know that the software from
next is not to be used in production.
Contributions are based on
Most of the developers send their patches to the mailing list. Once those diffs are peer reviewed, the git maintaner (Junio Hamano) applies them in his repository as “topic branches” that forks
master. Junio’s repository, with all those topic branches, is available at http://github.com/gister/git.
Topic branches are merged into
Alice and Bob, from the picture, are good developers and wrote patches that pass all tests and had favorable reviews on the mailing list. But how do their changes interact together? The combined result might not work at all.
This is the culprit of the whole workflow: Junio uses a throw-away integration branch to try things out, the
next is built and used by people willing to tolerate alpha software and help the developers reporting their experiences with it. I hear that the Debian community is involved into using Git built form
next and reporting what they see.
Topics may (or may not) graduate to
In the picture, the topic branch from Bob is merged into
master. This process is called “graduation”, and happens when Junio sees that Bob’s feature is sufficiently mature for prime time.
master will eventually be tagged as an official Git release, so graduation is kind of a big deal.
Note that in my picture Bob was the first to be merged into
next, and it’s also the first feature to graduate. Things might not happen in that order. Namely, a feature can stay in
next indefinitely — there is no timeline for graduation. It might even not happen at all. The whole point of the “
next workflow” is to have
next acting as a buffer. When you like a feature, you pick it from
next and merge it in
master. Note that this doesn’t happen via cherry picking, but via merging branches; merging offers some benefits over cherry-picking:
- changes are in a single place in the graph, as opposed to having the same changeset in many places under different hashes.
- merges are easier to handle, since with a single merge you can add many commits at once to a development line, as opposed to cherry-picking commits one by one.
Uh, look, while we were talking about merging and cherry picking, Alice’s topic graduated to
master. Congratulations, Alice!
What if we merge
This would transform the “
next branch workflow” in a completely different thing. I’ve seen this workflow erroneously called “topic branches workflow”, as if the central part of it was the fact that developers do their work in separate branches. While being an important characteristic, topic branches are not the defining trait of this way to work; better: it’s not what surprised me the most when learning about this workflow. The very point is that you try things out in
next, and when you’re happy you put the in
If you were to merge the whole
master, it would become “all-or-nothing”; no more freedom to accept mature features into the release while giving less stable developments some more time to stabilize.
Bugfixes go to the oldest stable branch…
There are changes that are both trivially correct and urgently needed. Those bugfixes get applied directly on a stable branch, skipping all the graduation dance.
… and propagates to experimental branches via merges
The small bugfixes I mentioned above aren’t needed only in the stable branch, they’re needed everywhere. The reason the get applied in
master and then propagated via a merge of
next is that merging in the opposite direction is forbidden.
Think of this: you apply the bigfix to
next first, then you’d want to merge
master. That’s not allowed, such merge would carry a lot of things that are not for
master (all the topics that haven’t graduated yet). This is to say: it have to be this way.
Here comes the criss-cross merge
The very reason I wrote this post is to share the following remark: when you merge
next you get a criss-cross merge. A criss cross merge is one where the greatest common ancestor of your two heads is not unique. Let’s see how the head of Alice’s topic branch is a greatest common ancestor for
as you can imagine, also Bob’s branch head is a greatest common ancestor:
Criss-cross merges might or might not be a big deal. Sometimes they present headaches. If you use this workflow and find yourself in this situation, I hope I provided some useful insights on the topology of the history graph that can help you understand what’s going on.
next continue their parallel lives…
The steps above are repeated as necessary:
next continue to evolve.
… until the end of the cycle
I said that
next is a “throw away” branch. If really
master where to live side to side indefinitely, you’d end up with two branches who’s content differs a lot. At some point, you want to kill
next and start afresh. At the end of a “cycle”, which for Git typically lasts for 8 to 10 weeks,
next is indeed erased and rebuild afresh by merging all topics that haven’t graduated yet. This gives the opportunity to restart clean, reorder topics etc.
This step is of capital importance to stay on top of all this growing complexity and helps to reason about what’s in where. An invariant like “
next has been forked off
master not more than 10 weeks ago” might come handy.
But it is also a lot of work: all the conflicts you had merging topics into
next will show up again, and again, and again until the conflicting topics finally gets into
master. Junio uses a git feature called rerere to remember how he solved conflicts in the past. Whatever trick you use, the luxury of merging features selectively from a sandbox branch to a production branch comes at the price of a lot of housekeeping.
It has been rightly observed that there’s no good reason to do this if you’re not automating
next. This is what Linux does:
Linux-next is an automatically-built branch combining >50 sources and detects conflicts and so on early.
- The next branch workflow uses two parallel branches: one is production and the other is a sandbox where you try things out. This architecture gives you complete freedom on what features to promote from the sandbox to the real world.
- It reflects a development model where you operate under no tight deadlines and only code that meet the highest quality standards is allowed to be released.
- The complexity of this workflow is nontrivial, and can only be handled if the
nextbranch is built automatically.
- If the product of your shop is a web service, i.e. you have marketing pushing for features, you operate your own platform and can patch the servers on the go if needed, probably a branch model where you merge the whole “development branch” into production all at once with scheduled release dates is most suited for your needs. The
nextbranch model lets you put features on hold indefinitely, but the sales people are on your neck anyway.
Thanks to Antoine Pelisse who introduced me to this workflow, and for the many insightful discussions we had on this.