嚎羸的博客

因为Hexo是静态博客,部署多有不便,建议查看我的语雀文档

0%

09、Git

版本控制工具的介绍

版本控制工具是什么

版本控制工具(VCS),可以看作一个一个的时间点。当我们发布了一个新的版本时,会出现一个新的时间点。当我们认为现有代码产生了巨大的BUG时,可以回退到上一个时间点,这样就可以十分快捷方便地对我们的代码进行管理。

版本控制工具有什么好处

1、首先一点就是做数据的备份,当我们不小心删除了某些内容想要恢复时,可以使用版本控制工具做到快速恢复备份的代码

2、版本管理,这也是VCS最重要的一点,可以方便地管理我们项目的版本,虽然可以使用复制副本的方式来进行控制,但是效率太低也容易混淆

3、多人协同,这也是VCS比较重要的一点,可以多人对一个项目进行协作,当某人进行了修改并提交新的版本时,可以十分明确某人修改的内容

常见的版本控制工具

1、SVN,跨平台的版本控制工具

2、CVS,老牌控制系统,可以容纳多用户,构成网络也十分方便

3、Git,这也是我们要讲解的工具

为什么选择Git

Git和CVS,SVN不同。

它是一个分布式的源代码管理工具,它非常快

它的绝大部分操作都可以在本地运行,我们可以在本地确定一个最终版之后,才向远端发送数据。

Git的基本概念

本地和远端

我们刚才在介绍Git的时候,曾经说过,Git的绝大部分操作都可以在本地运行,我们只需要在本地确定一个最终版之后才向远端发送新的数据,而不是每一次操作都向远端交互。那样的话费时又费力。

Git的基本使用

我们绝大部分的操作都是使用类似IDEA这种工具来完成的,可以大大缩减我们的时间,但是有一些Git的基本命令使用还是要了解的

初始化git仓库

刚才我们讲的,git分为远程仓库和本地仓库,那么远程的仓库自然是在网络上进行创建的,对于我们本地的仓库,可以使用git init这个命令来创建。

一旦创建,会在文件夹下创建出一个*.git*的文件夹,这个文件夹默认是隐藏的,你可以通过查看隐藏的项目来查看这个文件夹。

出现了这个文件夹,说明我们的本地仓库已经初始化好了

image-20201217103916938


添加文件到仓库

刚才我们创建好了本地仓库,但是这并不意味着和*.git*文件夹同级的都是git仓库内的东西,现在虽然看似是一级,但其实划分为了两类:git文件和普通文件。

如果你使用JetBrains家族的产品,可以很清楚的看到哪些文件属于Git,哪些不属于,这也是使用工具的强大之处:便于查看和管理。

我们可以看到,红色的都是没有被git进行管理的

image-20201217104301098

那么想要让一个文件加入到git仓库中,我们只需要执行命令git add 文件名,将所有文件都交给git管理,就是git add .

当然,你也可以使用工具来直接进行添加:

image-20201217104655103

image-20201217104713564

可以看到,在添加之后,文件变为了绿色的,这代表着我们已经将这个文件交给了git进行管理

那么问题来了,假如我不想让一些文件添加到git中呢?这就需要一个文件了

文件叫做*.gitignore*,我们平常使用maven项目时,它会自己创建,一般情况下不需要管,但是假如你要特别定制那就另说


提交

我们之前在介绍版本控制工具的时候曾经讲过点这个概念,在这里也差不多的概念。

我们每一次在本地提交一次,就会产生一个版本点,依托这个点我们可以进行版本的管理,比如回退操作。

它的代码也就是*git commit -m “注释”*,比如说:

image-20201217104830588

当然,你也可以使用工具来进行这项简单的操作

image-20201217104903328

image-20201217104931900

在提交之后,我们可以在工具中非常方便地查看提交的点,在之后我们可以随时回滚到这个点

image-20201217105105841


回滚

介绍了初始化,添加,提交的概念,接下来我们来讲一下回滚,为了演示方便,我首先更改一些内容,然后进行提交,来演示这些区别

image-20201217110004913

image-20201217110040530

