12.3.2 时间旅行二
《回到未来》第二集,布朗博士改进的时间旅行车使用了未来科技,是陆天两用的飞车,而且燃料不再依赖核物质,而是使用无处不在的生活垃圾。而此次实践使用的工具也进行了升级,采用强大的git rebase命令。
命令git rebase是对提交执行变基操作,即可以实现将指定范围的提交“嫁接”到另外一个提交之上。其常用的命令行格式有:
用法1:git rebase—onto<newbase><since><till>
用法2:git rebase—onto<newbase><since>
用法3:git rebase<since><till>
用法4:git rebase<since>
用法5:git rebase-i…
用法6:git rebase—continue
用法7:git rebase—skip
用法8:git rebase—abort
不要被上面的语法吓到,用法5会在下节(时间旅行三)中予以介绍,后三种用法则是变基运行过程被中断时可采用的命令——继续变基或终止等。
用法6是在变基遇到冲突而暂停的情况下,先完成冲突解决(添加到暂存区,不提交),然后在恢复变基操作的时候使用该命令。
用法7是在变基遇到冲突而暂停的情况下,跳过当前提交的时候使用。
用法8是在变基遇到冲突而暂停的情况下,终止变基操作,回到之前的分支时候使用。
而前四个用法如果把省略的参数补上(方括号内是省略掉的参数),看起来和用法1就都一致了。
下面就以归一化的git rebase命令格式来介绍其用法。
命令格式:git rebase—onto<newbase><since><till>
变基操作的过程:
(1)首先会执行git checkout切换到<till>。
因为会切换到<till>,因此如果<till>指向的不是一个分支(如master),则变基操作是在detached HEAD(分离头指针)状态进行的,当变基结束后,还要像在“时间旅行一”中那样,对master分支执行重置以实现变基结果在分支中生效。
(2)将<since>..<till>所标识的提交范围写到一个临时文件中。
还记得前面介绍的版本范围语法吗,<since>..<till>是指包括<till>的所有历史提交排除<since>及<since>的历史提交后形成的版本范围。
(3)将当前分支强制重置(git reset—hard)到<newbase>。相当于执行:git reset—hard<newbase>。
(4)从保存在临时文件中的提交列表中,将提交逐一按顺序重新提交到重置之后的分支上。
(5)如果遇到提交已经在分支中包含,则跳过该提交。
(6)如果在提交过程遇到冲突,则变基过程暂停。用户解决冲突后,执行git rebase—continue继续变基操作。或者执行git rebase—skip跳过此提交。或者执行git rebase—abort就此终止变基操作切换到变基前的分支上。
很显然为了执行将E和F提交跳过提交D,“嫁接”到提交C上。可以如此执行变基命令:
$git rebase—onto C E^F
因为E^等价于D,并且F和当前HEAD的指向相同,因此可以这样操作:
$git rebase—onto C D
有了对变基命令的理解,就可以开始新的“回到未来”之旅了。
确认舞台已经布置完毕。
$git status-s-b
master
$git log—oneline—decorate-6
b6f0b0a(HEAD,tag:F,master)modify hello.h
48456ab(tag:E)add hello.h
3488f2c(tag:D)move.gitignore outside also works.
b3af728(tag:C)ignore object files.
d71ce92(tag:hello_1.0,tag:B)Hello world initialized.
c024f34(tag:A)README is from welcome.txt.
1.现在演出第一幕:干掉坏蛋D
(1)执行变基操作。
因为下面的变基操命令行使用了参数F。F是一个里程碑指向一个提交,而非master,会导致后面变基完成后还需要对master分支执行重置。在第二幕中使用分支master作为参数,会发现省事不少。
$git rebase—onto C E^F
First,rewinding head to replay your work on top of it…
Applying:add hello.h
Applying:modify hello.h
(2)最后一步必需的操作,就是要将master分支指向变基后的提交上。
下面的切换操作使用了reflog的语法,即HEAD@{1}相当于切换回master分支前的HEAD指向,即3360440。
$git checkout master
Previous HEAD position was 3360440…modify hello.h
Switched to branch 'master'
$git reset—hard HEAD@{1}
HEAD is now at 3360440 modify hello.h
(3)经过检查,操作完毕,收工。
$git log—oneline—decorate-6
3360440(HEAD,master)modify hello.h
1ef3803 add hello.h
b3af728(tag:C)ignore object files.
d71ce92(tag:hello_1.0,tag:B)Hello world initialized.
c024f34(tag:A)README is from welcome.txt.
63992f0 restore file:welcome.txt
2.幕布拉上,后台重新布景
为了第二幕能够顺利演出,需要将master分支重新置回到提交F上。执行下面的操作完成“重新布景”。
$git checkout master
Already on 'master'
$git reset—hard F
HEAD is now at b6f0b0a modify hello.h
布景完毕,大幕即将再次拉开。
3.现在演出第二幕:坏蛋D被感化,融入社会
(1)执行git checkout命令,暂时将HEAD头指针切换到坏蛋D。切换过程显示处于非跟踪状态的警告,没有关系,因为剧情需要。
$git checkout D
Note:checking out 'D'.
You are in 'detached HEAD' state.You can look around,make experimental
changes and commit them,and you can discard any commits you make in this
state without impacting any branches by performing another checkout.
If you want to create a new branch to retain commits you create,you may
do so(now or later)by using-b with the checkout command again.Example:
git checkout-b new_branch_name
HEAD is now at 3488f2c…move.gitignore outside also works.
(2)悔棋两次,以便将C和D融合。
$git reset—soft HEAD^^
(3)执行提交,提交说明重用C提交的提交说明。
$git commit-C C
[detached HEAD 2d020b6]ignore object files.
1 files changed,3 insertions(+),0 deletions(-)
create mode 100644.gitignore
(4)记住这个提交ID:2d020b6。
用里程碑是记忆提交ID的最好方法:
$git tag newbase
$git rev-parse newbase
2d020b62034b7a433f80396118bc3f66a60f296f
(5)执行变基操作,将E和F提交“嫁接”到newbase上。
下面的变基操命令行没有像之前的操作那样使用参数F,而是使用分支master。所以接下来的变基操作会直接修改master分支,而无须再对master进行重置操作。
$git rebase—onto newbase E^master
First,rewinding head to replay your work on top of it…
Applying:add hello.h
Applying:modify hello.h
(6)看看提交日志,看到提交C和提交D都不见了,代之以融合后的提交newbase。
还可以看到最新的提交除了和HEAD的指向一致,也和master分支的指向一致。
$git log—oneline—decorate-6
2495dc1(HEAD,master)modify hello.h
6349328 add hello.h
2d020b6(tag:newbase)ignore object files.
d71ce92(tag:hello_1.0,tag:B)Hello world initialized.
c024f34(tag:A)README is from welcome.txt.
63992f0 restore file:welcome.txt
(7)当前的确已经在master分支上了,操作全部完成。
$git branch
*master
(8)清理一下,然后收工。
前面的操作中为了方便创建了标识提交的新里程碑newbase,这个里程碑现在没有什么用处了,删除吧。
$git tag-d newbase
Deleted tag 'newbase'(was 2d020b6)
4.别忘了后台的重新布景
为了接下来的时间旅行三能够顺利开始,需要重新布景,将master分支重新置回到提交F上。
$git checkout master
Already on 'master'
$git reset—hard F
HEAD is now at b6f0b0a modify hello.h