【摘】Git-从零单排 03期

前言

书接上回,02期里大概的介绍了 Git  部分操做的底层原理。这一期呢,笔者想谈谈 指针 。在 Git 里, 指针  这个概念过重要了。若是理解不到位,你可能根本不敢碰 reset 、 rebase 等指令,就算用了,也是只知其一;不知其二。相反,若是你对 指针 理解深入,遇到任何状况,你都会在第一时间想到多种解决办法,你将再也不被局限在 add、commit、pull、push这四个指令里。 git

指针

咱们先不聊指针,咱们先来看看 Git 里常见的 5 个事务,它们究竟是什么?
首先是 tag ,项目功能上线,咱们须要打 tag 。具体的命令以下:vim

git tag v1.0.0 # 针对当前的commit,打了一个名为v1.0.0的轻量标签
git tag -a v1.0.0 -m 'tag说明,项目1.0.0,功能有xxxx' # 这是打一个详细的附注标签
git tag -a v1.0.0 75f75b1 -m 'tag说明,项目1.0.0,功能有xxxx' # 在 75f75b1 提交对象上打了一个附注标签
git tag # 查看整个仓库全部 tag
git tag -d v1.0.0 # 删除 tag v1.0.0
git tag -l 'v3.*' # 只查看v3.开头的tag
git push origin --tags # 把标签推送到远端共享
git push origin :refs/tags/v1.0.0 # 删除远端标签
复制代码

咱们实际操做一下,咱们先进入到项目位置,这里就继续使用以前文章里操做过的项目目录。bash

vim hello-git.txt # 输入字符 tag 1
git commit -am 'chore: tag 1' # 生成一个 commit
[master 1fce6d3] chore: tag 1
 1 file changed, 1 insertion(+)
git tag tag-1 # 在 1fce6d3 上打一个轻量标签,标签名为 tag-1
git cat-file -t tag-1 # 查看 tag-1 的类型
commit # 显示为类型 commit ,这里咱们先记一下,tag的类型是 commit
git cat-file -p tag-1 # 查看 tag-1 的具体内容
tree 7ff2b6fd216355c918546fb3ce23e07dc9bbc6be
parent 75f75b1a233547abb2672049ca330881d5055bda
author kael <kael_yp@foxmail.com> 1591448989 +0800
committer kael <kael_yp@foxmail.com> 1591448989 +0800

chore: tag 1
复制代码

看到这里,有没有发现一个现象, tag 的行为跟咱们之前查看 commit 是否是超级像?咱们暂且,按下不表。
咱们接着看一下 branch 。平时,开发中,咱们接到一个新的需求,通常都会从新开一个新的分支,用于开发,等开发完成,再合并到主线分支。 branch 命令以下:post

git branch branchName # 建立分支
git branch # 查看分支 能够带 -a -vv 等参数。
git branch -d # 删除分支,-D 强制删除。
git branch -u # 设置上游分支
git branch -m # 修改分支名称
复制代码

来,实际操做一波。ui

git branch -a # 查看咱们项目的分支
* master # 只有一个master分支,由于咱们还没建立过度支,目前就只会有一个默认的master分支。这里有一个 * ,表明咱们当前处于master
git branch test # 建立一个test
git branch -m test newTest # 修改 test 分支为 newTest
git branch -a # 查看分支状况
* master 
  newTest # 和预料同样,如今有一个 newTest 分支
git cat-file -t newTest 
commit # 也是 commit
git cat-file -p newTest # 查看 newTest 这个分支的内容,跟 tag-1 彻底同样。看官,请记一下
tree 7ff2b6fd216355c918546fb3ce23e07dc9bbc6be
parent 75f75b1a233547abb2672049ca330881d5055bda
author kael <kael_yp@foxmail.com> 1591448989 +0800
committer kael <kael_yp@foxmail.com> 1591448989 +0800

chore: tag 1
git branch -d newTest # 删除 newTest 分支
Deleted branch newTest (was 1fce6d3). # 成功删除newTest分支,这里 1fce6d3 又出现了,跟上面的tag-1的同样
复制代码

这里咱们就大概演示一下branch的查看、建立、删除、改名操做。而且,咱们也发现,newTest 分支跟 tag-1 彻底同样,也在后续统一说明。切换分支后面讲。
接下来,咱们再看看 HEAD ,这个咱们通常称它为头指针。spa

