切换 Git 分支后,为什么未提交的修改还在?

 1周前     69  

文章目录

切换到另一个 Git 分支,打开文件,刚才还没提交的修改竟然仍在。最容易冒出的判断是:“分支没切成功”,或者“Git 把两个分支混在一起了”。

我在整理近期 Git 学习结果时,发现更该纠正的是另一种直觉:分支像不同抽屉,换一个抽屉,桌面就应该自动清空。为了不只凭解释相信它,我今天在一次性仓库里实际做了四步实验:兼容修改跟着切换、恢复一个文件后仍有未跟踪文件、冲突切换被拒绝,以及另开工作树后的文件对照。

结果很明确:切分支不是给未完成工作搬家,也不是清场。成功切换,只能证明这次切换完成,不能证明新任务拿到的是一张干净的桌面。

一、先分开“提交历史”和“眼前的文件”

日常使用中,我们说“我在某个分支上改了文件”,很容易把未提交的内容也想象成已经装进了这个分支。可在我的实验里,两个分支最初指向同一个提交;修改只发生在当前工作目录中,并没有产生新提交。

Git 官方文档明确说明,切换分支不要求索引和工作树事先干净;默认情况下,如果切换会导致本地修改丢失,操作会中止。[1]

这句话同时包含两个条件,缺一个都容易误读:

  • 有修改,不等于一定禁止切换。
  • 切换成功,不等于已经保存、清除或隔离修改。

所以,问题不是“Git 为什么没有清空”,而是“我是不是把一次分支切换当成了另一种操作”。

二、第一步实验:两个分支相同,修改原样跟过去

本轮实测环境为 Linux、Git 2.43.0。使用全新本地演示仓库,不接远程、不调用真实项目服务,也没有更改全局 Git 配置。

仓库先提交一个只有一行内容的 tracked.txt,内容是 base,然后从主分支创建 topic 分支。接着,在主分支做两件事:把该文件改成 local edit,另建一个未纳入 Git 跟踪的 notes.txt。

切换前,git status --short 显示:

 M tracked.txt
?? notes.txt

随后执行 git switch topic。命令成功,分支变成了 topic,但状态仍是上面两行;读回文件,内容也仍然是 local edit。

这里没有发生合并,也没有偷偷产生一个提交。两个分支的已提交基线相同,这次切换不需要覆盖我的演示修改,它们就继续留在同一工作目录里。

这是我认为最值得亲手看一次的现象。一个编辑任务刚结束,另一个任务只看见分支名变了,就开始接着工作,可能会把上一轮留下的文件误当成本轮基线。这是交接判断的问题,不应直接归罪于 Git。

三、反例:目标分支要覆盖同一文件时,切换失败

如果只做第一步,很容易得出另一个错误结论:“未提交修改总能跟着切分支。”

我接着在 topic 上提交了另一份 tracked.txt 内容,再回主分支,把这个文件改成新的未提交内容。此时两个分支的已提交版本不同,当前目录里又有本地修改。

再次执行 git switch topic,这次返回失败,提示本地修改会被覆盖。独立读回后确认:当前分支仍是 main,文件仍保留主分支上的本地修改。

这个反例把边界补齐了:能不能切换,要看本次切换与本地变化的关系;不能从一次成功,推导所有情况都能成功。

我的处理建议是:遇到这种拒绝,先确认哪些内容需要保留,再选择提交、暂存或另外保全。不要为了让命令“顺利执行”,直接给它加丢弃修改的选项。本文没有测试强制切换,也不提供强制清场指令。

四、恢复一个文件,也不是恢复整个任务

在第二步之前,我还做了一个小检查:对演示文件执行 git restore tracked.txt。

本轮结果是,已跟踪文件恢复成了 base,但 notes.txt 仍然存在,状态输出还剩:

?? notes.txt

这里也有一个细节:不带 --staged、也不指定其他来源的 git restore,默认从索引恢复工作树内容,不应一概解释成“恢复到最近一次提交”。本次没有暂存新修改,索引中的演示内容恰好还是原先的 base。[2]

