web-dev-qa-db-ja.com

多くのサブディレクトリを新しい別個のGitリポジトリにデタッチします

この質問は サブディレクトリを個別のGitリポジトリに接続する に基づいています

単一のサブディレクトリをデタッチする代わりに、カップルをデタッチしたいと思います。たとえば、現在のディレクトリツリーは次のようになります。

/apps
  /AAA
  /BBB
  /CCC
/libs
  /XXX
  /YYY
  /ZZZ

代わりにこれが欲しいです:

/apps
  /AAA
/libs
  /XXX

--subdirectory-filtergit filter-branch引数は、指定されたディレクトリ以外のすべてを最初に実行したときに削除されるため、機能しません。すべての不要なファイルに--index-filter引数を使用することは(退屈ではありますが)うまくいくと思いましたが、2回以上実行しようとすると、次のメッセージが表示されます。

Cannot create a new backup.
A previous backup already exists in refs/original/
Force overwriting the backup with -f

何か案は? TIA

121
prisonerjohn

ここで自分の質問に答える...多くの試行錯誤の後。

git subtreegit-stitch-repo の組み合わせを使用してこれを行うことができました。これらの指示は以下に基づいています。

最初に、私が保持したいディレクトリを独自の個別のリポジトリに取り出しました:

cd origRepo
git subtree split -P apps/AAA -b aaa
git subtree split -P libs/XXX -b xxx

cd ..
mkdir aaaRepo
cd aaaRepo
git init
git fetch ../origRepo aaa
git checkout -b master FETCH_HEAD

cd ..
mkdir xxxRepo
cd xxxRepo
git init
git fetch ../origRepo xxx
git checkout -b master FETCH_HEAD

次に、新しい空のリポジトリを作成し、最後の2つをインポート/ステッチしました。

cd ..
mkdir newRepo
cd newRepo
git init
git-stitch-repo ../aaaRepo:apps/AAA ../xxxRepo:libs/XXX | git fast-import

これにより、master-Amaster-Bの2つのブランチが作成され、それぞれがステッチリポジトリの1つのコンテンツを保持します。それらを組み合わせてクリーンアップするには:

git checkout master-A
git pull . master-B
git checkout master
git branch -d master-A 
git branch -d master-B

今、私はこれがどのように/いつ起こるかよくわかりませんが、最初のcheckoutpullの後、コードは魔法のようにmasterブランチにマージされます(ここで何が起こっているのかについての洞察!)

newRepoコミット履歴を調べると、変更セットがapps/AAAlibs/XXXの両方に影響を与えたときに重複があることを除いて、すべてが期待どおりに機能しているようです。重複を削除する方法があれば、それは完璧でしょう。

19
prisonerjohn

サブシェルを処理し、ext globを使用する代わりに(kynanが示唆したように)、このはるかに単純なアプローチを試してください。

git filter-branch --index-filter 'git rm --cached -qr --ignore-unmatch -- . && git reset -q $GIT_COMMIT -- apps/AAA libs/XXX' --Prune-empty -- --all
135
David Smiley

単純なgitコマンドを使用した手動ステップ

計画では、個々のディレクトリを独自のリポジトリに分割し、それらをマージします。次の手動の手順では、オタクに使用するスクリプトを使用せず、コマンドを理解しやすくしており、余分なN個のサブフォルダーを別の単一のリポジトリーにマージするのに役立ちます。

除算

元のレポが:original_repoであると仮定しましょう

1-アプリの分割:

git clone original_repo apps-repo
cd apps-repo
git filter-branch --Prune-empty --subdirectory-filter apps master

2-分割ライブラリ

git clone original_repo libs-repo
cd libs-repo
git filter-branch --Prune-empty --subdirectory-filter libs master

3つ以上のフォルダーがある場合は続行します。これで、2つの新しいgitリポジトリが作成されます。

アプリとライブラリのマージによる征服

3-新しいレポを準備します。

mkdir my-desired-repo
cd my-desired-repo
git init

そして、少なくとも1つのコミットを行う必要があります。次の3行をスキップする必要がある場合、最初のリポジトリはリポジトリのルートの下にすぐに表示されます。

touch a_file_and_make_a_commit # see user's feedback
git add a_file_and_make_a_commit
git commit -am "at least one commit is needed for it to work"

一時ファイルがコミットされると、後のセクションのmergeコマンドは期待どおりに停止します。

a_file_and_make_a_commitのようなランダムファイルを追加する代わりに、ユーザーのフィードバックから取得して、.gitignoreまたはREADME.mdなどを追加することを選択できます。

4-最初にアプリリポジトリをマージします。

