Hacker News new | past | comments | ask | show | jobs | submit login
A successful Git branching model (2010) (nvie.com)
70 points by saaspirant on Sept 8, 2023 | hide | past | favorite | 75 comments



This was terrible at the time and it remains terrible now. Extremely few teams need this level of complexity and some parts of it – like the redundant long-lived develop branch – are pointless safety blankets. It seems to have only caught on because a lot of people felt adrift when it comes to Git and this was written far too authoritatively for what it is. I’ve seen so many small teams tie themselves up wasting time working for Git Flow that I think this might actually be the worst thing to ever happen to version control. I’m glad the author has mostly walked it back, but unfortunately the damage has been done.

If you are putting a team together and haven’t picked a Git workflow and are unsure what to do, start with something simple like trunk-based development and add complexity if and when you encounter problems that justify it.

https://trunkbaseddevelopment.com

Previous discussions:

Please stop recommending Gitflow: https://news.ycombinator.com/item?id=22485489

GitFlow considered harmful: https://news.ycombinator.com/item?id=9744059

The problem with Git Flow: https://news.ycombinator.com/item?id=23622071


Strong agreement with everything you've said here. I can't say enough good things about trunk-based development. I started managing a team with 15+ developers. They had used long-lived branches, with a develop, a staging, and a release branch. Not using TBD, "releases" were a huge ceremony and extremely inertial. Velocity went way, way up when we adopted TBD. It took some time for people to become comfortable with the new approach, not because it's complex, but precisely because they had grown so used to unnecessary complexity and had to unlearn many things.


> Strong agreement with everything you've said here. I can't say enough good things about trunk-based development.

I don't agree. Git flow is just trunk development applied to release-centric, non-web projects, after you learned very basic lessons on how to put together a production-ready release. There is absolutely no good reason to advocate in favour of ignorance and force you to reinvent the wheel on a process that exists for decades.

Failing to understand these needs only set you up to learn hard lessons the hard way at a time you least want to learn them: when Product is pushing you to release the damn thing and asking you why you are incapable of just posting a bugfix over a previous release.


In my experience, git tags are far more effective at managing releases than git branches. Each version is released once and only once. The relative immutability of a tag is a higher guarantee of success than the merge swamps that long-running release branches can sometimes become. If you need a bug fix to a previous release you check out the tag of the past release into a new short-lived branch, PR what you need to for the bug fix to that branch (and also any long term changes to the trunk branch), similarly version tag any releases made from the short-lived branch and then delete it.


> In my experience, git tags are far more effective at managing releases than git branches.

I'm sorry to say that your experience doesn't seem to be with production software with a significant release cadence. Tags fail very basic requirements of any release process. A release requires release-specific changes such as bumping version numbers, toggling configurations and feature flags, update changelogs and documentation, and more importantly accommodate bugfixes without having to merge in unrelated work on features actively under development that are not intended to ship.

Tags are only good to point to specific commits. Unless you're working on a pet project or you're the single developer working on software no one uses, a release is way more than pointing to a random commit in the development branch.


You don't know my experience and making assertions about what you don't know here verges on an ad hominem attack. Please rethink your approach.

I never said tags come only from a single development branch, I said that release branches are best when short-lived and that tags are a better long-term representation of released versions than long-running release branches.

If you want to discuss the complexities of the differences in workflow that entails, feel free to ask questions without assuming you have any idea what my experience level is.


Second that.

If one names development=trunk it is the same.

In git flow team also should not be active on release branch, it should only be fixes for the release and release branches should not be long lived. Well you might have staging or acceptance main branch but you don’t use it like long lived branch once you start release development/trunk becomes new truth so team should not maintain these “branches” really.

Developers worry only about trunk/development an their own feature benches in git flow. For me it is exactly the same as TBD.


> It seems to have only caught on because a lot of people felt adrift when it comes to Git and this was written far too authoritatively for what it is.

My hypothesis is the design of the page and the images played a big role in convincing people this is "legit".


It seemed much beloved by teams who’d used SVN or other source controls that had incremental version numbers, switched to Git, and strongly wanted to implement their old workflow in Git.

I worked at a shop that tried really hard to pretend that it was possible to strictly order commits (“What if dev A writes commit 1234, then dev B writes commit 5678 and pushes it, then dev A pushes 1234. Which came first?” “The earlier one!” “What does ‘earlier’ mean here?” “Why can’t you just do it!”) and they thought Git Flow was the greatest thing ever.

Put another way, it was popular among the same people who write every language as though it were their favorite. Have Python but they only have Java experience? All their Python looks like Java! Well, they thought Git Flow was spiffy, too.


This is the second comment I've seen in this thread comparing "Gitflow" to what was typical in SVN. Having been there when SVN was in vogue, I can assure you that our process looked nothing like Gitflow. SVN encourages what we now refer to as "trunk based development". The default branch was even called "trunk".


