merge

git merge 将分支合并到你的当前分支

一旦某分支有了独立内容,你终究会希望将它合并回到你的主分支。 你可以使用 git merge 命令将任何分支合并到当前分支中去。 我们那上例中的“removals”分支为例。假设我们创建了一个分支,移除了一些文件,并将它提交到该分支, 其实该分支是与我们的主分支(也就是“master”)独立开来的。 要想将这些移除操作包含在主分支中,你可以将“removals”分支合并回去。

$ git branch
* master
  removals
$ ls
README   hello.rb more.txt test.txt
$ git merge removals
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.txt
$ ls
README   hello.rb

更多复杂合并

当然,合并并不仅仅是简单的文件添加、移除的操作,Git 也会合并修改 —— 事实上,它很会合并修改。 举例,我们看看在某分支中编辑某个文件,然后在另一个分支中把它的名字改掉再做些修改, 最后将这俩分支合并起来。你觉得会变成一坨 shi?我们试试看。

$ git branch
* master
$ cat hello.rb
class HelloWorld
  def self.hello
    puts "Hello World"
  end
end

HelloWorld.hello

首先,我们创建一个叫做“change_class”的分支,切换过去,从而将重命名类等操作独立出来。我们将类名从 “HelloWorld” 改为 “HiWorld”。

$ git checkout -b change_class
M hello.rb
Switched to a new branch "change_class"
$ vim hello.rb
$ head -1 hello.rb
class HiWorld
$ git commit -am "changed the class name"
[change_class 3467b0a] changed the class name
 1 files changed, 2 insertions(+), 4 deletions(-)

然后,将重命名类操作提交到 “change_class” 分支中。 现在,假如切换回 “master” 分支我们可以看到类名恢复到了我们切换到 “change_class” 分支之前的样子。 现在,再做些修改(即代码中的输出),同时将文件名从 hello.rb 改为 ruby.rb

$ git checkout master
Switched to branch "master"
$ git mv hello.rb ruby.rb
$ vim ruby.rb
$ git diff
diff --git a/ruby.rb b/ruby.rb
index 2aabb6e..bf64b17 100644
--- a/ruby.rb
+++ b/ruby.rb
@@ -1,7 +1,7 @@
 class HelloWorld

   def self.hello
-    puts "Hello World"
+    puts "Hello World from Ruby"
   end

 end
$ git commit -am "added from ruby"
[master b7ae93b] added from ruby
 1 files changed, 1 insertions(+), 1 deletions(-)
 rename hello.rb => ruby.rb (65%)

现在这些改变已经记录到我的 “master” 分支了。请注意,这里类名还是 “HelloWorld”,而不是 “HiWorld”。 然后我想将类名的改变合并过来,我把 “change_class” 分支合并过来就行了。 但是,我已经将文件名都改掉了,Git 知道该怎么办么?

$ git branch
  change_class
* master
$ git merge change_class
Renaming hello.rb => ruby.rb
Auto-merging ruby.rb
Merge made by recursive.
 ruby.rb |    6 ++----
 1 files changed, 2 insertions(+), 4 deletions(-)
$ cat ruby.rb
class HiWorld
  def self.hello
    puts "Hello World from Ruby"
  end
end

HiWorld.hello

不错,它就是发现了。请注意,在这部操作,我没有遇到合并冲突,并且文件已经重命名、类名也换掉了。挺酷。

合并冲突

那么,Git 合并很有魔力,我们再也不用处理合并冲突了,对吗?不太确切。 不同分支中修改了相同区块的代码,电脑自己猜不透神马的情况下,冲突就摆在我们面前了。 我们看看两个分支中改了同一行代码的例子。

$ git branch
* master
$ git checkout -b fix_readme
Switched to a new branch "fix_readme"
$ vim README
$ git commit -am "fixed readme title"
[fix_readme 3ac015d] fixed readme title
 1 files changed, 1 insertions(+), 1 deletions(-)

我们在某分支中修改了 README 文件中的一行,并提交了。我们再在 “master” 分支中对同个文件的同一行内容作不同的修改。

$ git checkout master
Switched to branch "master"
$ vim README 
$ git commit -am "fixed readme title differently"
[master 3cbb6aa] fixed readme title differently
 1 files changed, 1 insertions(+), 1 deletions(-)

有意思的来了 —— 我们将前一个分支合并到 “master” 分支,一个合并冲突就出现了。

$ git merge fix_readme
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
>>>>>>> fix_readme

This project has examples of hello world in
nearly every programming language.

你可以看到,Git 在产生合并冲突的地方插入了标准的与 Subversion 很像的合并冲突标记。 轮到我们去解决这些冲突了。在这里我们就手动把它解决。如果你要 Git 打开一个图形化的合并工具, 可以看看 git 合并工具 (比如 kdiff3、emerge、p4merge 等)。

$ vim README   here I"m fixing the conflict
$ git 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 README
$ git add README 
$ git status -s
M  README
$ git commit
[master 8d585ea] Merge branch "fix_readme"

现在我们成功解决了合并中的冲突,并提交了结果

简而言之 使用 git merge 将另一个分支并入当前的分支中去。 Git 会自动以最佳方式将两个不同快照中独特的工作合并到一个新快照中去。

文章导航