拆开 git rebase:feature 上的 3 个提交变成 3 个新提交(parent 链和 tree 都变,作者保留、提交者变成执行者);旧提交靠 ORIG_HEAD 和 reflog 保留,git range-diff 把新旧一一配对;冲突停下时 .git/rebase-merge 记录 head-name、onto、orig-head、todo/done 与进度;上游已有的等价补丁(patch-id 相同)自动跳过;无事可做时 up to date。
实测 git rebase -i 的 todo 列表:pick/reword/edit/squash/fixup/exec/drop 等命令;改顺序和 drop;squash 拼接说明、fixup 丢弃说明;edit 停下来 amend 后继续;--exec 在每个提交后插入任务并在失败处停下;commit --fixup 加 --autosquash 自动排序;没被修改的前缀提交 hash 保持不变。
实测 cherry-pick 与 patch-id:cherry-pick 保留作者、换提交者与 parent,-x 追加来源;它等价于以 parent 为基础的三方合并,与 git merge-tree 的结果 tree 一致;patch-id 是忽略行号的补丁指纹,同一补丁摘到别处不变,多改一个字符就变,--stable 忽略空白;git cherry 与 log --cherry-pick 靠它识别等价提交;手工解决冲突后 patch-id 变化;补丁已存在时 cherry-pick 变空。
实测 rerere:冲突时记录 preimage,提交时记录 postimage,rr-cache/<指纹>/ 里的目录名是排序后的两段冲突内容的 SHA-1(自己算出的值与目录名完全一致);同样的冲突再出现时自动套用并提示 Resolved using previous resolution,但默认不自动 add;merge、cherry-pick、rebase 都认;rerere forget 删掉记录的解法。
实测撤销一个合并提交:git revert 直接报错 is a merge but no -m option was given;-m 1(以 main 为主线)撤销 feature 带来的东西,-m 2 反过来撤销 main 带来的东西;revert 之后再合并同一个分支,git 认为已合并过,只带来之后新增的提交;要先 revert 那次 revert 才能恢复。
用同一个三区域各不相同的起点实测 reset:--soft 只动 HEAD,--mixed 动 HEAD 和暂存区,--hard 三个都动;--hard 丢的是从没 add 过的工作区改动,已提交或 add 过的仍能找回;--keep 与 --merge 遇到会被覆盖的本地改动就拒绝;带路径的 reset 只动暂存区;reset 只移动当前分支。
在 1000 个提交的历史上实测 git bisect:手工回答时剩余可疑提交数 499 → 249 → 124 → 62 → 30 逐步减半;bisect run 用 10 次测试找到第 637 个提交;100 个随机 bug 位置全部找对,测试次数 9~10(平均 9.95);非线性历史按“可疑集合折半”选提交;脚本退出 125 可跳过无法测试的提交。
实测 git blame 的搬运检测:默认把挪动的行算挪动者;-M 追踪同一文件内移动的行(每行至少 20 个字母数字,-M5 降低阈值);-C 追踪同一提交里被修改的其它文件,-C -C 还找新建的文件;--ignore-rev 与 .git-blame-ignore-revs 让格式化提交不再“偷走”整个文件的 blame。
对比 git diff 的四种算法:在一个随机搜出的例子里 myers/minimal/histogram 给出 +6 −1,patience 给出 +8 −3;300 次随机编辑中 myers 与 minimal 合计 10,741 行改动(一致),patience 10,823,histogram 10,763,只有 14 次改动行数不全相同;20 万行文件上四种算法耗时 54~63 ms;diff.algorithm 默认 myers。
实测 git log 的历史简化:git log -- f 在 X、Y 两个分支都把 f 改成 2 时只显示 X1(合并 y 与两个 parent 都 TREESAME,只沿第一个 parent 走),--full-history 把 Y1 找回来,--simplify-merges 保留解释形状的合并,--sparse 保留全部 10 个;以及 --first-parent、--ancestry-path、--simplify-by-decoration,和 --graph 的画法。
实测 pickaxe:git log -S 找字符串出现次数发生变化的提交(改参数、挪行不算),-G 找 diff 里增删的行匹配正则的提交(都算);-S 默认是字面字符串,--pickaxe-regex 让它按正则;--pickaxe-all 保留命中提交的全部文件;3000 个提交的历史上 -S 54 ms、-G 72 ms、log -p | grep 86 ms。
实测四种本地 clone:路径方式默认硬链接 .git/objects(链接数 2,额外磁盘 0 MB,但在 APFS 上要创建 3003 个链接,耗时 1977 ms 反而最慢);--no-hardlinks 真复制(12.2 MB,1103 ms);file:// 走 fetch 协议重新打包成 1 个 pack(795 ms);--shared 用 alternates 指向源仓库(729 ms,0 MB),源仓库 prune 后 shared clone 的 fsck 报 invalid sha1 pointer。