Step 88. Branches and merge — Parallel Universes and Merging

Step 88. Branches and merge — Parallel Universes and Merging

Level 1 — Programming and the Inside of a Computer | Difficulty ★★★☆☆ | Estimated time: 3 hours

Prerequisites: Git basics from Step 86 (add/commit/log/status) and the remote repository concepts from Step 87.

  • What you need: a computer with Git installed and Git Bash. All experiments happen in a new practice repository.
  • Caution: today includes an experiment that deliberately causes a conflict. It’s not an error — it’s the curriculum. Don’t panic.

Suppose you’re adding a new feature to your well-running scanner, and halfway through, someone asks, "please demo the current version." Reverting wastes the work so far; leaving it leaves the original broken. Git’s answer is to split the universe in two. A branch is a parallel universe where you experiment without touching the original, and when it works out, you merge it into the main universe. Today we create this parallel universe, merge it, and resolve with our own hands the moment two universes edit the same line differently and Git gives up judging — the conflict.


1. Learning Objectives

By the end of this chapter, you will be able to:

  • Explain that the true nature of a branch and HEAD is "an arrow pointing to a commit"
  • Create and move between branches with git switch -c, confirming that each universe’s file state differs
  • Distinguish a fast-forward merge from a merge commit
  • Read conflict markers (<<<<<<< ======= >>>>>>>), erase them, and resolve by writing an integrated version
  • Read the divergence and convergence of history with git log --graph --oneline

2. Background Knowledge — Today’s Tools and Concepts

Today’s Tools at a Glance

Category Details
Language/environment Git Bash (Windows), Git 2.x
Today’s commands git branch, git switch, git merge, git log --graph --oneline, git stash, git merge --abort
Concepts needed Branch (pointer), HEAD, fast-forward, merge commit, conflict and conflict markers
Today’s artifact A practice repository branch-lab that has experienced conflict resolution

2-1. The True Nature of a Branch — A Pointing Arrow

It’s easy to imagine a branch as a giant copy, but its true nature is astonishingly light. A branch is just one pointer (arrow) noting "which commit it points to."

main     → commit C
feature  → commit C   (another arrow pointing to the same place)

When you commit in the feature universe, only that arrow moves forward.

main     → commit C
feature  → commit C → commit D → commit E

With no file copying, creating a branch is instantaneous. And the marker showing which universe you’re standing in now is HEAD.

2-2. merge — Two Universes into One

merge is "combining one branch’s history into another branch." Two representative situations:

  • Fast-forward: main stayed still and only the other branch moved ahead. Just slide main’s arrow forward to where the other is, and done. A conflict is impossible.
  • Merge commit: both sides moved ahead on their own. Git compares the two histories and weaves them into one new commit.

2-3. Conflict — The Moment Git Says "I Don’t Know"

What if two universes edited the same line of the same file differently? Git can’t know which side is right and hands the judgment to a human. This is a conflict. Git engraves marks like this into the file:

<<<<<<< HEAD
current universe's content
=======
other universe's content
>>>>>>> other-branch

The human’s job is simple — erase all three kinds of markers, decide and write the final content yourself, then add and commit. Conflict resolution = erase the three markers and write the right answer — nothing more, nothing less.


3. Follow Along

3-1. Preparing the Experiment Repository

Input:

mkdir branch-lab && cd branch-lab && git init
echo "the universe's original file" > story.txt
git add story.txt && git commit -m "original: story.txt"

Output (measured 2026-09-09):

[main (root-commit) fe80361] original: story.txt
 1 file changed, 1 insertion(+)

How to read it: a single universe (main) with one commit has been born. Every parallel universe branches off from here. A branch is meaningful only when there’s a "branching point" (a common ancestor commit).

3-2. Creating a Branch and Moving to It

Input:

git switch -c feature
git branch

Output (measured 2026-09-09):

Switched to a new branch 'feature'
* feature
  main

How to read it: switch -c is a one-shot command meaning "create and move." The asterisk in git branch is your current position. Work from now on is recorded in the feature universe, and main stays safely preserved, frozen.

3-3. Working in the Parallel Universe, Then Coming Back to Check

Input:

