
A note of reflection after 10 years of “A successful Git branching model” - nilsandrey
https://nvie.com/posts/a-successful-git-branching-model/
======
umvi
I tried using Gitflow on our team, but there was too much complexity for me.
We ended up switching to Trunk Based Development (TBD)[1] which I and my team
found to be much simpler.

[1] [https://trunkbaseddevelopment.com/](https://trunkbaseddevelopment.com/)

~~~
zoomablemind
Same here. Trunk based is easy to explain and enforce.

The only challenge we faced was maintaining patch releases. Folding in fixes
for common issues which were found in newer releases sometimes involved more
than just cherrypicking the fix into a patch release for an older version.

This inevitably led to asking users to update to more up-to-date versions.
This further enforced the linear development paradigm.

~~~
gregmac
This is the one thing that I find lacking in trunk-based development. I work
on multiple products that have a release branch for each minor release, and it
works incredibly well other than this as a minor pain point.

I find the easiest way is that if you _know_ you want (or even think you might
want) a particular fix in a patch version, you can make a branch _from_ the
oldest release branch you want to support. When you create a PR, you can
easily target to the release branch (or another later release branch) or
master, depending on what you want. You can even do multiple PRs to merge it
multiple places.

No matter how you go about it, it should always be safe to merge a release
branch back into master. In fact, this is a good thing to do after you release
a patch version, but can also be done whenever you need a particular fix in
newer (master, or other feature branch code) code immediately.

Merging release branches like this makes following a fix significantly easier
than if you do it with cherry picking. Consider if you're looking at bug
ticket, and trying to figure out what branches/releases it was fixed in. If
the PR was merged to master, then later cherry picked to an older release, all
you'll see is it was in master. Unless someone manually comments about it or
your tooling picks it up based on references, the only other thing to do is
check the code itself to see if the fix is there. When you instead merge
forward, even if the ticket comments are wrong or not there, you can literally
follow the branches from the PR/commits and see every release it got into.

~~~
brandmeyer
> Unless someone manually comments about it or your tooling picks it up based
> on references.

I don't find this to be overly burdensome on the tooling. If the ticket-
tracking system can't automate traceability between git commits and tickets,
then it isn't well-suited to git-based software development.

We use the following policy:

\- A bugfix commit must fix the bug, the whole bug, and nothing but the bug.
If that seems impractical for the task at hand, make it practical by breaking
up the bug's ticket into smaller elements.

\- Bugfix commits always cite the bug tracking system's ticket number.

\- The bug tracking system observes the git tree and maintains its own linkage
to it.

\- When cherry-picking the bugfix to other affected branches, pass the `-x`
argument to `git cherry-pick`.

That's it. The (cherry-picked from) comment helps out when just viewing the
git history on its own. And the bug tracking system lets you quickly see all
of the commits and pull requests that referred to the bug.

------
yodsanklai
Reading the comments, I realize I'm a git neophyte (limited experience working
on some open source software, didn't know "gitflow" was a thing) so excuse me
for my naive question. But what about this simple workflow. Developers submit
PR on top of a master branch (developer is in charge of rebasing, PR must pass
integration test). Administrator is in charge of merging, maintains a linear
history. Releases are just tags on master. Has this workflow a name? anything
wrong with it?

~~~
woodrowbarlow
the risk of this workflow is that PRs will languish for too long, and the work
required to rebase them when they're actually needed will cause friction and
mistakes. this will often manifest when your software reaches a certain level
of maturity and you have established a release model that distinguishes
between a release that includes only bugfixes and a release that introduces
new features.

you definitely want to get your PRs merged in to _something_ as soon as they
are complete, to avoid bitrot. but just because they're complete doesn't mean
you want them included in the next release.

~~~
grovellogic
You can merge master/develop into your PR multiple times, always keeping it up
to date with the latest until until it can be merged.

This is the magic of git, it allows multiple merges something that wasn't
possible in older systems.

~~~
ivankolev
That is what I do too. Combined with restriction on the pull request so you
can't merge it if it's behind the branch you are merging onto.

------
gulbrandr
Is this related to the "Please stop recommending Gitflow" [1] article posted
yesterday?

[1]
[https://news.ycombinator.com/item?id=22485489](https://news.ycombinator.com/item?id=22485489)

~~~
sytse
Kind of, that article recommends making things less complex if you do
continuous delivery to a SaaS.

The update article mentions it was meant for versioned software.

I tried to articulate the various levels of branching you need in GitLab Flow
[https://docs.gitlab.com/ee/topics/gitlab_flow.html](https://docs.gitlab.com/ee/topics/gitlab_flow.html)
The various levels are: feature branches, production branches, environment
branches, and release branches.

~~~
waheoo
Sounds like gitlab tried to fix githubflow instead of realising its
fundamentally broken and going with the gerrit pattern that works fine for
both trunk based and release style applications.

~~~
sytse
We offer multiple options that depend on your need. GitHub flow and Git flow
are at two ends but there are also intermediate steps.

------
keithnz
We started with gitflow, but our team ended up going with a Release based
branching system as the constant integration of various features ended up
giving less control on what to deploy and when. Most common scenario...

Devs create feature branches from master

Features will be targeted to a release at some point, Release branch gets
taken from master

Devs PR feature branches to the release branch

Release branch merges back to master on deployment.

~~~
rojobuffalo
that workflow has always been my preference. QA can review features in
batches, you can schedule releases to minimize impact, and several eyes are on
the release to catch possible bugs.

------
kureikain
A problem with GitFlow is it's painful to follow PR model.

Especially on the Github repository where pushing to master is a lock and can
only done by PR and merge(or squash or whatever).

Let's take a look at a release:

1\. Branch of release/1.0 from develop 2\. Fix some bug, then at the time of
deploymennt. Merge it back to master _and_ develop

But once we merge, github close the PR... If we attempt to create another PR
for other branch. The Github now see different history between
master/develop...

~~~
cfstras
There exist scripts and workflows to perform auto-merges from all release
branches back to master. This way, you only have to care about branching off
releases, and hotfixes will automatically land on develop once released.

(Assuming master is your "next release", and develop is your main development
branch)

~~~
kureikain
Like I said, I cannot merge because we cannot push from our computer to
Github(It's locked).

The only way to get code into branch is by doing a PR, and click merge button
on Github UI. But a PR cannot be merged twice.

And If I open two PRs two master/develop then I will see this kind of error on
Github: "This branch is 1 commit behind. 1 commit ahead of develop"

~~~
cfstras
the way to go here is probably creating an "administrator" account which is
used by those merging scripts, and allow push access to protected branches for
administrators.

~~~
dillonmckay
So, on my team, juniors can create feature branches and easily deploy a
feature branch to a ‘staging’ server.

If the feature is deemed good, an ‘admin’ will ‘finish’ the feature and it is
merged into develop branch.

Then, when we are ready for a release, the ‘admin’ will create a release and
both develop and master are updated.

Updating master triggers a webhook that deploys the release.

For a hotfix, the release step is skipped, but both develop and master are
updated, again causing the webhook to trigger a deployment.

------
debaserab2
I've yet to work on a codebase that was so congested it required such strong
delineation between feature/hotfix/release but I can see how it might be
required at a certain size. I wonder what that threshold is.

~~~
stickydink
It depends what you're working on. We have a fairly small team (7 engineers)
working on a reasonably complex app (many features).

Our "features" take anything from 1 day to 3 weeks of effort, and generally
need to roll out as a whole. They are often really upgrades of existing
features, and the work to continuously merge w/ feature flags, supporting both
flows/designs, is just not something we've felt was worth the extra time.

We release on a 2 week cycle, the same day each time. We merge our features
into the develop branch as they are ready and safe for production.

Our scheduled release day is Wednesday. The Thursday beforehand, we branch
from develop into a release/x.y.0. This builds release candidates. We have a
sanity check on Friday, then our remote QA team runs through them over the
weekend. Any issues they find, we try to have squared away by Monday, Tuesday
at the latest.

Meanwhile other team members are still merging new things into develop. It's
useful to have that release branch because it lets the rest of the team keep
moving - we maybe only put out 1 or 2 developer's features each cycle, the
rest are still trickling in.

Once we release, we tag, merge into master, and start over again. If any
issues show up on production, we use the hotfix branch, hotfix/x.y.z. That
let's us resolve issues with risking a ship of any of the changes that have
come into develop that haven't had a few days of stability behind it.

For our team scale, we have a lot of users (100k's DAU), and we find this
method is a nice balance between the pace of output you might get from true CI
but with the reliability of a more traditional cycle.

Backend and web projects, however... Straight CI, into master, and we roll out
test > staging > production when we're happy.

~~~
debaserab2
I basically follow the same protocol as you up to this point:

> Once we release, we tag, merge into master, and start over again. If any
> issues show up on production, we use the hotfix branch, hotfix/x.y.z. That
> let's us resolve issues with risking a ship of any of the changes that have
> come into develop that haven't had a few days of stability behind it.

In our case, there's no point in a hotfix branch because master is always
considered stable. You don't merge to master unless you're ready for that code
to hit production. Feature gates are used for hiding live code when necessary
for marketing announcements or if the feature simply isn't ready for actual
customer usage yet (sometimes we leave the endpoints live but turn off all the
navigation to the endpoint so we can send out the link as a "beta" test for
specific customers).

This does occasionally cause problems with feature branches lagging behind,
but it's on the developer to ensure that their code merges into master
smoothly (usually best done by syncing your feature branch back up to master
frequently)

------
cjfd
It all depends on the situation. The simplest possible situation of mostly
just having a master branch also can work quite well. When you have
specialized testers that test a candidate release you start to need release
branches. Feature branches start being necessary if changes are temoprarily
incompatible with current master. This should not be too common, though. In
many cases one can avoid feature branches by having feature switches that get
removed as soon as a feature is done. If one wants to or needs to use feature
branches they should be short-lived because one will be creating a merge hell
if this is not the case. The git flow seems just too complicated for most
cases. One comment already notices that it is quite unclear why a separate
development and master branch are necessary.

------
edpichler
I love this branching model. I have being used in all projects and teams I
can. I use it even in projects where I work alone. For me it is the ultimate
branching model.

~~~
sguav
Same for me, I see many comments about this not being ideal for scalable
projects or Saas, but in my experience gitflow has worked well even for
maintaining pcb design repositories!

~~~
edpichler
For me it is being super good in scalable projects, and together with well
oiled CICD pipelines.

------
arghskwidge
AFAICT Gitflow just has one more branch than it needs. If you got rid of the
master branch and supported production releases off release branches it would
be just as useful and significantly less complicated. Can anyone explain to me
what real value the master branch adds in this model? And without using fluffy
meaningless words like, "source of truth," please.

~~~
jordigh
I don't really understand the distinction between develop and master either.
You're supposed to branch off develop and merge forward back into develop,
then merge into a release branch, then merge into master. So everything in
develop is supposed to end up in master.

So... why not branch off master and merge back into master?

~~~
kubanczyk
There is a small trade secret, that's hard to visualize. If you merge two
changes into master you can break it.

The fact that a change passed a CI and another change passed a CI doesn't mean
they can merge and the result is guaranteed to pass CI. It will often break
and if you think for a minute you will come up with really rudimentary
examples of this.

The really primitive solution is to call the merge-recipient branch the
"develop" and only merge to "master" when you are sure what you are merging is
good. You'll be ok if you do it sequentially (no parallel merges to master).

A more complicated solution is to actually test (in CI) "what happens when I
merge this" but in hiding, without showing it to others. Even more complicated
is to do it in parallel. Gitlab calls it Merge Trains (Premium only). Zuul-CI
calls it gate/gating and has a pretty good description here: [https://zuul-
ci.org/docs/zuul/discussion/gating.html#testing...](https://zuul-
ci.org/docs/zuul/discussion/gating.html#testing-in-parallel)

~~~
geofft
Also known as the "Not Rocket Science" rule:
[https://graydon2.dreamwidth.org/1597.html](https://graydon2.dreamwidth.org/1597.html)

When you're ready to merge, have CI test the result of the merge, and only
push the merge commit if things pass. Don't let anyone other than CI push to
master.

You can set up a bot with a queue to do this, of course, but you can also
define "CI" as a shell script. GitHub, GitLab, etc. will automatically close a
pull request if you push to master; you don't have to push the button on the
website. Train people to not hit "merge" and instead to run a script,
something like this which I just threw together:

    
    
        #!/bin/sh -e
        repo="$(mktemp -d)"
        git clone https://... "$repo"
        cd "$repo"
        git merge "$1"
        make test
        git push
        cd
        rm -rf "$repo"
    

If tests fail, it'll abort at "make test". If someone else pushed something,
it'll abort at "git push" and you can just rerun the command, which will test
the result of the latest merge. If you retry a lot, it's a sign your team is
busy enough you should invest in setting up a real bot - not a sign that you
should forego this property and land untested things on the develop branch and
make yourselves deal with doing development on top of untested code.

~~~
kubanczyk
I don't like the article - too many words, too many digressions. Also the
chosen catch-phrase is poor.

As long as GitHub does it wrong, you won't be able to "train people" not to
hit the big juicy green button, that should be really a big red "Emergency
Merge - Can Break The Receiving Branch" button.

------
ggm
I have never succeeded in working in a branch and having the real owners
accept my state.

I am of course talking about 'outsider' changes to code, but the effect is
real: the master owner can elect to say "sorry, we decided to re-architect
significantly and what you did is now superfluous, or at least too hard for us
to re-integrate" and almost everything I have been involved with, this is what
happened.

Submitting small patches to their head state is more effective for me than
trying to maintain state against a moving target with a realistic chance of
making significant change, and having it accepted into the mainline.

------
nilsandrey
Strongly agreed with the new author note. I used the same ideas with the teams
I've been coaching: "Consider your context...." as the need or not of support
for multiple versions and "...Decide for yourself.

------
juped
I accept your apology.

History is for reading, not writing. Do we make decisions that give us
readable and informative histories? The people who give up and rebase
absolutely everything into one linear trunk are not working optimally, but at
least they're trying.

------
mkchoi212
So cool that something Linus Torvalds made to solve his own problems, spread
all around the world so quickly. I guess great dev tools can only be made by
great developers, who have been through a lot of scenarios/problems
themselves.

~~~
bmn__
Don't neglect to give enough credit to BitKeeper, the DVCS some kernel folks
used before and whose design in broad strokes was cloned by Linus.

~~~
koreth1
Or even further back: BitKeeper was a successor to an earlier DVCS from the
early 1990s, Sun Workshop TeamWare. Everything old is new again.

