Git Good
Git Good
Git Good
Branch Like It's Free
S1 E720m · Apr 05, 2026
Git's revolutionary branching made branches a forty-one-byte pointer instead of an expensive server operation, transforming how teams collaborate and igniting decades of debate about the best workflow.

Branch Like It's Free

Five Hundred Megabytes of Copies

Picture yourself in two thousand six. You are working on a large Java application at a company with maybe sixty developers. The codebase lives in a Subversion repository. Five hundred megabytes of source code, build artifacts, configuration files. You have been asked to prototype a new feature, something experimental, something that might not ship. Your team lead says, "just create a branch."

So you open your terminal and type svn copy, pointing the server at the trunk and naming a new directory for your branch. And then you wait. Subversion does what Subversion does: it copies the entire project tree on the server. Every file. Every directory. The server churns through five hundred megabytes of metadata, writing a new revision, updating the repository structure. Depending on the server hardware and network conditions, this takes anywhere from a few seconds to a few minutes for a truly large repository.

But that is only the beginning. Now you need to switch your working copy to this new branch, which means another round-trip to the server. And when your experiment is done, two weeks from now, you will need to merge it back. Subversion's merge tracking was notoriously incomplete before version one point five, released in two thousand eight. Before that, merging a branch back to trunk meant manually tracking which revisions you had already merged and which you had not. Get it wrong, and you would apply changes twice. Get it really wrong, and you would spend a day untangling the mess.

The result was predictable. Teams avoided branching. They worked directly on trunk, stepping on each other's code, breaking builds, shipping half-finished features because nobody wanted to pay the cost of creating and merging a branch. Branching was theoretically possible. In practice, it was expensive enough that most teams treated it like elective surgery: technically available, but you had better have a very good reason.

Now imagine a different world. You type two words and a name. Git creates your branch in microseconds. Not seconds. Microseconds. It does not matter if your repository is five hundred megabytes or five gigabytes. The operation writes forty-one bytes to a single file. That is it. Your branch exists. You can switch to it instantly. You can delete it instantly. You can have fifty branches and the performance cost is essentially zero.

That difference, between branching as a heavyweight server operation and branching as a line written to a file, changed software development more profoundly than any other feature Git introduced. And the story of what people built on top of that difference is the story of how modern software teams actually work.

A Pointer, Not a Copy

The first time a developer runs git branch on a large repository, they usually run it again. Not because it failed, but because it felt like it did. Nothing happened. No progress bar. No server acknowledgment. No pause at all. They check, and the branch is just there. They expected a process. They got an instant.

Last episode, we talked about how Git stores everything as snapshots. Every commit is a frozen picture of your entire project at one moment. Commits point to previous commits, forming a chain that stretches back to the very first one. This chain is your history.

A branch, in Git, is breathtakingly simple. It is a file. That file contains one thing: the forty-character fingerprint of a commit. That is the entire implementation. When you run git branch followed by a name, Git creates a text file in a hidden directory, writes one line to it, and you are done.

When you make a new commit on that branch, Git updates the file to point to the new commit. The branch moves forward. That is all "being on a branch" means in Git. You are working, and a small pointer file follows along, always pointing at your latest commit. Linus himself described it with characteristic bluntness.

In Git, a branch is nothing more than a pointer to a single commit. There is no expensive overhead. It costs nothing.

Compare this to Subversion. In Subversion, a branch was a directory copy on the server. It had real weight. The server had to process it. The network had to transfer it. Your working copy had to know about it. It was a thing that existed in the world, taking up space and time.

In Git, a branch is a bookmark. A sticky note on a page. You can have a thousand bookmarks in a book and the book weighs exactly the same. You can move them around, remove them, add new ones. The content does not change. Only the pointers change.

This is the direct consequence of the design decisions Linus made in those two weeks in April two thousand five. Content-addressing means every snapshot already exists as an immutable object in the database. A branch does not need to copy anything because there is nothing to copy. It just needs to say, "I am pointing at this particular snapshot." Forty-one bytes. Done.

