Git 各指令的本质,真的是通俗易懂!

作为当前世界上最强大的代码管理工具 Git 相信大家都很熟悉,但据我所知有很大一批人停留在 clone、commit、pull、push...的阶段,是不是对 rebase 心里没底只敢用 merge?

碰见版本回退就抓瞎?别问我怎么知道的,问就是:“我曾经就是这样啊~~”。

针对这些问题,今天我就将这几年对 Git 的认知和理解分享出来,尽可能的从本质去讲解 Git,帮助你一步一步去了解 Git 的底层原理,相信读完本篇文章你便可以换种姿态,更加风骚得使用 Git 各种指令。

基本概念

Git 的优势

Git 是一个分布式代码管理工具,在讨论分布式之前避免不了提及一下什么是中央式代码管理仓库:

  • 中央式:所有的代码保存在中央服务器,所以提交必须依赖网络,并且每次提交都会带入到中央仓库,如果是协同开发可能频繁触发代码合并,进而增加提交的成本和代价。最典型的就是 svn。

  • 分布式:可以在本地提交,不需要依赖网络,并且会将每次提交自动备份到本地。每个开发者都可以把远程仓库 clone 一份到本地,并会把提交历史一并拿过来。代表就是 Git。

那 Git 相比于 svn 有什么优势呢?

打个比方:"巴拉巴拉写了一大堆代码,突然发现写的有问题,我想回到一个小时之前",对于这种情况 Git 的优势就很明显了,因为 commit 的成本比较小并且本地会保存所有的提交记录,随时随刻可以进行回退。

在这并不是说 svn 的不能完成这种操作,只是 Git 的回退会显得更加的优雅。Git 相比于中央式工具还有很多优点,就不一一列举了,感兴趣的可自行了解。

文件状态

在 Git 中文件大概分为三种状态:已修改(modified)、已暂存(staged)、已提交(committed)

  • 修改:Git 可以感知到工作目录中哪些文件被修改了,然后把修改的文件加入到 modified 区域

  • 暂存:通过 add 命令将工作目录中修改的文件提交到暂存区,等候被 commit

  • 提交:将暂存区文件 commit 至 Git 目录中永久保存

commit 节点

为了便于表述,本篇文章我会通过节点代称 commit 提交

在 Git 中每次提交都会生成一个节点,而每个节点都会有一个哈希值作为唯一标示,多次提交会形成一个线性节点链(不考虑 merge 的情况),如图 :

图片

节点上方是通过 SHA1 计算的哈希值

C2 节点包含 C1 提交内容,同样 C3 节点包含 C1、C2 提交内容

HEAD

HEAD 是 Git 中非常重要的一个概念,你可以称它为指针或者引用,它可以指向任意一个节点,并且指向的节点始终为当前工作目录,换句话说就是当前工作目录(也就是你所看到的代码)就是 HEAD 指向的节点。

还以上图举例,如果 HEAD 指向 C2 那工作目录对应的就是 C2 节点。具体如何移动 HEAD 指向后面会讲到,此处不要纠结。

同时 HEAD 也可以指向一个分支,间接指向分支所指向的节点。

远程仓库

虽然 Git 会把代码以及历史保存在本地,但最终还是要提交到服务器上的远程仓库。通过 clone 命令可以把远程仓库的代码下载到本地,同时也会将提交历史、分支、HEAD 等状态一并同步到本地,但这些状态并不会实时更新,需要手动从远程仓库去拉取,至于何时拉、怎么拉后面章节会讲到。

通过远程仓库为中介,你可以和你的同事进行协同开发,开发完新功能后可以申请提交至远程仓库,同时也可以从远程仓库拉取你同事的代码。

「注意点」

因为你和你的同事都会以远程仓库的代码为基准,所以要时刻保证远程仓库的代码质量,切记不要将未经检验测试的代码提交至远程仓库

分支

什么是分支?

分支也是 Git 中相当重要的一个概念,当一个分支指向一个节点时,当前节点的内容即是该分支的内容,它的概念和 HEAD 非常接近同样也可以视为指针或引用,不同的是分支可以存在多个,而 HEAD 只有一个。通常会根据功能或版本建立不同的分支。

那分支有什么用呢?

