将先前连接的代码分成多个git存储库

时间:2012-04-21 10:16:12

标签: git git-filter-branch

这个问题与许多人提出的问题听起来类似,但这是令人讨厌的不同。

我有一个git存储库,它曾经是一个svn存储库(曾经是一个cvs存储库)。这包含的数据可以追溯到1999年左右。

现在是时候将这个存储库拆分为几个不同的存储库,保留所有这些丰富的历史记录。但是,存储库的结构经常发生变化。所有当前项目都来自一个基础项目,该项目发展到一些项目,这些项目缩减为两个项目,然后再次增长。代码已被移动但从未重复过;它现在已经在几个成熟的项目中找到了最后的休息场所。

如果我想保留历史记录,这会非常难以拆分存储库。使用git-filter-branch似乎是正确的方法,但所有这些似乎都会破坏存储库的某些部分并使用它们截断历史记录。

编辑添加为了澄清,这是一个小例子,假装我在存储库的根目录中。假设存储库看起来像这样:

foo/
    bar/
        file.txt
    baz/

现在让我说我编辑file.txt的内容。然后我将其重命名为newfile.txt。然后我再次编辑内容。然后我将此文件移出bar/并移至baz/。我的存储库现在看起来像这样:

foo/
    bar/
    baz/
        newfile.txt

好的,现在让我们说我想将baz/拆分到自己的存储库中。使用git filter-branch或使用git子树拆分将丢失newfile.txtbar/内的所有提交消息和历史记录,并将其命名为file.txt

我知道查看历史修订可能会很疯狂;它可能会引用一个名为../bar/的东西,或者它可能引用一个不存在的无效目录并且会非常失败。只要我能查看任何特定版本的文件内容,我就不在乎了。

结束编辑

似乎我想要做的事情有两条路径:

  1. 将存储库克隆N次,保留我想要存储在该存储库中的文件夹(通过git rm-ing其他文件夹),并以某种方式破解最终不会引用HEAD中的文件的任何修订。我意识到这将产生一些负面的副作用,因为检查旧版本不会提供有意义的代码库 - 我不在乎。为了做到这一点,我需要找到一种方法来获取HEAD中存在的所有文件的所有路径,我可以使用丑陋的脚本。

  2. 在每个索引中构建存储库外观的某种历史索引。使用树过滤器并删除各自修订版中不匹配的文件。然后,删除HEAD中未出现或来自文件的文件。

  3. 是否可以找到未出现在HEAD中的所有文件并删除与其相关的所有历史记录?我不关心恢复长期删除的文件,这似乎是我的问题的关键。

    备选解决方案也将受到赞赏。我对git比较新,所以我可能会遗漏一些明显的东西。

2 个答案:

答案 0 :(得分:1)

我最终必须在几个阶段的过程中这样做。

首先,我得到了存储库中找到的所有文件路径的列表:

git log --pretty=format: --name-only --diff-filter=A | sort -u

使用它,我能够确定我想要保留的文件在哪一处停留。就我而言,他们一生中一直居住在存储库中的四个独立目录中。我使用此信息手动创建正则表达式,例如(?:^foo|^bar/baz|^qux/(?:moo|woof))。这与我想保留的目录相匹配。

然后我创建了一个perl脚本来保存这些路径名和包含它们的任何父路径名。

use Path::Class;    
if(scalar(@ARGV) < 1) { die "no regex"; }

my $regex = qr/$ARGV[0]/;    
my @want; my @remove; my $last = undef; my $lastrm = undef;

while(<STDIN>) {
    chomp;
    my $d = $_;
    if( $d =~ $regex ) {
        if( ! defined($last) || ! dir($last)->subsumes(dir($d)) ) {
            $last = $d;
            push @want, $d;
        }
    } else {
        if( ! defined($last) || ! dir($last)->subsumes(dir($d)) ) {
           push @remove, $d;
        }
    }
}
foreach $rm (@remove) {
    my $no_rm = 0;
    if( defined($lastrm) && dir($lastrm)->subsumes($rm) ) {
        $no_rm++;
    } else {
        foreach $keep (@want) {
            if( dir($rm)->subsumes(dir($keep)) ) {
                $no_rm++;
            }
        }
    }
    if( $no_rm == 0 ) {
        print "$rm\n";
        $lastrm = $rm;
    }
}

最后,我使用git filter-branch在我的正则表达式中使用我的新过滤器来保留我想要的路径。

git filter-branch --prune-empty --index filter '
    git ls-tree -d -r -t --name-only --full-tree $GIT_COMMIT 
    | sort | /path/to/filter.pl "(?:regex|of|paths)" 
    | xargs -n 50 git rm -rf --cached --ignore-unmatch' -- --all

排序是必要的,因为它确保perl脚本以适当的层次结构获取目录。

我希望这会对某人有所帮助,因为我花了很多很长时间才想到这一点。 :)

答案 1 :(得分:0)

你应该考虑安装和使用git子树https://github.com/apenwarr/git-subtree来处理拆分回购和保存历史。