git魔法:HEAD的指针变化

一、HEAD是什么?

在git中撤回操作,无论是reset、checkout和revert撤回上一步,都会用到HEAD这个指令字段,但这个HEAD到底指得是什么,一直没搞明白。其实一开始在学git原理的时候,都会看到下面这种图。

image

告诉你HEAD是一个指针,如果你用cat .git/HEAD这个命令查看HEAD,就会知道这里存储的是当前分支,如:ref: refs/heads/master

但正如每个git入门教程里说的,这个refs/heads/master里存储其实就是当前commit的引用。这里可以理解为一个仓库就是一颗树,每个分支则是不同的树枝,树枝上有不同的节点(代表每一个commit),而commit之前也有父子关系,HEAD指针则是指向commit id,HEAD所在的commit就是目前本地仓库的状态。

那平时我们提交commit则是增加节点,同时HEAD指针后移,这个不必多说。但git的强大,不止如此,还可以通过reset、checkout、revert、merge和rebase等操作指令,花式移动指针,游走于整颗commit树,这个是这次笔记的重点。

二、merge和rebase

merge是合并分支,rebase是整合分支(个人理解),是直接将分支信息整合进一个链条,这样的好处是看起来简洁。通常的操作是现在短期分支上rebase目标分支,然后在将短期分支merge进去。虽然rebase之后的分支看起来很整洁一贯,但正因为rebase强行将commit整合,就会出现下图这样提交时间先后不分的情况,git统一的处理是将合并进来的分支所有提交放在最前端;

image

当然,对于某些洁癖来说,rebase确实是个救星,就算是在--no-ff这种带分支信息的合并中,用了rebase,也能将一个分支的提交单独提出到一个分叉中,看起来比较直观,不会跟主分支里的提交混淆,不过如果说是一个健康的主分支一般也不会出现这种混淆的情况(分支内直接push),所以这里也就是提一提。

但实际上rebase的使用是有原则的,就是不要将私有分支的提交rebase到公共分支,因为这样会导致共有分支的提交记录改变(rebase的原理就是对比合并分支并将commit一一合并,从而用新的commit去覆盖),这样对其他协作者非常不利。可参考rebase的使用:https://cn.atlassian.com/git/tutorials/merging-vs-rebasing#the-golden-rule-of-rebasing

如果再带上squash命令,就可以直接把被合并分支中的所有commit合并成一个,提交记录就更简洁了,不过可能无法追溯细节提交记录,并且回滚也比较麻烦。

三、reset、checkout、revert

开发的时候,经常需要进行提交撤回的操作,一般用到这三个指令,他们的区别是:
1、reset只更改HEAD指针指向的commit id,如果这个操作撤回某些commit,则这些commit在log里会消失,并且这些commit引用会在git的垃圾回收处理过程中被删除,也就是这部分树枝之后会被锯掉;
2、checkout则为移动的目标指针单独建立一个分支,并移动HEAD,原分支不变;
3、revert新建一个commit,指针后移,并将目标commit的内容作为本次commit的内容,个人感觉这种操作更安全,毕竟会保留之前的记录;(但是要注意,如果你合并了某个分支,并且revert该分支中的一个commit,不要以为再合并一次这个分支就可以还原那个revert,是不行的,git会默认把这个revert导致的差异对冲掉,你如果想还原,要么reset或者revert那次revert)

下面是三种命令的使用场景总合,来源:https://cn.atlassian.com/git/tutorials/resetting-checking-out-and-reverting

image

四、~ 和 ^

这两个符号在三中的操作中,经常会用到,个人理解为移动指针的单位。一中提到,commit之间存在父子关系,当commit是一条链没有分叉时,父子关系是递增下去的。如果是中间有合并操作,则上一次合并操作为父亲(暂时怎么理解,我了解的也不够深)。

所以从下面两张图可以看到,^n 符号是父亲节点中找第n-1个(因为1就是当前节点),像这里2则是到第一个父亲节点,3是第二个,如果4则会报错,因为不存在第三个父亲节点。

而 ~n 则是往上找到n-1层到第一个节点,2则是找到父亲节点,3则找到爷爷节点。

image

(c1是曾爷爷)

image

image

截图里的操作序列,还少了一个比较重要的点,就是git reset HEAD~命令会将HEAD移动到当前commit的第一个父亲节点(9b13a6c 合并),所以从上两个图可以看出,^和~的区别,但目前本人很少遇到用到这两个命令的场景,也暂不了解这两个命令有什么高级的用法,所以点到为止。

五、cherry-pick

这个命令也是一个很好用用的改变commit的指令,如这个指令名,它的作用就是将一个或多个commit捡出(pick),然后合并进当前分支。有点git merge some commit的意思。

六、git update-ref

命令用于更新一个指针文件中的Git对象ID。

在理解这个命令前,需要先了解一下git 的refs文件(http://www.chenchunyong.com/2017/01/06/git-refs-%E8%AF%A6%E8%A7%A3/

我的理解是,git的refs文件,就是存储git下的各种管理分支的引用,同时远程分支和本地分支的追踪也是依靠这个文件。

我会去了解这个是因为遇到下面这个问题,git在创建新分支时,因为分支名为hotfix/1129,但由于前面refs的实现的原理,本地之前有一个hotfix分支,而这个hotfix分支在.git/refs/heads/hotfix这里标记了一个ref,而创建hotfix/1129时,则是想覆盖.git/refs/heads/hotfix这个文件为.git/refs/heads/hotfix/1129,这么做git自然不允许,所以报错 refs/heads/hotfix exists

image

知道了原因后,解决方法有两个,一是使用git update-ref -d refs/heads/hotfix去删除hotfix的refs;二是直接删除hotfix这个分支,因为refs/heads/hotfix这里其实就是对hotfix分支的引用;

但个人觉得真正的原因就是这个命名导致的,因为出现了hotfix这种过于简单且不符合项目规范的命名,又没有及时删除导致的。因为这次错误是出现在测试人员那里,所以项目最好规范开发不允许取这种简单的分支名,或者不采用 / 符号来做分支划分,可以用 _ 等代替。

七、git revert

将一个操作撤回,并将这次撤回当作一个commit。这么做的好处有很多:

  • 这次撤回操作可追溯;
  • 相比直接reset,这个不需要强制push远程,因为这是增量操作。如果是reset,远程commit是多于本地的,这时候需要force push才能使远程同步,这个过程如果有人提交了远程就炸了;

如果是revert一次merge的话,需要带上-m %d 命令,表示你需要撤回的阶级,是这次merge还是merge中的某次commit,我的理解就是上面HEAD笔记里提到的父子commit概念;

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,122评论 6 505
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,070评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,491评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,636评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,676评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,541评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,292评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,211评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,655评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,846评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,965评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,684评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,295评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,894评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,012评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,126评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,914评论 2 355

推荐阅读更多精彩内容