进阶教程 · Git

main 前进以后怎样安全 rebase

在干净功能分支上取得最新主线,重放自己的提交,并正确处理冲突和远端更新。

功能分支开发几天以后,main 往往已经前进。rebase 会选择当前分支中需要重放的提交,再把它们应用到新的基线之上。提交内容可以保留,提交号通常会改变,因此开工前要确认工作区干净,也要知道这个分支是否已经与别人共享。

rebase 适合整理自己的功能分支,不要用它改写团队共享的 main。当前窗口只负责功能开发时,目标应是让功能提交落在最新 origin/main 之后,合并和发布仍交给指定的集成窗口。

先固定当前状态

git status --short --branch
git branch --show-current
git log --oneline --decorate -5

状态中仍有修改、暂存内容或未跟踪文件时,先把当前任务安全保存。属于本任务的代码可以整理成提交,本地配置、密钥和临时产物不能为了清空状态而误提交。不要在一棵来源不明的脏工作树上 rebase。随后获取远端引用,并查看双方各有多少提交。

git fetch origin
git rev-list --left-right --count origin/main...HEAD
git log --oneline origin/main..HEAD

第一条数量是主线独有提交数,第二条数量是功能分支独有提交数。下一条日志显示按提交可达性计算的功能分支独有历史,可以用来发现混入的任务。它只是一份候选清单。上游已有等价补丁时,rebase 会跳过对应提交,普通 rebase 默认也不会保留 merge commit。

还可以记录共同祖先。

git merge-base origin/main HEAD
git rev-parse HEAD

第一条记录原分支与主线的共同祖先,用来解释分叉位置。第二条记录 rebase 前的分支顶端,恢复旧位置时可以直接使用这个 SHA。rebase 也会把旧分支顶端写入 reflog。动手前已有测试时先保存命令和结果,rebase 后运行同一组检查,差异才有比较依据。

如果功能分支已经推送,再记录远端当前提交。

git ls-remote origin refs/heads/codex/example

把返回的提交号保存为 <old-remote-sha>。它能帮助你判断 rebase 以后远端是否被其他人推进,也能给后面的显式 lease 提供精确预期值。分支正在接受审查时,应先告诉审查者历史会变化,避免他们继续评论旧提交。

main 前进以后怎样安全 rebase的入口、目标与准备条件操作示意图
图 1 · 进入任务前先确认目标、范围和准备条件

把功能提交放到最新主线上

确认范围后执行。

git rebase origin/main

需要重放的提交通常会得到新的提交号。已经可以快进的提交、上游已有的等价补丁和默认被省略的 merge commit 可能不会生成对应的新提交。命令结束后再看提交图和差异。

git log --graph --oneline --decorate -12
git diff --stat origin/main...HEAD

差异应只包含本任务。随后运行项目约定的测试和构建,代码能重放不代表它与最新主线行为兼容。

遇到冲突时保留退出路线

Git 会停在第一个无法自动应用的提交。先用 git status 找到未合并文件,读懂主线当前行为和本提交原本意图,再编辑最终版本。

git rebase --show-current-patch
git diff --name-only --diff-filter=U

--show-current-patch 显示正在重放的那笔提交,第二条命令缩小到冲突文件。旧提交可能依赖已经被主线改名的函数,也可能修复了主线随后以另一种方式解决的问题。先比较行为,再决定保留、改写或删除哪一段。

git add <resolved-file>
git rebase --continue

判断失去把握时运行 git rebase --abort,分支会回到 rebase 开始前的位置。--skip 会丢掉当前补丁带来的变化,只有确认这笔提交已经被主线完整吸收时才使用。

rebase 结束以后,核对旧功能提交数量不能沿用旧 SHA。查看 git reflog 可以找到 rebase 前的分支位置,用它与新 HEAD 比较文件变化。确认无意外丢失后再让 reflog 保持自然过期,不要为了整洁急着清理恢复线索。

main 前进以后怎样安全 rebase的三步关键操作与请求骨架示意图
图 2 · 把关键操作拆成三步,并给每一步留下可观察结果

已推送分支需要额外确认

rebase 改写了提交号,普通 push 通常会被远端拒绝。单独使用 git push --force-with-lease 时,Git 会把本地远端跟踪引用当作预期值。后台 fetch 或另一个 worktree 的 fetch 都可能推进这个共享引用,让保护范围偏离你亲自核对过的远端状态。

确定没有别人基于旧历史继续工作后,把前面记录的 SHA 写进显式 lease。

git push \
  --force-with-lease=refs/heads/codex/example:<old-remote-sha> \
  origin HEAD:refs/heads/codex/example

远端分支仍指向 <old-remote-sha> 时,这次更新才会继续。远端已经变化时,命令会拒绝覆盖。共享分支先沟通,再改写历史。

push 成功后重新运行 git ls-remote,确认远端指向新的 HEAD。随后更新合并请求说明,把最新测试结果和 rebase 后提交写进去。主线在测试期间再次前进时,先判断变化是否触及本任务,不必为了追求零落后反复改写已经通过审查的提交。

main 前进以后怎样安全 rebase的结果验收与交付证据清单示意图
图 3 · 用结果、检查与交付证据确认任务真的完成

官方参考