举个例子:你们的 App 经历了千辛万苦终于发布了 v1.0 版本,由于需求紧急 v1.0 上线之后便马不停蹄的开始 v1.1,正当你开发的兴起时,QA 同学说用户反馈了一些 bug,需要修复然后重新发版,修复 v1.0 肯定要基于 v1.0 的代码,可是你已经开发了一部分 v1.1 了,此时怎么搞?

面对上面的问题通过引入分支概念便可优雅的解决,如下图所示:

图片

先看左边示意图,假设 C2 节点既是 v1.0 版本代码,上线后在 C2 的基础上新建一个分支 ft-1.0

再看右边示意图,在 v1.0 上线后可在 master 分支开发 v1.1 内容,收到 QA 同学反馈后提交 v1.1 代码生成节点 C3,随后切换到 ft-1.0 分支做 bug 修复,修复完成后提交代码生成节点 C4,然后再切换到 master 分支并合并 ft-1.0 分支,到此我们就解决了上面提出的问题

除此之外利用分支还可以做很多事情,比如现在有一个需求不确定要不要上线,但是得先做,此时可以单独创建一个分支开发该功能,等到啥时候需要上线直接合并到主分支即可。分支适用的场景很多就不一一列举了。

「注意点:」

当在某个节点创建一个分支后,并不会把该节点对应的代码复制一份出来,只是将新分支指向该节点,因此可以很大程度减少空间上的开销。一定要记着不管是 HEAD 还是分支它们都只是引用而已,量级非常轻

命令详解

提交相关

前面我们提到过,想要对代码进行提交必须得先加入到暂存区,Git 中是通过命令 add 实现。

添加某个文件到暂存区:

git add 文件路径

添加所有文件到暂存区:

git add .

同时 Git 也提供了撤销工作区和暂存区命令

撤销工作区改动:

git checkout -- 文件名

清空暂存区:

git reset HEAD 文件名

提交:

将改动文件加入到暂存区后就可以进行提交了,提交后会生成一个新的提交节点,具体命令如下:

git commit -m "该节点的描述信息"

分支相关

创建分支

创建一个分支后该分支会与 HEAD 指向同一节点,说通俗点就是 HEAD 指向哪创建的新分支就指向哪,命令如下:

git branch 分支名

切换分支

当切换分支后,默认情况下 HEAD 会指向当前分支,即 HEAD 间接指向当前分支指向的节点

git checkout 分支名

同时也可以创建一个分支后立即切换,命令如下:

git checkout -b 分支名

删除分支

为了保证仓库分支的简洁,当某个分支完成了它的使命后应该被删除。比如前面所说的单独开一个分支完成某个功能,当这个功能被合并到主分支后应该将这个分支及时删除。

删除命令如下:

git branch -d 分支名

合并相关

关于合并的命令是最难掌握同时也是最重要的。我们常用的合并命令大概有三个 merge、rebase、cherry-pick

merge

merge 是最常用的合并命令,它可以将某个分支或者某个节点的代码合并至当前分支。具体命令如下:

git merge 分支名/节点哈希值

如果需要合并的分支完全领先于当前分支,如下图所示

图片

由于分支 ft-1 完全领先分支 ft-2 即 ft-1 完全包含 ft-2,所以 ft-2 执行了“git merge ft-1”后会触发 fast forward(快速合并),此时两个分支指向同一节点,这是最理想的状态。

但是实际开发中我们往往碰到是是下面这种情况:如下图(左)

图片

这种情况就不能直接合了,当 ft-2 执行了“git merge ft-1”后 Git 会将节点 C3、C4 合并随后生成一个新节点 C5,最后将 ft-2 指向 C5,如上图(右)。

「注意点:」

如果 C3、C4 同时修改了同一个文件中的同一句代码,这个时候合并会出错,因为 Git 不知道该以哪个节点为标准,所以这个时候需要我们自己手动合并代码

rebase

rebase 也是一种合并指令,命令行如下:

git rebase 分支名/节点哈希值

与 merge 不同的是 rebase 合并看起来不会产生新的节点(实际上是会产生的,只是做了一次复制),而是将需要合并的节点直接累加,如下图

图片

当左边示意图的 ft-1.0 执行了 git rebase master 后会将 C4 节点复制一份到 C3 后面,也就是 C4',C4 与 C4'相对应,但是哈希值却不一样。

