35.4 Git版本库整理

Git提供了太多武器进行版本库的整理,可以将一个Git版本库改头换面成另外一个Git版本库。

使用交互式变基操作,将多个提交合并为一个(参见第2篇第12章“12.3.3时间旅行三”小节)。

使用StGit,合并提交及更改提交(参见第3篇第20章"20.3.1 StGit"小节)。

借助变基操作,抛弃部分历史提交(参见第2篇第12章“12.4丢弃历史”小节)。

使用子树合并,将多个版本库整合在一起(参见第4篇“第24章 子树合并”)。

使用git-subtree插件,将版本库的一个目录拆分出来成为独立版本库的根目录(参见第4篇第24章"24.5.4 git subtree split"小节)。

但是有些版本库重整工作如果使用上面的工具会非常困难,而采用另外一个还没有被用到的Git命令git filter-branch却可以做到事半功倍。看看使用这个新工具来实现下面的这几个任务是多么的简单和优美。

(1)将版本库中某个文件彻底删除[1]。即凡是有该文件的提交都逐一做出修改,撤出该文件。


$git filter-branch—tree-filter 'rm-f filename'——all


(2)更改历史提交中某一提交者的姓名及邮件地址。


$gitfilter-branch—commit-filter'

if["$GIT_AUTHOR_NAME"="Xin Jiang"];then

GIT_AUTHOR_NAME="Jiang Xin"

GIT_AUTHOR_EMAIL="jiangxin@ossxp.com"

GIT_COMMITTER_NAME="$GIT_AUTHOR_NAME"

GIT_COMMITTER_EMAIL="$GIT_AUTHOR_EMAIL"

f

git commit-tree "$@";

'HEAD


(3)为没有包含签名的历史提交添加签名。


$git filter-branch-f—msg-filter'

signed=false

while read line;do

if echo$line|grep-q Signed-off-by;then

signed=true

f

echo$line

done

if!$signed;then

echo""

echo "Signed-off-by:YourName<your@email.address>"

f

'HEAD


通过上面的例子,可以看出命令git filter-branch针对不同的过滤器提供可执行脚本,从不同的角度对Git版本库进行重构。该命令的用法如下:


git filter-branch[—env-filter<command>][—tree-filter<command>]

[—index-filter<command>][—parent-filter<command>]

[—msg-filter<command>][—commit-filter<command>]

[—tag-name-filter<command>][—subdirectory-filter<directory>]

[—prune-empty]

[—original<namespace>][-d<directory>][-f|—force]

[—][<rev-list options>…]


这条命令异常复杂,但是大部分参数是用于提供不同接口的,因此还是比较好理解的。

该命令最后的<rev-list>参数提供要修改的版本范围,如果省略则相当于HEAD指向的当前分支。也可以使用—all来指代所有引用,但是要在—all和前面的参数间使用分隔符“—”。

运行git filter-branch命令改写分支之后,被改写的分支会在refs/original中对原始引用做备份。对于在refs/original中已有备份的,该命令拒绝执行,除非使用-f或—force参数。

其他的后面可带命令脚本<command>的参数(如—env-filter<command>),为git filter-branch命令提供相应的编程接口,从不同的角度实现对Git版本库的过滤。下面针对各个过滤器分别进行介绍。

35.4.1 环境变量过滤器

参数—env-filter用于设置一个环境变量过滤器。该过滤器用于修改环境变量,对特定的环境变量的修改会改变提交。下面的示例可用于修改作者/提交者的邮件地址[2]


$git filter-branch—env-filter'

an=" $GIT_AUTHOR_NAME"

am=" $GIT_AUTHOR_EMAIL"

cn=" $GIT_COMMITTER_NAME"

cm=" $GIT_COMMITTER_EMAIL"

if[" $cn"="Kanwei Li"];then

cm=" kanwei@gmail.com"

f

if["$an"="Kanwei Li"];then

am="kanwei@gmail.com"

f

export GIT_AUTHOR_EMAIL=$am

export GIT_COMMITTER_EMAIL=$cm

'


这个示例和本节一开始介绍的更改作者/提交者信息的示例功能相同,但是使用了不同的过滤器,你可以根据喜好自由选择。

[1]这里使用的命令并非最优实现,后面会介绍一个运行得更快的命令。

[2]http://kanwei.com/code/2009/03/29/fixing-git-email.html