博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
git总结三、关于分支下——团队合作中最重要的合并分支
阅读量:5842 次
发布时间:2019-06-18

本文共 7512 字,大约阅读时间需要 25 分钟。

合并分支是团队合作开发中常见的操作,这里涉及到两个命令:git merge 和 git rebase

下面来好好说一下git merge和git rebase都是怎样工作的

 一、

1、新建一个空目录并初始化为一个git项目

git init # 初始化git项目

master分支上添加一个文件(readme.txt),并在其中添加内容

git add .  //提交刚添加的内容

git commit -m "c1"

2、创建并切换到dev分支

git checkout -b dev

此时在readme.txt文件添加内容

git add .

git commit -m "c2"

3、切换回master分支,并修改readme.txt文件内容

git checkout master

修改readme.txt文件在第二行增加一些内容:i am in master

git add .

git commit -m "c3"

4、在master分支新增一个文件addfile.js

git add .

git commit -m "c4"

5、切回到dev分支,同样新增addfile.js文件

git add .

git commit -m "c5"

 

至此,我们来理清一下刚才我们所做的提交:

master: c1--->c3--->c4

dev: c1--->c2--->c5

这时我们分别用两种方式来进行分支合并的操作,观察两种操作导致的结果有什么不同

1、git  merge:

git checkout master

git merge dev

quyangdeMacBook-Pro:testgit quyang$ git merge devAuto-merging readme.txtCONFLICT (content): Merge conflict in readme.txtAuto-merging addfile.jsCONFLICT (add/add): Merge conflict in addfile.jsAutomatic merge failed; fix conflicts and then commit the result.

手动解决两个文件的冲突后

git add .

git commit -m "c6"

git log命令查看带有分支图的历史提交信息:

quyangdeMacBook-Pro:testgit qy$ git logcommit 2d56c742fabc3db93969f9e5e357fc8e8e9e7f54 (HEAD -> master)Merge: b845628 6dbfc35Author: qy 
Date: Thu Apr 25 17:55:06 2019 +0800 c6commit 6dbfc350483c6115ea22ca162b41eae4e75f6cab (dev)Author: qy
Date: Thu Apr 25 17:52:20 2019 +0800 c5commit b8456285e2282a7edb9b89c21045484486b25403Author: qy
Date: Thu Apr 25 17:51:21 2019 +0800 c4commit b58867fd0e2f35f667c2f905125879733be933a9Author: qy
Date: Thu Apr 25 17:48:54 2019 +0800 c3commit 38045deffc089318328079257c34ef334db253ebAuthor: qy
Date: Thu Apr 25 17:48:24 2019 +0800 c2commit 0c0b12c0c9bba105e617675db58eddbb115714c1Author: qy
Date: Thu Apr 25 17:47:43 2019 +0800 c1

  

quyangdeMacBook-Pro:testgit quyang$ git log --graph --oneline --decorate

*   2d56c74 (HEAD -> master) c6

|\  

| * 6dbfc35 (dev) c5

| * 38045de c2

* | b845628 c4

* | b58867f c3

|/  

* 0c0b12c c1

另外补充:

git log --graph --oneline --decorate

--oneline 显示在一行

–graph 选项会绘制一个 ASCII 图像来展示提交历史的分支结构。

–decorate 是用来可以显示出指向提交的指针的名字,也就是 HEAD 指针, feature/test等分支名称,还有远程分支,标签等。

 

可以看到通过merge合并分支后的历史提交信息和分支图

历史提交:c1--->c2--->c3--->c4--->c5--->c6 采用git merge dev处理提交log是按照时间戳先后顺序的

分支图:比较乱,因为现在我们做的提交比较少,真正开发的时候,分支图会很杂乱繁琐

下面我们来用git rebase 来处理

qy-Pro:testgit qy$ git rebase master

