Branching & Merging
Branches are pointers, merges are graph operations — that's the whole trick.
35 minDifficulty 2/5toolingAI-writtenWritten by a model on 16 August 2026 and not yet read by a person. Checked automatically: schema, the pedagogical rules the hand-written material is held to, and every diagram parsed for real.
Before this
Why this exists
Two people work on the same repository. One fixes a bug, the other builds a feature. Without branches, their commits interleave in one history and a half-finished feature can break the bug fix. Branches let each line of work happen in isolation, and merging is how the isolated lines rejoin without either person touching the other's files directly.
The mental model
A branch is not a copy of the repository — it is a single pointer, a name attached to one commit. `git branch feature` costs nothing because it copies nothing; it just writes a 40-character hash into a small file. Commit on that branch and the pointer moves forward with you. Two branches pointing at nearby commits still share almost all of history underneath.
How it works
A branch is a pointer, not a container
`HEAD` points at the branch you're on; the branch points at a commit; the commit points at its parent. Switching branches moves `HEAD` to point somewhere else and rewrites your working directory to match — nothing is copied, only pointers change and files are checked out from the object database.
Merging finds a common ancestor and combines two histories
`git merge` locates the most recent commit both branches share — the merge base — then applies the combined changes from both branches since that point. If the changes touch different lines, git resolves it automatically and creates a merge commit with two parents. If they touch the same lines, you get a conflict.
Fast-forward vs true merge
If your target branch hasn't moved since you branched off it, merging just slides its pointer forward to your latest commit — no merge commit, no combining, because there was nothing to combine. This is a fast-forward. The moment the target branch has new commits of its own, a real merge commit is required to represent both lines joining.
Delete the branch, keep the history
Once merged, a branch name is disposable — the commits it pointed at are already part of the target branch's history and stay there forever. Deleting `feature/login` after merging removes a label, not the work. This is why teams delete branches aggressively: an unmerged branch is real risk, a merged one is a stale label.
The mechanism
Main advances through A and B. `feature` branches off B and adds C and D. Meanwhile main advances again to E, so the branches have diverged. Merging feature into main finds B as the common ancestor, combines the changes from C-D and E, and creates F with two parents — the merge commit. History now shows both lines and where they rejoined.
gitGraph commit id: "A" commit id: "B" branch feature checkout feature commit id: "C" commit id: "D" checkout main commit id: "E" merge feature id: "F: merge"
What people get wrong
- Branching in Git is slow or expensive, like branching a large binary repository.
- A Git branch is a 41-byte file containing a commit hash. Creating one is instant regardless of repository size. Developers coming from centralised version control systems where branches genuinely copy the whole tree carry that cost model over, and end up avoiding branches that Git was specifically designed to make cheap.
- A merge conflict means something went wrong.
- A conflict means two people made a deliberate, incompatible change to the same lines — it is information, not an error state, and git is correctly refusing to guess which change should win. The panic response to a conflict is usually to abort and redo the work from scratch, which throws away a signal that two people need to actually talk about what the code should do.
- Deleting a branch deletes the commits on it.
- A merged branch's commits are already reachable from the branch you merged into, so deleting the branch label leaves the history untouched. This is the reason teams can delete branches immediately after merging without any anxiety about losing work — the label and the work are different things.
When not to use it
- You need a clean, linear history with no merge commits for a small feature.
- Rebase the feature branch onto the target before merging (fast-forward merge), which is a different operation covered in `git-rebase-conflicts` — never rebase a branch other people are also committing to.
- Two long-lived branches need to exchange changes repeatedly over months.
- Merge frequently in both directions rather than letting them diverge for weeks — the size of a conflict grows with how long branches have been apart, not with how much code exists.
Terms
- HEAD
- — The reference to whichever commit or branch you currently have checked out — what moves when you switch branches.
- Merge base
- — The most recent commit reachable from both branches being merged; the starting point for computing what changed on each side.
- Fast-forward
- — A merge where the target branch simply moves its pointer forward because it has no new commits of its own to combine with.
- Merge commit
- — A commit with two (or more) parents, created to represent two divergent histories joining back together.
In an interview
What actually happens in the repository when you run `git checkout` to switch branches?
- HEAD moves to point at the other branch
- working directory files are rewritten to match that commit's tree
- no copying of the repository occurs
Can you recall it?
What is a Git branch at the storage level, and what makes a fast-forward merge different from a true merge?