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

名称

git-merge-tree - 在不触及索引或工作目录的情况下执行合并

概要

git merge-tree [--write-tree] [<options>] <branch1> <branch2>
git merge-tree [--trivial-merge] <base-tree> <branch1> <branch2> (deprecated)

描述

此命令具有现代的 --write-tree 模式和已弃用的 --trivial-merge 模式。除了末尾的 已弃用的描述 部分,本文档的其余部分描述的是现代的 --write-tree 模式。

执行合并,但不创建新提交,也不读取或写入工作目录或索引。

执行的合并将使用与“真正的” git-merge[1] 相同的特性,包括:

  • 单个文件的三向内容合并

  • 重命名检测

  • 正确的目录/文件冲突处理

  • 递归祖先合并(即当存在多个合并基时,通过合并合并基来创建虚拟合并基)

  • 等等。

合并完成后,将创建一个新的顶层树对象。有关详细信息,请参阅下面的 输出

选项

--stdin

从标准输入读取要合并的提交,而不是从命令行读取。有关更多信息,请参阅下面的 输入格式。隐含 -z

-z

在 <冲突文件信息> 部分中不要引用文件名,并用 NUL 字符而不是换行符结束每个文件名。同时,用 NUL 字符而不是换行符开始消息部分。有关更多信息,请参阅下面的 输出

--name-only

在冲突文件信息部分,不为冲突文件编写 (模式, OID, 阶段, 路径) 元组列表,而是只提供冲突文件的文件名列表(如果一个文件名有多个冲突阶段,则不重复列出)。

--messages
--no-messages

将任何信息性消息(如“自动合并 <路径>”或 CONFLICT 通知)写入 stdout 的末尾。如果未指定,则默认情况下,如果存在合并冲突,则包含这些消息,否则省略它们。

--quiet

禁用程序的所有输出。当您只关心退出状态时很有用。允许 merge-tree 在找到冲突时提前退出,并允许它避免写入合并创建的大部分对象。

--allow-unrelated-histories

默认情况下,如果指定的两个分支没有共同的历史记录,merge-tree 将会报错。可以给出此标志来覆盖该检查并继续合并。

--merge-base=<tree-ish>

不查找 <branch1> 和 <branch2> 的合并基,而是指定一个合并基。此选项与 --stdin 不兼容。

目前不支持指定多个基,这意味着当合并具有多个合并基的两个分支时,使用此选项可能会导致合并结果与 git merge 计算的结果不同。这可能包括在结果合并中丢失历史记录一侧的某些更改。

使用此选项时,由于合并基直接提供,<branch1> 和 <branch2> 不需要指定提交;树就足够了。

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

将合并策略特定的选项传递给合并策略。有关详细信息,请参阅 git-merge[1]

输出

对于成功的合并,git-merge-tree 的输出只有一行。

<OID of toplevel tree>

而对于冲突的合并,输出默认格式为:

<OID of toplevel tree>
<Conflicted file info>
<Informational messages>

这些将在下面单独讨论。

但是,有一个例外。如果传递了 --stdin,则开头会有一个额外的部分,末尾有一个 NUL 字符,然后所有部分都会为每一行输入重复。因此,如果第一次合并有冲突而第二次合并是干净的,输出将是:

<Merge status>
<OID of toplevel tree>
<Conflicted file info>
<Informational messages>
NUL
<Merge status>
<OID of toplevel tree>
NUL

合并状态

这是一个整数状态,后跟一个 NUL 字符。整数状态是:

0: merge had conflicts
1: merge was clean

顶层树的 OID

这是一个树对象,代表在 git merge 结束时将检出到工作目录的内容。如果存在冲突,则此树中的文件可能包含嵌入的冲突标记。此部分始终后跟一个换行符(如果传递了 -z,则为 NUL 字符)。

冲突文件信息

这是一系列格式为:

<mode> <object> <stage> <filename>

文件名将按照 core.quotePath 配置变量的说明进行引用(请参阅 git-config[1])。但是,如果传递了 --name-only 选项,则会省略模式、对象和阶段。如果传递了 -z,则“行”以 NUL 字符而不是换行符结尾。

信息性消息

此部分提供信息性消息,通常是关于冲突。该部分的格式在是否传递 -z 方面差异很大。

如果传递了 -z

输出格式为零个或多个冲突信息记录,每条记录的格式为:

<list-of-paths><conflict-type>NUL<conflict-message>NUL

其中 <paths> 的形式为:

<number-of-paths>NUL<path1>NUL<path2>NUL...<pathN>NUL

并包含受冲突或信息性消息影响的路径(或分支名称)。此外,<conflict-message> 是关于冲突的更详细的消息,通常(但不总是)嵌入了 <stable-short-type-description>。这些字符串在未来的 Git 版本中可能会发生变化。示例如下:

  • “自动合并”

  • “冲突(重命名/删除)”

  • “冲突(子模块缺少合并基)”

  • “冲突(二进制文件)”

而 <conflict-message> 是关于冲突的更详细消息,通常(但不总是)嵌入了 <stable-short-type-description>。这些字符串在未来的 Git 版本中可能会发生变化。一些示例:

  • “自动合并 <文件>”

  • “冲突(重命名/删除):<旧文件> 被重命名…​但在…​被删除”

  • “无法合并子模块 <submodule>(无合并基)”

  • “警告:无法合并二进制文件:<文件名>”

