每次合并冲突都以同样的方式收场:改完文件,然后暂存。而恰恰是这个时刻,一个小事故发生了——大多数人出于习惯敲下 git add -u 或 git add .。这两条命令会暂存所有被修改的文件,而不只是你刚刚解决冲突的那几个。
这种坑我踩过不止一次。某个无关文件里留下的 set -x 或调试打印,悄悄跟着冲突解决一起进了合并提交。没人会发现,因为没人会逐行去读合并提交。几周后有人问:为什么启动脚本突然把每条命令都打印出来了?
Git 2.56 终于给出了正经答案:git add --resolved。它只暂存处于冲突状态的文件,并且只要文件里还残留冲突标记,就拒绝暂存任何东西。工作区里的其他内容原封不动。
它到底做了什么
这条命令的行为可以拆成几条清晰的规则:
- 只看未合并路径,也就是 git status 里的 UU、AA、UD 这些条目
- 逐个检查这些文件是否还残留冲突标记
- 只要有文件还带标记,就拒绝暂存任何内容,并列出仍需处理的文件
- 全部干净,才把它们一起暂存
- 不在冲突中的已跟踪文件,永远不会被暂存
- 可以用路径收窄范围,比如 git add --resolved config.ini
- 不能和 -u 或 -A 组合使用
换句话说,过去的安全做法是手动敲每一个冲突路径。但即便那样,也没有任何机制检查你是否真的清掉了所有冲突标记。现在这一步被工具接管了。
2.56 里还有哪些顺手的变化
这是一个改动很多的版本,翻完发布说明后,挑几个对日常干活最有用的:
- git branch --delete-merged:删掉那些工作已经进入所跟踪上游的本地分支。它带安全规则——保留当前检出的分支、尚未合并的分支,以及你标记为"保留"的分支
- git bisect --reset-when-found:一旦找到坏提交,自动结束二分会话
- git history drop:从历史中间移除某个提交,依赖它的分支会跟着调整
- git refs create/update/delete/rename:一套可读且安全的 ref 操作方式,不会静默覆盖,更新采用比较并交换
- git replay --linearize:在内存里把带合并的分支压平,裸仓库里也能用
- git log --graph:不再把互不相关的历史画得像连在一起
- [includeIf "worktree:..."]:按目录配置,对链接工作树同样生效
- fetch.followRemoteHEAD:一个设置,让每个仓库里的 origin/HEAD 与远端保持同步
- git repack --drop-filtered:在部分克隆里回收磁盘空间
还有些小细节挺讨喜。比如 git push 敲错时现在会给提示:
$ git push origin/main
fatal: 'origin/main' is not a valid push target
hint: Did you mean to use: git push origin main?
另外 -h 在大多数命令里现在以退出码 0 结束(以前是 129),所以脚本里的 git status -h 不再看起来像个错误。
怎么拿到 2.56
Linux 发行版需要一些时间才会带上新版 Git。在我的 Fedora 上,系统 Git 还是 2.55。我不想从源码编译,于是用 mise 配合 conda 后端装了一个预编译版本:
# mise.toml
[tools]
"conda:git" = "2.56.0"
[settings]
experimental = true # conda 后端仍标记为实验性
然后执行 mise trust、mise install,再用 mise exec -- git --version 验证,输出 git version 2.56.0。它不会替换系统 Git,新版本只在这个带 mise.toml 的项目里生效。
从空目录走一遍
拿一个小项目试最直观。下面每一步都在真实终端里用 Git 2.56.0 跑过,冲突在 vim 里解决,和平时干活一样。提示符会显示目录和当前分支,比如 shop (main) $,合并进行中则是 shop (main|MERGING) $。你的提交 ID 会和我的不同,这很正常。
第一步,建仓库:mkdir shop、cd shop、git init、git status,确认还没有任何提交。
第二步,写三个小文件:服务器配置、变更日志、启动脚本。git status --short 会把它们都标成未跟踪(??)。
第三步,首次提交:git add config.ini CHANGELOG.md app.sh,然后 git commit -m "Initial version",再用 git log --oneline 看一眼。这个提交(我这边是 a25ceb7)是两个分支的共同起点。
第四步,开功能分支:git switch -c feature,把服务器端口从 8080 改到 9090,并在变更日志里加一行。git branch 会用 * 标出当前分支,git diff 能在提交前看到这两处改动。
第五步,与此同时 main 分支上有人把 worker 数量提到了 8。这一行紧挨着端口那一行,Git 没法把两处改动自动合并——冲突就此产生。而这,正是 git add --resolved 要登场的地方。
热门跟贴