git remote add apps-repo ../apps-repo
git fetch apps-repo
git merge -s ours --no-commit apps-repo/master # see below note.
git read-tree --prefix=apps -u apps-repo/master
git commit -m "import apps"

これで、新しいリポジトリ内にappsディレクトリが表示されます。 git logは、関連するすべての履歴コミットメッセージを表示する必要があります。

注:Chrisがコメントで以下に述べたように、gitの新しいバージョン(> = 2.9)の場合、--allow-unrelated-historiesgit mergeで指定する必要があります

5-同じ方法で次にlibsリポジトリをマージします:

git remote add libs-repo ../libs-repo
git fetch libs-repo
git merge -s ours --no-commit libs-repo/master # see above note.
git read-tree --prefix=libs -u libs-repo/master
git commit -m "import libs"

マージするリポジトリが3つ以上ある場合は続行します。

参照: gitで別のリポジトリのサブディレクトリをマージ

36
chfw

_filter-branch_を複数回実行するのはなぜですか?すべてを一度に実行できるため、強制する必要はありません(これを機能させるには、シェルでextglobを有効にする必要があります)。

_git filter-branch --index-filter "git rm -r -f --cached --ignore-unmatch $(ls -xd apps/!(AAA) libs/!(XXX))" --Prune-empty -- --all
_

これは、不要なサブディレクトリのすべての変更を取り除き、すべてのブランチとコミットを保持する必要があります(_--Prune-empty_により、プルーニングされたサブディレクトリ内のファイルにのみ影響する場合を除きます)-重複したコミットなどの問題はありません.

この操作の後、不要なディレクトリは_git status_によって追跡されていないものとしてリストされます。

$(ls ...)は必須です。 extglobは、sh組み込みevalextglobは使用不可)を使用するインデックスフィルターではなく、シェルによって評価されます。詳細については、 gitでシェルオプションを有効にする方法 を参照してください。

27
kynan

「git splits」git拡張機能を使用する

git splits は、 jkeatingのソリューション に基づいてgit拡張として作成したgit branch-filterのラッパーであるbashスクリプトです。

それはまさにこの状況のた​​めに作られました。エラーについては、git splits -fオプションを使用してバックアップを強制的に削除してみてください。 git splitsは新しいブランチで動作するため、現在のブランチは書き換えられないため、バックアップは無関係です。詳細についてはreadmeを参照し、リポジトリのコピー/クローンで使用してください(念のため!)

  1. インストール git splits
  2. ディレクトリをローカルブランチに分割します#change into your repo's directory cd /path/to/repo #checkout the branch git checkout XYZ
    #split multiple directories into new branch XYZ git splits -b XYZ apps/AAA libs/ZZZ

  3. 空のリポジトリをどこかに作成します。パスが[email protected]:simpliwp/xyz.gitであるGitHubにxyzという空のリポジトリを作成したと仮定します。

  4. 新しいレポジトリにプッシュします。 #add a new remote Origin for the empty repo so we can Push to the empty repo on GitHub git remote add Origin_xyz [email protected]:simpliwp/xyz.git #Push the branch to the empty repo's master branch git Push Origin_xyz XYZ:master

  5. 新しく作成したリモートリポジトリを新しいローカルディレクトリに複製します。
    #change current directory out of the old repo cd /path/to/where/you/want/the/new/local/repo #clone the remote repo you just pushed to git clone [email protected]:simpliwp/xyz.git

7
AndrewD

この問題を正確に解決するためにgitフィルターを作成しました。 git_filterという素晴らしい名前があり、次のgithubにあります。

https://github.com/slobobaby/git_filter

優れたlibgit2に基づいています。

多くのコミット(〜100000)で大きなリポジトリを分割する必要があり、git filter-branchに基づくソリューションの実行には数日かかりました。 git_filterは同じことをするのに1分かかります。

7
slobobaby

うん。 -fへの後続の呼び出しでfilter-branchフラグを使用して、その警告を上書きすることにより、バックアップを強制的に上書きします。 :)それ以外の場合は、解決策があると思います(つまり、filter-branchで不要なディレクトリを一度に削除します)。

3
Jakob Borg
git clone [email protected]:thing.git
cd thing
git fetch
for originBranch in `git branch -r | grep -v master`; do
    branch=${originBranch:7:${#originBranch}}
    git checkout $branch
done
git checkout master

git filter-branch --index-filter 'git rm --cached -qr --ignore-unmatch -- . && git reset -q $GIT_COMMIT -- dir1 dir2 .gitignore' --Prune-empty -- --all

git remote set-url Origin [email protected]:newthing.git
git Push --all
1