git cat-file -t HEAD # 看看头指针是什么类型
commit # 也是 commit
git cat-file -p HEAD # 看看头指针的内容,跟以前查看的也是同样
tree 7ff2b6fd216355c918546fb3ce23e07dc9bbc6be
parent 75f75b1a233547abb2672049ca330881d5055bda
author kael <kael_yp@foxmail.com> 1591448989 +0800
committer kael <kael_yp@foxmail.com> 1591448989 +0800

chore: tag 1
复制代码

HEAD 这个是一个特殊的指针,笔者对它的理解就是像一个指挥官,控制着你的大部分对历史记录的操做。3d

git log --oneline # 查看一下历史记录
1fce6d3 (HEAD -> master, tag: tag-1, newTest) chore: tag 1 # 最重要的在这里
75f75b1 create lib/git.txt
9eb3d5f create hello-git.txt


复制代码

好了,到这里,咱们来分析一下。当咱们输入查看历史记录的时候,你会看到几个熟悉的字眼, 1fce6d3 (HEAD -> master, tag: tag-1, newTest) chore: tag 1  1fce6d3 是一个提交对象的 hashId  , HEAD -> master 的意思是头指针当前在 master 分支上,这也就是当前分支,查看分支时,分支前面的那个 * 号就是代码头指针在那,同时 master 分支指向 1fce6d3 这个提交对象。 tag: tag-1 的意思是,这个提交对象上有一个标签,名称是 tag-1 。 newTest 的意思是 newTest 分支也指向 1fce6d3 这个提交对象。 chore: tag 1 是 1fce6d3 这个提交对象的提交说明。

上面描述的是现象,笔者如今来针对这个现象,表达本身的理解。

首先是 HEAD ,这是一个无论你怎么操做,都指向当前提交对象的指针,你们都叫它 头指针 。这个指针的能带着 branch 一块儿移动,当你进行commit提交时。

而后是 branch ,你们都叫它分支,可是,我本身的理解里,我更愿意叫它 游标 ,由于 分支 这个词在记忆理解的时候,会让我联想到 树木的树枝,河流的支流,而后会把分支想成一个 有头有尾的连续的 线条状的事务。其实,这是大错特错的,若是你把分支理解成线条状的事务,你就会没法理解切分支的原理,(我之前就是把切分支理解成,把一根根线条状事务移来移去,把删除分支理解成把某一整条历史记录都删除)对于分支操做出现问题时,你可能会没法理解。其实,分支就是一个指向提交对象的指针,删除分支就只是把一个指针删除而已,删除分支并不会影响到任何一个提交对象,也不影响到任何一个历史记录(其它操做分支的行为也都是同样)。对应 HEAD 来理解,头指针是一个永远指向当前提交对象的指针,而 branch 是一个指向当前‘业务’所在的提交对象的指针。
解释一下这里说的‘业务’。好比咱们开发一个项目,同事A开发订单模块,她就切一个名为 order 的 分支 ,那么通常正常状况下,这个order分支,必定是指向订单模块当下所在的那个提交对象的。而后同事B呢,她开发门店模块,她就切一个名为 shop 的 分支 ,同理,这个shop的分支,必定是指向门店模块当下所在的提交对象。
咱们使用 git log order 来看订单分支的历史记录,而后出来的就是 order 所指向的 提交对象的 parent对象造成的链表,这是一个以当前提交对象为‘起始结点’,第一个提交对象为‘终端结点’的链表。再次强调,这个链表不是分支,分支只是一个指向这个链表‘起始结点’的指针,因此你删除分支,用reset移动分支时,并不会影响这个已经造成的链表记录。
因此呢, 分支 就是一个提交记录的别名,由于 order 这种跟业务相关,语义化强的字符,确定比40位的无规律的 hashId好记,对应着 ip 与 域名记忆哈(你就说你记得 www.baidu.com, 仍是记得 180.101.49.11)。还有要强调的一点, 分支 指向的提交对象是能够变化的。因此我更愿意叫它 游标。

