热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

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

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

开篇之前,我们来说说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)。

《关于Git协作,除了知道merge,你可能还需要知道rebase》

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

那么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):

《关于Git协作,除了知道merge,你可能还需要知道rebase》

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

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

《关于Git协作,除了知道merge,你可能还需要知道rebase》

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

《关于Git协作,除了知道merge,你可能还需要知道rebase》

恩,我们先在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来将两个分支合并,看看结果怎么样:

《关于Git协作,除了知道merge,你可能还需要知道rebase》

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

update a.txt

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

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

rebase

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

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

《关于Git协作,除了知道merge,你可能还需要知道rebase》

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

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

先在我们执行一下rebase操作,看看会发生什么:

git checkout master // 切换回master

git rebase rebaseDemo // 执行rebase命令

《关于Git协作,除了知道merge,你可能还需要知道rebase》

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

《关于Git协作,除了知道merge,你可能还需要知道rebase》

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

小结

从结果来看,我们对比一下两个种方式产生的结果:

《关于Git协作,除了知道merge,你可能还需要知道rebase》 (左边的是merge操作,右边的是rebase操作)

一目了然,使用rebase衍合操作更能产生干净的、线性的、可读性高的文件版本树。这对于文件的管理是非常有帮助的。

所以,当协作人员过多的时候,使用rebase来替换掉merge其实是有好处的。

如果你只是想弄懂merge和rebase有什么区别的话,那么看到这里,其实能够解答你的这个问题了。

如果你跟笔者一样,对这里面的原理有一些好奇心的话,那么不妨继续看下去,笔者将深入对里面的实现一一分析,引入自己的问题和看法。

深入细节

假设这是我们的一个版本树:

《关于Git协作,除了知道merge,你可能还需要知道rebase》

merge命令,它会把两个分支最新的快照(C3和C4)以及二者共同的祖先(C2)进行三方合并,达到整合分支的目的。像这样:

《关于Git协作,除了知道merge,你可能还需要知道rebase》

如果C4想要整合C3的分支,那么通过rebase的方式,其实就是C3里产生的变化的补丁重新在C4的基础上打一遍,相当于C3中提交的变动在C4中重新重新执行了一遍。

rebase会回到两个分支(当前所在的分支和你想要rebase的分支)的共同祖先,提取当前所在分支每次提交时产生的差异(diff),把这些差异分别保存到临时文件里,然后从当前分支转到你需要rebase的分支,依次序的使用每一个差异补丁文件。像这样 :

《关于Git协作,除了知道merge,你可能还需要知道rebase》 (在C3所在的分支中执行)

git rebase master // master 这里是C4

这个时候C3的分支处整个版本树的最前面,master落后一个版本,这个时候如果想让master保持最新的版本库内容,你需要切换回master分支并且执行

git merge [C3 branch name]

但是这个时候的merge其实只是把当前的[HEAD]指针往前移了一个单位而已,并没有做任何合并的动作,这种合并,一般称之为快进合并。

另外一种方式就是,当前分支是master,并且在master上执行了rebase其他分支的操作,这个时候如果rebase成功,则master分支就会领先目标分支一个版本。

但是在实际开发流程中,master一般不被允许这样直接去整合另外的分支。所以实际操作中,还是使用第一种方式。

还要merge做什么?

我们知道merge和rebase的区别,也知道使用rebase的种种好处,但是我们的还是要思考一个问题:

“rebase好处这么多,那是不是可以替代merge了?以后一直用rebase就好了。

如果你这样想,那就真的是too young too naive了。”

《关于Git协作,除了知道merge,你可能还需要知道rebase》

存在必定有它的道理,不然人家干吗弄两个命令出来。

事实上,在使用rebase的时候我们的遵循这样一个原则:

永远不要rebase那些已经推送到公共仓库的更新。

在rebase的时候,实际上抛弃了一些现存的commit而创造了一些类似但是不同的新的commit。如果这些commit推送到服务器上,你的小伙伴pull下来并在这个基础上进行开发并提交了新的commit,然后你用rebase重写这些commit再推送一次,你的小伙伴就不得不重新合并他们的工作,这样会对别人的理解造成困扰。

举个栗子:

新加入一个项目组,老大给了你项目的git地址。毫无疑问,第一步就是把项目clone到本地,然后你对这个本地仓库做了一些新的功能并且暂时提交到了本地:

《关于Git协作,除了知道merge,你可能还需要知道rebase》

总共提交了两个commit到你的本地(C2和C3)。

那么在你开发的时候,团队其他的小伙伴也在同时做着另外的开发,并且将这些改动合并提交到了服务器上。然后通知你,服务器上有些改动,你更新一下:

《关于Git协作,除了知道merge,你可能还需要知道rebase》

服务器上被其他小伙伴更新到了C6。

这个时候你本地还是基于C1更新到了C3。

这个时候你合并服务器的版本产生了C7。

看上去一切正常。但是如果这个时候,你的小伙伴觉得他之前的提交用的是merge,觉得稍微有些不妥,准备用rebase替换掉服务器上的提交历史,让提交历史看上去更友好:

《关于Git协作,除了知道merge,你可能还需要知道rebase》

服务器上的提交用rebase覆盖了之后变成了C4。

这个时候对于你来说,需要再次合并这个新内容——即使内容没有什么改变。rebase会改变那些commit的SHA-1校验值,这样Git会把它们当做新的commit(尽管你的本地早已有了C4的内容)。

为了保持代码同步,你迟早需要并入其他小伙伴提交的内容。上面这种情况会让你的本地的提交历史里面同时出现C4和C4‘,这两个commit有不一样的SHA-1校验值,但却拥有同样的作者、日期和commit说明。这就是问题,这样的提交历史如果提交到了服务器,别的人再去合并你的代码到他们自己本地的电脑上,则每个人都会有这样一个莫名的提交。

关于rebase的使用,我们更应该把它当成一种在提交到服务器之前清理本地提交历史的手段,而不当成合并的唯一手段。

sourceTree使用rebase

容笔者啰嗦一下关于sourceTree的一些使用。英文好的可以移步去这个网址看一下。

Interactive rebase in SourceTree

写在最后

讲真,非常感谢屏幕前你的看完这篇冗长的文章。写的东西有人看,认真的看,没有什么比这个更欣慰的了。

《关于Git协作,除了知道merge,你可能还需要知道rebase》

关于rebase就讲到这里啦。总的来说是啰嗦了一点,但是笔者认为还是很有必要的。

很多时候,我们评价一段好的代码,看的是它的健壮性,可维护性,可拓展性…..

那么同样,一个优秀的工程师也是方方面面的,做项目不同于写demo,追求团队的效率,优化整个团队的工作习惯,正是作为一个团队成员,从这种一点一滴的小事去理解,去分享的。

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


