first-contributions 进阶指南:用 Git Stash 暂存工作进度,安全切换分支不丢代码
【免费下载链接】first-contributions🚀✨ Help beginners to contribute to open source projects项目地址: https://gitcode.com/gh_mirrors/fi/first-contributions
本文是 first-contributions 开源贡献入门仓库 docs/additional-material/git_workflow_scenarios/stashing-a-file.md 及其多语言版本(如 白俄罗斯语译文、简体中文译文)的技术导读。当你完成仓库 README.md 中的 fork → clone → 修改 → 提 PR 基本流程后,在真实协作场景中会频繁遇到"代码写一半却要切换分支"的困境,本文的git stash系列命令就是解决这一问题的标准答案。读完本文,你将掌握git stash、git stash list、git stash apply、git stash pop、git stash drop、git stash branch的完整用法,以及用反向补丁"撤销应用"与自定义 Git 别名的进阶技巧。
问题场景:未提交的更改挡住了切换分支
假设你在一个大型代码项目中工作,正在master分支上修改代码,突然需要切换到另一个分支处理紧急任务。此时代码尚未写完、也没有配套测试,你大概率不想提交这些不完整、可能无法通过 CI 的更改。但 Git 的分支切换机制要求工作区保持整洁,否则不允许你直接切换,以免破坏开发流。
怎么办?既避免一次多余的 commit,又能自由跳转分支?这正是git stash(暂存/搁置)机制要解决的问题。它相当于把当前工作区"存档"到一个栈中,还你一个干净的工作目录,等你需要时再恢复。
在深入命令之前,先建立与仓库其他进阶文档的关联认知:
- additional-material 索引 列出了本仓库全部进阶 Git 主题(修改提交、配置 Git、保持 fork 同步、移除文件、删除分支、解决合并冲突、revert、squash、撤销本地提交等),stash 教程正是其中之一;
- 为什么需要频繁切换分支,可参考 为什么使用分支:分支让多人、多特性并行开发而不互相干扰,切换分支因此成为日常操作,stash 则是切换时的"安全气囊"。
暂存你的工作(Stashing)
第一步:用 git status 确认当前更改
假设你在项目某个分支上修改了若干文件。运行git status,可以看到更改的分布情况:
$ git status # On branch master # Changes to be committed: # (use "git reset HEAD <file>..." to unstage) # # modified: index.html # # Changes not staged for commit: # (use "git add <file>..." to update what will be committed) # # modified: lib/simplegit.rb #注意输出里有两类更改:index.html已经git add进入暂存区(index/staging area),lib/simplegit.rb还停留在工作区(working directory)。git stash默认会同时保存这两类更改,这也是后面--index参数存在的意义。
第二步:git stash 压栈保存
现在你想切换分支,但暂时不想提交任何更改,于是执行git stash,把当前改动推入一个"stash 栈":
$ git stash Saved working directory and index state \ "WIP on master: 049d078 added the index file" HEAD is now at 049d078 added the index file (To restore them type "git stash apply")输出中的WIP on master: 049d078 added the index file表示:这是master分支上基于提交049d078的"工作进行中(Work In Progress)"状态。Git 会提示你用git stash apply来恢复。
此时再次运行git status,工作目录已经干净:
$ git status # On branch master nothing to commit, working directory clean第三步:git stash list 查看栈中所有存档
现在你可以切换到任意分支继续工作,stash 的内容以**栈(stack)**的形式保存在本地。使用git stash list查看栈中已有哪些存档:
$ git stash list stash@{0}: WIP on master: 049d078 added the index file stash@{1}: WIP on master: c264051 Revert "added file_size" stash@{2}: WIP on master: 21d80a5 added number to log每个条目以stash@{编号}标识,stash@{0}永远是最近一次 stash。注意输出格式与git reflog相似,底层上都对应本地引用(ref),git stash系列命令正是围绕这些引用操作(例如后文git stash branch输出中的Dropped refs/stash@{0})。
第四步:git stash apply 恢复更改
想重新应用刚才保存的更改,使用git stash apply。默认恢复最近一次的 stash:
$ git stash apply # On branch master # Changes not staged for commit: # (use "git add <file>..." to update what will be committed) # # modified: index.html # modified: lib/simplegit.rb #可以看到,Git 重新改回了你在保存 stash 时未提交的文件。如果想应用栈中其他条目,可以显式指定名称:
git stash apply <stash-name>把<stash-name>替换为具体的条目名,例如git stash apply stash@{1}。
两个重要事实(原文档特别强调):
- 本示例中,你是在干净的工作目录下、在保存 stash 的同一分支上应用 stash,但这两种条件都不是必须的。你完全可以在 A 分支保存 stash、切到 B 分支后再应用;即使应用时工作目录存在未提交更改也可以,只是如果某些内容无法干净地应用,Git 会抛出合并冲突(merge conflict),需要按 解决合并冲突 的流程手工处理——冲突标记、
git add标记已解决、再git commit。 git stash apply恢复的是文件内容,但不会恢复暂存状态。对比上面的输出会发现:index.html在保存前是 staged(Changes to be committed),应用后却变成了 unstaged。要让暂存状态一并恢复,需要加--index参数:
$ git stash apply --index # On branch master # Changes to be committed: # (use "git reset HEAD <file>..." to unstage) # # modified: index.html # # Changes not staged for commit: # (use "git add <file>..." to update what will be committed) # # modified: lib/simplegit.rb #加上--index后,输出与 stash 之前完全一致:index.html回到暂存区,lib/simplegit.rb保持未暂存——你回到了"原来的位置"。
第五步:git stash drop 与 git stash pop
apply只负责应用,stash 条目仍保留在栈中(方便反复应用)。想从栈中删除某个条目,用git stash drop并指定名称:
$ git stash list stash@{0}: WIP on master: 049d078 added the index file stash@{1}: WIP on master: c264051 Revert "added file_size" stash@{2}: WIP on master: 21d80a5 added number to log $ git stash drop stash@{0} Dropped stash@{0} (364e91f3f268f0900bc3ee613f9f733e82aaed43)输出末尾的364e91f3f268f0900bc3ee613f9f733e82aaed43是这条 stash 对应的提交对象哈希,可作为审计依据。
如果你希望"应用最近一次 stash并同时从栈中删除",可以用git stash pop——它等价于git stash apply+git stash drop的组合,是最常见的恢复用法:
git stash pop取消应用已应用的 Stash(Un-applying)
有些场景下,你应用了 stash、做了一些工作,但之后想撤销那些原本来自 stash 的更改。Git 并没有内建的git unapply命令,但可以通过"取出 stash 对应的补丁,再反向应用"来实现:
$ git stash show -p stash@{0} | git apply -R命令拆解:
git stash show -p stash@{0}:以补丁(patch)形式显示stash@{0}的完整差异;git apply -R:将补丁**反向(Reverse)**应用到当前工作区,等于把 stash 带来的更改逐行回滚。
同样地,不指定 stash 名称时 Git 默认取最新一条:
$ git stash show -p | git apply -R由于这条组合命令较长,原文档推荐把它配置成 Git 全局别名,等效于新增一个stash-unapply子命令:
$ git config --global alias.stash-unapply '!git stash show -p | git apply -R' $ git stash apply $ #... work work work $ git stash-unapply注意别名值开头的!表示这是一条Shell 命令而非普通 Git 子命令,Git 会交给 shell 执行整段管道。之后每次需要"撤销 stash 应用"时,直接敲git stash-unapply即可。
从 Stash 创建新分支
最后一个常见困境:你把某份工作 stash 后搁置了一段时间,又在原分支上继续开发。当你想重新应用这份 stash 时,如果apply试图修改的文件已经被后续开发改过,就会产生合并冲突,需要手工解决,很麻烦。
更省事的方式是用git stash branch:它会自动完成四件事——①创建一个新分支;②检出你当时 stash 工作所处的那个提交;③在该提交上重新应用 stash 内容;④如果应用成功,自动丢弃这条 stash。
$ git stash branch testchanges Switched to a new branch "testchanges" # On branch testchanges # Changes to be committed: # (use "git reset HEAD <file>..." to unstage) # # modified: index.html # # Changes not staged for commit: # (use "git add <file>..." to update what will be committed) # # modified: lib/simplegit.rb # Dropped refs/stash@{0} (f0dfc4d5dc332d1cee34a634182e168c4efc3359)输出末尾的Dropped refs/stash@{0}印证了"应用成功即自动删除"的行为。由于新分支是基于 stash 时的旧提交创建的,与当前分支的后续改动完全隔离,通常能避免冲突;而暂存状态(index.html已 staged)也会一并保留。这是"轻松恢复被 stash 的工作并在新分支上继续开发"的绝佳捷径——非常适合在开源贡献场景中把搁置的试验性改动单独拉出来验证。
实战小结:把 stash 融入日常贡献工作流
结合 first-contributions 仓库的整套进阶材料,可以梳理出 stash 在真实贡献流程中的定位:
| 场景 | 推荐命令 | 效果 |
|---|---|---|
| 切换分支前保存未提交改动 | git stash | 工作区变干净,改动入栈 |
| 查看已保存的改动 | git stash list | 按stash@{n}列出 |
| 恢复最近改动(保留栈内条目) | git stash apply | 只应用,不删除 |
| 恢复改动并还原暂存状态 | git stash apply --index | 暂存区/工作区都还原 |
| 恢复最近改动并出栈 | git stash pop | 应用后自动删除该条目 |
| 删除指定条目 | git stash drop stash@{n} | 手动清理栈 |
| 撤销已应用的 stash 改动 | git stash show -p \| git apply -R | 反向补丁回滚 |
| 从 stash 开辟新分支继续开发 | git stash branch <name> | 建分支 + 应用 + 自动删除 |
需要提醒的边界与注意事项:
git stash默认只暂存已跟踪文件的改动,新建但从未git add过的文件默认不会被包含(可结合git add -u或git stash -u处理未跟踪文件,视实际需求取舍);- stash 保存在本地,不会随
git push上传,跨机器恢复需要自行处理; - 若在应用 stash 时遭遇冲突,参照 解决合并冲突 的标记识别、
git add、git commit流程解决; - stash 与分支、reset 等操作组合使用时,建议先
git stash list确认栈内容再执行删除类命令,避免误丢工作; - 频繁切换分支与保持分支同步也是协作常态,可配合 保持 fork 与上游同步 与 撤销本地提交 一起学习,形成完整的本地仓库管理能力。
原文档英文版位于 stashing-a-file.md,并已翻译为 白俄罗斯语、简体中文、斯洛文尼亚语、土耳其语 等多个语言版本,读者可按需对照阅读。掌握 stash,你就能在开源协作中从容应对"代码写一半就要切换分支"的高频场景,不丢一行代码。
【免费下载链接】first-contributions🚀✨ Help beginners to contribute to open source projects项目地址: https://gitcode.com/gh_mirrors/fi/first-contributions
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考