I was used to SVN too, and you’re right about the trunk bit. I’m mainly talking about SVN’s nicely linear revision numbers and how it was unambiguous that r23 came after r22. That’s the part that GitFlow adherents wanted to simulate.


I used SVN extensively since the early 2000's, and while I still don't like git (at least for projects that also involve non-coders), I liked git-flow even less (and as the sibling poster said, SVN encouraged a much simpler trunk-based development model).

My impression was that git-flow was pitched by people who didn't have much experience with other version control systems and were also new to git.


> it was possible to strictly order commits

This holds no relevance at all. Nothing in gitflow calls for strictly ordered commits. Just because there is a branch represented with semver that does not mean that the revision control system is involved in versioning.


Thank you.

This blog post is responsible for more toil than anything else I can think of.

> It seems to have only caught on because a lot of people felt adrift when it comes to Git and this was written far too authoritatively for what it is.

I think "Git Flow" was successful for the same reason that Javascript "Standard" linter was: misleading branding that lends credibility.


Agreed. In my teams, I've always implemented a simple model, which hasn't failed me. Main branch triggers deployment, and name rest of the branches <author>/<feature>. Just works.

In some cases, an extra caveat to the above was triggering deployment/build in Main, only when the tag/version number gets incremented.


> Agreed. In my teams, I've always implemented a simple model, which hasn't failed me. Main branch triggers deployment, and name rest of the branches <author>/<feature>. Just works.

So you work with a small team that does not need to put together releases nor does have quality assurance engineers verifying your code before it's released. That's perfectly fine.

Your team also has the luxury of pausing any additional development work while lining up a release. That's cool too.

Perhaps you don't even have releases, and just continuously integrate everything you throw at it to deploy it somewhere and rely on a multi-stage pipeline to catch problems. That's fine. That's also not the usecase for gitflow.

I don't think it's a good idea to project your team's needs to absolutes, and pretend that just because you don't benefit from a process it means none do.


You are making too many assumptions in the answer there; most of the things you assume about me, my team or experience are not correct :)

I do think the gitflow model is over-complicated.


Personally, I simply don't want to see any merge-commits, ever. Merges are hard to follow and may include conflict resolutions which are hard to show (if they show up at all).

Sure, "merging" is an amazing feature. But I am not working on projects with an organizational complexity like the Linux kernel. Short-lived & rebased branches so that code gets integrated into main fast. Conflict resolution is never postponed nor shared. Versioning/release/deploy/rollback is dealt with mostly outside of git, or with tags-on-main at most.


Merge commits also break regression finding, since if you checkout a commit that was part of a merge, you land in a tree state reflecting what tree at this commit looked like before the merge, which is a state the master branch has never been in. It often breaks things which work both before and after the merge commit. So you can never find with certainty the regression point at commit level, other than locally linearising this merge into a series of cherry picks. It’s just awful.


On the other hand, rebasing can create its own problems re: regression finding

If you rebase more than one commit, you can also have an intermediate commit which does not run fine, or even compile. This can happen silently, without conflict resolution while you rebase. Think a function call in trunk needs one additional parameter.

So you have a commit message saying "Protocol X tested successfully with Y", which was true when the code done and tested. After the rebase, it might not run anymore because of changes done in the trunk. You will probably notice it and correct it in a later commit, but good luck in 3 month when someone points a bug at protocol X and you want to re-run the test in this commit...


That’s why at work we have a “commit queue” that tests that every commit builds as it comes in in the order it’s merged (or more accurately: cherry-picked). Don’t leave those things to manual checks and human vigilance. They’re too fallible.


I think this can be mostly worked-around by requiring branches (PRs) to be rebased and tested/CI-passed before merging.

If PRs contain multiple commits, either squash them (on merge or before), or use some kind of queued / stacked PR system.


--first-parent, and both with git bisect and many other things, is your friend here.


That doesn’t fix the issue I described. It leads to a situation where you can say that a merge introduced a regression, but you don’t know which of the merged commits did, because it skips over them all. So it’s not commit-level.


> Merges are hard to follow and may include conflict resolutions which are hard to show (if they show up at all).

My experience is much the opposite. You can follow all the merge parents and three(+)-way diff them with the final merge result to see just the conflict resolutions in a merge.

On the other side, tracking down accidental bad conflict resolutions (which result in regressions) in rebased branches is nearly impossible because there's no single commit that just represents the conflict resolutions; depending on the developer doing the rebases they might be smeared across every commit in the entire branch. It gets hard to tell what changes were deliberate and which were conflict resolutions that the developer handled in some way maybe not so deliberately. There are some massive failure cases for regressions I've seen including having to get deep into the woods of git's rerere cache infrastructure and forcing developers to clear that cache so that they stop reintroducing the same kinds of hard to track down regressions in their rebases. (I am grateful that this worst case behavior is rare enough not everyone feels it; I've seen it just enough I prefer that junior developers rarely to never rebase because merge commits preserve more of the process and don't hide the mistakes under the rug.)

