您可以捐助,支持我们的公益事业。

1元 10元 50元





认证码:  验证码,看不清楚?请点击刷新验证码 必填



  要资料 文章 文库 Lib 视频 Code iProcess 课程 认证 咨询 工具 火云堂 讲座吧   成长之路  
会员   
 
   
 
  
每天15篇文章
不仅获得谋生技能
更可以追随信仰
 
 
     
   
 订阅
  捐助
Git由浅入深之分支管理
 
来源:极客头条 发布于 2017-4-19
408 次浏览     评价:      
 

几乎所有的版本控制系统都以分支的方式进行操作,分支是独立于项目主线的一条支线,我们可以在不影响主线代码的情况下,在分支下进行工作。对于传统的一些版本控制工具来说,我们通常需要花费比较多的时间拷贝主线代码,创建一个分支,并且对分支的管理效率也越来越不令人满意,而如今备受推崇的Git确实名副其实,Git中的分支非常轻量,我们可以随时随意创建任意数量的新分支,几乎感觉不到什么延时,而且对分支的操作也很高效,如,切换分支,暂存内容,分支合并,分支提交等。

Git分支的与众不同

上面我们提到相对于其他大多数版本控制系统,Git分支是轻量且高效的,为什么呢?答案是:传统的版本控制系统存储的数据是文件的变更,而Git则是存储一系列的文件快照(snapshot)。

Git分支的这些特性,使得分支对我们几乎没有什么限制,一般针对每一个功能或需求都可以随意创建分支,而在传统的版本控制系统,这样几乎是不现实的。

当我们向服务器提交数据时,Git会存储一个提交对象(commit object),这个存储对象包括一系列有用信息,详见另一篇文章中提交对象。

Git主干分支(master)

master,有主人,大师的意思,在Git是通常作为主干分支,Git初始化仓库时,默认创建的分支名就是master,就像默认的远端主机别名是origin一样,大多数人不会修改它,这并不说明它与别的分支有什么区别,你可以随意修改名称。

分支类型

在Git中,除了默认的master主干分支,我们创建的每一个分支,一般可分为两种:

1.长运行分支(Long-Running branch):与master并行,长期存在使用的分支,如用以测试项目稳定性或作为主分支;

2.主题分支(topic branch):针对每一个需求或功能或bug而暂时创建的分支,一旦任务完成,即可能回收。

分支指针(HEAD)

Git中有一个HEAD指针,始终指向当前分支,如图可见,项目当前处在master分支,之前一共有三次提交:

上图可见,第一行显示了当前项目所有分支,HEAD -> master表明当前所处分支为master,我们可以总结如下图:

我们可以在项目根目录.git文件下找到一个HEAD文件:vi .git/HEAD,其内保存了指向当前分支最新提交的指针:

该指针指向refs/heads/分支名文件,我们进入.git/refs/heads/目录,其下以分支名为文件名列出了所有分支:

我们查看当前分支文件,执行vi master:

可以看到,其内存储的就是当前分支的最新一次提交对象ID。

创建分支(git branch, git checkout -b)

接下来,假设有一个需求A,我们创建一个分支work-a:

 git checkout -b 分支名

-b参数声明为创建新分支。

等价于以下两条指令:

 git branch 分支名
git checkout 分支名

切换分支(git checkout)

git checkout 分支名表示切换到该分支,上文提到指定-b配置即说明创建新分支。

注:在切换分支前,一定确保当前分支的修改已经提交或者缓存。

多分支并行

我们经常会遇到同时需要开发多个功能和需求,或者突然发现线上bug需要紧急处理,我们只需要提交当前分支修改,然后切换到主干分支,从其基础上再切出一个新分支fix-bug1:

可以看到,在work-a分支上我们新增了一次提交:

b287b8e22470b20cc98e6224a8023708b4cc6989。

现在我们在fix-bug1分支上修复bug后,进行提交:

可以看到,在fix-bug1分支上多了一个提交:ca270e6,现在整个结构就变成如下图:

合并分支(git merge)

我们已经修复了某bug或完成了功能开发,这时要做的是把代码并入主干,,当然一般公司或团队都需要经过代码审查,才能并入主干,在此略过不谈,分支合并相关指令:

 git merge 分支名

该指令告诉Git将指定分支合并到当前分支,当然是可能出现冲突的,我们按照指示解决冲突,即可。

现在我们先切换到master分支,然后把fix-bug1分支并入主干:

 

可以看到执行git merge指令后,状态信息显示:

1.第一行Updating,告诉我们提交记录更新至ca270e6;

2.第二行Fast-forward,即快速推进,说明Git直接将当前分支推进到指向新提交对象;

3.后面是merge的内容信息。

 

非快速推进合并(NO FAST-FORWARD)

