Stash, Cherry-Pick & Tag

Course 2 · Ch 5
Stashing, Cherry-Picking, and Tagging Releases
Three independent tools for three specific situations — pausing unfinished work, copying one commit elsewhere, and marking a release permanently

This chapter covers three commands that don't fit neatly into "branching" or "merging" but come up constantly once you're working on real projects: temporarily shelving unfinished changes, taking one specific commit and applying it somewhere else, and permanently marking a specific point in history as a release.

git stash — Pausing Unfinished Work

Imagine you're mid-edit on a feature, and an urgent bug needs fixing on a different branch right now. Your changes aren't ready to commit, but you need a clean working directory to switch branches. git stash sets your uncommitted changes aside temporarily, without committing them anywhere.

$ # Mid-edit, urgent bug needs attention on a different branch
$ git stash
Saved working directory and index state WIP on feature/login: a1b2c3d Add validation

$ git switch hotfix/urgent-bug
# ...fix the bug, commit, push, switch back...
$ git switch feature/login
$ git stash pop
On branch feature/login
Changes not staged for commit:
modified: login.js

Useful stash variations

$ # List all stashes — you can have more than one
$ git stash list
stash@{0}: WIP on feature/login: a1b2c3d Add validation

$ # Add a description, since "WIP on ..." gets unhelpful with several stashes
$ git stash push -m "half-finished password strength meter"

$ # Apply a stash WITHOUT removing it from the stash list
$ git stash apply

$ # Discard a stash you no longer need
$ git stash drop
stash pop vs stash apply
git stash pop applies the most recent stash AND removes it from the list. git stash apply applies it but leaves it in the list — useful if you want to apply the same stashed changes to more than one branch.

git cherry-pick — Taking One Specific Commit Elsewhere

Sometimes you need exactly one commit from another branch — not a full merge, not a rebase, just that one specific change — copied onto your current branch. git cherry-pick does precisely that.

the fix you need other-branch your-branch
cherry-pick copies just that one commit's changes onto your-branch as a brand-new commit, without bringing anything else from other-branch
$ # Find the commit hash on the other branch first
$ git log other-branch --oneline
9f8e7d6 Fix critical null-check bug
a1b2c3d Add unrelated feature

$ # Apply just that one commit to your current branch
$ git cherry-pick 9f8e7d6
[your-branch 4f3e2d1] Fix critical null-check bug

When cherry-picking genuinely earns its place

  • Backporting a fix. A critical bug fix landed on main, but you also need it on an older release branch that's still being maintained — cherry-pick the one commit rather than merging all of main's other changes too.
  • Recovering a commit made on the wrong branch. Accidentally committed to the wrong branch? Cherry-pick it onto the correct one, then remove it from the wrong one (Course 1, Chapter 8's reset tools).
  • Avoid it as a substitute for merging — repeatedly cherry-picking individual commits between two branches that should really just be merged tends to create confusing, duplicated history over time.
A cherry-picked commit gets a brand-new hash, just like rebasing
Same underlying mechanism as rebase (Chapter 2) — cherry-pick replays a commit's changes as a new commit, not a copy of the original. The content is identical; the commit identity is not. This matters if you're tracking exactly which commits have landed where.

git tag — Marking a Release Permanently

A tag is a permanent, named pointer to one specific commit — unlike a branch (Course 1, Chapter 4), a tag's pointer never moves once created. Tags are how releases get marked: "this exact commit is version 1.2.0."

Tag typeWhat it is
Lightweight Just a named pointer to a commit — no extra metadata. Quick, simple, fine for personal/internal markers.
Annotated A full object with its own message, author, and date — recommended for actual releases, since it carries proper metadata (and can be cryptographically signed — Course 3, Chapter 6).
$ # Lightweight tag — quick personal marker
$ git tag v1.2.0-rc1

$ # Annotated tag — the recommended way to mark a real release
$ git tag -a v1.2.0 -m "Release 1.2.0 — login fixes and performance improvements"

$ # List existing tags
$ git tag
v1.0.0
v1.1.0
v1.2.0

$ # Tags don't push automatically — push them explicitly
$ git push origin v1.2.0
# Or push every local tag at once:
$ git push origin --tags
A tag pushed to GitHub can become a "Release" with one extra step
Pushing a tag is the underlying git operation; GitHub's "Releases" feature (Releases tab → Draft a new release, choosing an existing tag) layers release notes, attached binaries, and a changelog on top of a tag — purely a GitHub convenience feature, same underlying git tag either way.

Command Reference

CommandWhat it does
git stashTemporarily shelves uncommitted changes
git stash popReapplies the most recent stash and removes it from the list
git stash listShows all currently stashed change-sets
git cherry-pick <hash>Copies one specific commit's changes onto your current branch
git tag -a <name> -m "msg"Creates an annotated tag marking the current commit
git push origin --tagsPushes all local tags to the remote (tags don't push automatically)

Chapter 5 Quick Reference

  • git stash — temporarily shelves uncommitted work so you can switch branches cleanly; pop to reapply
  • git cherry-pick <hash> — copies one specific commit onto your current branch as a new commit
  • Cherry-pick is best for: backporting fixes, recovering commits made on the wrong branch — not as a merge substitute
  • git tag — a permanent pointer to one commit; unlike branches, never moves once created
  • Annotated tags (-a) are preferred for real releases — carry message, author, date metadata
  • Tags don't push automatically — use git push origin <tag> or --tags explicitly
  • Next chapter: GitHub Issues, Projects, and linking pull requests to issues