Git学习——分支详解

来源:互联网 发布:淘宝怎么扫码付款 编辑:程序博客网 时间:2024/05/22 06:24

    前言:在大项目的开发中,很有可能需要把工作从开发主线上分离出来,以免影响开发主线。对于大部分的版本管理系统来说,这就意味着完全创建一个源代码目录的副本,然后基于这个分支开发。在上一节的学习中,我们知道Git是以快照的形式管理软件版本,这就使得创建分支这个业务流程非常高效、快捷。



    一、Git保存文件的方式

    假设现在有一个工作目录,里面包含了三个将要被暂存和提交的文件。暂存操作会为每一个文件计算校验和(SHA-1 哈希算法),然后会把当前版本的文件快照保存到 Git 仓库中(Git 使用 blob 对象来保存它们),最终将校验和加入到暂存区域等待交,此时Git仓库中仅包含三个blob对象。提交操作时,Git会先计算每一个子目录(本例中只有三个已经暂存文件)的校验和,然后在 Git 仓库中这些校验和保存为树对象。随后,Git 便会创建一个提交对象,它除了包含上面提到的那些信息外,还包含指向这个树对象(项目根目录)的指针。如此一来,Git就可以在需要的时候重现此次保存的快照。现在,Git 仓库中有五个对象:三个 blob 对象(保存着文件快照)、一个树对象(记录着目录结构和 blob 对象索引)以及一个提交对象(包含着指向前述树对象的指针和所有提交信息)。


                         Git仓库示意图



 做些修改后再次提交,那么这次产生的提交对象会包含一个指向上次提交对象(父对象)的指针。




   二、分支的概念

    Git 的分支,其实本质上仅仅是指向提交对象的可变指针,Git 的默认分支是 master(git init 命令默认创建)。在多次提交操作之后,都有一个指向最新那个提交对象的master分支,它会在每次的提交操作中自动向前移动。

    



      三、创建分支

    前文中已经说明分支是一个指向提交对象的可变指针,比如使用git branch testing命令即创建了一个指向当前提交对象的分支,分支名为testing。那么现在的问题就是有两个分支指向当前提交对象,Git是如何区分当前所处的是哪一个分支?Git中有一个名为HEAD的特殊指针,这个指针指向的即是Git系统当前所处的分支!如图所示:


      a、查看所有分支指向的提交对象

        git log --decorate

    b、分支切换

        git checkout testing     命令切换HEAD指针指向新建分支testing,即切换系统所处分支为testing分支。

    c、查看分支发展的分差情况

        git log --oneline --decorate --graph --all

    d、删除分支

        git branch -d


    注:切换分支即切换当前工作目录为该分支指向的项目提交版本。注意和版本回滚git reset有区别。


    四、分支的合并

    假设情形:1、开发某个项目

                     2、为实现某个开发计划外新增的功能,创建一个分支并在这个分支上继续开发

                     3、基于主线master分支继续开发

                     4、主线和分支开发完毕,合并分支


    操作逻辑:a、创建新的分支newbranch并切换

                         git checkout -b newbranch  等价于 git branch newbranch && git checkout newbranch

                     b、在newbranch分支上开发提交数个版本后,切换回主分支master

                         git checkout master

                     c、合并分支

                         分两种情况,第一种,newbranch分支和master分支在一条链路上;第二种newbranch分支和master分支在分叉的两条链路上。

                         情形一:git merge newbranch   由于master分支指向的提交是newbranch分支指向的提交在同一条链路上的上游,所以合并分支只是移动master分支指向newbranch分支指向的提交。

                         情形二:git merge newbranch   执行的命令同情形一是一致的,但是由于两个分支指向的提交处于分叉的两个链路中,Git系统将基于两个分支的提交对象以及分叉起始点的提交对象生成一个新提交对象,master分支指向这个新提交对象。


   注:分支合并的时候可能会遇到存在冲突的情况!比如在两个分支中同一个文件的同一处地方存在差异,Git系统无法合并这两个分支中的冲突文件并生成新的提交对象。此时,使用git status可以查看到哪些文件存在冲突(unmerge状态),打开这些文件处理掉存在冲突的内容,使用git add命令添加文件为暂存,使用git commit提交生成提交对象。


    五、查看分支

        git branch 命令不带任何参数执行后可以获取到当前所有分支的一个列表,分支名字前的符号*表示HEAD指针;查看各个分支最后一次的提交对象使用 git branch -v;查看哪些分支已经合并到当前分支或哪些分支还没有合并到当前分支,使用git branch --merged 或 git branch --no-merged。


    六、分支开发工作流

    长期分支模式

        master分支上保留完全稳定的代码,可以认为是正式发布的版本;其他一些指向更新提交对象的分支,例如下图中的develop和topic分支,用作后续开发或者测试,在版本稳定后再并入master分支。这种开发模式非常适合大型的、版本稳定性要求比较高的项目,对于小型项目的开发就显得比较繁琐。

    


    特性分支模式

       特性分支模式充分发挥了分支开发的好处,在主线开发和分支开发间切换切换、合并,解决多人协同开发和突发性需求开发的困难考虑这样一个例子,你在master分支上工作到c1,这时为了解决一个问题而新建iss91分支,在iss91分支上工作到c4,然而对于那个问题你又有了新的想法,于是你再新建一个iss91v2分支试图用另一种方法解决那个问题,接着你回到master分支工作了一会儿,你又冒出了一个不太确定的想法,你便在c10的时候新建一个dumbideal分支,并在上面做些实验。 你的提交历史看起来像下面这个样子:



   现在,我们假设两件事情:你决定使用第二个方案来解决那个问题,即使用iss91v2分支中方案;另外,你将dumbidea分支拿给你的同事看过之后,结果发现这是个非常棒的想法。这时你可以抛弃iss91分支(即丢弃c5和c6提交,使用git reset回滚分支版本到c4,然后删除分支),然后把另外两个分支合并入主干分支。最终你的提交历史看起来像下面这个样子:




   七、远程分支

        光说概念会非常难理解远程分支,这里举一个实例。网络里面有一个在域名git.ourcompany.com的Git服务器,从这个服务器上clone版本仓库到本地建仓库(假定远程Git服务器所处分支为master),Git会在本地创建一个同样的master分支和一个命名为origin/master的远程分支,如图所示:



    注:远程仓库的别名“origin”与分支名字“master”一样都是默认命名!如果执行git clone -o babytiger 命令克隆远程版本仓库,那么别名就是“babytiger”。

     

    1、同步远程分支    

        如果本地Git系统不与远程Git服务器进行连接,远程分支origin/master指向就会一直保持不变,而此时无论本地还是远程可能都有新的版本提交,如图所示:


        运行git fetch origin命令,该命令将与别名为“origin”的远程Git服务器连接,从中拉取本地没有的数据并更新本地版本库,将远程分支origin/master的指向对齐到远程Git服务器的master分支指向(该命令还将拉取新的远程分支,但是不会创建相应的本地分支)。如图所示:


   注:可能会存在多个远程版本服务器和多个远程分支的情况,运行 git remote add jerry xxxxxx 命令将添加一个新的远程仓库引用到当前项目中,需要注意的是要拉取新的远程仓库中的版本提交数据并创建新的远程分支,运行git fetch jerry命令。运行git fetch --all将拉取所有远程仓库数据!


   2、推送远程分支

        我们可以将本地分支推送到有写入权限的远程仓库上,Git系统不会自动完成这样一个操作。

        运行命令:git push origin newbranch    等价于 git push origin newbranch:newbranch

        推送本地的newbranch分支更新远程仓库上的newbranch分支

       运行命令:git push origin newbranch:master

        推送本地的newbranch分支更新远程仓库上的master分支


    3、跟踪分支

         从一个远程跟踪分支检出一个本地分支会自动创建一个叫做“跟踪分支”(也叫做 “上游分支”)。跟踪分支是与远程分支有直接关系的本地分支。如果在一个跟踪分支上输入git pull,Git 能自动地识别去哪个服务器上抓取、合并到哪个分支。例如,origin/master与master分支就是一个远程分支和本地跟踪分支。

         创建跟踪分支:

         git checkout -b [branch] [remotename]/[branch] 等价于 git checkout --track [remotename]/[branch]

         设置一个已有的本地分支跟踪一个拉取到的远程分支:

         git branch -u [remotename]/[branch]

   

     4、查看跟踪分支

        git branch -vv    列出所有分支信息,包括跟踪远程分支的情况。

     

     5、拉取与合并

         使用git fetch命令从服务器拉取本地没有的数据并同步远程分支,但是该命令不会合并本地分支和远程分支,需要自己使用git merge命令完成合并。可以使用git pull命令完全这两步操作,前提是合并的本地分支是远程分支的跟踪分支。

     6、删除远程分支

         git push [remotename] --delete [branch]



     八、变基

         Git中整合来自不同分支的修改主要的两种方法是:merge(合并)以及rebase(变基)。

     

     1、变基的基本操作

          考虑如何合并处于分叉中的两个不同分支,如图所示:

          

         方案一,运行git merge命令,将两个分支指向的最新提交c3和c4以及二者最近的共同祖先c2进行合并,合并的结果是生成一个新的快照(并提交)。合并结果如图所示:


         方案二,运行git rebase命令,将提交到某一分支的所有修改都移至另一分支上,并以修改结果作为新的提交,这种合并方式称为变基,合并结果如图所示:

        

         两个方案比较,合并生成的提交对象是等价的,但是变基的合并结果只有一个父提交,显得提交历史更加的简洁。

     

     2、复杂变基

          考虑这么一个情况,如图所示有master、server、client三个分支,现在希望client中的修改到master上。

          

         运行git rebase --onto master server client命令,合并结果如下:

           

     

     3、变基的风险

         最重要的规则:不要对在你的仓库外有副本的分支执行变基。变基实质上已经影响了版本仓库的历史提交记录,这就使得本地仓库和远程仓库的数据对齐非常麻烦。

0 0