この質問は サブディレクトリを個別のGitリポジトリに接続する に基づいています
単一のサブディレクトリをデタッチする代わりに、カップルをデタッチしたいと思います。たとえば、現在のディレクトリツリーは次のようになります。
/apps
/AAA
/BBB
/CCC
/libs
/XXX
/YYY
/ZZZ
代わりにこれが欲しいです:
/apps
/AAA
/libs
/XXX
--subdirectory-filter
のgit 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
ここで自分の質問に答える...多くの試行錯誤の後。
git subtree
と git-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-A
とmaster-B
の2つのブランチが作成され、それぞれがステッチリポジトリの1つのコンテンツを保持します。それらを組み合わせてクリーンアップするには:
git checkout master-A
git pull . master-B
git checkout master
git branch -d master-A
git branch -d master-B
今、私はこれがどのように/いつ起こるかよくわかりませんが、最初のcheckout
とpull
の後、コードは魔法のようにmasterブランチにマージされます(ここで何が起こっているのかについての洞察!)
newRepo
コミット履歴を調べると、変更セットがapps/AAA
とlibs/XXX
の両方に影響を与えたときに重複があることを除いて、すべてが期待どおりに機能しているようです。重複を削除する方法があれば、それは完璧でしょう。
サブシェルを処理し、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
計画では、個々のディレクトリを独自のリポジトリに分割し、それらをマージします。次の手動の手順では、オタクに使用するスクリプトを使用せず、コマンドを理解しやすくしており、余分な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-histories
をgit 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つ以上ある場合は続行します。
_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
組み込みeval
(extglob
は使用不可)を使用するインデックスフィルターではなく、シェルによって評価されます。詳細については、 gitでシェルオプションを有効にする方法 を参照してください。
git splits
は、 jkeatingのソリューション に基づいてgit拡張として作成したgit branch-filter
のラッパーであるbashスクリプトです。
それはまさにこの状況のために作られました。エラーについては、git splits -f
オプションを使用してバックアップを強制的に削除してみてください。 git splits
は新しいブランチで動作するため、現在のブランチは書き換えられないため、バックアップは無関係です。詳細についてはreadmeを参照し、リポジトリのコピー/クローンで使用してください(念のため!)。
git splits
。ディレクトリをローカルブランチに分割します#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
空のリポジトリをどこかに作成します。パスが[email protected]:simpliwp/xyz.git
であるGitHubにxyz
という空のリポジトリを作成したと仮定します。
新しいレポジトリにプッシュします。 #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
新しく作成したリモートリポジトリを新しいローカルディレクトリに複製します。#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
この問題を正確に解決するためにgitフィルターを作成しました。 git_filterという素晴らしい名前があり、次のgithubにあります。
https://github.com/slobobaby/git_filter
優れたlibgit2に基づいています。
多くのコミット(〜100000)で大きなリポジトリを分割する必要があり、git filter-branchに基づくソリューションの実行には数日かかりました。 git_filterは同じことをするのに1分かかります。
うん。 -f
への後続の呼び出しでfilter-branch
フラグを使用して、その警告を上書きすることにより、バックアップを強制的に上書きします。 :)それ以外の場合は、解決策があると思います(つまり、filter-branch
で不要なディレクトリを一度に削除します)。
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