git stash 是临时保存而非暂存,与 git add 的暂存区无关;它默认只保存已跟踪文件的修改,未跟踪文件需显式加 -u 参数才能保存。

git stash 不是“暂存”,它是临时保存,和 git add 的暂存区(Index)完全无关。 很多人卡在这一步:以为 git stash 是把修改“加到暂存区”,结果恢复时发现暂存区空了、文件没变、甚至丢失未跟踪文件——问题就出在概念混淆和默认行为上。
为什么 git stash 恢复后工作区“看起来没变”
这是最常被误判的现象。根本原因在于:git stash 默认只保存「已跟踪文件」的修改(即曾经 git add 过或提交过的文件),它不保存未跟踪文件(untracked files),比如新写的 utils.js 或刚生成的 config.local.yaml。
你执行:
git status # 会显示: # modified: main.go # untracked: new-feature.ts
然后运行 git stash —— new-feature.ts 就被丢掉了,main.go 的修改虽被保存,但恢复时若用错命令,也可能只还原工作区、不还原暂存状态。
- 想保留未跟踪文件?必须加
-u:git stash -u或git stash push -u - 连
.gitignore里的文件也要存?用-a:git stash -a(极少需要,慎用) - 恢复时只应用不删除?用
git stash apply;要弹出并删掉?用git stash pop
git stash apply 和 git stash pop 的关键区别
这两个命令都“恢复”stash,但对 stash 栈的影响完全不同,且影响后续操作的确定性。
假设你有:
git stash list
# stash@{0}: On feature: user-profile-ui
# stash@{1}: On main: hotfix-login-
git stash apply:只把stash@{0}的内容打回工作区和暂存区,stash@{0}仍保留在栈里 → 可重复应用,适合比对或调试 -
git stash pop:等价于apply+drop stash@{0}→ stash 被移除,不可逆。如果恢复时发生冲突,pop会中止并保留 stash,但栈顶已被尝试移除,容易混乱 - 要指定恢复某一个?必须写全索引:
git stash apply stash@{1},否则永远作用于stash@{0}
带备注的 stash 才能长期可维护
默认 git stash 生成的描述是 WIP on branch: commit-hash message,毫无业务信息。两周后你看到 stash@{3}: WIP on dev: a1b2c3d Refactor auth,根本不知道这“重构”到底改了登录还是注册流程。
- 强制加备注:
git stash push -m "login: add SSO fallback when OIDC timeout" - 别用过时的
git stash save,它已被标记为 deprecated - 查看时一眼定位:
git stash list输出直接包含你的-m内容,不用再git stash show stash@{0}展开看 - 团队协作中,没有备注的 stash 基本等于“技术垃圾”,没人敢动
不小心丢了未跟踪文件?补救和预防
如果你已经执行了裸 git stash,又删了未跟踪文件,Git 不会帮你找回来——因为它们压根没进 stash。但还有两条路:
- 检查文件系统:很多编辑器(VS Code、JetBrains)会自动保存本地历史或临时副本,路径类似
~/.local/share/Code/Backups/... - 用
git fsck --lost-found扫描 Git 对象库,可能找回已写入但未引用的 blob(成功率低,仅限刚删且没 gc 过) - 预防永远比补救强:把
git stash -u设为团队规范,或 alias 成git st,避免手滑
真正麻烦的不是 stash 本身,而是它默认不保存你“以为它该保存”的东西。每次执行前,先 git status 看清哪些是 untracked,再决定要不要加 -u —— 这个动作花不了两秒,但能省去半小时的 panic search。


