That said, I definitely agree that short-lived branches and code getting integrated as fast as possible so that conflict resolution is done early and often and as visibly as possible is a good idea.


> Versioning/release/deploy/rollback is dealt with mostly outside of git, or with tags-on-main at most.

This is simply outright wrong. Releases are not a tagged versions of a development branch. There is stuff that belongs in releases but should not be present ever in develop branches at all, and vice-versa.

Once you need specific changes going into a release, you need branches.

Also, you should not block development work on the development branch just because you need to push a release.

Also, if you find a bug in a release candidate, you fix that bug alone. You do not add other changes just because someone committed them to the development branch.

Cutting a release means picking up an unstable version and work your way up to make it stable. This means freezing the introduction of additional random features, and commit bugfixes only. Tagging does not allow that.


> Once you need specific changes going into a release, you need branches.

I don't have this need. Then I don't need to deal with the sheer insanity of loosely spreading all those different git refs you mention over everything including the future.

Changes get integrated fast into main, because nothing is blocked, frozen, or left for someone else in the future. This also makes commits/PRs/changes small. Feature flags are used to ease this even more.

In the face of absolute disaster it is possible to simply revert/rollback a release/deploy. However in practice I don't really need to do this, almost never. Some hotfix here and there maybe, but it's still all much faster than the sheer insanity of git-flow overcomplications. Often a hotfix is the only solution, a revert/rollback fixes nothing when the problem is outside of git (DBs, client storage...).

In the end, you have to evaluate all the time you sink into this model, and how much risk you actually mitigate. Wherever I've seen git-flow, the overhead was huge and ever-growing, and it never really helped with any risk mitigation. I would go as far as saying that overall it increased the risk, due to its complexity.


> There is stuff that belongs in releases but should not be present ever in develop branches at all, and vice-versa.

Divergence between development branches and release branches may be a sign of stuff that should be configuration-only/environment-configurable and outside of source control. "Build-once, run-everywhere" is a useful mantra: once a version has been cut, if it can be built once and only once and run in every development or release environment, you know that you are stably testing the same build artifacts in your QA environments that you will eventually release.

> Also, if you find a bug in a release candidate, you fix that bug alone. You do not add other changes just because someone committed them to the development branch.

If you are marking your released versions with git tags then you just build a short-lived branch from the release candidate tag, PR the fix to the short-lived branch (and any long term changes to the main/trunk branch), and cut the bug fix version from the short-lived branch (tag the new version). You don't need long-lived release branches to bug fix older versions and releases.


Thanks for this insight. This is useful advice for those who are re/discovering some of these practices for newer projects and would benefit from adopting a more saner approach.

I see a reference to GitHub Flow which seems to make sense: https://docs.github.com/en/get-started/quickstart/github-flo...


I agree. Seeing this again with the added “successful” attribute made me think: “successful at what?”

At influencing developers? Yes. At helping developers? I want to say no.

I don’t want to blame the author for this catching on, but I don’t think it was helpful that it did.


I've worked in an environment where they used git-flow, but it was very much a "traditional" (?) software company; they offered long term support, that is, backporting patches to older released versions, that kinda thing.

But they also had long lived branches that lived for like 9 months. It was still manageable because the feature was usually limited to one 'domain', and they usually had only one or two people working on the feature at once.


From your linked source:

>* main for the Git community since 2020 (master with unsavory connotations before)

That was fake news. There were no "unsavory connotations" before. I can safely discard anything that author states from then on.

EDIT: And like clockwork, the author has #BLM and pronouns in his profile.


Agreed, but your linked article on trunk-based development does include this strange idea:

> the core requirement of Continuous Integration that all team members commit to trunk at least once every 24 hours


That’s been the case since day one; it’s not something trunk-based development invented.

Remember, continuous integration is literally that – people integrate their changes continuously. It’s not just “we run Jenkins”.

https://martinfowler.com/articles/continuousIntegration.html...


The article suggests *GitHub Flow* not Git-Flow. Which from reading does just seem like trunk based development.

After a while, I think we all just end up there from exhaustion.


The article is the original git-flow article. It suggests GitHub Flow for projects with continuous deployment and no version numbers in a 2020 addendum, but otherwise, it's the overengineered git-flow.


> This was terrible at the time and it remains terrible now.

I strongly disagree. This branching model didn't came out of the blue. This branching model is a direct mapping between regular run-of-the-mill release processes for applications intended to be delivered and installed by the general public, and how Git implements branches.

Let's actually look at the git flow workflow branching model and see what it does. There are:

* a develop branch. Where commits/pull requests are continuously integrated as part of the everyday development work. Is this complex? No.