现在我们所处在第二次提交的节点上,只需要在第一次提交的节点上右击–>重置即可

image-20201217110149197

概念理解

在开始讲解回滚方式之前,我们需要首先理解一下刚才的概念,刚才我们为了易于理解,讲了几个基本命令,对应着几个基本命令有几个概念需要理解一下:

image-20201217112620554

如图,我们在进行git add之后,将我们工作区中的代码提交到暂存区,然后使用git commit将暂存区中的代码提交到本地仓库

当我们提交到本地仓库之后,就产生了一个节点,比如:

image-20201217112743792


四种回滚的方式

接下来我们可以看到有四种回滚的方式,中文和英文可能不太一样,但是我会将两种都标记出来

image-20201217113938281

  • soft(软):仅仅将本地仓库回滚到上一个节点,而暂存区和工作区的代码保持不变,所以会出现未提交的提示出现
  • mixed(混合):回滚本地仓库和暂存区的代码,工作区的代码保持不变
  • hard(硬):回滚本地仓库、暂存区、工作区的代码
  • keep(保持):回滚本地仓库、工作区的代码,而暂存区的代码保持不变

当然了,我们一般使用的就是hard

节点与分支

基本概述

Git中存在着一个节点与分支的概念,我们每一次的提交记录都是一个提交的节点。

Git中的节点很有意思,它是一种单项链表的思想,每一个节点都会有一个指向父节点的指针,而且单独维护着一个HEAD指针,用于指向当前所使用的数据,所以它其实是这样的:

image-20201217115313974

那么我们在图中看到的这个链表,其实就是一个分支,我们默认的分支叫做master,这个master默认是主分支,当然了,也可以随便更改其他的分支为主分支

那么,我们现在来看一下多个分支的情况

image-20201217120204857

这样看起来杂乱无章,甚至你可以看到分支里面还有其他的分支,假如分支杂乱无章,就会出现一大堆的问题,这样显然是不可以的,所以我们需要一个统一的规章制度,这个等会再说。


分支合并

基本的分支合并

分支合并还是比较重要的,在多人协作的时候肯定需要涉及到代码的合并,对于git来讲就是分支的合并

下面我将新建一个项目,演示一下没有冲突的分支合并

我将模拟A,B两个用户(分别创建两个类进行提交)

image-20201217134839464

现在,我切换到master分支去合并B分支,因为两者间没有任何的冲突,所以可以直接合并

image-20201217135030562

再看master分支:已经有了刚才的B

image-20201217134935009

冲突解决

没有冲突的话还好说,但是假如出现了冲突,该如何解决?这个时候程序不能帮助我们解决,于是它会将冲突的代码交给我们,我们去解决这个冲突。

这个时候假如你使用命令行,那感觉就是生不如死,所以我们选择工具

为了测试方便,我现在重新开一个项目作为演示,注意,在演示冲突的时候必须要看准我们是从哪个节点开始新建分支的,比如下面两种情况就有所不同

image-20201217141658854

对于第一种来说,B节点是根据A节点来进行分的,所以A和B节点不会存在冲突的情况,哪怕B修改了A中某行的代码,也不会出现冲突节点

而对于第二种来说,B节点是根据master节点来进行分的,所以只要是出现了代码不一致的情况,肯定会出现冲突

我们演示的时候,注意要选择第二种方式来进行演示,也就是首先根据master创建A节点,然后回到master,根据master创建B节点


我们下面来演示一下

image-20201217142144908

如图,这是三次提交的代码,分别代表着三个分支,假如一切正常的话,逻辑图应该是这样的:

image-20201217142228246

也就是和我们之前画的图一样,B也是从master开始分出来的,而不是从A中分出来的

分支合并解决冲突也十分简单,首先按照正常的合并来走,然后遇到冲突它会让你手动解决,假如我们现在从B合并A分支的代码

image-20201217142739405

有三种处理方式:

1、丢弃他们的,完全按照我的方式去做

2、丢弃我的,完全按照他的方式去做

3、手动合并,进行判断,下面是手动合并的窗口

image-20201217142907758

合并之后的逻辑图应当是这样的:

image-20201217142947254


