What does git stash push --keep-index do, and when would you use it?
answer
- About the staged/unstaged boundary
- Test what you are about to commit
- One part stays on disk, the rest is shelved
- The stash still holds more than you think
- -k pairs with git add -p
basics
~20 sIt stashes everything as usual but leaves the staged changes in place in the index and working tree, so you can build and test exactly the content you are about to commit, with all unstaged work temporarily out of the way.
solid answer
~40 s`git stash push -k` (long form `--keep-index`) shelves your changes but restores the staged content back into the working tree afterwards, so what remains on disk is precisely what `git commit` would record. That makes it the tool for verifying a partial commit: you have staged one coherent slice with `git add -p`, and you want to run the tests against that slice alone rather than against it plus half a refactor. The trap is that the stash entry still contains **everything**, staged parts included — so after you commit the staged slice, popping replays those changes again and conflicts with the commit you just made. Recent Git offers `git stash push --staged`, which stashes only the staged changes and sidesteps the overlap for the inverse workflow.
code
bash · 5 linesgit add -p
git stash push -k -m "rest of the refactor"
./run-tests
git commit -m "fix: off-by-one in range check"
git stash popgo deeper
Know that -k leaves your staged changes in the working tree while the rest is stashed, so you can check the commit you are about to make in isolation.
Explain the mechanics against the index: the stash still records the full state, and -k only restores the staged portion to disk afterwards. That asymmetry is the whole point of the flag.
Show the workflow and its trap — stage a slice, test it alone, commit, then handle the overlapping pop. Contrast it with --staged on recent Git and with -p for hunk-level stashing.
Consider whether commit-level test isolation should be a team practice at all, and whether a hook that stashes around a test run is worth the failure modes it introduces on interruption.
## The problem it solves You have been working for an hour and your tree contains two things: a bug fix and the beginnings of an unrelated refactor. You stage just the fix with `git add -p`, ready to commit it on its own. But you have not tested the fix **in isolation** — the tests you ran covered the whole messy tree. If the fix only passes because of something in the refactor, you are about to commit a broken commit and find out later during a bisect or a CI run on that commit alone. `--keep-index` gives you that isolated test. ## What it does mechanically ``` git add -p # stage the coherent slice git stash push -k -m "rest" # shelve everything, keep staged content on disk # run the build and tests here git commit -m "fix: off-by-one in range check" git stash pop ``` `git stash push` normally records both index and working-tree state and then resets both to `HEAD`. With `-k`, Git performs the stash as usual but restores the index — and the corresponding working-tree content — afterwards. The result: on disk you have exactly the staged content, nothing more. Everything you did **not** stage is safely in the stash and out of the way. So the tests you now run are the tests for the commit you are about to make. ## The overlap trap This is what interviewers probe. The stash entry created with `-k` still contains your **full** working state, including the staged part. It is not "the unstaged remainder" — that is the common misreading of the flag's name. Consequence: after you commit the staged slice and run `git stash pop`, Git tries to reapply changes that are now already committed. Often the merge absorbs it silently; when the staged content was reworked before committing, you get a conflict against your own new commit. The fix when it happens is ordinary conflict resolution, keeping the committed version and taking only the genuinely new hunks. ## The modern alternative for the inverse case Recent Git added `git stash push --staged`, which stashes **only** the staged changes and leaves everything unstaged in place. That is the mirror-image tool: `--keep-index` keeps staged content on disk and shelves the rest; `--staged` shelves the staged content and keeps the rest. If your goal is to set aside a prepared slice rather than to test it, `--staged` gives a stash entry with no overlap, so the later pop is clean. It requires Git 2.35 or newer, so on older installations `--keep-index` remains the tool available. ## Related selection flags `--keep-index` is about the staged/unstaged boundary. Two neighbours are worth naming because they answer adjacent questions: - **`-p` / `--patch`** — choose hunks interactively for the stash itself, the same selection interface as interactive staging. Use it when the split you want does not correspond to what is staged. - **A pathspec** — `git stash push -m "parser" -- src/parser/` shelves only that subtree. And always give these stashes a message with `-m`: a stash created mid-workflow is exactly the kind that gets forgotten, and `WIP on main:` tells you nothing three days later. ## When not to bother If your working tree only contains one logical change, `--keep-index` buys nothing — the tree already is the commit. It earns its place specifically when you are committing a *subset* of a messy tree, which is also when the tests-in-isolation risk is real. Some people wire this into a pre-commit hook so every commit is tested against its staged content. That works, but be deliberate: a hook that stashes and pops around a test run can leave the developer in a confusing state if the test run is interrupted, precisely because of the overlap described above. ## What a good answer sounds like "`-k` stashes everything but leaves the staged content in the working tree, so I can test exactly what I am about to commit. The catch is that the stash still holds the staged changes too, so popping after the commit can conflict with what I just committed — `--staged` on recent Git covers the inverse case cleanly."
- Why can `git stash pop` conflict after you commit the slice you tested with `--keep-index`?Because the stash entry recorded your **entire** working state, staged parts included — `-k` only affects what stays on disk, not what is stored. After committing the staged slice, popping replays those same changes on top of the commit that already contains them, which conflicts whenever the content was reworked before committing.
- How does `git stash push --staged` differ from `--keep-index`?`--staged` stashes only the staged changes and leaves unstaged work in place; `--keep-index` stashes everything and leaves the staged content on disk. Use `--staged` to set a prepared slice aside with no overlap in the entry, and `--keep-index` to test the slice you are about to commit. `--staged` needs Git 2.35 or newer.
- What does `git stash push -p` give you that `--keep-index` does not?Hunk-level choice over what goes into the stash itself, using the same interactive selection interface as interactive staging. `--keep-index` can only split along the existing staged/unstaged boundary, so `-p` is the answer when the division you want does not match what you have staged.
saying these in an interview costs you the question
- Thinking --keep-index stashes only the unstaged changes
- Expecting a clean pop after committing the staged slice
- Confusing it with --index on apply, which restores staging
- Using it when the tree holds only one logical change
- Leaving unmessaged mid-workflow stashes to accumulate