现在,我们再次创建一个分支fix-bug2,并进行几次修改提交:

多次提交后,状态如下:

我们通过非快速推进方式合并分支进主干分支:

如上图,指定–no-ff即声明进行非快速推进合并,第二行的Merge made by the ‘recursive’ strategy表明通过非快速推进方式合并,我们发现除了分支上进行的提交记录外,Git创建了一个新的提交对象:7a657a,使用

git log –graph指令查看其信息:

如图,快速推进方式合并入主干的fix-bug1分支的提交记录直接并入主线,且不会创建新的提交对象;而对于非快速推进方式合并的fix-bug2分支,其提交历史也都保存,但是并未进入主线,而是保存了一条支线,同时,在主线上创建一个新的提交对象。

最后描述其结构如图:

非快速推进与快速推进合并(FAST-FORWARD & NO FAST-FORWARD)

从上例,对比一下两种方式合并分支的异同:

1.提交对象都会保存;

2.报存提交对象方式不同:快速推进方式是直接在主线(合并主分支)上,添加这些提交对象,即直接移动HEAD指针;而非快速推进方式是将提交对象保存在支线,然后在主线新建一个提交对象,修改HEAD指针及新建提交对象的指针,而且此新建提交对象有两个父提交对象(即有两个parent指针)。

3.合并后分支指向不同:快速推进合并后,两个分支将同时指向最新提交对象,而非快速推进合并后,合并主分支指向新建的提交对象,另一分支指向不变。

 

4.我们查看一下新创建提交对象:

可以看到该提交对象中有两个指针指向父提交对象,一个指向主线中的父提交对象,一个指向fix-bug2分支合并而来的支线父提交对象。

三路合并(THREE-WAY MERGE)

除了之前提到的两种合并的情况,其实还存在这样一种情况,就是现在假如我完成了work-a分支的开发,需要将其并入主干,我们能看到当前master主干分支已经推进到7a6576了,而work-a分支指向b287b8,两者有共同祖先提交对象6d50f6,我们将其合并:

上图第二行表明此次是通过非快速推进方式合并,我们查看提交对象记录图:

结构如图:

我们发现,三路合并结构是在需要合并的两个分支的最新提交对象的基础上,创建一个新提交对象(4ae14b),将合并主分支(即执行合并指令时,当前所处分支)的HEAD指针前移指向该提交对象,该提交对象有两个父提交对象,分别为合并前待合并分支的最新提交对象(即b287b8和7a657a)。

关于三路合并需要明确:

1.三路合并其实是一种非快速推进合并方式;

2.三路合并的前提是两个分支有共同祖先提交对象;

分支冲突(conflict)

在合并分支,不可避免会发生冲突,当我们在两个分支对同一文件同一部分进行不同修改后,发起合并时就会提示有冲突,假设我们有work-b分支,在其基础上切出新分支work-b-1,然后在两分支上分别对README.md文件同一部分进行不同修改并提交,然后将work-b-1分支合并到work-b分支:

发现README.md文件有冲突,查看该文件:

如上图,列出了两个分支的不同修改,HEAD表明当前分支的修改内容,下面是work-b-1分支的修改,我们选择需要保留的内容,删除其他无关信息和内容,然后保存该文件,查看当前状态:

根据提示,解决冲突后提交:

查看分支

对于创建过但并未删除的分支,我们可以查看分支列表,依然使用git branch指令,不传入任何参数:

图中列出了所有分支,前面带星号的表示当前分支,当然我们还可以查看指明最新提交信息的分支列表,可以添加-v参数:

筛选分支

除了可以查看所有分支列表,Git还支持筛选已合并或未合并至当前分支的所有分支:

1.–merged参数指明筛选已合并分支;

2.–no-merged参数指明筛选未合并分支。

删除分支(git branch -d)

当分支合并入主干后,也许我们不再需要那个分支了,我们需要将其删除,使用指令:

  git branch -d 分支名

之前介绍到使用git branch是创建新分支,而指定-d参数,说明需要删除该分支:

远程分支(remote branch)

我们注意到,前文所讲述的分支都是存在本地的,即本地分支,还需要了解远程分支,如[remote]/[branch]这种形式,表示是远端主机的某分支,关于远端主机详情请查看,其实远程分支和本地分支基本理论概念还是相同的,区别是有些指令不同而已:

 git checkout -b test origin/develop
        

以上指令即从远程分支(远端主机origin上的develop分支)切出新的本地分支test分支。

跟踪分支(TRACKING BRANCH)

前文已经介绍了本地分支和远程分支的概念及操作,那么这两类分支之间应该有某种关系将他们关联起来,本地项目都需要与远端主机仓库同步(pull & push),当我们从一个远程分支切出(创建)一个本地分支时,这个分支就叫跟踪分支(tracking branch),而远程分支叫上游分支(upstream branch)。

