Git
英语 ▾ 主题 ▾ 版本 2.44.0 ▾ git-rebase 最后更新于 2.44.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)

说明

如果指定了 <branch>git rebase 将在执行其他任何操作之前执行自动 git switch <branch>。否则它将保留在当前分支上。

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

当前分支中由提交所做的所有更改,但不在 <upstream> 中,都将保存到一个临时区域。这是 git log <upstream>..HEAD 将显示的相同提交集;或者通过 git log 'fork_point'..HEAD,如果 --fork-point 处于活动状态(请参阅以下有关 --fork-point 的说明);或者通过 git log HEAD,如果指定了 --root 选项。

当前分支将重置为 <upstream><newbase>,如果提供了 --onto 选项。这与 git reset --hard <upstream>(或 <newbase>)具有完全相同的效果。ORIG_HEAD 被设置为指向重置前分支的提示符。

注意
如果在 rebase 期间使用了其他写入该伪引用(例如 git reset)的命令,则无法保证 ORIG_HEAD 在 rebase 结束时仍指向之前的分支提示符。但是,可以通过使用当前分支的 reflog(即 @{1},请参阅 gitrevisions[7])访问之前的分支提示符。

之前保存在临时区域中的提交随后按顺序逐个重新应用到当前分支。请注意,HEAD 中的任何提交都引入了与 HEAD..<upstream> 中的提交相同的文本更改,这些提交将被省略(即,已使用不同的提交消息或时间戳接受的上游补丁将被跳过)。

合并失败可能会阻止此过程完全自动。你必须解决任何此类合并失败并运行 git rebase --continue。另一个选择是使用 git rebase --skip 绕过导致合并失败的提交。要检出原始 <branch> 并删除 .git/rebase-apply 工作文件,请改用命令 git rebase --abort

假设存在以下历史记录,当前分支为“主题”

          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 的简写。当 rebase 退出时,topic 将仍然是检出的分支。

如果上游分支已经包含你所做的更改(例如,因为你邮寄了一个已应用于上游的补丁),那么该提交将被跳过,并且会发出警告(如果使用了合并后端)。例如,在以下历史记录(其中 A'A 引入了相同的更改集,但具有不同的提交者信息)上运行 git rebase master

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

将导致

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

以下是使用 rebase --onto 将基于一个分支的主题分支移植到另一个分支的方法,以假装你从后一个分支派生了主题分支。

首先让我们假设你的主题基于分支下一个。例如,在主题中开发的功能依赖于在下一个中找到的一些功能。

    o---o---o---o---o  master
         \
          o---o---o---o---o  next
                           \
                            o---o---o  topic

我们希望使主题从分支派生;例如,因为主题所依赖的功能已合并到更稳定的分支中。我们希望我们的树看起来像这样

    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> 参数可以是任何有效的提交标识。

发生冲突时,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|ask)

如何处理一开始不为空且不是任何上游提交的干净樱桃选择,但在重新设定后变为空(因为它们包含已上游更改的子集)的提交。使用 drop(默认值),将删除变为空的提交。使用 keep,将保留此类提交。使用 ask(由 --interactive 暗示),当应用空提交时,重新设定将停止,允许您选择是删除它、编辑更多文件还是仅提交空更改。其他选项(如 --exec)将使用 drop 默认值,除非明确指定 -i/--interactive

请注意,将保留一开始为空的提交(除非指定 --no-keep-empty),并且将检测并删除干净的樱桃选择(由 git log --cherry-mark ... 确定)作为初步步骤(除非传递 --reapply-cherry-picks--keep-base)。

另请参阅下面的不兼容选项。

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

不要在结果中保留在重新设定之前一开始为空(即未从其父项更改任何内容)的提交。默认情况下,保留一开始为空的提交,因为创建此类提交需要将 --allow-empty 覆盖标志传递给 git commit,表示用户非常有意地创建此类提交,因此希望保留它。

此标志的使用可能很少见,因为您可以通过启动交互式重新设定并删除与您不想要的提交相对应的行来摆脱一开始为空的提交。此标志作为便捷的快捷方式而存在,例如外部工具生成许多空提交且您希望删除所有这些提交的情况。

对于在变基后从非空变为空的提交,请参见 --empty 标志。

另请参阅下面的不兼容选项。

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

重新应用任何上游提交的所有干净的 cherry-pick,而不是预先删除它们。(如果这些提交在变基后变为空,因为它们包含已上游更改的子集,则 --empty 标志控制对它们的处理方式。)

