大家好,今天我们来聊git当中一个非常非常重要的特性,就是branch

git branch可以说是git当中最重要的概念了,甚至没有之一。因为git最重要的使用场景就是协同开发,大家一起在一个项目当中开发不同的功能。正是由于有了分支的概念,可以让大家在开发的时候互不影响。如果没有这个功能,git的其他功能做的再好,可能都没有用。

所以某种程度上可以理解为,学git最重要的就是学习分支的相关内容。当然分支的相关内容和命令非常多,我们想要瞬间全部都学会显然不太现实。但对这个概念有一些理解,懂得一些基本命令的用法应该还是做得到的。

在理解分支这个概念之前,我们需要先来介绍一下Git的结构。

Git的结构

branch的英文就是树枝,后来衍生出了分叉、支路等意思。这个单词非常形象,因为git仓库的所有提交节点之间的关系,其实就是一棵树,所以一个分支也可以看成是树上的一条链路。但是这样有一个小问题是,如果说分支是一条链路的话,那么这个链路上的每一个节点代表的是一个commit提交,意味着一份代码快照,有些像是游戏存档。

一个branch上有多个commit,一个游戏也可以有多个存档,但是当下显然只能加载一个。所以git当中用一个指针指向当前加载的commit,也就是说纵向来看一个分支代表的是一连串的提交,但在git当中我们使用的分支其实是一个指针,一个在commit当中切换的指针。

我们来看个例子,比如一开始的时候我们只有默认分支master,它指向当前的一个提交。

现在我们使用git branch test命令创建一个测试分支,执行之后,其实只不过是多了一个指针也指向当前的commit。git当中的结构变成这样:

当我们在test分支上做了改动提交之后,git会产生一个新的提交,并且移动test指针,而master指针会留在原地。

如果我们再回到master也进行了改动和提交之后,又会产生新的节点,并且这个节点会和test的节点区分开,形成新的链路,于是就形成了一棵树的样子。

分支与HEAD指针

怎么分支创建,我们刚才已经讲过了,可以通过git branch加上我们想要的分支名来完成。使用了这个命令之后,git内部会创建一个新的指针指向当前的commit。

有一个问题是git怎么知道我们当前的代码在哪里呢?即使知道了代码在哪个分支上,又怎么确定在哪一个节点呢?其实git内部还有一个特殊的指针叫做HEAD,它指向的是当前代码仓库的位置。当我们提交代码的时候,不止只有分支的这些指针会往前移动,HEAD指针也会随着移动。

其实HEAD指针不仅可以往前移动,还可以移动到任意节点上,哪怕不再当前的分支上也可以。移动HEAD指针需要用到git checkout命令,它可以指定HEAD指针移动到其他位置。既可以是某一个分支,也可以是根据commit id来确定的节点。

比如我们当前在master分支,我们要切换到test分支上,我们只需要运行:

git checkout master

另外,使用git checkout命令加上参数-b,我们还可以创建分支。比如假如当前test分支不存在,我们可以通过git checkout -b test来创建,并且还会自动切换到新建出的test分支上。

我们在新的分支上做了提交之后,可以通过git log --oneline --decorate命令来查看每一个分支所指向的位置。这里的oneline是将log压缩到一行展示,decorate用来查看分支所指向的位置。

我们可以发现test和master分支指向的提交不同,并且当前我们的HEAD在test上,说明我们当前在test分支。我们前面说了git checkout命令可以改变HEAD指针指向的位置,假如我们在当前目录下执行git checkout 18a417,这个18a417对应的是add article 6这个提交。这个提交是在master分支的,是test分支的上游,我们使用命令会自动将HEAD跳转到master分支。

使用之后我们发现的确到了master分支,这里由于我配置了zsh工具,它会提示我当前所处的位置是比master分支指向的最新位置落后3个提交。

这也验证了我们说的,HEAD指针可以随意跳转。现在想必你们应该能理解上一篇文章当中介绍的,撤销当前分支的命令git reset HEAD^的含义了,HEAD指的就是HEAD指针,^表示的上一个提交。如果是前多个提交,我们可以用~加数字的形式来表示。比如上图当中划了红线标注的master~3,就表示master节点上3个提交。

