Skip to content

git-merge

将两个或多个开发历史合并在一起。

概要

bash
git merge [-n] [--stat] [--compact-summary] [--no-commit] [--squash] [--[no-]edit]
	[--no-verify] [-s <strategy>] [-X <strategy-option>] [-S[<keyid>]]
	[--[no-]allow-unrelated-histories]
	[--[no-]rerere-autoupdate] [-m <msg>] [-F <file>]
	[--into-name <branch>] [<commit>...]
git merge (--continue | --abort | --quit)

描述

将命名提交(自从它们的历史从当前分支分叉以来)的更改合并到当前分支中。此命令被 git pull 用于合并来自另一个仓库的更改,也可用于手动将一个分支的更改合并到另一个分支中。

假设存在以下历史且当前分支是 master

          A---B---C topic
         /
    D---E---F---G master

然后 git merge topic 将在 master 之上重放自 topic 分支从 master 分叉以来(即 E)所做的更改直到其当前提交(C),并将结果记录在新提交中,同时包含两个父提交的名称和用户描述更改的日志消息。在操作之前,ORIG_HEAD 设置为当前分支的提示(G)。

          A---B---C topic
         /         \
    D---E---F---G---H master

如果存在无法自动解决的冲突或在启动合并时提供了 --no-commit,合并将停止。此时您可以运行 git merge --abortgit merge --continue

git merge --abort 将中止合并过程并尝试重建合并前的状态。但是,如果合并开始时有未提交的更改(特别是如果这些更改在合并开始后被进一步修改),git merge --abort 在某些情况下将无法重建原始(合并前)的更改。因此:在运行 git merge 时存在非平凡的未提交更改是不鼓励的:虽然可能,但在冲突的情况下可能会使您处于难以回退的状态。

选项

--commit, --no-commit

执行合并并提交结果。此选项可用于覆盖 --no-commit。 使用 --no-commit 时,执行合并并在创建合并提交之前停止,给用户一个检查和进一步调整合并结果的机会。 请注意,快速前进更新不会创建合并提交,因此无法用 --no-commit 停止这些合并。因此,如果您想确保您的分支不被合并命令更改或更新,请将 --no-ff--no-commit 一起使用。

--edit, -e, --no-edit

在提交成功的机械合并之前调用编辑器以进一步编辑自动生成的合并消息,以便用户可以解释和证明合并。--no-edit 选项可用于接受自动生成的消息(通常不鼓励这样做)。 --edit(或 -e)选项在您从命令行用 -m 选项提供草稿消息并想在编辑器中编辑时仍然有用。 较旧的脚本可能依赖于不允许用户编辑合并日志消息的历史行为。当它们运行 git merge 时将看到编辑器打开。为了更容易调整此类脚本以适应更新的行为,可以在脚本开头将环境变量 GIT_MERGE_AUTOEDIT 设置为 no

--cleanup=<mode>

:此选项确定合并消息在提交前如何清理。有关更多详细信息,请参阅 git-commit(1)。此外,如果 <mode> 给定值 scissors,则在合并冲突的情况下,剪刀线将附加到 MERGE_MSG,然后再传递给提交机制。

--ff, --no-ff, --ff-only

指定当合并的历史已经是当前历史的后代时如何处理合并。--ff 是默认值,除非合并未存储在 refs/tags/ 层次结构中自然位置的带注释(可能签名)标签,此时假定 --no-ff。 使用 --ff 时,如果可能,将合并解决为快速前进(仅更新分支指针以匹配合并的分支;不创建合并提交)。如果不可能(当合并的历史不是当前历史的后代时),创建合并提交。 使用 --no-ff 时,在所有情况下都创建合并提交,即使合并可以解决为快速前进。 使用 --ff-only 时,如果可能将合并解决为快速前进。如果不可能,拒绝合并并以非零状态退出。

-S[<key-id>], --gpg-sign[=<key-id>], --no-gpg-sign

