前奏:
1、用过git的人都知道,通过git log查看的提交记录的哈希值是很长的,就像fed2da64c0efc5293610bdd892f82a58e8cbc5d8
好消息是,git对哈希的处理很智能,你只需要提供能够唯一标识提交记录的前几个字符就可以了,因此上述的字符串,对于git来说fed2就可以定位到具体哪条提交记录
。接下来的提交记录的hash值用c0-cxxx标识。
2、HEAD
-
HEAD是说简单点就是指向你正在进行工作的提交记录。他是一个指针,告诉git你在哪个分支工作
-
HEAD 总是指向当前分支上最近一次提交记录。大多数修改提交树的 Git 命令都是从改变 HEAD 的指向开始的
-
HEAD 通常情况下是指向分支名的
分离的 HEAD 就是让其指向了某个具体的提交记录而不是分支名。就是说一个分支有多次提交记录,分离的HEAD就是让head这个指针指向其中一次提交记录,然后操作这项记录
例:在命令执行之前的状态如下所示:
其中的c0-c4是各分支的提交记录。当前git的工作分支是bugFix,最新的提交记录是c4
一、相对引用(^)和相对引用(~)
在实际应用中,有时需要回溯到之前提交记录找问题,这里就用到了两种引用
- ^:使head向上移动1个提交记录
- ~<num>: 如~2,head向上移动2个提交记录
具体的命令有两种:
- git checkout <branch>^ 或 git checkout <branch>~<num> (将HEAD移动到指定branch的前n个记录,n∈[1,num])
!!!注意!!!!这里移动的是HEAD,通过移动head来指定工作位置,因为git checkout 实际上是修改HEAD文件的内容,让其指向不同的branch。
如果这时候使用 git checkout -b test来新创建分支,则会如下:在c1这个提交记录这里分出一个新的test分支
- git branch -f <要操作的分支名> <目标提交记录位置>
通过git branch -f bugFix bugFix^ 指令,将bugFix分支的工作位置移到了c5这里,而c6这个提交记录处于detached状态
这时候,如果使用merge命令合并分支,如下图,就是把c5的提交内容合并到master。(这里因为checkout到了master,所以master——>HEAD——>c7,也就是说HEAD指向了最新一次的提交)
二、git reset 和 git revert
git reset
通过把分支记录回退几个提交记录来实现撤销改动。你可以将这想象成“改写历史”。git reset
向上移动分支,原来指向的提交记录就跟从来没有提交过一样,如图
--------------git reset HEAD~1 --------------->
(注意,前面讲到了分离的HEAD,如果当前是分离的HEAD状态,不能reset)
虽然git reset能在本地使用,但是对于远程分支是无效的,为了撤销更改并分享给别人,我们需要使用git revert。
--------------------git revert HEAD -------------->
有没有发现多了一个C2‘ ??在我们要撤销的提交记录后面居然多了一个新提交!
这是因为新提交记录 C2'
引入了更改 —— 这些更改刚好是用来撤销 C2
这个提交的。也就是说 C2'
的状态与 C1
是相同的
三、git cherry-pick (知道提交记录的哈希值)
命令:git cherry-pick <提交号1> <提交号2> <提交号3>....
git cherry-pick命令能把之前的提交记录,复制到当前所在的位置(head指向位置)
如下图,当前的工作分支是master,最新的提交记录是c5,head指向c5。通过命令git cherry-pick c2 c4 将c2 c4两次的提交记录,依次复制到c5下面
---------------git cherry-pick c2 c4------------>
当你知道提交记录的哈希值时,用cherry-pick就能复制提交记录,但不清楚时,就靠如下的交互式rebase
四、交互式rebase(知道提交记录的哈希值)
交互式rebase指的是使用带参数 --internactive 的rebase命令,简写为 -i
如果在命令后面增加这个选项,Git会打开一个UI界面,并列出要被复制到目标分支的备选提交记录。
当 rebase UI界面打开时, 你能做3件事:
- 调整提交记录的顺序(通过鼠标拖放来完成)
- 删除你不想要的提交(通过切换
pick
的状态来完成,关闭就意味着你不想要这个提交记录) - 合并提交。 遗憾的是由于某种逻辑的原因,我们的课程不支持此功能,因此我不会详细介绍这个操作。简而言之,它允许你把多个提交记录合并成一个。
git rebase -i HEAD~4
五、本地栈式提交
在开发时经常会碰到这样一个问题:正在解决某个特别棘手的 Bug,为了便于调试而在代码中添加了一些调试命令并向控制台打印了一些信息。这些调试和打印语句都在它们各自的提交记录里。最后我终于找到了造成这个 Bug 的根本原因,解决掉以后觉得沾沾自喜。最后就差把分支合并到master了,可是这样的话master上就会包含那些调试语句了,这时候本地栈式提交可以帮到你。通过 git rebase -i 和 git cherry-pick 两个命令
如图,目前在bugFix分支上工作,完成了bug修复,现在要把bugFix合并到master,我们通过
git checkout master; git cherry-pick bugFix c4这个记录就到了master
六、提交的技巧
下面这种情况也是很常见的:你之前在 newImage
分支上进行了一次提交,然后又基于它创建了 caption
分支,然后又提交了一次。
此时你想对的某个以前的提交记录进行一些小小的调整。比如设计师想修改一下 newImage
中图片的分辨率,尽管那个提交记录并不是最新的了
我们可以通过下面的方法来克服困难:
- 先用
git rebase -i
将提交重新排序,然后把我们想要修改的提交记录挪到最前 - 然后用
commit --amend
来进行一些小修改 - 接着再用
git rebase -i
来将他们调回原来的顺序