The command git switch, which replaced the confusingly overloaded git checkout for branch-switching duties, does something equally lightweight. It updates which branch pointer Git considers "current," then adjusts the files in your working directory to match the commit that branch points to. Fast. Quiet. No network. No server. Just your local disk reshuffling files.

And git branch with the dash d flag? It deletes the pointer file. The commits that branch pointed to do not disappear. They are still in the repository, still reachable through other branches or through the reflog. You are just removing the label. Like peeling a sticky note off a page without tearing the page out.

The Blog Post That Launched a Thousand Arguments

For the first few years of Git's life, people branched however they wanted. There were no widely agreed-upon conventions. Some teams created a branch for every feature. Some teams worked directly on the main branch. Some teams had elaborate hierarchies of branches that only the lead developer understood. Git gave you the tools and then stepped back completely. You figured out the social conventions yourself.

Then, on January fifth, two thousand ten, a Dutch software developer named Vincent Driessen published a blog post on his personal site called "A successful Git branching model." The post included a diagram. A tall, colorful diagram showing branches flowing in parallel like river tributaries, splitting off and merging back in a carefully choreographed dance. Feature branches splitting from develop. Release branches splitting from develop. Hotfix branches splitting from the main branch. Everything flowing in specific directions, merging at specific points, following specific rules.

That diagram went everywhere. It was printed out and taped to walls in offices around the world. It was included in onboarding documents. It was cited in job interviews. It became, for millions of developers, the answer to the question, "how should we use branches?"

Driessen was not a famous developer. He was not working at Google or Facebook or any company whose engineering blog would have carried automatic authority. He was an independent software engineer in the Netherlands who had been using this branching model on his own projects for about a year and decided to write it up. The post was clear, well-illustrated, and it arrived at exactly the right moment. Teams were adopting Git in droves but had no shared vocabulary for how to organize their branches. Driessen gave them one.

He also built a set of command-line extensions called git-flow that automated the model. Instead of remembering which branch to split from and which branch to merge back to, you typed git flow feature start and the tool did it for you. This was crucial. The blog post told you the theory. The tool enforced the practice.

Within a year, Git Flow had become the default branching model for a significant portion of the industry. Not because anyone mandated it. Not because Git recommended it. Because one person wrote a clear blog post with a good diagram at the right time, and it spread through word of mouth and shared frustration. The developer community was hungry for structure, and Driessen fed them.

Git Flow and the Church of Structure

The model Driessen described had a pleasing symmetry to it. Two permanent branches: the main branch, which always reflected the production-ready state of the code, and the develop branch, which served as an integration point for features. Three types of temporary branches: feature branches for new work, release branches for preparing a release, and hotfix branches for urgent production fixes.

The rules were specific. Feature branches split from develop and merge back to develop. Release branches split from develop, get polished and tested, then merge into both main and develop. Hotfix branches split from main, fix the urgent problem, then merge into both main and develop. Nothing goes directly to main except through a release or a hotfix. The develop branch is where the daily work accumulates.

For teams that shipped software on a schedule, maybe every two weeks, maybe every quarter, this model was a revelation. It gave you a place for everything. Features being developed? Feature branches. Getting ready for a release? Release branch. Something broke in production? Hotfix branch. The ceremony was real, but for software with version numbers and release cycles and customers running old versions, the ceremony matched the complexity of the problem. Driessen himself eventually acknowledged the limits.

If your team is doing continuous delivery of software, I would suggest to adopt a much simpler workflow instead of trying to shoehorn git-flow into your team.

That quote is not from two thousand ten. It is from a reflection Driessen added to the top of his original post a decade later, in March two thousand twenty. By that point, the world had changed. Most web applications did not ship versioned releases. They deployed continuously, sometimes dozens of times a day. And for those teams, Git Flow's ceremony was not a feature. It was overhead.

But the original blog post did not have that caveat. And for the better part of a decade, Git Flow became something close to dogma. Teams adopted it wholesale without asking whether it fit their situation. Interviewers tested candidates on it. Code review checklists referenced it. It was the branching model, singular, as if Driessen had discovered a natural law rather than proposed one workflow among many.

