TT Lab
开始
学习 学习路径 课程

Git 实战

变基与解冲突

在 TT Lab 中继续学习

目标

通过哈希确认 rebase 不是移动 commit,而是重写 commit;亲手完成冲突解决、中止,以及交互式 rebase 的 squash/drop。产物放在 /root/gitx3/ 下。

为什么重要

commit 对象中包含父 commit 哈希。父节点变化,commit 哈希必然变化,其余结论都源自这一点。如果其他人已在该 commit 上继续工作,重写会破坏对方历史。因此黄金法则的准确说法不是“推送后不要做”,而是“不要重写已有他人工作建立其上的 commit”。冲突反复出现也来自同一结构:merge 只比较三个点,而 rebase 会逐个重新应用 commit,每次都执行三方合并,所以rebase 不是一次合并,而是 N 次合并。

步骤

没有全局 git 身份。每个新仓库都要配置本地 user.email / user.name。

  1. 在 /root/gitx3/repo 创建仓库。在 main 上提交 3 个 commit,从那里创建 topic 分支并添加 2 个 commit;再回到 main 添加 1 个 commit,形成分叉状态(topic 总 commit 数至少 4)。在 /root/gitx3/diverged.txt 写三行:main_ahead=1、topic_ahead=2、merge_base=<두 브랜치의 공통 조상 해시>。
  2. 将 topic rebase 到 main 上。rebase 之前记录 topic 尾部哈希,并在 /root/gitx3/rebased.txt 写两行:before=<리베이스 전 해시>、after=<리베이스 후 해시>。两值必须不同,after 与当前 topic 尾部一致,且不得残留进行中的 rebase。
  3. 在 main 上以快进方式合并 topic。结果要求:main 的 merge commit 为 0 个,main 的总 commit 至少 6 个,main 与 topic 指向同一 commit。
  4. 在 /root/gitx3/conflict 创建新仓库。main 中放置 shared.txt;在 conflict-topic 分支修改该文件并提交,使其包含 from-topic;随后在 main 同一位置修改并提交,使其包含 from-main。将 conflict-topic rebase 到 main 会产生冲突,请保留双方变更。完成条件:rebase 已结束;索引无未解决路径;文件中无 <<<<<<< 等冲突标记;shared.txt 同时含 from-main 和 from-topic;工作树干净。
  5. 在 /root/gitx3/abort 创建新仓库,让 risky 分支和 main 分别以不同方式修改同一文件并提交。rebase 开始前将 risky 尾部哈希写入 /root/gitx3/abort-before.txt(仅哈希字符串)。启动 rebase,遇到冲突后中止。完成条件:无进行中的 rebase;risky 尾部与记录哈希完全一致;reflog 中有 rebase 记录。
  6. 在 /root/gitx3/squash 创建新仓库,在 feature/three 分支堆叠 3 个 commit(分别添加 s1.txt、s2.txt、s3.txt)。使用交互式 rebase 将3 个合为 1 个。完成条件:main..feature/three 只有 1 个 commit;标题包含 feat: combined;s1.txt/s2.txt/s3.txt 三个文件都存在。
  7. 在 /root/gitx3/drop 创建新仓库,在 feature/four 分支堆叠 4 个 commit:d1.txt、d2.txt、一个标题为 debug: temp log 且添加 debug.txt 的 commit,最后是 d3.txt。使用交互式 rebase 只丢弃调试 commit。完成条件:main..feature/four 有 3 个 commit;没有 debug: temp log commit;没有 debug.txt;d1/d2/d3.txt 均存在。
  8. 编写 /root/gitx3/rebase.md。必须包含:对 --force-with-lease 的说明及其与 --force 的区别;何时不能 rebase(包含共享、推送、其他人中任一表述);为什么哈希变化(包含哈希、重写中任一表述);并在 merge_commits_on_main= 行实际统计并写入 /root/gitx3/repo 的 main merge commit 数量。

参考

创建两个分叉分支

在 /root/gitx3/repo 创建仓库。在 main 上提交 3 个 commit,从那里创建 topic 分支并添加 2 个 commit;再回到 main 添加 1 个 commit,形成分叉状态(topic 总 commit 数至少 4)。在 /root/gitx3/diverged.txt 写三行:main_ahead=1、topic_ahead=2、merge_base=<두 브랜치의 공통 조상 해시>。

