CHAPTER 10

Cherry-pick, stash, tags, bisect

Four specific, sharp tools — each one exists because "just merge it" or "just rebase it" genuinely doesn't fit the situation.

Three separate Tuesdays

These four situations happened to different people on the team, weeks apart, but they share something: each one felt, in the moment, like there was no clean command for it — until there was.

Cherry-pick: one commit, without the rest of its branch

Sahil tagged last month's site as v1.0.0 right before the buildathon (Chapter 19 covers tagging properly). A visitor reports a broken link on that exact snapshot — something Rachit already happened to fix on main three commits ago, as a side effect of unrelated work. The main branch has moved on considerably since v1.0.0; merging all of main into a "v1.0.1 patch" branch would drag in half-finished features nobody wants backported. Sahil needs exactly one commit's changes, nothing around it.

taking one commit, leaving the rest
$ git switch -c patch/v1.0.1 v1.0.0
$ git log --oneline main -5 # find the exact fix commit
a4b5c6d fix(footer): correct broken sponsor link
 
$ git cherry-pick a4b5c6d
[patch/v1.0.1 f7e6d5c] fix(footer): correct broken sponsor link

One commit's changes, replayed onto an entirely different branch, as a brand new commit with its own hash. Everything else main has picked up since v1.0.0 stays exactly where it is — untouched, unreleased.

Stash: parking unfinished work without committing it

Anvesha is mid-edit on the events page — half-written, doesn't compile cleanly, definitely not commit-worthy — when 06pratyush pings that the Discord invite link on the homepage footer expired and needs a two-line fix immediately. She can't commit broken work just to clear her working directory, and Chapter 7 already taught you that Git won't let you switch branches if it would overwrite uncommitted changes the target branch also touches.

the pocket, not the shelf
$ git stash push -m "wip: events page rewrite"
Saved working directory and index state On main: wip: events page rewrite
 
# working directory is now clean — as if the edit never started
$ git commit -am "fix: update expired Discord invite link"
 
# back to exactly where she left off:
$ git stash pop

The distinction from a commit matters: a stash is explicitly not part of the project's history. It's a pocket, not a shelf — nobody else will ever see it, it never gets pushed, and it exists purely so unfinished, uncompilable work can get out of the way for five minutes without being dignified with a permanent record.

managing more than one
$ git stash list
$ git stash apply stash@{1} # restore a specific one, keep it in the list too
$ git stash drop stash@{0} # discard one you no longer need

Tags: a name that refuses to move

Branches, by design, move forward every time someone commits. That's exactly wrong for one specific need: marking "this precise commit is what we handed the buildathon judges" in a way that stays true no matter how much work happens afterward on every branch.

Sahil, right before the demo
$ git tag -a v1.0.0 -m "State of the site at buildathon demo"
$ git push origin v1.0.0 # tags don't push automatically with everything else

Six months later, no matter how unrecognizable the homepage has become, v1.0.0 still points at exactly that one commit. It's the "-a" annotated form that's worth defaulting to over a bare git tag v1.0.0 — annotated tags carry a tagger name, date, and message, the same accountability Chapter 2 argued for on every commit.

Bisect: finding one bad commit in fifty, by asking half as many questions as you'd think

The scariest of the four, story-wise: three weeks after a big batch of unrelated changes landed, someone notices images on the resources page have stopped lazy-loading — page load is noticeably slower on mobile data. Nobody remembers which of the roughly forty commits since the last known-good state caused it, and staring at diffs one by one would take all afternoon.

binary search, but for commits
$ git bisect start
$ git bisect bad # current HEAD has the slow-loading bug
$ git bisect good v1.0.0 # the buildathon tag definitely didn't have it
Bisecting: 20 revisions left to test (roughly 5 steps)
 
# Git checks out the midpoint automatically. Load the page, check.
$ git bisect good # or `bisect bad` — whichever it turned out to be
# Git narrows the range and checks out the new midpoint. Repeat.
 
c9d4e5f is the first bad commit
$ git bisect reset # return to where you started

Forty commits, five or six checks, done — because each answer eliminates half the remaining suspects, the same way guessing a number between 1 and 100 takes at most seven guesses if you always split the range in half. If checking "is the bug present" can be scripted — a test command that exits non-zero on failure — git bisect run npm test automates the whole search with no manual checking at all.

Worktree: two working copies of the same repository, at once

One more tool worth knowing exists, for the moment it's needed: git worktree add ../hotfix main creates a second, fully independent folder checked out to a different branch of the same repository — useful when switching branches would mean restarting a dev server or losing your place in the middle of something that takes a while to rebuild.

CHECK
Sahil needs one specific bug fix from main applied to a patch branch based on an old tag, without any of main's other, unrelated recent commits. What's the right tool?
Merge or rebase would bring in every commit main has gained since the tag — exactly what he doesn't want on a patch release. Cherry-pick takes only the one commit he actually needs, as a new commit on the patch branch.
SHORTCUTS WORTH SETTING UP NOW
git config --global alias.co switch, alias.st "status -sb", alias.lg "log --graph --oneline --decorate --all", alias.wip "!git add -A && git commit -m wip". Small, but by week three you'll have typed git status a hundred times — git st adds up.

Everything so far has happened entirely on local machines — your laptop, Rachit's, Anvesha's. Which raises a question that's been quietly unanswered this whole time: how does anyone actually see anyone else's commits in the first place? Every "pull" and "push" mentioned so far has been taken on faith. That faith runs out starting next chapter.