Git

What Does Git Stash Do? Save and Restore Changes

What Does Git Stash Do? Save and Restore Changes

Say you’re halfway through a change and someone needs you on another branch right now. git stash shelves that unfinished work on a stack and puts the working directory back at the HEAD commit, so you can switch or pull without making a commit you don’t want.

Out of the box it saves modified tracked files and staged changes. Untracked and ignored files stay exactly where they were.

Most explanations call the saved data a diff, which is a little off. Each entry is actually a commit, not a diff. Its first parent is the HEAD commit at stash time, and its second parent records the index (git-scm.com manual, Git 2.56.0).

What git stash does to your working directory

YouTube player

The git-scm.com manual describes the goal as recording the state of the working directory and the index, then going back to a clean tree (documentation for Git 2.56.0, released 2026-09-28).

Nothing is deleted. The changes sit in a stash entry that you can list, inspect and restore later, even on top of a different commit.

After a plain git stash, modified tracked files match HEAD again and staged changes are rolled back too. A new entry lands on top of the stack as stash@{0}, labeled “WIP on” plus the branch name and last commit.

Running git stash with no arguments is the same as git stash push, per the manual.

Pro Git frames the use case like this. A half-finished change is in the tree, another branch needs attention, and a throwaway commit would be the only other way to park the work. The manual’s own example is an urgent fix that interrupts feature work. Stash, fix, commit, pop.

Which changes does git stash save, and which does it skip?

Modified tracked files and staged changes go in by default. Untracked files and ignored files don’t.

Tracked and staged changes

Modified tracked files are saved and then reverted to HEAD. Staged changes are saved with the entry and end up unstaged in the working tree.

Why is GitHub the heart of open source?

Uncover GitHub statistics: developer community growth, repository trends, collaboration patterns, and the platform that powers modern software development.

Explore GitHub Data →
  • -k or –keep-index stashes the staged content but leaves it staged, a variant Pro Git calls quite popular.
  • -S or –staged stashes only what is staged, a bit like a commit that lands in the stash instead of on the branch.

The entry records the index separately, so the state of staged files only comes back when apply or pop runs with –index (Pro Git).

Untracked and ignored files

A brand new file that Git has never tracked stays in the working directory after a plain stash.

Add -u (–include-untracked) and Git stashes those files too, cleaning them out of the tree. Add -a (–all) and ignored files go in as well, according to the manual.

Pro Git points out the catch. -u alone still leaves explicitly ignored files behind, so build output matched by your .gitignore rules stays put unless –all is passed.

My rule here is simple. Before running git clean on a folder full of generated files, run git stash –all instead. The tree ends up just as clear, but the files land in a stash entry instead of disappearing, which Pro Git calls the safer option.

Looking to sharpen your Git skills? Branching, merging, rebasing, and everything else you need - including git rebase, git stash, and commit workflows - is on one page in the Git Cheat Sheet.

How git stash stores entries in the stash stack

YouTube player

Git keeps the newest stash in refs/stash and the older ones in that reference’s reflog. The latest is stash@{0}, the one before it is stash@{1}, and so on down the stack.

The manual also accepts a bare integer, so 2 means stash@{2}. Time-based names such as stash@{2.hours.ago} work through the usual reflog syntax.

Anatomy of one entry

YouTube player

Every entry is a commit, and its parents record what was stashed (git-scm.com manual, Git 2.56.0). The manual documents the first three pieces below. When -u is used, Git also adds a third parent for the untracked files.

  • The entry commit (W) has a tree that holds the working directory state.
  • The first parent (H) is the HEAD commit at stash time.
  • The second parent (I) is a commit recording the index.
  • The third parent (U) holds untracked files and exists only when -u was used.

That structure is why git stash show can report a diff between the stashed contents and the commit the entry was created from.

A few numbers worth keeping in your head. Entries carry 2 parents by default and 3 with -u. Since Git 2.32 (2021-06-06), stash show –only-untracked lists the untracked files in an entry. And by default a stash is visible in the local repository only.

▶ GIT EXPLAINED

If you commit, branch, or merge code every day, it helps to know how Git came to be. This video covers how Linus Torvalds created Git in just 10 days after a license dispute and explains how its data model actually works.

Linus Torvalds Built Git in Days Out of Spite
Built in 10 Days Out of Spite. Git and its data model explained in plain language.

Stashes do not travel with a push. The current manual lists export and import subcommands that turn stashes into a chain of commits for fetch and push, so sharing is possible, just never automatic.

To keep an eye on the stack, set status.showStash to true and git status reports how many entries are stored (git-config documentation).

Subcommands and flags of git stash

YouTube player

Day to day you’ll use push, list, show, apply, pop, drop, clear and branch. Scripts lean on create and store, and export and import move entries between repositories.

