一个共享工作区里躺着20个文件的暂存改动,56处新增、1305处删除。第一反应多半是:谁动了我的代码?但把文件和提交历史摆在一起看,这些改动反映的是大约三周前的状态,不是新编辑。

这就是整件事最反常识的地方:diff 看起来像一场大手术,实际上是一具没被清理的旧尸体。处理方式也不复杂——先把旧状态存到分支上,再用reset --hard和pull --ff-only把工作区更新到最新。

为什么没把 diff 当成新改动

第一步是拿暂存内容和磁盘上的文件做对比。索引是改动进入下一次提交前的存放处。对比结果是:工作区和索引之间没有差异,也没有未跟踪文件。

也就是说,这不是"旧改动只残留在索引里"的情况。磁盘上的文件本身就是旧状态。diff 里显示为删除的那些文件,在磁盘上同样不存在。

于是可以停止把暂存内容当作新工作了。三个缺失的文件在事发前两天就已经被并入。它们分别覆盖测试框架、用量测量和上下文提示。

主分支的 HEAD(指向当前提交的名字)自事发当天的合并提交起,已经卡了大约三周。单看 diff 的大小解释不了发生了什么。把提交时间线和文件是否存在放在一起看,才能判断这是更早状态的残留,而不是最近的编辑。

只做 stash 为什么救不回工作区

如果 stash 完就停手,主工作区里仍然是那棵旧树。从这个状态构建,用的还是旧版本,下一次回滚照样发生。

stash 的作用是把残留物保存下来、不至于丢失。它和"把工作区恢复到当前状态"是两件事。只做其中一步,共享工作区对下一个任务来说仍然不可用。

所以这里把"保存旧状态"和"把主工作区更新到最新版本"当成同一个恢复流程来处理。

旧改动怎么存,主分支怎么更新

先把剩余的工作作为一个提交保存到有名字的分支上,这个分支充当一条独立工作线的标记。保留备份分支意味着内容之后还能复查。这个分支没有被删除。

接着运行reset --hard,让工作区与 HEAD 对齐。然后用pull --ff-only更新。结果是一次196个提交的快进,没有产生合并提交。

stash 同样被保留下来,没有删除。这样旧内容留住了,主工作区也往前走了。

为什么第四次 stash pop 才失败

事后了解到的情况是:同样的步骤当天成功过三次,第四次才第一次失败。失败时显示的是The stash entry is kept。至于为什么偏偏第四次失败,原因无法确定,也就不能声称知道原因。

这次能确认的是:共享工作区里仍然留着大约三周前的状态,磁盘上的文件和索引一样陈旧。同时也学到一点——光靠 stash 保存改动,恢复并不算完成。

通过保存旧状态来"恢复",和通过把主工作区带到最新来"更新",可以当作两个独立步骤。但在共享工作区里,恢复意味着两步都做完。stash 之后更新了主树,才没有带着旧树进入下一次构建。

  • git diff --shortstat看 diff 规模。
  • 检查工作区与索引的差异,并留意未跟踪文件。
  • 对比文件是否存在、改动何时并入、以及 HEAD 的位置。
  • 把旧改动保存到分支,再运行 reset --hard 和 pull --ff-only。
  • 分支和 stash 都留着,方便之后复查。