变基(衍合)

有冲突时的变基

在我们开发中,假如有二十条分支,每一个分支之间都有冲突,那么这个情况你想想,那滋味简直太酸爽了。

这还不算啥,但是假如中间有一条分支出现了严重的BUG,那么你该如何回滚?去一堆合并里面找吗?显然是不可能的。

所以这个时候有一项技术,叫做变基,这项技术可以使主分支一直保持线性而不是一直合并来合并去。

他的思想大概是这个样子:

根据当前HEAD所指向的节点向上递归,寻找一个当前分支和要进行变基的分支的共同父节点,然后进行将当前分支的所有节点移动到将要进行变基的节点的最后一个子节点,形成一个链表

image-20201217202043572

变基这种操作虽然保证了线性关系,但是有一些缺点:

1、经过变基之后,原来的节点关系已经全部消失不见,也就是说,一旦你变基之后,想回滚是不可能的了

2、虽然我们在逻辑上把这条链表看为了一个链表,但是在git中,第一个图的E和第二个图的E它仍然认为是两个点

变基也十分简单:假如我现在HEAD指向A分支,要去和B分支进行变基(衍合),那么这样做

image-20201217163225347

image-20201217150112655

如果一切操作无误,最后变基之后应当是这样的:

image-20201217163314667

到了这里,分支之间的关系已经被打乱了,你想要回滚到刚才那种分开的形式是不可能的


原理刚才其实是提到了一点

现在有分支A和分支B,HEAD指针指向分支A,分支B作为基底,然后分支A向分支B进行Rebase操作

这个时候,会去寻找分支A和分支B共同的父节点C。

分支B为基底,我们寻找分支B的最后一个节点,去和分支A的第一个节点(也就是和C的下一个节点)进行比较,假如有冲突则解决冲突,没有冲突则继续下一步

分支B不动,分支A会向分支B的最后进行回放(也就是节点向B进行重新提交)

也就是我们之前看到的:

image-20201217201957662

到底如何使用

这一点是非常重要的,虽然Git没有为我们指定规范,但是我们应该在开发中指定一种标准的规范,下面是业界通用的几种规范:

1、只有一条主分支:主分支不一定是master,但是我们一定要保证,只有一条主分支

2、永远保证主分支的可用性:主分支必须是时时刻刻可以跑起来的

3、每一个需求划分为一个业务分支:不要按照人头来划分分支,要按照业务来划分分支,当然,一个人负责一个业务是最好的

4、业务分支自行负责和主分支进行同步

5、业务分支一定要从主分支上开始开辟,而不是从其他业务分支上进行开辟

6、每一个业务分支都要对应着一条紧急修复分支,出现重大BUG时可以快速顶上去(HotFix)

7、在自己本地的时候,你可以随意创建提交点,但是当向主分支合并的时候,最好将自己的提交点合并为一个点(选择多个提交点->右键->Squash)


远程

本地推送到远程就不多BB了,有几个概念需要说一下

1、标签

在本地推送到远程,可以对提交点设置一个TAG,也就是版本号,等到提交之后,在我们的代码托管平台上就会出现在Relases里面

image-20201217151910650

image-20201217151922460

2、组织和团队

假如你不想把所有的项目都扔到你一个人的仓库里,你可以创建一个组织,然后在组织里面创建团队,在团队仓库里面提交项目

这样还有一个好处,就是可以很方便的对成员的权限进行管理

不过注意,一旦将项目移动到这里,项目的远程地址就会发生变化,应该重新指定

3、合并请求

当你是主分支的维护人员时,可以设置主分支为受保护分支,其他人不能随便对主分支进行更改,必须要发起一次合并请求(pull request)

image-20201217152413357

当管理员允许之后,可以设置如何将分支合并,比如合并或者变基

4、工单

在Github中,我们经常会看到一些bug的提交,这就是工单(issues),通过工单可以进行一些BUG的提交,也可以通过工单对小组成员进行任务的分派等等

所有人都可以提交一个工单,管理员可以针对不同的工单分配不同的标签,还可以指定时间,不同的责任人等等

image-20201217152901859