设为首页 收藏本站
查看: 924|回复: 0

[经验分享] To be F2Eer with passion!

[复制链接]

尚未签到

发表于 2018-1-14 15:57:47 | 显示全部楼层 |阅读模式
  一. 引子
  在git操作中,我们可以使用checkout命令检出某个状态下文件,也可以使用reset命令重置到某个状态,这里所说的“某个状态”其实对应的就是一个提交(commit).

  我们可以把一个git仓库想象成一棵树,每个commit就是树上的一个节点。家家都有一本自己的祖谱。祖谱记录了一个家族的生命史,它不仅记录着该家族的来源、迁徙的轨迹,还包罗了该家族生息、繁衍、婚姻、文化、族规、家约等历史文化的全过程。类似的,每个git仓库都有一本自己的祖谱,仓库中commit>  在git中,我们其实可以通过^和~来定位某个具体的commit,而不用每次都去敲繁琐的hash值。为了便于大家理解,先把结论放在前面:



  • “^”代表父提交,当一个提交有多个父提交时,可以通过在”^”后面跟上一个数字,表示第几个父提交,”^”相当于”^1”.

  • ~<n>相当于连续的<n>个”^”.

  • checkout只会移动HEAD指针,reset会改变HEAD的引用值。
  使用git log –graph 命令,可以查看自己仓库的当前分支提交ID的树状图,如下图所示。
DSC0000.jpg

  使用git log –pretty=raw命令,可以查看commit之间的父子关系,如下图所示,需要注意的是最开始的commit是没有父提交的。
DSC0001.jpg

  二. 困惑
  在使用git的过程中,你也许会有很多的困惑。
  在使用reset或checkout命令的时候,需要一个<commit>参数,但是每次都输入commit hash值是一件比较麻烦的事情。首先你得去查询下日志,然后再用键盘将前面几位hash值输入。有时候你一次还搞不定,突然开个小差,暗恋下女神,想一想基友,都容易把hash值遗忘或弄错。肿么办???
  又话说突然间,一堆带有hash值的符号出现在生活中,HEAD^1~4,<commit>~3^2,我擦!这是TMD玩意儿?不懂啊,使用过程中,HEAD和引用各种乱窜,根本不听从我的指挥,哎呀,妈呀!我成了git的奴隶,从此生活不再美好。肿么办???
  不,生活还要继续,要和git做朋友。做朋友当然先要摸清楚朋友的性情和脾气咯,有了好友,生活才会充满希望。
  三. 解惑
  古有“射人先射马,擒贼先擒王”,今有“git仓库顺藤摸瓜”。既然commit形成的树状图,表明了各个commit之间的关系,那么我们也可以顺着这棵树去查询commit的值。一般情况下,一个commit都会有一个父提交,那么通过<commit>^这个表达式,就可以访问到其父提交的ID值;使用<commit>~也可以达到同样的功效哦。
  我们知道每提交一次,HEAD就会自动移到版本库中最近的一次提交。那么HEAD^就代表了最近一次提交的父提交,HEAD~也是同样的道理;但是如果你想当然的认为^和~的用法相同,那就错了,其实它们的区别还是蛮大的。
  四. 详解
  我们来通过一个具体的例子,来讲解一下^和~的用法区别,同时在checkout或reset的过程中,看看HEAD和引用的变化。
  查看HEAD和引用的值
  我们可以通过命令来查看HEAD和引用的值,也可以通过当前仓库下的.git目录去访问。当前分支为master时,我们查看HEAD的值,命令如下:
  

$ cat .git/HEAD  
ref: refs/heads/master
  

  

  然后,我们可以查看master引用的值
  

$ cat .git/refs/heads/master  
3b0370b.......  # hash code
  

  

  master分支上初始化,并提交一次

  在master分支上新建一个提交”c1”,生成commit>  

$ git init  
Initialized empty Git repository
  
$ echo c1 >> a
  
$ git add a
  
$ git commit
  
[master (root-commit) 973c5dd] c1
  
1 files changed, 1 insertions(+), 0 deletions(-)
  
create mode 100644 a
  
$ git log --oneline
  
973c5dd c1
  

  

  对应的图如下所示:
