分布式版本控制系统Git------分支管理与合并(merge与rebase)

前言

这篇文章是我16年4月写的,当时发布在CSDN上。被Git知识库收录。
链接:
http://blog.csdn.net/aroundme/article/details/51223687

现在想想之前的排版(没有用MarkDown)和一些语句以及文章整体结构有点问题,所以这次就使用MarkDown重新编辑,放在简书。

0、需要使用到的命令:

        git branch                           查看当前分支。

        git branch <name>                    创建一个名为<name>的分支。

        git checkout <name>                  切换到名字为<name>的分支。

        git checkout -b <nama>               创建一个名为<name>的分支,并且切换到此分支
                                             (等于git branch <name>命令 + git checkout <name>命令)。

        git merge <name>                     把一个名为<name>的分支合并到当前分支。

        git branch -d <name>                 删除一个名为dev的分支。

        git merge   <分支名>                  把<分支名>合并到当前分支

        git rebase   <分支名>                 把<分支名>合并到当前分支(与merge不同)

        git fetch<主机名> <分支名>             将版本库中的内容取回本地

        git stash                            备份当前工作区的内容至git栈,再从最近的一次commit提交覆盖到当前工作区。
        git stash list                       显示当前git栈所有的备份
        git stash pop                        从git栈中恢复一次内容至工作区,默认是恢复最近的一次
        git stash clear                      清空git栈。
        
        git pull <远程主机名> <远程分支名>:<本地分支名> 
        将版本库中的内容取回本地并和<本地分支名>合并(merge合并方式)。
        
        git pull --rebase<远程主机名> <远程分支名>:<本地分支名>         
        将版本库中的内容取回本地并和<本地分支名>合并(rebase合并方式)。 
        
        
        

1、简单介绍一下分支的基本操作。

先看看分支创建 -> 切换 -> 提交 -> 合并 -> 删除的基本套路吧。

使用上面的几个命令可以实现最基础的分支操作。

下面是例子(截图丑翔见谅)

  • 1.创建分支:git branch name


  • 2.查看当前所有分支:git branch


当前有两个分支,分别是master和gzl。

  • 3.切换分支:git checkout name


git提示当前已经切换到分支gzl。

  • 4.在分支上修改文件或者添加一个新的文件,在分支上进行一个commit。


  • 5.回到master分支:git checkout master

这个时候的打开master分支的test.txt文件看看

发现test.txt文件回到了没有更改的之前的内容。这是因为我们修改是在gzl分支的,而不是master分支。
可以将分支看成一个个平行空间,你在其中修改并不会影响到其他分支的内容。

  • 6.合并gzl分支的内容到master分支 :git merge gzl

这里采用的Fast-forward合并方式,即快速合并。也就是说,只是把master指向了gzl分支。

  • 7.删除gzl分支:git branch -d gzl


再用git branch 命令查看,发现只剩下了master分支了。

2、两种合并模式

  • Fast forward模式:
    如果顺着一个分支走下去可以达到另外一个分支的话,那么git合并的时候,就只要简单的把master指针往后移。因为这种单线的操作不需要合并不同的分支,所以不会产生分歧,所以称为Fast forward模式。

  • 普通模式:
    在一个commit节点发生分支,有两个不同的分支,如果想要合并这两个分支,这个时候不能室友Fast forward模式。往往需要手动修改产生冲突的文件,然后在进行一次commit。

3、合并分支的两种方式

前面使用的是git的merge合并方式,分支还有一种合并方式叫做rebase合并。

rebase合并与merge合并最终的结果其实是一样的,但是执行的过程却是不相同的。

merge方式(合并):

看看官方文档里面的示例图,master分支合并experiment分支,将会产生一个新的C5快照,而且只要你不删除experiment分支它就会一直存在。

rebase方式(衍合):

rebase流程:

  1. Git首先切换到C4所在的experiment分支。
  2. 从C4开始向前找,直到找到和master分支最近的一个相同commit快照,C4和C4之前的所有commit快照生成patch文件。
  3. 强制转化到master分支,从C2开始,将上面生成的patch文件,从C2开始往下一个一个打上patch文件。
  4. 最后生成新的C4‘commit快照,它之前所在的分支experiment也随着它的改变指向了master的上游。最后可以使用Fast forward方式让master分支和experiment保持一致。

注意: rabase在第三步之中会发生冲突。

在这种情况下,Git会停止rebase并会让你去解决冲突,在解决完冲突后,用"git-add"命令去更新这些内容的索引(index),然后,你无需执行 git-commit,只要执行:

 git rebase --continue

这样git会继续应用(apply)余下的补丁。

在任何时候,你可以用--abort参数来终止rebase的行动,并且"mywork" 分支会回到rebase开始前的状态。

git rebase --abort

4、Rebase的优势和劣势

可以看到当使用rebase方式的时候,产生了C4'快照,这个快照和merge方式得到的快照是一样的。可是不一样的地方还是有的。

  1. experiment分支从独立的分支,到和master分支相同的上游去了。
  2. C4快照消失了。乍一看没什么,可是仔细想想,如果你在C4修改了一次重要的操作,可是使用rebase方式合并之后,你的C4却丢失,那么会造成不可忽略的影响。

通过查看文档,得到rebase相对于merge的优劣比较:

  • 优势:
    采用rebase方式合并的快照,在主分支上看起来就像是一条平行线一样整洁干净,让人一目了然,这样管理人员就不需要花费时间去整理分 分支了。
  • 劣势:
    就像上张图那样,采用rebase方式合并之后,以前的分支的快照都会消失。引用文档的概括的一句话------
