简体中文 ▾ 主题 ▾ 最新版本 ▾ git-rebase 最后更新于 2.52.0

名称

git-rebase - 在另一个基点之上重放提交

概要

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)

描述

将一系列提交移植到另一个起点。您也可以使用 git rebase 来重新排序或合并提交:有关如何操作,请参见下面的交互模式。

例如,假设您一直在处理此历史中的 topic 分支,并且您想“跟上”在 master 分支上完成的工作。

          A---B---C topic
         /
    D---E---F---G master

您想将您自 topicmaster 分叉以来所做的提交(即 A、B 和 C)移植到当前的 master 之上。您可以通过在签出 topic 分支时运行 git rebase master 来实现。如果您想在其他分支上 rebase topicgit rebase master topicgit checkout topic && git rebase master 的快捷方式。

                  A'--B'--C' topic
                 /
    D---E---F---G master

如果在此过程中发生合并冲突,git rebase 将在第一个有问题的提交处停止,并留下冲突标记。如果发生这种情况,您可以执行以下操作之一:

  1. 解决冲突。您可以使用 git diff 查找标记(<<<<<<)并编辑以解决冲突。对于您编辑的每个文件,您都需要告诉 Git 冲突已解决。您可以使用 git add <文件名> 将冲突标记为已解决。解决所有冲突后,您可以使用以下命令继续 rebase 过程:

    git rebase --continue
  2. 使用以下命令中止 git rebase 并将您的分支恢复到原始状态:

    git rebase --abort
  3. 使用以下命令跳过导致合并冲突的提交:

    git rebase --skip

如果您没有指定要 rebase 的 <upstream>,则将使用在 branch.<name>.remotebranch.<name>.merge 选项中配置的上游(有关详细信息,请参阅 git-config[1]),并且假定使用 --fork-point 选项。如果您当前不在任何分支上,或者当前分支没有配置的上游,则 rebase 将中止。

以下是 git rebase <upstream> 执行操作的简化描述:

  1. 列出您的当前分支自从从 <upstream> 分叉以来,在 <upstream> 中没有等效提交的所有提交。

  2. 使用 git checkout --detach <upstream> 的等效命令签出 <upstream>

  3. 按顺序逐个重放提交。这类似于为每个提交运行 git cherry-pick <commit>。有关如何处理合并,请参阅 REBASING MERGES。

  4. 使用 git checkout -B <branch> 的等效命令更新您的分支以指向最终提交。

注意
开始 rebase 时,ORIG_HEAD 被设置为指向待 rebase 分支的顶端提交。但是,如果在 rebase 期间使用了更改 ORIG_HEAD 的其他命令(如 git reset),则 ORIG_HEAD 并不保证在 rebase 结束时仍指向该提交。但是,可以通过当前分支的 reflog(即 @{1},请参阅 gitrevisions[7])访问之前的分支顶端。

使用 --ONTO 移植主题分支

这是使用 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 选项的另一个示例是 rebase 分支的一部分。如果我们有以下情况:

                            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。

模式选项

本节中的选项不能与其他任何选项一起使用,包括它们本身也不能互相使用。

--continue

在解决合并冲突后重新开始 rebase 过程。

--skip

通过跳过当前补丁重新开始 rebase 过程。

--abort

中止 rebase 操作并将 HEAD 重置到原始分支。如果启动 rebase 操作时提供了 <branch>,则会将 HEAD 重置为 <branch>。否则,HEAD 将被重置为启动 rebase 操作时的状态。

--quit

中止 rebase 操作,但 HEAD 不会重置回原始分支。索引和工作树也保持不变。如果使用 --autostash 创建了临时 stash 条目,它将被保存在 stash 列表中。

--edit-todo

在交互式 rebase 期间编辑 todo 列表。

--show-current-patch

在交互式 rebase 中或当 rebase 因冲突停止时显示当前补丁。这相当于 git show REBASE_HEAD

选项

--onto <newbase>

