2

この質問は、ここで提起された多くの質問と似ていますが、不快なほど異なります。

かつてsvnリポジトリだったgitリポジトリがあります(以前はcvsリポジトリでした)。1999年頃までさかのぼるデータが含まれています。

この 1 つのリポジトリを複数の異なるリポジトリに分割し、この豊富な履歴をすべて保存する時が来ました。ただし、リポジトリの構造は頻繁に変更されています。現在のすべてのプロジェクトは、基本プロジェクトから派生し、それがいくつかのプロジェクトに成長し、2 つのプロジェクトに縮小され、その後再び成長しました。コードは移動されましたが、複製されることはありませんでした。現在、いくつかの成熟したプロジェクトの 1 つで最終的な安息の地を見つけています。

これにより、履歴を保存したい場合、リポジトリの分割が非常に難しくなります。git-filter-branch を使用することは正しいアプローチのように思えますが、これらはすべて、リポジトリの一部をハッキングして履歴を切り捨てているようです。

EDIT ADDED明確にするために、ここに小さな例を示します。私はリポジトリのルートにいるふりをしています。リポジトリが次のようになっているとします。

foo/
    bar/
        file.txt
    baz/

の内容を編集するとしfile.txtます。次に、名前を に変更しnewfile.txtます。その後、再度内容を編集します。次に、このファイルを との間で移動しbar/ますbaz/。私のリポジトリは次のようになります。

foo/
    bar/
    baz/
        newfile.txt

baz/では、独自のリポジトリに分割したいとしましょう。git filter-branch を使用するか、git subtree split を使用するとnewfile.txt、それが内部にbar/あり、名前が付けられたときのすべてのコミット メッセージと履歴が失われfile.txtます。

歴史的なリビジョンをチェックアウトするのは気が狂っているかもしれないことは理解しています。呼び出されたものを参照../bar/するか、存在しない無効なディレクトリを参照して見事に失敗する可能性があります。特定のリビジョンでファイルの内容を見ることができる限り、私は気にしません。

編集終了

私がやりたいことには2つの道があるようです:

  1. リポジトリをN回複製し、そのリポジトリに必要なフォルダーを保存し(git rm-ing他のフォルダーを介して)、最終的にHEADにあるファイルを参照しないリビジョンを何らかの方法でハックします。古いリビジョンをチェックアウトしても意味のあるコードベースが提供されないという点で、これにはいくつかのマイナスの副作用があることを認識しています-気にしません。これを行うには、HEAD に存在するすべてのファイルから派生するすべてのパスを取得する方法を見つける必要があります。これは、醜いスクリプトで行うことができます。

  2. 各インデックス中にリポジトリがどのように見えるかについて、ある種の履歴インデックスを作成します。ツリー フィルターを使用して、それぞれのリビジョンで一致しないファイルを切り取ります。次に、HEAD 内のファイルに表示されないファイルまたはその派生ファイルを削除します。

HEAD に表示されないすべてのファイルを見つけて、それらに関連する履歴を削除することは可能ですか? 長い間削除されていたファイルを復活させることは気にしません。これが私の問題の核心にあるようです。

代替ソリューションも高く評価されます。私は比較的 git に慣れていないので、明らかな何かが欠けている可能性があります。

4

2 に答える 2

1

私は最終的に、いくつかの段階のプロセスでこれを行う必要がありました。

まず、リポジトリで見つかったすべてのファイル パスのリストを取得しました。

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

それを使用して、保持したいファイルがどこにあったかを特定することができました。私の場合、それらは生涯を通じてリポジトリ内の 4 つの個別のディレクトリに存在していました。この情報を使用して、 などの正規表現を手動で作成しました(?:^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 スクリプトが適切な階層でディレクトリを取得するようにするため、並べ替えが必要です。

これを思いつくのに何時間もかかったので、これが誰かの役に立てば幸いです。:)

于 2012-04-23T21:47:36.287 に答える
0

git subtree https://github.com/apenwarr/git-subtreeをインストールして使用することを検討する必要があります。これは、リポジトリの分割と履歴の保持を適切に処理します。

于 2012-04-21T13:25:15.670 に答える