如何从一个 git repo 中分离出部分目录或文件至独立的 git repo
如何从一个 git repo 中分离出部分目录或文件至独立的 git repo
如何从一个 git repo 中分离出部分目录或文件至独立的 git repo
之前我把我所有的 emacs lisp 扩展都放在同一个 git repo -- xwl-elisp 下面。例如 其中有扩展 smart-compile.el, buffer-action.el, ga/ 等。这样的方式方便了我自己, 但是对于只对我其中某个扩展感兴趣的人说,就显得很不方便。刚好最近有个叫 Jonas 的 家伙在 github 创建了一个 emacsmirror,用来镜像他所找到的 elisp (emacs lisp) 扩 展,对跟我“抱怨”:能不能把我的扩展分别放到各自的 git repo 了,而不是一锅粥一 样。实际上,之前 binjo 也跟我建议过,说可以用 submodule 来管理。现在我对 submodule 还算会用了,所以剩下我关心的问题就是,从 xwl-elisp 分离出来后,如何才 能保留各个扩展的 commit log,同时又能不混入别的扩展。下面介绍一下我是怎么做的。
1 分离子目录
分离一个子目录相对简单,直接使用 filter-branch 中的 --subdirectory-filter 即 可, 但要注意在新的 branch 或者 clone 里操作,这样不影响原始版本,以便进行多次分 离。
$ git checkout -b new master $ git filter-branch -f --prune-empty --subdirectory-filter DIR $ cd .. $ git clone GIT_REPO new -b new $ ## update .git/config
这样你就能得到一个只含有 DIR 的干净 commit log.
2 分离单个文件
比分离目录稍微麻烦点,用的是 filter-branch 的 --index-filter. (--tree-filter 也可 以,只是会比较慢)。具体步骤:
1). 创建 branch
$ git check -b new master
2). 列出所有出现过的文件名
$ git log --full-history --name-status | grep -E "A\t" | cut -d ' ' -f2 > /tmp/remove.orig $ cp /tmp/remove.orig /tmp/remove
3). 在 /tmp/remove 中删除想要分离的文件名
$ sed -i s/FILE// /tmp/remove
4). 分离
$ git filter-branch -f --prune-empty --index-filter 'git rm --cached --ignore-unmatch $(cat /tmp/remove)'
5). 扫尾
$ cd .. $ git clone GIT_REPO new -b new $ ## update .git/config
3 清理空间
如果一些 tags 已经没意义,也可以删除:
$ git tag -l | xargs git tag -d
经前面的分离操作后,一切看起来完好。但如果你算一下 repo 大小,发现分离出后的 repo 占的空间并没有相应减少多少。我们还需要对 repo 做一下 "GC":
$ git reset --hard
$ git for-each-ref --format="%(refname)" refs/original/ | xargs -n 1 git update-ref -d
$ git reflog expire --expire=now
$ git gc --aggressive --prune=now
另外,倘若你的 repo 中含有中文文件名(这个问题纠缠了我几天,后来 Jonas 帮忙指 出了问题),那么在执行 filter-branch 之前,需要先把中文文件给删掉,否则会影响 后面的一些操作,结果就是你得到的 commit log 只得到部分清理,并不是完全干净的。 例如:
$ git filter-branch --prune-empty -f --index-filter 'git rm --cached --ignore-unmatch "wubi/QQ五笔系统词库_2010_6_13.txt"'
4 一点感想
别的问题:我发现一些不相干的 branch merge 信息依然会出现分离后的 commit log中, 不知道有没有办法将它们也清理掉。这次使用 filter-branch 的经历令我又一次感受到了 git 的强大。在 git 眼里,历史总能如此轻易的被改写...
参考: http://stackoverflow.com/questions/359424/detach-subdirectory-into-separate-git-repository