1. Why Branch?
A branch is just a movable pointer to a commit — creating one is instant and
cheap because it doesn't copy any files. main (or historically
master) is the conventional default branch; everything else is a
parallel line of history that starts from wherever you branched off.
The usual reason to branch: isolate work in progress from the stable, deployable
state of main. You build a feature or fix a bug on its own branch,
and only bring it into main once it's done and reviewed — that way
main stays something you can deploy from at any moment.
2. Creating & Switching Branches
git branch # list local branches, * marks the current one
git branch feature/login # create a new branch (doesn't switch to it)
git switch feature/login # switch to it
git switch -c feature/signup # create AND switch in one step
git branch -d feature/login # delete a branch (only if merged)
git branch -D feature/login # force-delete, even if unmerged
git switch is the modern, purpose-built command for changing
branches (Git 2.23+). You'll also see git checkout <branch> in
older tutorials and codebases — it does the same job, but checkout
is an older, overloaded command that also does several unrelated things (like
restoring files), which is exactly why switch was split out.
git switch -c feature/signup
echo "signup form" > signup.html
git add signup.html
git commit -m "Add signup page skeleton"
git switch main
cat signup.html
# cat: signup.html: No such file or directory
That last line is the point: main genuinely doesn't have
signup.html yet. Each branch has its own independent working
directory state, computed from its own commit history.
3. Fast-Forward vs. Three-Way Merges
git merge <branch>, run from the branch you want to merge
into, combines another branch's history into the current one. There are
two outcomes:
Fast-forward merge — if main hasn't moved since you branched off it, Git just slides the main pointer forward to your branch's latest commit. No new commit is created:
git switch main
git merge feature/signup
# Updating 3f9a1c2..7b2e910
# Fast-forward
# signup.html | 1 +
Three-way merge — if main has moved (someone else merged something), Git looks at three commits — the two branch tips plus their common ancestor — and creates a new merge commit with two parents, combining both histories:
git switch main
git merge feature/login
# Merge made by the 'ort' strategy.
# login.html | 1 +
# (a new merge commit is created, with two parent commits)
Both are equally valid — a fast-forward simply means there was nothing to
reconcile. If you always want an explicit merge commit for traceability even when
a fast-forward is possible, use git merge --no-ff.
4. Resolving Merge Conflicts
A conflict happens when both branches changed the same lines of the same file. Git can't guess which version you want, so it pauses the merge and marks the file for you to resolve by hand:
git merge feature/pricing
# Auto-merging pricing.html
# CONFLICT (content): Merge conflict in pricing.html
# Automatic merge failed; fix conflicts and then commit the result.
<h1>Pricing</h1>
<<<<<<< HEAD
<p>Starting at $9/month</p>
=======
<p>Starting at $12/month, billed annually</p>
>>>>>>> feature/pricing
<<<<<<< HEAD down to ======= is your current
branch's version; ======= down to >>>>>>> feature/pricing
is the incoming branch's version. Edit the file down to what it should
say — deleting the marker lines entirely — then stage and commit:
# after manually editing pricing.html to the final text:
git add pricing.html
git commit -m "Merge feature/pricing, resolve pricing copy conflict"
git merge --abort puts the repo back exactly how it was before you ran git merge, as if it never happened. Use it any time a conflict resolution goes sideways — there's no penalty for starting over.
5. Rebase Basics: Rebase vs. Merge
git rebase main, run from a feature branch, replays that branch's
commits one by one on top of main's latest state — instead of
creating a merge commit, it rewrites the feature branch's history so it looks
like it was built starting from where main is now:
git switch feature/signup
git rebase main
# Successfully rebased and updated refs/heads/feature/signup.
The result: a clean, linear history with no merge commits — often preferred for feature branches before they're merged, because it reads like the work happened in a straight line. The trade-off is that rebase rewrites commit hashes, so it's only safe on branches nobody else has already pulled and built on top of. Merge is always safe on shared branches; rebase is a local cleanup tool. Week 4 covers rewriting history in more depth.
6. Hands-on Exercise
Create a deliberate conflict, then resolve it
The fastest way to stop fearing merge conflicts is to cause one on purpose and walk through fixing it.
Requirements:
- In a repo with one committed file
about.md, create branchaand change its first line; commit. - Switch back to
main, create branchbfrom it, change the same first line to something different; commit. - Merge
aintomain— this should fast-forward or merge cleanly. - Merge
bintomain— this should now conflict on that line. - Resolve the conflict by hand, choosing or combining the wording, then stage and commit the merge.
- Run
git log --oneline --graph --alland identify which commit is the merge commit (it has two parent lines in the graph).
If you get lost mid-conflict, git merge --abort resets everything and lets you try again — there's no wrong way to practice this.
7. Knowledge Check
Four quick questions. Expand each to check your answer.
Q1
When does Git perform a fast-forward merge instead of creating a merge commit?
When does Git perform a fast-forward merge instead of creating a merge commit?
When the branch being merged into (e.g. main) hasn't had any new commits since the other branch diverged from it. There's nothing to reconcile, so Git just moves the branch pointer forward to match — no new commit is created, unless --no-ff is passed to force one.
Q2
What exactly triggers a merge conflict?
What exactly triggers a merge conflict?
Both branches changing the same lines of the same file (or one deleting a file the other edited). Changes to different files, or even different lines of the same file, merge automatically without a conflict — Git only stops and asks when it genuinely can't tell which version you want.
Q3
What's the key risk of running git rebase on a branch other people have already pulled?
What's the key risk of running git rebase on a branch other people have already pulled?
Rebase rewrites commit hashes as it replays them. Anyone who already has the old commits will have a history that's diverged from yours, causing confusing conflicts and duplicate commits the next time they sync. Rule of thumb: rebase branches only you are working on; merge for anything shared.
Q4
How do you back out of a merge that's mid-conflict and start over?
How do you back out of a merge that's mid-conflict and start over?
git merge --abort — it restores the repository to exactly the state it was in before the merge started, discarding any partial conflict resolution. There's no risk in aborting and retrying; nothing is lost from history.