zoukankan      html  css  js  c++  java
  • 本地分支的重要操作

    Git应用详解第三讲:本地分支的重要操作

    https://www.cnblogs.com/AhuntSun-blog/category/1650856.html

    前言  

    前情提要:Git应用详解第二讲:Git删除、修改、撤销操作

    分支是git最核心的操作之一,了解分支的基本操作能够大大提高项目开发的效率。这一讲就来介绍一些分支的常见操作及其基本原理。

    一、分支概述

    在开发当中,往往需要分工合作。比如:小红开发A功能,小明开发B功能,小刚开发C功能。如何才能做到三者并行开发呢?git为我们提供的分支功能就能实现这一需求,如下图所示:

    image-20200413193720239

    在实际的开发过程中,master分支是用来发布项目稳定版本的。新的功能往往是在一个新建的分支上进行开发,等到新功能开发完毕并经过测试,表现稳定后,才会合并到master分支上进行版本更新。这样就可以在保持一款软件发行的同时,同步进行新功能的开发。

    通常来说,远程仓库的Git分支会有如下几种:master分支、test分支、develop分支,除此之外可能还有紧急修复bughotfix分支;但是,本地的分支可以有很多;本文主要介绍Git本地分支的内容。

    二、查看本地分支

    1.git branch

    查看当前版本库中的所有分支:

    image-20200310151203938

    其中的 * 表示当前处于的分支,可见当前处于master分支;

    使用git init初始化git仓库时,git会自动创建一个master分支。但是,如果没有在master分支上进行任何提交就切换到其他分支,那么在切换分支的时候master分支会被销毁。并且,无法查看没有提交记录的分支,如下图所示:

    image-20200413115148907

    2.git branch -a

    查看所有本地分支,包括本地分支和本地远程分支:

    image-20200328164045256

    3.git branch -v

    查看所有本地分支上最近一次的提交记录:

    image-20200413122049091

    但是,该指令无法查看本地远程分支:

    image-20200413123220561

    4.git branch -r

    -r参数用于单独查看本地远程分支:

    image-20200413174143173

    5.git branch -av

    该指令不仅可以显示所有的本地分支,包括本地远程分支,以及对应分支上的最新提交信息:

    image-20200409115958334

    6.git branch -vv

    -vv参数表示查看所有本地分支与远程分支的关联情况。如图所示,本地master分支有本地远程分支origin/master与之关联,说明它已与远程master分支建立了关联;

    image-20200410121145929

    至于上面提到的本地远程分支,将在下一讲中详细介绍。

    三、创建本地分支

    1.git branch <branch_name>

    可通过上述命令创建新分支new_branch

    image-20200310151357642

    由于是在master分支上创建的new_branch分支,所以new_branch分支master分支有着部分共同的提交历史;所以,master分支上的文件,new_branch分支上都有。但是,在new_branch分支上添加的new_branch文件,不会存在于master分支上:

    image-20200413120006856

    此时两分支的状态为:

    image-20200413120509219

    2.git branch -b <branch_name>

    通过上述命令可创建并切换到new_branch分支:

    image-20200413120945856

    如图所示,本来所在分支为master,并且没有new_branch分支。执行上述命令后,创建并切换到了new_branch分支上。

    四、切换本地分支

    1.git checkout <branch_name>

    比如切换到new_branch分支:

    image-20200310151539203

    2.git checkout -

    切换回上次操作的分支:

    image-20200310152313013

    五、重命名本地分支

    1.git branch -m <oldName> <newName>

    如下图所示,将本地分支master重命名为master2

    image-20200311115657966

    六、删除本地分支

    1.git branch -d new_branch

    删除new_branch分支:

    image-20200310163652841

    注意点:

    • 不能删除当前所处的分支;

    • 当需要删除的分支上有master分支没有的内容,并且删除前没有进行合并(merge)时,会报错:

    image-20200310163934006

    此时可以通过git branch -D new_branch 使用参数D,在不合并的情况下强制删除分支

    七、合并分支

    注意:这里所讲的分支指的是有公共提交节点的分支,如下图中的devmaster分支所示,提交节点A为它们的公共提交节点:

    image-20200411210604441

    当两分支没有公共提交节点,如下图所示,应采用rebase进行合并,后面会详细介绍:

    image-20200411210330211

    1.git merge <branch_name>

    • 首先,创建并切换到新分支dev中,并为test.txt文件添加内容dev1

      image-20200413213753822

      注意:要将dev分支上的这一修改提交到版本库,才能进行后续合并。因为合并的是提交对象链,详情见后面讲解的合并原理:

      image-20200413124513814

    • 然后,切换回master分支,通过git merge dev指令,将dev分支中的内容合并到当前所处master分支中;合并后master分支与dev分支上test.txt文件的内容达到了同步:

      image-20200413213934629

    2.分支合并的原则

    git分支的合并采用的是三方合并的原则:找到两分支最新提交AB的公共父节点C,在这三个节点的基础上合并为节点D。这个节点D就包含了两个分支上的所有内容:

    image-20200407160615973

    八、分支的本质

    分支:指向一条commit对象链或一条工作记录线的指针;

    快照A~D分别表示四次提交(commit),注意提交的顺序为:A -> B -> C -> D

    image-20200408150401090

    从图中可以看到每一次提交的对象内都会保存上一次提交的commit id,由此可以从后往前把所有的提交(commit)串起来形成一条(类似单向链表),这条链就组成了一条完整的分支信息

    • 当版本库中只有一条分支:该分支的最新提交就包含了整条分支的所有内容,代表版本库的当前状态。如上图的快照D,里面包含了快照A~C中的所有内容,此时快照D中的内容就是整个版本库中的内容:

      image-20200413132342942

    • 当版本库中有多条分支:每条分支上的最新提交包含了所处分支的全部内容,将各个分支的最新提交进行合并。合并的节点就包含了所有分支的内容,也就是现阶段的版本库本身;如下图中的d1m2t3分别包含了devmastertest分支上的所有内容:

      image-20200413132409178

    1.分支 == 指针

    情景一:

    image-20200406131416304

    从图中可以看到:

    • HEAD为一个指针:指向当前分支;

    • master也为一指针:指向提交;

    情景二:

    image-20200406131503784

    上图中,devmaster分支上创建的新分支,可知:

    • git在创建新分支时,文件本身不变化,只需要创建一个代表新分支,并指向当前分支的指针;如图中的devmaster指向同一个提交,文件没有发生任何变化;
    • HEAD指向dev分支,表示当前所处分支为dev,相当于执行了:git checkout -b dev 后的状态;

    相信你已经发现:HEAD是一个始终指向当前分支指针

    2.HEAD标识符

    HEAD文件是一个指向当前所在分支的引用标识符,也可以理解为一个指针,它与分支之间的关系是这样的:

    image-20200408000947960

    查看HEAD

    HEAD文件中并不包含SHA1值(每次提交的commit ID),而是包含一个指向另外一个引用的指针。我们可以查看.git目录下的HEAD文件:

    image-20200310172919110

    可见HEAD指向的是当前所在的master分支;

    当我们通过git checkout -b dev 创建并切换到dev分支后,再次进入.git文件夹查看HEAD,会发现此时HEAD指向了dev

    image-20200310173205214

    由此证明了HEAD始终指向当前分支;

    当执行git commit命令时,git会创建一个commit对象(比如下图D)。并且将这个commit对象的parent指针指向HEAD 所指向的引用(master)指向的提交(也就是C),这样就能形成一条提交链:

    image-20200408001856063

    我们对于HEAD修改的任何操作,都会被git reflog完整记录下来:

    image-20200329145707276

    但是手动地修改HEAD文件,这些信息就不会被记录下来,所以十分不建议手动修改git相关的配置文件,而是应该尽量采用命令行的方式来修改。

    修改HEAD

    实际上,我们可以通过git的底层命令symbolic-ref来实现对HEAD文件内容的修改;

    git中的命令可分为两类:高级命令底层命令;之前介绍的git add 等都是高级命令;

    读取:

    image-20200329150120113

    写入:

    image-20200329150239094

    要注意格式:refs/heads/develop

    查看ORIG_HEAD文件:

    image-20200329150514036

    里面是一个SHA1值,查看当前的提交信息:

    image-20200329150643422

    可以发现,ORIG_HEAD里面的SHA1值就是最新一次提交的SHA1值。

    查看FETCH_HEAD文件:

    image-20200329150904196

    里面有两个信息,一个是最新提交的commit ID,另一个是提交信息。

    所以,对于git而言commit ID是十分重要的信息,通过这个SHA1值可以回溯或查找需要的提交。

    3.git merge原理

    过程图解
    • 在新分支上进行提交操作

      image-20200406131605850

      上图表示在dev分支上进行了一次提交,此时:

      • 分支master的提交记录由:ABC组成;
      • 而分支dev的提交历史则由:ABCD组成;
    • 对两分支进行合并操作

      image-20200406131708763

      master分支上执行:git merge devdev分支的内容合并到了master分支上;这种合并方式叫做:Fast-forward没有冲突,改变的只是master指针的指向;

      image-20200310180632403

    详细过程

    在执行合并操作前:

    • master分支上查看该分支的提交记录:

    image-20200413135649151

    • dev分支上查看该分支的提交记录:

    image-20200413135632944

    可以看到dev分支只是比master分支多进行了一次提交(dev1),两分支状态如下图所示:

    image-20200413140621228

    执行合并操作:

    先切换到master分支,然后执行git merge dev合并dev分支:

    image-20200413135914872

    可以看到使用了Fast-forward方式进行合并,合并后两分支状态如下图所示:

    image-20200413140634456

    合并后,HEAD同时指向了masterdev分支;并且masterdev分支的提交历史完全一致;这就说明了:使用Fast-forward(快进合并)方式进行分支合并,只会改变master分支指针的指向;

    4.Fast-forward

    • 默认情况下,合并分支时git会使用Fast-forward模式;
    • 在这种模式下,删除分支会丢弃分支信息;
    • 进行分支合并操作时加上--no-ff 参数会禁止使用Fast-forward方式,这样会多出一次提交记录;

    ff表示Fast-forward

    具体演示如下:

    使用Fast-forward

    首先,查看master分支上最新的3次提交:

    image-20200413140911195

    此时两分支的状态为:

    image-20200413141208043

    随后在dev分支上新增一次提交:dev2。查看dev分支上最新的3次提交:

    image-20200413141111031

    此时两个分支的状态为:

    image-20200413141246153

    切换回master分支,通过git merge dev合并dev分支,此时默认采用Fast-forward方式:

    image-20200413141412571

    可以看到合并后,master直接指向了dev的最新提交,并没有产生新的提交。合并后两分支的状态如下所示:

    image-20200413141554807

    由此验证了Fast-forward方式只会改变分支指针的指向。

    禁用Fast-forward

    合并时可以通过:

    复制代码
    git merge --no-ff dev
    

    禁用Fast-forward模式。

    • 继续在dev分支新增一次提交:dev3。然后查看dev分支上最新的3次提交:

      image-20200413142011002

    • 不修改master分支,查看其最新的3次提交:

      image-20200413142116641

      此时两个分支的状态为:

      image-20200413142145430

    • 然后,在master分支上不使用Fast-forward方式合并dev分支。合并命令采用:

      复制代码
      git merge --no-ff dev
      

      执行后进入如下的vim编辑器界面,要求我们填写提交注释:

      image-20200413142507135

    这说明不使用Fast-forward方式合并分支,会触发了一次提交。填写提交注释后完成提交操作,合并完成后,查看master分支的提交记录:

    image-20200413142907282

    可以发现禁用了Fast-forward模式的合并会比dev分支多产生一次提交:Merge branch 'dev',即使合并后的内容是一样的。此时两分支的状态为:

    image-20200413144533217

    由此验证了,禁用Fast-forward方式合并,会多出一个表示合并的提交记录。

    5.合并冲突

    合并的两分支只有一条分支发生了改变,并且其中一分支是基于另一分支创建的。比如上述的masterdev分支,两分支没有分岔,此时不会出现合并冲突;git会通过Fast-forward方式自动完成合并操作;

    但是,当合并的两分支都发生改变时,即分支出现分岔,如下图所示。此时就需要解决冲突后手动合并分支了:

    image-20200413145058222

    具体演示如下:

    合并前

    首先,分别对两分支上的test.txt文件进行修改,并分别将修改提交到各自的分支;

    • master分支上进行新的提交:mas3,然后查看文件test.txt内容和分支提交记录:

    image-20200413150045837

    • dev分支上进行新的提交:dev1,然后查看文件test.txt内容和分支提交记录:

    image-20200413150139932

    可见两分支的提交记录只有最新一次提交不一样:

    image-20200413153145295

    合并后

    master分支上,通过git merge dev合并dev分支时,会在共同修改的test.txt文件中出现合并冲突,如下图所示:

    image-20200413150334009

    出现冲突的原因为:两个分支都对同一个文件test.txt进行了修改,git合并时并不知道以哪个分支的修改为标准。所以不能采用Fast-forward方式自动合并,需要解决冲突,手动合并。

    手动合并过程

    手动合并操作需要分如下三步进行:

    • 第一步:选择需要保留的内容,手动解决合并冲突;

    此时进入发生合并冲突的test.txt文件:

    image-20200413150400943

    会出现典型的冲突呈现方式(此时HEAD指向的是master分支),其中:

    • <<<HEAD>>>dev之间的内容表示:两分支上test.txt文件的不同之处;

    • <<<HEAD===之间的内容表示:当前分支mastertest.txt文件的修改;

    • ===>>>dev之间的内容表示:dev分支对test.txt文件的修改;

    此时只需要在test.txt中保留想要的内容即可,例如:将两分支对test.txt的修改都进行保存,删除3、5、7行多余的内容:

    image-20200413150810935

    除此之外,还可以通过git mergetool指令,调用vimdiff工具进入vim编辑器,来解决test.txt文件的冲突:

    image-20200413212456463

    image-20200413212331710

    在实际开发中,我们很少手动进行合并,而是借助于一些工具来实现。

    • 第二步:使用git add test.txt将手动解决冲突时对test.txt的修改提交到暂存区;

    编辑完毕后,可以看到此时仍然处于合并过程中(MERGING)。通过git status 查看状态,发现手动解决冲突时对test.txt文件的修改操作还在工作区中,需要通过git add test.txt将这一修改纳入暂存区,继续进行合并:

    image-20200413151548133

    • 第三步:通过git commit -m '合并注释'将手动解决冲突时对test.txt的修改进行提交,完成合并操作;

    如果需要填写较多的合并注释,可以通过git commit进入vim编辑器编辑。提交后,完成整个手动合并过程。

    image-20200310185125770

    完成手动合并分支后,查看两分支的提交历史:

    • master分支上的提交历史:

    image-20200413152813941

    • dev分支上的提交历史:

    image-20200413152928725

    可以发现此时两分支转变为了可以通过Fast forward方式合并的形式了,如图所示:

    • 手动解决冲突前:

    image-20200413153154756

    • 手动解决冲突后:

      image-20200413153427160

    同步两分支

    若想将devmaster分支上的内容进行同步,只需要在dev分支中通过git merge master 合并master分支即可。此时就可以使用Fast-forward方式进行合并了,合并结果如下图所示:

    image-20200413153555178

    验证:

    合并后,查看dev分支的提交历史:

    image-20200413154132053

    可以看到HEAD同时指向devmaster,即三个指针都指向了最新的一次提交,符合上述分析得出的结论;

    经过上面的讨论,不难看出:合并分支的实质就是不同提交的合并,以及HEAD和分支指针的移动;

    以上就是今天介绍的本地分支重要操作,相信看到这里的你已经对git本地分支的操作了如指掌了。在下一讲中将介绍git最神奇的功能:版本回退。俗话说得好:世上没有后悔药。但是在git中,就存在所谓的"后悔药"!那么我们下一节再见。

     

    多抽出1分钟来学习,让你的生命更加精彩!

  • 相关阅读:
    I NEED A OFFER!
    水题 Codeforces Round #303 (Div. 2) A. Toy Cars
    模拟 HDOJ 5099 Comparison of Android versions
    模拟 HDOJ 5095 Linearization of the kernel functions in SVM
    贪心 HDOJ 5090 Game with Pearls
    Kruskal HDOJ 1863 畅通工程
    Kruskal HDOJ 1233 还是畅通工程
    并查集 HDOJ 1232 畅通工程
    DFS/并查集 Codeforces Round #286 (Div. 2) B
    水题 Codeforces Round #286 (Div. 2) A Mr. Kitayuta's Gift
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/12717702.html
Copyright © 2011-2022 走看看