Git
简体中文 ▾ Topics ▾ Latest version ▾ git-rebase last updated in 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`选项。 如果您当前不在任何分支上,或者当前分支没有配置上游,变基操作会中止。

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

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

Note
如果在重置过程中使用了其他写入伪引用的命令(例如 git reset),就不能保证 ORIG_HEAD 在重置结束时仍然指向前一个分支的分支提示。不过,使用当前分支的 引用日志(即 @{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。当 rebase 退出时,topic 将 仍是已签出的分支。

如果上游分支已经包含了您所做的修改(例如,因为您邮寄的补丁被应用到了上游),那么该提交将被跳过,并发出警告(如果使用了 “合并” 后台)。 例如,在以下历史记录上运行 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

Another example of --onto option is to rebase part of a branch. If we have the following situation:

                            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

这在主题 B 不依赖于主题 A 的情况下非常有用。

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

    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

MODE OPTIONS

本节中的选项不能与任何其他选项一起使用,包括不能相互使用:

--continue

解决合并冲突后,重新启动重新分区进程。

--skip

跳过当前补丁,重新启动重新分区进程。

--abort

终止变基操作并将 HEAD 重置为原始分支。如果在启动变基操作时提供了 <分支>,那么 HEAD 将被重置为 <分支>。否则,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,除非明确指定了 -i/-interactive,否则将使用默认的 drop。

需要注意的是,开始时为空的提交会被保留(除非指定了 --no-keep-empty),而干净的 cherry-picks 提交(由 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

使用合并策略重定向(默认)。

请注意,变基合并是通过在 <上游> 分支上重放工作分支的每次提交来实现的。 因此,当发生合并冲突时,被报告为 ours 的一方是迄今为止以 <上游> 为起点的重定向系列,而 theirs 则是工作分支。 换句话说,双方是对调的。

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

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

使用给定的合并策略,而不是默认的 ort。暗指 --merge

由于 git rebase 会使用给定的策略在 <上游> 分支之上重复工作分支的每次提交,因此使用 ours 策略只会清空 <分支> 中的所有补丁,这没有什么意义。

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

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

将 <策略选项> 传递给合并策略。 这意味着 --merge,如果没有指定策略,则是 --s ort。 请注意 ourstheirs 的颠倒,就像上面的 `-m`选项一样。

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

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

Be quiet. Implies --no-stat.

-v
--verbose

Be verbose. Implies --stat.

--stat

显示上次变基后上游变化的差异状态。差异状态也由配置选项 rebase.stat 控制。

-n
--no-stat

不要将差异状态作为变基过程的一部分。

--no-verify

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

--verify

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

-C<n>

确保每次更改前后至少有 <n> 行周围上下文匹配。 如果周围的上下文行数较少,则必须全部匹配。 默认情况下,不会忽略任何上下文。 暗指 --apply

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

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

单独重放所有重定向的提交,而不是快进不变的提交。 这样就能确保重建分支的整个历史都是由新提交组成的。

在还原主题分支合并后,你可能会发现这很有帮助,因为该选项会用新提交的内容重新创建主题分支,这样就可以成功地重新合并,而无需 “恢复原状”(详见如何还原故障合并)。

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

在计算哪些提交由 <分支> 引入时,使用引用日志在 <上游仓库><分支> 之间找到更好的共同祖先。

当启用 --fork-point 时,将使用 fork_point 而不是 <上游仓库> 来计算要重置的提交集,其中 fork_pointgit merge-base --fork-point <上游仓库> <分支> 命令的结果(参见 git-merge-base[1])。 如果 fork_point 最终为空,<上游仓库> 将作为备用。

如果在命令行中提供了 <上游仓库> ` 或 `--keep-base ` 参数,则默认为 `--no-fork-point,否则默认为 --fork-point。另请参阅 git-config[1] 中的 rebase.forkpoint

如果你的分支基于 <上游仓库>,但 <上游仓库> 被回退了,而你的分支包含了被删除的提交,那么可以使用 --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 模式下,没有 <上游仓库> 作为直接祖先的提交将保留其原始分支点,也就是说,被 git-log[1]--ancestry-path 选项排除在外的提交将默认保留其原始祖先。在 rebase-cousins 模式下,此类提交会被重定向到 <上游仓库>(或是`<onto>`,如果指定了)。

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

另请参阅下面的重置合并和不兼容选项。

-x <命令>
--exec <cmd>

在最终历史记录中创建提交的每一行后添加 "exec <命令>"。<命令> 将被解释为一个或多个 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

重置从 <分支> 到的所有提交,而不是用 <上游分支> 来限制它们。 这样就可以重定向分支上的根提交。

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

--autosquash
--no-autosquash

当提交日志信息以 "squash!…​" 或 "fixup!…​" 或 "amend!…​…​",并且待办事项列表中已经有一个与相同的 ... 匹配的提交时,就会自动修改 rebase -i 的待办事项列表,使标记为压扁的提交紧跟在要修改的提交之后,并将移动提交的操作从 pick 分别改为 squashfixupfixup -C。如果提交主题匹配 ...,或者 ... 指的是提交的哈希值,则该提交匹配 ...。作为后备,提交主题的部分匹配也会起作用。建议使用 git-commit[1]--fixup--fixup=amend:--fixup=reword:--squash 选项来创建 fixup/amend/squash 提交。

如果默认情况下使用配置变量 rebase.autoSquash 启用了 --autosquash 选项,则可以使用此选项覆盖并禁用此设置。

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

--autostash
--no-autostash

在操作开始前自动创建临时存储条目,并在操作结束后应用它。 这意味着你可以在脏工作树上运行变基操作。 不过,请谨慎使用:成功重储后的最终储藏应用可能会导致非实质性冲突。

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

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

尽管该选项在启动一个变基操作时就会应用,但它会在启动时根据 rebase.rescheduleFailedExec 配置(参见 git-config[1] 或下面的 “配置”)或是否提供了该选项为整个变基的设置。否则,如果存在 rebase.rescheduleFailedExec=true 配置,一开始明确的 --no-reschedule-failed-exec 就会被覆盖。

--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 when used without --keep-base

  • --update-refs

  • --root when used without --onto

此外,以下几对选项是不兼容的:

  • --keep-base and --onto

  • --keep-base and --root

  • --fork-point and --root

行为差异

git rebase 有两个主要的后台:‘应用’ 和 ‘合并’(‘应用’ 后台曾被称为 am 后台,但这个名字看起来像动词而非名词,因此引起了混淆)。 此外,‘合并’ 后台曾被称为交互式后台,但现在也用于非交互式情况。 两者都是根据各自的底层功能重新命名的)。这两个后台在行为方式上有一些微妙的区别:

Empty commits

不幸的是,‘应用’ 后台会丢弃故意为空的提交,即开始时为空的提交,不过这种情况在实践中并不多见。 它还会丢弃变为空的提交,而且没有控制这种行为的选项。

默认情况下,‘合并’ 后台会保留故意为空的提交(不过,如果使用 -i 选项,这些提交会在待办事项列表编辑器中标记为空,或者使用 --no-keep-empty 自动删除)。

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

目录重命名检测

由于缺乏准确的目录树信息(利用补丁中的有限信息构建假祖先),‘应用’ 后台禁用了目录重命名检测。 禁用目录重命名检测意味着,如果历史记录的一方重命名了一个目录,而另一方在旧目录中添加了新文件,那么新文件就会被留在旧目录中,而不会在重新编排时发出任何警告,提醒您可能需要将这些文件移到新目录中。

目录重命名检测与 ‘合并’ 后台协同工作,在这种情况下会向你发出警告。

Context

‘应用’ 后台的工作方式是创建一系列补丁(在内部调用 format-patch),然后依次应用这些补丁(在内部调用 am)。 补丁由多个块组成,每个块包含行号、上下文区域和实际更改。 行号的获取必须谨慎,因为另一方很可能在文件中插入或删除了更早的行。 上下文区域的目的是帮助找到如何调整行号,以便将更改应用到正确的行上。 但是,如果代码的多个区域有相同的上下文行,就可能选错。 在现实世界中,这种情况曾导致提交被错误地重新应用,而没有报告任何冲突。 将 diff.context 设置为更大的值可能会避免此类问题,但会增加发生虚假冲突的几率(因为需要更多行匹配的上下文才能应用)。

‘合并’ 后台使用每个相关文件的完整副本,从而避免了此类问题。

Labelling of conflicts markers

当出现内容冲突时,合并机制会尝试在每一方的冲突标记上标注内容来源的提交。 由于 ‘应用’ 后台丢弃了关于重建的提交及其父提交的原始信息(而是根据生成的补丁中的有限信息生成新的假提交),因此无法识别这些提交,而只能使用提交摘要。 此外,当 merge.conflictStyle 设置为 diff3zdiff3 时,‘应用’ 后端将使用 "constructed merge base"(构建的合并基础)来标注来自合并基础的内容,因此不会提供任何关于合并基础提交的信息。

‘合并’ 后台使用历史上双方的完整提交,因此没有这些限制。

Hooks

传统上,‘应用’ 后台并不调用提交后(post-commit)钩子,而 ‘合并’ 后台却调用了。 虽然 ‘合并’ 后台已取消了其输出,但两者都调用了检出后(post-checkout)钩子。 此外,这两个后端都只调用了变基操作的起点提交,而不是中间提交或最终提交的检出后(post-checkout)钩子。 在每种情况下,调用这些钩子都是偶然的,而不是设计出来的(两个后台最初都是以 shell 脚本的形式实现的,碰巧调用了其他会调用钩子的命令,如 git checkoutgit commit)。 这两个后台应该具有相同的行为,但目前还不完全清楚哪一个才是正确的。 我们将来可能会让变基操作停止调用这些钩子。

可中断性

如果用户在错误的时间按下 Ctrl-C 试图终止重置,重置就会进入无法通过后续的 git rebase --abort 终止的状态。 ‘合并’ 后台似乎不存在同样的缺陷。 (详见 https://lore.kernel.org/git/20200207132152.GC2868@szeder.dev/)

重写提交

当重建过程中发生冲突时,变基操作停止并要求用户解决。 由于用户可能需要在解决冲突的同时做一些显著的改动,因此在冲突解决且用户运行了 git rebase --continue 之后,变基时应打开一个编辑器并要求用户更新提交信息。 ‘合并’ 后台会这样做,而 ‘应用’ 后台则会盲目应用原始的提交信息。

其他差异

还有一些行为上的差异,大多数人可能会认为无关紧要,但为了完整起见,还是要提一下:

  • 引用日志: 两个后台在描述引用日志中的更改时会使用不同的措辞,但都会使用 “变基(rebase)” 一词。

  • 进度、信息和错误信息: 两个后端提供的进度和信息略有不同。 此外,应用后端会将错误信息(如 “您的文件将被覆盖…​…​”)写入标准输出流,而合并后台则写入标准错误流。

  • 状态目录:两个后台将状态保存在不同的目录中,分别位于`.git/`下

合并战略

合并机制(git merge`和`git pull`命令)允许用`s`选项来选择后端'合并策略'。 一些策略也可以采取自己的选项,可以通过给`git merge`和/或`git pull`的-X<选项>`参数来传递。

ort

这是拉取或合并一个分支时的默认合并策略。 这个策略只能使用三方合并算法解决两个头。 当有一个以上的共同祖先可用于三方合并时,它会创建一个共同祖先的合并树,并将其作为三方合并的参考树。 据报道,通过对Linux 2.6内核开发历史中的实际合并提交的测试,这导致了较少的合并冲突,而不会引起错误的合并。 此外,这个策略可以检测并处理涉及重命名的合并。 它并不使用检测到的副本。 这个算法的名字是一个缩写("Ostensibly Recursive’s Twin"),来自于它是作为以前的默认算法`recursive`的替代而编写的。

ort 策略可以采取以下选项:

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> 的同义词。

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]

在合并时使用不同的差异算法,这可以帮助避免由于不重要的匹配行(比如不同函数的大括号)而发生的错误合并。 参见git-diff[1] --diff-algorithm。 注意,ort 特定 diff-algorithm=histogram,而`recursive`默认为`diff.algorithm`配置设置。

no-renames

关闭重名检测。这覆盖了 merge.renames 的配置变量。 参见git-diff[1] --no-renames

resolve

这只能用三方合并算法解决两个头(即当前分支和你拉来的另一个分支)。 它试图仔细检测纵横交错的合并歧义。 它不处理重名。

octopus

这可以解决有两个以上头的情况,但拒绝做复杂的合并,需要手动解决。 它主要是用于将主题分支头捆绑在一起。 当拉动或合并一个以上的分支时,这是默认的合并策略。

ours

这可以解决任何数量的头,但合并的结果总是当前分支头的树,有效地忽略了所有其他分支的变化。 它是用来取代侧边分支的旧开发历史的。 注意,这与 recursive 合并策略的-Xours选项不同。

subtree

这是一个修正的 ort 策略。当合并树A和B时,如果B对应于A的子树,B首先被调整为与A的树结构相匹配,而不是在同一级别读取树。这种调整也是针对共同祖先树进行的。

对于使用三方合并的策略(包括默认的 ort 策略),如果在两个分支上都做了修改,但后来在其中一个分支上被撤销,那么这个修改就会出现在合并后的结果中;有些人觉得这种行为令人困惑。 出现这种情况是因为在执行合并时只考虑头部和合并基数,而不是单个提交。 因此,合并算法认为被恢复的修改根本就没有变化,而是用被修改的版本来代替。

NOTES

You should understand the implications of using git rebase on a repository that you share. See also RECOVERING FROM UPSTREAM REBASE below.

在运行变基命令时,如果存在 pre-rebase (变基前)钩子,它会首先执行该钩子。 您可以使用此钩子进行正确性检查,并在不合适时拒绝重置。 有关示例,请参阅模板 pre-rebase(变基前) 钩子脚本。

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

交互模式

交互式重定向意味着你有机会编辑被重定向的提交。 你可以重新排列提交的顺序,也可以删除它们(剔除坏的或不需要的补丁)。

互动模式就是为这种工作流程设计的:

  1. 灵机一动

  2. 敲代码

  3. 编写提交系列

  4. submit

其中第 2 点由以下几个实例组成

a) 常规使用

  1. 完成值得提交的事情

  2. commit

b) 独立修复

  1. 发现有什么东西没有用

  2. fix that

  3. 提交

有时,b.2.中修正的内容无法修正到它所修正的并不完美的提交中,因为该提交深埋在一系列补丁中。 这正是交互式变基的用途:在大量的 "a" 和 "b" 之后使用它,重新排列和编辑提交,将多个提交合并为一个提交。

从您希望保留原样的最后一次提交开始:

git rebase -i <after-this-commit>

编辑器会弹出当前分支的所有提交(忽略合并提交),这些提交都在给定提交之后。 你可以随心所欲地调整列表中提交的顺序,也可以删除它们。 列表大致如下:

选择 deadbee ,本次提交的上线
选中 fa1afe1 ,下一次提交的上线
...

上线描述纯粹是为了方便您查看;git rebase 不会查看这些描述,只会查看提交名称(本例中为 "deadbee" 和 "fa1afe1"),因此请勿删除或编辑这些名称。

用 "edit" 命令替换 "pick" 命令后,你可以告诉 git rebase 在应用该提交后停止,这样你就可以编辑文件和/或提交信息,修改提交,然后继续重置。

要中断重置(就像 "edit" 命令一样,但不会先选择任何提交),请使用 "break "命令。

如果只想编辑提交信息,请用 "reword" 命令替换 "pick" 命令。

要放弃提交,请用 "drop" 替换 "pick" 命令,或直接删除匹配的行。

如果要将两个或更多提交折叠为一个提交,请用 "squash" 或 "fixup" 代替命令 "pick" 来处理第二个及后续提交。 如果提交的作者不同,折叠后的提交将归属于第一个提交的作者。 除非使用了 "fixup -c" 命令,否则折叠提交的建议提交信息是第一个提交信息与 "squash" 命令确定的提交信息的合并,省略了 "fixup" 命令确定的提交信息。 在这种情况下,建议的提交信息只是 "fixup -c" 提交的信息,而且会打开一个编辑器允许你编辑信息。 "fixup -c" 提交的内容(补丁)仍然包含在折叠提交中。如果有多个 "fixup -c" 提交,则使用最后一个提交的信息。 您也可以使用 "fixup -C" 获得与 "fixup -c" 相同的效果,但不需要打开编辑器。

当 "pick" 命令被替换为 "编辑" 命令或命令因合并错误而失败时,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")在历史的中间点重新编译。 为此,您可以创建类似这样的待办事项列表:

选择 deadbee 执行功能 XXX
修复 f1a5c00 修复功能 XXX
执行 make
选取 c0ffeee 下一次提交的上线
编辑 deadbab 之后提交的上线
exec cd subdir; make test
...

当命令失败(即以非 0 状态退出)时,交互式变基操作就会停止,以便您有机会解决问题。您可以使用 git rebase --continue 继续。

"exec" 命令在 shell(在 $SHELL 中指定的 shell,如果未设置 $SHELL,则使用默认 shell)中执行命令,因此可以使用 shell 功能(如 "cd"、">"、";"…​…​)。命令从工作区的根目录运行。

$ git rebase -i --exec "make test"

该命令用于检查中间提交是否可编译。 待办事项列表就会变成这样:

选择 5928aea one
exec make test
选 04d0fda 二
exec make test
选择 ba46169 三
执行制作测试
选择 f4593F9 四
exec make test

拆分提交

在交互模式下,可以用 "edit"(编辑)来标记提交。 不过,这并不一定意味着 git rebase 希望编辑的结果是一个提交。 事实上,你可以撤销提交,也可以添加其他提交。 这可以用来将一个提交一分为二:

  • 使用 git rebase -i <提交>^ 开始交互式变基,其中 `<提交>`是要分割的提交。 事实上,只要包含该提交,任何提交范围都可以。

  • 用 "edit" 操作标记要分割的提交。

  • 在编辑该提交时,执行 git reset HEAD^。 其效果是将 HEAD 重绕一圈,索引也跟着重绕一圈。 但工作区保持不变。

  • 现在,将您希望在第一次提交中的改动添加到索引中。 你可以使用 git add (可能是交互式的)或 git gui (或两者)来完成。

  • 以任何合适的提交信息提交当前索引。

  • 重复最后两个步骤,直到工作区干净为止。

  • 使用 git rebase --continue 继续变基。

如果不能绝对确定中间修订是一致的(编译、通过 testsuite 等),则应使用 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

这种重复通常是不受欢迎的,因为它们会使历史记录变得杂乱无章,难以跟踪。 要清理这些重复提交,就需要将 ‘主题’ 上的提交移植到新的 ‘子系统’ tip 上,即重置 ‘主题’。 这就会产生连锁反应:‘主题’ 的任何下游提交都会被迫重置,依此类推!

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

简单的例子: 变化完全相同。

如果 ‘子系统’ 变基是简单的重置,没有冲突,就会出现这种情况。

较难的例子: 变化不一样。

如果 ‘子系统’ 变基有冲突,或者使用了 --interactive 来省略、编辑、压制或修复提交,或者上游使用了 commit --amend, reset 或完整历史重写命令(如 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
困难的例子
~~~~~

如果 ‘子系统’ 的变化与重置前的变化不完全一致,情况就会变得更加复杂。

NOTE: 虽然 ‘简单恢复案例’ 有时看似成功
      即使在疑难案件中,也可能产生意想不到的后果。
      例如,通过 `git rebase
      --interactive` 删除的提交将被**恢复**!

我们的想法是手动告诉 `git rebase` “旧的 ‘子系统’ 分支在哪里结束,而您的 ‘主题’ 分支在哪里开始”,也就是说,它们之间的旧合并基础是什么。 例如,你必须找到一种方法来命名旧 ‘子系统’ 分支的最后一次提交:

* 使用 ‘子系统’ 引用日志:在 `git fetch` 之后,‘子系统’ 的旧 tip 位于 `subsystem@{1}`。 随后的获取将增加这个数字。 (参见 git-reflog[1])

* 与 ‘主题’ 分支的 tip 有关:由于 ‘topic’ 分支有三次提交,所以 ‘子系统’ 分支的旧提示必须是 `topic~3`。

然后,您可以将旧的 `subsystem..topic`(子系统..主题)移植到新的提示中,方法是(对于引用日志的情况,假设您已经在 ‘主题’ 分支中):
$ 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` 分支。