对结果合并提交进行 GPG 签名。<key-id> 参数是可选的,默认为提交者身份;如果指定,必须紧跟选项不加空格。--no-gpg-sign 用于取消 commit.gpgSign 配置变量和之前的 --gpg-sign

--log[=<n>], --no-log

除了分支名称外,用最多 <n> 个实际合并提交的单行描述填充日志消息。另请参阅 git-fmt-merge-msg(1)。 使用 --no-log 时不列出实际合并提交的单行描述。

--signoff, --no-signoff

在提交日志消息末尾添加提交者的 Signed-off-by 尾部。签名的含义取决于您提交的项目。例如,它可能证明提交者有权在项目许可下提交工作或同意某些贡献者声明,例如开发者原产地证书。(有关 Linux 内核和 Git 项目使用的证书,请参阅 https://developercertificate.org。)请查阅您贡献的项目的文档或领导层以了解签名在该项目中的使用方式。 --no-signoff 选项可用于取消命令行上之前的 --signoff 选项。

--stat, -n, --no-stat

在合并结束时显示差异统计。差异统计也由配置选项 merge.stat 控制。 使用 -n--no-stat 时,不在合并结束时显示差异统计。

--compact-summary

:在合并结束时显示紧凑摘要。

--squash, --no-squash

生成工作树和索引状态,如同发生了真正的合并(除了合并信息),但实际上不创建提交、移动 HEAD 或记录 $GIT_DIR/MERGE_HEAD(以使下一个 git commit 命令创建合并提交)。这允许您在当前分支之上创建一个单个提交,其效果与合并另一个分支(或在章鱼合并的情况下更多分支)相同。 使用 --no-squash 时,执行合并并提交结果。此选项可用于覆盖 --squash。 使用 --squash 时,不允许 --commit,并将失败。

--verify, --no-verify

默认情况下,运行 pre-merge 和 commit-msg 钩子。给出 --no-verify 时,这些被绕过。另请参阅 githooks(5)

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

使用给定的合并策略;可以多次提供以指定它们应尝试的顺序。如果没有 -s 选项,则使用内置的策略列表(合并单个头时为 ort,否则为 octopus)。

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

将合并策略特定选项传递给合并策略。

--verify-signatures, --no-verify-signatures

验证正在合并的侧面分支的提示提交是否用有效密钥签名,即具有有效 uid 的密钥:在默认信任模型中,这意味着签名密钥已被受信任的密钥签名。如果侧面分支的提示提交未用有效密钥签名,则中止合并。

--summary, --no-summary, --stat, --no-stat

--stat--no-stat 的同义词;这些已弃用,将来将被移除。

-q, --quiet, --no-progress

安静地操作。隐含 --no-progress

-v, --verbose

详细。

--progress, --no-progress

显式开启/关闭进度。如果未指定,当标准错误连接到终端时显示进度。请注意,并非所有合并策略都支持进度报告。

--autostash, --no-autostash

在操作开始前自动创建临时存储条目,记录在引用 MERGE_AUTOSTASH 中,并在操作结束后应用它。这意味着您可以在脏工作树上运行操作。但是,请谨慎使用:成功合并后的最终存储应用可能会导致非平凡的冲突。

--allow-unrelated-histories

:默认情况下,git merge 命令拒绝合并不共享公共祖先的历史。此选项可用于在合并两个独立开始的项目历史时覆盖此安全检查。由于这种情况非常罕见,不存在也不会添加默认启用此功能的配置变量。

-m <msg>

:设置用于合并提交的提交消息(如果创建了一个)。 如果指定了 --log,正在合并的提交的简短日志将附加到指定的消息。

--into-name <branch>

:准备默认合并消息,如同合并到分支 <branch>,而不是实际合并到的分支的名称。

-F <file>, --file=<file>

从给定文件读取用于合并提交的提交消息(如果创建了一个)。 如果指定了 --log,正在合并的提交的简短日志将附加到指定的消息。

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

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

--overwrite-ignore, --no-overwrite-ignore

静默覆盖合并结果中的被忽略文件。这是默认行为。使用 --no-overwrite-ignore 中止。

--abort

:中止当前的冲突解决过程,并尝试重建合并前的状态。如果存在自动存储条目,则将其应用到工作树。 如果合并开始时存在未提交的工作树更改,git merge --abort 在某些情况下将无法重建这些更改。因此建议在运行 git merge 之前始终提交或存储您的更改。 git merge --abort 等同于存在 MERGE_HEAD 时的 git reset --merge,除非同时存在 MERGE_AUTOSTASH,此时 git merge --abort 将存储条目应用到工作树,而 git reset --merge 将存储的更改保存在存储列表中。

--quit

:忘记正在进行的合并。保持索引和工作树原样。如果存在 MERGE_AUTOSTASH,存储条目将保存到存储列表中。

--continue

:在 git merge 因冲突停止后,您可以通过运行 git merge --continue 来完成合并(请参阅下面的"如何解决冲突"部分)。

<commit>...

:要合并到我们分支中的提交,通常是其他分支头。指定多个提交将创建具有两个以上父提交的合并(亲切地称为章鱼合并)。 如果命令行上未给出提交,合并当前分支配置为其上游的远程跟踪分支。另请参阅本手册页的配置部分。 当指定 FETCH_HEAD(且没有其他提交)时,前一次调用 git fetch 记录在 .git/FETCH_HEAD 文件中的用于合并的分支将被合并到当前分支。

合并前检查

在应用外部更改之前,您应该将自己的工作整理好并在本地提交,以便在存在冲突时不会被破坏。另请参阅 git-stash(1)git pullgit merge 在本地未提交的更改与 git pull/git merge 可能需要更新的文件重叠时将停止而不做任何事情。

为避免在合并提交中记录不相关的更改,如果索引相对于 HEAD 提交有任何注册的更改,git pullgit merge 也将中止。(根据使用的合并策略,此规则可能存在特殊的狭义例外,但通常索引必须匹配 HEAD。)

如果所有命名的提交已经是 HEAD 的祖先,git merge 将提前退出并显示消息 "Already up to date."。

快速前进合并

通常当前分支头是命名提交的祖先。这是最常见的情况,特别是从 git pull 调用时:您正在跟踪上游仓库,没有提交本地更改,现在您想更新到更新的上游修订。在这种情况下,不需要新提交来存储组合的历史;相反,HEAD(连同索引)被更新为指向命名的提交,而不创建额外的合并提交。

此行为可以用 --no-ff 选项抑制。

真正合并

除了快速前进合并(见上文)外,要合并的分支必须通过一个将它们都作为其父提交的合并提交连接在一起。

一个协调所有要合并分支的更改的合并版本被提交,您的 HEAD、索引和工作树被更新到它。可以在工作树中有修改,只要它们不重叠;更新将保留它们。

当如何协调更改不明显时,会发生以下情况:

  1. HEAD 指针保持不变。
  2. MERGE_HEAD 引用设置为指向另一个分支头。
  3. 干净合并的路径在索引文件和工作树中都被更新。
  4. 对于冲突的路径,索引文件记录最多三个版本:阶段 1 存储公共祖先的版本,阶段 2 来自 HEAD,阶段 3 来自 MERGE_HEAD(您可以用 git ls-files -u 检查阶段)。工作树文件包含合并操作的结果;即带有熟悉的冲突标记 +<<<+ === +>>>+ 的三方合并结果。
  5. 写入名为 AUTO_MERGE 的引用,指向对应于工作树当前内容的树(包括文本冲突的冲突标记)。请注意,此引用仅在使用 ort 合并策略(默认)时写入。
  6. 不做其他更改。特别是,您在开始合并之前有的本地修改将保持不变,它们的索引条目保持原样,即匹配 HEAD

如果您尝试的合并导致复杂冲突并想重新开始,可以用 git merge --abort 恢复。

合并标签

合并带注释的(可能签名的)标签时,Git 总是创建合并提交,即使可以快速前进合并,并且提交消息模板用标签消息准备。此外,如果标签已签名,签名检查将作为注释报告在消息模板中。另请参阅 git-tag(1)

当您只想与导致恰好被标记的提交的工作集成时,例如与上游发布点同步,您可能不希望创建不必要的合并提交。

在这种情况下,您可以在将标签提供给 git merge 之前自己"解包"标签,或者在您自己没有任何工作时传递 --ff-only。例如:

git fetch origin
git merge v1.2.3^0
git merge --ff-only v1.2.3

冲突如何呈现

在合并期间,工作树文件被更新以反映合并的结果。在对公共祖先版本所做的更改中,不重叠的更改(即您更改了文件的某个区域而另一方保持该区域不变,反之亦然)被逐字纳入最终结果。然而,当双方都对同一区域做了更改时,Git 无法随机选择一方而不是另一方,并要求您通过保留双方对该区域所做的更改来解决它。

默认情况下,Git 使用与 RCS 套件中的 "merge" 程序使用的相同样式来呈现此类冲突块,如下所示:

Here are lines that are either unchanged from the common
ancestor, or cleanly resolved because only one side changed,
or cleanly resolved because both sides changed the same way.
<<<<<<< yours:sample.txt
Conflict resolution is hard;
let's go shopping.
=======
Git makes conflict resolution easy.
>>>>>>> theirs:sample.txt
And here is another line that is cleanly resolved or unmodified.

发生冲突的一对更改的区域用标记 +<<<<<<<+、======= 和 +>>>>>>>+ 标记。======= 之前的部分通常是您这边,之后的部分通常是他们那边。

默认格式不显示原始内容在冲突区域中的样子。您无法判断在您这边有多少行被删除并替换为 Barbie 的评论。您唯一能判断的是您这边想说它很难而您更愿意去购物,而另一方想声称它很容易。

可以通过将 merge.conflictStyle 配置变量设置为 diff3zdiff3 来使用替代样式。在 diff3 样式中,上面的冲突可能如下所示:

Here are lines that are either unchanged from the common
ancestor, or cleanly resolved because only one side changed,
<<<<<<< yours:sample.txt
or cleanly resolved because both sides changed the same way.
Conflict resolution is hard;
let's go shopping.
||||||| base:sample.txt
or cleanly resolved because both sides changed identically.
Conflict resolution is hard.
=======
or cleanly resolved because both sides changed the same way.
Git makes conflict resolution easy.
>>>>>>> theirs:sample.txt
And here is another line that is cleanly resolved or unmodified.

zdiff3 样式中,它可能如下所示:

Here are lines that are either unchanged from the common
ancestor, or cleanly resolved because only one side changed,
or cleanly resolved because both sides changed the same way.
<<<<<<< yours:sample.txt
Conflict resolution is hard;
let's go shopping.
||||||| base:sample.txt
or cleanly resolved because both sides changed identically.
Conflict resolution is hard.
=======
Git makes conflict resolution easy.
>>>>>>> theirs:sample.txt
And here is another line that is cleanly resolved or unmodified.

除了 +<<<<<<<+、======= 和 +>>>>>>>+ 标记外,它使用另一个 +|||||||+ 标记,后跟原始文本。您可以看到原始内容只是陈述了一个事实,而您这边只是屈服于该陈述并放弃了,而另一方试图采取更积极的态度。有时通过查看原始内容可以想出更好的解决方案。

如何解决冲突

看到冲突后,您可以做两件事:

  • 决定不合并。您需要的唯一清理是将索引文件重置为 HEAD 提交以撤消 2.,并清理 2. 和 3. 所做的工作树更改;git merge --abort 可用于此目的。

  • 解决冲突。Git 将在工作树中标记冲突。将文件编辑成形并 git add 它们到索引。使用 git commitgit merge --continue 来完成交易。后一个命令在调用 git commit 之前检查是否有(中断的)合并正在进行。

您可以用多种工具解决冲突:

  • 使用合并工具。git mergetool 启动图形合并工具,它将与您一起解决合并。

  • 查看差异。git diff 将显示三方差异,高亮来自 HEADMERGE_HEAD 版本的更改。git diff AUTO_MERGE 将显示您为解决文本冲突所做的更改。

  • 查看来自每个分支的差异。git log --merge -p <path> 将首先显示 HEAD 版本的差异,然后显示 MERGE_HEAD 版本的差异。

  • 查看原始内容。git show :1:filename 显示公共祖先,git show :2:filename 显示 HEAD 版本,git show :3:filename 显示 MERGE_HEAD 版本。

示例

  • 在当前分支之上合并分支 fixesenhancements,进行章鱼合并:

    $ git merge fixes enhancements
  • 使用 ours 合并策略将分支 obsolete 合并到当前分支:

    $ git merge -s ours obsolete
  • 将分支 maint 合并到当前分支,但不自动创建新提交:

    $ git merge --no-commit maint

    当您想对合并进行进一步更改或想编写自己的合并提交消息时,可以使用此选项。您应避免滥用此选项将实质性更改偷偷放入合并提交中。小的修正如更新发布/版本名称是可以接受的。

合并策略

合并机制(git mergegit pull 命令)允许通过 -s 选项选择后端"合并策略"。某些策略还可以接受自己的选项,可以通过向 git merge 和/或 git pull 给出 -X<option> 参数来传递。

ort

:这是拉取或合并一个分支时的默认合并策略。此策略只能使用三方合并算法解决两个头。当有多个可用于三方合并的公共祖先时,它创建公共祖先的合并树并将其用作三方合并的参考树。据报道,这会导致更少的合并冲突而不会在 Linux 2.6 内核开发历史的实际合并提交上引起错误合并。此外,此策略可以检测和处理涉及重命名的合并。它不利用检测到的复制。此算法的名称是一个缩写("Ostensibly Recursive's Twin"),来自它是作为先前默认算法 recursive 的替代品编写的事实。 在路径是子模块的情况下,如果合并一方使用的子模块提交是另一方使用的子模块提交的后代,Git 尝试快速前进到后代。否则,Git 将此情况视为冲突,建议作为解决方案使用冲突提交的后代子模块提交(如果存在)。 ort 策略可以接受以下选项:

  • ours:此选项通过优先选择"我们的"版本强制冲突块被自动干净解决。来自其他树的不与我们方冲突的更改反映在合并结果中。对于二进制文件,整个内容取自我方。这不应与 ours 合并策略混淆,后者根本不看另一棵树包含什么。它丢弃另一棵树所做的所有事情,宣布"我们的"历史包含其中发生的所有事情。
  • theirs:这与 ours 相反;请注意,与 ours 不同,没有 theirs 合并策略可以与此合并选项混淆。
  • ignore-space-changeignore-all-spaceignore-space-at-eolignore-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
  • no-renames:关闭重命名检测。这覆盖 merge.renames 配置变量。另请参阅 git-diff(1) --no-renames
  • diff-algorithm=(histogram|minimal|myers|patience):在合并时使用不同的差异算法,这可以帮助避免由于不重要的匹配行(如来自不同函数的花括号)导致的错误合并。另请参阅 git-diff(1) --diff-algorithm。请注意,ort 默认为 diff-algorithm=histogram,而常规差异目前默认为 diff.algorithm 配置设置。
  • subtree[=<path>]:此选项是 'subtree' 策略的更高级形式,策略猜测合并时两棵树必须如何移动以相互匹配。相反,指定的路径被前缀(或从开头剥离)以使两棵树的形状匹配。

recursive

:现在是 ort 的同义词。在 v2.49.0 之前它是替代实现,但在 v2.50.0 中被重定向为表示 ort。以前的递归策略是从 Git v0.99.9k 到 v2.33.0 解决两个头的默认策略。

resolve

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

octopus

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

ours

:这解决任意数量的头,但合并的结果树始终是当前分支头的树,有效地忽略所有其他分支的所有更改。它旨在用于取代侧面分支的旧开发历史。请注意,这与 ort 合并策略的 -Xours 选项不同。

subtree

:这是修改的 ort 策略。合并树 A 和 B 时,如果 B 对应于 A 的子树,则首先调整 B 以匹配 A 的树结构,而不是在同一级别读取树。此调整也对公共祖先树进行。 使用三方合并的策略(包括默认的 ort)中,如果在两个分支上都进行了更改,但后来在一个分支上被撤消,则该更改将存在于合并结果中;有些人觉得这种行为令人困惑。它发生是因为执行合并时只考虑头和合并基础,而不考虑单个提交。因此,合并算法认为撤消的更改根本不是更改,而用更改的版本替代。

配置

branch.<name>.mergeOptions

:设置合并到分支 <name> 的默认选项。语法和支持的选项与 git merge 相同,但目前不支持包含空白字符的选项值。 以下内容选自 git-config(1) 文档:

merge.conflictStyle

:指定合并时将冲突块写入工作树文件的样式。默认为 "merge",显示 +<<<<<<<+ 冲突标记、一方所做的更改、======= 标记、另一方所做的更改,然后是 +>>>>>>>+ 标记。替代样式 "diff3" 在 ======= 标记之前添加 +|||||||+ 标记和原始文本。"merge" 样式倾向于产生比 diff3 更小的冲突区域,因为排除了原始文本,并且当两方的行子集匹配时,它们只是从冲突区域中拉出。另一种替代样式 "zdiff3" 类似于 diff3,但当匹配行出现在冲突区域的开头或结尾附近时,将两方匹配的行从冲突区域中移除。

merge.defaultToUpstream

:如果调用 merge 时没有提交参数,通过使用存储在其远程跟踪分支中的最后观察值合并为当前分支配置的上游分支。默认为 true。

merge.ff

:默认情况下,Git 在合并当前提交的后代提交时不创建额外的合并提交。相反,当前分支的提示被快速前进。当设置为 false 时,此变量告诉 Git 在这种情况下创建额外的合并提交(等同于从命令行给出 --no-ff 选项)。当设置为 only 时,仅允许此类快速前进合并(等同于从命令行给出 --ff-only 选项)。

merge.verifySignatures

:如果为 true,这等同于 --verify-signatures 命令行选项。有关详细信息,请参阅 git-merge(1)

merge.stat

:在合并结束时在 ORIG_HEAD 和合并结果之间打印什么(如果有的话)。可能的值为:

  • false:不显示任何内容。
  • true:显示 git diff --diffstat --summary ORIG_HEAD
  • compact:显示 git diff --compact-summary ORIG_HEAD。 但任何无法识别的值(例如,未来版本的 Git 添加的值)被视为 true 而不是触发错误。默认为 true

merge.autoStash

:当设置为 true 时,在操作开始前自动创建临时存储条目,并在操作结束后应用它。这意味着您可以在脏工作树上运行合并。但是,请谨慎使用:成功合并后的最终存储应用可能会导致非平凡的冲突。此选项可被 git-merge(1)--no-autostash--autostash 选项覆盖。默认为 false

merge.tool

:控制 git-mergetool(1) 使用的合并工具。

merge.verbosity

:控制递归合并策略显示的输出量。级别 0 除最终错误消息外不输出任何内容(如果检测到冲突)。级别 1 仅输出冲突,2 输出冲突和文件更改。级别 5 及以上输出调试信息。默认为级别 2。可被 GIT_MERGE_VERBOSITY 环境变量覆盖。

另请参阅

git-fmt-merge-msg(1)git-pull(1)gitattributes(5)git-reset(1)git-diff(1)git-ls-files(1)git-add(1)git-rm(1)git-mergetool(1)

Git

git(1) 套件的一部分

基于 CC BY-NC-SA 3.0 许可发布