Git
简体中文 ▾ Topics ▾ Latest version ▾ git-merge last updated in 2.44.0

名称

git-merge - 将两个或多个开发历史合并在一起

概述

git merge [-n] [--stat] [--no-commit] [--squash] [--[no-]edit]
	[--no-verify] [-s <策略>] [-X <策略选项>] [-S[<键 ID>]]
	[--[no-]allow-unrelated-histories]
	[--[no-]rerere-autoupdate] [-m <信息>] [-F <文件>]
	[--into-name <分支>] [<提交>…​]
git merge (--continue |--abort |--quit)

描述

将指定的提交内容(从它们的历史与当前分支相分离时起)并入当前分支。 这条命令被 "git pull "用来合并另一个仓库的改动,也可以用手将一个分支的改动合并到另一个分支。

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

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

然后 git merge topic 将重现 topic 分支从 master(即 E)分流到 master 之上的当前提交( C )所做的修改,并将结果与两个父提交的名称和用户描述修改的日志信息一起记录在一个新提交中。在操作之前,ORIG_HEAD`被设置为当前分支(`C)的顶端。

	  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`选项来接受自动生成的信息(但是不鼓励亲这么做)。 如果你在命令行中用`-m`选项给出一个草稿信息,并想在编辑器中编辑它,--edit(或`-e`)选项仍然有用。

旧的脚本可能依赖于不允许用户编辑合并日志信息的历史行为。他们会在运行`git merge`时看到一个编辑器被打开。为了使这些脚本更容易调整到最新的行为,可以在脚本的开头将环境变量`GIT_MERGE_AUTOEDIT`设置为`no`。

--cleanup=<模式>

这个选项决定了在提交前如何清理合并信息。更多细节见git-commit[1]。此外,如果`<模式>的值为 `scissors,在发生合并冲突时,scissors将被附加到 "MERGE_MSG "上,然后传递给提交机制。

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

指定当被合并的历史已经是当前历史的后代时,如何处理合并的问题。 默认为`--ff`,除非合并的是一个有注释的(可能是有签名的)标签,在这种情况下,`--no-ff`假设这个标签并没有存储在`refs/tags/`层次结构中的自然位置。

如果使用 --ff,可能的话,以快进方式解决合并问题(只更新分支指针以匹配合并后的分支;不创建合并提交)。 当不可能时(当合并的历史不是当前历史的后代),创建一个合并提交。

使用`--no-ff`,在所有情况下都创建一个合并提交,即使该合并可以作为一个快进解决。

使用`--ff-only`,在可能的情况下,以快进的方式解决合并的问题。 当不可能时,拒绝合并并以非零状态退出。

-S[<keyid>]
--gpg-sign[=<键 ID>]
--no-gpg-sign

对合并后的提交进行 GPG 签名。keyid 参数是可选的,默认为提交者的身份;如果指定,它必须与选项连在一起,不能有空格。--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项目使用的证书,请参见http://developercertificate.org)。 请查阅你要贡献的项目的文档或领导层,以了解该项目如何使用签名。

--no-signoff选项可以用来反驳先前在命令行上的—​signoff选项。

--stat
-n
--no-stat

在合并结束时显示一个差异状态。差异状态也由配置选项merge.stat控制。

使用-n或—​no-stat,在合并结束时不显示差异状态。

--squash
--no-squash

产生工作区和索引状态,就像发生了真正的合并一样(除了合并信息),但不实际提交,不移动`HEAD`,也不记录`$GIT_DIR/MERGE_HEAD`(以导致下一个`git commit`命令创建一个合并提交)。 这允许你在当前分支的基础上创建一个单一的提交,其效果与合并另一个分支相同(如果是多路分支,则更多)。

使用 --no-squash 进行合并并提交结果。这个选项可以用来覆盖 --squash 选项。

使用 --squash,--commit 是不允许的,而且会失败。

--[no-]verify

默认情况下,会运行 pre-merge 和 commit-msg 事件hook。 当给定 `--no-verify`时,这些将不会执行。 参见 githooks[5]

-s <策略>
--strategy=<策略>

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

-X <选项>
--strategy-option=<选项>

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

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

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

--summary
--no-summary

与 --stat 和 --no-stat 同义;这些都弃用了,将来会被删除。

-q
--quiet

安静地操作。暗指 --no-progress。

-v
--verbose

详细日志。

--progress
--no-progress

明确地打开/关闭进度。如果两者都没有指定,如果标准错误连接到终端,就会显示进度。 注意,并非所有的合并策略都支持进度报告。

--autostash
--no-autostash

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

--allow-unrelated-histories

默认情况下,`git merge`命令拒绝合并那些没有共同祖先的历史。 当合并两个独立开始的项目的历史时,这个选项可以用来覆盖这个安全性。由于这是一个非常罕见的情况,没有配置变量来默认启用,也不会被添加。

-m <消息>

设置用于合并提交的提交信息(如果创建了一个的话)。

如果指定了`--log`,正在合并的提交的简短日志将被附加到指定的消息中。

git fmt-merge-msg 命令可以为自动 git merge 调用提供一个良好的默认值。自动信息可以包括分支描述。

--into-name <分支>

准备默认的合并信息,就像合并到分支`<分支>`一样,而不是真正要合并的分支名称。

-F <文件>
--file=<文件>

读取用于合并提交的提交信息(如果创建了)。

如果指定了`--log`,正在合并的提交的简短日志将被附加到指定的消息中。

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

在 rerere 机制重用当前冲突的记录解析来更新工作树中的文件后,允许它也用解析的结果来更新索引。 --no-rerere-autoupdate`是一个很好的方法,在用单独的 `git add 提交结果到索引之前,可以反复检查 rerere 所做的事情,并抓住潜在的错误合并。

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

默认会静默地覆盖合并结果中被忽略的文件。使用`--no-overwrite-ignore`来终止。

--abort

中止当前的冲突解决过程,并尝试重建合并前的状态。工作区会自动应用自动存储条目。

如果合并开始时有未提交的工作区变化,git merge --abort 在某些情况下将无法重现这些变化。因此,建议在运行 git merge 之前,一定要提交或储存你的修改。

MERGE_HEAD 存在时,git merge --abort 等同于 git reset --merge ,除非 MERGE_AUTOSTASH`也存在,在这种情况下 `git merge --abort 会将暂存项应用到工作区重,而 git reset --merge 则会将暂存的改动保存在暂存列表中。

--quit

忘记当前正在进行的合并。让索引和工作区保持原样。如果`MERGE_AUTOSTASH`存在,储藏库条目将被保存到储藏库列表。

--continue

git merge 因冲突而停止后,你可以通过运行 git merge --continue 来结束当前合并(见下文“如何解决冲突”部分)。

<提交>…​

提交,通常是其他分支负责人,合并到我们的分支中。 指定多个提交将创建具有两个以上父项的合并(亲切地称为八爪/多路合并)。

如果命令行没有给出提交,则合并当前分支被配置为上游的远程跟踪分支。 参见本手册页的配置部分。

当指定`FETCH_HEAD`(没有其他提交)时,之前调用 git fetch`进行合并时记录在.git/FETCH_HEAD`文件中的分支会被合并到当前分支。

合并前检查

在应用外部的修改之前,你应该把自己的工作做好,并在本地提交,这样在有冲突的时候就不会被打乱了。 参见 git-stash[1]git pulgit merge 会在本地未提交的修改与 git pull/git merge 可能需要更新的文件重叠时会不做任何操作,立即停止。

为了避免在合并提交中记录不相关的变化,git pullgit merge 也会在相对于`HEAD`提交的索引中存在任何变化时中止。 (根据使用的合并策略,这一规则可能存在特殊的狭义例外,但一般来说,索引必须与HEAD相匹配。)

如果所有命名的提交都已经是`HEAD`的祖先,git merge 将提前退出,提示 "已经是最新的。"

快进式合并

通常情况下,当前的分支头是指定提交的祖先。 这是最常见的情况,特别是当从’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"程序相同的样式来呈现这样一个有冲突的大块,像这样:

这里是与共同的祖先相比没有变化的行。
祖先没有变化,或者因为只有一方发生了变化而得到干净的解决、
或者因为两边都有相同的变化而被干净地解决了。
<<<<<<< yours:sample.txt
冲突的解决是困难的;
让我们去购物吧。
=======
Git让冲突解决变得简单。
>>>>>>> theirs:sample.txt
而这里是另一行干净利落的解决或未修改的内容。

发生一对冲突变化的区域被标记为`<<<<<<<=======>>>>>>>`。 `=======`之前的部分通常是你做的修改,而之后的部分通常是别人的修改。

默认的格式并不显示原文在冲突区说了什么。 你无法知道有多少行被删除,并被替换成你方的芭比娃娃的言论。 你唯一能知道的是,你方想说这很难,你更愿意去购物,而另一方则想说这很容易。

通过将 "merge.conflictStyle "配置变量设置为 "diff3 "或 "zdiff3",可以使用另一种风格。 在 "diff3 "风格中,上述冲突可能看起来像这样:

这里是与共同的祖先相比没有变化的行。
祖先没有变化,或者因为只有一方发生了变化而得到了干净的解决、
<<<<<<< yours:sample.txt
或干净地解决了,因为双方都以同样的方式改变了。
冲突的解决是很难的;
让我们去购物吧。
||||||| base:sample.txt
或干净利落地解决了,因为双方的变化都一样。
冲突的解决是很难的。
=======
或干净利落地解决了,因为双方都有相同的变化。
Git使冲突解决变得容易。
>>>>>>> theirs:sample.txt
而这里是另一行被干净地解决或未修改的。

而在 "zdiff3" 样式中,它可能看起来像这样:

这里是与共同的祖先相比没有变化的行。
祖先没有变化,或者因为只有一方发生了变化而得到干净的解决、
或者因为两边都有相同的变化而被干净地解决了。
<<<<<<< yours:sample.txt
冲突的解决是困难的;
让我们去购物吧。
||||||| base:sample.txt
或干净利落地解决,因为双方的变化都一样。
冲突的解决是很难的。
=======
Git 让冲突解决变得简单。
>>>>>>> theirs:sample.txt
而这里是另一行被干净地解决或未修改的内容。

除了`<<<<<<<=======>>>>>>>标记外,它还使用了另一个|||||||`标记,后面是原文。 你可以看出,原文只是陈述了一个事实,而你的一方只是屈服于这个陈述而放弃了,而另一方则试图有一个更积极的态度。 你有时可以通过查看原文得出一个更好的解决方案。

如何解决冲突

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

  • 决定不进行合并。 唯一需要清理的是将索引文件重置为`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 <路径>`将首先显示`HEAD`版本的差异,然后是`MERGE_HEAD`版本。

  • 看一下原件。 `git show :1:filename`显示共同的祖先,`git show :2:filename`显示`HEAD`版本,`git show :3:filename`显示`MERGE_HEAD`版本。

实例

  • 合并分支 fixesenhancements 到当前分支之上,进行多路合并:

    $ git merge 修复增强功能
  • 将分支`obsolete`合并到当前分支,使用`ours`的合并策略:

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

    $ git merge --no-commit maint

    当您想在合并中加入进一步的更改,或者您想手动编写合并提交信息时,就可以使用此功能。

    你应该避免滥用这个选项,它可以在合并提交中偷偷地进行版本库中实质性的修改。 小的修正,例如修改发布日志内容/版本名称,是推荐的。

合并战略

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

配置

分支 .<名字>.合并操作选项

设置合并到分支 <分支名> 的默认选项。语法和支持的选项与 git merge 相同,不过目前不支持包含空格的选项值。

Warning

Missing zh_HANS-CN/includes/cmd-config-section-rest.txt

See original version for this content.

Warning

Missing zh_HANS-CN/config/merge.txt

See original version for this content.

GIT

属于 git[1] 文档

scroll-to-top