创建新提交的起点。如果未指定 --onto 选项,则起点为 <upstream>。可以是任何有效的提交,而不仅仅是现有的分支名称。

作为特殊情况,您可以使用“A...B”作为 A 和 B 的合并基点的快捷方式,前提是只有一个合并基点。您可以省略 A 和 B 中的最多一个,在这种情况下,它默认为 HEAD。

有关示例,请参阅上面的 TRANSPLANTING A TOPIC BRANCH WITH --ONTO。

--keep-base

将创建新提交的起点设置为 <upstream><branch> 的合并基点。运行 git rebase --keep-base <upstream> <branch> 等同于运行 git rebase --reapply-cherry-picks --no-fork-point --onto <upstream>...<branch> <upstream> <branch>

此选项在开发基于上游分支的特性的情况下很有用。在开发特性的过程中,上游分支可能会更新,并且最好不要一直 rebase 到上游分支之上,而是保持基提交不变。由于基提交未更改,因此此选项隐含 --reapply-cherry-picks 以避免丢失提交。

尽管此选项和 --fork-point 都找到 <upstream><branch> 之间的合并基点,但此选项使用合并基点作为创建新提交的起点,而 --fork-point 使用合并基点来确定将要 rebase 的提交集

另请参阅下面的 INCOMPATIBLE OPTIONS。

<upstream>

要比较的上游分支。可以是任何有效的提交,而不仅仅是现有分支名称。默认为当前分支的已配置上游。

<branch>

工作分支;默认为 HEAD

--apply

使用应用策略进行 rebase(内部调用 git-am)。一旦合并后端处理了应用后端所做的一切,此选项将来可能会成为一个 no-op(空操作)。

另请参阅下面的 INCOMPATIBLE OPTIONS。

--empty=(drop|keep|stop)

如何处理那些最初不为空且不是上游提交的干净 cherry-pick,但在 rebase 后变为空的提交(因为它们包含已上游更改的子集)。

drop

提交将被丢弃。这是默认行为。

keep

提交将被保留。当指定 --exec 时,此选项是隐含的,除非同时指定了 -i/--interactive

stop
ask

rebase 将在提交应用时停止,允许您选择是丢弃它、进一步编辑文件,还是仅提交空更改。当指定 -i/--interactive 时,此选项是隐含的。askstop 的已弃用同义词。

请注意,最初为空的提交(除非指定 --no-keep-empty)将被保留,而上游提交的干净 cherry-pick(由 git log --cherry-mark ... 确定)将被检测并作为初步步骤删除(除非传递了 --reapply-cherry-picks--keep-base)。

另请参阅下面的 INCOMPATIBLE OPTIONS。

--no-keep-empty
--keep-empty

不在结果中保留 rebase 前为空的提交(即,与父提交没有任何更改)。默认情况下会保留最初为空的提交,因为创建此类提交需要向 git commit 传递 --allow-empty 覆盖标志,这表明用户有意创建了此类提交并希望保留它。

使用此标志的情况可能很少见,因为您可以通过启动交互式 rebase 并删除对应于您不想要的提交的行来删除最初为空的提交。此标志作为方便的快捷方式存在,例如在外部工具生成大量空提交并且您希望全部删除它们的情况下。

对于那些并非最初为空但在 rebase 后变为空的提交,请参阅 --empty 标志。

另请参阅下面的 INCOMPATIBLE OPTIONS。

--reapply-cherry-picks
--no-reapply-cherry-picks

重新应用上游提交的所有干净 cherry-pick,而不是预先丢弃它们。(如果这些提交在 rebase 后变为空,因为它们包含已上游更改的子集,那么对它们的处理由 --empty 标志控制。)

在没有 --keep-base(或给出 --no-reapply-cherry-picks)的情况下,这些提交将被自动丢弃。由于这需要读取所有上游提交,因此在具有大量需要读取的上游提交的存储库中可能会很昂贵。使用merge 后端时,将为每个丢弃的提交发出警告(除非给出 --quiet)。除非将 advice.skippedCherryPicks 设置为 false(请参阅 git-config[1]),否则也将发出建议。