First, rewinding head to replay your work on top of it...Applying: c2Using index info to reconstruct a base tree...M	readme.txtFalling back to patching base and 3-way merge...Auto-merging readme.txtCONFLICT (content): Merge conflict in readme.txterror: Failed to merge in the changes.Patch failed at 0001 c2Use 'git am --show-current-patch' to see the failed patchResolve all conflicts manually, mark them as resolved with"git add/rm 
", then run "git rebase --continue".You can instead skip this commit: run "git rebase --skip".To abort and get back to the state before "git rebase", run "git rebase --abort".
手动解决完冲突后查看历史提交信息以及分支图 quyangdeMacBook-Pro:testgit qy$ git logcommit ec0fcbe53db0021eec6fb9f76f139197086e5f01 (HEAD -> dev)Author: qy 
Date: Thu Apr 25 18:14:59 2019 +0800 c5commit 4dd6e0918eff806e158714327fd0b5b638d5e5d0Author: qy
Date: Thu Apr 25 18:13:05 2019 +0800 c2commit 91a30e861cab43ccc86b06e9ac5fba5d81cdbf58 (master)Author: qy
Date: Thu Apr 25 18:14:15 2019 +0800 c4commit 0ea798038e237ae76364c613a780e27e50311a63Author: qy
Date: Thu Apr 25 18:13:38 2019 +0800 c3commit 5b3f829baf568ce71e926b9fcc55ae1aa0d88fcfAuthor: qy
Date: Thu Apr 25 18:12:34 2019 +0800 c1quyangdeMacBook-Pro:testgit quyang$ git log --oneline --graph --decorate* ec0fcbe (HEAD -> dev) c5* 4dd6e09 c2* 91a30e8 (master) c4* 0ea7980 c3* 5b3f829 c1quyangdeMacBook-Pro:testgit quyang$   

最后切换回master分支:

git checkout master

git merge dev

 

通过历史提交信息和分支图可以看出:

采用rebase 处理的历史提交信息和分支图:

提交历史:c1--->c3--->c4--->c2--->c5    采用git rebase处理不是按照时间戳顺序的

分支图: 采用rebase处理得到了一个清晰的分支图

 

git rebase过程相比较git merge合并整合得到的结果没有任何区别,但是通过git rebase衍合能产生一个更为整洁的提交历史。

如果观察一个衍合(git rebase)过的分支的历史提交记录,看起来会更清楚:仿佛所有修改都是在一根线上先后完成的,尽管实际上它们原来是同时并行发生的。

 

二、

为什么会产生这样不同的效果呢,就要弄懂,git merge和git rebase这两个指令到底分别做了什么?

基点的概念:如上例子中,在master的c1节点上创建了dev分支,这个c1就是dev分支的基点。

首先执行git merge将a分支合并到b分支其实就是将a分支上所有从未被合并到b分支的提交节点(commit)和b分支上的基点(a和b最新同步的提交节点)以后的所有提交节点按照时间戳的顺序整合,会有两种情况:

(1)在上面的例子中(如下图),c1是master和dev分支的共同祖先,当要合并的时候,dev的祖先节点c1已经不是master分支的最新提交节点了,即dev分支从master创建后master又往前走了一些commits(这可能是由于其他的branch已经merge到了master,或者在master上直接做了commit,或者有人在master上cherry-picked了一些commits),这时候git merge dev将dev分支合并到master分支,就是将dev分支上所有commit节点(如下面的c2、c5)和master分支上c1以后的所有commit节点(c3,c4),按照时间戳的顺序进行整合(如下右图),若产生冲突,则需要手动解决冲突后再进行一次提交,这种情况会因为解决冲突多增加一次提交(如下右图中的的c6)。即便不需要解决冲突,这时也会默认产生一个merge commit的历史信息。

  

 

(2)另一种情况,master分支和dev分支的共同祖先c4之后,master分支没有继续往前走了,而dev分支进行了一些提交,这之后将dev分支合并到master分支的时候,GIT默认地在merge时是执行一个fast-forward的merge策略,git并不会创建一个merge commit而是简单地把master分支标签移动到dev分支tip(最新提交)所指向的commit,合并后如右图。

  

这种情况在历史图谱中无法得知dev分支的起始位置在哪里,并且一旦这个branch被删除,那么从历史上我们再也无法看到任何关于这个开发分支曾经存在的历史渊源。这就用到了--no-ff参数,它可以强制git产生一个真正的merge。

 git merge --no-ff dev    //强制git产生一个merge历史信息

接下来看git rebase: 它其实是一个变基操作,改变当前分支的基点(不要拘泥于合并分支的使用,合并分支只是它的众多用法之一)

在b分支上执行git rebase a 其实做的是将指定分支a上的与当前分支b最新一次同步的节点后的commit节点放到当前分支b所有没同步过的commit节点之前。