* feature branches. Where developers present their changes as part of pull requests prior to merging them onto the development branch. Is this complex? No. Software development platforms such as github and gitlab even support creating them automatically as part of the normal ticket creation workflow, and even support triggering dedicated pipeline workflows.

* Release branches. These are used to pick a specific state of the development branch to prepare it to be releasable to the public, and prevent it from receiving additional features. This involves things like bumping up version numbers, update changelogs and docs, flip up feature flags, and more importantly trigger pipeline workflows to generate production-ready installers. These installers are then subjected to build verification tests, manual tests, deployment tests, and Product managers use them to evaluate what users will receive. Is this complex? No.

* bugfix branches. What happens if a bug is found in a release candidate? You push a fix, of course. You push it directly into the release branch and afterwards create another release candidate version to be subjected to BVTs and manual tests. You also need to fix the bug in the development branch. Is this complex? No.

* The prod branch. Once your release candidates are finalized, you cut a release. This can mean tweaking version numbers and docs again, but the goal is to persist the exact version that was released to the public. Also, if an important bug is found in production, such as a crash, you're going to have to prepare a patch release shipping only a fix for it. This branch is used to fork a new release candidate, tweak stuff like version numbers, changelogs, docs, get the fix in, run regression tests, have Product Managers double-check that the bug is fixed, and release a new version. Is this complex? No.

I followed this exact branching model for all non-web projects I worked on for a timespan that goes over a decade.

Here's the fun part: I followed it before I even knew this page existed. Why? Because this is what you eventually end up converging to if you are a professional doing professional work with a professional team to deliver a production ready version of a product built by professionals.

Why is that? Because real world requirements emerge. You learn the hard way that you cannot pause continuous integration when preparing a release, and thus you need release candidate branches to not block other developers. You learn the hard way that you need to do additional work over a release candidate in order to get it ready for production. You learn the hard way that after you release a version of your app then eventually you will need to scramble to release another version to ship a critical bugfix. You learn the hard way that you need to work in multiple releases in parallel. You learn the hard way that sometimes a release is scrapped because the Product Manager changes their mind and instead of shipping a hotfix we ship additional features. Etc etc etc.

Now, there are things in this branching model that might not be ideal. For example, merging release branches back into a single prod/master/mainline branch is convenient as a way to ensure the stable release is front and center in a git repo, but it doesn't work if you need to have multiple releases for multiple versions. In the last few years the teams I worked on ended up not merging release branches anywhere, and just kept them dangling out of the development branch. This helps manage the complexity of a major release bump where you need to continue maintaining both the new and old release versions, and possibly even ship minor/patch versions of the old release.


> Because this is what you eventually end up converging to if you are a professional doing professional work with a professional team to deliver a production ready version of a product built by professionals.

This is a ridiculous thing to say. GitLab says it’s too complex. GitHub says that it’s too complex. Linus Torvalds doesn't use it. Both Google and Facebook use trunk-based development. You think none of these people are professionals?


> This is a ridiculous thing to say.

I understand that inexperienced people oblivious to basic requirements of a simple release process might have a hard time understanding why those requirements need to be addressed.

> GitLab says it’s too complex. GitHub says that it’s too complex. Linus Torvalds doesn't use it.

I recommend you think things through by yourself instead of holding on to meaningless appeals to authority.

For starters, you should read what the CEO of Gitlab actually said about Gitflow. The "complexity" examples boiled down to complaining about using rebase, which is really immaterial to the discussion and pointless nitpicking, as nothing forces anyone to rebase anything anywhere nor does it have a fundamental role in gitflow, and complaining that "there should be one main branch" is frankly a pointless remark. That's the full extent of the nitpicking.

Name-dropping Linus Torvalds to point out he doesn't use it is also a very silly attempt at an appeal to authority. I mean, the Pope himself doesn't use trunk-based development. Is that relevant?

Your silly attempt at an appeal to authority is outright absurd when you factor in the fact that the Linux release process is far more complex and convoluted than gitflow, involving cutting release branches by pulling cherry-picked changes from countless remote repositories which may or may not even track the same point in the branch.

https://www.kernel.org/doc/html/v4.15/process/2.Process.html

https://news.ycombinator.com/item?id=9744606

> Both Google and Facebook use trunk-based development.

Are you really sure? I know for a fact that you are not, and I call out your bullshit by stating that I know for a fact that some FANG teams use Gitflow, because I worked in them.

> You think none of these people are professionals?

I know that your baseless and silly appeals to authority show you are certainly not a professional with any degree of experience doing releases, and that's where your name drop attempts end.


> This branching model is a direct mapping between regular run-of-the-mill release processes for applications intended to be delivered and installed by the general public, and how Git implements branches.

Git-flow kinda works for only one model, in which you have a single release out to the world at all times. If you have multiple concurrent supported releases, like Windows 2000/XP/Vista, or Chrome stable/beta/dev/canary, this model does not work.

