Lesson 04 · team collaboration
You already know a branch is a movable pointer. Merging is just how two pointers rejoin — and there are exactly two ways it can happen. Learn to tell them apart and branching stops being scary.
This is the first lesson of the team half of the mission. Everything here is built from Lesson 02's model: commits are immutable snapshots, a branch is a ~40-byte pointer, HEAD says which branch you're on. Merging adds no new kind of object except one — a commit with two parents. That's the whole idea.
A branch lets you build something without touching main. Create one, switch to it, commit — main doesn't move.
git switch -c feature # create branch "feature" AND move HEAD onto it
# ...edit, git add, git commit...
git commit -m "add feature"
git switch -c is create-and-switch in one step. The old spelling is git checkout -b — same effect. GUI: the branch name in the bottom-left status bar → "Create new branch…"
main still points at C. Your new commit D hangs off it, and feature + HEAD moved forward to D. Two lines of history now exist. Merging is the act of bringing one back into the other.
The rule to memorize: you merge into the branch you're standing on. To fold feature into main, switch to main first, then merge.
git switch main
git merge feature
What happens next depends entirely on one question: did main move since you branched? That splits into the two — and only two — kinds of merge.
main is still at C — directly behind feature. There's nothing to reconcile. Git just slides the main pointer forward onto D. No new commit.
History stays a straight line.
Someone committed E on main while you worked. The lines diverged. Git can't just slide — it builds a new merge commit M with two parents (E and D), tying the lines together.
History forks and rejoins.
D), their tip (E), and the merge base — the last commit both shared (C). Comparing each side against that common ancestor is how Git figures out what actually changed on each line. When both sides changed the same lines, that's a conflict — the whole of next lesson.
After a merge, the feature branch pointer has done its job. Deleting it throws away nothing — the commits are now reachable from main. You're removing a label, not history.
git branch -d feature # -d refuses if the branch isn't merged (a safety net)
GUI: Source Control panel → "…" menu → Branch → Delete Branch. Use lowercase -d; the capital -D forces deletion even if unmerged — save that for branches you're deliberately abandoning.
A fast-forward is tidy but erases the fact that a feature ever existed as a separate line. Many teams prefer an explicit merge commit so the branch's shape survives in history:
git merge --no-ff feature # always make a merge commit, even when a fast-forward was possible
This is exactly what a GitHub "Merge pull request" button does by default — which is why git log --graph on a shared repo is full of these rejoin points. You'll read those graphs fluently by the end of this half.
main hasn't moved; three-way merge commit when it has. That single distinction explains 90% of what confuses people about branching.
Primary source — read this one: Pro Git §3.2, "Basic Branching and Merging". It walks these exact two scenarios with Git's own pointer diagrams, then rolls straight into the conflict case.
Then play: Learn Git Branching — the "Ramping Up" levels animate fast-forward vs. three-way merges as you type. The fastest way to make it stick.