在 /root/gitx3/repo 中让 main 和 topic 各自拥有不同 commit。分支后两边都必须有 commit 才算分叉。可用 git rev-list --count A..B 统计领先数。

把 topic 重写到 main 上

将 topic rebase 到 main 上。rebase 之前记录 topic 尾部哈希,并在 /root/gitx3/rebased.txt 写两行:before=<리베이스 전 해시>、after=<리베이스 후 해시>。两值必须不同,after 与当前 topic 尾部一致,且不得残留进行中的 rebase。

必须在 rebase 前记录 topic 尾部哈希才能比较。完成后共同祖先变为 main 尾部;由于父节点改变,哈希也会改变。

通过快进形成线性历史

在 main 上以快进方式合并 topic。结果要求:main 的 merge commit 为 0 个,main 的总 commit 至少 6 个,main 与 topic 指向同一 commit。

rebase 后 main 是 topic 的祖先,因此无需 merge commit 即可合并。使用 --ff-only 可在条件不满足时拒绝操作,更安全。

保留双方变更解决冲突

在 /root/gitx3/conflict 创建新仓库。main 中放置 shared.txt;在 conflict-topic 分支修改该文件并提交,使其包含 from-topic;随后在 main 同一位置修改并提交,使其包含 from-main。将 conflict-topic rebase 到 main 会产生冲突,请保留双方变更。完成条件:rebase 已结束;索引无未解决路径;文件中无 <<<<<<< 等冲突标记;shared.txt 同时含 from-main 和 from-topic;工作树干净。

在 /root/gitx3/conflict 中让双方修改同一文件同一位置,再 rebase。解决时不是选择一方,而是保留双方变更,并删除冲突标记。

中止 rebase 并恢复原状

在 /root/gitx3/abort 创建新仓库,让 risky 分支和 main 分别以不同方式修改同一文件并提交。rebase 开始前将 risky 尾部哈希写入 /root/gitx3/abort-before.txt(仅哈希字符串)。启动 rebase,遇到冲突后中止。完成条件:无进行中的 rebase;risky 尾部与记录哈希完全一致;reflog 中有 rebase 记录。

在 /root/gitx3/abort 中先把分支尾部哈希写入文件,再启动会冲突的 rebase 并中止。中止会精确恢复到开始前状态。

把 3 个 commit 合为 1 个

在 /root/gitx3/squash 创建新仓库,在 feature/three 分支堆叠 3 个 commit(分别添加 s1.txt、s2.txt、s3.txt)。使用交互式 rebase 将3 个合为 1 个。完成条件:main..feature/three 只有 1 个 commit;标题包含 feat: combined;s1.txt/s2.txt/s3.txt 三个文件都存在。

在 /root/gitx3/squash 中以 main 为基准交互式 rebase feature/three。合并 commit 不应丢失修改,三个文件都必须保留。若难以打开编辑器,可用 GIT_SEQUENCE_EDITOR 修改列表。

丢弃一个 commit

在 /root/gitx3/drop 创建新仓库,在 feature/four 分支堆叠 4 个 commit:d1.txt、d2.txt、一个标题为 debug: temp log 且添加 debug.txt 的 commit,最后是 d3.txt。使用交互式 rebase 只丢弃调试 commit。完成条件:main..feature/four 有 3 个 commit;没有 debug: temp log commit;没有 debug.txt;d1/d2/d3.txt 均存在。

在 /root/gitx3/drop 的 feature/four 中只删除调试 commit。drop commit 时,该 commit 添加的文件也会消失,其余三个 commit 的文件必须保留。

总结黄金法则

编写 /root/gitx3/rebase.md。必须包含:对 --force-with-lease 的说明及其与 --force 的区别;何时不能 rebase(包含共享、推送、其他人中任一表述);为什么哈希变化(包含哈希、重写中任一表述);并在 merge_commits_on_main= 行实际统计并写入 /root/gitx3/repo 的 main merge commit 数量。

在 /root/gitx3/rebase.md 中写明 --force-with-lease 与 --force 的区别、何时不应 rebase、哈希为何变化,并在 merge_commits_on_main= 行实际统计第 3 步结果。