Git Stash 完全指南
你正在开发某个功能,深陷一场重构之中,这时队友突然找你处理生产环境的一个严重 bug。你的工作目录里堆满了尚未完成的修改,既不想提交,又无法带着这些未提交的改动切换分支。
这正是 git stash 的用武之地。本指南将介绍所有你真正用得上的 git stash 命令、如何处理冲突,以及让 stash 操作变得自然而非冒险的工作流技巧。
核心要点
git stash会把未提交的修改保存到本地栈中,并将工作目录恢复到与HEAD一致的状态,让你能够在不提交半成品的情况下切换上下文。- 默认情况下,stash 只包含已跟踪的修改。使用
-u可包含未跟踪文件,使用-a可包含被忽略的文件。 - 当你希望恢复修改并清除 stash 时使用
git stash pop;当你希望保留 stash 以便复用时使用git stash apply。 - 当应用 stash 时冲突难以处理时,
git stash branch会基于原始提交创建一个新分支,并干净地应用 stash。 - 始终用
-m添加描述信息,保持 stash 列表精简,并将 stash 视作临时存储,而非备份。
什么是 Git Stash?
git stash 会将你未提交的修改(包括已暂存和未暂存的)保存到本地栈中,并将工作目录还原到与最近一次提交(HEAD)一致的状态。之后你可以在同一分支或其他分支上恢复这些修改。
Stash 是仅本地存储的。它不会通过 git push 推送到远程仓库,也不会与队友共享。
默认会被 stash 的内容:
- 已暂存的修改(索引)
- 已跟踪文件的未暂存修改
默认不会被 stash 的内容:
- 未跟踪文件(尚未添加到 Git 的新文件)
- 被忽略的文件
核心 Git Stash 命令
使用 git stash push 保存修改
git stash
直接运行 git stash 是 git stash push 的简写形式,这是获得干净工作目录的最快方式。
添加描述信息,以便日后了解其内容:
git stash push -m "WIP: refactor auth middleware"
注意: 旧版的
git stash save语法已被弃用。请改用git stash push。
Stash 未跟踪文件
默认情况下,尚未暂存的新文件不会被处理。使用 -u 可将它们包含进来:
git stash push -u -m "WIP: new login component"
如果还想 stash 被 .gitignore 匹配的文件,可以使用 -a(--all)。这种情况很少见,但当被忽略的文件会干扰分支切换或构建时会很有用。
Stash 特定文件
可以通过 pathspec 仅 stash 指定文件:
git stash push -m "WIP: api changes" -- src/api/client.js src/api/utils.js
这样工作目录中其他文件不会受到影响。
查看 Stash 列表
git stash list
输出:
stash@{0}: On main: WIP: refactor auth middleware
stash@{1}: WIP on feature/login: 9ab3c12 Add login form
Stash 按后进先出(LIFO)的顺序存储在栈中。stash@{0} 始终是最近的一个。
应用前检查 Stash
git stash show stash@{0} # 摘要
git stash show -p stash@{0} # 完整 diff
Git Stash Pop 与 Apply:该用哪一个?
这是关于 git stash 命令最常见的困惑点之一。
git stash pop | git stash apply | |
|---|---|---|
| 恢复修改 | ✅ 是 | ✅ 是 |
| 从栈中移除 | ✅ 是(仅当应用无冲突时) | ❌ 否 |
| 发生冲突时 | 保留在栈中 | 保留在栈中 |
| 最适用于 | 一次性恢复 | 应用到多个分支 |
使用 pop:当你不再需要这个 stash,只想恢复修改时。
使用 apply:当你想把同一个 stash 应用到多个分支,或者想在确认移除之前先测试效果时。
应用指定的 stash:
git stash pop stash@{1}
git stash apply stash@{1}
使用 --index 恢复暂存状态
默认情况下,应用 stash 时所有修改都会作为未暂存的修改恢复。如果你想保留 stash 时的暂存/未暂存区分,使用 --index:
git stash pop --index
Discover how at OpenReplay.com.
应用 Stash 时解决冲突
如果分支在你 stash 之后发生了变化,可能会遇到冲突:
CONFLICT (content): Merge conflict in src/api/client.js
The stash entry is kept in case you need it again.
当发生冲突时,pop 和 apply 都会保留栈中的 stash。解决步骤:
- 运行
git status找出冲突文件。 - 打开每个文件并解决冲突标记。
- 暂存已解决的文件:
git add src/api/client.js。 - 确认无误后手动删除 stash:
git stash drop stash@{0}。
当冲突变得棘手时:使用 git stash branch
如果冲突错综复杂,git stash branch 是最干净的应急方案。它会基于你 stash 时的精确提交创建一个新分支,然后在该分支上应用 stash。由于分支起点正是创建 stash 时的提交,这通常能避免冲突:
git stash branch fix/auth-refactor stash@{0}
如果应用成功,stash 会被自动删除。
管理与清理 Stash
git stash drop stash@{0} # 删除指定的 stash
git stash clear # 删除所有 stash(不可恢复)
⚠️
git stash clear是永久性的,无法撤销。运行前请再三确认。
进阶用法:部分 Stash 与仅 Stash 已暂存内容
交互式补丁模式允许你选择要 stash 的代码块:
git stash push -p
Git 会逐个询问你是否要 stash 每个修改。当一个文件中既有相关又有无关的修改时,这非常有用。
仅 stash 已暂存的修改(Git 2.35 及更高版本):
git stash push --staged
这会 stash 索引中的内容,同时保留未暂存的修改——便于在不丢失当前进度的情况下,隔离出你想搁置的修改。
Stash 其他修改但保留已暂存内容在索引中:
git stash push --keep-index
需要注意的是,--keep-index 仍然会 stash 已暂存和未暂存的所有修改;只是在 stash 之后,已暂存的修改仍保留在工作目录中。
恢复已删除的 Git Stash
如果你不小心 drop 或 clear 了 stash,它们可能仍然能作为悬挂提交在仓库中找回。Stash 条目以合并提交的形式存储,可以这样搜索:
git fsck --unreachable | grep commit | cut -d' ' -f3 | xargs git log --merges --no-walk --grep=WIP
这会列出看起来像 stash 条目的不可达合并提交。一旦找到所需的提交哈希,使用以下命令恢复:
git stash apply <commit-hash>
恢复并不能保证成功——Git 的垃圾回收器最终会清理不可达对象——所以请尽快行动。
在不同机器之间传输 Stash
最新版本的 Git 新增了 git stash export 和 git stash import,允许通过常规的 fetch 和 push 工作流传输 stash。
对于较旧的 Git 版本,或简单的一次性传输,实用的替代方案仍包括:
- 导出为补丁:
git stash show -p stash@{0} > my-stash.patch,然后在其他地方用git apply my-stash.patch应用。 - 提交到一次性分支: 创建一个分支,提交修改,推送到远程,然后在另一台机器上拉取并重置。
对于一次性传输,补丁通常是最简单的方式。
Git Stash 最佳实践
- 始终添加描述信息。
git stash push -m "WIP: 做什么和为什么"能让你免于面对一堆匿名的WIP on main条目。 - Stash 是临时存储,不是备份。 当工作达到一个合理的检查点时,请提交它。
- 保持 stash 列表精简。 定期审视并删除不再需要的 stash。
- pop 之前先 pull。 在恢复 stash 之前更新分支,可以减少冲突的可能性。
- 不确定时优先用
apply而非pop。 在确认一切正常后,你随时可以手动删除 stash。
速查表:Git Stash 命令
| 命令 | 功能 |
|---|---|
git stash | Stash 已跟踪的修改(push 的简写) |
git stash push -m "msg" | 带描述信息的 stash |
git stash push -u | 包含未跟踪文件 |
git stash push -a | 包含未跟踪和被忽略的文件 |
git stash push -- <path> | 仅 stash 指定文件 |
git stash push -p | 交互式选择要 stash 的代码块 |
git stash push --staged | 仅 stash 已暂存的修改 |
git stash push --keep-index | Stash 全部修改,但在索引中保留已暂存内容 |
git stash list | 列出所有 stash |
git stash show -p stash@{n} | 显示某个 stash 的完整 diff |
git stash pop | 应用最新 stash 并将其删除 |
git stash apply stash@{n} | 应用 stash 但不删除 |
git stash pop --index | 恢复已暂存/未暂存状态 |
git stash drop stash@{n} | 删除指定 stash |
git stash clear | 删除所有 stash |
git stash branch <name> [stash] | 从 stash 创建分支 |
结语
git stash 是那种你不真正需要时觉得可有可无,而一旦需要就成为日常工作流一部分的命令。关键习惯是:始终为 stash 命名,保持列表整洁,当冲突让简单的 pop 变得不切实际时,记得使用 git stash branch。用得好的话,stash 能让你的 Git 历史保持整洁,上下文切换毫无负担。
常见问题
不包含。默认情况下,git stash 只保存已跟踪的修改——即 Git 已知文件的已暂存和未暂存修改。尚未通过 git add 添加的新文件会留在工作目录中。要包含它们,请使用 git stash push -u 或 --include-untracked。如果还要 stash 被 .gitignore 匹配的文件,请使用 -a 或 --all。
两者都会将 stash 的修改恢复到工作目录。区别在于之后会发生什么。git stash pop 在应用成功后会将 stash 从栈中移除,而 git stash apply 则会保留它,以便你稍后或在其他分支上重新应用。如果 pop 期间发生冲突,stash 会被保留,这样你不会丢失工作。
如果及时行动,通常可以。Stash 条目以合并提交的形式存储,被删除的 stash 会变成不可达对象,Git 的垃圾回收器最终会清理它们。运行 git fsck --unreachable 来查找悬挂提交,通过 WIP 信息识别出 stash,然后用 git stash apply 加上提交哈希将其恢复。
不会。Stash 严格属于本地,存放在仓库的 refs/stash 引用中。它们绝不会被包含在 git push、git fetch 或 git pull 中。如果你需要与队友共享进行中的工作,请将其提交到临时分支并推送该分支,或者使用 git stash show -p 将修改导出为补丁。
Gain control over your UX
See how users are using your site as if you were sitting next to them, learn and iterate faster with OpenReplay. — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data. Check our GitHub repo and join the thousands of developers in our community.