The `master` branch and short-lived release branches in git-flow are a complicated dance and a lot of merging without much clear benefit. Compare with a trunk-based model:

* new features go into main branch (from feature branches)

* when making a big release, create a branch like release/23.09, do whatever preparations and testing you need, and release 23.09.0 - tag the commit that the release is built from

* bugfixes go into main branch (from feature branches) and then are cherry-picked into release branches (hopefully via pull requests)

* if the bug exists in release/23.08 and release/23.09, we can fix both and release 23.08.x + 23.09.y simultaneously

This model is much easier to comprehend, has less branching, and allows maintenance for older versions.


Out of curiosity...you mention fixing multiple releases at once (release/23.08 and release/23.09).

in your example, are you keeping those older release branches around forever in git, or deleting them as soon as the release is made and the commit tagged? You would recreate something like release/23.08 from the tag, right?


After multiple attempts to use Git Flow at multiple different companies, I'm certain that it is untenable. No matter how competent your team is, Git Flow demands a level of consistency with such a complex process that empirically, your team will always end up making mistakes, trashing the commit tree, and calling the team lead in to move the commits around to where they were supposed to go.

We've had much more luck with trunk-based development, as other commenters have mentioned.


I don't get the hate for this at all. I wonder if it's because I've mostly mostly in corporate environments, with dedicated test/QA teams.

One of the big advantages of either fixed dev/test/prod branches or dev/versionN branches is that the testers get a stable codebase to test against, which can have bugfixes applied without bringing everything else from dev along for the ride. It's virtually impossible to regression test a release if it has constant changes being made to it.


Actually it was a poor qa/test relationship that destroyed git flow for me. Because the presence of release branches made the PO confident they could release any fixes or "emergency features" they wanted, they kept the QA team on the release branches while the dev team kept cranking through the backlog until it came time to validate like a year's worth of work and QA+PO threw up their hands and said it was too much to validate.

So now I don't merge a feature unless I know it's allowed to be released. If it's not releasable, it's not merging.


Regression should be covered mostly by automated tests. We have a huge suite of e2e tests covering everything from frontend, through microservices, dbs, kafkas, mobile apps, pipelines.

Over 8 years we added so much features that we would need to triple the QA teams every year, this simply doesn't scale well.

We have dedicated QA people in full stack teams, they test features that are going to get merged and help with writing the automated tests. Trunk based development.

We used to do it like you say. Non-stop issues with releases dragging, series of hotfixes, teams being torn between new features and firefighting something that should have been released already. Our velocity, quality and predictibility is soooo much better now.


We have a huge suite of e2e tests covering everything from frontend, through microservices, dbs, kafkas, mobile apps, pipelines.

It's easy to advocate for a process that isn't git flow (eg TBD) if you have that. The problem is how to get from an environment without extensive tests to an environment with those tests safely without constantly having to fight fires everywhere. Saying something like "your git process is bad" or "feature branches are a waste of time" requires context. If you're in a situation where you need the 'safety blanket' of branches because everything is effectively untested then you have to fix that problem first.

TBD works well if you have a robust, trustworthy environment. If you don't then it'd make things even worse.


That's quite interesting. We also have a huge suite of API integration tests, plus a lot of UI automation which does most of our regression. But we also have clients who are high profile enough that it's simply not feasible to push changes out without a human sanity-checking them first.

We also have a fairly complex, interconnected domain and the system reflects that. I love the idea of having manual QA on feature branches being enough, but I'm not sure it's feasible for us.


I used to use something like git flow, but the long-lived dev branch proved to be a real problem for QA because it changes constantly which led to tester confusion and wasted time.

What I do now is keep master always release-ready and work is done in feature branches. The feature branches each automatially get their own (numerous) builds for testing, and internal QA is done against those. QA can be held up for weeks or months, so doing it this way keeps unfinished code out of releases and allows the business to change their priorities to shelve stuff that's suddenly not as urgent as it was last week - and also to suddenly do new surprise submissions/releases the day after randomly promising them in a phone call.

If two feature branches have overlapping changes and it's taking people months to approve them then the merges are annoying, but usually sorted out in the lifetime of a single cup of tea.