分支合并

最后来简单说说分支合并,我们在使用git进行协同开发的过程当中,虽然大家都在各自的分支。但是最后代码还是要合并到一起的,这样才可以投入使用。git当中代码的合并是通过分支合并来体现的。

比如当前的这一篇文章被我加在了test分支当中,这显然是不行的,因为使用方不可能一一去理解每一个分支做了什么,当中的代码逻辑。所以大多数的分支只是暂时的,用来暂时完成一项功能的,等功能完成之后,一般都会再合并回master分支,将所有的改动合并进去。

合并的方式非常简单,我们只需要先checkout我们想要合并的目标分支。比如我们要合并到master,就checkout到master。然后使用git merge test命令,表示和test这个分支合并。

合并之后,如果没有报错就算是合并成功了。它会展示出来合并进来的代码改动,我们注意到日志里有一个fast-forward这个单词,它表示快速合并。快速合并的意思也很简单,因为我们test分支是从master分支当中切出去的。后来master分支就再也没有进行过改动,那么当我们合并的时候,其实只需要移动一下master指针,将它移动到test分支上即可。

我们用图来展示,合并前:

合并后:

那如果我们在master分支上也有改动,不再是待合并分支的直接上游,会发生什么呢?

上图当中我们做了一系列操作,首先我们创建了一个叫做test_merge的分支,在其中创建了一个文件叫做a.txt,接着我们切回master分支创建了b.txt。最后我们把两个分支合并。

合并当然也没有问题,但是我们来用git log来查看一下日志:

会发现日志里多了一个commit,这个commit并不是我提交的,而是它自动产生的。我们一样用图来展示一下,这是合并前:

合并之后:

由于不再拥有直接上下游关系了,所以git创建了一个新的commit用来合并两个分支的代码。当我们合并完成之后,我们就可以把没用的分支都删除了。删除的命令是git branch -d test。

当然git merge的时候并不是永远都一帆风顺的,难免会遇到冲突。所谓的冲突也就是两个人修改了同一份代码,git会不知道应该保留哪一个,于是提示冲突,让程序员自己搞定。关于git merge时遇到冲突怎么办的问题,我们放到下一篇文章当中和大家分享。

