您好、欢迎来到现金彩票网!
当前位置:众彩 > 分支 >

Git 由浅入深之分支管理

发布时间:2019-05-08 19:38 来源:未知 编辑:admin

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  如图,主线a,第二行Fast-forward说明此次合并属于快速推进合并方式,结构如下:

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

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

http://jigsawesl.com/fenzhi/170.html
锟斤拷锟斤拷锟斤拷QQ微锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷微锟斤拷
关于我们|联系我们|版权声明|网站地图|
Copyright © 2002-2019 现金彩票 版权所有