简体中文 ▾ 主题 ▾ 最新版本 ▾ git-merge-tree 最后更新于 2.50.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] 相同的功能,包括

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

  • 重命名检测

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

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

  • 等等。

合并完成后,将创建一个新的顶层树对象。详见下面的 OUTPUT

选项

--stdin

从标准输入而非命令行读取要合并的提交。更多信息请参见下面的输入格式。此选项隐含 -z

-z

在“<冲突文件信息>”部分不引用文件名,并且每个文件名以 NUL 字符而不是换行符结束。消息部分也以 NUL 字符而不是换行符开始。更多信息请参见下面的输出

--name-only

在“冲突文件信息”部分中,对于冲突文件,不输出 (模式、OID、阶段、路径) 元组列表,只提供冲突文件名列表(如果文件名有多个冲突阶段,则不重复列出)。

--[no-]messages

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

--quiet

禁用程序的所有输出。当您只对退出状态感兴趣时很有用。允许 merge-tree 在发现冲突时提前退出,并允许它避免写入大多数由合并创建的对象。

--allow-unrelated-histories

merge-tree 默认情况下,如果指定了两个分支没有共同的历史,则会报错。可以给定此标志以覆盖该检查并仍进行合并。

--merge-base=<tree-ish>

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

由于直接提供了合并基础,<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

其中 <路径列表> 的形式为

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

并包含受 <冲突消息> 中冲突或信息性消息影响的路径(或分支名称)。此外,<冲突类型> 是一个稳定的字符串,解释冲突类型,例如

  • "Auto-merging"(自动合并)

  • "CONFLICT (rename/delete)"(冲突(重命名/删除))

  • "CONFLICT (submodule lacks merge base)"(冲突(子模块缺少合并基础))

  • "CONFLICT (binary)"(冲突(二进制))

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

  • "Auto-merging <文件>"(自动合并 <文件>)

  • "CONFLICT (rename/delete): <旧文件> 重命名…​但在…​中删除"(冲突(重命名/删除):<旧文件> 重命名…​但在…​中删除)

  • "Failed to merge submodule <子模块> (no merge base)"(合并子模块 <子模块> 失败(无合并基础))

  • "Warning: cannot merge binary files: <文件名>"(警告:无法合并二进制文件:<文件名>)

如果未传递 -z

此部分以一个空行开始,将其与前几个部分分隔开,然后只包含前一节中的 <冲突消息> 信息(由换行符分隔)。这些是非稳定字符串,不应被脚本解析,仅供人类阅读。此外,请注意,虽然 <冲突消息> 字符串通常不包含嵌入的换行符,但有时会包含。(然而,自由形式的消息永远不会有嵌入的 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>

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

避免的错误

请勿通过遍历生成的顶层树来尝试查找哪些文件存在冲突;请改为解析冲突文件信息部分。在大型仓库中解析整个树对象不仅会非常慢,而且有许多类型的冲突无法用冲突标记表示(修改/删除、模式冲突、两侧都更改了二进制文件、文件/目录冲突、各种重命名冲突排列等)。

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

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

请勿假设冲突文件信息中的每个路径与信息性消息中的逻辑冲突之间存在一对一映射,也不存在一对多映射,也不存在多对一映射。多对多映射是存在的,这意味着每个路径在单次合并中可以有多种逻辑冲突类型,并且每种逻辑冲突类型可以影响多个路径。

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

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

已废弃描述

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

除了可选的 --trivial-merge 之外,此模式不接受其他选项。

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

这种形式不仅适用范围有限(简单合并无法处理单个文件的内容合并、重命名检测、正确的目录/文件冲突处理等),而且输出格式也难以处理,并且即使在成功合并的情况下,其性能通常也会低于第一种形式(特别是在大型仓库中工作时)。

Git

Git[1] 套件的一部分

scroll-to-top