SubcommandWhat it doesEffect on the stack
pushSaves changes as a new entry and rolls the tree back to HEADAdds one entry
list, showLists entries, or shows one as a diffNone, read only
apply, popReapplies an entry on the current treeapply keeps it, pop removes it after a clean apply
drop, clearDeletes one entry, or all of themRemoves entries
branchCreates a branch from the entry’s original commit and applies it thereDrops the entry if the apply succeeds

show prints a diffstat by default. Pass -p for the full patch, or use any other format git diff understands. The keys stash.showStat (true by default) and stash.showPatch (false by default) change that default.

Flags that change what gets stashed

  • -m or –message labels the entry instead of leaving the default WIP text.
  • -p or –patch lets you pick hunks interactively, and it implies –keep-index.
  • -k or –keep-index leaves staged changes in the index.
  • -S or –staged stashes only the staged changes.
  • A pathspec records only the matching files and leaves the rest of the tree alone.

Without the word push, a pathspec is only accepted after a double hyphen. The rule exists so a misspelled subcommand can’t create a stash you never wanted. Typing push removes the trap.

git stash save is deprecated

The manual marks save as deprecated in favor of push. The reason is pathspec support. save can’t take one, and all its extra arguments become the message.

Pro Git traces the change to a Git mailing list discussion in late October 2017 and adds that save is not going away soon.

Keep a git cheat sheet open during the first week. -k, -S and -p are easy to mix up.

Pop or apply: how git stash restores work and handles conflicts

YouTube player

Pop suits a one-time round trip. Apply suits entries that should outlive the restore. When a restore conflicts, the two behave identically and the entry stays in the stash list.

Choosing between pop and apply

Pop makes sense when the work goes straight back onto the branch it came from and you’d like the stack to stay short.

Apply is the better pick when the same changes need to land on a second branch, or when you’d rather check the result before deleting anything. It’s also the only one that accepts a recovered commit instead of a stash reference, since apply takes any commit that looks like a stash (git-scm.com manual).

The price of apply is one extra command, git stash drop. What you get is a safety net.

What happens when the restore conflicts

A conflicted pop never deletes the entry. apply, pop and branch all return exit status 1 on conflicts and leave the entry in place, which a script can test for.

Conflict markers are labeled “Updated upstream” and “Stashed changes” by default, and –label-ours, –label-theirs and –label-base rename them on apply.

Restoring the staged state with –index can fail on conflicts. Conflicts live in the index, and the old staged state can no longer be reapplied on top of them. If you want –index behavior every time, set stash.index to true and apply and pop will act as if it was passed (default: false).

Once the markers are gone, resolve the conflicts by hand the usual way, stage the files, and drop the entry yourself. Forget that last drop and a stale entry sits on the stack indefinitely.

Escaping a bad apply with git stash branch

Sometimes the target branch has moved far enough that apply conflicts badly. git stash branch builds a new branch on the commit the entry was created from and applies the changes there. That commit is the recorded first parent, so the stashed state comes back without conflicts, according to the manual (Git 2.56.0).

The catch is that you now own a branch, and getting the work back onto the original one is an ordinary merge.

How to stash work before switching branches or pulling

YouTube player

The round trip goes push, switch or pull, then pop, with a list check in between.

  1. Run git status and note any untracked files that need to travel with the stash.
  2. Run git stash push -m “short description”, adding -u when new files are involved.
  3. Run git stash list and confirm the entry sits at stash@{0}.
  4. Switch branches or run git pull.
  5. Return to the original branch and run git stash pop (or git stash apply stash@{n} to keep the entry).
  6. Run git status to confirm the files are back, and resolve conflicts if any appear.

You don’t always need a stash when switching branches. The git-switch documentation says a switch needs no clean tree and aborts only when local changes would be lost, so edits in files the target branch doesn’t touch simply come along.

Pulling can be stricter. The manual’s example shows git pull refusing to overwrite local changes that conflict with the incoming ones, and the fix is stash, pull, stash pop. When the changes don’t conflict, a plain pull goes through.

When Git stashes for you

git pull –autostash creates a temporary entry, records it in MERGE\_AUTOSTASH and applies it after the pull. git switch -m does something similar, stashing before the switch and reporting “Applied autostash” once it reapplies. The merge.autoStash and rebase.autoStash config keys give merge and rebase the same behavior (git-config documentation).

Conflicts are the weak point. The pull documentation warns that the final stash application can produce non-trivial conflicts, and the switch documentation says a conflicted autostash stays in the stash list for you to sort out.

Applying a stash on another branch

An entry isn’t tied to its branch. Pro Git says you can stash on one branch, switch, and reapply there, even with uncommitted files already in the tree.

Conflicts show up only where a change no longer applies cleanly. The farther the target branch drifts from the entry’s base commit, the likelier that gets.

When to use git stash, and when a commit or worktree fits better