The irony is that Driessen himself always understood the model had limits. The blog post was titled "a successful Git branching model," with the emphasis on the indefinite article. A model. Not the model. But titles get lost when ideas spread, and what spread was the diagram, the tool, and the assumption that this was how professional teams used Git.

GitHub Flow and the Gospel of Simplicity

While Git Flow was conquering corporate development teams, a different philosophy was taking shape a few hundred miles south of the Netherlands, metaphorically speaking, inside GitHub's own engineering team.

By two thousand eleven, GitHub had about thirty-five employees and was deploying to production multiple times a day. They tried Git Flow. It did not fit. The problem was fundamental: Git Flow was designed for software that ships in releases. GitHub was a web application that deployed constantly. There were no version numbers. There was no "release branch" because every merge to the main branch was a release.

On August thirty-first, two thousand eleven, Scott Chacon, one of GitHub's early employees and the author of the Pro Git book, published his own blog post. He called the workflow "GitHub Flow" and it was almost aggressively simple compared to Git Flow.

The rules fit on one hand. The main branch is always deployable. When you want to work on something, create a descriptively named branch from main. Commit to that branch and push it regularly. When you want feedback, open a pull request. After someone reviews and approves it, merge to main. Deploy immediately.

That is it. No develop branch. No release branches. No hotfix branches. One permanent branch. Short-lived feature branches. Pull requests as the quality gate. Deploy on merge.

The main issue is that we deploy all the time. We deploy to production every day, often several times a day. If you deploy every few hours, it is almost impossible to introduce large numbers of big bugs.

Chacon's argument was not that Git Flow was wrong. It was that Git Flow was designed for a different context. If you ship packaged software with version numbers, Git Flow makes sense. If you deploy a web application fifty times a week, Git Flow is ceremony without purpose. The release branch exists to stabilize code before shipping. If you are always shipping, there is nothing to stabilize.

Its simplicity gives it a number of advantages. One is that it is easy for people to understand, which means they can pick it up quickly and they rarely if ever mess it up or have to undo steps they did wrong.

GitHub Flow spread as fast as Git Flow had, maybe faster, because GitHub itself was the platform where the model was practiced. Every open source project on GitHub was, by default, using something close to GitHub Flow. Fork the repository. Create a branch. Open a pull request. Get it reviewed. Merge. The platform's design encoded the workflow. You did not need to read a blog post to learn it. You absorbed it by participating.

But GitHub Flow had its own assumptions. It assumed you could deploy at any time. It assumed the main branch was always in a working state. It assumed you had automated tests and continuous integration to catch problems before merge. For a thirty-five-person startup with a sophisticated deployment pipeline, these assumptions held. For a team of two hundred building a banking application that ships quarterly, they might not.

The Radicals Who Abolished Branches

While Driessen and Chacon were arguing about the right number of long-lived branches, a third camp was arguing that the answer was approximately zero.

Trunk-based development predates Git entirely. The idea is old, going back to the earliest days of continuous integration in the late nineteen nineties. The core principle is radical in its simplicity: everyone commits to the main branch. Directly. All the time. No feature branches that live for weeks. No integration branches. No branch hierarchy. Just one line of history, and everyone contributing to it.

This sounds like chaos. It sounds like the bad old days of CVS where everyone committed to trunk because branching was too painful. But trunk-based development is not an absence of process. It is a highly disciplined process that replaces branching with other techniques.

Paul Hammant, a British software consultant who has spent years advocating for the practice and literally wrote the book on it, describes the philosophy bluntly.

Branches create distance between developers and we do not want that.

The technique that makes it work is called feature flags. Instead of developing a new feature on a branch and merging it when it is done, you develop it directly in the main codebase but hide it behind a toggle. The code is there, in production, but invisible to users until the flag is switched on. When the feature is ready, you flip the flag. No merge. No integration headaches. No branch that has drifted so far from the main line that bringing it back is a three-day project.

Google has operated this way from the beginning. Their entire codebase, billions of lines of code, lives in a single repository with a single main branch. Developers commit to that branch tens of thousands of times per day. The infrastructure to support this is extraordinary: pre-submit testing, automated code review, build systems that can retest the entire codebase in minutes. But the principle is simple. One branch. Everyone commits to it. Always.