在没有 --keep-base 的情况下(或者如果给出了 --no-reapply-cherry-picks),这些提交将被自动删除。由于这需要读取所有上游提交,因此在需要读取大量上游提交的大型存储库中,这可能会很昂贵。在使用合并后端时,将针对每个已删除的提交发出警告(除非给出了 --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 选项所述,ourstheirs 颠倒了。

另请参阅下面的不兼容选项。

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

显示自上次 rebase 以来上游更改的 diffstat。diffstat 还受配置选项 rebase.stat 控制。

-n
--no-stat

不要在 rebase 过程中显示 diffstat。

--no-verify

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

--verify

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

-C<n>

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

另请参阅下面的不兼容选项。

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

逐个重放所有 rebased 提交,而不是快速转发未更改的提交。这确保了 rebased 分支的整个历史记录都由新提交组成。

在还原主题分支合并后,您可能会发现此选项很有用,因为此选项使用新的提交重新创建主题分支,以便在无需“还原还原”的情况下成功重新合并(有关详细信息,请参阅还原有故障的合并操作方法)。

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

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

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

另请参阅下面的不兼容选项。

--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-cousinsno-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 命令。任何失败的命令都会中断 rebase,退出代码为 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 分别更改为 squashfixupfixup -C,并且它们被移动到它们修改的提交的正后方。可以在继续之前使用 --interactive 选项来查看和编辑待办事项列表。

使用 squash 标记创建提交的推荐方法是使用 git-commit[1]--squash--fixup--fixup=amend:--fixup=reword: 选项,这些选项将目标提交作为参数,并自动从此提交中填写新提交的主题行。

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

另请参阅下面的不兼容选项。

--autostash
--no-autostash

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

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

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

此选项在启动 rebase 后应用。它将基于以下内容为整个 rebase 保留:提供给初始git rebase的命令行选项、rebase.rescheduleFailedExec配置(参见git-config[1]或下面的“配置”),或默认为 false。

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

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

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

如果配置变量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 有两个主要后端:applymerge。(apply 后端以前称为 am 后端,但这个名称容易混淆,因为它看起来像动词而不是名词。此外,merge 后端以前称为交互式后端,但现在也用于非交互式情况。两者都根据支撑它们的底层功能进行了重命名。)这两个后端的行为有一些细微的差别

空提交

不幸的是,apply 后端会删除有意留空的提交,即从一开始就是空的提交,尽管在实践中这种情况很少见。它还会删除变成空的提交,并且没有选项来控制此行为。

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

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

目录重命名检测

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

目录重命名检测与 merge 后端一起工作,以便在这种情况向您发出警告。

上下文

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

merge 后端使用每个相关文件的完整副本,使其免受此类问题的困扰。

冲突标记的标记

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

merge 后端使用历史两侧的完整提交,因此没有此类限制。

钩子

apply 后端传统上不调用后提交钩子,而 merge 后端则调用。两者都调用后检出钩子,尽管 merge 后端会抑制其输出。此外,两个后端仅使用重新提交的起始点提交调用后检出钩子,而不是中间提交或最终提交。在每种情况下,调用这些钩子都是实现的偶然,而不是设计(两个后端最初都是作为 shell 脚本实现的,碰巧调用了其他命令,如 git checkoutgit commit,它们会调用钩子)。两个后端应该有相同行为,尽管目前尚不清楚哪个(如果有的话)是正确的。我们很可能会在未来让重新提交停止调用这些钩子。

可中断性

apply 后端在中断时机不当时存在安全问题;如果用户在错误的时间按 Ctrl-C 尝试中止重新提交,则重新提交可能会进入无法使用后续 git rebase --abort 中止的状态。merge 后端似乎没有同样的缺点。(有关详细信息,请参阅 https://lore.kernel.org/git/[email protected]/。)

提交重新表述

在重新提交期间发生冲突时,重新提交会停止并要求用户解决。由于用户可能需要在解决冲突时进行显著更改,因此在解决冲突后且用户运行了 git rebase --continue 后,重新提交应打开一个编辑器并要求用户更新提交消息。merge 后端会执行此操作,而 apply 后端会盲目应用原始提交消息。

其他差异

还有一些行为差异,大多数人可能认为无关紧要,但为了完整性起见,在此提及

  • 引用日志:两个后端在描述引用日志中所做更改时会使用不同的措辞,尽管两者都会使用“重新提交”一词。

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

  • 状态目录:两个后端在 .git/ 下的不同目录中保留其状态

合并策略

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

ort

这是拉取或合并一个分支时的默认合并策略。此策略只能使用 3 路合并算法解析两个头。当有多个可用于 3 路合并的公共祖先时,它会创建一个公共祖先的合并树,并将其用作 3 路合并的参考树。据报告,在对从 Linux 2.6 内核开发历史中获取的实际合并提交进行的测试中,这会导致更少的合并冲突,而不会导致错误合并。此外,此策略可以检测和处理涉及重命名的合并。它不会使用检测到的副本。此算法的名称是一个首字母缩写词(“表面递归的双胞胎”),并且源于它被编写为先前默认算法 recursive 的替代品这一事实。

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

  • 如果their 版本仅对一行引入空白更改,则使用our 版本;

  • 如果our 版本引入空白更改,但their 版本包含实质性更改,则使用their 版本;

  • 否则,合并将按通常方式进行。

renormalize

在解决三方合并时,此选项会对文件的三个阶段运行虚拟签出和签入。此选项旨在在合并具有不同清理过滤器或行尾规范化规则的分支时使用。有关详细信息,请参阅 gitattributes[5] 中的“合并具有不同签入/签出属性的分支”。

no-renormalize

禁用 renormalize 选项。这将覆盖 merge.renormalize 配置变量。

find-renames[=<n>]

开启重命名检测,可以选择设置相似性阈值。这是默认值。这将覆盖 merge.renames 配置变量。另请参阅 git-diff[1] --find-renames

rename-threshold=<n>

find-renames=<n> 的已弃用同义词。

subtree[=<path>]

此选项是 subtree 策略的更高级形式,其中该策略在合并时猜测如何移动两棵树以相互匹配。相反,指定路径被添加前缀(或从开头剥离)以使两棵树的形状匹配。

recursive

这只能使用三方合并算法解决两个头。当有多个可用于三方合并的公共祖先时,它会创建公共祖先的合并树,并将其用作三方合并的参考树。据报告,在对从 Linux 2.6 内核开发历史中获取的实际合并提交执行的测试中,这导致合并冲突减少,而不会导致错误合并。此外,这可以检测和处理涉及重命名的合并。它不会使用检测到的副本。这是从 Git v0.99.9k 到 v2.33.0 解决两个头的默认策略。

recursive 策略采用与 ort 相同的选项。但是,有三个附加选项,ort 忽略(上面未记录),这些选项可能对 recursive 策略有用

patience

diff-algorithm=patience 的弃用同义词。

diff-algorithm=[patience|minimal|histogram|myers]

合并时使用不同的 diff 算法,这有助于避免由于不重要的匹配行(例如来自不同函数的大括号)而发生的错误合并。另请参阅 git-diff[1] --diff-algorithm。请注意,ort 特别使用 diff-algorithm=histogram,而 recursive 默认为 diff.algorithm 配置设置。

no-renames

关闭重命名检测。这会覆盖 merge.renames 配置变量。另请参阅 git-diff[1] --no-renames

resolve

这只能使用三向合并算法解决两个头(即当前分支和您从中拉取的另一个分支)。它尝试仔细检测交叉合并歧义。它不处理重命名。

octopus

这会解决具有两个以上头的案例,但拒绝执行需要手动解决的复杂合并。它主要用于将主题分支头捆绑在一起。这是在拉取或合并多个分支时的默认合并策略。

ours

这会解决任意数量的头,但合并的结果树始终是当前分支头的树,有效地忽略了所有其他分支的所有更改。它用于取代旁系分支的旧开发历史记录。请注意,这与递归合并策略的 -Xours 选项不同。

subtree

这是一个修改后的 ort 策略。在合并树 A 和 B 时,如果 B 对应于 A 的子树,则首先调整 B 以匹配 A 的树结构,而不是在同一级别读取树。此调整也针对公共祖先树进行。

对于使用 3 路合并的策略(包括默认的 ort),如果在两个分支上都进行更改,但稍后又在一个分支上还原了更改,那么该更改将出现在合并结果中;有些人会发现此行为令人困惑。发生这种情况是因为在执行合并时只考虑了 HEAD 和合并基准,而不考虑各个提交。因此,合并算法将还原的更改视为根本没有更改,而是替换了已更改的版本。

注释

您应该了解在共享存储库上使用 git rebase 的含义。另请参阅下面的从上游 REBASE 中恢复。

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

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

交互模式

交互式 rebase 意味着您有机会编辑正在 rebase 的提交。您可以重新排列提交,也可以删除它们(清除不良或不需要的补丁)。

交互模式适用于此类工作流

  1. 有一个精彩的想法

  2. 破解代码

  3. 准备提交系列

  4. 提交

其中第 2 点包含以下几个实例

a) 常规使用

  1. 完成值得提交的内容

  2. commit

b) 独立修复

  1. 意识到某些内容不起作用

  2. 修复它

  3. 提交它