rebase 相比于 merge 提交历史更加线性、干净,使并行的开发流程看起来像串行,更符合我们的直觉。既然 rebase 这么好用是不是可以抛弃 merge 了?其实也不是了,下面我罗列一些 merge 和 rebase 的优缺点:

merge 优缺点

  • 优点:每个节点都是严格按照时间排列。当合并发生冲突时,只需要解决两个分支所指向的节点的冲突即可

  • 缺点:合并两个分支时大概率会生成新的节点并分叉,久而久之提交历史会变成一团乱麻

rebase 优缺点

  • 优点:会使提交历史看起来更加线性、干净

  • 缺点:虽然提交看起来像是线性的,但并不是真正的按时间排序。以我们前面发的图为例,不管 C4 早于或者晚于 C3 提交它最终都会放在 C3 后面。并且当合并发生冲突时,理论上来讲有几个节点 rebase 到目标分支就可能处理几次冲突

对于网络上一些只用 rebase 的观点,作者表示不太认同,如果不同分支的合并使用 rebase 可能需要重复解决冲突,这样就得不偿失了。但如果是本地推到远程并对应的是同一条分支可以优先考虑 rebase。所以我的观点是 根据不同场景合理搭配使用 merge 和 rebase,如果觉得都行那优先使用 rebase

cherry-pick

cherry-pick 的合并不同于 merge 和 rebase,它可以选择某几个节点进行合并。

命令行:

git cherry-pick 节点哈希值

图片

假设当前分支是 master,执行了 git cherry-pick C3(哈希值),C4(哈希值)命令后会直接将 C3、C4 节点抓过来放在后面,对应 C3'和 C4'

回退相关

分离 HEAD

在默认情况下 HEAD 是指向分支的,但也可以将 HEAD 从分支上取下来直接指向某个节点,此过程就是分离 HEAD,具体命令如下:

git checkout 节点哈希值//也可以直接脱离分支指向当前节点git checkout --detach

由于哈希值是一串很长很长的乱码,在实际操作中使用哈希值分离 HEAD 很麻烦,所以 Git 也提供了 HEAD 基于某一特殊位置(分支/HEAD)直接指向前一个或前 N 个节点的命令,也即相对引用,如下:

//HEAD分离并指向前一个节点
git checkout 分支名/HEAD^
//HEAD分离并指向前N个节点
git checkout 分支名~N

将 HEAD 分离出来指向节点有什么用呢?举个例子:如果开发过程发现之前的提交有问题,此时可以将 HEAD 指向对应的节点,修改完毕后再提交,此时你肯定不希望再生成一个新的节点,而你只需在提交时加上--amend 即可,具体命令如下:

git commit --amend

回退

回退场景在平时开发中还是比较常见的,比如你巴拉巴拉写了一大堆代码然后提交,后面发现写的有问题,于是你想将代码回到前一个提交,这种场景可以通过 reset 解决,具体命令如下:

//回退N个提交
git reset HEAD~N

reset 和相对引用很像,区别是 reset 会使分支和 HEAD 一并回退。

远程相关

当我们接触一个新项目时,第一件事情肯定是要把它的代码拿下来,在 Git 中可以通过 clone 从远程仓库复制一份代码到本地,具体命令如下:

git clone 仓库地址

前面的章节我也有提到过,clone 不仅仅是复制代码,它还会把远程仓库的引用(分支/HEAD)一并取下保存在本地,如下图所示:

图片

其中 origin/master 和 origin/ft-1 为远程仓库的分支,而远程的这些引用状态是不会实时更新到本地的,比如远程仓库 origin/master 分支增加了一次提交,此时本地是感知不到的,所以本地的 origin/master 分支依旧指向 C4 节点。我们可以通过 fetch 命令来手动更新远程仓库状态

小提示

并不是存在服务器上的才能称作是远程仓库,你也可以 clone 本地仓库作为远程,当然实际开发中我们不可能把本地仓库当作公有仓库,说这个只是单纯的帮助你更清晰的理解分布式

fetch

说的通俗一点,fetch 命令就是一次下载操作,它会将远程新增加的节点以及引用(分支/HEAD)的状态下载到本地,具体命令如下:

