12.3.3 时间旅行三

《回到未来》第三集,成了铁匠的布朗博士手工打造了可以时光旅行的飞行火车,使用蒸汽作为动力。这款时间旅行火车更大、更安全、更舒适,适合一家四口外加宠物的时空旅行。与之对应本次实践也将采用“手工打造”:交互式变基。

交互式变基就是在上一节介绍的变基命令的基础上,添加了-i参数,在变基的时候进入一个交互界面。使用了交互界面的变基操作,不是自动化变基转换为手动确认那么没有技术含量,而是充满了魔法。

执行交互式变基操作,会将<since>..<till>的提交悉数罗列在一个文件中,然后自动打开一个编辑器来编辑这个文件。可以通过修改文件的内容设定变基操作,实现删除提交、将多个提交压缩为一个提交、更改提交的顺序,以及更改历史提交的提交说明等。

例如,下面的界面就是针对当前DEMO版本库执行的交互式变基时编辑器打开的文件:


pick b3af728 ignore object files.

pick 3488f2c move.gitignore outside also works.

pick 48456ab add hello.h

pick b6f0b0a modify hello.h

Rebase d71ce92..b6f0b0a onto d71ce92

#

Commands:

p,pick=use commit

r,reword=use commit,but edit the commit message

e,edit=use commit,but stop for amending

s,squash=use commit,but meld into previous commit

f,fixup=like "squash",but discard this commit's log message

x<cmd>,exec<cmd>=Run a shell command<cmd>,and stop if it fails

#

If you remove a line here THAT COMMIT WILL BE LOST.

However,if you remove everything,the rebase will be aborted.


从该文件中可以看出:

开头的四行由上到下依次对应于提交C、D、E、F。

前四行默认的动作都是pick,即应用此提交。

参考文件中的注释,可以通过修改动作名称,在变基的时候执行特定操作。

动作reword,或者简写为r。在变基时会应用此提交,但是在提交的时候允许用户修改提交说明。这个功能在Git 1.6.6之后开始提供,对于修改历史提交的提交说明非常方便。对于老版本的Git没有reword动作,可以使用edit动作。

动作edit,或者简写为e。也会在变基时应用此提交,但是会在应用后暂停变基,提示用户使用git commit—amend执行提交,以便对提交进行修补。当用户执行git commit—amend完成提交后,还需要执行git rebase—continue继续变基操作。用户在变基暂停状态下可以执行多次提交,从而实现把一个提交分解为多个提交。edit动作非常强大,对于老版本的Git没有reword动作,可以使用edit动作实现相同的效果。

动作squash,或者简写为s。该提交会与前面的提交压缩为一个。

动作fixup,或者简写为f。类似动作squash,但是此提交的提交说明被丢弃。这个功能在Git 1.7.0之后开始提供,老版本的Git还是使用squash动作吧。

可以通过修改变基任务文件中各个提交的先后顺序,进而改变最终变基后提交的先后顺序。

可以修改变基任务文件,删除包含相应提交的行,这样该提交就不会被应用,进而在变基后的提交中被删除。

有了对交互式变基命令的理解,就可以开始新的“回到未来”之旅了。

确认舞台已经布置完毕。


$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)执行交互式变基操作。


$git rebase-i D^


(2)自动用编辑器修改文件。文件内容如下:


pick 3488f2c move.gitignore outside also works.

pick 48456ab add hello.h

pick b6f0b0a modify hello.h

Rebase b3af728..b6f0b0a onto b3af728

#

Commands:

p,pick=use commit

r,reword=use commit,but edit the commit message

e,edit=use commit,but stop for amending

s,squash=use commit,but meld into previous commit

f,fixup=like "squash",but discard this commit's log message

x<cmd>,exec<cmd>=Run a shell command<cmd>,and stop if it fails

#

If you remove a line here THAT COMMIT WILL BE LOST.

However,if you remove everything,the rebase will be aborted.

#


(3)将第一行删除,使得上面的文件看起来像是这样(省略井号开始的注释):


pick 48456ab add hello.h

pick b6f0b0a modify hello.h


(4)保存退出。

变基自动开始,即刻完成。

显示下面的内容:


Successfully rebased and updated refs/heads/master.


(5)看看日志。当前分支master已经完成变基,消灭了“坏蛋D”。


$git log—oneline—decorate-6

78e5133(HEAD,master)modify hello.h

11eea7e 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)同样执行交互式变基操作,不过因为要将C和D压缩为一个,因此变基从C的父提交开始。


$git rebase-i C^


(2)自动用编辑器修改文件。文件内容如下(忽略井号开始的注释):


pick b3af728 ignore object files.

pick 3488f2c move.gitignore outside also works.

pick 48456ab add hello.h

pick b6f0b0a modify hello.h


(3)修改第二行(提交D),将动作由pick修改为squash。修改后的内容如下:


pick b3af728 ignore object files.

squash 3488f2c move.gitignore outside also works.

pick 48456ab add hello.h

pick b6f0b0a modify hello.h


(4)保存退出。自动开始变基操作,在执行到squash命令设定的提交时,进入提交前的日志编辑状态。

显示的待编辑日志如下。很明显C和D的提交说明显示在了一起。


This is a combination of 2 commits.

The first commit's message is:

ignore object files.

This is the 2nd commit message:

move.gitignore outside also works.


(5)保存退出,即完成squash动作标识的提交合并及后续变基操作。

看看提交日志,看到提交C和提交D都不见了,代之以一个融合后的提交出现。


$git log—oneline—decorate-6

c0c2a1a(HEAD,master)modify hello.h

c1e8b66 add hello.h

db512c0 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


(6)可以看到融合C和D的提交日志实际上是两者日志的融合。在前面单行显示的日志中看不出来。


$git cat-file-p HEAD^^

tree 00239a5d0daf9824a23cbf104d30af66af984e27

parent d71ce9255b3b08c718810e4e31760198dd6da243

author Jiang Xin<jiangxin@ossxp.com>1291720899+0800

committer Jiang Xin<jiangxin@ossxp.com>1292153393+0800

ignore object files.

move.gitignore outside also works.


时光旅行结束了,多么神奇的Git啊。