Module 05

Branching, Merging & Rebasing

Developer Tools Git, GitHub & GitHub Copilot

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 parentsE 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:

  1. Open the file. The part between <<<<<<< HEAD and ======= is the current branch’s version; between ======= and >>>>>>> is the incoming version.
  2. Edit the file to the version you actually want, and delete all three marker lines.
  3. 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

  1. On main, commit a file with a line color = blue.
  2. Branch to feature, change it to green, commit.
  3. Switch back to main, change the same line to red, commit.
  4. Run git merge feature — you’ll get a conflict. Resolve it, then git add and git commit.
  5. Run git log --oneline --graph --all to see the merge commit with two parents.
  6. Repeat the experiment but try git rebase instead, 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.

Next: Module 06 — Remotes & GitHub