有时,b.2 中修复的内容无法修改为它修复的不太完美的提交,因为该提交深深地埋藏在一个补丁系列中。这正是交互式 rebase 的用途:在大量“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 在每次提交、测试后隐藏尚未提交的更改,并在必要时修改提交。

从上游变基中恢复

变基(或任何其他形式的重写)一个其他人基于其工作的分支是一个坏主意:它下游的任何人都被迫手动修复他们的历史记录。本节说明如何从下游的角度进行修复。然而,真正的修复方法是首先避免变基上游。

为了说明,假设您处于某人开发子系统分支的情况,并且您正在处理依赖于此子系统主题。您最终可能会得到以下历史记录

    o---o---o---o---o---o---o---o  master
	 \
	  o---o---o---o---o  subsystem
			   \
			    *---*---*  topic

如果子系统针对主系统进行变基,则会发生以下情况

    o---o---o---o---o---o---o---o  master
	 \			 \
	  o---o---o---o---o	  o'--o'--o'--o'--o'  subsystem
			   \
			    *---*---*  topic

如果您现在像往常一样继续开发,并最终将主题合并到子系统,则子系统中的提交将永远保持重复

    o---o---o---o---o---o---o---o  master
	 \			 \
	  o---o---o---o---o	  o'--o'--o'--o'--o'--M	 subsystem
			   \			     /
			    *---*---*-..........-*--*  topic