echo "experimenting with a new feature" >> story.txt
git add story.txt && git commit -m "experiment: add a new-feature sentence"
git switch main
cat story.txt

Output (measured 2026-09-09):

Switched to branch 'main'
the universe's original file

How to read it: the sentence you just added is gone. Don’t be surprised — that sentence is safe in the feature universe. Coming back to main, you see main’s last appearance. After git switch feature again, cat shows (measured 2026-09-09) the sentence back in place. No folder was copied, no file hidden — only which commit you’re looking at changed.

3-4. A Clean Merge — Experiencing fast-forward

Input (while on main):

git merge feature
cat story.txt
git log --oneline

Output (measured 2026-09-09):

Updating fe80361..af62581
Fast-forward
 story.txt | 1 +
af62581 experiment: add a new-feature sentence
fe80361 original: story.txt

How to read it: Fast-forward — main’s arrow simply slid forward to where feature was. No new commit was "created" on main; feature’s commit became main’s. story.txt shows the experiment sentence. A merge where only one universe worked is always this quiet.

3-5. Make a Prediction — What If Both Sides Edit the Same Line?

Now let’s make a conflict on purpose. First predict: if both main and another branch edit the first line of story.txt differently and you merge, what happens?

  • (a) The side committed later wins
  • (b) Both go in
  • (c) Git stops and leaves the choice to a human

Check for yourself: edit the first line on main and commit, then create a new branch at the branching point (af62581) and edit the first line differently there too.

printf "first line of the main universe\nexperimenting with a new feature\n" > story.txt
git add story.txt && git commit -m "main: edit first line"

git switch -c rival af62581
printf "first line of the feature universe\nexperimenting with a new feature\n" > story.txt
git add story.txt && git commit -m "rival: edit first line"

git switch main
git merge rival

(On Windows, editing the first line directly in Notepad works too. For the hash af62581, use the value you read from your own log --oneline.)

Output (measured 2026-09-09):

Auto-merging story.txt
CONFLICT (content): Merge conflict in story.txt
Automatic merge failed; fix conflicts and then commit the result.

The answer is (c). Git declared CONFLICT and stopped. It’s not a malfunction — it’s a request saying "your judgment is needed."

Note: if you create the branch at main’s latest commit instead of at the branching point, you get a fast-forward, not a conflict (that’s actually what happened the first time we measured, so we redid it). To see a conflict, both sides must move ahead from a common ancestor.

3-6. Resolving the Conflict — Erase the Markers, Write the Right Answer

Input: open story.txt in an editor.

Output (measured 2026-09-09, file contents):

<<<<<<< HEAD
first line of the main universe
=======
first line of the feature universe
>>>>>>> rival
experimenting with a new feature

How to read it: between <<<<<<< and ======= is the current universe (main); between ======= and >>>>>>> is the other universe (rival). git status shows this state like this (measured 2026-09-09):

You have unmerged paths.
  (fix conflicts and run "git commit")
  (use "git merge --abort" to abort the merge)

Unmerged paths:
	both modified:   story.txt

What to do: erase the three marker lines (<<<<<<<, =======, >>>>>>>) and leave only the final content. Example:

a first line combining main and feature
experimenting with a new feature

Then finish:

git add story.txt
git commit -m "merge: resolve conflict with rival — unify first line"

Output (measured 2026-09-09): [main 444eea2] merge: resolve conflict with rival — unify first line

Why: the entire conflict-resolution process was those 3 steps — ① open the file ② erase the markers and write the right answer ③ add/commit. However huge the project or scary the conflict, it’s ultimately these 3 steps repeated.

⚠️ The most common beginner accident: committing without erasing the three markers. Code with <<<<<<< left in it won’t even run. Build the habit of searching the file for <<< before committing.

3-7. Seeing History as a Picture — log –graph

Input:

git log --graph --oneline

Output (measured 2026-09-09):

*   444eea2 merge: resolve conflict with rival — unify first line
|\
| * 068c6cb rival: edit first line
* | cb31f7f main: edit first line
|/
* af62581 experiment: add a new-feature sentence
* fe80361 original: story.txt

How to read it: * is a commit; the lines are the universes diverging and converging. A shape that splits into a V and joins into a Λ — a map of the history you just made by hand. The topmost commit, which has two parents, is the merge commit.

3-8. A Merge Without Conflict — Git’s Cleverness

What happens if both sides edited the same file but different parts? Let’s edit the first line on main, append a line at the end of the file in a new branch diverge, then merge.

Output (measured 2026-09-09):

Auto-merging story.txt
Merge made by the 'ort' strategy.
 story.txt | 1 +
the first line main changed again
experimenting with a new feature
appended tail line
diverge's last line

How to read it: both edits survived and merged quietly. A conflict arises not from editing "the same file" but only from editing "the same line (or nearby)". Merge made by the 'ort' strategy means Git succeeded in merging on its own and created a merge commit.

3-9. Understanding branch and switch Separately

So far we’ve created and moved with the single line git switch -c, but this command is actually a composition of two actions.

Input:

git branch idea        # create only (no moving)
git branch             # view the list
git switch idea        # move only

How to read it: git branch name is "plant one more arrow at this spot"; git switch name is "move HEAD to that arrow." When you only want to leave a backup marker, create without switching.

Try it (measured 2026-09-09): git branch -d ideaDeleted branch idea (was 444eea2). Only the arrow is deleted; if that commit is connected through another branch, the history remains intact.


4. Missions & Exercises

Mission — Reproducing the Whole Branch Process

  1. In a new repository, create tool.py and commit a basic feature on main
  2. On an add-timeout branch, add a timeout feature, commit, and merge into main (confirm Fast-forward)
  3. Create a change-msg branch at the common ancestor, and edit the same line (the output phrase) differently on both sides
  4. Merge to cause a CONFLICT, erase the three markers, write an integrated version, and resolve with add/commit
  5. Copy the git log --graph --oneline result into your notes and explain in words where it diverged and converged

Exercises

Exercise 1. Explain the statement that a branch is "an arrow," not "a folder copy," together with why branch creation finishes instantly.

Exercise 2. Explain in which situations a fast-forward merge and a merge commit each occur, and how their git log --oneline results look different.

Exercise 3. Explain what each part of the conflict markers <<<<<<< HEAD, =======, >>>>>>> rival refers to, and state the 3-step resolution procedure.

Exercise 4. When do two branches edit the same file yet merge without conflict, and what phrase appears in Git’s output then?


5. Model Answers & Completion Criteria

Mission Model Answer

Exactly the measured flow of section 3. Collecting the essentials:

git init && echo "print('v1')" > tool.py
git add tool.py && git commit -m "basic feature"

git switch -c add-timeout
echo "timeout = 5" >> tool.py
git add tool.py && git commit -m "add timeout"
git switch main && git merge add-timeout      # confirm Fast-forward

BASE=$(git rev-parse HEAD)                    # remember the common ancestor
echo 'MSG = "main version"' >> tool.py && git add tool.py && git commit -m "main: phrase"
git switch -c change-msg $BASE
echo 'MSG = "feature version"' >> tool.py && git add tool.py && git commit -m "change-msg: phrase"
git switch main && git merge change-msg       # CONFLICT occurs
# → open tool.py, erase the <<< === >>> three lines, and write the final phrase
git add tool.py && git commit -m "merge: unify phrase"
git log --graph --oneline

How to verify: ① did the first merge’s output include Fast-forward? ② did the second merge show CONFLICT (content)? ③ after resolving, is there no <<< left in tool.py (check with grep "<<" tool.py)? ④ does log --graph show the V/Λ shape? All "yes" means complete.

Exercise Answers

Answer 1. Because a branch is just one pointer (arrow) to a commit, no files need copying when created, so it finishes instantly. Files changing as you move between branches happens because "which commit you’re looking at" changes (measured in 3-3).

Answer 2. If only one branch is ahead, it’s a fast-forward and only the arrow slides forward; if both sides committed on their own, a merge commit is newly created, weaving the two histories. In log, the former appears as a straight line, the latter as a V/Λ shape with --graph (measured in 3-4, 3-7).

Answer 3. From <<<<<<< HEAD to ======= is the current branch’s content; from ======= to >>>>>>> rival is the other branch’s content. Resolution procedure: ① open the file ② erase the three markers and write the final content ③ git add, then git commit.

Answer 4. When they edited different parts of the same file. Git can merge on its own, so after Auto-merging, Merge made by the 'ort' strategy. appears and it ends quietly (measured in 3-8).

Completion Criteria Checklist

  • [ ] I can create a branch and move to it with git switch -c
  • [ ] I confirmed that file contents change to each universe’s last state as I move between branches
  • [ ] I can state the difference between fast-forward and a merge commit
  • [ ] I read the three conflict markers, erased them, and resolved by writing an integrated version
  • [ ] I can read divergence and convergence with git log --graph --oneline
  • [ ] I know how to cancel a merge midway (git merge --abort)
  • [ ] Mission: I reproduced both fast-forward and conflict resolution

6. Common Pitfalls & Fixes

Wall 1. switch gets rejected

Symptom (measured 2026-09-09):

error: Your local changes to the following files would be overwritten by checkout:
	story.txt
Please commit your changes or stash them before you switch branches.
Aborting

Cause: you have uncommitted edits, and Git blocks the move because they’d risk being overwritten.
Fix: commit what you were working on, or if you want to set it aside for a while, put it in git stash (a temporary storage box), then switch. Coming back, git stash pop restores it (measured 2026-09-09: Dropped refs/stash@{0} ...).

Wall 2. I erased the conflict markers but the merge won’t finish

Symptom: status still shows "Unmerged paths" (measured 2026-09-09: both modified: story.txt).

Cause: you edited the file but didn’t git add. add is the signal that "this file is resolved."
Fix: git add filename, then git commit. Committing completes the merge.

Wall 3. I regret starting the merge

Symptom: there are too many conflicts to handle right now.
Fix: a single line, git merge --abort, cleanly returns you to the state right before the merge. In the measurement (2026-09-09), status right after abort was nothing to commit, working tree clean. The very fact that "there’s an abort button" gives you courage.

Wall 4. I edited the same file but no conflict occurs

Symptom: both sides touched the same file, yet it merges quietly.
Cause: conflicts arise not from editing "the same file" but only from editing "the same line (or nearby)" (measured in 3-8).
Fix: it’s not an error — it’s Git’s cleverness. Open the resulting file and confirm both edits survived.

Wall 5. I try to reproduce a conflict but get a fast-forward

Symptom: you merge the branch you deliberately made, and it ends with Fast-forward.
Cause: you created the new branch at main’s latest commit, producing a shape where only the other branch moved ahead (a mistake we actually made during measurement).
Fix: create the new branch at the common ancestor commitgit switch -c rival <ancestor-hash>. Both sides must move ahead on their own for a conflict to occur.


7. Summary

Today’s Concepts

Concept One-line explanation
Branch A light arrow pointing to a commit — not a copy
HEAD A marker pointing to "the branch I’m standing on now"
Fast-forward A merge where only one side is ahead — just slide the arrow and done
Merge commit A commit with two parents, weaving histories both sides advanced separately
Conflict A state where the same line was edited differently and Git hands judgment to a human
Conflict markers <<<<<<< ======= >>>>>>> — erase them and write the right answer to resolve

Today’s Commands

Command What it does
git switch -c name Create a branch and move to it
git switch -c name hash Create a branch at a specific commit and move to it
git branch / git branch name View the list / create only
git branch -d name Delete a branch (arrow)
git merge branch Merge the other branch’s history into the current branch
git merge --abort Cancel an in-progress merge
git stash / git stash pop Temporarily store / retrieve uncommitted edits
git log --graph --oneline See the shape of history as a map

An Instinct More Important Than Commands

A conflict isn’t a malfunction — it’s a question. Even when CONFLICT appears, there’s no need to panic — Git is just asking "which of the two do you want?", and the answering procedure is nothing more than opening the file, erasing the three markers, and writing the right answer. And you can always retreat with git merge --abort.

The connection to collaboration: push a branch to GitHub and you can open a PR (pull request) proposing "shall we merge this into main?" A PR is a checkpoint where colleagues read the code and leave comments before merging, and in fact many vulnerabilities are caught at this review stage — "a second pair of eyes" is one of the cheapest and most powerful security devices. You’ve already gotten its core motions (create, merge, resolve conflicts) into your hands.


Once every box is checked, Step 88 is complete. Click the checkbox in the sidebar to save your progress.