Branching, Merging & Rebasing
Module 05 — Branching, Merging & Rebasing
Goal: Use branches fearlessly, understand the difference between merging and rebasing, and resolve conflicts without panic.
Now that you know (from Module 04) that a branch is just a one-line file pointing at a commit, branching will feel light and natural.
5.1 Why branch?
A branch lets you work on something — a new feature, a risky experiment, a bug fix — without disturbing the stable main line of code. If it works out, you merge it in. If it doesn’t, you throw it away. Because branches are nearly free (one tiny pointer file), good Git users create them constantly.
The conventional setup: a long-lived branch called main holds production-ready code; short-lived feature branches branch off it, get worked on, then merge back.
5.2 Creating and switching branches
# Create a branch and switch to it in one step (modern command)
git switch -c feature-login
# Older equivalent you'll still see everywhere:
git checkout -b feature-login
Check where you are:
git branch # lists branches; * marks the current one
git switch main # go back to main
git switch feature-login # return to the feature
What just happened on disk (recall Module 04): Git created .git/refs/heads/feature-login containing the current commit’s hash, and rewrote .git/HEAD to ref: refs/heads/feature-login. Switching branches updates HEAD and refreshes your working files to match that branch’s snapshot.
5.3 How branches diverge
Say main is at commit C. You branch off and make commits D and E on feature-login. Meanwhile a teammate adds F to main:
D ◄── E (feature-login)
/
A ◄─ B ◄─ C ◄── F (main)
The two branches have diverged. To bring feature-login’s work into main, you have two tools: merge or rebase.
5.4 Merging
git switch main
git merge feature-login
There are two outcomes:
Fast-forward merge
If main hasn’t moved since you branched (no F), Git just slides the main pointer forward to E. No new commit needed — clean and simple.
Three-way merge (a merge commit)
If both branches advanced (our F case), Git creates a brand-new merge commit M that has two parents — E and F — tying the histories together:
D ◄── E
/ \
A ◄─ B ◄─ C ◄── F ◄── M (main)
Merge commits are honest: they preserve exactly what happened, including the fact that two lines of work existed in parallel. The downside is history can look like a tangled subway map on big teams.
5.5 Rebasing
Rebase rewrites history to make it linear, as if you’d done your work on top of the latest main all along.
git switch feature-login
git rebase main
Git takes your commits (D, E), sets them aside, fast-forwards to main’s tip (F), then replays your commits on top — creating brand-new commits D' and E' (new hashes, because their parent changed):
A ◄─ B ◄─ C ◄── F ◄── D' ◄── E' (feature-login, now linear)
Then you can fast-forward main onto it. Result: a clean, straight history with no merge commit.
The golden rule of rebasing
Never rebase commits that you’ve already pushed and others may have pulled.
Because rebase creates new commits (new hashes) and discards the old ones, rebasing shared history forces everyone else’s copies out of sync and causes chaos. Rebase freely on your own local, unpushed branches; use merge for anything shared.
Merge vs. rebase — quick guidance
- Merge: preserves true history, safe always, but messier graph. Default choice for integrating shared branches.
- Rebase: clean linear history, great for tidying your own feature branch before sharing — but never on public commits.
5.6 Resolving merge conflicts
A conflict happens when two branches changed the same lines of the same file, and Git can’t decide which to keep. Git pauses and marks the file:
<<<<<<< HEAD
color = "blue"
=======
color = "green"
>>>>>>> feature-login
To resolve:
- Open the file. The part between
<<<<<<< HEADand=======is the current branch’s version; between=======and>>>>>>>is the incoming version. - Edit the file to the version you actually want, and delete all three marker lines.
- Stage the resolved file and continue:
git add conflicted-file.js
git commit # for a merge
# or, if you were rebasing:
git rebase --continue
To bail out entirely and return to before the attempt:
git merge --abort # or git rebase --abort
Conflicts feel scary the first time but are just “Git asking you to make a decision it can’t make.” Tools like VS Code show them with clickable “Accept Current / Accept Incoming / Accept Both” buttons.
5.7 Deleting branches
Once a feature branch is merged, clean it up:
git branch -d feature-login # safe delete (refuses if unmerged)
git branch -D feature-login # force delete (even if unmerged)
Deleting a branch just removes the pointer file in .git/refs/heads/ — the commits themselves remain reachable if anything else points to them, and recoverable via the reflog even if not (Module 08).
Try it yourself
- On
main, commit a file with a linecolor = blue. - Branch to
feature, change it togreen, commit. - Switch back to
main, change the same line tored, commit. - Run
git merge feature— you’ll get a conflict. Resolve it, thengit addandgit commit. - Run
git log --oneline --graph --allto see the merge commit with two parents. - Repeat the experiment but try
git rebaseinstead, and compare the graph shape.
Key takeaways
- A branch is a cheap pointer; create them liberally for features and experiments.
- Merge combines branches, sometimes via a two-parent merge commit; it preserves true history.
- Rebase replays your commits onto another branch for a clean linear history — but never rebase shared/pushed commits.
- Conflicts are just Git asking you to choose; edit the file, remove the markers, stage, continue.