git——^和~的区别(转)
原文地址: http://www.cnblogs.com/softidea/p/4967607.html
一. 引子
在git操作中,我们可以使用checkout命令检出某个状态下文件,也可以使用reset命令重置到某个状态,这里所说的“某个状态”其实对应的就是一个提交(commit).
我们可以把一个git仓库想象成一棵树,每个commit就是树上的一个节点。家家都有一本自己的祖谱。祖谱记录了一个家族的生命史,它不仅记录着该家族的来源、迁徙的轨迹,还包罗了该家族生息、繁衍、婚姻、文化、族规、家约等历史文化的全过程。类似的,每个git仓库都有一本自己的祖谱,仓库中commit ID的繁衍,HEAD指针的迁徙,分支的增加、更新,同样的记录着一个仓库从无到有的点点滴滴。
在git中,我们其实可以通过^和~来定位某个具体的commit,而不用每次都去敲繁琐的hash值。为了便于大家理解,先把结论放在前面:
- “^”代表父提交,当一个提交有多个父提交时,可以通过在”^”后面跟上一个数字,表示第几个父提交,”^”相当于”^1”.
- ~<n>相当于连续的<n>个”^”.
- checkout只会移动HEAD指针,reset会改变HEAD的引用值。
使用git log –graph 命令,可以查看自己仓库的当前分支提交ID的树状图,如下图所示。
使用git log –pretty=raw命令,可以查看commit之间的父子关系,如下图所示,需要注意的是最开始的commit是没有父提交的。
二. 困惑
在使用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 ID 973c,这时候master引用指向973c,HEAD指向master引用。
$ 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 |
对应的图如下所示:
基于master新建br1分支,并提交两次
接下来在master分支基础上新建分支”br1”,并在”br1”上提交”c2”,commit ID为1c73,这时候HEAD指向br1,br1引用指向”c2”对应提交1c73.
$ 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 |
对应的图如下所示:
在分支”br1”上,提交”c3”,commit ID为4927,此时HEAD指向br1,br1引用指向”c3”对应提交4927.
$ 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 |
对应的图如下所示:
切换到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 |
对应的图如下所示:
切换到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 |
对应的图如下所示:
切换到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查看生成的树状图,如下所示.
从上图分析,在第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.
对应的图如下所示:
我们再来看看3b03对应节点的父提交,如下图所示:
从图得知,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 |
对应的图如下所示:
这时候,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 |
对应的图如下所示:
这时候,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 对应的图如下所示:
现在再切回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 |
对应的图如下所示:
果然没错,一切都在我们的预料之中!
现在验证下HEAD~的用法,切换到master分支,然后git checkout HEAD~2
$ git checkout master $ git checkout HEAD ~ 2 HEAD is now at 1c7383c... c2 |
这时候HEAD悄然来到了”c2”的commit 1c73,因此,HEAD~2 相当于HEAD的第一个父提交的第一个父提交。即HEAD~2 = HEAD^^ = HEAD^1^1, 符合预期!好开心的哟!
五.总结
- “^”代表父提交,当一个提交有多个父提交时,可以通过在”^”后面跟上一个数字,表示第几个父提交,”^”相当于”^1”.
- ~<n>相当于连续的<n>个”^”.
- checkout只会移动HEAD指针,reset会改变HEAD的引用值。
现在看到^和~两个符号,再也不会彷徨和害怕了,因为我们知道了它们之间的关系及区别,从此我们过上了幸福的生活。
http://www.cnblogs.com/hutaoer/archive/2013/05/14/3078191.html
git——^和~的区别(转)的更多相关文章
- git revert 和 git reset的区别
git revert 撤销 某次操作,此次操作之前和之后的commit和history都会保留,并且把这次撤销 作为一次最新的提交 * git revert HEAD ...
- git的几种回滚 git revert 和 git reset的区别
git的几种回滚 git revert 和 git reset的区别:强烈建议:对HEAD不熟的话最好不要用HEAD,直接用commitID吧,我遇到的问题:reset HEAD~1之后,可能是别人提 ...
- Git merge 与 git rebase的区别
Git merge的用法: git merge Dev // Dev表示某分支,表示在当前分支合并Dev分支 git merge -m "Merge from Dev" Dev ...
- git revert用法以及与git reset的区别
git revert用法 git revert 撤销 某次操作,此次操作之前和之后的commit和history都会保留,并且把这次撤销 作为一次最新的提交 * git revert HEAD ...
- git init 与 git init --bare 区别
git init 与 git init --bare 区别 发现问题 最早是在公司的wiki上发现了这个命令,google后发现值得记录下来 实践中发现的区别 网上找了很多资料,但说的很乱,干脆在自己 ...
- Git fetch和git pull的区别, 解决Git报错:error: You have not concluded your merge (MERGE_HEAD exists).
Git fetch和git pull的区别, 解决Git报错:error: You have not concluded your merge (MERGE_HEAD exists). Git fet ...
- git merge和git rebase的区别和异同
1.git merge和git rebase作用差不多,都是将远程代码和本地代码合并 2.git merge和git rebase作用差不多,都是将远程代码和本地代码合并 3.git merge ...
- git stash与git commit的区别
问题的出现 写这篇文章的缘由是在工作中初次使用Git的时候遇到了一个奇怪的现象,即每次提交代码的时候,如果没有及时拉取代码就会导致本地库的代码不是最新的,这样自己修改代码之后想要push到远程仓 ...
- get merge --no-ff和git merge区别、git fetch和git pull的区别
get merge --no-ff和git merge区别 git merge -–no-ff可以保存你之前的分支历史.能够更好的查看 merge历史,以及branch 状态. git merge则不 ...
- git:Git fetch和git pull的区别, 解决Git报错:error: You have not concluded your merge (MERGE_HEAD exists).
Git fetch和git pull的区别, 解决Git报错:error: You have not concluded your merge (MERGE_HEAD exists). 解决办法一:保 ...
随机推荐
- 解决在Mac的Vmware Fusion中装win7系统和mac原生系统直接切换win7系统分辨率变化的问题
虚拟机 - 设置 - 显示屏 - 全屏显示retina (此选项钩去掉)
- Java遍历文件夹下的所以文件
利用Java递归遍历文件夹下的所以文件,然后对文件进行其他的操作.如:对文件进行重命名,对某一类文件进行重编码.可以对某一工程下的全部.java文件进行转码成utf-8等 代码如下,这里只对文件进行重 ...
- C语言常用标准库函数
数学函数: 在math.h中 abs(x) :求整型数x的绝对值 cos(x):x(弧度)的余弦 fabs(x):求浮点数x的绝对值 ceil(x):求不小于x的最小整数 floor(x):求不大于x ...
- xml添加新节点
#!/usr/bin/env python #coding:utf-8 # Author: xiaobaichuangtianxia--<> # Purpose: add jacoco d ...
- YII2中behavior行为的理解与使用
YII2中的行为说白了就是对组件功能的扩展,在不改变继承关系的条件下. 行为附加到组件后,行为将注入自已的方法和属性到组件,可以像组件访问自定义的方法和属性一样访问行为. 注意行为是对功能的扩展,不要 ...
- 移动端IOS和androi及浏览器js判断[转载]
转载自:http://www.niutifa.com/?p=561 移动端IOS和androi及浏览器js判断: <script type="text/javascript" ...
- Magento2 php商城在windows10上安装
magento2 下载地址:https://github.com/magento/magento2/archive/develop.zip 参考地址: 版本要求 这个magento2 要选择好php ...
- hdu 1698+poj 3468 (线段树 区间更新)
http://acm.hdu.edu.cn/showproblem.php?pid=1698 这个题意翻译起来有点猥琐啊,还是和谐一点吧 和涂颜色差不多,区间初始都为1,然后操作都是将x到y改为z,注 ...
- Kali xrdp远程桌面
发现论坛没有该教程,在这里分享给需要的基友.源还是要更新的,楼主在网上百度的kali源,而不是linux源,比163.搜狐的源好些.首先安装xrdp: apt-get install xrdp 复制代 ...
- linux利用crontab设置定时任务运行jar包
参考链接: 1.http://blog.csdn.net/javadhh/article/details/42779505 2.http://blog.csdn.net/cctv_liu/articl ...