制造冲突

  1. 在github上创建一个仓库。
  2. 在本地两个文件夹分别clone同一仓库。称之为仓库A,仓库B。
  3. 在仓库A修改文件,提交到远端。
    git add -A
    git commit -m “2aaaa”
    git push origin master:master
    修改的文件:test.txt
    1test
    2aaaa
  4. 在仓库B修改同一文件的同一行:test.txt
    1test
    2bbbb
    同样保存修改后,尝试提交到远端,发现冲突了,导致无法提交到远端:
    image

既然冲突了,就必须解决冲突,解决冲突有两种方式:
rebase, merge。主要区别是合并之后commit的个数不一样:rebase保留原分支上的每个commit,merge只会生成一个commit。

解决冲突

方法1:使用rebase合并分支

  1. git fetch
    下载所有分支的最新代码
  2. git rebase origin/master(以origin/master分支为基线,合入master分支的修改到origin/master。)
    当然,会提示冲突:
    image
    可以看出冲突的文件是test.txt。
    打开test.txt,可以看到冲突的内容:
    1test
    <<<<<<< 1041dfeabc9e796478294adbcf72aa61f64f8f84
    2aaaa
    =======
    2bbbb
    >>>>>>> 2bbbb
    

留下想保存的内容,其他删除掉。就OK了。

也可以用git提供的左右比较的方式进行编辑,解决冲突:
选中冲突的文件->右击->TortoiseGit->Edit confilicts:
image

  1. git add -A
    冲突解决完成之后,提交修改
  2. git rebase --continue
    继续合并。合并的过程中,还有可能产生冲突。解决方法同上。
  3. git push origin master:refs/for/master
    冲突解决完之后。推送到远端服务器

用一个图概括rebase解决冲突的前后变化:
解决冲突前:

B
A
C
D
origin/master
E
master

解决冲突后:

B
A
C
origin/master
D'
E'
master

方法2:使用merge合并分支

  1. git fetch
    下载所有分支的最新代码

  2. git checkout origin/master
    直接切到远端的master分支
    image

  3. git merge master
    将master分支的修改合入
    提示冲突:
    image

和rebase一样,手动解决冲突。

  1. git add -A;git commit
    提交修改。合并完成。
    合并完成之后,origin/master,master分支指向的地方都未发生改变:
    image
  2. git push origin HEAD:refs/for/master
    推送到远端服务器
    从上面的图可以看出,master分支并未指向最新,所以不能使用:
    git push origin master:refs/for/master
    如果要使用,则可以先将master分支指向最新的commit:
    git checkout -B master HEAD
    然后:
    git push origin master:refs/for/master

用一个图概括merge解决冲突的前后变化:
解决冲突前:

B
A
C
D
origin/master
E
master

解决冲突后:

B
A
C
origin/master
D'+E'
master

cherry-pick冲突及解决冲突

在gerrit的界面上使用cherry-pick是在服务端进行操作的,完全不涉及本地分支。
cherry-pick的原理可以简单理解为:
将本commit与其父commit之间的差异(patch)应用到另一个分支上。
所以说,如果如果patch对应的源文件对应的行在另一个分支已经被改变了,那么就无法成功应用patch,会提示冲突。gerrit未提供远程解决冲突的能力,必须要在本地手动解决冲突。
假如要将master某个commit提交到dev分支,操作流程如下:

  1. git fetch
    下载所有分支的最新代码
  2. git checkout -B dev origin/dev
    直接用远端的dev分支覆盖本地的dev分支,并切换到dev分支
  3. git cheery-pick commitId
    将commitId对应的commit应用到dev分支上来。
    如果产生了冲突,则需要和上面讲到的一样,手动解决冲突。
  4. git add -A;git commit
    冲突解决完之后,提交修改
  5. git puhs origin dev:refs/for/dev
    提交到远端

git pull

网上搜到的解释都是说pull是fetch+merge操作的合并。
那么到底是谁merge谁,merge的过程中是否会产生新的提交,产生的提交push的时候又是否会推送到远端呢?
实践一下:

1. origin/master比master多一个节点

B
A
C
D
master
origin/master

此时使用git pull拉取分支时,本地master直接更新为和远端master一样。

B
A
C
D
master
origin/master

2. orgin/master 和master分叉以后,各自多了一个节点,并且无冲突

B
A
C
D
E
master
origin/master

然后进行pull操作,会发现在本地的master分支上形成了一个新的提交,对应的message为:Merge branch ‘master’ of https://github.com/copbint/blogs
origin/master并未发生改变。
图示:

B
A
C
D
origin/master
E
D'
master

将master推送到远端。
远端生成了一个新的提交。
由于master指向的节点,是orgin/master指向的节点的子节点,并无分叉,所以一定不会出现冲突。

再执行git pull将远端最新代码取下来,master和origin/master都指向了最新的提交。即D‘

3. orgin/master 和master分叉以后,各自多了一个节点,有冲突

执行,git pull报冲突:
此时必须手动解决冲突。然后提交修改。此时会产生一个新的commit。
和上面的例子唯一的区别就是需要手动解决冲突,然后提交commit。

如果此时,再提交一个新的commit,然后推送到远端。会推送几个commit呢?

实践了一下,远端产生了两个提交。

4. 总结

git pull就是先获取远端分支,然后将对应的远端分支merge到本地分支上来。
如果本地分支和远端分支已经分叉,则会在本地分支上产生一个新的提交。
如果有冲突,则需要手动解决冲突。

其他

github什么情况下会产生冲突呢?测试了一番,只有修改了同一个文件,就会产生冲突。
测试的方法包括:

  1. 修改同一文件间隔很远的不同行
  2. 修改同一文件,修改的内容完全一样

都提示冲突。但是在本地使用rebase,merge合并的时候,无需要手动解决冲突,直接就自动合并完成了。
这一点,gerrit似乎要机智一些,只要不是修改了文件的同一行,就可以自动合入。



在某个不用的服务上创建两个test分支,用完之后可以删除掉。
test_MASTER_test
test_DEV_test

gerrit的展示不是很准确,commit的内容并不在当前分支也展示出来。而真正改变当前分支的merge …对应commit的改变却并没有提现出来。

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