接下来,就该是 tag 了,能够叫它 标记。功能上来讲,它就是一个指向特定提交对象的指针,或者说,它也是一个特定提交对象的别名。为何有了分支的别名,还要再搞一个 tag 的别名呢?这是由于,分支是能够移动的,今天分支指向的是 A 提交对象,明天可能就指向 B 了。tag 呢,是一个永远都不能移动的别名,无论何时使用它,它指向的提交对象绝对是同一个。因此,在项目上线了新功能时,会打个 tag 。若是后面须要让这个新功能下架,咱们只要操做这个 tag 就行。tag 相对于分支来讲,它更特殊一点,若是 tag 与 分支重名(假设是 abc)了,除了在 git branch 命令后,其它地方,使用 abc,拿到的必定是 tag 指针。因此遇到 tag 和 分支重名,必定要记得用 git branch -m abc abcBranch 命令把分支的名称修改。

就如前面让看官记下的,tag-1 、 newTest 、HEAD的内容都是同样的,为何呢?由于它们在那个时候都指向同一个提交对象 1fce6d3 。在 Git 里,指针还有不少,好比,stash@{0}, origin/master, HEAD^ , HEAD~1 等。好了,咱们如今都理解了指针,那看看咱们在工做中,能够怎么操做吧
指针

示例

新建分支。操做以下:code

git branch test 
# 这是新建一个叫test的分支,这是怎么新建的呢?其实它原来是这样的。
git branch test HEAD 
# 就是在头指针指向的提交对象上创建一个test别名,强调:在 Git 里大部分放 HEAD 的地方,均可以把 HEAD 省略。
# 好比 
git show # 对应就是 git show HEAD
git log # 对应就是 git log HEAD
# 继续哈,既然 HEAD 是指针,那这个放 HEAD 的地方就能够放其它任何指针,就算放 hashId 也是能够的。
git branch dev 1fce6d3 # 这就是在 1fce6d3 这个提交对象上创建一个 dev 分支。
复制代码

新建 tag。操做以下:对象

git tag v1.0.0
# 这里新建一个轻量标签 v1.0.0,同理可得,它原来是这样的。
git tag v1.0.0 HEAD
# 在头指针的位置打一个标签。如今你理解指针以后,你就能够给任何一个你打标签的提交对象打tag,只要你能找到它。
# 好比,我如今根本不知道其它同事在写的order分支到哪一步了,你也能够
git tag testTag order # 本系列01期说了,这些操做都是本地,因此你的本地要有拉取到同事的 order 分支。
# 这样就给 order 分支所指向的提交对象打了一个名叫 testTag 的标签。而后,你就会被同事打了!
复制代码

查看之前版本代码。操做以下:

git reset --hard v1.0.0 
# 假如,咱们已经提交了不少次了,项目已经到了v3.0.0了。而后,领导说,我很怀念咱们加班加点作的1.0版本啊
# 那你就能够用上面的命令,让代码回到1.0的样子。老板看完了,你再使用
git reset --hard feat 
# 假设你以前是在feat分支开发。
复制代码

后语

最后,再来讲说, git checkout 这个命令叫签出,它的做用是移动 HEAD 头指针。好比,你使用 git checkout branchName 那就是把头指针挂在 branchName 这个分支上,下一次提交时,头指针往前移,同时会带着 branchName 分支一块儿移动。若是你使用 git reset 命令移动头指针,头指针也会带着 branchName 一块儿走。你就想象分支身上是有一个洞的,头指针有一个钩。头指针钩住了分支,就会带着一块儿走。 git checkout 呢,就是把头指针的钩从分支身上取下,并移动另外一个地方,新地方有洞就钩住,没洞的话,Git 会提示你,如今头指针是分离状态。 git checkout -b name master 这个命令是,先从 master 分支所指向的提交对象上,新建一个 name 分支,而后把头指针从当下的位置拿下来,挂到 name 这个分支上,也就新建加切换分支。基于以上观点,笔者认为 tag 呢,它是被焊死在提交对象上的,永远在那。
还有一个,那就是,在操做 Git 的时候,不要担忧,无论你怎么玩,只要你不搞 .git 里的东西,你就不能够把项目搞坏,你想要的东西,经过 git reflog 命令就能所有找到。因此,若是你理解了指针,那就大胆的操做吧,玩不坏的啦!

世界上任何值得去的地方,都没有捷径!

相关文章
相关标签/搜索