关于Git协作,除了知道merge,你可能还需要知道rebase【一】

2016-11-23 原创

1

开篇之前,我们来说说Git这个东西。至于Git是个什么东西这种问题,我相信不用我说,点开这篇文章的你应该知道的。但是这里,我也不赶尽杀绝,用一句简单粗暴的概念简单介绍一下 :

Git,是一个版本管理工具。

那么关于Git的基本使用和一些基础概念,这里不是本文讨论的重点。本文也不是一篇Git的科普文,教你Git怎么用啊,环境怎么搭建啊,用Git有什么好处啊…..这种问题。如果你有这种问题,请关闭此页面,在浏览器地址栏输入 http://www.google.com/

Good Luck~

作文初衷

恩…接触Git有些时日。大大小小的项目也做了不少,基本都是用Git在做代码管理。

在多人协作开发的时候,最最频繁的操作就是merge了。简单方便快捷。直到遇到一家对Git提交规范严格的公司——要求使用rebase来替代merge操作。

恩,基于这个原因,就去熟悉原本不怎么熟悉的rebase命令。过程中,踩了不少坑,走了不少弯路,特写下这篇文章,希望能对后来者有所帮助。

本文内容不深,会从实际的操作和例子出发,旨在为各位看官弄明白git rebase命令的各种相关的问题。

一般的Git协作流程

说是一般的,其实只是我本人见过的一些项目以来的Git的管理的流程。见识有限,给大家分享的,只是我实践过可行性比较好的方案。各位看官看完后可以做个参考,有什么不对的或者疑问,欢迎联系我,或者留言评论,先行谢过~~~

一个成规模的项目一般的Git的分支结构应该是这样:
master //对应线上产品的代码版本
dev //开发总分支
module1 //新feature分支
module2 //新feature分支
....

master : 这个分支主要对应的线上的产品的代码,如果APP上线后收到反馈,出现了Bug,需要立马修复,那么基于这个分支新建一个新的分支去解决Bug。总之,这个分支总是映射现在的项目的线上的代码,正常情况下只合并dev上的代码。

dev: 总的开发分支。当开发新版本的时候,细分的每个feature,都会去基于这个分支去新建一个分支,然后再去开发。

module: 具体的功能分支。

新版本的开发应该是先由dev分支牵出各种module分支,各个module分支先后完成后再merge到dev分支(当然,这当中涉及到code reviewer之类的流程)。最终是从dev分支上打包测试。没有问题之后再merge到master上。恩,至少之前我就是这么干的。

merge 和 rebase简述

用过git的话应该都知道,git的世界里,分支是一个很基础的概念。可以让你在不干扰现有版本的情况下去做你想做的事情,事情做完之后再合并回你的主分支。

那么分支和分支之间的合并,也就是我们经常会用到的merge命令。

当你有两个分支,一个主分支master(当然,你可以叫任何名字),一个从master上牵出来的子分支branchA。当你想要将branchA的改动合并到master上时,你大概需要用如下两个命令:
git checkout master // 将当前分支切换到master分支
git merge branchA //合并branchA分支

那么,除了merge我们知道是合并之后,今天介绍另外一种合并的方式,上面的命令也可以这么写:
git checkout master // 第一步一样,切换到master分支
git rebase branchA //合并branchA分支

将上面的merge替换成rebase就好啦。然后你将看到,两种方式最后产生的结果并无区别,两个分支的内容确实合并在了一起。

合并的过程中可能会有冲突,这个时候需要你去手动解决冲突再次提交。
无论是merge还是rebase,都是一个主动的操作。

当你需要合并另一个分支的时候,就会将你要合并的分支(目标分支),合并到你当前所在的分支。

merge 和rebase 使用对比

rebase命令在中文的Pro.Git-zh_CN.pdf 中文手册中,翻译成衍合。乍一看不怎么好理解。

在一些我使用的git图形化工具软件的时候,rebase翻译成变基(比如本人使用的SourceTree)。
2

就真的不怕人想歪么….所以,更加不好理解。

那么rebase到底怎么理解呢?它跟merge又有什么区别?

先回答第二个问题,rebase和merge的作用都一样,能帮助你在当前分支合并另一个分支的内容。他们唯一的区别体现在这个分支的提交历史上。我们知道,你的每次一个commit,每一次merge都会在git的版本历史(版本树)中有明确的记录,像是脚印一样,这样方面我们浏览这个版本的前世今生。

rebase的操作会让你的版本一直保持一个比较干净的线性结构往下发展,而merge不会。

merge
好的,我们来写一个例子:
首先,我们在本地的一个随便找一个文件夹新建一个git仓库,恩…文件夹的名字就叫test好了。
git init

然后,我们先做一次提交,提交内容,我们随便新建一个文件
vi a.txt
git add *
git commit -m "first commit"

执行完上面的命令如果不出意外的话,这个工程如果使用你的第三方图形化工具来看的话,应该是这样(我这里用的是SrouceTree):

3

默认帮你创建了一个master分支,并且这个分支上有你的一个提交历史。

接下来,我们新建一个分支test,并且在这个分支上新建一个文件b.txt,然后提交,如果不出意外,你会得到这样:

4

之后,我们分别在各自两个分支上做几次各自不同内容的提交,让两个分支的差异越来越大 :

5

恩,我们先在test分支上提交了一次,内容是
update b.txt

然后我们切回master分支,分别前后做了两次提交,内容分别是
update a.txt
create c.txt

OK ,那么现在的情况就是,master上有a.txt、c.txt两个文件。test分支上有a.txt(test从master新牵出的时候,就带了a.txt)、b.txt两个文件。

好的,现在我们按照传统的方式merge来将两个分支合并,看看结果怎么样:
6

我们先在从master分支上执行了merge命令,合并了test分支上的内容,继而又继续提交了一次更新,更新内容为
update a.txt

以上,就是一个简单的merge操作的流程,经历了那么多的操作后,回顾整个版本库的提交历史可以发现,出现了两条线,一条蓝色(master)、一条红色(test)。

并且由于merge的操作,这两条分支存在两个交点,一个是从test牵出来的时候跟master产品的交点,一个是由merge操作,同master产生的交点。

rebase
好的,还是刚刚那个demo,让我们用rebase的方式来比较一下区别。

我们新建一个分支,叫做rebaseDemo,并且在这个新分支上做一些提交:

7

我们先在rebaseDemo上做了两次提交,一次新建了一个d.txt文件,一次修改了d.txt文件。

然后切换回master,做了一次提交,新建了一个e.txt文件。

先在我们执行一下rebase操作,看看会发生什么:
git checkout master // 切换回master
git rebase rebaseDemo // 执行rebase命令

8

当我们运行完rebase命令后再回去看我们发现,整个版本树的分支只保留了一条,也不会出现第二条分支,更别说分支交错的情况了。那我们来看看master上的内容:

9

确实合并了rebaseDemo中新建的文件d.txt。从结果出发,无论是merge还是rebase达到的效果其实是一致的。




本文作者:梁紫枫(点融黑帮),江湖诨名疯子。来自点融Clients团队。喜欢打扫房间和健身的巨蟹座男子。

随着点融网新一轮融资,点融网即将开始大规模的扩张,需要各种优秀人才的加入,如果你觉得自己够优秀,欢迎加入我们!获取更多职位信息,请关注点融黑帮。