Git Rebase
git rebase 的核心用法:交互式变基、onto、冲突处理,以及"不要 rebase 已推送提交"的黄金规则。
#type / howto
#status / growing
#tech / ops
#resource / git
[!info] related notes
- 所属 MOC: Git MOC
- 相关概念: Git中的merge和rebase, Git 中的 fast-forward merge
- 相关 howto: 用 Rebase 把功能分支线性合并回 main
Git Rebase
一句话定义
rebase 把一系列提交”移植”到新的基点上,让历史变成线性。
rebase vs merge
| 场景 | rebase | merge |
|---|---|---|
| 历史形态 | 线性,干净 | 保留分支结构,有合并提交 |
| 冲突处理 | 逐个提交解决 | 一次性解决 |
| 已推送历史 | 会改写(危险) | 不改写(安全) |
| 适用场景 | 同步主分支、整理提交 | 合并功能分支到主分支 |
基本用法:同步主分支
# 当前在 feature 分支,想把 main 的最新提交同步过来
git checkout feature
git rebase main
# 效果:feature 的提交被"摘下来",重新放到 main 最新提交之后
# 原来: main ---A---B---C
# \
# D---E (feature)
#
# rebase 后: main ---A---B---C
# \
# D'---E' (feature,新的提交哈希)
交互式 rebase(最常用)
# 修改最近 3 个提交
git rebase -i HEAD~3
打开编辑器后的操作指令:
pick abc1234 第一个提交
squash def5678 第二个提交(合并到上一个)
reword ghi9012 第三个提交(修改提交信息)
drop jkl3456 删除这个提交
edit mno7890 暂停,允许修改内容
fixup pqr1234 类似 squash,但丢弃此提交信息
# pick = 保留
# squash = 合并到前一个提交,保留两者信息
# fixup = 合并到前一个提交,丢弃此提交信息
# reword = 保留内容,修改提交信息
# drop = 删除提交
# edit = 暂停在此提交,允许修改
实战:压缩提交
# 把 5 个小提交压缩成 1 个
git rebase -i HEAD~5
# 编辑器中:
pick abc1234 feat: 初始化用户模块
fixup def5678 fix: 拼写错误
fixup ghi9012 fix: 格式化代码
fixup jkl3456 feat: 添加邮箱验证
fixup mno7890 fix: 修复验证逻辑
# 结果:只剩一个干净的提交
实战:修改提交信息
git rebase -i HEAD~1
# 把 pick 改为 reword,保存后会打开编辑器修改提交信息
--onto 用法
# 把 feature 分支上"不在 A 上"的提交,移植到 B 上
git rebase --onto B A feature
# 典型场景:feature 从 develop 拉出,但要改到 main 上
git rebase --onto main develop feature
原始:
main ---M1---M2
\
develop D1---D2
\
feature F1---F2
rebase --onto main develop feature 后:
main ---M1---M2
\
feature F1'---F2' (只移植了 F1、F2,不包含 D1、D2)
冲突处理
# rebase 过程中遇到冲突
git rebase main
# CONFLICT (content): Merge conflict in src/app.js
# 1. 解决冲突后
git add src/app.js
# 2. 继续 rebase
git rebase --continue
# 3. 如果想放弃整个 rebase
git rebase --abort
# 4. 如果想跳过当前提交
git rebase --skip
冲突处理技巧:
# 查看哪些文件有冲突
git status
# 查看冲突详情
git diff
# 使用某个版本的文件
git checkout --ours src/app.js # 用当前分支的版本
git checkout --theirs src/app.js # 用目标分支的版本
黄金规则
不要 rebase 已经推送到公共仓库的提交。
原因:rebase 会改写提交哈希。如果别人基于旧提交工作,rebase 后他们的分支基点就不存在了。
# 安全:rebase 本地未推送的提交
git rebase main # 本地 feature 分支,还没 push
# 危险:rebase 已推送的提交
git rebase main # feature 分支已经 push 过,别人可能在用
git push --force # 强制推送会覆盖别人的提交
安全替代方案:
# 如果必须对已推送的分支做 rebase,先通知协作者
# 或者用 merge 代替
git merge main
rebase 的自动化策略
# rebase 时自动处理空白字符问题
git rebase --whitespace=fix main
# 自动 stash 未提交的更改
git rebase --autostash main
# rebase 时保留合并提交
git rebase --remerges-merges main
常见误区
- rebase 不是”更好的 merge”:两者适用场景不同
- rebase 后的提交是新的:哈希变了,不是原来的提交
git pull --rebase等于fetch + rebase:比默认的fetch + merge历史更干净- 冲突解决后用
add而不是commit:rebase 的流程和 merge 不同