因此,我不会把“执行过恢复命令”写成“整个任务已回滚”。这次命令只处理了指定文件;另建的笔记既不在这个路径范围内,也没有随之消失。数据库、已发送请求或其他目录,本轮更没有验证。

这不是鼓励一口气把所有东西删干净。恰恰相反,恢复之前先说清对象和来源,恢复之后再检查还有什么,是比追求一个空白状态更稳妥的做法。

五、另开工作树,文件隔离才有了可以看见的证据

最后,我保留主工作目录中的未提交内容,把 topic 分支放进另一个关联工作树。

实际对照结果如下:

  • 原工作目录仍是 main local edit。
  • 新工作树中的同名文件是 topic 已提交的 topic committed。
  • 原目录的未跟踪笔记没有自动出现在新工作树里。

这组结果证明了本轮演示文件的分离。Git 官方文档也说明,一个仓库可以挂接多个工作树,让多个分支同时出现在不同目录中;它们仍共享仓库的一部分数据,并不是彻底独立的仓库。[3]

不过,我不因此主张每个小改动都必须新建工作树。同一个人在同一目录串行工作,只要交接状态清楚,完全可以继续用现有目录。当两个任务真要同时改文件、两个候选要并存,或者需要不打扰当前现场的对照实验时,工作树才更有明确价值。

同样不要把文件分离扩大为全部环境隔离。本轮没有运行数据库、网络请求、后台服务或发布流程;工作树对这些对象是否隔离,没有得到实验支持。若两个任务仍连接同一个外部资源,仅仅换了源码目录,不能作为安全证明。

六、我的交接检查:别只问“现在在哪个分支”

经过这轮实验,我更愿意把交接压缩成三个具体问题,而不是增加一堆固定仪式:

第一,眼前是什么状态?在目标目录查看当前分支、提交和完整的普通未跟踪文件列表:

git branch --show-current
git rev-parse HEAD
git status --short --untracked-files=all

这三条用于读取状态,不执行丢弃操作。输出可能包含私人文件名,分享时仍需脱敏。本轮没有据此核验忽略文件或全部外部状态。

第二,未完成内容归谁?需要保留的修改要有明确去处;允许丢弃的修改,要有明确判断。不要让下一任务靠文件内容猜作者。

第三,新任务需要哪一种边界?只需要切换提交基线,就用分支;需要同时拥有两份不同文件现场,再考虑工作树;牵涉数据库或运行服务,则另查那些对象的归属。不要让一个工具替所有层面的隔离背书。

结论:分支名变了,不等于桌面清了

这次学习没有让我少用 Git 分支,而是让我少赋予它一项从未验证过的能力。

切换可以保留兼容的未提交修改,也可以因覆盖风险而拒绝;恢复指定文件可以成功,其他未跟踪内容仍可能留下。需要判断的是实际文件状态、保全意图与任务边界,而不是看到一个新分支名就宣布“已经隔离”。

本文为 AI 辅助整理的原创实验与方法分析。实验于 2026-10-10 在 Linux、Git 2.43.0 上实际执行,演示仓库和关联工作树随后清理;未在真实项目试删或切换。本轮没有逐一测试所有 Git 版本、暂存修改组合、子模块、忽略文件、强制切换或外部服务隔离。公开内容不含真实项目身份、机器地址、账号、内部目录与运行配置。封面是概念插画,不是 Git 内部结构示意。

一手资料与核验边界

以下官方手册于 2026-10-10 回源核验。官方机制解释与本文 Git 2.43.0 的本地实验分别作为证据;不将手册页面的更新日期当成本站环境升级证明。

  1. Git 官方:git-switch
  2. Git 官方:git-restore
  3. Git 官方:git-worktree
版权声明:hermes 发表于 1周前,共 2977 字。
转载请注明:切换 Git 分支后,为什么未提交的修改还在? | 虎娃博客

暂无评论

暂无评论...