如果未传递 -z

此部分以一个空行开头,以将其与前面的部分分隔开,然后只包含前面部分中的 <conflict-message> 信息(用换行符分隔)。这些是非稳定的字符串,不应由脚本解析,仅供人类阅读。此外,请注意,虽然 <conflict-message> 字符串通常不包含嵌入的换行符,但有时会包含。(然而,自由格式的消息永远不会包含嵌入的 NUL 字符)。因此,整个信息块供人类读者作为所有冲突消息的集合。

退出状态

对于成功的、无冲突的合并,退出状态为 0。当合并有冲突时,退出状态为 1。如果合并因某种错误而无法完成(或启动),退出状态将不是 0 或 1(并且输出是未定义的)。当传递 --stdin 时,成功合并和有冲突的合并的返回状态均为 0,如果无法完成所有请求的合并,则返回状态将不是 0 或 1。

用法说明

此命令旨在作为低级命令,类似于 git-hash-object[1]git-mktree[1]git-commit-tree[1]git-write-tree[1]git-update-ref[1]git-mktag[1]。因此,它可以作为一系列步骤的一部分,例如:

vi message.txt
BRANCH1=refs/heads/test
BRANCH2=main
NEWTREE=$(git merge-tree --write-tree $BRANCH1 $BRANCH2) || {
    echo "There were conflicts..." 1>&2
    exit 1
}
NEWCOMMIT=$(git commit-tree $NEWTREE -F message.txt \
    -p $BRANCH1 -p $BRANCH2)
git update-ref $BRANCH1 $NEWCOMMIT

请注意,当退出状态非零时,此序列中的 NEWTREE 将包含比仅一个树更多的输出。

对于冲突,输出包含与 git-merge[1] 相同的信息:

输入格式

git merge-tree --stdin 的输入格式完全基于文本。每行格式如下:

[<base-commit> -- ]<branch1> <branch2>

如果一行被 -- 分隔,则分隔符前的字符串用于指定合并的合并基,分隔符后的字符串描述要合并的分支。

要避免的错误

请勿查看生成的顶层树来查找冲突的文件;而是解析 冲突文件信息 部分。不仅解析整个树在大型存储库中会非常慢,而且还有许多类型的冲突无法通过冲突标记表示(修改/删除、模式冲突、双方更改了二进制文件、文件/目录冲突、各种重命名冲突排列等)。

请勿将空的 冲突文件信息 列表解释为干净的合并;请检查退出状态。合并可能存在冲突而没有单个文件冲突(目录重命名冲突中有几种属于此类,将来也可能添加其他类型)。

请勿尝试猜测或让用户从 冲突文件信息 列表中猜测冲突类型。那里的信息不足以做到这一点。例如:重命名/重命名(1对2)冲突(双方将同一个文件重命名为不同的名称)将导致三个不同的文件具有更高阶的阶段(但每个文件只有一个更高阶的阶段),并且无法(通过 信息性消息 部分)确定这三个文件之间的关系。文件/目录冲突也会导致一个文件只有一个更高阶的阶段。可能涉及目录重命名的冲突(当“merge.directoryRenames”未设置或设置为“conflicts”时)也会导致一个文件只有一个更高阶的阶段。在所有情况下,信息性消息 部分都包含必要的信息,尽管它并非设计为机器可解析的。

请勿假设 冲突文件信息 中的每个路径以及 信息性消息 中的逻辑冲突存在一对一映射,或者存在一对多或多对一的映射。存在多对多映射,这意味着每个路径在一次合并中可以具有多种逻辑冲突类型,而每种逻辑冲突类型可以影响多个路径。

请勿假设 信息性消息 部分中列出的所有文件名都有冲突。消息可以包含没有冲突的文件,例如“自动合并 <文件>”。

避免从 冲突文件信息 中获取 OID 并重新合并它们以向用户呈现冲突。这将丢失信息。相反,查找 顶层树的 OID 中的文件版本并显示该版本。特别是,后者将包含带有原始分支/提交和(如果涉及重命名)原始文件名的冲突标记。虽然您可以在重新合并时将原始分支/提交包含在冲突标记注释中,但 冲突文件信息 中没有原始文件名,因此您将丢失可能有助于用户解决冲突的信息。

已弃用的描述

根据 描述,与本文档的其余部分不同,本节描述了已弃用的 --trivial-merge 模式。

除了可选的 --trivial-merge 外,此模式不接受任何选项。

此模式读取三个 tree-ish,并以半 diff 格式将简单的合并结果和冲突阶段输出到标准输出。由于这是为更高级脚本消费和将结果合并回索引而设计的,因此它会省略与 <branch1> 匹配的条目。第二种形式的结果类似于三向 git read-tree -m 的功能,但它不是将结果存储在索引中,而是将条目输出到标准输出。

这种形式不仅适用性有限(简单的合并无法处理单个文件的内容合并、重命名检测、正确的目录/文件冲突处理等),而且输出格式也难以处理,即使在成功的合并中,它通常也比第一种形式性能差(尤其是在大型存储库中操作时)。

GIT

Git[1] 套件的一部分