当我们克隆一个远端仓库时,会默认创建一个跟踪分支master,其上游分支就是远端主机别名/master。

创建跟踪分支

创建跟踪分支指令如下:

git checkout -b 本地分支名 远端主机别名/远程分支名

当然也可以不指定分支名,使用远程分支同名:

 git checkout --track 远端主机别名/远程分支名

修改跟踪关系

有时候,可能需要为本地分支设置其上游分支,添加-u参数:

git branch -u 远端主机别名/远程分支名

以上指令就指明当前分支跟踪某远端主机的远程分支。

查看跟踪分支(git branch -vv)

使用以下指令查看分支的上游分支:

git branch -vv
        

上图输出信息第二行表明master分支跟踪远程origin/master分支,ahead 7表明本地有7个提交未推到服务器,其他分支不是跟踪分支,没有上游分支。

删除远程分支

对于不再需要的远程分支,是可以删除的:

git push origin --delete test

以上指令删除远端主机origin的test分支,但是在垃圾回收之前,Git服务器仍然会保留分支数据,我们可以很方便的恢复数据,之后会详细介绍。

变基(rebase)

Git中有两种方式整合不同分支的修改:第一种是前文介绍的合并(merge),另一种就是本节的主题变基(rebase)。

变基其实与前文提到的三路合并(three-way merge)颇有渊源:

如图work-a分支与主干master分支合并后,创建一个新提交对象,我们还可以通过变基完成两个分支的修改整合,由于work-a分支已合并到master分支,我们在work-a分支再提交一次修改e0ae7dc,然后我们将work-a分支对master分支进行变基:

执行变基时,由于两个分支对同一文件同一部分进行了不同修改,会提示冲突,需要解决冲突,我们修改文件解决冲突,然后查看状态:

上图,第一行rebase in progress; onto 4ae14b3说明当前分支针对4ae14b3快照进行变基,第三到第五行分别说明:

1.第三行:解决冲突然后执行git rebase –continue指令继续变基;

2.第四行:执行git rebase –skip指令,跳过解决冲突;

3.第五行:执行git rebase –abort指令,终止变基,回到分支变基前状态。

下面第6到第八行说明:

4.第七行:使用git reset HEAD 指令撤销某文件变更;

5.第八行:使用git add 指令标记冲突为已解决状态。

最后一行no changes added to commit (use “git add” and/or “git commit -a”),说明尚未标记冲突,需要使用指令标记变更,在继续执行变基:

如上图,变基后,在主线上创建新提交对象640b83,并修改work-a分支指针指向该提交对象:

之后我们可以正常的合并:

变基后合并

如图,主线分支更新提交对象到640b83a,第二行Fast-forward说明此次合并属于快速推进合并方式,结构如下:

三路合并与变基

基于上例,三路合并,整合修改变更后会保留分支的原始提交记录,新创建提交对象有两个父提交对象,一个在主线上,一个在待合并分支上;而变基则不能保留待合并分支的原始提交记录,主线上新建的提交对象只有一个位于主线上的父提交对象。更多变基相关内容计划单独出文介绍。

至于到底选用哪种方式整合变更,变基还是合并,这个一直有争论,没有哪一种方式绝对合理,我们只需要把握一个原则:无论变基还是合并,你应该只操作本地历史记录,任何已经推到服务器并入主干的内容和提交历史不应该更改。

   
 订阅
  捐助
相关文章

每日构建解决方案
如何制定有效的配置管理流程
配置管理主要活动及实现方法
构建管理入门
相关文档

配置管理流程
配置管理白皮书
CM09_C配置管理标准
使用SVN进行版本控制
相关课程

配置管理实践
配置管理方法、工具与应用
多层次集成配置管理
产品发布管理
 

软件配置管理的问题、目的
软件配置管理规范
CQWeb 7.1性能测试与调优指南
为什么需要使用ClearCase
ClearCase与RTC的集成
利用ClearQuest 进行测试管理
更多...   

产品发布管理
配置管理方法、实践、工具
多层次集成配置管理
使用CC与CQ进行项目实践
CVS与配置管理
Subversion管理员

相关咨询服务
SCM启动咨询
SCM流程规范咨询
SCM评估性咨询

配置管理实践(从组织级到项目级)
通号院 配置管理规范与应用
配置管理日构建及持续集成
丹佛斯 ClearCase与配置管理
中国移动 软件配置管理
中国银行 软件配置管理
天津华翼蓝天科技 配置管理与Pvcs
 
 
 
 
 
每天2个文档/视频
扫描微信二维码订阅
订阅技术月刊
获得每月300个技术资源
 
 

关于我们 | 联系我们 | 京ICP备10020922号 京公海网安备110108001071号