Git
英语 ▾ 主题 ▾ 最新版本 ▾ git-rebase 最后更新于 2.46.1

名称

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 选项。如果你当前不在任何分支上,或者当前分支没有配置的上游分支,则变基操作将中止。

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

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

注意
如果在变基过程中使用了其他写入该伪引用(例如 git reset)的命令,则不能保证 ORIG_HEAD 在变基结束时仍指向之前分支的顶端。但是,可以使用当前分支的引用日志访问之前分支的顶端(即 @{1},请参见 gitrevisions[7])。

然后,之前保存到临时区域的提交将按顺序逐个重新应用于当前分支。请注意,HEAD 中任何引入与 HEAD..<upstream> 中提交相同文本更改的提交都将被忽略(即,如果一个补丁已经在 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 将仍然是已检出分支。

如果上游分支已经包含你所做的更改(例如,因为你发送了一个在 upstream 应用的补丁),那么该提交将被跳过,并将发出警告(如果使用 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

我们想让 topicmaster 分支分叉出来;例如,因为 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 时很有用。

也可以使用变基删除一系列提交。如果我们有以下情况

    E---F---G---H---I---J  topicA

那么命令

git rebase --onto topicA~5 topicA~3 topicA

将导致删除提交 FG

    E---H'---I'---J'  topicA

这在 FG 有缺陷或不应该作为 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 合并基础的快捷方式。最多可以省略 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>

此选项在将功能开发在 upstream 分支的基础上时很有用。在开发功能期间,upstream 分支可能会向前推进,并且可能不是在 upstream 之上进行 rebase 的最佳选择,而是将基础提交保持原样。由于基础提交保持不变,此选项意味着 --reapply-cherry-picks 以避免丢失提交。

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

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

<upstream>

要比较的 upstream 分支。可以是任何有效的提交,不一定是现有的分支名称。默认为当前分支的配置 upstream。

<branch>

工作分支;默认为 HEAD

--apply

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

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

--empty=(drop|keep|stop)

如何处理从头开始不是空的并且不是对任何 upstream 提交的干净 cherry-pick 的提交,但这些提交在 rebase 后变为空(因为它们包含已存在的 upstream 更改的子集)。

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

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

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

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

此标志的使用可能很少见,因为您可以通过启动交互式 rebase 并删除与您不想要的提交相对应的行来删除从头开始为空的提交。此标志的存在是为了提供方便的快捷方式,例如,当外部工具生成许多空提交并且您希望将它们全部删除时。

对于从头开始不为空但在 rebase 后变为空的提交,请参阅 --empty 标志。

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

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

重新应用任何 upstream 提交的所有干净 cherry-pick,而不是抢先删除它们。(如果这些提交在 rebase 后变为空,因为它们包含已存在的 upstream 更改的子集,那么它们的行为由 --empty 标志控制。)

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

--reapply-cherry-picks 允许 rebase 放弃读取所有 upstream 提交,从而可能提高性能。

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

--allow-empty-message

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

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

-m
--merge

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

请注意,rebase 合并通过在 <upstream> 分支之上重放工作分支中的每个提交来工作。因此,当发生合并冲突时,报告为我们的一方是到目前为止的 rebased 系列,从 <upstream> 开始,而他们是工作分支。换句话说,双方被交换了。

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

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

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

因为 git rebase 使用给定的策略将工作分支中的每个提交重放到 <upstream> 分支之上,所以使用 ours 策略只是从 <branch> 中清空所有补丁,这毫无意义。

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

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

将 <strategy-option> 传递给合并策略。这意味着 --merge,并且如果没有指定策略,则意味着 -s ort。请注意上面提到的 我们他们的反转,如 -m 选项中所述。

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

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

在 rerere 机制使用记录的解决方案在当前冲突上更新工作树中的文件后,允许它也使用解决方案结果更新索引。--no-rerere-autoupdate 是在使用单独的 git add 将结果提交到索引之前仔细检查 rerere 所做的事情并捕获潜在的错误合并的好方法。

-S[<keyid>]
--gpg-sign[=<keyid>]
--no-gpg-sign

GPG-sign 提交。keyid 参数是可选的,默认为提交者身份;如果指定,则必须粘贴到选项中,中间没有空格。--no-gpg-sign 用于抵消 commit.gpgSign 配置变量和之前的 --gpg-sign

-q
--quiet

保持安静。意味着 --no-stat

-v
--verbose

详细显示。意味着 --stat

--stat

显示从上次 rebase 以来 upstream 发生更改的 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 分支的整个历史记录都是由新的提交组成的。

您可能会在还原主题分支合并后发现这很有帮助,因为此选项使用新的提交重新创建主题分支,因此可以成功地重新合并它,而无需“还原还原”(有关详细信息,请参阅 还原错误的合并 How-To)。

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

使用 reflog 在计算 <branch> 引入了哪些提交时,找到 <upstream><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 一起使用,以便从您的分支中删除这些提交。

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

--ignore-whitespace

在尝试协调差异时忽略空白差异。当前,每个后端都实现了对此行为的近似实现。

apply 后端

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

merge 后端

在合并时,将仅包含空白更改的行视为未更改。不幸的是,这意味着任何旨在修改空白且没有其他内容的补丁块都将被删除,即使另一方没有发生冲突的更改。

--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 命令。任何失败的命令都将中断变基,退出代码为 1。

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

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

或者通过给出多个 --exec

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

如果使用 --autosquash,则不会为中间提交附加 exec 行,而只会出现在每个压缩/修复系列的末尾。

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

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

--root

变基从 <branch> 可到达的所有提交,而不是使用 <upstream> 对其进行限制。这使您可以变基分支上的根提交。

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

--autosquash
--no-autosquash

自动将带有特殊格式消息的提交压缩到正在变基的先前提交中。如果提交消息以“squash! ”、“fixup! ”或“amend! ”开头,则主题行的其余部分将被视为提交说明符,如果它匹配主题行或该提交的哈希值,则该说明符将匹配先前提交。如果没有任何提交完全匹配,则会考虑与提交主题开头匹配的说明符。

在变基待办事项列表中,压缩、修复和修改提交的操作从 pick 变为 squashfixupfixup -C,并且它们被移动到它们修改的提交之后。--interactive 选项可用于在继续之前查看和编辑待办事项列表。

使用压缩标记创建提交的推荐方法是使用 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 时,初始的显式 --no-reschedule-failed-exec 将被 rebase.rescheduleFailedExec=true 配置的存在所覆盖。目前,您无法将 --[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 有两个主要后端:applymerge。(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设置为diff3zdiff3时,apply 后端将使用“构造的合并基准”来标记来自合并基准的内容,因此不会提供有关合并基准提交的任何信息。

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

钩子

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

可中断性

apply 后端在遇到不适时的中断时存在安全问题;如果用户在错误的时间按下 Ctrl-C 尝试中止变基,则变基可能会进入无法通过后续的git rebase --abort中止的状态。merge 后端似乎没有遭受相同的缺陷。(有关详细信息,请参阅https://lore.kernel.org/git/[email protected]/。)

提交重写

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

其他差异

还有一些其他行为差异,大多数人可能会认为这些差异无关紧要,但为了完整起见,这里提到了这些差异。

  • Reflog:两个后端在描述 reflog 中所做的更改时将使用不同的措辞,尽管两者都会使用“变基”一词。

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

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

合并策略

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

ort

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

ort 策略可以采用以下选项

ours

此选项强制冲突的块通过支持我们的版本来自动干净地解决。来自另一棵树的不会与我们这边冲突的更改会反映在合并结果中。对于二进制文件,整个内容将从我们这边获取。

这不要与ours 合并策略混淆,该策略根本不查看另一棵树包含的内容。它会丢弃另一棵树所做的所有更改,声明我们的历史记录包含其中发生的所有更改。

theirs

这是ours 的相反;请注意,与ours 不同,没有theirs 合并策略可以与这个合并选项混淆。

ignore-space-change
ignore-all-space
ignore-space-at-eol
ignore-cr-at-eol

为了进行 3 路合并,将带有指示类型的空白更改的行视为未更改。与其他行更改混合的空白更改将不被忽略。另请参阅git-diff[1] -b-w--ignore-space-at-eol--ignore-cr-at-eol

  • 如果他们的版本仅对行引入了空白更改,则使用我们的版本;

  • 如果我们的版本引入了空白更改,但他们的版本包含重大更改,则使用他们的版本;

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

renormalize

这会在解决 3 路合并时对文件的三个阶段运行虚拟签出和签入。此选项旨在用于合并具有不同清除过滤器或行尾规范化规则的分支。有关详细信息,请参阅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

这只能使用 3 路合并算法解决两个头。当存在多个可用于 3 路合并的共同祖先时,它会创建共同祖先的合并树,并将其用作 3 路合并的参考树。据报道,通过对从 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

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

octopus

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

ours

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

subtree

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

对于使用三方合并的策略(包括默认的 ort),如果在两个分支上都进行了更改,但后来在一个分支上回退了,该更改将出现在合并结果中;有些人觉得这种行为令人困惑。之所以会发生这种情况,是因为在执行合并时只考虑了头和合并基,而不是单个提交。因此,合并算法将回退的更改视为没有更改,并代之以更改后的版本。

注意

您应该了解在共享的存储库上使用 git 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”命令一样,但首先不执行任何 cherry-picking 操作),请使用“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(默认 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

如果 子系统 相对于 master 进行变基,将会发生以下情况

    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),就会发生这种情况。

简单情况

仅适用于 子系统 上的更改(基于差异内容的补丁 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 中时,例如,为了解决与对 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 命令之外,还有 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。可以使用在调用变基时使用 --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 用于编辑变基指令文件的文本编辑器。该值旨在在使用时由 shell 解释。它可以通过 GIT_SEQUENCE_EDITOR 环境变量覆盖。如果未配置,则使用默认的提交消息编辑器。

GIT

git[1] 套件的一部分

scroll-to-top