写点什么

【摘】Git- 从零单排 03 期

用户头像
卡尔
关注
发布于: 2020 年 06 月 07 日
【摘】Git-从零单排 03期

前言

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

指针

我们先不聊指针,我们先来看看 Git 里常见的 5 个事务,它们到底是什么?

首先是 tag ,项目功能上线,我们需要打 tag 。具体的命令如下:

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 # 查看整个仓库所有 taggit tag -d v1.0.0 # 删除 tag v1.0.0git tag -l 'v3.*' # 只查看v3.开头的taggit push origin --tags # 把标签推送到远端共享git push origin :refs/tags/v1.0.0 # 删除远端标签
复制代码

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

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

看到这里,有没有发现一个现象, tag 的行为跟我们以前查看 commit 是不是超级像?我们暂且,按下不表。

我们接着看一下 branch 。平时,开发中,我们接到一个新的需求,一般都会重新开一个新的分支,用于开发,等开发完成,再合并到主线分支。 branch 命令如下:

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

来,实际操作一波。

git branch -a # 查看我们项目的分支* master # 只有一个master分支,因为我们还没创建过分支,目前就只会有一个默认的master分支。这里有一个 * ,代表我们当前处于mastergit branch test # 创建一个testgit branch -m test newTest # 修改 test 分支为 newTestgit branch -a # 查看分支情况* master   newTest # 和预料一样,现在有一个 newTest 分支git cat-file -t newTest commit # 也是 commitgit cat-file -p newTest # 查看 newTest 这个分支的内容,跟 tag-1 完全一样。看官,请记一下tree 7ff2b6fd216355c918546fb3ce23e07dc9bbc6beparent 75f75b1a233547abb2672049ca330881d5055bdaauthor kael <kael_yp@foxmail.com> 1591448989 +0800committer kael <kael_yp@foxmail.com> 1591448989 +0800
chore: tag 1git branch -d newTest # 删除 newTest 分支Deleted branch newTest (was 1fce6d3). # 成功删除newTest分支,这里 1fce6d3 又出现了,跟上面的tag-1的一样
复制代码

这里我们就大概演示一下 branch 的查看、创建、删除、更名操作。并且,我们也发现,newTest 分支跟 tag-1 完全一样,也在后续统一说明。切换分支后面讲。

接下来,我们再看看 HEAD ,这个我们一般称它为头指针。

git cat-file -t HEAD # 看看头指针是什么类型commit # 也是 commitgit cat-file -p HEAD # 看看头指针的内容,跟之前查看的也是一样tree 7ff2b6fd216355c918546fb3ce23e07dc9bbc6beparent 75f75b1a233547abb2672049ca330881d5055bdaauthor kael <kael_yp@foxmail.com> 1591448989 +0800committer kael <kael_yp@foxmail.com> 1591448989 +0800
chore: tag 1
复制代码

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

git log --oneline # 查看一下历史记录1fce6d3 (HEAD -> master, tag: tag-1, newTest) chore: tag 1 # 最重要的在这里75f75b1 create lib/git.txt9eb3d5f 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 的分支,一定是指向门店模块当下所在的提交对象。<br />我们使用 git log order 来看订单分支的历史记录,然后出来的就是 order 所指向的 提交对象 的 parent 对象形成的链表,这是一个以当前提交对象为‘起始结点’,第一个提交对象为‘终端结点’的链表。再次强调,这个链表不是分支,分支只是一个指向这个链表‘起始结点’的指针,所以你删除分支,用 reset 移动分支时,并不会影响这个已经形成的链表记录。<br />所以呢, 分支 就是一个提交记录的别名,因为 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 等。好了,我们现在都理解了指针,那看看我们在工作中,可以怎么操作吧

示例

新建分支。操作如下:

git branch test # 这是新建一个叫test的分支,这是怎么新建的呢?其实它原来是这样的。git branch test HEAD # 就是在头指针指向的提交对象上建立一个test别名,强调:在 Git 里大部分放 HEAD 的地方,都可以把 HEAD 省略。# 比如 git show # 对应就是 git show HEADgit 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 命令就能全部找到。所以,如果你理解了指针,那就大胆的操作吧,玩不坏的啦!

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


发布于: 2020 年 06 月 07 日阅读数: 46
用户头像

卡尔

关注

还未添加个人签名 2019.03.01 加入

还未添加个人简介

评论

发布
暂无评论
【摘】Git-从零单排 03期