--reapply-cherry-picks 允许 rebase 避免读取所有上游提交,从而可能提高性能。

另请参阅下面的 INCOMPATIBLE OPTIONS。

--allow-empty-message

无操作。以前,rebase 具有空消息的提交会失败,此选项将覆盖该行为,允许 rebase 具有空消息的提交。现在,具有空消息的提交不会导致 rebase 停止。

另请参阅下面的 INCOMPATIBLE OPTIONS。

-m
--merge

使用合并策略进行 rebase(默认)。

请注意,rebase 合并通过将工作分支中的每个提交重放到 <upstream> 分支之上来工作。因此,当发生合并冲突时,报告为ours 的是到目前为止已 rebase 的系列,以 <upstream> 开始,而theirs 是工作分支。换句话说,两侧是颠倒的。

另请参阅下面的 INCOMPATIBLE OPTIONS。

-s <strategy>
--strategy=<strategy>

使用给定的合并策略,而不是默认的 ort。这隐含 --merge

由于 git rebase 使用给定的策略将工作分支中的每个提交重放到 <upstream> 分支之上,因此使用 ours 策略只会清空 <branch> 中的所有补丁,这几乎没有意义。

另请参阅下面的 INCOMPATIBLE OPTIONS。

-X <strategy-option>
--strategy-option=<strategy-option>

将 <strategy-option> 传递给合并策略。这隐含 --merge,并且如果没有指定策略,则隐含 -s ort。请注意,如上 -m 选项所述,ourstheirs 的顺序是颠倒的。

另请参阅下面的 INCOMPATIBLE OPTIONS。

--rerere-autoupdate
--no-rerere-autoupdate

在 rerere 机制使用记录的冲突解决来更新工作树中的文件后,允许它也用解决结果更新索引。--no-rerere-autoupdate 是一个很好的方式来双重检查 rerere 所做的操作,并在使用单独的 git add 将结果提交到索引之前,捕获潜在的错误合并。

-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

显示自上次 rebase 以来上游变化的 diffstat。diffstat 也受 rebase.stat 配置选项控制。

-n
--no-stat

在 rebase 过程中不显示 diffstat。

--no-verify

此选项绕过 pre-rebase 钩子。另请参阅 githooks[5]

--verify

允许 pre-rebase 钩子运行,这是默认行为。此选项可用于覆盖 --no-verify。另请参阅 githooks[5]

-C<n>

确保在每次更改之前和之后至少有 <n> 行的上下文匹配。当周围的上下文行数较少时,它们都必须匹配。默认情况下,从不忽略任何上下文。隐含 --apply

另请参阅下面的 INCOMPATIBLE OPTIONS。

--no-ff
--force-rebase
-f

单独重放所有 rebased 提交,而不是快进(fast-forward)不变的提交。这确保了 rebased 分支的整个历史由新提交组成。

您可能会发现这在回滚主题分支合并后很有用,因为此选项会用新的提交重新创建主题分支,以便它可以成功地重新合并,而无需“撤销回滚”(有关详细信息,请参阅 revert-a-faulty-merge How-To)。

--fork-point
--no-fork-point

在使用 reflog 查找 <upstream><branch> 之间的更好公共祖先,以计算 <branch> 引入了哪些提交。

--fork-point 处于活动状态时,将使用 fork_point 而不是 <upstream> 来计算要 rebase 的提交集,其中 fork_pointgit 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 一起使用,以从您的分支中删除这些提交。

另请参阅下面的 INCOMPATIBLE OPTIONS。

--ignore-whitespace

在尝试协调差异时忽略空格差异。目前,每个后端都实现了这种行为的近似值。

apply 后端

应用补丁时,忽略上下文行中的空格更改。不幸的是,这意味着,如果补丁要替换的“旧”行仅在空格方面与现有文件不同,您将遇到合并冲突,而不是成功的补丁应用。

merge 后端

将仅包含空格更改的行视为未更改进行合并。不幸的是,这意味着任何旨在修改空格而不是其他内容的补丁块都将被丢弃,即使另一方没有任何冲突的更改。

--whitespace=<option>

此标志传递给应用的 git apply 程序(请参阅 git-apply[1])。它隐含 --apply

另请参阅下面的 INCOMPATIBLE OPTIONS。

--committer-date-is-author-date

而不是使用当前时间作为提交者日期,而是使用要 rebase 的提交的作者日期作为提交者日期。此选项隐含 --force-rebase

--ignore-date
--reset-author-date

而不是使用原始提交的作者日期,而是使用当前时间作为 rebased 提交的作者日期。此选项隐含 --force-rebase

另请参阅下面的 INCOMPATIBLE OPTIONS。

--signoff

在所有 rebased 提交中添加 Signed-off-by 尾部。请注意,如果给出了 --interactive,则只有标记为 pick、edit 或 reword 的提交才会添加此尾部。

另请参阅下面的 INCOMPATIBLE OPTIONS。

-i
--interactive

列出即将 rebase 的提交。让用户在 rebase 前编辑该列表。此模式也可以用于拆分提交(请参阅下面的 SPLITTING COMMITS)。

提交列表的格式可以通过设置 rebase.instructionFormat 配置选项来更改。自定义的指令格式会自动在格式前加上提交哈希。

另请参阅下面的 INCOMPATIBLE OPTIONS。

-r
--rebase-merges[=(rebase-cousins|no-rebase-cousins)]
--no-rebase-merges

默认情况下,rebase 将简单地从 todo 列表中删除合并提交,并将 rebased 提交放入一个单一的、线性的分支。使用 --rebase-merges 时,rebase 将尝试通过重新创建合并提交来保留要 rebase 的提交内的分支结构。任何已解决的合并冲突或这些合并提交中的手动修改都需要手动解决/重新应用。--no-rebase-merges 可用于抵消 rebase.rebaseMerges 配置选项和先前的 --rebase-merges

Rebase 合并时,有两种模式:rebase-cousinsno-rebase-cousins。如果未指定模式,则默认为 no-rebase-cousins。在 no-rebase-cousins 模式下,不以 <upstream> 作为直接祖先的提交将保留其原始分支点,即 git-log[1]--ancestry-path 选项会排除的提交将默认保留其原始祖先。在 rebase-cousins 模式下,此类提交将被 rebase 到 <upstream>(如果指定了 <onto>,则为 <onto>)。

目前只能使用 ort 合并策略重新创建合并提交;只能通过显式的 exec git merge -s <strategy> [...] 命令使用不同的合并策略。

另请参阅下面的 REBASING MERGES 和 INCOMPATIBLE OPTIONS。

-x <cmd>
--exec <cmd>

在创建最终历史中的每个提交行之后追加“exec <cmd>”。<cmd> 将被解释为一个或多个 shell 命令。任何失败的命令都将中断 rebase,退出代码为 1。

您可以通过使用一个 --exec 实例和多个命令来执行多个命令:

git rebase -i --exec "cmd1 && cmd2 && ..."

或者通过提供多个 --exec

git rebase -i --exec "cmd1" --exec "cmd2" --exec ...

如果使用了 --autosquash,则不会为中间提交追加 exec 行,它们将仅出现在每个 squash/fixup 系列的末尾。

这在内部使用 --interactive 机制,但也可以在没有显式 --interactive 的情况下运行。

另请参阅下面的 INCOMPATIBLE OPTIONS。

--root

Rebase <branch> 可达的所有提交,而不是用 <upstream> 限制它们。这允许您 rebase 分支上的根提交。

另请参阅下面的 INCOMPATIBLE OPTIONS。

--autosquash
--no-autosquash

自动将具有特殊格式消息的提交挤压(squash)到正在 rebase 的前一个提交中。如果提交消息以“squash! ”、“fixup! ”或“amend! ”开头,则标题的其余部分被视为提交说明符,如果它匹配标题或该提交的哈希,则匹配前一个提交。如果没有提交完全匹配,则考虑说明符与提交标题开头的匹配。

在 rebase todo 列表中,squash、fixup 和 amend 提交的操作从 pick 更改为 squashfixupfixup -C,并且它们会被移动到它们修改的提交的右侧。可以使用 --interactive 选项在继续之前查看和编辑 todo 列表。

创建带有 squash 标记的提交的推荐方法是使用 git-commit[1]--squash--fixup--fixup=amend:--fixup=reword: 选项,这些选项接受目标提交作为参数并自动从该提交填充新提交的标题。

将配置变量 rebase.autoSquash 设置为 true 会为交互式 rebase 启用自动挤压(auto-squash)。--no-autosquash 选项可用于覆盖该设置。

另请参阅下面的 INCOMPATIBLE OPTIONS。

--autostash
--no-autostash

在操作开始前自动创建一个临时 stash 条目,并在操作结束后应用它。这意味着您可以在不干净的工作树上运行 rebase。但是,请谨慎使用:成功的 rebase 后最终的应用 stash 可能会导致非琐碎的冲突。

--reschedule-failed-exec
--no-reschedule-failed-exec

自动重新安排失败的 exec 命令。这只在交互模式下(或提供了 --exec 选项时)有意义。

此选项在 rebase 开始后应用。它将在整个 rebase 中保留,顺序为:命令行选项提供给初始 git rebaserebase.rescheduleFailedExec 配置(请参阅下面的 git-config[1] 或“配置”),或者默认为 false。

为整个 rebase 记录此选项是一种便利功能。否则,在启动时显式的 --no-reschedule-failed-exec 将被 rebase.rescheduleFailedExec=true 配置的存在所覆盖,当调用 git rebase --continue 时。目前,您无法将 --[no-]reschedule-failed-exec 传递给 git rebase --continue

--update-refs
--no-update-refs

自动强制更新指向被 rebase 的提交的任何分支。在工作树中签出的任何分支都不会以这种方式更新。

如果设置了配置变量 rebase.updateRefs,则此选项可用于覆盖和禁用此设置。

另请参阅下面的 INCOMPATIBLE OPTIONS。

不兼容的选项

以下选项:

  • --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 有两个主要后端:applymerge。(apply 后端以前称为 am 后端,但名称会引起混淆,因为它看起来像动词而不是名词。此外,merge 后端以前称为交互式后端,但现在也用于非交互式场景。两者都基于支撑它们的底层功能进行了重命名。)这两种后端在行为上存在一些细微的差异:

空提交

apply 后端不幸地丢弃了有意为空的提交,即最初为空的提交,尽管这在实践中很少见。它还会丢弃变为空的提交,并且没有选项可以控制此行为。

merge 后端默认保留有意为空的提交(尽管使用 -i 时,它们会在 todo 列表编辑器中标记为空,或者可以使用 --no-keep-empty 自动丢弃)。

与 apply 后端类似,默认情况下,merge 后端会丢弃变为空的提交,除非指定了 -i/--interactive(在这种情况下,它会停止并询问用户该怎么做)。merge 后端还有一个 --empty=(drop|keep|stop) 选项,用于更改处理变为空的提交的行为。

目录重命名检测

由于缺乏准确的树信息(这源于使用补丁中有限的信息构建的伪祖先),apply 后端禁用了目录重命名检测。禁用的目录重命名检测意味着,如果历史的一侧重命名了一个目录,而另一侧向旧目录添加了新文件,那么这些新文件将留在旧目录中,而不会在 rebase 时警告您可能希望将这些文件移动到新目录中。

目录重命名检测与merge后端协同工作,在这种情况下会为您提供警告。

上下文

apply后端的工作方式是创建一个补丁序列(通过内部调用format-patch),然后按顺序应用补丁(内部调用am)。补丁由多个块组成,每个块都有行号、上下文区域和实际的更改。行号需要考虑一些偏移量,因为另一方可能在文件前面插入或删除了行。上下文区域旨在帮助找到如何调整行号以将更改应用到正确的行。但是,如果代码的多个区域具有相同的周围上下文行,则可能会选择错误的区域。现实情况是,这导致提交被错误地重新应用而没有报告任何冲突。将diff.context设置为更大的值可以防止此类问题,但会增加出现误报冲突的可能性(因为它需要更多匹配的上下文行才能应用)。

merge后端使用每个相关文件的完整副本进行工作,使其免受此类问题的影响。

冲突标记的标注

当存在内容冲突时,合并机制会尝试用内容来源的提交来标注双方的冲突标记。由于apply后端会丢弃有关被变基提交及其父提交的原始信息(而是根据生成的补丁中的有限信息生成新的伪造提交),因此无法识别这些提交;它必须回退到提交摘要。此外,当merge.conflictStyle设置为diff3zdiff3时,apply后端将使用“构造的合并基”来标注来自合并基的内容,因此不提供任何有关合并基提交的信息。

merge后端使用历史记录双方的完整提交进行工作,因此没有此类限制。

钩子

apply后端传统上不调用post-commit钩子,而merge后端则调用。两者都调用了post-checkout钩子,尽管merge后端会抑制其输出。此外,两个后端都仅使用变基的起始点提交调用post-checkout钩子,而不使用中间提交或最终提交。在这两种情况下,调用这些钩子都是由于实现的偶然性,而不是设计使然(两个后端最初都是作为shell脚本实现的,碰巧会调用其他命令,如git checkoutgit commit,这些命令会调用钩子)。两个后端都应该具有相同的行为,尽管尚不完全清楚哪一个(如果有的话)是正确的。我们将来可能会停止rebase调用这两个钩子中的任何一个。

可中断性

apply后端在不合时宜的中断时存在安全问题;如果用户在错误的时间按下Ctrl-C以尝试中止变基,变基可能会进入一种状态,即无法通过后续的git rebase --abort中止。merge后端似乎没有遭受同样的缺点。(有关详细信息,请参阅https://lore.kernel.org/git/20200207132152.GC2868@szeder.dev/。)

提交重写

当变基时发生冲突,变基会停止并要求用户解决。由于用户在解决冲突时可能需要进行重大更改,因此在解决冲突并运行git rebase --continue后,变基应打开编辑器并要求用户更新提交消息。merge后端会这样做,而apply后端会盲目应用原始提交消息。

杂项差异

还有一些行为差异,大多数人可能认为微不足道,但此处提及以求完整。

  • Reflog:两个后端在描述reflog中进行的更改时会使用不同的措辞,但都会使用“rebase”一词。

  • 进度、信息和错误消息:两个后端提供略有不同的进度和信息消息。此外,apply后端将错误消息(例如“您的文件将被覆盖…”)写入stdout,而merge后端则将它们写入stderr。

  • 状态目录:两个后端将它们的状态保存在.git/下的不同目录中。

合并策略

合并机制(git mergegit pull 命令)允许使用 -s 选项选择后端合并策略。一些策略还可以接受自己的选项,这些选项可以通过向 git merge 和/或 git pull 传递 -X<option> 参数来传递。

ort

这是拉取或合并一个分支时的默认合并策略。该策略只能使用三向合并算法来解析两个头。当存在多个可用于三向合并的共同祖先时,它会创建一个共同祖先的合并树,并将其用作三向合并的参考树。根据从 Linux 2.6 内核开发历史中提取的实际合并提交进行的测试,这可以减少合并冲突,而不会导致错误合并。此外,该策略可以检测和处理涉及重命名的合并。它不利用检测到的复制。该算法的名称是一个首字母缩略词(“Ostensibly Recursive’s Twin”),源于它是作为先前默认算法 recursive 的替代品而编写的。

在路径是子模块的情况下,如果合并一方使用的子模块提交是合并另一方使用的子模块提交的后代,Git 会尝试快进到后代。否则,Git 会将这种情况视为冲突,并建议一个与冲突的子模块提交的后代作为解决方案(如果存在)。

ort 策略可以接受以下选项:

ours

此选项强制冲突的块自动干净地解决,优先采用我们的版本。来自另一棵树但与我们这方没有冲突的更改会反映在合并结果中。对于二进制文件,整个内容将来自我们这边。

这不应与 ours 合并策略混淆,后者根本不查看另一棵树包含的内容。它丢弃了另一棵树所做的所有更改,声称我们的历史包含了发生的所有事件。

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>]

此选项是子目录策略的高级形式,在该策略中,合并时策略会猜测两个树必须如何偏移才能匹配。相反,指定的路径会作为前缀(或从开头剥离)以使两个树的形状匹配。

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的含义。另请参阅下面的“从上游rebase恢复”。

运行变基时,它将首先执行pre-rebase钩子(如果存在)。您可以使用此钩子进行健全性检查,如果变基不合适,则拒绝变基。请参阅模板pre-rebase钩子脚本以获取示例。

完成后,<branch>将是当前分支。

交互模式

交互式变基意味着您可以有机会编辑被变基的提交。您可以重新排序提交,也可以删除它们(剔除不良的或不需要的补丁)。

交互模式用于此类工作流程:

  1. 有一个绝妙的想法

  2. 编写代码

  3. 准备系列以供提交

  4. 提交

其中第2点包含多个实例:

a)常规使用

  1. 完成值得提交的事情

  2. commit

b)独立的修复

  1. 意识到某事不起作用

  2. 修复它

  3. 提交它

有时,在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”相同的行为,只是不打开编辑器。

git rebase会在“pick”被替换为“edit”时停止,或者当命令由于合并错误而失败时停止。当您完成编辑和/或解决冲突后,可以继续使用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中启动命令(默认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将尚未提交的更改暂存起来,进行测试,并在必要时修复并修改提交。

从上游rebase恢复

对他人已在其基础上进行开发的某个分支进行变基(或其他形式的重写)是一个坏主意:任何下游的都将被迫手动修复其历史记录。本节从下游的角度解释如何进行修复。然而,真正的修复是避免最初对上游进行变基。

举例来说,假设您处于这样一种情况:有人开发了一个子系统分支,而您正在处理一个依赖于该子系统主题。您可能会得到如下历史记录:

    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 --amendreset或像filter-repo这样的完整历史重写命令。

简单情况

仅当subsystem变基前后subsystem上的更改(基于diff内容的补丁ID)完全相同时才起作用。

在这种情况下,修复很简单,因为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

然后,您可以说(针对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时,例如,为了解决对已合并到master的DownloadButton类所做的更改的合并冲突。

此变基可以使用--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命令之外,还有labelresetmerge命令。

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进行八爪合并。可以通过在调用rebase时使用--strategy参数来为所有合并指定默认策略,或者可以通过在命令的交互式列表中使用exec命令显式调用git merge并带有--strategy参数来覆盖特定的合并。请注意,当显式调用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

用于变基的默认后端。可能的选择是applymerge。将来,如果merge后端获得apply后端的所有剩余功能,此设置可能不再使用。

rebase.stat

是否显示自上次变基以来上游更改的diffstat。默认为False。

rebase.autoSquash

如果设置为true,则在交互模式下默认启用git-rebase[1]--autosquash选项。这可以用--no-autosquash选项覆盖。

rebase.autoStash

设置为true时,在操作开始前自动创建一个临时stash条目,并在操作结束后应用它。这意味着您可以在繁忙的工作树上运行rebase。但是,请谨慎使用:成功的rebase后的最终stash应用可能会导致非平凡的冲突。此选项可以通过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-cousinsno-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中使用时会被shell解释。它可以被GIT_SEQUENCE_EDITOR环境变量覆盖。如果未配置,则使用默认的提交消息编辑器。

GIT

Git[1] 套件的一部分