通常不赞成此类重复,因为它们会使历史记录变得混乱,从而更难跟踪。要清理内容,您需要将主题上的提交移植到新的子系统提示,即变基主题。这会产生连锁反应:主题下游的任何人都被迫也进行变基,依此类推!

有两种修复方法,将在以下小节中讨论

简单情况:更改完全相同。

如果子系统变基是简单的变基并且没有冲突,就会发生这种情况。

困难情况:更改不相同。

如果子系统变基有冲突,或使用 --interactive 来省略、编辑、合并或修复提交;或者如果上游使用 commit --amendreset 或类似 filter-repo 的完整历史重写命令,就会发生这种情况。

简单情况

仅在子系统上的更改(基于 diff 内容的补丁 ID)在子系统执行变基之前和之后完全相同的情况下才有效。

在这种情况下,修复很容易,因为 git rebase 知道跳过新上游中已经存在的更改(除非给出了 --reapply-cherry-picks)。因此,如果您说(假设您在主题上)

    $ git rebase subsystem

您最终将得到固定的历史记录

    o---o---o---o---o---o---o---o  master
				 \
				  o'--o'--o'--o'--o'  subsystem
						   \
						    *---*---*  topic

困难的情况

如果子系统更改与重新设定基准之前的更改不完全对应,事情会变得更加复杂。

注意
虽然“简单情况恢复”有时甚至在困难的情况下也会看似成功,但它可能会产生意想不到的后果。例如,通过 git rebase --interactive 删除的提交将被复活

其想法是手动告诉 git rebase “旧子系统结束并您的主题开始的地方”,即它们之间的旧合并基准是什么。您将不得不找到一种方法来命名旧子系统的最后一个提交,例如

  • 使用子系统 reflog:在 git fetch 之后,子系统 的旧提示位于 subsystem@{1}。后续获取将增加数字。(请参阅 git-reflog[1]。)

  • 相对于主题的提示:知道您的主题有三个提交,子系统的旧提示必须是 topic~3

然后,您可以通过以下方式将旧的 subsystem..topic 移植到新提示(对于 reflog 情况,并假设您已经在主题上)

    $ git rebase --onto subsystem subsystem@{1}

“困难情况”恢复的连锁反应特别糟糕:主题下游的每个人现在也必须执行“困难情况”恢复!

重新设定合并

交互式重新设定命令最初设计为处理单个补丁系列。因此,从待办事项列表中排除合并提交是有道理的,因为开发人员可能在处理分支时合并了当时的当前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>)创建,并在 rebase 完成后删除。这样,链接到同一存储库的多个工作树中的 rebase 操作就不会相互干扰。如果 label 命令失败,它会立即重新安排,并附有如何继续操作的有用消息。

reset 命令将 HEAD、索引和工作树重置到指定的修订版。它类似于 exec git reset --hard <label>,但拒绝覆盖未跟踪的文件。如果 reset 命令失败,它会立即重新安排,并附有如何编辑待办事项列表的有用消息(这通常发生在 reset 命令被手动插入到待办事项列表中并包含错别字时)。

merge 命令将指定的修订版合并到当时的 HEAD 中。使用 -C <original-commit>,将使用指定的合并提交的提交消息。当 -C 更改为小写的 -c 时,在成功合并后,消息将在编辑器中打开,以便用户可以编辑消息。

如果 merge 命令因合并冲突以外的任何原因失败(即合并操作甚至没有开始),它将立即重新安排。

默认情况下,merge 命令将使用 ort 合并策略进行常规合并,并使用 octopus 进行 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。将来,如果合并后端获得了应用后端的所有剩余功能,此设置可能不再使用。

rebase.stat

是否显示自上次变基以来上游更改的 diffstat。默认情况下为 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-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 用于编辑 rebase 指令文件时使用的文本编辑器。该值在使用时由 shell 解释。它可以被 GIT_SEQUENCE_EDITOR 环境变量覆盖。未配置时,将使用默认的提交消息编辑器。

GIT

git[1] 套件的一部分

scroll-to-top