A branch is the feature that made Git famous. It lets you split off a private line of development, experiment freely without endangering your working code, and merge the result back when it's ready. This article builds the idea up from scratch โ what a branch actually is under the hood, how Git tracks where you are, the handful of commands you need day to day, and a complete workflow from creating a branch to merging it.
What Is a Branch, and Why Does It Exist?
Imagine you have a working, shippable app. Now you want to add a risky new feature. If you edit your only copy of the code directly, you risk breaking something that already works โ and if the feature takes a week, your project sits in a half-finished, unshippable state the whole time.
A branch solves this. It gives you an isolated line of development where you can build, experiment, and commit freely without touching the known-good version of your code. If the experiment fails, you throw the branch away and your main code is untouched. If it succeeds, you merge it back in.
Think of your main branch as the "sacred timeline." When you create a branch, you spin off a parallel universe that starts identical to main but can diverge however you like. Anything you do in that universe โ write code, make mistakes, commit โ has no effect on the others. If the experiment works out, you bring that timeline's changes back into main. If it doesn't, you delete the universe and nobody ever knows it existed. The one catch: juggle too many parallel universes at once and you'll struggle to keep track of them all.
Here's what makes Git special: branching is its killer feature. In some older version control systems โ Subversion, for example โ creating a branch meant physically copying the entire source tree, a slow and heavyweight operation reserved for occasional large efforts. Git branches are radically lighter. Creating one, switching between them, and merging them back is so fast and cheap that Git actively encourages you to branch constantly โ often several times a day. To understand why it's so cheap, we need to look at what a branch actually is.
How Git Stores Branches: Commits, Pointers, and the DAG
Every time you commit, Git stores a commit object: a snapshot of your files, some metadata (author, date, message), and a pointer back to its parent โ the commit that came before it. Each commit links to the one before it, forming a chain. Branches that split and rejoin turn that chain into a structure Git calls a DAG (Directed Acyclic Graph): "directed" because every commit points back to its parent, "acyclic" because you can never loop back on yourself.
Hold on to this one sentence, because everything else follows from it: commits never move โ only pointers move.
So what is a branch? A branch is just a lightweight, movable pointer to one commit. That's the whole story. When you create a branch, Git doesn't copy a single file โ it writes a tiny text file under .git/refs/heads/ containing the 40-character hash of the commit it points at, plus a newline. That's 41 bytes. Creating a branch is, in the words of the Pro Git book, "as quick and simple as writing 41 bytes to a file."
main is simply a small pointer that names the latest commit on that line.That's the secret behind Git's speed: creating a branch takes milliseconds and costs essentially nothing, because there are no files to copy โ just a tiny reference to write.
HEAD: How Git Knows Where You Are
If branches are pointers to commits, one question remains: when you have several branches, how does Git know which one you're currently working on? The answer is a special pointer called HEAD.
The simplest way to understand HEAD is this: HEAD is the "you are here" marker. It almost always points at a branch โ and that branch points at a commit. So there's a little chain of pointers:
You don't have to take this on faith โ you can see it directly. HEAD is literally a small file inside the .git folder. Let's open it and follow the chain of pointers by hand:
Now here is the elegant part โ the reason this design is so clever. When you make a new commit, the branch HEAD points to moves forward automatically, and HEAD comes along for the ride. You never have to update HEAD by hand. Let's watch it happen:
And when you switch branches, Git simply re-points HEAD at the other branch and updates the files in your working directory to match that branch's snapshot. The commits don't move; only HEAD does. Once you internalize "HEAD โ branch โ commit, and only pointers move," almost every confusing thing about Git branches becomes obvious.
Detached HEAD: When You Step Off the Path
There's one situation where HEAD behaves differently, and it alarms a lot of beginners the first time they see it: the detached HEAD.
Remember the normal chain: HEAD โ branch โ commit. A detached HEAD is what happens when you cut out the middle link, so HEAD points directly at a commit with no branch in between:
This happens whenever you check out something that isn't a branch โ a specific commit by its hash, or a tag. Git lets you do it (it's useful for looking at old versions of your code), but it puts up a clear warning, because it changes the rules.
Let's trigger it on purpose so the warning isn't a surprise the first time you meet it in the wild:
So why the alarm? Because any commits you make in detached HEAD don't belong to a branch. Recall that branches are how Git keeps commits "anchored." With no branch pointing at them, those commits are floating โ and once you switch away, there's nothing pointing at them anymore, so Git may eventually clean them up and delete them for good.
The fix is simple, and it's right there in the warning:
- If you were just looking around (reading old code, running an old version), do nothing special โ just
git switch mainto reattach to a real branch. - If you made commits you want to keep, create a branch before you leave, with
git switch -c <new-branch>. That plants a permanent pointer on your new commits, anchoring them safely.
The Essential Branch Commands
With the mental model in place, the commands are easy. There are really only two tools you need every day: git branch to manage branches, and git switch to move between them.
git switch โ moving between branches
For years, the tool for switching branches was git checkout. The problem was that git checkout did far too many unrelated things โ switching branches, restoring individual files, detaching HEAD โ which made it a frequent source of beginner confusion. So in August 2019, Git 2.23 introduced two focused replacements: git switch for branches and git restore for files.
The recommendation is simple: use git switch for branch work. It's clearer and safer. You'll still see git checkout everywhere in older tutorials, so it's worth recognizing both โ but reach for switch first. (It needs Git 2.23 or newer; check with git --version.)
Jump to an existing branch. The legacy form is git checkout main.
Create a new branch and switch to it in one step. -c = "create." Legacy: git checkout -b.
Jump back to the branch you were on previously โ like "undo last switch."
git branch โ managing branches
The git branch command creates, lists, renames, and deletes branches. Importantly, it does not switch between them โ that's switch's job.
-d and -D matters: lowercase is the safe version that protects unmerged work; uppercase forces deletion regardless.A Complete Branching Workflow
Now let's put it all together in the canonical scenario: you're on main, and you want to add a feature without endangering it.
Step 1 โ Create your branch and switch to it
You're now on feature/add-login, which points at the exact same commit as main. Both branches, same starting point.
Step 2 โ Make commits on the branch
With each commit, feature/add-login moves forward โ exactly as we saw in Figure 2. Crucially, main stays exactly where it was.
Step 3 โ Confirm main is untouched
Git resets your working directory to look like main's last commit, so your feature work simply isn't there. Switch back to feature/add-login and it reappears. The two timelines are fully isolated โ exactly the parallel-universe promise from the start of the article.
Step 4 โ Visualize the structure
The single most useful command for seeing where your branches stand is:
|/ marks where the two branches split from a common ancestor. This ASCII graph is the cheapest way to build an accurate mental picture of your branches.Step 5 โ Merge it back (fast-forward)
When the feature is done and main hasn't moved since you branched, merging is trivial:
Because there's a direct, linear path from main to your branch's tip, Git performs a fast-forward merge: instead of creating a new merge commit, it simply slides the main pointer forward to the tip of your feature branch. No extra commit, clean linear history โ and it's just pointers moving again.
(When both branches have new commits โ when they've genuinely diverged โ Git can't fast-forward and must create a real merge commit, which can introduce conflicts. That's the subject of the next article.)
Naming Conventions and Good Habits
Branch names are communication. A good name tells your whole team what a branch is for without anyone opening a single file.
Use category prefixes
The widely used convention is <type>/<description>:
feature/โ new functionality (e.g.feature/user-authentication)bugfix/โ fixing a known, non-urgent bug (e.g.bugfix/login-redirect-loop)hotfix/โ an urgent production fix (e.g.hotfix/payment-gateway-timeout)release/โ preparing a release (e.g.release/2.4.0)
Other common prefixes include chore/, refactor/, docs/, and test/.
Format rules
- Use lowercase kebab-case โ words separated by hyphens (
feature/user-login-redesign), not camelCase or snake_case. - Be descriptive but concise โ keep names readable, ideally under 50โ60 characters.
- Avoid spaces, punctuation, and special characters; stick to lowercase letters, numbers, and hyphens.
- If your team uses an issue tracker, embed the ticket ID for traceability:
feature/PROJ-123-user-auth.
main and merges cleanly. Long-lived branches drift far from main and lead to painful "merge hell." The research backs this up: in Accelerate (Forsgren, Humble & Kim), high-performing teams keep branches alive for less than a day before merging.
Clean up after merging
Once a branch is merged, delete it with git branch -d <name>. This keeps your repository tidy and prevents you or your teammates from accidentally reusing a stale branch. If git branch -d ever refuses, that's Git protecting unmerged work โ investigate with git log before reaching for the forceful -D.
The Mental Model in One Picture
Everything in this article reduces to a few ideas that are worth committing to memory:
- A commit is a permanent snapshot. Commits never move.
- A branch is a tiny, movable pointer to a commit. Creating one is nearly free.
- HEAD is the "you are here" marker. Normally it points at a branch, which points at a commit:
HEAD โ branch โ commit. - Committing moves the current branch forward, and HEAD follows.
- Switching just re-points HEAD at a different branch.
- Detached HEAD means HEAD points straight at a commit with no branch โ safe to look around, but make a branch before leaving if you committed anything.
Hold those six facts in your head and Git branches stop being mysterious. The commands are just different ways of moving small pointers around an unchanging chain of snapshots.
Main References
-
Chacon, S. & Straub, B. Pro Git (2nd ed.). Apress โ "Branches in a Nutshell," "Basic Branching and Merging," "Branch Management."
git-scm.com/book/en/v2/Git-Branching-Branches-in-a-Nutshell -
The Git Project. Official Reference โ git-switch & git-branch.
git-scm.com/docs/git-switch -
Taylor Blau. Highlights from Git 2.23. The GitHub Blog, August 16, 2019.
github.blog/open-source/git/highlights-from-git-2-23 -
Atlassian. Using Branches โ Git Tutorials.
atlassian.com/git/tutorials/using-branches -
Forsgren, N., Humble, J. & Kim, G. Accelerate: The Science of Lean Software and DevOps. IT Revolution, 2018.
atlassian.com/continuous-delivery/continuous-integration/trunk-based-development