Release builds are done off tags, usually on master. Hotfixes to old releases (when there's no QA resource available to test a big update) can be done in a low-impact way by branching ("zombie/customer-platform-x.y.z") off the tag for that release, cherry-picking onto it and adding a release tag.

Customers get demo/QA builds, also done off tags so when they get around to reporting an issue weeks later we know what code they've been looking at.

(For context, the software is a client-side white label thing with different builds targeting multiple platforms - so there's a customer/platform matrix meaning many different releases, and almost every release has to be a manual submission each involving different 3rd-party QA which takes 2-6 weeks (best case, with no issues found). Config + content is dynamic, no two customers use the same subset of functionality. Keeping everything current is impossible.)


Separate release branches do make a lot of sense and allow making only bugfixes to already released versions. The develop/master separation suggested by git-flow has no benefits and only makes a bigger mess. Trunk-based development also involves release branches, but is much easier: https://trunkbaseddevelopment.com/


I know there's been a lot of gripes against git-flow but I think one of the reasons it had such an impact is that project maintainers/leaders are looking for some sort of "workflow" or process around git.

As a project grows, you want some process around how to name branches, when to use tags, how to handle merges, from which branch to base your work on, etc.

Git flow gave all the answers in an authoritative way. You could just say to junior team members/contributors that the project uses git-flow and send them the link to the doc.

I applied git-flow to my projects but simplified the process, eventually abandoning almost all its principles. It did give me a starting point to think about what we really needed, what is important and how to communicate it with a team.

One particular process I am quite fond of is to use the develop branch as a free for all integration/testing branch with automated deployment to a test (dev) environment with absolutely no guarantee of working whatsoever to any stake holder. This allows devs to demo their work, collaborate and spot conflicts early, without being slowed down by fear of breaking something or waiting for a code review.

Unlike git-flow, that develop branch is a dead end. It does not get merged to master or any other branch ever and gets force-push (reset) when things go wrong.

Feature branches eventually get proper code review, their history can be cleaned up and eventually merged (or dropped) via a proper pull request.


The concept of a “develop” branch here isn’t really a requirement to have a branch deploy. Most CI I’ve used let you pick any tag/commit/branch to run on - so nothing stopping you just deploying your feature branch without having a special branch for it.

The trunk (i.e main/master) is the only branch you should really be collaborating on, otherwise you’re going to end up with painful, long-live branches with that are messy to integrate.


There is no one-size fits all branching model for git. But they are extremely important. Some people swear by gitflow and others don't. I wish there was a set of tried ones though, with some simple questions to help decide. I can think of two questions:

First ask whether you need to be supporting multiple versions in production or not. A simple web service with single tenant model would do OK with just one maintained version. There is always a "current" latest production release and whether that branch it's built from is called "production" or "master" doesn't really matter. You have a stable main, and then unstable branches are develop and feature. But if you must support multiple production versions longer term, as for any software that is installed by clients (Desktop, self hosted web backends, ...) then of course having a single "production" branch no longer flies. You need to have long lived release branches, that might even diverge and never merge back to any long term development branch. These could be called "release/v1.0" or similar. And obviously if you have designated release branches then there is no point having an unstable develop branch - you can just make main your unstable develop branch.

Second after deciding between maintaining 1 or N active versions in production is deciding whether you can do continuous deployment. If you can deploy to production multiple times per day, that might affect your branching model. If on the other hand you have fixed waterfall-like deadlines because e.g. you only have a chance to get regulatory review for the new version (if you make security critical software e.g. medical, automotive) twice per year then that might have an effect on your branching model, you'd then use your release/v1.0 branches before the release as a long term stabilization before the release.


Highly recommend going with trunk-based deployment these days. I think git flow just made sense to people from older VCS's and didn't know they could just not have a prod branch. With a medium or larger team that wants to release early and often, git flow will actually slow you all down, I don't recommend it.


My experience was that older VCS's (SVN in my case) leaned in heavily to the trunk based paradigm. I don't know what you're talking about with prod branches. The default branch in SVN was called - trunk.


I think that's what they're saying. People think that when they switch to git they have to use prod and other, newer workflows. But you can keep doing the same thing you did in SVN, using trunk primarily in git, with easier merges.


We came pretty close to using this at one point. Thankfully I was able to propose a similar sounding but very different approach called "GitHub flow" (aka normal development). Quite a bullet dodge imo.


This one blog post resulted in so much pain for so many. I'm glad it has been addended, but the damage was done.

For the record, I don't think it's the authors fault.


Related:

A note of reflection after 10 years of “A successful Git branching model” - https://news.ycombinator.com/item?id=22496724 - March 2020 (100 comments)

A successful Git branching model (2010) - https://news.ycombinator.com/item?id=15376841 - Oct 2017 (120 comments)

What is wrong with “A successful Git branching model”? - https://news.ycombinator.com/item?id=11190310 - Feb 2016 (120 comments)

What is wrong with “A successful Git branching model”? - https://news.ycombinator.com/item?id=11059485 - Feb 2016 (4 comments)

A simple git branching model - https://news.ycombinator.com/item?id=6456193 - Sept 2013 (157 comments)

A successful Git branching model (2010) - https://news.ycombinator.com/item?id=6437983 - Sept 2013 (15 comments)

A successful Git branching model - https://news.ycombinator.com/item?id=5928979 - June 2013 (3 comments)

A successful Git branching model - https://news.ycombinator.com/item?id=1966820 - Dec 2010 (11 comments)

Tutorial: How to use Git in a team development environment - https://news.ycombinator.com/item?id=1826279 - Oct 2010 (26 comments)

A successful Git branching model - https://news.ycombinator.com/item?id=1063198 - Jan 2010 (44 comments)


Many jr devs cite this model to me. Every new hire after on boarding looking to make an impact cites this. And I’m getting tired of explaining why this model is not always a good idea.

And we’re building apps that have multiple versions in the wild.


I think one major shortcoming of git-flow is that semantically commits belong to a specific branch, but it conflicts with git's branching model, which are just moving labels.

Git flow ironically probably works better on mercurial branches, where changesets (commits) belong to specific branches permanently.

Also I think it's hard to implement complicated git branching strategies when most developers like to navigate the history by visualizing the graph of commits by GUI tools or just by git log. It doesn't help that git log doesn't implement a powerful query language over commits, but most developers are not aware of the simpler but powerful queries either that are available.


Anyone have a good resource on monorepo design and workflow? I find myself keeping supporting projects up to date in their own branch and then cherrypicking commits from random other projects' dev branches, where random commits happen to improve a non-focused branch. But the commit history in my main dev branch gets muddied with these random merge commits (merging changes that have improved the secondary projects).

In other words, merge commits aren't necessarily indicative of critical chronological moments. But they muddy up a nice chronology of development commits.

I could use some ideas on the architecture of monorepos...


In our branching model we have a develop branch for $current and version specific branches for LTS (2-4). Both get fed via PRs. Pretty okay complexity-wise.

Our issue is more with the JIRA issue logistics to get everything into the correct branches. We can't use multiple fix versions per issue, so we clone the issue for all branches that it is needed for. That is cumbersome and also error prone since sometimes creating clones gets forgotten. Also the tickets can diverge if people are careless and comment one of the clone issues instead of the main one.


I honestly don't understand the hate that Git Flow gets these days. We've been using it in our little client-services agency (projects of 2 to 8 developers with infrequent deployments) for the last couple of years and never had any problem with it. It's easy to explain and automate and mostly just works without getting in the way.


Upvoted for the retraction.

Personal opinions on best practices:

- If you have multiple components to an application, whether frontend/backend or microservices, put them all in the same repository by default. It is so easy to split them apart again after your team grows to the point where it matters, but before that point you will need to carefully coordinate merging PRs to multiple repos, and sometimes rolling them back, and it is so easy with a small application monorepo. You can just store git’s “tree hashes” in s3 and check if they changed when deciding whether to build and deploy, it's not hard.

- If you're versioning, a version.txt file at the root of the versioned content is your friend. Have a {subproject_name}-v1.6 branch, you tag commits on that branch v1.6.3-rc1, rc2, ... automatically with CI/CD, until you tag one RC as a release when you release it.

- Everyone merges into main, you cherrypick from main out to those other branches. This keeps the graph clean. When you cut v1.6 you decide whether your next project on main is v2.0 or v1.7.

- Feature toggles are your friend, see Rod Hilton’s Three-Flow[1]. Just clean them up when you release!

- with about a day's involvement in CI/CD, the `main` branch can have a `k8s/` folder with subfolders for each of your deployment environments. You want to deploy something to prod, just update a version number in `k8s/prod` and merge to `main`. This is even a little simpler than Three-Flow and you can very precisely answer when something was shipped.

- folders `rfc/` and `docs/` and `issues/` at the root level would be amazing but I can't get any company to actually do them. One stop shopping! Why not? Once `main` is authoritatively central, it becomes a great place for these things, just the same as you use it for `k8s/prod` and `k8s/dev`.

- The underlying point of all of this is to make sure that everybody merges their code every day. If that would break something, put the breakage behind a feature toggle. If it's a config file that doesn't have feature toggles, run the C preprocessor on it or invest in Dhall or some other hack. Do whatever you can so that there is one big conversation happening, and everybody is collaborating. The worst is when you have these huge merge conflicts, the second worst is when you have been doing a lot of work and someone else sweeps it all away before you merge, "oh I didn't see you working on that so I solved it in this inferior hacky way."

1. https://www.rodhilton.com/2017/04/09/a-different-branching-s...


This is almost as bad as the original article...

Just deploy any change to main... don't worry about version numbers unless it matters, even then, just version your spec, not your application. If it does matter, deploy all changes to your QA environment. Once you're happy (at the end of a sprint or whatever), tag a commit and release that to production. Need a hotfix? Branch off of your tag, do the thing, and tag that commit and merge it to main.

There's no reason for "version" files. you can pull your previous version image and compare the SHA hashes for your new version. If they're different, then deploy the new one (heh, maybe you got some security patches from the base image). Otherwise, no reason to deploy -- or if you have zero-downtime deployments, just deploy it anyway and don't worry about comparing hashes or whether or not something changed.


Any release I have made whether it's to a package manager or an app store or a windows app etc has had to have not just versions for the product but also some times exact versions for individual files (e.g. Traditional Windows installer apps where screwing up the versioning on even one file is a huge mistake that might prevent the next deployment from working toe to downgrade rules.)

I have spent a lot more time in my life on thinking about "versioning schemes" than I have "branching schemes" due to this. E.g. windows files and apps don't have SemVer, just a 4 component version, so you really need to think long and hard about the kind of thing whether 1.0-dev should be 1.0.10 or 1.0.1 in order to sort properly over your 1.0-final and so on.

A world where you can just "deploy an image" and know it just replaces your previous one and in general one where you can deploy yourself, to machines you control sounds nice compared to making packages which should work at 10k different users and follow the whims of various stores, package managers and installer systems.


It's still not any different. Just tag your release in git. No need for versions files. 1.0 matches 1.0 matches 1.0 of all the components. Want to know what version 1.1 is compatible with? You guessed it, 1.1!


> If you're versioning, a version.txt file at the root of the versioned content is your friend. Have a {subproject_name}-v1.6 branch, you tag commits on that branch v1.6.3-rc1, rc2, ... automatically with CI/CD, until you tag one RC as a release when you release it.

You can save a bunch of text editing and merge conflicts with `git describe` [0]. git describe at any commit will give you a CI/CD version number in the general format of `{most-recent-tag}-{commit count since}-g{hash abbreviation of current commit}`. If the current commit corresponds with the most recent tag, git describe doesn't show the other parts. The `--dirty` flag to git describe will optionally also add a `-dirty` to the end of the version output if the repo has uncommitted changes.

You can pipe `git describe` into a (non-source-controlled) VERSION.TXT file (or a VERSION.JSON makes a simple API for web services) as a build artifact in your CI system. Just about any git command that takes a "commitish" will accept the git describe version format and you can generally just copy and paste the contents of your VERSION.TXT artifact, such as: git switch v1.6.2-11-g1abcde

If you've got multiple subprojects independently versioned in the same repo it gets a bit trickier but if your naming scheme is consistent you can use the `--match` flag to git describe to give a tag pattern. `git describe --match v*` for main tags that start with v versus `git describe --match subproject-name/v*` for a subproject. (Aside: using a forward slash in your subproject tags creates a nice folder structure in many UIs, as with slashes in branch names.)

In early development before you cut a first tag you might want the `--always` flag which will fallback to just the `g{current commit hash}` (and optional dirty with that flag).

The only other twist to git describe is that it defaults to only using annotated tags and doesn't include "lightweight" tags and not everyone or every UI is in the habit of `git tag -a` to create the annotated commit. It can be sometimes useful to have that distinction between annotated and lightweight so you just need to learn to annotate the tags that you care to show up in your git describe versions. Or you can use the `--all` flag to git describe and include lightweight tags for consideration in version numbers as well.

I've used git describe consistently in a number of CI builds for years and it is great. The one small criticism I have of it is that many times for proper semver compliance you really want the first `-` to be a plus (`v1.6.2+11-g1abcde`) as these are generally post-version indicators not pre-version indicators, but that's easy enough to swap with a possibly simple find/replace in the shell script or scripting language of your choice (simplicity depending in part on whether or not you use pre-versions in your tags like v1.6.2-rc.1 which would yield from git describe something like v1.6.2-rc.1-11-g1abcde). There are some off the shelf libraries [1] for that, too.

[0] https://git-scm.com/docs/git-describe

[1] https://www.npmjs.com/package/git-describe (namely the "semverString" output)


we're using this model - quite successfully - i'd say. our releases are infrequent and huge and release prep may take months (due to things like security audits by external reviewers). i don't see how we could do this with a simpler trunk-based model without blocking development for weeks.


I am using git flow for a project that is around since 2012, I'm pretty much the single developer on said project. gitflow it keeps everything very orderly and I had no issues. Whenever I make a release I have to look up the workflow but that only takes a minute.

Due to the criticism that I came across over the past few years it's unlikely I would use it for a new project though.


git branching used to be a real pain in the ass for me, because once I started getting a steady stream of new ideas built, the whole system got so complicated that I couldn't find the version I wanted.


New to this and learning, thanks for sharing


Tis a little disconcerting to see master called master. I thought we'd all changed that?


I go out of my way to change the default tech companies try to push on you, back to 'master'. I was migrating a team at work from SVN to git and the project lead was telling me the trunk branch should be called 'main' because that's the standard. I explained this is American nonsense and git has always used master, but because the Google algorithm wouldn't find any evidence except articles from the looney American left, I ended up having to show him on the CLI (which of course he never used before) in a bare new project to prove it.

Thankfully our hatred of American cultural imperialism united us and he agreed on using master. Three cheers for Ramirez!


The default branch name in Git is still master. Regardless, this is an article originally published in 2010.




Consider applying for YC's Spring batch! Applications are open till Feb 11.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: