Exercises — Git operations — branch, merge, rebase, cherry-pick, stash, bisect
A quick reminder of the one picture everything rests on — commits are nodes in a Directed Acyclic Graph, branches are sticky-notes pointing at nodes:

Level 1 — Recognition
Goal: name the concept from its description. No commands to run, just "what is this called?"
L1.1
Which single Git object is described by: "an immutable snapshot of the whole tree plus metadata (author, message, and the SHA of its parent(s))"?
Recall Solution
A commit. A branch is only a movable pointer; HEAD points to where you are; the index is the staging snapshot. The word "immutable snapshot" is the giveaway — snapshots that never change are commits. This links to Immutability and content-addressable storage: the commit's SHA is a fingerprint of its content, so changing content would change the name.
L1.2
You run git merge feature and the target branch is a direct ancestor of feature (no divergence). Git creates no new commit and just slides the pointer forward. What is this special merge called?
Recall Solution
A fast-forward (FF) merge. WHY no commit: there is nothing to reconcile — one line already contains the other's full history, so Git only has to move the sticky-note.
L1.3
Which operation finds the first bad commit by repeatedly asking "good or bad?" and halving the search range each time?
Recall Solution
bisect. Under the hood it is Binary Search over the commit history.
Level 2 — Application
Goal: run the command in your head and state the exact result.
L2.1
On main you run:
git switch -c feature
List the two things Git did, in order.
Recall Solution
- Created a new branch pointer
featurepointing at the current commit (a 41-byte file write — , i.e. constant time, independent of how big the repo is). - Moved HEAD to
feature(so future commits advancefeature, notmain).-c= "create then switch". Without-c,git branch featurecreates the pointer but leaves you onmain.
L2.2
Your working tree has uncommitted edits to app.js. You try git switch hotfix and Git refuses. What single command clears the way, and what does it do to your edits?
Recall Solution
git stash.
It saves the dirty working tree + index onto a stack and reverts you to a clean HEAD. Now git switch hotfix succeeds. Later git stash pop reapplies the edits and removes them from the stack.
L2.3
You want exactly one bug-fix commit a1b2c3d from feature to appear on main, without merging the rest of feature. Which command?
Recall Solution
git switch main
git cherry-pick a1b2c3d
It computes the patch parent(a1b2c3d) → a1b2c3d and re-applies it on main as a new commit with a new SHA — see Diff and patch (Myers algorithm) for how that patch is built.
Level 3 — Analysis
Goal: predict the resulting graph and outcome, reasoning from the merge/rebase logic.
L3.1 — Three-way merge, no conflict
Common ancestor (base) B has line10 = "x". On main (ours ) nobody touched line 10. On feature (theirs ) it became "y". You merge feature into main. What is line 10 in the result, and is there a conflict?
Recall Solution
Result: "y". No conflict.
Apply the parent note's per-line rule:
Here "x" (we did not change) and "y". That matches case take . Only one side changed, so the answer is unique — no human needed. See Three-way merge algorithm.
L3.2 — When is it a conflict?
Base B = "x", ours O = "a", theirs T = "b". Which case fires, and why does Git refuse to auto-decide?
Recall Solution
Case: → CONFLICT.
Both sides changed the same region to different values. Git has no rule to prefer "a" over "b" — picking one would silently discard someone's work. So it writes <<<<<<< ======= >>>>>>> markers and hands you the decision. See Conflict resolution.
L3.3 — Merge vs rebase graph shape
main and feature diverged: feature has 3 unique commits, main has 2 unique commits since their base. You integrate. Describe the resulting graph shape for (a) git merge feature run on main, and (b) git rebase main run on feature.
Recall Solution
(a) Merge: creates one merge commit with two parents — the graph keeps both diverging lines and joins them at a "we came together here" node. History is true but branchy.
(b) Rebase: takes feature's 3 commits, replays each as 3 brand-new commits with new SHAs on top of main's tip → one straight line, no merge commit. History is linear but rewritten.

Level 4 — Synthesis
Goal: combine several tools to reach a goal, and count operations.
L4.1 — Bisect count
A bug was introduced somewhere among commits between a known-good tag and the broken HEAD. Assuming each test reliably answers good/bad, how many tests (worst case) does git bisect need?
Recall Solution
WHY: each good/bad answer halves the candidate range . The number of halvings to shrink 1000 down to 1 is , and the ceiling rounds up because you cannot do a fractional test.
L4.2 — Design the workflow
You have a dirty working tree on feature. Production is on fire. You must: ship a one-line hotfix on main, then return to feature exactly as you left it, with the hotfix not mixed into your feature work. Write the command sequence.
Recall Solution
git stash # shelve dirty feature work → clean HEAD
git switch main # now allowed (tree is clean)
# ...edit the one-line fix...
git commit -am "hotfix" # lands on main only
git switch feature # back to feature
git stash pop # restore your work, drop from stack
WHY each step: stash is required because Git refuses to switch when edits would be overwritten. The hotfix commits only on main, so feature never sees it. pop returns you to the exact dirty state.
L4.3 — Squash count
You made 5 tiny commits on feature and want them collapsed into 1 clean commit before opening a pull request, keeping the code identical. Which command, and how many commits will remain from those 5?
Recall Solution
git rebase -i HEAD~5
# keep the 1st as 'pick', mark commits 2–5 as 'squash' (or 'fixup')
Remaining from those 5: 1 commit. HEAD~5 is the base; interactive rebase replays the range, folding 4 commits into the 1st. New SHAs are produced (rebase always rewrites), so only do this while the branch is local/unpushed.
Level 5 — Mastery
Goal: reason about edge cases and justify a choice from first principles.
L5.1 — The duplicate-change hazard
You cherry-pick commit C from feature onto main. A week later you also git merge feature into main. The change from C now potentially appears twice. Explain precisely why, and one way it can go wrong.
Recall Solution
Cherry-pick duplicates C's diff as a new commit C' on main (different SHA, same textual change). Git tracks history by SHA identity, not by "did this text already appear". So when you later merge feature, Git's three-way merge treats C as a change feature still "owns" that main supposedly doesn't have — because main has C', not C.
How it goes wrong: if surrounding lines shifted, applying C's diff on top of already-present C' lands in the CONFLICT case () — the same edit fighting itself. Best case Git notices they're textually identical and no-ops; worst case you get a spurious conflict or a doubled hunk.
Better: prefer merge/rebase for integration; reserve cherry-pick for isolated hot-fixes you won't later merge.
L5.2 — Degenerate bisect
In git bisect, what happens if the range collapses to a single suspect commit? And what if your test is flaky (sometimes says good, sometimes bad for the same commit)?
Recall Solution
Single suspect (): further tests — bisect immediately reports that commit as the first bad one. There is nothing left to halve.
Flaky test: binary search assumes monotonicity — everything before the bug is good, everything after is bad, one clean transition. A flaky test breaks that assumption: a false "good" on a truly-bad commit sends bisect into the wrong half and it converges on an innocent commit. Fix: make the test deterministic (or use git bisect skip on commits you genuinely can't judge, so they're excluded rather than guessed).
L5.3 — Why 41 bytes makes branching free
From the "branch is a file containing a SHA" fact, justify from first principles why creating 1000 branches is essentially free, but why a rebase of a long branch is not.
Recall Solution
A branch is a file in .git/refs/heads/ holding a 40-hex-char SHA + newline = 41 bytes. Creating it writes 41 bytes and touches no commit objects → (constant, independent of history size). 1000 branches ≈ 41 KB total; the commit graph is untouched.
A rebase is the opposite: it must (1) find the merge base, (2) extract the diff of each of the replayed commits (a Myers diff each), (3) re-apply all patches, creating new commit objects. Cost scales with commits × diff size — real work per commit, possibly with conflicts at each step. Pointers are free; rewriting content-addressed snapshots is not.
Recall Feynman recap: the whole ladder in one breath
Name the objects (commit/branch/HEAD), run the pointer moves (switch/stash/cherry-pick), predict the graph from the three-way rule, combine stash+switch+rebase into a workflow, and defend your choice by counting bytes and tests. Every scary command is just new commits + moved sticky-notes on a Directed Acyclic Graph.