今天的文章就到这里,衷心祝愿大家每天都有所收获。如果还喜欢今天的内容的话,请来一个三连支持吧~(点赞、关注、转发

原文链接,求个关注

本文使用 mdnice 排版

- END -

图解git,用手绘图带你理解git中分支的原理和应用的更多相关文章

  1. Git复习(九)之理解git工作区和暂存区

    前言 Git和其他版本控制系统如SVN的一个不同之处就是有暂存区的概念. 版本库 在工作区目录中有一个.git文件,这个其实不是工作区而是Git的版本库 版本库中包含两个部分,一个是暂存区index/ ...

  2. 从底层带你理解Python中的一些内部机制

    下面博文将带你创建一个字节码级别的追踪API以追踪Python的一些内部机制,比如类似YIELDVALUE.YIELDFROM操作码的实现,推式构造列表(List Comprehensions).生成 ...

  3. 一文带你理解TDengine中的缓存技术

    作者 | 王明明,涛思数据软件工程师 小 T 导读:在计算机系统中,缓存是一种常用的技术,既有硬件缓存,比如我们经常听到的 CPU L2 高速缓存,也有软件缓存,比如很多系统里把 Redis 当做数据 ...

  4. 【SpringBoot】 理解Spirng中的IOC原理

    前言 前文已经介绍了Spring Bean的生命周期,在这个周期内有一个重要的概念就是: IOC容器 大家也知道IOC是Sping 的重要核心之一,那么如何理解它呢,它又是产生什么作用呢?本文就IOC ...

  5. 深入理解JDK中的Reference原理和源码实现

    前提 这篇文章主要基于JDK11的源码和最近翻看的<深入理解Java虚拟机-2nd>一书的部分内容,对JDK11中的Reference(引用)做一些总结.值得注意的是,通过笔者对比一下JD ...

  6. 深入理解spring中的AOP原理 —— 实现MethodInterceptor接口,自已动手写一个AOP

      1.前言 AOP是面向切面编程,即“Aspect Oriented Programming”的缩写.面对切面,就是面向我们的关注面,不能让非关注面影响到我们的关注面.而现实中非关切面又必不可少,例 ...

  7. 深入理解Mybatis中sqlSessionFactory机制原理

    对于任何框架而言,在使用前都要进行一系列的初始化,MyBatis也不例外.本章将通过以下几点详细介绍MyBatis的初始化过程. 1.MyBatis的初始化做了什么 2. MyBatis基于XML配置 ...

  8. 理解Git的工作流程(转)

    英文原文:Understanding the Git Workflow 如果你不理解Git的设计动机,那你就会处处碰壁.知道足够多的命令和参数后,你就会强行让Git按你想的来工作,而不是按Git自己的 ...

  9. 深入理解Git - 一切皆commit

    在对 git 有了基本理解和知道常规操作之后,如何对 git 的使用有进一步的理解? 一切皆 commit 或许是个不错的理解思路. 本文将从『一切皆 commit 』的角度,通过 git 中常见的名 ...

随机推荐

  1. redis之哨兵 springboot配置

    转载自https://blog.csdn.net/m0_37367413/article/details/82018125 springboot整合redis哨兵方式配置 2018年08月24日 14 ...

  2. zookeeper(2) 文件系统

    这一节我们主要来看一下zookeeper文件系统的实现. 树结构 为了提高对指定节点的操作,zookeeper使用一个HashMap来存储树结构数据,key为数据路径,value为节点数据. 树节点( ...

  3. 【5】JMicro免费在线消息服务

    JMicro是一个用Java语言实现的开源微服务全家桶, 源码地址:https://github.com/mynewworldyyl/jmicro, Demo地址:http://jmicro.cn/. ...

  4. java学习从“菜鸟”到“放弃”

    今天学到java的对象和类中, 由于刚考完c++面向对象与程序设计这门课,对于c++中的类掌握自认为不错,就开始过渡到java. 今天面对的问题,在书写一个类的时候,发现了许多与c++不同的地方. 比 ...

  5. Linux环境下C++调试的三板斧

    调试解决程序的漏洞,是程序员最基本的技能之一.用惯了图形化IDE,在目前使用gtest框架进行单元测试,需要通过xshell远程连接Linux虚拟机进行C++代码的调试时,觉得很不适应.经过几天查资料 ...

  6. 软件定义网络实验记录①--Mininet 源码安装和可视化拓扑工具

    第一步: 在 Ubuntu 系统的 home 目录下创建一个目录,目录名为自己的标识,包括但 不限于学号.姓名拼音等,目录不要包含中文. 第二步: 在创建的目录下,完成 Mininet 的源码安装. ...

  7. Excel双击“单元格”后,自动跳转到相关“工作表

    Private Sub Worksheet_BeforeDoubleClick(ByVal Target As Range, Cancel As Boolean)If Target.Column = ...

  8. 【随笔】菜刀(代码执行)函数和命令执行函数详解及Getshell方法

    代码执行函数 VS 命令执行函数 一直想整理这两块的内容,但是一直没时间弄,直到前两天碰上一个写入了菜刀马但是死活连不上菜刀的站,顿时不知道怎么继续了,所以就趁这个机会整理了一下代码执行函数怎么get ...

  9. c++中CreateEvent函数

    参考:https://blog.csdn.net/u011642774/article/details/52789969 函数原型: HANDLE CreateEvent( LPSECURITY_AT ...

  10. Java (一)下载APACHE Commons IO

    一,进入APACHE的官网,在首页往下找APACHE PROJECT LIST > Commons,点击进入. 二,进入Apache Commons页面,找IO组件,点击进入. 三.点击链接Do ...