存档

文章标签 ‘scm’

git remove file from revision history

2009年3月18日 dipplum 没有评论

最近学习 git,真是非常强大,非常不好用的软件。

一个常见的需求:有一个文件,包含敏感信息,不小心加进git库里面去了。希望从源码库的所有历史记录中删除该文件的所有版本。

其他的源码管理系统也会有类似的功能。如果是 CVS,就到源码库目录下直接删除 RCS 文件。如果是 Subversion,则是用 svnadmin dump 之后,用 import filter 再倒入。

git-filter-branch 命令包含各种回溯修改 revision history 的功能,经过尝试,获得心得若干:

  • 首先,git 的命令可扩展性很强,但对初学者来说,要记忆的命令太长,像看天书;
  • 其次,git 往往一个命令完成不了任务,需要多个命令配合,但文档对此语焉不详,初学者无所适从;
  • 最后,也是最难的,git 中概念太多,名字非常不直观,从传统源码管理的层次理解起来台阶太高。

为此,写了几个 bash function,封装 git 的上述功能。用法如下

  • git-rewrite-commits exclude_files_list:将文件从 reivision history 中删除,可指定要删除的目录;
  • git-prune-commits:上面的命令执行之后,可能出现空的 commit 对象,用这个命令可以清空这类对象;
  • git-rewrite-msg regex:regex 是可被 sed 接受的正则表达式,用该命令修改所有的 commit 注释;

如果直接使用 filter-branch 从 git 历史中删除文件,会将工作拷贝中的文件也删除。而如果只是想把清除 git 库中的敏感文件,用户可能希望保留工作拷贝中的这些文件。使用 git-rewrite-commits 可以起到保留要删除的文件的效果。

另外,由于 git 是分布式源码管理,用这种做法因此光删除一个库中的文件历史并不够。为了避免下游的库将删除后的敏感文件又 push 回来,应该对 git 库的所有副本执行相同的清除操作,这样才保险。

附:相关 bash 脚本,加入 .bashrc 中生效

function git-remove-refs()
{
  if [ -z $@ ] ; then
    echo "Usage: git-remove-refs refs_name"
    return
  fi
  git for-each-ref --format="%(refname)" refs/$1 | xargs -I Ref bash -c "echo removing git ref: Ref; git update-ref -d Ref"
}
 
function git-backup-files()
{
  FILES=`git ls-files $@`
  for File in $FILES
  do
    echo save backup for $File
  done
  GIT_BACKUP_TMP=`tempfile`.tar.gz
  tar czf $GIT_BACKUP_TMP $FILES
  echo backup file saved at $GIT_BACKUP_TMP
}
 
function git-restore-files()
{
  if [ -f $GIT_BACKUP_TMP ] ; then
    echo backup file $GIT_BACKUP_TMP restored
    tar xzf $GIT_BACKUP_TMP
    unset GIT_BACKUP_TMP
  fi
}
 
function git-rewrite-commits()
{
  if [ -z $@ ] ; then
    echo "Usage: git-rewrite-commits files_to_remove"
    return
  fi
  git-backup-files $@
  git filter-branch --index-filter "git ls-files -z $@ | git update-index --remove -z --stdin"
  git-remove-refs original
  git-restore-files
}
 
function git-prune-commits()
{
  if [ ! -z $@ ] ; then
    echo "Usage: git-prune-commits"
    return
  fi
  git filter-branch --prune-empty
  git-remove-refs original
}
 
function git-rewrite-msg()
{
  if [ -z $@ ] ; then
    echo "Usage: git-rewrite-msg regex_pattern"
    return
  fi
  git filter-branch --msg-filter "sed -e \"$1\""
  git-remove-refs original
}
分类: 乱七八糟 标签: ,
This work by dipplum is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 2.5 China Mainland.