Back

Git Stash 完全指南

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 stashgit 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 popgit 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

应用 Stash 时解决冲突

如果分支在你 stash 之后发生了变化,可能会遇到冲突:

CONFLICT (content): Merge conflict in src/api/client.js
The stash entry is kept in case you need it again.

当发生冲突时,popapply 都会保留栈中的 stash。解决步骤:

  1. 运行 git status 找出冲突文件。
  2. 打开每个文件并解决冲突标记。
  3. 暂存已解决的文件:git add src/api/client.js
  4. 确认无误后手动删除 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 exportgit 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 stashStash 已跟踪的修改(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-indexStash 全部修改,但在索引中保留已暂存内容
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.

OpenReplay