Facebook followed a similar path, though they chose Mercurial over Git and built their own infrastructure to make it scale. The philosophy was the same: short-lived branches measured in hours, not days. Commit to the trunk constantly. Use feature flags for work in progress. Deploy from the trunk because that is the only branch that matters.

The trunk believers look at Git Flow's five branch types and see unnecessary complexity. They look at GitHub Flow's pull request ceremony and see a bottleneck. Their argument is that if your tests are good enough and your deployments are fast enough, you do not need branches at all. Branches are a workaround for lack of confidence. If you can trust your main branch is always working, why would you work anywhere else?

The counterargument is equally strong. Not every team has Google's infrastructure. Not every codebase has comprehensive test coverage. Not every organization can deploy on demand. For a team of eight building a mobile app that ships to the App Store every two weeks, trunk-based development with feature flags might be overkill. A simple feature branch workflow gives them isolation without requiring a sophisticated flag management system.

When Branching is Free, Fear Goes Away

Here is what connects all three philosophies. None of them could exist without cheap branching.

Git Flow needs feature branches, release branches, and hotfix branches. That is a lot of branches. In Subversion, the overhead of creating and managing that many branches would have been prohibitive. Git Flow is only practical because Git made branching free.

GitHub Flow needs developers to create branches constantly, for every bug fix, every small feature, every experiment. If branching cost anything, the friction would kill the workflow. Developers would batch changes to avoid creating branches, which defeats the entire purpose.

Even trunk-based development, the approach that uses the fewest branches, still relies on short-lived branches for code review. Google's workflow has developers working on brief local branches that last hours before being merged. The branches are so cheap they barely register.

Before Git, teams avoided branching because it was expensive. They worked directly on the main line because the cost of branching was higher than the cost of stepping on each other's code. They accepted merge conflicts, broken builds, and half-finished features as the price of collaboration.

Git made that cost disappear. And when the cost disappeared, people discovered that branching was not just a feature of version control. It was a way of thinking. A branch is a safe space to try something without consequences. A branch is permission to experiment. A branch is the ability to say "what if" without risking "what is."

This is the thing Linus understood intuitively when he made branches cheap pointers instead of expensive copies. He was solving a technical problem, making the Linux kernel workflow efficient. But the consequence was cultural. When you remove the cost of trying something new, people try more new things. When failure is free, people take risks they would never take if failure cost them a day of merge conflict resolution.

The three workflows, Git Flow, GitHub Flow, and trunk-based development, are not competing answers to the same question. They are answers to different questions. How much structure does your team need? How often do you deploy? How much do you trust your test suite? How large is your team? How complex is your release process?

Git does not answer these questions. Git gives you branching as a primitive, a nearly free operation with no overhead, and lets you build whatever workflow your team needs on top of it. The tool does not prescribe the process. That is a social and organizational choice, not a technical one.

And the debates about "the right branching model" that have raged for fifteen years, the conference talks, the blog posts, the pull request comments that turn into philosophical arguments? Those are not really about Git. They are about how teams should work together. About how much structure is enough and how much is too much. About trust, autonomy, discipline, and the tradeoffs between moving fast and staying safe.

That a single design decision, making a branch a forty-one-byte pointer file instead of a server-side copy, could generate fifteen years of debate about organizational culture and team dynamics is a testament to something Linus probably did not think about on that Sunday in April two thousand five. Good design has emergent properties. You build a feature to solve one problem, and it unlocks possibilities nobody anticipated. Cheap branching was designed to make kernel development faster. It ended up changing how the entire industry thinks about collaboration.

Next episode, we find out what happens when all those branches need to come back together. Because branching is the easy part. The hard part is the merge.

git switch. Two words that replaced a small identity crisis. Before it existed, git checkout handled branch switching, file restoration, and half a dozen other things that had nothing to do with each other. Developers memorized which combination of flags did which job. git switch does one thing: it walks you onto the branch you named. That is all. In a tool famous for doing too much with too few commands, git switch is the rare moment of clarity.