首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >将以前合并的代码分离为多个git存储库

将以前合并的代码分离为多个git存储库
EN

Stack Overflow用户
提问于 2012-04-21 10:16:11
回答 2查看 437关注 0票数 2

这个问题听起来与这里提出的许多问题相似,但却是惊人的不同。

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

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

如果我想保存历史的话,这使得拆分存储库变得非常困难。使用git-filter分支似乎是正确的方法,但所有这些似乎都破坏了存储库的一部分,并用它们截断历史。

编辑添加了来澄清,这里有一个小例子,假装我在存储库的根中。假设存储库如下所示:

代码语言:javascript
复制
foo/
    bar/
        file.txt
    baz/

现在,假设我编辑file.txt的内容。然后我将它重命名为newfile.txt。然后我再次编辑内容。然后,我将该文件移出bar/并进入baz/。我的存储库现在如下所示:

代码语言:javascript
复制
foo/
    bar/
    baz/
        newfile.txt

好的,现在假设我想将baz/拆分到它自己的存储库中。使用git过滤器分支或使用git子树拆分将丢失newfile.txt的所有提交消息和历史记录,当它位于bar/内部和命名为file.txt时。

我理解,签出一个历史修订版可能是疯狂的;它可能引用一些名为../bar/的内容,或者它可能引用了一个无效目录,该目录并不存在,而且异常失败。只要我能在任何特定的版本中查看文件内容,我就不在乎。

端编辑

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

  1. 克隆存储库N次,保存我想要的存储库中的文件夹(通过git rm-ing其他文件夹),并以某种方式删除任何最终不引用头中文件的修订。我意识到这会有一些负面的副作用,因为签出旧版本不会提供有意义的代码基础--我不在乎。为了做到这一点,我需要找到一种方法,从HEAD中存在的所有文件中获取所有路径,这可以用一个丑陋的script.
  2. Build来实现--这是存储库在每个索引期间的样子的某种历史索引。使用树筛选器,并将不匹配的文件从各自的版本中删除。然后,删除HEAD.

中没有出现的文件或从文件中降下来的文件。

是否有可能找到所有没有出现在头部的文件,并删除与它们相关的任何历史记录?我不关心已经被删除的文件的复活,这似乎是我问题的症结所在。

另一种解决办法也将受到赞赏。我对git还比较陌生,所以我可能遗漏了一些显而易见的东西。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2012-04-23 21:47:36

最后,我不得不在几个阶段中完成这个任务。

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

代码语言:javascript
复制
git log --pretty=format: --name-only --diff-filter=A | sort -u

使用它,我能够确定我想要保存的文件在某个点或另一个点的位置。在我的例子中,他们在整个生命周期都居住在存储库中的四个单独的目录中。我使用这些信息手动创建正则表达式,如(?:^foo|^bar/baz|^qux/(?:moo|woof))。这与我想保存的目录相匹配。

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

代码语言:javascript
复制
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过滤器分支来使用我的新过滤器和正则表达式来保留我想要的路径。

代码语言:javascript
复制
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
EN

Stack Overflow用户

发布于 2012-04-21 13:25:15

您应该考虑安装和使用git子树https://github.com/apenwarr/git-subtree,它处理拆分的repos并很好地保存历史记录。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/10258054

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档