12.4 丢弃历史

历史有的时候会成为负担。例如一个人使用的版本库有一天需要作为公共版本库多人共享,最早的历史可能不希望或者没有必要继续保持存在,需要一个抛弃部分早期历史提交的精简的版本库以用于和他人共享。再比如用Git做文件备份,不希望备份的版本过多而导致不必要的磁盘空间占用,同样会有精简版本的需要:只保留最近的100次提交,抛弃之前的历史提交。那么应该如何操作呢?

使用交互式变基当然可以完成这样的任务,但是如果历史版本库有成百上千个,把成百上千个版本的变基动作中有pick的修改为fixup可真的很费事,实际上Git有更简便的方法。

现在DEMO版本库有如下的提交记录:


$git log—oneline—decorate

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

7161977 delete trash files.(using:git add-u)

2b31c19(tag:old_practice)Merge commit 'acc2f69'

acc2f69 commit in detached HEAD mode.

4902dc3 does master follow this new commit?

e695606 which version checked in?

a0c641e who does commit?

9e8a761 initialized.


如果希望把里程碑A(c024f34)之前的历史提交全部清除,可以这样操作:基于里程碑A对应的提交构造一个根提交(即没有父提交的提交),然后再将master分支在里程碑A之后的提交变基到新的根提交上,实现对历史提交的清除。

由里程碑A对应的提交构造出一个根提交至少有两种方法。第一种方法是使用git commit-tree命令,可以进行如下操作。

(1)查看里程碑A指向的目录树。

用A^{tree}语法访问里程碑A对应的目录树。


$git cat-file-p A^{tree}

100644 blob 51dbfd25a804c30e9d8dc441740452534de8264b README


(2)使用git commit-tree命令直接从该目录树创建提交。


$echo "Commit from tree of tag A."|git commit-tree A^{tree}

8f7f94ba6a9d94ecc1c223aa4b311670599e1f86


(3)命令git commit-tree的输出是一个提交的SHA1哈希值。查看这个提交。会发现这个提交没有历史提交,是我们需要的根提交。


$git log—pretty=raw 8f7f94b

commit 8f7f94ba6a9d94ecc1c223aa4b311670599e1f86

tree 3f9b9459e9c0532ff6e3c16c3098c947d55fba41

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

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

Commit from tree of tag A.


另外一个方法是使用git hash-object命令,从里程碑A指向的提交建立一个根提交。可以进行如下操作。

(1)查看里程碑A指向的提交。

用A^0语法访问里程碑A对应的提交。


$git cat-file commit A^0

tree 3f9b9459e9c0532ff6e3c16c3098c947d55fba41

parent 63992f05a72865754809cc0772a9e1fbf134e380

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

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

README is from welcome.txt.


(2)将上面的输出(里程碑A指向的提交)过滤掉以parent开头的行,并将结果保存到一个文件中。


$git cat-file commit A^0|sed-e '/^parent/d'>tmpfile


(3)运行git hash-object命令,将文件tmpfile作为一个commit对象写入对象库。


$git hash-object-t commit-w—tmpfile

4d387fd42a023aadcf0702907b848444e8c4429c


(4)上面执行git hash-object命令的输出结果就是写入Git对象库中的新的提交对象ID。查看会发现该提交就是我们需要的新的根提交。


$git log—pretty=raw 4d387fd

commit 4d387fd42a023aadcf0702907b848444e8c4429c

tree 3f9b9459e9c0532ff6e3c16c3098c947d55fba41

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

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

README is from welcome.txt.


无论采用哪种方法创建了新的根提交后,就可以执行变基操作,将master分支在里程碑A之后的提交变基到新的根提交上。下面的示例选择第一种方法创建的根提交8f7f94b。

(1)执行变基,将master分支里程碑A之后的提交全部迁移到根提交8f7f94b上。


$git rebase—onto 8f7f94b A master

First,rewinding head to replay your work on top of it…

Applying:Hello world initialized.

Applying:ignore object files.

Applying:add hello.h

Applying:modify hello.h


(2)查看日志看到当前master分支的历史已经精简了。


$git log—oneline—decorate

2584639(HEAD,master)modify hello.h

30fe8b3 add hello.h

4dd8a65 ignore object files.


12.4 丢弃历史 - 图1