git fetch 远程仓库地址/分支名

pull

pull 命令可以从远程仓库的某个引用拉取代码,具体命令如下:

git pull 远程分支名

其实 pull 的本质就是 fetch+merge,首先更新远程仓库所有状态到本地,随后再进行合并。合并完成后本地分支会指向最新节点

另外 pull 命令也可以通过 rebase 进行合并,具体命令如下:

git pull --rebase 远程分支名

push

push 命令可以将本地提交推送至远程,具体命令如下:

git push 远程分支名

如果直接 push 可能会失败,因为可能存在冲突,所以在 push 之前往往会先 pull 一下,如果存在冲突本地解决。push 成功后本地的远程分支引用会更新,与本地分支指向同一节点。

综上所述

  • 不管是 HEAD 还是分支,它们都只是引用而已,引用+节点是 Git 构成分布式的关键

  • merge 相比于 rebase 有更明确的时间历史,而 rebase 会使提交更加线性应当优先使用

  • 通过移动 HEAD 可以查看每个提交对应的代码

  • clone 或 fetch 都会将远程仓库的所有提交、引用保存在本地一份

  • pull 的本质其实就是 fetch+merge,也可以加入--rebase 通过 rebase 方式合并

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
4S店客户管理小程序-毕业设计,基于微信小程序+SSM+MySql开发,源码+数据库+论文答辩+毕业论文+视频演示 社会的发展和科学技术的进步,互联网技术越来越受欢迎。手机也逐渐受到广大人民群众的喜爱,也逐渐进入了每个用户的使用。手机具有便利性,速度快,效率高,成本低等优点。 因此,构建符合自己要求的操作系统是非常有意义的。 本文从管理员、用户的功能要求出发,4S店客户管理系统中的功能模块主要是实现管理员服务端;首页、个人中心、用户管理、门店管理、车展管理、汽车品牌管理、新闻头条管理、预约试驾管理、我的收藏管理、系统管理,用户客户端:首页、车展、新闻头条、我的。门店客户端:首页、车展、新闻头条、我的经过认真细致的研究,精心准备和规划,最后测试成功,系统可以正常使用。分析功能调整与4S店客户管理系统实现的实际需求相结合,讨论了微信开发者技术与后台结合java语言和MySQL数据库开发4S店客户管理系统的使用。 关键字:4S店客户管理系统小程序 微信开发者 Java技术 MySQL数据库 软件的功能: 1、开发实现4S店客户管理系统的整个系统程序; 2、管理员服务端;首页、个人中心、用户管理、门店管理、车展管理、汽车品牌管理、新闻头条管理、预约试驾管理、我的收藏管理、系统管理等。 3、用户客户端:首页、车展、新闻头条、我的 4、门店客户端:首页、车展、新闻头条、我的等相应操作; 5、基础数据管理:实现系统基本信息的添加、修改及删除等操作,并且根据需求进行交流信息的查看及回复相应操作。
现代经济快节奏发展以及不断完善升级的信息化技术,让传统数据信息的管理升级为软件存储,归纳,集中处理数据信息的管理方式。本微信小程序医院挂号预约系统就是在这样的大环境下诞生,其可以帮助管理者在短时间内处理完毕庞大的数据信息,使用这种软件工具可以帮助管理人员提高事务处理效率,达到事半功倍的效果。此微信小程序医院挂号预约系统利用当下成熟完善的SSM框架,使用跨平台的可开发大型商业网站的Java语言,以及最受欢迎的RDBMS应用软件之一的MySQL数据库进行程序开发。微信小程序医院挂号预约系统有管理员,用户两个角色。管理员功能有个人中心,用户管理,医生信息管理,医院信息管理,科室信息管理,预约信息管理,预约取消管理,留言板,系统管理。微信小程序用户可以注册登录,查看医院信息,查看医生信息,查看公告资讯,在科室信息里面进行预约,也可以取消预约。微信小程序医院挂号预约系统的开发根据操作人员需要设计的界面简洁美观,在功能模块布局上跟同类型网站保持一致,程序在实现基本要求功能时,也为数据信息面临的安全问题提供了一些实用的解决方案。可以说该程序在帮助管理者高效率地处理工作事务的同时,也实现了数据信息的整体化,规范化与自动化。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值