首页 / Git 入门教程 / 变基 Rebase

Git 入门教程

变基 Rebase

本教程共 26 篇 · 第 11 篇 · 更新于 2026-07-29 · 约 9 分钟阅读

GitGit 入门教程变基rebase交互式变基rebase -imerge vs rebase

11. 变基 Rebase

本节目标:理解 rebase 把分叉历史”拉直”的原理,会用交互式 rebase 整理本地提交,并能根据场景在 merge 与 rebase 之间做出正确选择。

上一章学了 merge。它保留所有分叉痕迹,用合并提交把两条线接起来。rebase 走的是另一条路:它把提交”挪”到另一个基点后面,假装一切是线性发生的。

rebase 一句话定义

把当前分支的提交一个个摘下来,重新拼到目标分支的最新提交后面。结果:历史是一条直线,没有合并提交。

先看效果。分叉的提交历史:

      D---E feature
     /
A---B---C main

执行 git rebase main 后:

A---B---C---D'---E' feature

注意:D’ 和 E’ 的内容不变,但 SHA-1 值会变,因为父提交变了。

基本用法

你想把 feature 分支”挪”到 main 最新提交之后:

git switch feature        # 切到要被挪动的分支
git rebase main

或者一步到位,不先切分支:

git rebase main feature

输出一堆 Applying: 就说明 Git 在逐个重放提交。

原理拆解

Git 干了三件事:

  1. 找到两条分支的共同祖先(上例中的 B)。
  2. 把当前分支从 B 之后的每个提交提取成补丁。
  3. 把当前分支指针移到目标分支最新提交(C),再把补丁依次打上去。

这和 merge 本质不同:merge 做三方合并生成新提交;rebase 重放每个提交生成全新提交。

更有趣的例子:跳过中间分支

你有一个 server 分支,从它又分出了 client。现在只想把 client 的改动合进 main,暂时不要 server

git rebase --onto main server client

读作:把 client 分支里”从 server 分叉之后”的那些提交,挪到 main 后面。

git switch main
git merge client          # 快进合并

--onto 是 rebase 的进阶用法,遇到复杂分支图时非常好用。

交互式 rebase:整理你的提交

普通 rebase 原样重放提交。加上 -i(interactive),Git 会停下来让你”编辑”提交列表。这是 rebase 最强大的用法。

假设最近三次提交都是半成品:第二个提交修了第一个的 typo,第三个提交信息写错了。

git rebase -i HEAD~3

编辑器会弹出类似这样的内容:

pick a1b2c3d 添加用户登录
pick d4e5f6g 修复了登录的一个 typo
pick h7i8j9k 用户管理页面

# Rebase 1a2b3c4..h7i8j9k onto 1a2b3c4
#
# Commands:
# p, pick = 保留这个提交
# r, reword = 保留提交,但修改提交信息
# e, edit = 保留提交,停下来让你改内容
# s, squash = 合并到前一个提交,保留两份提交信息
# f, fixup = 合并到前一个提交,丢弃这份提交信息
# d, drop = 删除这个提交

常用命令速查

  • reword(r):改提交信息,不改内容。提交信息写错了就用它。
  • edit(e):停下来让你改代码。比如发现某个提交漏了个文件。
  • squash(s):把当前提交并到前一个提交里,保留两份信息。
  • fixup(f):把当前提交并到前一个提交里,丢弃当前提交信息。
  • drop(d):直接删除这个提交。

实际例子:合并两个 typo 修复

把第二行从 pick 改成 fixup

pick a1b2c3d 添加用户登录
fixup d4e5f6g 修复了登录的一个 typo
pick h7i8j9k 用户管理页面

保存退出后,Git 会把第二个提交吃进第一个,最终只剩两个提交。

执行到一半停下怎么办

如果某个提交标记为 edit,Git 会在那里暂停。改完后:

git add .
git rebase --continue

想放弃整个 rebase:

git rebase --abort

merge vs rebase:经典决策表

这是新手最容易纠结的问题。一句话口诀:本地未推送整理用 rebase,已共享历史用 merge。

场景推荐原因
本地分支,还没 push 过rebase整理成干净线性历史再推送
多人协作的共享分支merge避免重写别人基于的提交
向开源项目提 PR 前rebase维护者可以直接快进合并
已经 push 过的本地提交merge重写已共享历史会搞乱队友
从主分支拉最新代码pull --rebase减少无意义的合并提交

rebase 黄金法则

不要 rebase 已经推送到远程、别人可能基于它工作的分支。

重写公共分支的后果:队友下次 pull 会发现相同的内容被重复提交,历史里出现”双胞胎”提交。修复起来很痛苦。

如果真的不小心这么干了,可以救:

git pull --rebase     # 让 Git 用 patch-id 识别重复内容

但最好的策略是守规矩。

pull --rebase 替代 pull

默认 git pull 等于 fetch + merge,会产生额外的合并提交。

git pull --rebase

等价于 fetch + rebase,你的本地提交会被挪到远程最新提交之后,保持线性。

如果想让它成为默认行为:

git config --global pull.rebase true

rebase 出错了怎么办

每个被重放的提交都可能触发冲突。Git 会一个个停住让你解决:

git rebase main
CONFLICT (content): Merge conflict in app.js
Resolve all conflicts manually, mark them as fixed with "git add <file>", then run "git rebase --continue".

解决单个提交的冲突:

git add app.js
git rebase --continue

想跳过这个补丁:

git rebase --skip

想整个放弃:

git rebase --abort

本章小卡片

命令作用
git rebase main把当前分支挪到 main 之后
git rebase --onto A B C把 C 从 B 之后的提交挪到 A
git rebase -i HEAD~n交互式编辑最近 n 个提交
git rebase --continue解决冲突后继续
git rebase --abort放弃本次 rebase
git pull --rebasefetch + rebase,避免合并提交
Warning

不要 rebase 已经推送给队友的提交。历史一旦共享,就把它当作只读。想整理就去整理本地未推送的部分。