Skip to content

Git Stash, Cherry-Pick and Worktrees: When to Use Each

Git stash explained with cherry-pick and worktrees: shelve work with -u, backport a fix with -x, and check out two branches at once without losing changes.

Dasun Kalupahana 6 min read 121 views 0 comments
On this page
  1. Which one do you need?
  2. git stash: shelve changes without committing
  3. git cherry-pick: copy one commit to another branch
  4. Picking several commits
  5. Conflicts while cherry-picking
  6. When not to cherry-pick
  7. git worktree: two branches checked out at once
  8. Cleaning up
  9. Common problems
  10. Before you move on

Git stash, cherry-pick and worktrees all solve the same everyday interruption: you are in the middle of something, and something else needs your attention right now. A production bug lands while your working tree is half-edited. A fix you wrote on a feature branch needs to ship on a release branch. A reviewer asks you to check out a colleague's branch while your own is mid-build. Each command handles a different version of that problem, and picking the right one saves you from messy commits and lost work. Part 5 covered undoing mistakes; this part covers not making them when you get interrupted.

Every command below was run on Git 2.43.0 in a scratch repository, and the screenshots are captured from those runs. The paths in the screenshots were shortened to /home/dev/shop for readability; the output is otherwise unedited.

Which one do you need?

SituationUse
Half-finished work, need a clean tree for a few minutesgit stash
One specific commit from another branch is needed heregit cherry-pick
Need two branches checked out at the same time, without disturbing eithergit worktree

git stash: shelve changes without committing

git stash saves your modified tracked files and restores a clean working tree, so you can switch branches. Later you bring the changes back.

git stash
git stash list
git stash show -p
Terminal screenshot: git stash saves Report.php changes, git stash list shows stash@{0}, git stash show -p prints the diff, and the untracked notes.txt is left in place
Default git stash in a scratch repository. Notice notes.txt (untracked) is still in the working tree.

That last detail matters. By default, git stash only saves changes to tracked files. A brand-new file stays behind. If you want it shelved too, add -u (--include-untracked), and give the stash a name so you can tell entries apart later:

git stash push -u -m "report WIP before hotfix"
git status -s          # empty: working tree is clean
git stash list
stash@{0}: On main: report WIP before hotfix

Do your interrupting work, commit it, and bring the stash back:

git stash pop
Terminal screenshot: git stash push -u -m saves work with a message, then after a hotfix commit git stash pop restores Report.php and notes.txt and the stash list is empty
Stash with -u -m, commit the hotfix, then git stash pop. Git merged the stash into the new commit ("Auto-merging Report.php").

pop vs apply

git stash pop re-applies the stash and then removes it from the list. git stash apply re-applies it and keeps the entry, which is handy when you want to apply the same changes to several branches. Both take a reference such as stash@{1}; the number is the position in git stash list, newest first.

Stash only some files

Put a pathspec after -- to stash just those files:

git stash push -m "only a.txt" -- a.txt
git status -s       # M b.txt  (b.txt was left alone)

When pop hits a conflict

If the code you are returning to changed in the same lines, pop conflicts, exactly like a merge in Part 3. The important safety feature: Git keeps the stash entry when a pop conflicts, so nothing is lost.

A conflicting pop. The stash stays in the list, and the markers are labelled "Updated upstream" (your current branch) and "Stashed changes".

Resolve the file, git add it, then drop the entry yourself once you are sure it is applied: git stash drop. If it is easier to work on the stashed code in isolation, turn it into a branch:

git stash branch rescue-work stash@{0}

That creates rescue-work from the commit the stash was made on, applies the changes there, and drops the stash on success. Because it starts from the original base, it can't conflict the way a pop onto a moved branch can.

Stash caveats

  • It's easy to forget what you stashed. A stash named WIP on main from three weeks ago is a puzzle. Use -m, and check git stash list before ending your day.
  • Ignored files are not stashed unless you use -a (--all). Files in vendor/ or .env (Part 2) stay put.
  • It's not a backup. Dropping a stash by accident is recoverable only through git fsck, as with the dangling blobs in Part 5.

git cherry-pick: copy one commit to another branch

Say a rounding fix was committed on feature/discount, between unrelated work, and the release/1.0 branch needs just that fix. Merging the feature branch would drag everything along. cherry-pick re-applies one commit's changes on the branch you are on:

git switch release/1.0
git cherry-pick -x 6e8b681
git cherry-pick -x: the fix lands on release/1.0 as a new commit, with a "cherry picked from" line recording where it came from.

Two things to note from that output. The new commit has a different hash (6e8b681 became 766ec36), because it's a new commit with a different parent. And -x appended (cherry picked from commit ...) to the message, which is worth using whenever you cherry-pick onto a shared or release branch, so people can trace the origin later.

Picking several commits

A range A..B excludes A and includes B. With three commits range 1, range 2, range 3 on src:

git cherry-pick src~2..src

copied range 2 and range 3, not range 1. Add -n (--no-commit) if you'd rather apply the changes to the working tree and commit them as one.

Conflicts while cherry-picking

A cherry-pick conflict. Git lists the three ways out; --abort returns to a clean tree.

Resolve and git add, then git cherry-pick --continue; skip the commit with --skip; or give up with --abort, which leaves the branch as it was before you started.

When not to cherry-pick

Cherry-pick duplicates a change under a different hash, so Git no longer sees the two commits as the same. If the original branch is later merged in, expect occasional confusing conflicts where both versions touched the same lines. It's the right tool for backporting a fix to a release branch; it's the wrong tool for routinely moving a whole feature between branches (that's what merge or rebase are for). If you are cherry-picking a merge commit, you need -m to pick the mainline parent, as with git revert in Part 5.

git worktree: two branches checked out at once

Stashing works, but it still forces you to flip one directory between branches, and rebuilding dependencies or restarting a dev server each time is a real cost. A worktree is a second working directory attached to the same repository, with its own checked-out branch:

git worktree add ../hotfix-wt -b hotfix/urgent release/1.0
git worktree list
git worktree add
Creating a worktree. Git refuses to check out the same branch in two worktrees at once.

Now ../hotfix-wt is a full working directory on hotfix/urgent, and your original directory is untouched, with all its uncommitted changes still in place. Commit in the worktree and the commit belongs to the shared repository; I committed Urgent hotfix there and git log hotfix/urgent from the main directory showed it immediately. There is no clone and no second copy of the history.

The refusal in the screenshot is a deliberate safeguard. A branch can only be checked out in one worktree at a time, because two directories moving the same branch pointer would corrupt each other's view. If you need to look at the same branch twice, check out a specific commit instead: git worktree add --detach ../peek <commit>.

Cleaning up

Removing a worktree properly, and cleaning up after one deleted by hand (marked prunable).
  • git worktree remove ../hotfix-wt deletes the directory and its record. It refuses if the worktree has modified or untracked files (contains modified or untracked files, use --force to delete it); add --force only once you are sure.
  • If you delete a worktree directory manually, Git still remembers it. git worktree list marks it prunable, and git worktree prune clears the stale record.
  • The branch survives removing its worktree. Delete it separately with git branch -d (Part 3) once it is merged.

One practical note for PHP and Node projects: ignored and untracked files, such as vendor/, node_modules/ and .env, are not part of a checkout, so a new worktree starts without them. Expect to run composer install or copy your .env into each worktree.

Common problems

SymptomCause and fix
New file didn't get stashedUntracked files are skipped. Use git stash push -u.
stash pop conflicted; is my work gone?No. The entry is kept. Resolve, then git stash drop.
Can't remember what a stash holdsgit stash show -p stash@{n}; name future stashes with -m.
Cherry-picked commit shows a different hashExpected: it's a new commit. Use -x to record the source.
Range cherry-pick missed the first commitA..B excludes A. Use A~1..B to include it.
fatal: 'main' is already used by worktreeA branch can be checked out once. Use another branch, or --detach.
Worktree folder deleted but still listedRun git worktree prune.

Before you move on

Use stash for short interruptions, and name your entries. Use cherry-pick with -x for backports, not for moving features around. Use a worktree when switching branches would disrupt a running build or a long task. And remember none of these are backups: commit early on a work-in-progress branch if the work matters.

References: git-stash, git-cherry-pick and git-worktree in the official Git documentation.

Related reading: Part 3: Git Merge Conflicts, Part 4: Git Rebase Explained and Part 5: Git Undo Commit.

Free resources for this topic

Get one practical tip every week

New tutorials, fixes and free cheat sheets from DEV Talk, straight to your inbox. No spam, unsubscribe in one click.

Free download: Git cheat sheet (PDF)

Have a question about this post?

Ask the community: other developers and the author can answer.

Ask the community

Get notified about new articles

One click, no email needed. Turn it off anytime with the bell at the top.

Get new articles on your iPhone

Add DEV Talk to your Home Screen, open it, then tap the bell.

Rate this post
0.0
Share

Enjoying the content?

If this article helped you, you can support the author with a coffee. It keeps new posts coming.

Buy me a coffee (opens in a new tab)

Comments 0

Be kind. Comments with links are reviewed before they appear.

No comments yet

Be the first to share your thoughts.

Get new articles in your inbox

Practical tutorials and deep dives, about once a week. No spam, unsubscribe anytime.

Privacy policy

Add DEV Talk to your Home Screen

  1. 1 Tap the Share button in Safari's toolbar.
  2. 2 Scroll down and tap Add to Home Screen.
  3. 3 Tap Add. Open it from the Home Screen to read like an app and to turn on notifications with the bell.

We use cookies to improve your experience and analyze traffic. By continuing, you agree to our Privacy Policy.