gitcheckout命令详解
在⽇常的git操作中,git checkout——检出,是我们的常⽤命令。最为常⽤的两种情形是创建分⽀和切换分⽀。
(⼀)基础——千⾥之⾏,始于切糕(checkout)
先熟悉下常⽤操作,创建分⽀和切换分⽀,也可以称为检出分⽀。
⾸先我们新建⼀个仓库gitTest,然后新建⽂件a,为什么要⽤a命名呢,这⾥是故意为之,后⾯为⼤家揭晓分⽀。呵呵。或许下⾯的介绍会有些枯燥乏味,因为您已经对这些命令烂熟于胸,⽽且运⽤得相当熟练,那么您可以直接跳过这⼀步。
在master分⽀上,做⼀次提交c1,然后现在新建⼀个分⽀a,并切换到a分⽀。
这个操作主要会⽤到两个命令:
创建新分⽀:git branch branchName
切换到新分⽀:git checkout branchName
然后,上⾯两个命令也可以合成为⼀个命令:
git checkout -b branchName
(⼆)真相——HEAD是checkout的灵魂
其实,我们在切换分⽀,和新建分⽀的时候,有没有想过,这些操作操作背后的⼯作原理是怎样的呢?最⼤的功⾂就是.git⽬录下的HEAD 引⽤,她宛如⼀个芭蕾舞者,从⼀个分⽀飘逸的跳到另⼀个分⽀,虽⽆声⽆息,却精准⽆⽐。
在我们⾝处master分⽀的时候,您⼀定很好奇,当前的HEAD的内容是什么?不妨来看看吧。
我们看到c1的提交hash值和HEAD对应分⽀master的当前hash值是⼀样的。也就是说,HEAD指向的是当前分⽀名master,⽽master⼜对应了当前的最新的⼀次提交ID.
好,那么我们再做⼀次提交,看看master对应的hash值有⽆变化。
从上图,我们可以不难看出,HEAD对应的ref没有变化,还是master,但是master对应的commit ID却变成了c2对应的commit ID,即更新为最后⼀次提交的ID咯。
现在,提交⼀次的原理,我们已然了解,那么切换分⽀的时候呢??
现在我们⾝处master分⽀,然后我们切换到a分⽀,看看会发⽣什么样的情况吧。
从上图分析,在master分⽀上的时候,HEAD指向的是master,对应的是c2的commit ID。⽽切换到a分⽀的时候,HEAD也相应的指向了a,同时a对应的是a分⽀上的最新commit ID。因此,我们可以得出结论,在切换分⽀的时候,HEAD也会相应的指向对应的分⽀引⽤。
但是,使⽤checkout命令的时候,并不是每次都会改变HEAD指针的指向哦。在什么情况下HEAD⼀直坚定不移的拥护者他的⼥神呢?可谓⾐带渐宽终不悔,长使英雄泪满襟啊!让我们接着往下看。
(三)进阶——HEAD动不动,看你怎么⽤
checkout命令⽤法如下:
1. git checkout [-q] [<commit>] [--] <paths> ...
2. git checkout [<branch>]
3. git checkout [-m] [ [-b | -- orphan ] <new_branch>]  [start_point]
git常用指令⽤法2⽐⽤法1的区别在于,⽤法1包含了路径。为了避免路径和引⽤(或提交ID)同名⽽发⽣冲突,可以在<paths>前⽤两个连续的连字符作为分隔。⽤法1的<commit>是可选项,如果省略,则相当于从暂存区进⾏检出。
来看个例⼦:
情景1,省略掉<commit>
现在我们处于master分⽀下,然后我们修改了⽂件a,输⼊“c3”⽂本到a中,这时候,暂存区中的内容是没有"c3"的,通过git diff可以⽐较。现在我们从当前分⽀暂存区中检出⽂件a。那么我们可以直接使⽤git checkout a。
这时候,提⽰检出失败,git以为我们想检出仓库a。还记得为什么在第⼀步中,我们曾新建的⽂件a吗?这⾥终于派上⽤场了,由于仓库中还存在分⽀a,同时当前分⽀中⼜存在⽂件a,于是git傻傻分不清楚了。这时候怎么办?有两个办法,第⼀,我们在命名分⽀的时候要注意语义性,分⽀名要具有⼀定的意义,不能使⽤简单的a,b,c来命名,这样很容易导致分⽀名和⽂件名重复;第⼆,参照⽤法1,使⽤两个连字符来分隔。在⽬前的情形中,我们使⽤第⼆种⽅法吧。
这时候,发现⼯作区的内容被暂存区的内容覆盖,"c3"⽂本也没有了,当然HEAD指针也没有什么变化,⼀切⼜恢复了平静。
再看⼀个例⼦:
情景2,不省略<commit>
在不省略<commit>的时候,<commit>既可以是某⼀个具体的commit hash值,也可以是某个分⽀名称,tag名称。不论分⽀也好,tag也好,它们本质上对应的都是⼀个commit hash值。
在检出a分⽀下的a⽂件的时候,最好把两个连字符加上,不然git也会⽆法区分。整个过程中,HEAD头指针没有发⽣改动。
总结:第1种⽤法(包含<paths>的⽤法)不会改变HEAD头指针,主要使⽤于指定版本的⽂件覆盖⼯作区中对应的⽂件。如果省略
<commit>,则会⽤暂存区的⽂件覆盖⼯作区中的⽂件,否则⽤指定提交中的⽂件覆盖暂存区和⼯作区
中的对应⽂件。
接下来,我们看看⽤法2,在第⼀部分中,我们知道切换分⽀,会改变HEAD的指向,那么如果我们是检出某个commit会怎样呢?同检出分⽀⼀样,会⽤该commit下的内容覆盖当前分⽀⼯作区和暂存区的内容,请看例⼦。
⽬前我们处于master分⽀上,且已经有了两次提交,分别是c1和c2,然后我们修改a,给a⽂件添加内容"c3",并add到暂存区,随即使⽤checkout到c1的commit 上。注意,刚开始checkout的时候,git不会允许你直接切换,因为你修改了暂存区的内容, 它会提醒你提交后再切换,这时候,你可以使⽤-f 强⾏切换。再查看状态的时候,git提⽰我们已经不在任何分⽀上,HEAD指针也是指向具体的c1的commit 值,进⼊了“分离头指针”状态。这个状态下,要回到master上⾯,只需要git checkout master即可,也可以在这个状态上新建分⽀。
如果,checkou后⾯不跟任何参数,则就是对⼯作区进⾏检查,请看例⼦。
我们⾝处master分⽀上,并且没有任何改动,这时候git checkout没有任何输出。然后,我们给a⽂件添加内容“c3”,然后再git checkout⼀下,git就会提⽰a⽂件有修改,是不是很简单?
总结:对于第2种⽤法,不是检出某个具体⽂件的的时候,即不指定<paths>的时候,单纯的检出某个commit或分⽀,是会改变HEAD头指针的。⽽且只有当HEAD切换到某个分⽀的时候才可以对提交进⾏跟踪,否则就会进⼊“分离头指针”的状态。如果省略⽤法2后⾯的
<branch>,则默认对⼯作区进⾏状态检查。
(四)熟悉的checkout,陌⽣的⽤法,妈妈再也不⽤担⼼我的checkout啦!
1. git branch <branch> <start point>
以某个commit创建新分⽀。 在通常情况下,我们都会在当前分⽀的基础上,创建新分⽀。⽐如git branch new_branch
也许你不知道,我们还可以基于当前分⽀的某⼀次commit来创建分⽀。请看!
从上图可见,我们想基于master分⽀的c1 提交ID创建新分⽀new_branch,创建成功后,切换到new_branch,查看log,只有c1,耶~~成功啦!
当然,也可以使⽤git checkout -b <new_branch> <start point>这个常⽤的命令。
2.  git checkout --datch <branch>
切换到分⽀的游离状态,默认以该分⽀下的最后⼀次提交ID,请看下⾯的例⼦。
当前分⽀为a,然后使⽤git checkout --detach master,那么HEAD就会切换到master的最后⼀次commit值的状态下!
3. git checkout -B <branch>
这个命令,可以强制创建新的分⽀,为什么加-B呢?如果当前仓库中,已经存在⼀个跟你新建分⽀同
名的分⽀,那么使⽤普通的git checkout -b <branch>这个命令,是会报错的,且同名分⽀⽆法创建。如果使⽤-B参数,那么就可以强制创建新的分⽀,并会覆盖掉原来的分⽀。请看具体操作。
当前分⽀为master,且仓库中已经存在分⽀a,我们先⽤git checkout -b a来创建a分⽀,必然会失败的,并提⽰我们仓库中已经有了⼀个a分⽀咯,仿佛在说“hi,哥们,你已经有了⼀个⽼婆了,⼀夫⼀妻制你的不懂?你以为这⾥是印度啊?”。随后,我们使⽤git checkout -B a,耶~~,it works!
4. git checkout --orphan <branch>
是的,假如你的某个分⽀上,积累了⽆数次的提交,你也懒得去打理,打印出的log也让你⽆⼒吐槽,那么这个命令将是你的神器,它会基于当前所在分⽀新建⼀个⾚裸裸的分⽀,没有任何的提交历史,但是当前分⽀的内容⼀⼀俱全。新建的分⽀,严格意义上说,还不是⼀个分⽀,因为HEAD指向的引⽤中没有commit值,只有在进⾏⼀次提交后,它才算得上真正的分⽀。还等什么呢?赶紧试试!

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。