在空目录里运行下面这段命令,大约十五秒,就是这篇文章的全部内容。
$ git init -q . && git config user.email t@t && git config user.name t$ printf 'v1\n' > a.txt && git add . && git commit -qm base$ printf 'v2-staged\n' > a.txt && git add a.txt # 暂存一个版本$ printf 'v3-worktree-only\n' > a.txt # 然后继续编辑$ git show :a.txtv2-staged$ cat a.txtv3-worktree-only$ git commit -qm 'commit with pathspec' -- a.txt$ git show HEAD:a.txtv3-worktree-only$ git status --porcelain$
暂存的版本没有进入提交。它哪里都不在了。工作区是干净的,之后没有任何迹象表明曾经有过选择。
这是文档记录的行为,不是bug。在git commit时指定路径,意思是"按这些路径当前的状态提交",索引对它们被绕过了。测试环境为git 2.54.0.windows.1。
那个看起来能解决问题的变体也不行:
$ git commit -qm 'include mode' -i a.txt$ git show HEAD:a.txtv3-worktree
-i参数会把索引的其余部分加入提交,但指定的路径仍然从磁盘读取。
两个真实事故,同一种形状
作者自己的仓库里,相隔一天的两个提交,出现了同样的形态。
b3a9236是在另一位作者同时编辑同一棵树时,用指定路径方式提交的。十一个文件,549行插入。其中一些插入是那位作者还没写完的行。指定路径并没有阻止问题,因为路径是对的,版本却不对。
ba03c95是更粗糙的同类。它用git add加了一个目录,捕获了一个74行的审计文件:
$ git show ba03c95:DB/.../AUDIT_FINAL_2_PROOF_TEST_SYNC_2026-09-05.md | wc -l74$ wc -l < DB/.../AUDIT_FINAL_2_PROOF_TEST_SYNC_2026-09-05.md198
提交的74行是现在198行的字节完全一致的前缀。这就是一个文件在句子写到一半时被拍照的样子。没有任何失败;提交是干净的,它的提交信息描述的工作只完成了37%。
问题的根源不在Git
作者改用指定路径,正是因为一次宽泛的git add把半成品扫了进去。这个举动是合理的,但它只修好了问题错误的一半。pathspec收窄了哪些文件进入提交,却对每个文件的哪个版本进入提交只字不提——而在这个问题上,它选择了最不谨慎的答案。
这下面才是真正的错误,跟git完全无关:作者让两个写作者在同一棵树里工作,然后去找一个能让这件事变得安全的命令。没有这样的命令。git stash更糟;作者试过一次,面对并发的working tree,文件能拿回来只是因为另一边在间隔期内没有写入。
现在作者的做法很无聊:刻意暂存,然后git commit不带任何pathspec,这样落地的内容恰好就是自己看过的东西。一棵树只有一个写作者。
热门跟贴