Stash handles interruptions measured in minutes or hours. A WIP commit on a branch is better for work that lasts days, and a worktree wins when two tasks need live files at the same time.

OptionSurvives long termVisible to teammatesCurrent tree afterwards
Stash entryWeakly, dropped entries need fsckNoClean, matches HEAD
WIP commitYes, in branch historyAfter a pushClean after the commit
BranchYesAfter a pushClean after switching away
WorktreeYes, files stay on diskNoUntouched, second tree opened

Where git stash falls short

The default label reads “WIP on branch: hash message”, which names the base commit and says nothing about the work. With several entries on the stack, only -m keeps things readable.

Per the manual, dropped or cleared entries sit outside the normal safety mechanisms. A commit on a branch doesn’t have that problem.

And every entry is anchored to the commit it was made from, so restores get more conflict-prone as the branch moves on.

If losing the work would cost more than a minute of retyping, commit it on a throwaway branch. That’s my cutoff, anyway.

The manual’s pre-stash routine still works for this: switch -c my\_wip, commit -a -m “WIP”, do the other work, switch back, then reset –soft HEAD^ to unwind the commit.

Why a worktree sometimes beats both

git worktree attaches extra working trees to the same repository. Branches and tags are shared, while each tree keeps its own HEAD and index (git-worktree documentation).

One branch can be checked out in only one worktree at a time, so the second task needs its own branch.

That makes a worktree the cleaner answer when half-written code has to stay open in the editor while another branch gets tested. No stash, no commit.

How to recover a dropped or cleared stash

Drop and clear remove entries from the stash list but leave the stash commits in the repository as unreachable objects. git fsck can still find them until garbage collection prunes them.

The manual warns that such entries can’t be recovered through the normal safety mechanisms, then offers a pipeline for listing the survivors.

  • git fsck –unreachable lists dangling objects, and the manual’s pipeline keeps only merge commits, since each stash entry is one.
  • –grep=WIP matches the default label, so entries created with -m need their own pattern.
  • Pass the hash to git stash apply, or use git stash store to put it back on the stack with the reflog updated.

Apply accepts any commit that looks like one made by stash push or stash create, which is why a bare commit hash works.

The clock is gc.pruneExpire, which defaults to 2.weeks.ago according to the git-gc documentation. Pruning runs inside git gc, and git gc –auto decides when, using thresholds such as gc.auto (6700 loose objects by default).

Once a prune has run, nothing brings the entry back. Run the pipeline the moment you notice the mistake.

Common Questions About git stash

How is git stash different from git reset?

Reset moves HEAD, and with –hard it overwrites the working tree and index, so uncommitted changes are gone (git-reset documentation).

Stash records those changes in an entry first, then rolls the tree back to HEAD.

Is git stash the same as staging files?

No. Staging copies a file’s current state into the index for the next commit, and the file stays in your working tree.

A stash saves the working tree and the index together, then resets both to HEAD.

What happens to a stash if I delete the branch it came from?

Nothing. The entry lives under refs/stash, not under the branch, and its first parent keeps the base commit reachable.

Only the label text still carries the old branch name.

Can I rename a stash message?

Not directly, because the manual’s synopsis has no rename subcommand. The workaround is to re-store the entry’s commit with git stash store -m and a new message.

Store first, drop the old entry second, so the work always has a reference.

Does a stash carry over to other git worktrees?

Yes. The git-worktree documentation says refs under refs/ are shared, except refs/bisect, refs/worktree and refs/rewritten, and refs/stash is not on that list.

An entry made in one worktree therefore shows up in git stash list in the others.

What are git stash create and git stash store for?

Both are built for scripts. Create builds a stash commit and prints its hash without adding it to the stack, while store files such a commit onto the stack and updates the reflog.

The manual says push is usually the command you want.

When a Stash Entry Should Become a Commit

git stash suits work that comes back within the same working session. Anything that outlives a session belongs in a commit on a branch.

A few habits keep stale entries from piling up. Label every entry with -m when you create it. Run git stash list before each pull or switch. Convert survivors with git stash branch, then commit the result.

Labeling comes first because no subcommand renames an entry afterward. Converting comes last because git stash branch is the step that gives an entry a branch of its own. It applies the changes there but does not commit them, so the commit that makes the work part of branch history is still yours to make, the same result you get from creating a new branch in Git and committing by hand.

The trade-off is a WIP commit that needs squashing or amending before review, paid in exchange for work that the normal safety mechanisms cover.

Verified against the Git 2.56.0 manual on 2026-10-02. The export and import subcommands are the one feature that could loosen this advice for teams, because they let stashes travel through fetch and push.

Bogdan Sandu

Stay sharp. Ship better code.

Every week: one curated article, one tool worth knowing, one tip you can use tomorrow. No noise, no padding.