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.

๐Ÿ’ก The Analogy โ€” Parallel universes

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."

A branch is just a pointer to a commit 98ca9 commit 34ac2 commit f30ab commit c2b9e commit each commit points back to its parent โ†’ main branch pointer
Figure 1 โ€” Commits form a chain, each linking to its parent. A branch like 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:

HEAD โ†’ branch โ†’ commit. HEAD points to your current branch; the branch points to its latest commit. When people say "where am I in the project right now?", the answer is whatever HEAD is pointing at.

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:

bash โ€” peeking at HEAD
# 1. Which branch am I on? Ask HEAD directly. $ cat .git/HEAD ref: refs/heads/main # HEAD doesn't hold a commit โ€” it holds the NAME of a branch. # 2. So what commit does that branch point to? Follow the pointer. $ cat .git/refs/heads/main c2b9e7a4f1d3b8e6a9c0d2f4b6a8e1c3d5f7a9b0 # That 40-character hash IS the commit. The chain is: # HEAD โ†’ main โ†’ commit c2b9e...
HEAD stores the name of a branch; the branch stores the hash of a commit. Two tiny text files, one clear chain.

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:

Before: HEAD โ†’ main โ†’ commit A A0 A main HEAD git commit After: both moved forward to commit B A0 A B main HEAD The new commit B is created; main slides forward to B; HEAD still points at main. You did nothing extra.
Figure 2 โ€” Committing moves the current branch forward automatically. HEAD points at the branch, so it follows along. The old commits stay exactly where they were.

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:

Normal: HEAD โ†’ branch โ†’ commit   ยท   Detached: HEAD โ†’ commit (no branch!)

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.

โœ“ Attached (normal) A B main HEAD HEAD โ†’ main โ†’ commit B โš  Detached A B main HEAD HEAD โ†’ commit A directly (no branch here)
Figure 3 โ€” Normally HEAD points at a branch (left). In detached HEAD (right), it points straight at a commit, with no branch attached to it.

Let's trigger it on purpose so the warning isn't a surprise the first time you meet it in the wild:

bash โ€” entering and leaving detached HEAD
# Check out an old commit directly, by its hash $ git checkout 34ac2f1 Note: switching to '34ac2f1'. You are in 'detached HEAD' state. You can look around, make experimental commits... but any commits you make in this state are not on any branch and will be lost when you switch away, unless you create a new branch to keep them. # --- Option 1: you were just looking. Reattach to a branch: --- $ git switch main Switched to branch 'main' # --- Option 2: you made commits here and want to KEEP them. --- # Create a branch right where you are, BEFORE leaving: $ git switch -c experiment Switched to a new branch 'experiment'
Detached HEAD is not an error โ€” it's a state. The warning text even tells you exactly how to keep your work if you need to.

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 main to 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.
One-line takeaway: detached HEAD just means "HEAD is pointing at a commit instead of a branch." It's safe to look around, but if you commit, make a branch before you leave โ€” or those commits can vanish.

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.)

switch Move to a branch git switch main

Jump to an existing branch. The legacy form is git checkout main.

switch -c Create & move git switch -c feature/login

Create a new branch and switch to it in one step. -c = "create." Legacy: git checkout -b.

switch - Go back git switch -

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.

bash โ€” git branch cheat sheet
$ git branch # list local branches (* marks the current one) $ git branch -v # list, plus the last commit on each $ git branch -a # list ALL branches, including remote ones $ git branch new-idea # create a branch (but stay where you are) $ git branch -m new-name # rename the current branch $ git branch -d old-work # delete (safely โ€” refuses if work is unmerged) $ git branch -D old-work # FORCE delete, even with unmerged work
The difference between -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

bash
$ git switch -c feature/add-login Switched to a new branch 'feature/add-login'

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

bash
$ git add index.html $ git commit -m "Add login form" [feature/add-login 87ab2] Add login form

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

bash
$ git switch main Switched to branch 'main' # Your login work has vanished from the files on disk โ€” # it's safely on the other branch. Switch back and it returns.

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:

bash โ€” the branch visualizer
$ git log --oneline --graph --all * c2b9e (HEAD, main) Make other changes | * 87ab2 (feature/add-login) Add login form |/ * f30ab Add feature #32 * 34ac2 Fix bug #1328 * 98ca9 Initial commit
The |/ 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:

bash
$ git switch main $ git merge feature/add-login Updating f30ab..87ab2 Fast-forward

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.
Keep branches small and short-lived. This is one of the most valuable habits in all of Git. A branch with a few small commits, merged within a day or two, stays close to 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.

Heads up โ€” switching needs a clean-ish working tree. If you have uncommitted changes that would clash with the branch you're switching to, Git refuses the switch to protect your work. Commit your changes first (or stash them โ€” a topic for another article).

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

  1. 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
  2. The Git Project. Official Reference โ€” git-switch & git-branch.
    git-scm.com/docs/git-switch
  3. Taylor Blau. Highlights from Git 2.23. The GitHub Blog, August 16, 2019.
    github.blog/open-source/git/highlights-from-git-2-23
  4. Atlassian. Using Branches โ€” Git Tutorials.
    atlassian.com/git/tutorials/using-branches
  5. 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
← Back to all articles