Stash 在 checkout 之后看不到了

git stash pop 或切换分支后,stash 列表里的条目消失,改动找不到了。本文解释 stash 的存储机制,给出从 reflog 找回丢失 stash 的完整操作步骤。

你在 feature/login 分支上做了一些改动,执行 git stash 保存后切到了 hotfix/urgent 分支处理紧急问题。处理完后切回来,执行 git stash list,列表空了,或者发现 stash 条目数量不对。又或者 git stash pop 时遇到了冲突,处理冲突后 stash 就被自动删除了,但改动也丢了。Stash 的底层是 Git 对象,不会凭空消失——但它的引用(refs/stash)在某些操作后会被清除,找回需要借助 reflog。

常见原因

1. git stash pop 遇到冲突后未正确完成

git stash pop 遇到冲突时会把 stash 内容展开到工作区但保留冲突标记,同时从 stash 列表中删除该条目。如果此时工作区一片混乱或者手动清理了文件,stash 的 SHA 就变成了唯一找回手段。

怎么判断git reflog stash 若还有条目但 git stash list 为空,说明 stash 被 pop 后删除了。

2. git stash dropgit stash clear 误操作

手滑执行了 git stash drop stash@{0}git stash clear,把所有 stash 删掉了。这是人为操作,不是 checkout 导致的,但很多人在排查时忘记了这个可能性。

怎么判断git reflog stash 查看 stash reflog,若有 dropclear 的记录,说明是手动删除。

3. 误以为 stash 是分支隔离的

Stash 是全局的,在所有分支间共享。但有人切换分支后以为「这个分支的 stash 不见了」,实际上 stash 还在,只是列表里没有分支信息,需要用 git stash list 查看所有 stash 并手动识别。

怎么判断git stash list 查看全部条目,注意每个条目的 WIP on <branch> 信息,找到对应分支的 stash。

4. stash 在 merge 或 rebase 时被自动清理

某些 Git 工具(如 IDE 内置的 Git 客户端)在执行 merge 或 rebase 前会先把工作区改动 stash,操作完后自动 pop。若操作过程出错,工具可能没有正确恢复 stash,导致条目消失而改动丢失。

怎么判断:查看 IDE 的 Git 操作日志,或者 git reflog stash 看时间戳是否与 merge/rebase 时间吻合。

5. git clean -fd 删掉了 untracked 文件,stash 里保存的正是 untracked

默认 git stash 只保存追踪文件的改动,untracked 文件不被保存。但用 git stash -u 保存了 untracked 文件,stash pop 后文件恢复,随后 git clean -fd 又把这些文件删掉了,让人误以为 stash 没起作用。

怎么判断git stash show -p stash@{0} 查看 stash 内容是否包含 untracked 文件(会有 new file: 标记)。

6. 不同用户共享同一个工作目录,stash 被他人操作

在容器、共享开发机或 pair programming 场景里,多人共用同一个 Git 工作区,其他人执行了 git stash popgit stash drop

怎么判断git reflog stash 查看操作时间和触发者(若配置了 user.name)。

最短修复路径

Step 1:查看 stash reflog 找到所有曾经存在的 stash SHA

git reflog stash

输出类似:

abc1234 refs/stash@{0}: WIP on feature/login: 3fa1b2c add validation
def5678 refs/stash@{1}: WIP on feature/login: 3fa1b2c fix typo

即使 git stash list 为空,reflog 里依然保存着历史引用。

Step 2:查看某个 stash SHA 的内容

git stash show abc1234
# 查看详细 diff:
git diff abc1234^1 abc1234

确认是否是需要找回的内容。

Step 3a:用 stash apply 恢复(不从列表删除)

git stash apply abc1234

Step 3b:用 cherry-pick 恢复(把 stash 作为普通 commit 应用)

git cherry-pick abc1234

Step 4:如果 stash reflog 也清空了,用 fsck 找回

git fsck --unreachable | grep commit | awk '{print $3}' | xargs -I{} git show --stat {}

逐条查看输出,找到包含你改动的悬挂 commit SHA。

Step 5:确认恢复后清理

git status
# 确认改动已恢复到工作区后,决定是否重新 stash 或直接提交

预防建议

  • 重要的工作改动不要只靠 stash,应该及时提交到 WIP(Work In Progress)commit:git commit -m "WIP: 临时保存,待完善",checkout 前保存,回来后 git reset HEAD~1 恢复。
  • git stash pop 遇到冲突时先解决冲突再继续,不要强行清理工作区,否则 stash 对象虽在但引用丢失。
  • .zshrc.bashrc 中给 git stash clear 加确认别名:alias gsc='echo "确定要清除所有 stash?" && read && git stash clear'
  • 配置 reflog 保留期:git config --global gc.reflogExpire 180,延长找回 stash 的时间窗口。
  • 每次 stash 时加描述信息:git stash push -m "feat/login: 未完成的表单验证逻辑",方便日后在 list 里识别。
  • IDE 使用内置 Git 功能时了解其 stash 行为,部分 IDE(如 IntelliJ)在 pull 前会自动 stash,操作日志在 Git 工具窗口里查看。
  • 定期 git stash list 检查积压的 stash,超过 7 天的 stash 整理成正式分支或删除,避免列表混乱。

常见问答 (FAQ)

Q: git stashgit stash save 有什么区别? A: git stash save "message" 是旧语法,新版 Git 推荐用 git stash push -m "message"。功能相同,push 额外支持 --pathspec 指定只 stash 部分文件:git stash push -m "desc" -- path/to/file

Q: git stash popgit stash apply 哪个更安全? A: git stash apply 更安全,因为它不会删除 stash 列表里的条目,应用成功后可以手动 git stash drop 删除。git stash pop 在遇到冲突时会删除条目但留下冲突工作区,容易搞混。日常使用建议用 apply + 手动 drop

Q: stash 是否跨 clone 共享? A: 不会。Stash 存储在本地 .git/refs/stash,不会被 git push 推送到远端,也不会被 git fetch 拉取。Stash 是纯本地状态,只在同一个仓库克隆内有效。

Q: 我想只 stash 某几个文件,而不是全部改动,怎么做? A: git stash push -m "desc" -- path/to/file1 path/to/file2,只把指定路径的改动 stash,其他文件保留在工作区。

相关阅读

标签: #git #version-control #排查