git merge
git merge
命令用于将两个或两个以上的 commit 历史合并一起。
语法
git merge [-n] [--stat] [--no-commit] [--squash] [--[no-]edit] [-s <strategy>] [-X <strategy-option>] [-S[<keyid>]] [--[no-]allow-unrelated-histories] [--[no-]rerere-autoupdate] [-m <msg>] [<commit>…]
- 合并:指定一个 commit,把它合并到当前的 commit 来。具体来讲:从目标 commit 和当前 commit (即 HEAD 所指向的 commit)分叉的位置起,把目标 commit 的路径上的所有 commit 的内容一并应用到当前 commit,然后自动生成一个新的 commit。
- 分支:当分支名称来合并的时候,是分支上的最后一次 commit。
最常用的场景有两处:
- 本地分支合并:当一个分支的开发已经完成,需要把内容合并到主分支时,用
git merge
来合并。 - 远程分支更新:把远端仓库的内容用
git fetch
取下来后,用git merge
来合并。
选项
--allow-unrelated-histories
默认情况下,git merge
命令拒绝合并不共享祖先的历史记录。在合并两个独立开始他们的生活的项目的历史时,可以使用此选项来覆盖此安全性。由于这是非常罕见的情况,因此默认情况下不存在配置变量,因此不会添加该变量。
合并示例
假设仓库有两个分支:master、branch1。当前处于 master 分支上,此时HEAD指向了 master。
git branch * master branch1git merge branch1
Git 会把 5 和 6 这两个 commit 的内容一并应用到 4 上,然后生成一个新的提交,并跳转到提交信息填写的界面,merge 操作已经帮你自动地填写简要的提交信息:
Merge branch 'branch' # Please enter a commit message to explain why this merge is necessary, # especially if it merges an updated upstream into a topic branch. # # Lines starting with '#' will be ignored, and an empty message aborts # the commit.
在提交信息修改完成后,就可以保存退出这个界面。也可以不修改,直接保存退出。至此,这次git merge
操作就算完成了。
合并情况
git merge
在做合并的时候,是有一定的自动合并能力的:
- 如果一个分支改了 A 文件,另一个分支改了 B 文件,那么合并后就是既改 A 也改 B,这个动作会自动完成。
- 如果两个分支都改了同一个文件,但一个改的是第 1 行,另一个改的是第 2 行,那么合并后就是第 1 行和第 2 行都改,也是自动完成。
- 如果两个分支修改了同一部分内容,merge 的自动算法就搞不定了。这种情况 Git 称之为:冲突(Conflict)。
解决冲突
看到冲突后,你可以做两件事:
- 决定不合并。您需要的唯一清理操作是将索引文件重置为 HEAD 提交以反向 2。并清除由 2 和 3 所做的工作树更改。
git merge --abort
可以用于此。 - 解决冲突。Git 将标记工作树中的冲突。将这些文件编辑修改去除冲突,然后
git add
添加。使用git commit
或git merge --continue
提交。git merge --continue
的意思是,在执行git commit
之前,先检查是否存在正在进行的(中断的)合并。
回退到合并前
git merge --abort
git merge --abort
只能在合并导致冲突后才能运行。git merge --abort
将中止合并过程并尝试重新构建合并前状态。
如果在合并开始时,存在未提交的工作树更改,git merge --abort
则在某些情况下无法重建这些更改。因此建议在运行之前始终提交或存储更改。
git merge --abort
相当于git reset --merge
何时 MERGE_HEAD 存在。
先验证后提交
git merge --continue
git merge --continue
只能在合并导致冲突后才能运行。它在执行git commit
之前,先检查是否存在正在进行的(中断的)合并。
您可以通过许多工具解决冲突:
- 使用 mergetool。git mergetool 启动一个图形化的合并工具,这将通过合并工作。
- 看看差异。
git diff
将显示三方差异,突出显示来自版本 HEAD 和 MERGE_HEAD 版本的更改。 - 看看每个分支的差异。
git log --merge -p path
将首先显示 HEAD 版本和 MERGE_HEAD 版本的差异。 - 看看原件。
git show :1:filename
显示共同的祖先,git show :2:filename
显示 HEAD 版本,并git show :3:filename
显示 MERGE_HEAD 版本。
示例
无冲突合并支
一旦某分支有了独立内容,你终究会希望将它合并回到你的主分支。你可以使用git merge
命令将任何分支合并到当前分支中去。假设我们创建了一个分支 topic,移除了一些文件,并将它提交到该分支,其实该分支是与我们的主分支 master 独立开来的。要想将这些移除操作包含在主分支中,你可以将 topic 分支合并回去。
git branch * master topicls README hello.rb more.txt test.txtgit merge topic Updating 8bd6d8b..8f7c949 Fast-forward more.txt | 1 - test.txt | 1 - 2 files changed, 0 insertions(+), 2 deletions(-) delete mode 100644 more.txt delete mode 100644 test.txtls README hello.rb
如果当前分支有工作未完成(工作区不是干净的:有未添加到暂存区的修改或者暂存区有内容),那么就会报错。
有冲突合并
将已命名的提交(从历史记录与当前分支分离的时间以来)的更改合并到当前分支中。该命令用于git pull
合并另一个存储库中的更改,并可用于手动合并从一个分支到另一个分支的更改。
假设存在以下历史记录,并且当前分支是 master:
A---B---C topic / D---E---F---G master
然后git merge topic
将重播在 topic 分支上发生的变化,因为它从 master(即 E )分支到其当前的 commit( C )之上 master,并且将结果记录在新的提交中,以及两个父提交的名称和日志消息来自描述更改的用户。
A---B---C topic / \ D---E---F---G---H master
不同分支中修改了相同区块的代码,需要手工修改冲突。我们看看两个分支中改了同一行代码的例子。
git branch * mastergit checkout -b topic Switched to a new branch 'topic'vim READMEgit commit -am 'fixed readme title' [topic 3ac015d] fixed readme title 1 files changed, 1 insertions(+), 1 deletions(-)
我们在某分支中修改了 README 文件中的一行,并提交了。我们再在 master 分支中对同个文件的同一行内容作不同的修改。
git checkout master Switched to branch 'master'vim READMEgit commit -am 'fixed readme title differently' [master 3cbb6aa] fixed readme title differently 1 files changed, 1 insertions(+), 1 deletions(-)
我们将 topic 分支合并到 master 分支,一个合并冲突就出现了。
git merge topic Auto-merging README CONFLICT (content): Merge conflict in README Automatic merge failed; fix conflicts and then commit the result.cat README <<<<<<< HEAD Many Hello World Examples ======= Hello World Lang Examples >>>>>>> topic This project has examples of hello world in nearly every programming language.
你可以看到,Git 在产生合并冲突的地方插入了标准的合并冲突标记。我们就手动把它解决。如果你要 Git 打开一个图形化的合并工具,可以看看 git 合并工具(比如 kdiff3、emerge、p4merge 等)。
vim README here I'm fixing the conflictgit diff diff --cc README index 9103e27,69cad1a..0000000 --- a/README +++ b/README @@@ -1,4 -1,4 +1,4 @@@ - Many Hello World Examples -Hello World Lang Examples ++Many Hello World Lang Examples This project has examples of hello world in
在 Git 中,处理合并冲突的时候有个很酷的提示。如果你执行git diff
,它会告诉你冲突的两方,和你是如何解决的。现在是时候把它标记为已解决了。在 Git 中,我们可以用git add
——要告诉 Git 文件冲突已经解决,你必须把它写入缓存区。
git status -s UU READMEgit add READMEgit status -s M READMEgit commit [master 8d585ea] Merge branch 'topic'
现在我们成功解决了合并中的冲突,并提交了结果。
简而言之使用git merge
将另一个分支并入当前的分支中去。Git 会自动以最佳方式将两个不同快照中独特的工作合并到一个新快照中去。
合并远程分支
合并远程分支时,格式:仓库名称/分支名称。当是主分支 master 时,可以省略 master,只写仓库名称。
# 合并远程分支 master 到当前分支git merge origin/master # 把远程仓库 origin 下的分支 master 和本地 master 分支,合并到当前分支git merge origin master
例如:本地有分支 master、dev。自己在 dev 上开发期间,别人把跟自己开发相关的功能开发完毕,已经推送到远程服务端,分支名称 dev2。自己需要把他的分支 dev2 合并到自己的分支 dev 上。然后再把 dev 分支推送到远程服务器端。
第一步:基于远程的分支 dev2,创建本地分支 dev2。本地分支 dev2 和远程的分支 dev2,拥有一致的提交历史以及工作目录下的代码。
git checkout -b dev2 origin/dev2
第二步:切换到本地分支 dev。然后把本地分支 dev2 合并到 dev。
git checkout devgit merge dev2
第三步:推送分支 dev 到远程服务器端。
git push origin dev
此方法,会在本地保留 dev2 分支,当其他人再次改动的时候,可以直接使用此分支,进行查看变化。
还可以,在本地不创建保留 dev2 分支,直接来合并 dev2 分支。若 dev2 分支,被开发完毕,以后不再需要跟进其变化,就采用下面的步骤:
# 更新本地分支git fetch origin # 切换到 dev 分支git checkout dev # 合并远程分支到到 dev 分支git merge origin/dev2 # 推送分支 dev 到远程服务器端。git push origin dev
合并多个分支
本地分支有 master、develop。远程分支有 origin/master、origin/feature1、origin/feature2。把远程分支 remotes/origin/feature1、remotes/origin/feature2 合并到本地分支 master 上。
git branch -a * develop master remotes/origin/HEAD -> origin/master remotes/origin/feature1 remotes/origin/feature2 remotes/origin/master
git fetch origingit checkout mastergit merge origin/feature1 origin/feature2
# 把 master 合并到 develop 中git checkout developgit merge master