2010-05-09 103 views
17

我想要的是类似于this question。不过,我想对被划分为一个单独的回购目录,以保持该回购子目录:如何拆分git存储库,同时保留子目录?

我有这样的:

foo/ 
    .git/ 
    bar/ 
    baz/ 
    qux/ 

而且我想把它分成两个完全独立的库:

foo/ 
    .git/ 
    bar/ 
    baz/ 

quux/ 
    .git/ 
    qux/ # Note: still a subdirectory 

如何在git中做到这一点?

我可以使用this answer中的方法,如果在整个历史中有一些方法可以将所有新回购的内容移动到子目录中。

回答

16

您确实可以使用子目录过滤器,然后使用索引过滤器将内容放回到子目录中,但是为什么要打扰,何时可以自己使用索引过滤器?

下面是从该名男子页的例子:

git filter-branch --index-filter 'git rm --cached --ignore-unmatch filename' HEAD 

这只是删除一个文件名;你想要做的是删除除了给定子目录以外的所有内容。如果你要谨慎,你可以明确地列出每个路径删除,但如果你想只是去全在,你可以做这样的事情:

git filter-branch --index-filter 'git ls-tree -z --name-only --full-tree $GIT_COMMIT | grep -zv "^directory-to-keep$" | xargs -0 git rm --cached -r' -- --all 

我预计可能有一个更优雅的方式;如果有人有东西,请提出建议!

对命令的几个注意事项:

  • 过滤器分支内部设置GIT_COMMIT到当前提交SHA1
  • 我也没有想到--full-tree是必要的,但显然过滤分支运行指标 - .git-rewrite/t目录中的过滤器,而不是回购的顶层。
  • grep可能是矫枉过正,但我​​不认为这是一个速度问题。
  • --all适用于所有裁判;我想你真的想要这样。 (--将其与筛选分支选项分开)
  • -z-0告诉ls-tree,grep和xargs使用NUL终止来处理文件名中的空格。

编辑,很久以后:Thomas有用地建议了一种方法来删除现在空的提交,但它现在已经过时。看看编辑的历史,如果你有一个旧版本的git的,但与现代的混帐,所有你需要做的就是这个选项的策略:

--prune-empty 

这将去除后是空的所有提交应用索引过滤器。

+0

除了嵌套的单引号(我冒昧替换)之外,这个工作几乎完美。唯一的问题是空的提交到现在不存在的目录保留在日志中。我删除了这些使用'git filter-branch -f --commit-filter'if [z $ 1 = z \'git rev-parse $ 3^{tree} \'];然后skip_commit“$ @”;否则git commit-tree“$ @”; fi'“$ @”',我在http://github.com/jwiegley/git-scripts/blob/master/git-remove-empty-commits – Thomas 2010-05-10 18:43:44

+0

@Thomas发现:托马斯:感谢您解决我粗心的错误!此外,您应该能够在与索引过滤器相同的命令中使用提交过滤器。过滤器按照文档中显示的顺序运行; commit-filter自然是在修改提交内容的过滤器之后。您可能还想使用'--remap-to-ancestor',这会导致refs指向跳过的提交将移动到最近的祖先,而不是排除它们。 – Cascabel 2010-05-10 19:01:40

+0

@Jefromi:'index-filter'参数应该更容易表达为'git rm -r -f --cached --ignore-unmatch $(ls!(目录保留))',看我的答案http ://stackoverflow.com/a/8079852/396967和http://stackoverflow.com/a/7849648/396967 – kynan 2011-12-04 14:20:55

3

这是我落得这样做,当我有它自己来解决这个问题:

git filter-branch --index-filter \ 
'git ls-tree --name-only --full-tree $GIT_COMMIT | \ 
grep -v "^directory-to-keep$" | \ 
sed -e "s/^/\"/g" -e "s/$/\"/g" | \ 
xargs git rm --cached -r -f --ignore-unmatch \ 
' \ 
--prune-empty -- --all 

该解决方案是基于Jefromi答案,并在这里SO Detach (move) subdirectory into separate Git repository加上许多意见。

Jeutti的解决方案不适合我的原因是,我的回购站中有文件和文件夹,其名称中包含特殊字符(主要是空格)。另外git rm抱怨不匹配的文件(与--ignore-unmatch解决)。

你可以保持过滤不可知在回购的根不是或者被移动时,该目录:

grep --invert-match "^.*directory-to-keep$" 

最后,你可以用它来过滤文件或目录的固定子集:

egrep --invert-match "^(.*file-or-directory-to-keep-1$|.*file-or-directory-to-keep-2$|…)" 

要清理之后,你可以使用以下命令:

$ git reset --hard 
$ git show-ref refs/original/* --hash | xargs -n 1 git update-ref -d 
$ git reflog expire --expire=now --all 
$ git gc --aggressive --prune=now 
3

我想做类似的事情,但由于我想保留的文件列表相当长,因此使用无数次greps来做这件事是没有意义的。我写了一个脚本,从文件中读取文件列表:

#!/bin/bash 

# usage: 
# git filter-branch --prune-empty --index-filter \ 
# 'this-script file-with-list-of-files-to-be-kept' -- --all 

if [ -z $1 ]; then 
    echo "Too few arguments." 
    echo "Please specify an absolute path to the file" 
    echo "which contains the list of files that should" 
    echo "remain in the repository after filtering." 
    exit 1 
fi 

# save a list of files present in the commit 
# which is currently being modified. 
git ls-tree -r --name-only --full-tree $GIT_COMMIT > files.txt 

# delete all files that shouldn't be removed 
while read string; do 
    grep -v "$string" files.txt > files.txt.temp 
    mv -f files.txt.temp files.txt 
done < $1 

# remove unwanted files (i.e. everything that remained in the list). 
# warning: 'git rm' will exit with non-zero status if it gets 
# an invalid (non-existent) filename OR if it gets no arguments. 
# If something exits with non-zero status, filter-branch will abort. 
# That's why we have to check carefully what is passed to git rm. 
if [ "$(cat files.txt)" != "" ]; then 
    cat files.txt | \ 
    # enclose filenames in "" in case they contain spaces 
    sed -e 's/^/"/g' -e 's/$/"/g' | \ 
    xargs git rm --cached --quiet 
fi 

相当令人惊讶,这竟然是更多的工作比我最初的预期,所以我决定把它张贴在这里。

+1

非常感谢分享!这对我来说是一个测试回购。我还加了'if(“$(cat $ 1)”==“”];然后 回声“排除文件中没有内容” 退出1 fi'来检查提供的文件是否存在。另外,似乎需要提供排除文件的完整路径。 – Denis 2014-02-20 20:35:27

+0

p.s.另外,排除文件应该有最后一行空/垃圾。 – Denis 2014-02-20 20:42:01

1

的清洁器的方法:

git filter-branch --index-filter ' 
       git read-tree --empty 
       git reset $GIT_COMMIT path/to/dir 
     ' \ 
     -- --all -- path/to/dir 

或坚持使用只是核心命令,子在git read-tree --prefix=path/to/dir/ $GIT_COMMIT:path/to/dir为复位。

在rev-list中指定path/to/dir args会尽早修剪,使用这种便宜的过滤器并不重要,但最好避免浪费精力。

相关问题