分类:Git| 发布时间:2014-03-13 23:29:00
Git是一个分布式的版本管理系统,因此它允许开发者在任何时候合并修改 而不需要一个中心代码库。 在Git中要合并的所有分支都必须在同一个代码库中。
当需要合并 other_branch 到 branch 中时,你需要签出目标分支然后合并 other_branch 进来,如下所示:
$ git checkout branch
$ git merge other_branch
在你进行合并之前,最好是保持你的工作目录的整洁
让我们通过一个最简单的例子来演示如何合并两个分支
$ git init
/tmp/conflict/.git/
$ cat > file
Line 1 stuff
Line 2 stuff
Line 3 stuff
^D
$ git add file
$ git commit -m"Initial 3 line file"
[master (root-commit) 4fe8c6c] Initial 3 line file
1 file changed, 3 insertions(+)
create mode 100644 file
让我们创建另一个提交到主分支
$ cat > other_file
Here is stuff on another file!
$ git add other_file
$ git commit -m"Another file"
[master e86b4f1] Another file
1 file changed, 1 insertion(+)
create mode 100644 other_file
现在我们有一个两个提交的分支,下一步让我们切换到另一个分支,并且修改第一个文件
$ git checkout -b alternate master^
Switched to a new branch 'alternate'
$ git show-branch
* [alternate] Initial 3 line file
! [master] Another file
--
+ [master] Another file
*+ [alternate] Initial 3 line file
$ cat >> file
Line 4 alternate stuff
^D
$ git commit -a -m"Add alternate's line 4"
[alternate 74fff9a] Add alternate's line 4
1 file changed, 1 insertion(+)
好了,现在我们一共有两个分支,并且每一个分支都有差异, master分支多了一个分支,而alternate分支修改了file, 下一步让我们来合并这两个分支
$ git checkout master
Switched to branch 'master'
$ git status
# On branch master
nothing to commit, working directory clean
$ git merge alternate
Merge made by the 'recursive' strategy.
file | 1 +
1 file changed, 1 insertion(+)
现在你可以通过以下命令来查看提交图
$ git log --graph --pretty=oneline --abbrev-commit
* 55d6e40 Merge branch 'alternate'
|\
| * 74fff9a Add alternate's line 4
* | e86b4f1 Another file
|/
* 4fe8c6c Initial 3 line file
合并操作难免会有冲突,让我们演示一下发生冲突的情况
$ cat >> file
Line 5 stuff
Line 6 stuff
^D
$ git commit -a -m "Add line 5 and 6"
[master c96a1d6] Add line 5 and 6
1 file changed, 2 insertions(+)
现在,在另一个分支中修改相同的文件
$ git checkout alternate
Switched to branch 'alternate'
$ git show-branch
* [alternate] Add alternate's line 4
! [master] Add line 5 and 6
--
+ [master] Add line 5 and 6
*+ [alternate] Add alternate's line 4
$ cat >> file
Line 5 alternate stuff
Line 6 alterante stuff
$ cat file
Line 1 stuff
Line 2 stuff
Line 3 stuff
Line 4 alternate stuff
Line 5 alternate stuff
Line 6 alterante stuff
$ git diff
diff --git a/file b/file
index f92ee5a..a8694a6 100644
--- a/file
+++ b/file
@@ -2,3 +2,5 @@ Line 1 stuff
Line 2 stuff
Line 3 stuff
Line 4 alternate stuff
+Line 5 alternate stuff^M
+Line 6 alterante stuff^M
$ git commit -a -m"Add alternate line 5 and 6"
[alternate da6dced] Add alternate line 5 and 6
1 file changed, 2 insertions(+)
让我们来查看现在的情况:
$ git show-branch
* [alternate] Add alternate line 5 and 6
! [master] Add line 5 and 6
--
* [alternate] Add alternate line 5 and 6
+ [master] Add line 5 and 6
*+ [alternate^] Add alternate's line 4
下一步,让我们切换到master分支,然后尝试合并
$ git checkout master
Switched to branch 'master'
$ git merge alternate
Auto-merging file
CONFLICT (content): Merge conflict in file
Automatic merge failed; fix conflicts and then commit the result.
当发生合并冲突时,你可以使用git diff来查看发生冲突的范围
$ git diff
diff --cc file
index 6789c80,a8694a6..0000000
--- a/file
+++ b/file
@@@ -2,5 -2,5 +2,10 @@@ Line 1 stuf
Line 2 stuff
Line 3 stuff
Line 4 alternate stuff
++<<<<<<< HEAD
+Line 5 stuff
+Line 6 stuff
++=======
+ Line 5 alternate stuff
+ Line 6 alterante stuff
++>>>>>>> alternate
这个git diff明显输出了你的工作目录的文件和index中的文件的差异, 但是和传统的git diff命令输出的不同, 在发生合并冲突时的git diff命令的输出前面有两列, 第一列的+表示工作目录的文件和目标分支文件增加的部分, 而第二列表示工作目录的文件和合并分支文件增加的部分
现在,简单地将文件修改为如下内容:
$ cat file
Line 1 stuff
Line 2 stuff
Line 3 stuff
Line 4 alternate stuff
Line 5 stuff
Line 6 alterante stuff
如果你已经解决了冲突问题,可以使用git add命令添加文件
$ git add file
然后提交修改
$ git commit
Merge branch 'alternate'
Conflicts:
file
#
# It looks like you may be committing a merge.
# If this is not correct, please remove the file
# .git/MERGE_HEAD
# and try again.
# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
# On branch master
# All conflicts fixed but you are still merging.
# (use "git commit" to conclude merge)
#
# Changes to be committed:
#
# modified: file
#
修改提交信息后提交
$ git show-branch
! [alternate] Add alternate line 5 and 6
* [master] Merge branch 'alternate'
--
- [master] Merge branch 'alternate'
+* [alternate] Add alternate line 5 and 6
在上一个例子中,演示了当发生冲突时不能自动合并的情况。 现在让我们通过另一个例子来看看Git提供了什么工具来帮助我们解决冲突。
$ git init
Initialized empty Git repository in /tmp/conflict/.git/
$ echo hello > hello
$ git add hello
$ git commit -m"Initial hello file"
[master (root-commit) 78a6935] Initial hello file
1 file changed, 1 insertion(+)
create mode 100644 hello
$ git checkout -b alt
Switched to a new branch 'alt'
$ echo world >> hello
$ echo 'Yay!' >> hello
$ git commit -a -m"one world"
[alt eb916ae] one world
1 file changed, 2 insertions(+)
$ git checkout master
Switched to branch 'master'
$ echo worlds >> hello
$ echo 'Yay!' >> hello
$ git commit -a -m"All worlds"
[master ddcd6cf] All worlds
1 file changed, 2 insertions(+)
$ git merge alt
Auto-merging hello
CONFLICT (content): Merge conflict in hello
Automatic merge failed; fix conflicts and then commit the result.
和预料的一样,Git警告你文件 hello 发生冲突了
当git merge因为冲突失败后,会在index文件中标记冲突的文件
$ git status
# On branch master
# You have unmerged paths.
# (fix conflicts and run "git commit")
#
# Unmerged paths:
# (use "git add <file>..." to mark resolution)
#
# both modified: hello
#
no changes added to commit (use "git add" and/or "git commit -a")
$ git ls-files -u
100644 ce013625030ba8dba906f756967f9e9ca394464a 1 hello
100644 e63164d9518b1e6caf28f455ac86c8246f78ab70 2 hello
100644 562080a4c6518e1bf67a9f58a32a67bff72d4f00 3 hello
当发生冲突时,Git会将冲突的部分标记出来
$ cat hello
hello
<<<<<<< HEAD
worlds
=======
world
>>>>>>> alt
Yay!
其中<<<<<<<这一行和=======这一行之间的部分是来自目标分支的, 而=======和>>>>>>>直接的内容是来自于要合并的分支的。
当发生合并冲突时git diff会输出其特有的格式
$ git diff
diff --cc hello
index e63164d,562080a..0000000
--- a/hello
+++ b/hello
@@@ -1,3 -1,3 +1,7 @@@
hello
++<<<<<<< HEAD
+worlds
++=======
+ world
++>>>>>>> alt
Yay!
什么意思?它只是简单地输出了两个差异的合成, 第一个对应HEAD,第二个对应要合并的分支MERGE_HEAD
$ git diff HEAD
diff --git a/hello b/hello
index e63164d..1f2f61c 100644
--- a/hello
+++ b/hello
@@ -1,3 +1,7 @@
hello
+<<<<<<< HEAD
worlds
+=======
+world
+>>>>>>> alt
Yay!
$ git diff MERGE_HEAD
diff --git a/hello b/hello
index 562080a..1f2f61c 100644
--- a/hello
+++ b/hello
@@ -1,3 +1,7 @@
hello
+<<<<<<< HEAD
+worlds
+=======
world
+>>>>>>> alt
Yay!
在新版本的Git中,git diff --ours等同于git diff HEAD。 而git diff --theirs等同于git diff MERGE_HEAD。 git diff --base等同于git diff $(git merge-base HEAD MERGE_HEAD)。
如果你将hello文件修改为
$ cat hello
hello
worldly ones
Yay!
那么此时git diff的结果将为:
$ git diff
diff --cc hello
index e63164d,562080a..0000000
--- a/hello
+++ b/hello
@@@ -1,3 -1,3 +1,3 @@@
hello
- worlds
-world
++worldly ones
Yay!
另外,如果你将hello选择为其中一个分支的版本比如:
$ cat hello
hello
world
Yay!
那么git diff的输出将为:
$ git diff
diff --cc hello
index e63164d,562080a..0000000
--- a/hello
+++ b/hello
当你选择其中一个分支的版本时, git diff会认为你不关心和其他分支的差异, 因而将不会将这个差异输出。
当你在解决冲突时,你可以使用如下命令查看修改是从哪里来的,以及修改的原因。
$ git log --merge --left-right -p
commit < ddcd6cf8fce9aa33d8c04d2b99694047bc30cbbc
Author: Jianlong Chen <jianlong99@gmail.com>
Date: Mon Feb 3 22:54:50 2014 +0800
All worlds
diff --git a/hello b/hello
index ce01362..e63164d 100644
--- a/hello
+++ b/hello
@@ -1 +1,3 @@
hello
+worlds
+Yay!
commit > eb916aed33f6128a216411a0e95e3313f54bfdb9
Author: Jianlong Chen <jianlong99@gmail.com>
Date: Mon Feb 3 22:54:20 2014 +0800
one world
diff --git a/hello b/hello
index ce01362..562080a 100644
--- a/hello
+++ b/hello
@@ -1 +1,3 @@
hello
+world
+Yay!
选项说明如下:
我们先用 git ls-files 来看看index的内容:
$ git ls-files -s
100644 ce013625030ba8dba906f756967f9e9ca394464a 1 hello
100644 e63164d9518b1e6caf28f455ac86c8246f78ab70 2 hello
100644 562080a4c6518e1bf67a9f58a32a67bff72d4f00 3 hello
可以看到确实有3个版本的hello
$ git cat-file -p e63164d951
hello
worlds
Yay!
$ git diff :1:hello :3:hello
diff --git a/:1:hello b/:3:hello
index ce01362..562080a 100644
--- a/:1:hello
+++ b/:3:hello
@@ -1 +1,3 @@
hello
+world
+Yay!
接下来让我们解决冲突
$ cat hello
hello
everyone
Yay!
$ git add hello
$ git ls-files -s
100644 ebc56522386c504db37db907882c9dbd0d05a0f0 0 hello
可以看到index只剩下修改后的hello了。 最后,你可以使用 git commit 来提交最后的结果 并且使用 git show 来查看合并的提交:
$ cat .git/MERGE_MSG
Merge branch 'alt'
Conflicts:
hello
$ git commit
[master 41ce868] Merge branch 'alt'
$ git show
commit 41ce8680c3ae210ee574b25fe46dc18e1f7a864f
Merge: ddcd6cf eb916ae
Author: Jianlong Chen <jianlong99@gmail.com>
Date: Tue Feb 4 09:17:54 2014 +0800
Merge branch 'alt'
Conflicts:
hello
diff --cc hello
index e63164d,562080a..ebc5652
--- a/hello
+++ b/hello
@@@ -1,3 -1,3 +1,3 @@@
hello
- worlds
-world
++everyone
Yay!
如果你要取消合并或者发现合并出错了想要重新合并可以使用以下命令:
$ git reset --hard HEAD
如果你已经提交了合并,想取消合并可以使用以下命令:
$ git reset --hard ORIG_HEAD
需要注意的是,如果你是在不整洁的工作目录下进行合并的, 那么你将丢失所有的未提交修改。
以下两种情况属于退化的合并
有三种合并策略:
默认的合并策略为 Recursive
以下合并策略主要用于特殊情况:
可以通过git commit -s strategy 来指定合并合并。
比如:
$ git merge -s resolve Bob
本文中介绍的所有合并策略都使用 merge driver 来处理要合并的文件
有以下几种合并驱动器:
默认情况下,Git会用 text merge driver 对所有文本文件进行处理, 而对二进制文件使用binary merge driver。