LabHub
学习 学习路径 课程

Git 实战

变基与解冲突

在 LabHub 中继续学习

目标

通过哈希确认 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=1topic_ahead=2merge_base=<두 브랜치의 공통 조상 해시>
  2. topic rebasemain 上。rebase 之前记录 topic 尾部哈希,并在 /root/gitx3/rebased.txt 写两行:before=<리베이스 전 해시>after=<리베이스 후 해시>。两值必须不同after 与当前 topic 尾部一致,且不得残留进行中的 rebase。
  3. main 上以快进方式合并 topic。结果要求:main 的 merge commit 为 0 个main 的总 commit 至少 6 个maintopic 指向同一 commit
  4. /root/gitx3/conflict 创建新仓库。main 中放置 shared.txt;在 conflict-topic 分支修改该文件并提交,使其包含 from-topic;随后在 main 同一位置修改并提交,使其包含 from-main。将 conflict-topic rebase 到 main 会产生冲突,请保留双方变更。完成条件:rebase 已结束;索引无未解决路径;文件中无 <<<<<<< 等冲突标记shared.txt 同时含 from-mainfrom-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.txts2.txts3.txt)。使用交互式 rebase 将3 个合为 1 个。完成条件:main..feature/three 只有 1 个 commit;标题包含 feat: combineds1.txt/s2.txt/s3.txt 三个文件都存在
  7. /root/gitx3/drop 创建新仓库,在 feature/four 分支堆叠 4 个 commit:d1.txtd2.txt、一个标题为 debug: temp log 且添加 debug.txt 的 commit,最后是 d3.txt。使用交互式 rebase 只丢弃调试 commit。完成条件:main..feature/four3 个 commit;没有 debug: temp log commit;没有 debug.txtd1/d2/d3.txt 均存在。
  8. 编写 /root/gitx3/rebase.md。必须包含:对 --force-with-lease 的说明及其与 --force 的区别;何时不能 rebase(包含共享、推送、其他人中任一表述);为什么哈希变化(包含哈希、重写中任一表述);并在 merge_commits_on_main= 行实际统计并写入 /root/gitx3/repomain merge commit 数量

参考

创建两个分叉分支

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

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

把 topic 重写到 main 上

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

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

通过快进形成线性历史

main 上以快进方式合并 topic。结果要求:main 的 merge commit 为 0 个main 的总 commit 至少 6 个maintopic 指向同一 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-mainfrom-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.txts2.txts3.txt)。使用交互式 rebase 将3 个合为 1 个。完成条件:main..feature/three 只有 1 个 commit;标题包含 feat: combineds1.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.txtd2.txt、一个标题为 debug: temp log 且添加 debug.txt 的 commit,最后是 d3.txt。使用交互式 rebase 只丢弃调试 commit。完成条件:main..feature/four3 个 commit;没有 debug: temp log commit;没有 debug.txtd1/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/repomain merge commit 数量

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