可以使用 `--rebase-merges` 选项进行重置。 它将生成如下的待办事项列表:

标签上的

分支:重构按钮

重置到 选 123456 从下载按钮类中提取一个通用按钮类 选取 654321 对所有按钮使用按钮类 label refactor-button(重构按钮)

分支:报告一个错误

reset refactor-button (重构按钮)# 所有按钮都使用按钮类 pick abcdef 添加反馈按钮 label report-a-bug(报告一个错误)

重置到 merge -C a1b2c3 refactor-button # 合并 “重构按钮” merge -C 6f5e4d 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 <源提交>` 时,将使用指定合并提交的提交信息。如果将 `-C` 改为小写的 `-c`,合并成功后,提交信息将在编辑器中打开,以便用户编辑信息。

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

默认情况下,`merge`(合并)命令将对常规合并使用 `ort ` 合并策略,对多路合并使用 `octopus` 合并策略。 我们可以在调用变基命令时使用 `--strategy` 参数为所有合并指定一个默认策略,也可以在交互式命令列表中使用 `exec` 命令显式地调用 `git merge` 并加上 `--strategy` 参数来覆盖特定的合并策略。 需要注意的是,像这样显式调用 `git merge` 时,可以利用标签是工作区本地引用(例如,引用 `refs/rewritten/onto` 将对应标签 `onto`)这一事实来引用要合并的分支。

