28

私は現在、多くのプロジェクトを含む大きな git リポジトリを持っており、それぞれが独自のサブディレクトリにあります。それを個々のリポジトリに分割し、各プロジェクトを独自のリポジトリに分割する必要があります。

私は試したgit filter-branch --prune-empty --subdirectory-filter PROJECT master

ただし、多くのプロジェクト ディレクトリは、その生涯で何度か名前が変更されており、名前のgit filter-branch変更には従わないため、抽出されたリポジトリには、最後の名前変更より前の履歴が事実上ありません。

1 つの大きな git リポジトリからサブディレクトリを効果的に抽出し、そのディレクトリのすべての名前を過去にさかのぼって追跡するにはどうすればよいですか?

4

3 に答える 3

18

@Chronial のおかげで、必要に応じてスクリプトを作成して git リポジトリをマッサージすることができました。

git filter-branch --prune-empty --index-filter '
    # Delete files which are NOT needed
    git ls-files -z | egrep -zv  "^(NAME1|NAME2|NAME3)" | 
        xargs -0 -r git rm --cached -q             
    # Move files to root directory
    git ls-files -s | sed -e "s-\t\(NAME1\|NAME2\|NAME3\)/-\t-" |
        GIT_INDEX_FILE=$GIT_INDEX_FILE.new \
        git update-index --index-info &&
        ( test ! -f "$GIT_INDEX_FILE.new" \
            || mv -f "$GIT_INDEX_FILE.new" "$GIT_INDEX_FILE" )
'

基本的にこれが行うことは次のとおりです。

  1. 必要な 3 つのディレクトリ NAME1、NAME2、または NAME3以外のすべてのファイルを削除します(1 つのプロジェクトは、その存続期間中に NAME1 -> NAME2 -> NAME3 に名前が変更されました)。

  2. これら 3 つのディレクトリ内のすべてをリポジトリのルートに移動します。

  3. 「$GIT_INDEX_FILE.new」が存在するかどうかをテストする必要がありました。これは、svn を git にインポートすると、ファイルなしでコミット (ディレクトリのみのコミット) が作成されるためです。リポジトリが最初に「git svn clone」で作成された場合にのみ必要です。

于 2013-02-08T01:37:52.970 に答える
7

非常に大きなリポジトリがあり、そこから 1 つのフォルダーを抽出する必要がありました。--index-filter完了するまでに8時間かかると予測されていました。代わりに私がしたことは次のとおりです。

  1. フォルダの過去のすべての名前のリストを取得します。old-name私の場合、との 2 つしかありませんでしnew-nameた。
  2. 各名前について:

    $ git checkout master
    $ git checkout -b filter-old-name
    $ git filter-branch --subdirectory-filter old-name
    

    これにより、いくつかの切断されたブランチが作成され、それぞれに名前の 1 つの履歴が含まれます。

  3. filter-old-nameブランチはフォルダーの名前を変更したコミットで終了し、ブランチは同じコミットで開始filter-new-nameする必要があります。(複数の名前変更があった場合も同じことが当てはまります。同じ数のブランチが作成され、それぞれが次のブランチと共有されているコミットがあります。) 1 つはすべてを削除し、もう 1 つはそれを再作成する必要があります。これら 2 つのコミットの内容が同一であることを確認してください。そうでない場合、ファイルは名前が変更されているだけでなく変更されているため、変更をマージする必要があります。(私の場合、この問題はなかったので、解決方法がわかりません。)

    これを確認する簡単な方法は、2 つのコミットのfilter-new-name上にリベースしfilter-old-nameてから 2 つのコミットを一緒に押しつぶすことです。git は、これにより空のコミットが生成されると文句を言うはずです。(予備のブランチでこれを実行してから削除することに注意してください。リベースすると、コミットからコミッター情報が削除されるため、保持したい履歴の一部が失われます。)

  4. 次のステップは、フォルダーの名前を変更した 2 つのコミットをスキップして、 2 つのブランチを一緒に移植することです。(そうしないと、すべてが削除されて再作成されるという奇妙なジャンプが発生します。) これには、2 つのコミットの完全な SHA (40 文字すべて!) を見つけて、新しい名前のブランチのコミットを最初に、古い名前のブランチを最初に、 git の情報に入れることが含まれます。name ブランチの commit 2 番目。

    $ echo $NEW_NAME_SECOND_COMMIT_SHA1 $OLD_NAME_PENULTIMATE_COMMIT_SHA1 >> .git/info/grafts
    

    これを正しく行っていればgit log --graph、新しい履歴の終わりから古い履歴の始まりまでの行が表示されるはずです。

  5. この接ぎ木は現在一時的なものです。まだ履歴の一部ではなく、クローンやプッシュと一緒には続きません。永続的にするには:

    $ git filter-branch
    

    これにより、それ以上の変更を試みることなくブランチが再フィルター処理され、移植が永続的になります (filter-new-nameブランチ内のすべてのコミットが変更されます)。.git/info/graftsこれで、ファイルを削除できるはずです。

このすべてが終わると、filter-new-nameブランチには、フォルダーの両方の名前からのすべての履歴が含まれているはずです。その後、この別のリポジトリを使用したり、別のリポジトリにマージしたり、この履歴でやりたいことが何でもできます。

于 2017-05-15T17:17:41.790 に答える
6

gitにはそのための組み込み機能があるとは思いません。独自のフィルターを作成する必要があります。を使用するだけgit filter-branch --prune-empty --tree-filter YOURSCRIPTです。次に、スクリプトは正しいフォルダーを識別し (おそらく、その中の特定のファイルの名前によって、またはこのプロジェクトが過去に持っていたすべての名前のリストを持っている可能性があります)、他のすべてを削除し、フォルダーの内容を上のレベルに移動する必要があります。 .

リポジトリが非常に大きく、このスクリプトを実行する夜がない場合は、 を使用して同じ効果をはるかに高速に実現できますが、--index-filterそのスクリプトの作成はより複雑になります。インデックスを変更するには、ファイル システムの変更コマンドではなく、git コマンドを使用する必要があります。

于 2013-02-07T22:55:36.657 に答える