设置和配置
获取和创建项目
基本快照
分支与合并
共享和更新项目
检查和比较
打补丁
调试
电子邮件
外部系统
服务器管理
指南
管理
底层命令
- 2.50.1 无更改
-
2.50.0
2025-06-16
- 2.49.1 无更改
-
2.49.0
2025-03-14
- 2.46.2 → 2.48.2 无更改
-
2.46.1
2024-09-13
- 2.45.1 → 2.46.0 无变化
-
2.45.0
2024-04-29
- 2.44.1 → 2.44.4 无更改
-
2.44.0
2024-02-23
- 2.43.3 → 2.43.7 无变更
-
2.43.2
2024-02-13
-
2.43.1
2024-02-09
-
2.43.0
2023-11-20
- 2.42.2 → 2.42.4 无更改
-
2.42.1
2023-11-02
- 2.41.1 → 2.42.0 无更改
-
2.41.0
2023-06-01
- 2.40.1 → 2.40.4 无更改
-
2.40.0
2023-03-12
- 2.39.4 → 2.39.5 无更改
-
2.39.3
2023-04-17
- 2.39.1 → 2.39.2 无更改
-
2.39.0
2022-12-12
- 2.38.1 → 2.38.5 无更改
-
2.38.0
2022-10-02
- 2.37.3 → 2.37.7 无更改
-
2.37.2
2022-08-11
- 2.36.3 → 2.37.1 无更改
-
2.36.2
2022-06-23
- 2.35.1 → 2.36.1 无更改
-
2.35.0
2022-01-24
- 2.34.1 → 2.34.8 无更改
-
2.34.0
2021-11-15
- 2.33.2 → 2.33.8 无更改
-
2.33.1
2021-10-12
- 2.32.1 → 2.33.0 无更改
-
2.32.0
2021-06-06
- 2.31.1 → 2.31.8 无更改
-
2.31.0
2021-03-15
- 2.30.1 → 2.30.9 无更改
-
2.30.0
2020-12-27
- 2.29.1 → 2.29.3 无更改
-
2.29.0
2020-10-19
- 2.28.1 无更改
-
2.28.0
2020-07-27
- 2.27.1 无更改
-
2.27.0
2020-06-01
- 2.26.1 → 2.26.3 无更改
-
2.26.0
2020-03-22
- 2.25.1 → 2.25.5 无更改
-
2.25.0
2020-01-13
- 2.24.1 → 2.24.4 无更改
-
2.24.0
2019-11-04
- 2.23.1 → 2.23.4 无更改
-
2.23.0
2019-08-16
- 2.22.1 → 2.22.5 无更改
-
2.22.0
2019-06-07
- 2.21.1 → 2.21.4 无更改
-
2.21.0
2019-02-24
- 2.20.1 → 2.20.5 无更改
-
2.20.0
2018-12-09
- 2.19.3 → 2.19.6 无更改
-
2.19.2
2018-11-21
- 2.19.1 无更改
-
2.19.0
2018-09-10
- 2.18.1 → 2.18.5 无更改
-
2.18.0
2018-06-21
- 2.17.1 → 2.17.6 无更改
-
2.17.0
2018-04-02
-
2.16.6
2019-12-06
-
2.15.4
2019-12-06
-
2.14.6
2019-12-06
-
2.13.7
2018-05-22
-
2.12.5
2017-09-22
- 2.10.5 → 2.11.4 无更改
-
2.9.5
2017-07-30
-
2.8.6
2017-07-30
- 2.7.6 无更改
-
2.6.7
2017-05-05
- 2.5.6 无更改
-
2.4.12
2017-05-05
-
2.3.10
2015-09-28
-
2.2.3
2015-09-04
-
2.1.4
2014-12-17
-
2.0.5
2014-12-17
概要
git rebase [-i | --interactive] [<options>] [--exec <cmd>] [--onto <newbase> | --keep-base] [<upstream> [<branch>]] git rebase [-i | --interactive] [<options>] [--exec <cmd>] [--onto <newbase>] --root [<branch>] git rebase (--continue|--skip|--abort|--quit|--edit-todo|--show-current-patch)
描述
如果指定了 <branch>,则 git
rebase
会在执行任何其他操作之前自动执行 git
switch
<branch>。否则,它会停留在当前分支上。
如果未指定 <upstream>,则会使用 branch.
<name>.remote
和 branch.
<name>.merge
选项中配置的上游分支(详见 git-config[1]),并假定使用 --fork-point
选项。如果您当前不在任何分支上,或者当前分支没有配置上游分支,则变基操作将中止。
当前分支中由提交引入但不在 <upstream> 中的所有更改都会保存到一个临时区域。这与 git
log
<upstream>..HEAD
显示的提交集相同;如果 --fork-point
处于活动状态(详见下方 --fork-point
的说明),则与 git
log
fork_point'..HEAD
显示的提交集相同;如果指定了 --root
选项,则与 git
log
HEAD
显示的提交集相同。
如果提供了 --onto
选项,则当前分支将重置为 <upstream> 或 <newbase>。这与 git
reset
--hard
<upstream>(或 <newbase>)具有完全相同的效果。ORIG_HEAD
将指向重置前分支的顶端。
注意
|
如果在变基过程中使用了其他写入该伪引用(例如 git reset )的命令,则在变基结束时,ORIG_HEAD 不保证仍指向先前的分支顶端。但是,可以使用当前分支的 reflog(即 @{1} ,详见 gitrevisions[7])访问先前的分支顶端。 |
之前保存到临时区域的提交将逐个按顺序重新应用到当前分支。请注意,HEAD
中引入与 HEAD..
<upstream> 中提交相同文本更改的任何提交都将被省略(即,一个上游已经接受但提交信息或时间戳不同的补丁将被跳过)。
合并失败可能会阻止此过程完全自动化。您将不得不解决任何此类合并失败并运行 git
rebase
--continue
。另一个选项是使用 git
rebase
--skip
绕过导致合并失败的提交。要检出原始的 <branch> 并移除 .git/rebase-apply
工作文件,请改用命令 git
rebase
--abort
。
假设存在以下历史,并且当前分支是 "topic"
A---B---C topic / D---E---F---G master
从这一点开始,以下任一命令的结果将是
git rebase master git rebase master topic
会是
A'--B'--C' topic / D---E---F---G master
注意: 后者只是 git
checkout
topic
后面跟着 git
rebase
master
的简写。当变基操作退出时,topic
将保持为已检出分支。
如果上游分支已包含您所做的更改(例如,因为您发送的补丁已应用于上游),则该提交将被跳过并发出警告(如果使用 merge 后端)。例如,在以下历史记录上运行 git
rebase
master
(其中 A'
和 A
引入了相同的更改集,但具有不同的提交者信息)
A---B---C topic / D---E---A'---F master
将导致
B'---C' topic / D---E---A'---F master
这是您如何将一个基于某个分支的主题分支移植到另一个分支,以假装您是从后者分支派生该主题分支的方法,使用 rebase
--onto
。
首先假设您的 topic 基于 next 分支。例如,在 topic 中开发的功能依赖于 next 中发现的一些功能。
o---o---o---o---o master \ o---o---o---o---o next \ o---o---o topic
我们想让 topic 从 master 分支派生;例如,因为 topic 所依赖的功能已合并到更稳定的 master 分支中。我们希望我们的树看起来像这样
o---o---o---o---o master | \ | o'--o'--o' topic \ o---o---o---o---o next
我们可以使用以下命令实现此目的
git rebase --onto master next topic
--onto
选项的另一个示例是变基分支的一部分。如果出现以下情况
H---I---J topicB / E---F---G topicA / A---B---C---D master
那么命令
git rebase --onto master topicA topicB
将导致
H'--I'--J' topicB / | E---F---G topicA |/ A---B---C---D master
这在 topicB 不依赖于 topicA 的情况下很有用。
还可以通过 rebase 删除一系列提交。如果出现以下情况
E---F---G---H---I---J topicA
那么命令
git rebase --onto topicA~5 topicA~3 topicA
将导致删除提交 F 和 G
E---H'---I'---J' topicA
如果 F 和 G 在某种程度上有缺陷,或者不应该成为 topicA 的一部分,这会很有用。请注意,--onto
的参数和 <upstream> 参数可以是任何有效的 commit-ish。
如果发生冲突,git
rebase
将在第一个有问题的提交处停止,并在树中留下冲突标记。您可以使用 git
diff
定位标记(<<<<<<)并进行编辑以解决冲突。对于您编辑的每个文件,您需要告诉 Git 冲突已解决,这通常通过以下方式完成
git add <filename>
手动解决冲突并使用所需的解决方案更新索引后,您可以使用以下命令继续变基过程
git rebase --continue
或者,您可以使用以下命令撤消 git rebase 操作
git rebase --abort
模式选项
本节中的选项不能与其他任何选项一起使用,包括彼此之间也不能一起使用
- --continue
-
解决合并冲突后重新开始变基过程。
- --skip
-
跳过当前补丁,重新开始变基过程。
- --abort
-
中止变基操作并将 HEAD 重置为原始分支。如果在开始变基操作时提供了 <branch>,则
HEAD
将重置为 <branch>。否则,HEAD
将重置为变基操作开始时的位置。 - --quit
-
中止变基操作,但
HEAD
不会重置回原始分支。索引和工作树也因此保持不变。如果使用--autostash
创建了临时暂存条目,它将被保存到暂存列表中。 - --edit-todo
-
在交互式变基期间编辑待办列表。
- --show-current-patch
-
在交互式变基或因冲突而停止变基时,显示当前补丁。这等同于
git
show
REBASE_HEAD
。
选项
- --onto <newbase>
-
创建新提交的起始点。如果未指定
--onto
选项,则起始点是 <upstream>。可以是任何有效的提交,而不仅仅是现有的分支名称。作为一种特殊情况,如果恰好只有一个合并基础,您可以使用 "A...B" 作为 A 和 B 合并基础的快捷方式。您最多可以省略 A 和 B 中的一个,在这种情况下,它默认为 HEAD。
- --keep-base
-
将创建新提交的起始点设置为 <upstream> 和 <branch> 的合并基础。运行
git
rebase
--keep-base
<upstream> <branch> 等效于运行git
rebase
--reapply-cherry-picks
--no-fork-point
--onto
<upstream>...
<branch> <upstream> <branch>。当在某个上游分支之上开发功能时,此选项很有用。在功能开发过程中,上游分支可能会推进,此时最好不要一直在上游之上进行变基,而是保持基础提交不变。由于基础提交未更改,此选项意味着使用
--reapply-cherry-picks
以避免丢失提交。尽管此选项和
--fork-point
都会在 <upstream> 和 <branch> 之间找到合并基础,但此选项使用合并基础作为创建新提交的起始点,而--fork-point
使用合并基础来确定将要变基的提交集。另请参阅下方的 不兼容选项。
- <upstream>
-
用于比较的上游分支。可以是任何有效的提交,而不仅仅是现有分支名称。默认为当前分支配置的上游。
- <branch>
-
工作分支;默认为
HEAD
。 - --apply
-
使用应用策略进行变基(内部调用
git-am
)。一旦合并后端处理了应用后端的所有功能,此选项将来可能成为空操作。另请参阅下方的 不兼容选项。
- --empty=(drop|keep|stop)
-
如何处理最初不为空且不是任何上游提交的干净拣选,但在变基后变为空的提交(因为它们包含上游已存在更改的子集)
请注意,最初为空的提交会被保留(除非指定了
--no-keep-empty
),而干净的拣选提交(由git
log
--cherry-mark
... 确定)会作为初步步骤被检测并丢弃(除非传递了--reapply-cherry-picks
或--keep-base
)。另请参阅下方的 不兼容选项。
- --no-keep-empty
- --keep-empty
-
在变基之前不保留结果中最初为空的提交(即,与其父提交相比没有任何更改的提交)。默认是保留最初为空的提交,因为创建此类提交需要向
git
commit
传递--allow-empty
覆盖标志,表示用户非常有意地创建此类提交并因此希望保留它。此标志的使用可能很少见,因为您可以通过启动交互式变基并删除对应于您不想要的提交的行来摆脱最初为空的提交。此标志作为一个方便的快捷方式而存在,例如在外部工具生成许多空提交而您希望全部删除它们的情况下。
对于最初不为空但在变基后变为空的提交,请参阅
--empty
标志。另请参阅下方的 不兼容选项。
- --reapply-cherry-picks
- --no-reapply-cherry-picks
-
重新应用任何上游提交的所有干净拣选,而不是预先丢弃它们。(如果这些提交在变基后变为空,因为它们包含上游已存在更改的子集,则对它们的行为由
--empty
标志控制。)在没有
--keep-base
的情况下(或如果给定了--no-reapply-cherry-picks
),这些提交将自动被丢弃。由于这需要读取所有上游提交,因此在具有大量需要读取的上游提交的仓库中,这可能会很耗时。当使用 merge 后端时,将为每个丢弃的提交发出警告(除非给出--quiet
)。除非advice.skippedCherryPicks
设置为 false(参阅 git-config[1]),否则也会发出建议。--reapply-cherry-picks
允许变基放弃读取所有上游提交,从而可能提高性能。另请参阅下方的 不兼容选项。
- --allow-empty-message
-
空操作。以前,变基空消息的提交会失败,此选项会覆盖该行为,允许变基空消息的提交。现在,空消息的提交不会导致变基停止。
另请参阅下方的 不兼容选项。
- -m
- --merge
-
使用合并策略进行变基(默认)。
请注意,变基合并通过将工作分支的每个提交重新应用到 <upstream> 分支之上来工作。因此,当发生合并冲突时,报告为 ours 的一方是迄今为止已变基的系列,从 <upstream> 开始,而 theirs 则是工作分支。换句话说,双方是互换的。
另请参阅下方的 不兼容选项。
- -s <strategy>
- --strategy=<strategy>
-
使用给定的合并策略,而不是默认的
ort
。这意味着隐含--merge
。因为
git
rebase
使用给定策略将工作分支的每个提交重新应用到 <upstream> 分支之上,所以使用ours
策略只会清空 <branch> 中的所有补丁,这意义不大。另请参阅下方的 不兼容选项。
- -X <strategy-option>
- --strategy-option=<strategy-option>
-
将 <strategy-option> 传递给合并策略。这意味着隐含
--merge
,如果未指定策略,则隐含-s
ort
。请注意,如上文-m
选项所述,ours 和 theirs 是颠倒的。另请参阅下方的 不兼容选项。
--rerere-autoupdate
--no-rerere-autoupdate
-
rerere 机制重用当前冲突上已记录的解决方案以更新工作树中的文件后,允许它同时使用解决方案结果更新索引。
--no-rerere-autoupdate
是在通过单独的git
add
将结果提交到索引之前,仔细检查rerere
的作用并捕获潜在的错误合并的好方法。 - -S[<keyid>]
- --gpg-sign[=<keyid>]
- --no-gpg-sign
-
GPG 签名提交。
keyid
参数是可选的,默认为提交者身份;如果指定,它必须紧跟在选项后面,中间不能有空格。--no-gpg-sign
对于取消commit.gpgSign
配置变量和先前的--gpg-sign
都很有用。 - -q
- --quiet
-
静默模式。隐含
--no-stat
。 - -v
- --verbose
-
详细模式。隐含
--stat
。 - --stat
-
显示自上次变基以来上游更改的 diffstat。diffstat 也由配置选项 rebase.stat 控制。
- -n
- --no-stat
-
在变基过程中不显示 diffstat。
- --no-verify
-
此选项绕过 pre-rebase 钩子。另请参阅 githooks[5]。
- --verify
-
允许运行 pre-rebase 钩子,这是默认行为。此选项可用于覆盖
--no-verify
。另请参阅 githooks[5]。 - -C<n>
-
确保在每次更改前后至少有 <n> 行的周围上下文匹配。如果周围上下文的行数少于 <n>,则它们都必须匹配。默认情况下,不会忽略任何上下文。隐含
--apply
。另请参阅下方的 不兼容选项。
- --no-ff
- --force-rebase
- -f
-
独立地重放所有变基的提交,而不是快速前进跳过未更改的提交。这确保了变基分支的整个历史都由新的提交组成。
您可能会发现,在撤销主题分支合并后,这很有用,因为此选项会使用新的提交重新创建主题分支,这样就可以成功重新合并,而无需“撤销撤销”(详见 revert-a-faulty-merge 操作指南)。
- --fork-point
- --no-fork-point
-
在计算 <branch> 引入了哪些提交时,使用 reflog 在 <upstream> 和 <branch> 之间找到一个更好的共同祖先。
当
--fork-point
处于活动状态时,将使用 fork_point 而不是 <upstream> 来计算要变基的提交集,其中 fork_point 是git
merge-base
--fork-point
<upstream> <branch> 命令的结果(详见 git-merge-base[1])。如果 fork_point 最终为空,则将使用 <upstream> 作为备用。如果在命令行中给出了 <upstream> 或
--keep-base
,则默认是--no-fork-point
,否则默认是--fork-point
。另请参阅 git-config[1] 中的rebase.forkpoint
。如果您的分支基于 <upstream>,但 <upstream> 已被回溯且您的分支包含已丢弃的提交,则此选项可与
--keep-base
一起使用,以从您的分支中丢弃这些提交。另请参阅下方的 不兼容选项。
- --ignore-whitespace
-
在尝试协调差异时忽略空白差异。目前,每个后端都实现了这种行为的近似
- --whitespace=<option>
-
此标志传递给应用补丁的
git
apply
程序(详见 git-apply[1])。隐含--apply
。另请参阅下方的 不兼容选项。
- --committer-date-is-author-date
-
不使用当前时间作为提交者日期,而是使用正在变基的提交的作者日期作为提交者日期。此选项隐含
--force-rebase
。 - --ignore-date
- --reset-author-date
-
不使用原始提交的作者日期,而是使用当前时间作为变基提交的作者日期。此选项隐含
--force-rebase
。另请参阅下方的 不兼容选项。
- --signoff
-
为所有变基的提交添加一个
Signed-off-by
尾部信息。请注意,如果给定了--interactive
,则只有标记为要拣选、编辑或重新措辞的提交才会添加尾部信息。另请参阅下方的 不兼容选项。
- -i
- --interactive
-
列出即将变基的提交。在变基之前,允许用户编辑该列表。此模式也可用于拆分提交(详见下方的 拆分提交)。
提交列表格式可以通过设置配置选项 rebase.instructionFormat 来更改。自定义的指令格式将自动在格式前加上提交哈希。
另请参阅下方的 不兼容选项。
- -r
- --rebase-merges[=(rebase-cousins|no-rebase-cousins)]
- --no-rebase-merges
-
默认情况下,变基会简单地从待办列表中删除合并提交,并将变基后的提交放入单个线性分支中。使用
--rebase-merges
,变基将尝试通过重新创建合并提交来保留要变基的提交中的分支结构。这些合并提交中任何已解决的合并冲突或手动修改都必须手动解决/重新应用。--no-rebase-merges
可用于撤销rebase.rebaseMerges
配置选项和先前的--rebase-merges
。在变基合并时,有两种模式:
rebase-cousins
和no-rebase-cousins
。如果未指定模式,则默认为no-rebase-cousins
。在no-rebase-cousins
模式下,没有将 <upstream> 作为直接祖先的提交将保留其原始分支点,即,被 git-log[1] 的--ancestry-path
选项排除的提交将默认保留其原始祖先。在rebase-cousins
模式下,此类提交将被变基到 <upstream> 之上(如果指定了 <onto>)。目前只能使用
ort
合并策略重新创建合并提交;不同的合并策略只能通过显式的exec
git
merge
-s
<strategy> [...] 命令使用。另请参阅下方的 合并变基 和 不兼容选项。
- -x <cmd>
- --exec <cmd>
-
在最终历史中创建提交的每一行之后附加 "exec <cmd>"。<cmd> 将被解释为一个或多个 shell 命令。任何失败的命令将中断变基,并退出代码 1。
您可以通过使用一个
--exec
实例和多个命令来执行多个命令git rebase -i --exec "cmd1 && cmd2 && ..."
或者通过提供多个
--exec
git rebase -i --exec "cmd1" --exec "cmd2" --exec ...
如果使用
--autosquash
,则exec
行不会附加到中间提交,而只会出现在每个 squash/fixup 系列的末尾。这在内部使用
--interactive
机制,但可以在没有显式--interactive
的情况下运行。另请参阅下方的 不兼容选项。
- --root
-
变基所有可从 <branch> 访问的提交,而不是使用 <upstream> 限制它们。这允许您变基分支上的根提交。
另请参阅下方的 不兼容选项。
- --autosquash
- --no-autosquash
-
自动将带有特殊格式消息的提交合并到正在变基的先前提交中。如果提交消息以 "squash! "、"fixup! " 或 "amend! " 开头,则标题的其余部分被视为提交规范符,如果它与该提交的标题或哈希匹配,则匹配先前的提交。如果没有提交完全匹配,则考虑规范符与提交标题开头的匹配。
在变基待办列表中,squash、fixup 和 amend 提交的操作分别从
pick
更改为squash
、fixup
或fixup
-C
,并且它们被移动到它们修改的提交之后。--interactive
选项可用于在继续之前查看和编辑待办列表。创建带有 squash 标记的提交的推荐方法是使用 git-commit[1] 的
--squash
、--fixup
、--fixup=amend:
或--fixup=reword:
选项,这些选项将目标提交作为参数并自动从目标提交中填充新提交的标题。将配置变量
rebase.autoSquash
设置为 true 会在交互式变基中默认启用自动合并。可以使用--no-autosquash
选项覆盖该设置。另请参阅下方的 不兼容选项。
- --autostash
- --no-autostash
-
在操作开始前自动创建一个临时暂存条目,并在操作结束后应用它。这意味着您可以在脏工作树上运行变基。但是,请谨慎使用:成功变基后的最终暂存应用可能会导致非平凡的冲突。
- --reschedule-failed-exec
- --no-reschedule-failed-exec
-
自动重新调度失败的
exec
命令。这仅在交互模式下(或提供了--exec
选项时)有意义。此选项在变基开始后生效。它将根据提供给初始
git
rebase
的命令行选项、rebase.rescheduleFailedExec
配置(参阅 git-config[1] 或下方的“配置”),或默认为 false,并在整个变基过程中保留。在整个变基过程中记录此选项是一个方便的功能。否则,如果调用
git
rebase
--continue
时存在rebase.rescheduleFailedExec=true
配置,则开始时的显式--no-reschedule-failed-exec
将被覆盖。目前,您无法将--
[no-
]reschedule-failed-exec
传递给git
rebase
--continue
。 - --update-refs
- --no-update-refs
-
自动强制更新指向正在变基的提交的任何分支。在工作树中检出的任何分支都不会以这种方式更新。
如果配置变量
rebase.updateRefs
已设置,则此选项可用于覆盖并禁用此设置。另请参阅下方的 不兼容选项。
不兼容选项
以下选项
-
--apply
-
--whitespace
-
-C
与以下选项不兼容
-
--merge
-
--strategy
-
--strategy-option
-
--autosquash
-
--rebase-merges
-
--interactive
-
--exec
-
--no-keep-empty
-
--empty=
-
--[no-]reapply-cherry-picks 在不使用 --keep-base 时
-
--update-refs
-
--root 在不使用 --onto 时
此外,以下选项对不兼容
-
--keep-base 和 --onto
-
--keep-base 和 --root
-
--fork-point 和 --root
行为差异
git
rebase
有两个主要后端:apply 和 merge。(apply 后端以前被称为 am 后端,但该名称导致混淆,因为它看起来像动词而不是名词。此外,merge 后端以前被称为交互式后端,但现在也用于非交互式情况。两者都根据支撑各自的底层功能进行了重命名。)这两个后端在行为上有一些细微的差异
空提交
apply 后端不幸地丢弃了有意为空的提交,即最初为空的提交,尽管这在实践中很少见。它还会丢弃变为空的提交,并且没有选项来控制此行为。
merge 后端默认保留有意为空的提交(尽管使用 -i
时,它们在待办列表编辑器中被标记为空,或者可以使用 --no-keep-empty
自动丢弃它们)。
与 apply 后端类似,默认情况下,merge 后端会丢弃变为空的提交,除非指定了 -i
/--interactive
(在这种情况下,它会停止并询问用户如何操作)。merge 后端还具有 --empty=
(drop
|keep
|stop
) 选项,用于更改处理变为空提交的行为。
目录重命名检测
由于缺乏准确的树信息(源于使用补丁中有限的信息构造伪祖先),apply 后端禁用了目录重命名检测。禁用的目录重命名检测意味着,如果历史的一侧重命名了一个目录,而另一侧向旧目录添加了新文件,那么新文件将留在旧目录中,在变基时不会有任何警告提示您可能需要将这些文件移动到新目录中。
目录重命名检测与 merge 后端协同工作,在这种情况下向您提供警告。
上下文
apply 后端通过创建一系列补丁(内部调用 format-patch
),然后按顺序应用这些补丁(内部调用 am
)来工作。补丁由多个块组成,每个块都包含行号、上下文区域和实际更改。行号必须带有一些偏移量,因为另一侧可能已经在文件的前面插入或删除了行。上下文区域旨在帮助找到如何调整行号以便将更改应用到正确的行。但是,如果代码的多个区域具有相同的周围上下文行,则可能会选中错误的区域。在实际案例中,这导致提交被错误地重新应用,但没有报告任何冲突。将 diff.context
设置为更大的值可能会防止此类问题,但会增加伪冲突的可能性(因为它将需要更多匹配的上下文行才能应用)。
merge 后端处理每个相关文件的完整副本,从而使其免受此类问题的影响。
冲突标记的标注
当存在内容冲突时,合并机制会尝试使用内容来源的提交来标注每一方的冲突标记。由于 apply 后端丢弃了关于变基提交及其父提交的原始信息(并根据生成的补丁中的有限信息生成新的伪提交),因此无法识别这些提交;相反,它必须退回到提交摘要。此外,当 merge.conflictStyle
设置为 diff3
或 zdiff3
时,apply 后端将使用“构造的合并基础”来标记来自合并基础的内容,从而不提供有关合并基础提交的任何信息。
merge 后端处理历史两侧的完整提交,因此没有此类限制。
钩子
apply 后端传统上不调用 post-commit 钩子,而 merge 后端调用。两者都调用了 post-checkout 钩子,尽管 merge 后端抑制了其输出。此外,两个后端都只使用变基的起始提交来调用 post-checkout 钩子,而不使用中间提交或最终提交。在每种情况下,调用这些钩子都是由于实现上的偶然性而非设计(两个后端最初都实现为 shell 脚本,并碰巧调用了其他会调用钩子的命令,如 git
checkout
或 git
commit
会调用钩子)。两个后端应该具有相同的行为,尽管目前尚不完全清楚哪个(如果有的话)是正确的。我们未来可能会让 rebase 停止调用其中任何一个钩子。
可中断性
apply 后端在中断时机不当的情况下存在安全问题;如果用户在错误的时间按下 Ctrl-C 试图中止变基,变基可能会进入一种状态,在这种状态下无法通过后续的 git
rebase
--abort
来中止。merge 后端似乎没有同样的缺陷。(详见 https://lore.kernel.org/git/20200207132152.GC2868@szeder.dev/)。
合并策略
合并机制(git
merge
和 git
pull
命令)允许使用 -s
选项选择后端合并策略。某些策略也可以接受自己的选项,这些选项可以通过向 git
merge
和/或 git
pull
提供 -X
<option> 参数来传递。
ort
-
这是拉取或合并一个分支时的默认合并策略。此策略只能使用三方合并算法解决两个头。当存在多个可用于三方合并的共同祖先时,它会创建共同祖先的合并树并将其用作三方合并的参考树。根据对从 Linux 2.6 内核开发历史中获取的实际合并提交进行的测试,据报道这会导致更少的合并冲突,而不会引起错误合并。此外,此策略可以检测和处理涉及重命名的合并。它不使用检测到的复制。此算法的名称是一个首字母缩略词("Ostensibly Recursive’s Twin"),来源于它被编写为先前默认算法
recursive
的替代品。在路径为子模块的情况下,如果合并一侧使用的子模块提交是合并另一侧使用的子模块提交的后代,Git 会尝试快进到该后代。否则,Git 会将这种情况视为冲突,并建议使用冲突提交的后代子模块提交作为解决方案(如果存在)。
ort
策略可以接受以下选项:ours
-
此选项强制冲突块通过偏向 our 版本来干净地自动解决。来自另一个树的、与我们侧不冲突的更改会反映在合并结果中。对于二进制文件,所有内容都取自我们侧。
这不应与
ours
合并策略混淆,后者根本不查看其他树包含什么。它丢弃其他树所做的所有更改,声明 our 历史包含其中发生的所有内容。 theirs
-
这与
ours
相反;请注意,与ours
不同,没有theirs
合并策略来混淆此合并选项。 ignore-space-change
ignore-all-space
ignore-space-at-eol
ignore-cr-at-eol
-
为了三方合并,将带有指示类型空白更改的行视为未更改。与其他更改混合的行中的空白更改不会被忽略。另请参阅 git-diff[1]
-b
、-w
、--ignore-space-at-eol
和--ignore-cr-at-eol
。-
如果*他们的*版本只引入了行的空白符更改,则使用*我们的*版本;
-
如果*我们的*版本引入了空白符更改但*他们的*版本包含了实质性更改,则使用*他们的*版本;
-
否则,合并按常规方式进行。
-
renormalize
-
这会对任何需要三方合并的文件进行所有三个阶段的虚拟检出和检入。此选项旨在用于合并具有不同清理过滤器或行尾规范化规则的分支时。详见 gitattributes[5] 中“合并具有不同检入/检出属性的分支”。
no-renormalize
-
禁用
renormalize
选项。这会覆盖merge.renormalize
配置变量。 find-renames
[=
<n>]-
启用重命名检测,可选地设置相似度阈值。这是默认值。这会覆盖
merge.renames
配置变量。另请参阅 git-diff[1]--find-renames
。 rename-threshold=
<n>-
已弃用的
find-renames=
<n> 同义词。 no-renames
-
关闭重命名检测。这会覆盖
merge.renames
配置变量。另请参阅 git-diff[1]--no-renames
。 histogram
-
已弃用的
diff-algorithm=histogram
同义词。 patience
-
已弃用的
diff-algorithm=patience
同义词。 diff-algorithm=
(histogram
|minimal
|myers
|patience
)-
在合并时使用不同的 diff 算法,这有助于避免因不重要的匹配行(例如来自不同函数的括号)而导致的错误合并。另请参阅 git-diff[1]
--diff-algorithm
。请注意,ort
默认使用diff-algorithm=histogram
,而常规 diff 目前默认使用diff.algorithm
配置设置。 subtree
[=
<path>]-
此选项是 subtree 策略的一种更高级形式,该策略在合并时猜测两个树必须如何移动才能相互匹配。相反,指定的路径被前缀(或从开头剥离)以使两个树的形状匹配。
recursive
-
这现在是
ort
的同义词。它在 v2.49.0 之前是一种替代实现,但在 v2.50.0 中被重定向为ort
。先前的 recursive 策略是 Git v0.99.9k 到 v2.33.0 之间解决两个头的默认策略。 resolve
-
这只能使用三向合并算法解决两个头部(即当前分支和你拉取的另一个分支)。它尝试仔细检测交叉合并歧义。它不处理重命名。
octopus
-
这解决了具有两个以上头部的情况,但拒绝进行需要手动解决的复杂合并。它主要用于捆绑主题分支头部。这是拉取或合并多个分支时的默认合并策略。
ours
-
这解决了任意数量的头部,但合并结果树始终是当前分支头部的树,有效忽略所有其他分支的所有更改。它旨在用于取代侧分支的旧开发历史。请注意,这与
ort
合并策略的-Xours
选项不同。 subtree
-
这是一个修改过的
ort
策略。当合并树 A 和树 B 时,如果 B 对应于 A 的子树,则首先调整 B 以匹配 A 的树结构,而不是读取相同级别的树。此调整也适用于共同祖先树。
对于使用三方合并的策略(包括默认的 ort
),如果在两个分支上都进行了更改,但后来在一个分支上被恢复,则该更改将存在于合并结果中;有些人觉得这种行为令人困惑。发生这种情况是因为在执行合并时只考虑头部和合并基础,而不是单个提交。因此,合并算法将恢复的更改视为完全没有更改,并用更改的版本代替。
注意事项
您应该了解在共享存储库上使用 git
rebase
的含义。另请参阅下方的 从上游变基中恢复。
运行变基时,如果存在 pre-rebase
钩子,它将首先执行该钩子。您可以使用此钩子进行健全性检查,并在不合适时拒绝变基。有关示例,请参阅模板 pre-rebase
钩子脚本。
完成后,<branch> 将是当前分支。
交互模式
交互式变基意味着您有机会编辑被变基的提交。您可以重新排序提交,也可以删除它们(清除错误或其他不需要的补丁)。
交互模式适用于以下类型的工作流
-
有一个绝妙的想法
-
编写代码
-
准备提交系列
-
提交
其中第 2 点包含以下几个实例
a) 常规使用
-
完成一些值得提交的东西
-
commit
b) 独立修正
-
意识到有些东西不起作用
-
修正它
-
提交它
有时,b.2 中修复的内容无法修正到它修复的那个不太完美的提交,因为该提交深埋在补丁系列中。这正是交互式变基的用途:在执行了大量的“a”和“b”之后,通过重新排列和编辑提交,以及将多个提交合并为一个,来使用它。
从您希望原样保留的最后一个提交开始
git rebase -i <after-this-commit>
编辑器将启动,其中包含当前分支中给定提交之后的所有提交(忽略合并提交)。您可以随意重新排序此列表中的提交,也可以删除它们。列表大致如下所示
pick deadbee The oneline of this commit pick fa1afe1 The oneline of the next commit ...
一行描述纯粹是为了方便您查看;git rebase 不会查看它们,而是查看提交名称(本例中为 "deadbee" 和 "fa1afe1"),因此请勿删除或编辑名称。
通过将命令 "pick" 替换为命令 "edit",您可以告诉 git
rebase
在应用该提交后停止,以便您可以编辑文件和/或提交消息,修改提交,然后继续变基。
要中断变基(就像 "edit" 命令一样,但无需先拣选任何提交),请使用 "break" 命令。
如果您只想编辑提交的提交消息,请将命令 "pick" 替换为命令 "reword"。
要丢弃一个提交,请将命令 "pick" 替换为 "drop",或者直接删除匹配的行。
如果您想将两个或多个提交合并为一个,请将第二个及后续提交的 "pick" 命令替换为 "squash" 或 "fixup"。如果这些提交有不同的作者,合并后的提交将归属于第一个提交的作者。合并提交的建议提交消息是将第一个提交的消息与 "squash" 命令标识的那些消息连接起来,省略 "fixup" 命令标识的提交消息,除非使用 "fixup -c"。在这种情况下,建议的提交消息仅为 "fixup -c" 提交的消息,并会打开一个编辑器允许您编辑消息。"fixup -c" 提交的内容(补丁)仍将合并到折叠提交中。如果存在多个 "fixup -c" 提交,则使用最后一个提交的消息。您也可以使用 "fixup -C" 获得与 "fixup -c" 相同的行为,只是不打开编辑器。
当 "pick" 被替换为 "edit" 或命令因合并错误而失败时,git
rebase
将停止。当您完成编辑和/或解决冲突后,可以使用 git
rebase
--continue
继续。
例如,如果您想重新排序最后 5 个提交,使 HEAD~4
成为新的 HEAD
。为此,您可以这样调用 git
rebase
$ git rebase -i HEAD~5
并将第一个补丁移动到列表的末尾。
您可能希望重新创建合并提交,例如,如果您的历史记录如下所示
X \ A---M---B / ---o---O---P---Q
假设您想将从 "A" 开始的侧分支变基到 "Q"。请确保当前 HEAD
是 "B",并调用
$ git rebase -i -r --onto Q O
重新排序和编辑提交通常会创建未经测试的中间步骤。您可能希望通过运行测试来检查您的历史编辑是否损坏了任何内容,或者至少通过使用 "exec" 命令(快捷方式 "x")在历史记录的中间点重新编译。您可以通过创建如下待办列表来实现
pick deadbee Implement feature XXX fixup f1a5c00 Fix to feature XXX exec make pick c0ffeee The oneline of the next commit edit deadbab The oneline of the commit after exec cd subdir; make test ...
当命令失败(即以非 0 状态退出)时,交互式变基将停止,以便您有机会解决问题。您可以使用 git
rebase
--continue
继续。
"exec" 命令在 shell(默认通常是 /bin/sh)中启动命令,因此您可以使用 shell 功能(如 "cd", ">", ";" …)。该命令从工作树的根目录运行。
$ git rebase -i --exec "make test"
此命令允许您检查中间提交是否可编译。待办列表变为那样
pick 5928aea one exec make test pick 04d0fda two exec make test pick ba46169 three exec make test pick f4593f9 four exec make test
拆分提交
在交互模式下,您可以使用“edit”动作标记提交。但是,这不一定意味着 git
rebase
期望此编辑的结果恰好是一个提交。实际上,您可以撤消提交,或者添加其他提交。这可用于将一个提交拆分为两个
-
使用
git
rebase
-i
<commit>^
开始交互式变基,其中 <commit> 是您要拆分的提交。实际上,任何包含该提交的提交范围都可以。 -
用“edit”动作标记您要拆分的提交。
-
当编辑该提交时,执行
git
reset
HEAD^
。其效果是HEAD
回溯一个,索引也随之回溯。但是,工作树保持不变。 -
现在将您希望包含在第一个提交中的更改添加到索引中。您可以使用
git
add
(可能以交互方式)或git
gui
(或两者)来完成此操作。 -
现在使用适当的提交消息提交当前索引。
-
重复最后两个步骤,直到您的工作树干净。
-
使用
git
rebase
--continue
继续变基。
如果您不完全确定中间修订版是一致的(它们可以编译、通过测试套件等),则您应该在每次提交后使用 git
stash
暂存未提交的更改,进行测试,并在必要时修改提交。
从上游变基中恢复
变基(或任何其他形式的重写)其他人的工作所基于的分支是一个糟糕的主意:任何下游用户都将被迫手动修复其历史。本节从下游用户的角度解释如何进行修复。然而,真正的解决方案是首先避免变基上游。
为了说明,假设您处于这样一种情况:某人开发了一个 subsystem 分支,而您正在处理一个依赖于此 subsystem 的 topic。您最终可能会得到如下历史记录
o---o---o---o---o---o---o---o master \ o---o---o---o---o subsystem \ *---*---* topic
如果 subsystem 针对 master 进行了变基,则会发生以下情况
o---o---o---o---o---o---o---o master \ \ o---o---o---o---o o'--o'--o'--o'--o' subsystem \ *---*---* topic
如果您现在像往常一样继续开发,并最终将 topic 合并到 subsystem,则来自 subsystem 的提交将永远保持重复
o---o---o---o---o---o---o---o master \ \ o---o---o---o---o o'--o'--o'--o'--o'--M subsystem \ / *---*---*-..........-*--* topic
此类重复通常不受欢迎,因为它们会使历史记录混乱,难以跟踪。为了清理,您需要将 topic 上的提交移植到新的 subsystem 顶端,即,变基 topic。这会产生连锁反应:topic 的任何下游用户都将被迫进行变基,依此类推!
有两种修复方法,将在以下小节中讨论
- 简单情况:更改完全相同。
-
如果 subsystem 变基是一个简单的变基且没有冲突,就会发生这种情况。
- 困难情况:更改不同。
-
如果 subsystem 变基存在冲突,或者使用了
--interactive
来省略、编辑、合并或修正提交;或者如果上游使用了commit
--amend
、reset
或像filter-repo
这样的完整历史重写命令,就会发生这种情况。
简单情况
仅当 subsystem 上(基于 diff 内容的补丁 ID)的更改在 subsystem 执行变基前后完全相同时才有效。
在这种情况下,修复很简单,因为 git rebase 知道跳过新上游中已存在的更改(除非给出 --reapply-cherry-picks
)。所以如果您说(假设您在 topic 分支上)
$ git rebase subsystem
您将得到修复后的历史记录
o---o---o---o---o---o---o---o master \ o'--o'--o'--o'--o' subsystem \ *---*---* topic
困难情况
如果 subsystem 的更改与变基之前的更改不完全一致,情况就会变得更加复杂。
注意
|
尽管“简单情况恢复”有时在困难情况下也看似成功,但它可能导致意想不到的后果。例如,通过 git rebase --interactive 删除的提交将**复活**! |
想法是手动告诉 git
rebase
“旧的 subsystem 在哪里结束,您的 topic 在哪里开始”,即它们之间的旧合并基础是什么。您将不得不找到一种方法来命名旧 subsystem 的最后一个提交,例如
-
使用 subsystem 的 reflog:在
git
fetch
之后,subsystem 的旧尖端在subsystem@{1}
。后续的 fetch 操作将增加这个数字。(参见 git-reflog[1]。) -
相对于 topic 的尖端:如果你知道你的 topic 有三次提交,那么 subsystem 的旧尖端必须是
topic~3
。
然后你可以通过以下方式将旧的 subsystem..topic
移植到新的尖端(对于 reflog 情况,并假设你已在 topic 分支上)
$ git rebase --onto subsystem subsystem@{1}
“硬性恢复”的连锁反应特别糟糕:topic 下游的所有人现在都必须执行“硬性恢复”!
变基合并
交互式变基命令最初设计用于处理单个补丁系列。因此,从待办列表中排除合并提交是有意义的,因为开发者可能在分支上工作时合并了当时的 master
,最终却将所有提交变基到 master
上(跳过合并提交)。
然而,开发者可能出于正当理由希望重新创建合并提交:在处理多个相互关联的分支时,保持分支结构(或“提交拓扑”)。
在下面的例子中,开发者在一个主题分支上重构了按钮的定义方式,并在另一个主题分支上使用该重构实现了一个“报告错误”按钮。git
log
--graph
--format=%s
-5
的输出可能如下所示:
* Merge branch 'report-a-bug' |\ | * Add the feedback button * | Merge branch 'refactor-button' |\ \ | |/ | * Use the Button class for all buttons | * Extract a generic Button class from the DownloadButton one
开发者可能希望将这些提交变基到更新的 master
上,同时保持分支拓扑结构,例如,当第一个主题分支预计比第二个主题分支更早集成到 master
中时,比如为了解决与 DownloadButton 类在 master
中引入的更改所导致的合并冲突。
此变基可以使用 --rebase-merges
选项执行。它将生成一个如下所示的待办列表:
label onto # Branch: refactor-button reset onto pick 123456 Extract a generic Button class from the DownloadButton one pick 654321 Use the Button class for all buttons label refactor-button # Branch: report-a-bug reset refactor-button # Use the Button class for all buttons pick abcdef Add the feedback button label report-a-bug reset onto merge -C a1b2c3 refactor-button # Merge 'refactor-button' merge -C 6f5e4d report-a-bug # Merge 'report-a-bug'
与常规交互式变基不同,除了 pick
命令外,还有 label
、reset
和 merge
命令。
label
命令在执行时将一个标签与当前的 HEAD 关联起来。这些标签被创建为工作区本地引用 (refs/rewritten/
<label>),它们将在变基完成后被删除。这样,链接到同一仓库的多个工作区中的变基操作不会相互干扰。如果 label
命令失败,它会立即重新调度,并显示一条有用的消息,指导如何继续。
reset
命令将 HEAD、索引和工作区重置到指定的版本。它类似于执行 exec
git
reset
--hard
<label>,但拒绝覆盖未跟踪的文件。如果 reset
命令失败,它会立即重新调度,并显示一条有用的消息,指导如何编辑待办列表(这通常发生在手动将 reset
命令插入待办列表并包含拼写错误时)。
merge
命令会将指定的版本合并到当时的 HEAD 中。使用 -C
<original-commit> 时,将使用指定的合并提交的提交消息。当 -C
更改为小写的 -c
时,成功合并后将在编辑器中打开消息,以便用户可以编辑该消息。
如果 merge
命令因合并冲突以外的任何原因失败(即合并操作甚至没有开始),它会立即重新调度。
默认情况下,merge
命令将对常规合并使用 ort
合并策略,对章鱼式合并使用 octopus
策略。可以通过在调用变基时使用 --strategy
参数为所有合并指定默认策略,或者通过使用 exec
命令显式调用带 --strategy
参数的 git
merge
来覆盖交互式命令列表中的特定合并。请注意,当像这样显式调用 git
merge
时,你可以利用标签是工作区本地引用(例如,引用 refs/rewritten/onto
将对应于标签 onto
)这一事实,以便引用你想要合并的分支。
注意:第一个命令 (label
onto
) 标记了提交变基到的版本;名称 onto
只是一个约定,是为了向 --onto
选项致敬。
还可以通过添加形式为 merge
<merge-head> 的命令来从头引入全新的合并提交。这种形式将生成一个临时提交消息,并始终打开编辑器供用户编辑。这在例如一个主题分支解决了不止一个问题并希望拆分为两个或更多主题分支时会很有用。考虑这个待办列表:
pick 192837 Switch from GNU Makefiles to CMake pick 5a6c7e Document the switch to CMake pick 918273 Fix detection of OpenSSL in CMake pick afbecd http: add support for TLS v1.3 pick fdbaec Fix detection of cURL in CMake on Windows
此列表中与 CMake 无关的那次提交很可能是为了修复切换到 CMake 所引入的所有错误而进行的,但它解决了不同的问题。要将此分支拆分为两个主题分支,可以像这样编辑待办列表:
label onto pick afbecd http: add support for TLS v1.3 label tlsv1.3 reset onto pick 192837 Switch from GNU Makefiles to CMake pick 918273 Fix detection of OpenSSL in CMake pick fdbaec Fix detection of cURL in CMake on Windows pick 5a6c7e Document the switch to CMake label cmake reset onto merge tlsv1.3 merge cmake
配置
本节中以下所有内容均从 git-config[1] 文档中选择性地包含。内容与彼处相同:
- rebase.backend
-
用于变基的默认后端。可能的选择是 apply 或 merge。将来,如果合并后端获得应用后端的所有剩余功能,此设置可能会变得未使用。
- rebase.stat
-
是否显示自上次变基以来上游更改的差异统计。默认为 false。
- rebase.autoSquash
-
如果设置为 true,则在交互模式下默认启用 git-rebase[1] 的
--autosquash
选项。这可以通过--no-autosquash
选项覆盖。 - rebase.autoStash
-
当设置为 true 时,在操作开始前自动创建一个临时暂存条目,并在操作结束后应用它。这意味着你可以在一个有未提交更改的工作区上运行变基。但是,请谨慎使用:成功变基后的最终暂存应用可能会导致非平凡的冲突。此选项可以通过 git-rebase[1] 的
--no-autostash
和--autostash
选项覆盖。默认为 false。 - rebase.updateRefs
-
如果设置为 true,默认启用
--update-refs
选项。 - rebase.missingCommitsCheck
-
如果设置为“warn”,
git rebase -i
会在某些提交被移除(例如,一行被删除)时打印警告,但变基仍将继续。如果设置为“error”,它将打印上述警告并停止变基,此时可以使用 git rebase --edit-todo 来纠正错误。如果设置为“ignore”,则不进行任何检查。要不带警告或错误地删除提交,请在待办列表中使用drop
命令。默认为“ignore”。 - rebase.instructionFormat
-
一个格式字符串,如 git-log[1] 中所指定,用于在交互式变基期间的待办列表。该格式将自动在前面加上提交哈希。
- rebase.abbreviateCommands
-
如果设置为 true,
git
rebase
将在待办列表中使用缩写的命令名称,结果如下所示:p deadbee The oneline of the commit p fa1afe1 The oneline of the next commit ...
而不是
pick deadbee The oneline of the commit pick fa1afe1 The oneline of the next commit ...
默认为 false。
- rebase.rescheduleFailedExec
-
自动重新调度失败的
exec
命令。这仅在交互模式下(或提供了--exec
选项时)有意义。这与指定--reschedule-failed-exec
选项相同。 - rebase.forkPoint
-
如果设置为 false,默认设置
--no-fork-point
选项。 - rebase.rebaseMerges
-
默认情况下是否以及如何设置
--rebase-merges
选项。可以是rebase-cousins
、no-rebase-cousins
,或一个布尔值。设置为 true 或no-rebase-cousins
等同于--rebase-merges=no-rebase-cousins
,设置为rebase-cousins
等同于--rebase-merges=rebase-cousins
,设置为 false 等同于--no-rebase-merges
。在命令行上传递--rebase-merges
(无论是否带参数)都会覆盖任何rebase.rebaseMerges
配置。 - rebase.maxLabelLength
-
从提交主题生成标签名称时,将名称截断到此长度。默认情况下,名称会截断到略小于
NAME_MAX
的长度(以便例如可以为相应的松散引用写入.lock
文件)。 - sequence.editor
-
git
rebase
-i
用于编辑变基指令文件的文本编辑器。该值旨在在使用时由 shell 解释。它可以通过GIT_SEQUENCE_EDITOR
环境变量覆盖。如果未配置,则使用默认的提交消息编辑器。