DSC0002.jpg

  基于master新建br1分支,并提交两次

  接下来在master分支基础上新建分支”br1”,并在”br1”上提交”c2”,commit>  

$ git checkout -b br1  
Switched to a new branch 'br1'
  
$ echo c2 >> b
  
$ git add b
  
$ git commit
  
[br1 1c7383c] c2
  
1 file changed, 1 insertion(+)
  
create mode 100644 b
  
$ git log --oneline
  
1c7383c c2
  
973c5dd c1
  

  

  对应的图如下所示:
DSC0003.png


  在分支”br1”上,提交”c3”,commit>  

$ echo c3 >> b  
$ git commit -a -m "c3"
  
[br1 4927c6c] c3
  
1 file changed, 1 insertion(+)
  
$ git log --oneline
  
4927c6c c3
  
1c7383c c2
  
973c5dd c1
  

  

  对应的图如下所示:
DSC0004.png   

  切换到master分支,基于master分支新建br2分支,并提交两次
  我们先切回到master分支,然后新建分支br2,先后提交”c4”和”c5”,对应的ID分别是”86ba”和”063f”,这时候HEAD指向br2,br2引用指向”c5”的对应提交063f.git 命令如下:
  

$ git chechout master  
Switched to branch 'master'
  
$ git checkout -b br2
  
Switched to a new branch 'br2'
  
$ echo c4 >> c
  
$ git add c
  
$ git commit -m "c4"
  
[br2 86ba564] c4
  
1 file changed, 1 insertion(+)
  
create mode 100644 c
  
$ git log --oneline
  
86ba564 c4
  
973c5dd c1
  
$ echo c5 >> c
  
$ git commit -a -m "c5"
  
[br2 063f6e6] c5
  
1 file changed, 1 insertion(+)
  
$ git log --oneline
  
063f6e6 c5
  
86ba564 c4
  
973c5dd c1
  

  

  对应的图如下所示:
DSC0005.png   

  切换到master分支,基于master分支创建br3分支,并提交两次
  这个操作同分支br2上类似,先从br2分支切换到master分支,然后新建分支br3,分别提交”c6”和”c7”,对应的ID分别是”50f1”和”4f9c”,这时候HEAD指向br3,br2引用指向”c7”的对应提交4f9c,git 命令如下:
  

$ git chechout master  
Switched to branch 'master'
  
$ git checkout -b br3
  
Switched to a new branch 'br3'
  
$ echo c6 >> d
  
$ git add d
  
$ git commit -m "c6"
  
[br3 50f14f6] c6
  
1 file changed, 1 insertion(+)
  
create mode 100644 d
  
$ git log --oneline
  
50f14f6 c6
  
973c5dd c1
  
$ echo c7 >> c
  
$ git commit -a -m "c7"
  
[br2 4f9ca79] c7
  
1 file changed, 1 insertion(+)
  
$ git log --oneline
  
4f9ca79 c7
  
50f14f6 c6
  
973c5dd c1
  

  

  对应的图如下所示:
DSC0006.png

  切换到master分支,合并br1,br2和br3分支
  先切换到master分支,然后合并br1 br2 br3,会新生成一个提交3b03.
  

$ git checkout master  
$ git merge br1 br2 br3
  
3 files changed, 6 insertions(+)
  
create mode 100644 b
  
create mode 100644 c
  
create mode 100644 d
  
$ git log --oneline
  
3b0370b Merge braches 'br1', 'br2' and 'br3'
  
4f9ca79 c7
  
50f14f6 c6
  
063f6e6 c5
  
86ba564 c4
  
4927c6c c3
  
1c7383c c2
  
973c5dd c1
  

  

  这时候,运用git log –oneline –graph查看生成的树状图,如下所示.
DSC0007.jpg

  从上图分析,在第1条红线上的commit顺序是: 3b03→4927→1c73→973c
  第2条红线上的commit顺序是:3b03→063f→86ba→973c
  第3条黄线上的commit顺序是:3b03→4f9c→50f1→973c
  这3条线的从左至右的顺序非常重要,因为HEAD^1对应的就是第1条红线的提交4927,HEAD^2对应的是第2条绿线的063f提交,HEAD^3对应的是第3条黄线的4f9c提交。3b03没有第4个父提交,因此也没有第4条线,这时候访问HEAD^n(n>3)都会报错。
  因此从任何一条线上,我们都可以追溯到”c1”的commit,但是每条线上的中间节点,只能通过这条线上的节点去访问。
  操作同上类似,最后的状态如下,这时候HEAD指向master,master引用指向”c8”的对应提交3b03.
  对应的图如下所示:
DSC0008.png

  我们再来看看3b03对应节点的父提交,如下图所示:
DSC0009.jpg

  从图得知,3b03一共有三个父提交,分别是4927,063f,4f9c.
  reset与checkou的区别
  在master分支上,当前提交为3b03,使用git reset –hard HEAD^,将master重置到HEAD的父提交;该命令也可以写成git reset –hard HEAD^1
  

$ git reset --hard HEAD^  
HEAD is now at 4927c6c c3
  

  

  对应的图如下所示:
DSC00010.png

  

  这时候,HEAD还是指向master分支,但是master引用的commit值已经变成了4927,即3b03的第一个父提交的ID.
  

  

  

  然后,我们再重置到”c8”的commit”3b03”,git reset –hard 3b03,然后使用命令git checkout HEAD~ ,git 操作如下:
  

$ git reset --hard 3b03  
HEAD is now at 3b0370b Merge branches 'br1', 'br2' and 'br3'
  
$ git checkout HEAD~
  
HEAD is now at 4927c6c... c3
  

  

  对应的图如下所示:
DSC00011.jpg

  这时候,HEAD指向了commit 4927,即3b03的第一个父提交ID,但是master引用还是对应的3b03.
  从上面的测试,我们可以得出以下结论:



  • HEAD^,HEAD^1和HEAD~三个表达式都是代表了HEAD的父提交

  • reset <commit>的时候,HEAD不变,但是HEAD指向的引用值会变成相应的<commit>值;checkout <commit>的时候,HEAD直接变成<commit>值,但原来引用中保存的值不变。
^n和~n的区别
  (<commit>|HEAD)^n,指的是HEAD的第n个父提交(HEAD有多个父提交的情况下),如果HEAD有N个父提交,那么n取值为n < = N.
  (<commit>|HEAD)~n,指的是HEAD的第n个祖先提交,用一个等式来说明就是:(<commit>|HEAD)~n = (<commit>|HEAD)^^^….(^的个数为n).我们通过例子来验证一下吧。
  我们沿用上面演示用的仓库,先检出到master分支,再使用git checkout HEAD^2,看看我们检出了哪个commit
  

$ git checkout master  
$ git checkout HEAD^2
  
HEAD is now at 063f6e6... c5
  

  

  我们发现”c5”对应的commit值063f正是3b03第二个父提交的commit 对应的图如下所示:
DSC00012.jpg

  现在再切回master分支,git checkout master
  然后使用git checkout HEAD^3,那么按照规律,就应该检出3b03的第三个父提交的commit,即”c7”的commit值4f9c.
  

$ git checkout master  
Previous HEAD position was 063f6e6... c5
  
Switched to branch 'master'
  
$ git checkout HEAD^3
  
HEAD is now at 4f9ca79... c7
  

  

  对应的图如下所示:
DSC00013.jpg

  果然没错,一切都在我们的预料之中!
  现在验证下HEAD~的用法,切换到master分支,然后git checkout HEAD~2
  

$ git checkout master  
$ git checkout HEAD~2
  
HEAD is now at 1c7383c... c2
  

  

DSC00014.png

  这时候HEAD悄然来到了”c2”的commit 1c73,因此,HEAD~2 相当于HEAD的第一个父提交的第一个父提交。即HEAD~2 = HEAD^^ = HEAD^1^1, 符合预期!好开心的哟!
  五.总结



  • “^”代表父提交,当一个提交有多个父提交时,可以通过在”^”后面跟上一个数字,表示第几个父提交,”^”相当于”^1”.

  • ~<n>相当于连续的<n>个”^”.

  • checkout只会移动HEAD指针,reset会改变HEAD的引用值。
  现在看到^和~两个符号,再也不会彷徨和害怕了,因为我们知道了它们之间的关系及区别,从此我们过上了幸福的生活。

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-435045-1-1.html 上篇帖子: 我的GIT工作流程 下篇帖子: Git 在团队中的最佳实践
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表