English ▾ 主题 ▾ 最新版本 ▾ git-merge-tree 上次更新于 2.49.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 模式。

执行合并,但不创建任何新的提交,也不从工作树或索引中读取或写入。

执行的合并将使用与 "real" git-merge[1] 相同的功能,包括

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

  • 重命名检测

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

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

  • 等等。

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

选项

--stdin

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

-z

不要在 <Conflicted file info> 部分引用文件名,并用 NUL 字符而不是换行符结束每个文件名。还要以 NUL 字符而不是换行符开始 messages 部分。有关更多信息,请参见下面的 输出

--name-only

在 Conflicted file info 部分中,不要为冲突文件输出 (mode, oid, stage, path) 元组列表,而只提供具有冲突的文件名列表(如果文件有多个冲突阶段,则不要多次列出文件名)。

--[no-]messages

将任何信息性消息(例如 "Auto-merging <path>" 或 CONFLICT 通知)写入 stdout 的末尾。如果未指定,则默认情况下,如果存在合并冲突,则包含这些消息,否则省略这些消息。

--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 选项,则将省略 mode,object 和 stage。如果传递了 -z,则“行”以 NUL 字符而不是换行符结尾。

信息性消息

此部分提供信息性消息,通常是关于冲突的。该部分的格式根据是否传递了 -z 而有很大差异。

如果传递了 -z

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

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

其中 <list-of-paths> 的格式为

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

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

  • "Auto-merging"

  • "CONFLICT (rename/delete)"

  • "CONFLICT (submodule lacks merge base)"

  • "CONFLICT (binary)"

和 <conflict-message> 是关于冲突的更详细的消息,通常(但并非总是)将 <stable-short-type-description> 嵌入其中。这些字符串可能会在将来的 Git 版本中更改。一些例子

  • "Auto-merging <file>"

  • "CONFLICT (rename/delete): <oldfile> renamed…​but deleted in…​"

  • "Failed to merge submodule <submodule> (no merge base)"

  • "Warning: cannot merge binary files: <filename>"

如果未传递 -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>

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

要避免的错误

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

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

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

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

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

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

已弃用的描述

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

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

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

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

GIT

属于 git[1] 套件

scroll-to-top