注意:第一条命令(`label onto`)会标注提交所基于的修订版本;`onto ` 只是一个约定俗成的名称,是对 `--onto` 选项的一种提示。

也可以通过添加 `merge <合并起点>` 命令,从头开始引入全新的合并提交。这种形式会生成暂定提交信息,并始终打开编辑器让用户编辑。例如,当一个特性分支要解决的问题不止一个,需要拆分成两个甚至更多特性分支时,这个命令就很有用。请看这个待办事项列表:

pick 192837 从 GNU Makefiles 切换到 CMake pick 5a6c7e 记录切换到 CMake 的过程 pick 918273 修复 CMake 中的 OpenSSL 检测问题 pick afbecd http:添加对 TLS v1.3 的支持 pick fdbaec 在 Windows 上修复 CMake 对 cURL 的检测

该列表中与 CMake 无关的一个提交很可能是为了修复所有因改用 CMake 而引入的错误,但它解决的是另一个问题。要把这个分支分成两个主题分支,可以这样编辑待办事项列表:

标签上的

选择 afbecd http:添加对 TLS v1.3 的支持 label tlsv1.3

reset onto pick 192837 从 GNU Makefiles 切换到 CMake pick 918273 修复 CMake 中的 OpenSSL 检测问题 pick fdbaec 在 Windows 上修复 CMake 对 cURL 的检测 pick 5a6c7e 记录切换到 CMake 的过程 label cmake

reset onto merge tlsv1.3 merge cmake

配置
--



[WARNING]
====
Missing `zh_HANS-CN/includes/cmd-config-section-all.txt`

See original version for this content.
====

[]



[WARNING]
====
Missing `zh_HANS-CN/config/rebase.txt`

See original version for this content.
====

[]


[WARNING]
====
Missing `zh_HANS-CN/config/sequencer.txt`

See original version for this content.
====

[]

GIT
---
属于 git[1] 文档
scroll-to-top