推荐阅读
  • 本文介绍了通过ABAP开发往外网发邮件的需求,并提供了配置和代码整理的资料。其中包括了配置SAP邮件服务器的步骤和ABAP写发送邮件代码的过程。通过RZ10配置参数和icm/server_port_1的设定,可以实现向Sap User和外部邮件发送邮件的功能。希望对需要的开发人员有帮助。摘要长度:184字。 ... [详细]
  • 本文介绍了Web学习历程记录中关于Tomcat的基本概念和配置。首先解释了Web静态Web资源和动态Web资源的概念,以及C/S架构和B/S架构的区别。然后介绍了常见的Web服务器,包括Weblogic、WebSphere和Tomcat。接着详细讲解了Tomcat的虚拟主机、web应用和虚拟路径映射的概念和配置过程。最后简要介绍了http协议的作用。本文内容详实,适合初学者了解Tomcat的基础知识。 ... [详细]
  • 《数据结构》学习笔记3——串匹配算法性能评估
    本文主要讨论串匹配算法的性能评估,包括模式匹配、字符种类数量、算法复杂度等内容。通过借助C++中的头文件和库,可以实现对串的匹配操作。其中蛮力算法的复杂度为O(m*n),通过随机取出长度为m的子串作为模式P,在文本T中进行匹配,统计平均复杂度。对于成功和失败的匹配分别进行测试,分析其平均复杂度。详情请参考相关学习资源。 ... [详细]
  • 如何实现织梦DedeCms全站伪静态
    本文介绍了如何通过修改织梦DedeCms源代码来实现全站伪静态,以提高管理和SEO效果。全站伪静态可以避免重复URL的问题,同时通过使用mod_rewrite伪静态模块和.htaccess正则表达式,可以更好地适应搜索引擎的需求。文章还提到了一些相关的技术和工具,如Ubuntu、qt编程、tomcat端口、爬虫、php request根目录等。 ... [详细]
  • 一、Hadoop来历Hadoop的思想来源于Google在做搜索引擎的时候出现一个很大的问题就是这么多网页我如何才能以最快的速度来搜索到,由于这个问题Google发明 ... [详细]
  • Linux服务器密码过期策略、登录次数限制、私钥登录等配置方法
    本文介绍了在Linux服务器上进行密码过期策略、登录次数限制、私钥登录等配置的方法。通过修改配置文件中的参数,可以设置密码的有效期、最小间隔时间、最小长度,并在密码过期前进行提示。同时还介绍了如何进行公钥登录和修改默认账户用户名的操作。详细步骤和注意事项可参考本文内容。 ... [详细]
  • 搭建Windows Server 2012 R2 IIS8.5+PHP(FastCGI)+MySQL环境的详细步骤
    本文详细介绍了搭建Windows Server 2012 R2 IIS8.5+PHP(FastCGI)+MySQL环境的步骤,包括环境说明、相关软件下载的地址以及所需的插件下载地址。 ... [详细]
  • 本文介绍了数据库的存储结构及其重要性,强调了关系数据库范例中将逻辑存储与物理存储分开的必要性。通过逻辑结构和物理结构的分离,可以实现对物理存储的重新组织和数据库的迁移,而应用程序不会察觉到任何更改。文章还展示了Oracle数据库的逻辑结构和物理结构,并介绍了表空间的概念和作用。 ... [详细]
  • android listview OnItemClickListener失效原因
    最近在做listview时发现OnItemClickListener失效的问题,经过查找发现是因为button的原因。不仅listitem中存在button会影响OnItemClickListener事件的失效,还会导致单击后listview每个item的背景改变,使得item中的所有有关焦点的事件都失效。本文给出了一个范例来说明这种情况,并提供了解决方法。 ... [详细]
  • Android Studio Bumblebee | 2021.1.1(大黄蜂版本使用介绍)
    本文介绍了Android Studio Bumblebee | 2021.1.1(大黄蜂版本)的使用方法和相关知识,包括Gradle的介绍、设备管理器的配置、无线调试、新版本问题等内容。同时还提供了更新版本的下载地址和启动页面截图。 ... [详细]
  • 知识图谱——机器大脑中的知识库
    本文介绍了知识图谱在机器大脑中的应用,以及搜索引擎在知识图谱方面的发展。以谷歌知识图谱为例,说明了知识图谱的智能化特点。通过搜索引擎用户可以获取更加智能化的答案,如搜索关键词"Marie Curie",会得到居里夫人的详细信息以及与之相关的历史人物。知识图谱的出现引起了搜索引擎行业的变革,不仅美国的微软必应,中国的百度、搜狗等搜索引擎公司也纷纷推出了自己的知识图谱。 ... [详细]
  • 本文介绍了Hyperledger Fabric外部链码构建与运行的相关知识,包括在Hyperledger Fabric 2.0版本之前链码构建和运行的困难性,外部构建模式的实现原理以及外部构建和运行API的使用方法。通过本文的介绍,读者可以了解到如何利用外部构建和运行的方式来实现链码的构建和运行,并且不再受限于特定的语言和部署环境。 ... [详细]
  • 本文介绍了Perl的测试框架Test::Base,它是一个数据驱动的测试框架,可以自动进行单元测试,省去手工编写测试程序的麻烦。与Test::More完全兼容,使用方法简单。以plural函数为例,展示了Test::Base的使用方法。 ... [详细]
  • http:my.oschina.netleejun2005blog136820刚看到群里又有同学在说HTTP协议下的Get请求参数长度是有大小限制的,最大不能超过XX ... [详细]
  • 本文介绍了计算机网络的定义和通信流程,包括客户端编译文件、二进制转换、三层路由设备等。同时,还介绍了计算机网络中常用的关键词,如MAC地址和IP地址。 ... [详细]
author-avatar
biosan
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有