(1)合并分支时,当情况如上例(如下图):当想要合并的分支dev在创建后,master分支又有了新的提交,导致,dev分支的基点c1已经不是master分支的最新提交。这时在dev分支上运用git rebase master,可以将master分支上与dev分支的最新一次同步节点c1以后的commit节点c3和c4放到dev分支上c1之后的commit节点后的c2、c5之前,这样在dev分支上的历史就变成了c1->c3->c4->c2->c5,从而使dev合并到master变成了可以使用fast-forward模式的合并。当然如果你想要保留分支信息,则可以使用--no-ff参数,来强制git产生一个merge commit

 

 (2)同步新的提交到一个古老的分支:有时候你创建一个feature分支开始工作后可能很长时间没有时间再做这个feature开发,当你回来时,你的feature分支就会缺失很多master上的bugfix或者一些其他的feature。在这种个情况下,我们先假设除了你没有其他人在这个分支上工作,那么你可以rebase你的feature分支:

git rebase [basebranch] [topicbranch] 注意这时git rebase的参数顺序,第一个为基分支,第二个为要变基的分支

git rebase origin/master feature    topicbranch不写默认为当前分支

git rebase origin/test_qy   当前分支的远程分支比本地分支超前了提交的时候,可以这样用

 

如果那个feature分支已经被push到remote了的话,你必须使用-f参数来push它,以便你覆盖这个分支的commits历史,这时覆盖这个branch历史也无所谓,因为历史的所有commits都已经相应重新生成了!!。(一个分支的历史由分支的起始commit和头tip commit来描述.有一点需要注意:一旦我们做一次rebase后,那么这个分支上的所有commit由于这次变基,其commit HASH都会改变!!)另外需要注意我们只能对private分支做这个rebase并且git push --force操作!!

 

三、常用的git pull命令的隐含操作

1. 将本地库和远程仓库做一次网络同步。这实际上就是一次git fetch,也只有这次我们需要有和远程仓库的网络连接;

2.默认的,一个git merge操作(将remote tracked branch(远程分支) merge到我们的local trakcing branch(本地分支),比如说orgin/featurex->featureX)

为了便于演示,我们假设如果我当前在feature分支上,而它的remote track branch是origin/feature,那么一个git pull操作就等效于:

1. git fetch;2.git merge origin/feature

 

我们就非常普遍地碰到下面的障碍:在我们的最近一次同步(使用git pull)和我们需要发布local history(要使用git push)的这个时刻,另外一个同事已经分享了他们的工作到中央库上面,所以remote branch(比如说origin/feature分支上) 是比我们的本地拷贝要更新一些。

这样,git push的时候,git就会拒绝接受(因为如果接受就会丢失历史)

(feature u+3) $ git pushTo /tmp/remote  ! [rejected] feature -> feature (fetch first)error: failed to push some refs to '/tmp/remote'hint: Updates were rejected because the remote contains workhint: that you do not have locally. This is usually caused byhint: another repository pushing to the same ref. You may wanthint: to first integrate the remote changeshint: (e.g., 'git pull ...') before pushing again.hint: See the 'Note about fast-forwards' in 'git push --help'hint: for details.(feature u+3) $

 以上我们可以按照git的提示直接使用git pull,但其实这样也不是完全合适,可以使用git pull --rebase来明确要求git,但是这也不是一个很可靠的解决方案,因为这需要我们在git pull操作时时时保持警惕,但这往往并不太可能,因为只要是人就容易犯错误。 

 

转载于:https://www.cnblogs.com/youyang-2018/p/10790422.html

你可能感兴趣的文章
linux RAID10测试
查看>>
(OK) Linux epoll模型—socket epoll server client chat
查看>>
学会写出"图形界面+数据库"的程序要多长时间?
查看>>
药店药品管理方案,药店药品的盘点方案,假设进行药店药品的高效盘点?药品盘点步骤是?...
查看>>
<html>
查看>>
dp4--codeVs1043 方格取数
查看>>
UVA 10733 - The Colored Cubes(Ploya)
查看>>
【python】如何查看已经安装的python软件包和版本
查看>>
Eclipse自己定义keystore
查看>>
Silverlight入门:第五部分 - 整合其它控件
查看>>
watch 命令
查看>>
KendoUI和wijmoUI 它们的Grid比较 20120423
查看>>
Object.wait()与Object.notify()的用法
查看>>
C语言对结构体何时用-> , 何时用.
查看>>
ReentrantLock和synchronized两种锁定机制
查看>>
Myeclipse的web项目移植到Eclipse中需要添加的包
查看>>
Matlab设置字体大小
查看>>
mkdir命令的-p和-m
查看>>
Ubuntu 14中,Foxmail关联163邮箱账号时,总提示“密码错误”的解决方案
查看>>
【C语言入门教程】4.7 指针的地址分配 - mallocl(), free()
查看>>