Do not rebase commits that exist outside your repository.

为什么会这样,文档上的例子很生动,我大概翻译一下(粗体为翻译):

**如果你rebase一个文件,放弃了目前的修改,然后创建了一个新的看起来相同可是内容却不同的快照一 。如果你将这个快照放到网上或者其他地方,别人pull下了它,然后按照这个为基准进行工作。这个时候你又重写了它并且使用了git rebase方法把它上传到了网上称为 快照二,你的同事将会被这个新的快照搞得头昏眼花。因为他们是基于你第一次快照来进行开发的,可是你使用rebase命令之后,你的快照一将会消失,那么你同事做的事情就会白费。
**

那么如果你面临这样的情况,改怎么办呢?别担心,还是有机会挽回损失的。

如果你面临这样的困难,那么你所面临的第一个挑战就是分辨出哪些是你写的,哪些是你同事写的。
原来commit 对象除了SHA-1计算校验之外,Git还基于你引进的补丁计算了一种校验码,叫做“patch-id”。
如果你从你同事那里拉下重改的代码是基于最新的一次提交,Git也能也能够成功的解决而且申请它回到一个新的分支上。
使用git rebase teamone/master命令。
1.找到唯一工作在分支上的commit快照。
2.找到没有合并的commit快照。
3.找到没有被改写的commit快照加入目标分支。
4.应用这些commit快照在master分支和teamone分支。

但是它只工作在旧commit快照C4和新快照C4’有着差不多的补丁,否则衍合就不能够分辨C4'是C4的复制快照。这样的话就会添加一个新的C4 ''的补丁文件(这将会导致衍合失败,而且这样的话会导致一些不可预料的变化)。
你也可以用git pull --rebase命令来代替平常的git pull命令。

附上git文档里面的最后一段,关于什么时候使用rebase和merge


5、git pull 和 git pull --rebase

先说说git pull命令。
git push是把本地文件上传至远程库的命令。那么自然而然就有把远程库的文件拉下来放到本地工作区的命令,这个命令就是git fetch命令。git fetch所做的只是把远程库的文件获取到本地。

git pull = git fetch + git merge。

如果后面加上--rebase参数。

git pull --rebase命令

表示把你的本地当前分支里的每个提交(commit)取消掉,并且把它们临时 保存为补丁(patch)(这些补丁放到".git/rebase"目录中),然后把本地当前分支更新 为最新的"origin"分支,最后把保存的这些补丁应用到本地当前分支上。
其实

git pull --rebase = git fetch  + git rebase。

浏览了一些博客,发现大多数博主都有个提醒的地方:尽量少使用git pull或者git pull --rebase命令,多使用git fetch命令 + git pull命令或者git fetch命令 + git rebase命令。因为逐步执行可以保证文件的安全性。直接git pull会隐藏一些细节,或许这些细节就是你所需要的。

而到底是使用git pull还是使用git pull --rebase,其实也就是面临是选择git merge还是git rebase合并一样的情景。git merge可以保存所有的commit记录方便之后的使用,而git rebase是为了有一条清晰明朗的主线,避免无谓的commit。存在即为合理,两种不同的方式各有优点,就看实际情况到底如何使用了。

Tips

1. git blame 防甩锅神器

如果你要查看文件的每个部分是谁修改的, 那么 git blame 就是不二选择. 只要运行'git blame [filename]', 你就会得到整个文件的每一行的详细修改信息:包括SHA串,日期和作者:

git blame [filename]

一行命令显示文件每一行是谁修改的,一目了然,妈妈再也不用担心我被甩锅了

2. git stash 保存进度暂存区

你工作做到一半,突然有个bug要放下手上的活儿去解决bug。可是直接pull会丢失你这几天的辛辛苦苦工作。下面几个步骤搞定。

  1. git add * 把所有值钱工作的文件放入暂存区。
  2. git stash 将文件放入git栈备份
  3. git pull origin 将远程库代码拉下来(避免冲突)
  4. git stash pop 将git栈的备份文件拿出来
  5. git merge 合并文件

简单测试

其实看到前面大概就已经够了,不过我还是打算把测试放上去,一个实际的问题能够更好的消化上面的内容

问题:git merge 分支的时候,合并冲突。

情景再现:在分支修改了某个文件之后,回到master分支想要将两个分支合并。因为某个文件内容不一样,出现了


git告诉你合并失败因为有一个未合并的文件。这个时候应该在工作区打开文件,手动修改,在使用add,commit命令,最后使用git merge命令最后才能成功的合并。
盗取廖雪峰老师的图来解释一下应该会更清楚点:

1.你修改了两个不同分支相同的文件,这个文件在不同分支内容是不同的,不能使用快速合并了。


2.这个时候你手动修改了这个文件,使用git merge命令发现出现下面的提示。


解决方法:手动修改未合并的文件,再次add,commit。合并之后相当于多了一次提交。改变图如下:



结论:如果merge的时候使用的是Fast forward模式,git只是将mater的分支移到另外一个分支上。如果没有使用Fast forward模式(普通模式),Git会产生一个新的commit。Fast forward模式合并之后是没有分支记录的,普通模式是用分支记录的。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,185评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,445评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,684评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,564评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,681评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,874评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,025评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,761评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,217评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,545评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,694评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,351评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,988评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,778评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,007评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,427评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,580评论 2 349

推荐阅读更多精彩内容