↓所推荐高度关注↓
0序言做为现期当今世界上最强悍的标识符管理软件Git坚信我们都很熟识,但有非常大一大批人逗留在clone、commit、pull、push…的期,呢对rebase心中很着急布季用merge?
遇见版班莱班县就THF1?别问我是不是晓得的,问是:“我曾是这种啊~~”。
特别针对那些难题,那时我就将这一两年对Git的知觉和认知撷取出,尽量的从其本质去传授Git,协助你一步棋一步棋去介绍Git的下层基本原理,坚信念完第一集该文你便能林美珠面目,更为潇洒得采用Git各式各样指示。
产品目录
1. 基本基本原理
1.1 Git的竞争优势
1.2 文档状况
1.3 commit 结点
1.4 HEAD
1.5 远距库房
2. 组成部分
2.1 甚么是组成部分?
3. 指示简述
3.1 提交相关
3.2 组成部分相关
3.3 合并相关
3.4 班莱班县相关
3.5 远距相关
1基本基本原理1.1 Git的竞争优势
Git是一个分布式标识符管理软件,在讨论分布式之前避免不了提及一下甚么是中央式标识符管理库房
中央式:所有的标识符保存在中央服务器,所以提交必须依赖网络,并且每次提交都会带入到中央库房,如果是协同开发可能频繁触发标识符合并,进而增加提交的成本和代价。最典型的是svn
分布式:能在本地提交,不需要依赖网络,并且会将每次提交自动备份到本地。每个开发者都能把远距库房clone一份到本地,并会把提交历史一并拿过来。代表是Git
那Git相比于svn有甚么竞争优势呢?
打个比方:”巴拉巴拉写了一大堆标识符,突然发现写的有难题,我想回到一个小时之前”,对于这种情况Git的竞争优势就很明显了,因为commit的成本比较小并且本地会保存所有的提交记录,随时随刻能进行班莱班县。
在这并不是说svn的不能完成这种操作,只是Git的班莱班县会显得更为的优雅。Git相比于中央式工具还有很多优点,就不一一列举了,感兴趣的可自行介绍。
1.2 文档状况
在Git中文档大概分为三种状况:已修改(modified)、已暂存(staged)、已提交(committed)
修改:Git能感知到工作产品目录中哪些文档被修改了,然后把修改的文档加入到modified区域
暂存:通过add指示将工作产品目录中修改的文档提交到暂存区,等候被commit
提交:将暂存区文档commit至Git产品目录中永久保存
1.3 commit结点
为了便于表述,第一集该文我会通过结点代称commit提交
在Git中每次提交都会生成一个结点,而每个结点都会有一个哈希值做为唯一标示,多次提交会形成一个线性结点链(不考虑merge的情况),如图1-1
结点上方是通过 SHA1计算的哈希值
C2结点包含C1提交内容,同样C3结点包含C1、C2提交内容
1.4 HEAD
HEAD是Git中非常重要的一个概念,你能称它为指针或者引用,它能指向任意一个结点,并且指向的结点始终为现期工作产品目录,换句话说是现期工作产品目录(也是你所看到的标识符)是HEAD指向的结点。
还以图1-1举例,如果HEAD指向C2那工作产品目录对应的是C2结点。具体如何移动HEAD指向后面会讲到,此处不要纠结。
同时HEAD也能指向一个组成部分,间接指向组成部分所指向的结点。
1.5 远距库房
虽然Git会把标识符以及历史保存在本地,但最终还是要提交到服务器上的远距库房。通过clone指示能把远距库房的标识符下载到本地,同时也会将提交历史、组成部分、HEAD等状况一并同步到本地,但那些状况并不会实时更新,需要手动从远距库房去拉取,至于何时拉、是不是拉后面章节会讲到。
通过远距库房为中介,你能和你的同事进行协同开发,开发完新功能后能申请提交至远距库房,同时也能从远距库房拉取你同事的标识符。
注意点因为你和你的同事都会以远距库房的标识符为基准,所以要时刻保证远距库房的标识符质量,切记不要将未经检验测试的标识符提交至远距库房
2组成部分2.1 甚么是组成部分?
组成部分也是Git中相当重要的一个概念,当一个组成部分指向一个结点时,现期结点的内容即是该组成部分的内容,它的概念和HEAD非常接近同样也能视为指针或引用,不同的是组成部分能存在多个,而HEAD只有一个。通常会根据功能或版建立不同的组成部分。
那组成部分有甚么用呢?
举个例子:你们的 App 经历了千辛万苦终于发布了v1.0版,由于需求紧急v1.0上线之后便马不停蹄的开始v1.1,正当你开发的兴起时,QA同学说用户反馈了一些bug,需要修复然后重新发版,修复v1.0肯定要基于v1.0的标识符,可是你已经开发了一部分v1.1了,此时是不是搞?
面对上面的难题通过引入组成部分概念便可优雅的解决,如图2-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还是组成部分它们都只是引用而已,量级非常轻
3指示简述3.1 提交相关
前面我们提到过,想要对标识符进行提交必须得先加入到暂存区,Git中是通过指示 add 实现
添加某个文档到暂存区:git add文档路径
添加所有文档到暂存区:git add.
同时Git也提供了撤销工作区和暂存区指示
撤销工作区改动:gitcheckout — 文档名
清空暂存区:git reset HEAD文档名
提交:将改动文档加入到暂存区后就能进行提交了,提交后会生成一个新的提交结点,具体指示如下:
git commit -m “该结点的描述信息”3.2 组成部分相关
创建组成部分创建一个组成部分后该组成部分会与HEAD指向同一结点,说通俗点是HEAD指向哪创建的新组成部分就指向哪,指示如下:
gitbranch 组成部分名
切换组成部分当切换组成部分后,默认情况下HEAD会指向现期组成部分,即HEAD间接指向现期组成部分指向的结点
gitcheckout 分支名
同时也能创建一个组成部分后立即切换,指示如下:
gitcheckout -b 组成部分名
删除组成部分为了保证库房组成部分的简洁,当某个组成部分完成了它的使命后应该被删除。比如前面所说的单独开一个组成部分完成某个功能,当这个功能被合并到主组成部分后应该将这个组成部分及时删除。
删除指示如下:
gitbranch -d 组成部分名
3.3 合并相关
关于合并的指示是最难掌握同时也是最重要的。我们常用的合并指示大概有三个merge、rebase、cherry-pick
mergemerge是最常用的合并指示,它能将某个组成部分或者某个结点的标识符合并至现期组成部分。具体指示如下:
gitmerge 组成部分名/结点哈希值
如果需要合并的组成部分完全领先于现期分支,如图3-1所示
由于组成部分ft-1完全领先组成部分ft-2即ft-1完全包含ft-2,所以ft-2执行了“git merge ft-1”后会触发fast forward(快速合并),此时两个组成部分指向同一节点,这是最理想的状况。
但是实际开发中我们往往碰到是是下面这种情况:如图3-2(左)
这种情况就不能直接合了,当ft-2执行了“git merge ft-1”后Git会将结点C3、C4合并随后生成一个新节点C5,最后将ft-2指向C5 如图3-2(右)
注意点:
如果C3、C4同时修改了同一个文档中的同一句标识符,这个时候合并会出错,因为Git不晓得该以哪个结点为标准,所以这个时候需要我们自己手动合并标识符
rebaserebase也是一种合并指示,指示行如下:
gitrebase 组成部分名/结点哈希值
与merge不同的是rebase合并看起来不会产生新的结点(实际上是会产生的,只是做了一次复制),而是将需要合并的结点直接累加 如图3-3
当左边示意图的ft-1.0执行了git rebase master后会将C4结点复制一份到C3后面,也是C4,C4与C4相对应,但是哈希值却不一样。
rebase相比于merge提交历史更为线性、干净,使并行的开发流程看起来像串行,更符合我们的直觉。既然rebase这么好用呢能抛弃merge了?其实也不是了,下面我罗列一些merge和rebase的优缺点:
merge优缺点:
优点:每个结点都是严格按照时间排列。当合并发生冲突时,只需要解决两个组成部分所指向的结点的冲突即可
缺点:合并两个组成部分时大概率会生成新的结点并分叉,久而久之提交历史会变成一团乱麻
rebase优缺点:
优点:会使提交历史看起来更为线性、干净
缺点:虽然提交看起来像是线性的,但并不是真正的按时间排序,比如图3-3中,不管C4早于或者晚于C3提交它最终都会放在C3后面。并且当合并发生冲突时,理论上来讲有几个结点rebase到目标组成部分就可能处理几次冲突
对于网络上一些只用rebase的观点,作者表示不太认同,如果不同组成部分的合并采用rebase可能需要重复解决冲突,这种就得不偿失了。但如果是本地推到远距并对应的是同一条组成部分能优先考虑rebase。所以我的观点是 根据不同场景合理搭配采用merge和rebase,如果觉得都行那优先采用rebase
cherry-pickcherry-pick的合并不同于merge和rebase,它能选择某几个结点进行合并,如图3-4
指示行:
gitcherry-pick 结点哈希值
假设现期组成部分是master,执行了git cherry-pick C3(哈希值),C4(哈希值)指示后会直接将C3、C4结点抓过来放在后面,对应C3和C4
3.4 班莱班县相关
分离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一并班莱班县。
3.5 远距相关
当我们接触一个新项目时,第一件事情肯定是要把它的标识符拿下来,在Git中能通过clone从远距库房复制一份标识符到本地,具体指示如下:
git clone库房地址
前面的章节我也有提到过,clone不仅仅是复制标识符,它还会把远距库房的引用(组成部分/HEAD)一并取下保存在本地,如图3-5所示:
其中origin/master和origin/ft-1为远距库房的组成部分,而远距的那些引用状况是不会实时更新到本地的,比如远距库房origin/master组成部分增加了一次提交,此时本地是感知不到的,所以本地的origin/master组成部分依旧指向C4结点。我们能通过fetch指示来手动更新远距库房状况
小提示:
并不是存在服务器上的才能称作是远距库房,你也能clone本地库房做为远距,当然实际开发中我们不可能把本地库房当作公有库房,说这个只是单纯的协助你更清晰的认知分布式
fetch说的通俗一点,fetch指示是一次下载操作,它会将远距新增加的结点以及引用(组成部分/HEAD)的状况下载到本地,具体指示如下:
gitfetch 远距库房地址/组成部分名
pullpull指示能从远距库房的某个引用拉取标识符,具体指示如下:
gitpull 远距组成部分名
其实pull的其本质是fetch+merge,首先更新远距库房所有状况到本地,随后再进行合并。合并完成后本地组成部分会指向最新结点
另外pull指示也能通过rebase进行合并,具体指示如下:
gitpull –rebase 远距组成部分名
pushpush指示能将本地提交推送至远距,具体指示如下:
gitpush 远距组成部分名
如果直接push可能会失败,因为可能存在冲突,所以在push之前往往会先pull一下,如果存在冲突本地解决。push成功后本地的远距组成部分引用会更新,与本地组成部分指向同一结点。
综上所述
不管是HEAD还是组成部分,它们都只是引用而已,引用+结点是 Git 构成分布式的关键
merge相比于rebase有更明确的时间历史,而rebase会使提交更为线性应当优先采用
通过移动HEAD能查看每个提交对应的标识符
clone或fetch都会将远距库房的所有提交、引用保存在本地一份
pull的其本质其实是fetch+merge,也能加入–rebase通过rebase方式合并
– EOF –
所推荐阅读 点击标题可跳转看完本文有收获?请撷取给更多人
所推荐高度关注「Linux 爱好者」,提升Linux技能
点赞和在看是最大的支持❤️