1. ホーム
  2. git

[解決済み] サブディレクトリの git リポジトリをマージする

2023-03-17 11:46:27

質問

リモート git リポジトリを、作業中の git リポジトリのサブディレクトリとしてマージしたいと思います。出来上がったリポジトリには、2つのリポジトリのマージされた履歴が含まれ、またマージされたリポジトリの各ファイルがリモートリポジトリにあったような履歴を保持するようにしたいと思います。で述べられているようにサブツリー戦略を使用してみました。 サブツリー マージ戦略の使用方法 にあるようにサブツリー戦略を使用してみましたが、この手順を実行すると、結果として得られるリポジトリには確かに 2 つのリポジトリのマージされた履歴が含まれますが、リモートから来る個々のファイルは履歴を保持していません(それらのいずれかに `git log' を実行すると、" Merged branch..." というメッセージが表示されるだけです)。

また、私はサブモジュールを使用したくありません。なぜなら、2 つの結合された git リポジトリがもう別々であって欲しくないと思っているからです。

リモートリポジトリから来る個々のファイルがその履歴を保持したまま、サブディレクトリとして別のリポジトリにリモートgitリポジトリをマージすることは可能ですか?

どんな助けにも非常に感謝します。

EDITです。 現在、マージされたリポジトリの履歴を書き換えるために git filter-branch を使用する解決策を試しています。うまくいっているように見えますが、もう少しテストする必要があります。私の発見を報告するために戻ってきます。

編集 2: 私が git のサブツリー戦略で使用した正確なコマンドを示すことで、私自身がより明確になることを願っています。 現在作業している git リポジトリを A とし、そのサブディレクトリとして A に組み込みたい git リポジトリを B とします。それは次のようなものでした。

git remote add -f B <url-of-B>
git merge -s ours --no-commit B/master
git read-tree --prefix=subdir/Iwant/to/put/B/in/ -u B/master
git commit -m "Merge B as subdirectory in subdir/Iwant/to/put/B/in."

これらのコマンドの後、subdir/Iwant/to/put/B/in ディレクトリに移動すると、B のすべてのファイルが表示されますが git log はコミットメッセージ "Bをsubdir/Iwant/to/put/B/inのサブディレクトリとしてマージする." を表示するだけで、Bにある彼らのファイル履歴は失われています。

が動作するように見えるのは(gitの初心者なので間違っているかもしれませんが)以下のようなものです。

git remote add -f B <url-of-B>
git checkout -b B_branch B/master  # make a local branch following B's master
git filter-branch --index-filter \ 
   'git ls-files -s | sed "s-\t\"*-&subdir/Iwant/to/put/B/in/-" |
        GIT_INDEX_FILE=$GIT_INDEX_FILE.new \
                git update-index --index-info &&
        mv "$GIT_INDEX_FILE.new" "$GIT_INDEX_FILE"' HEAD 
git checkout master
git merge B_branch

上のfilter-branchのコマンドは git help filter-branch から取ったもので、サブディレクトリのパスを変更しただけです。

どのように解決するのですか?

何が起こっているかについての詳細な説明を受けた後、私はそれを理解し、いずれにせよ一番下に回避策があります。 具体的には、私は、リネーム検出が --prefix によるサブツリー マージに惑わされていることが原因だと考えています。 以下は私のテストケースです。

mkdir -p z/a z/b
cd z/a
git init
echo A>A
git add A
git commit -m A
echo AA>>A
git commit -a -m AA
cd ../b
git init
echo B>B
git add B
git commit -m B
echo BB>>B
git commit -a -m BB
cd ../a
git remote add -f B ../b
git merge -s ours --no-commit B/master
git read-tree --prefix=bdir -u B/master
git commit -m "subtree merge B into bdir"
cd bdir
echo BBB>>B
git commit -a -m BBB

git ディレクトリ a と b を作成し、それぞれにいくつかのコミットを入れておきます。 サブツリーのマージを行い、そして新しいサブツリーで を行い、新しいサブツリーで最終的なコミットを行います。

実行中 gitk (z/a)を実行すると、履歴が表示されることがわかります。 実行中 git log を実行すると、履歴が表示されることがわかります。 しかし、特定のファイルを見るには問題があります。 git log bdir/B

さて、私たちができるトリックがあります。 特定のファイルのリネーム前の履歴を --follow を使って見ることができるのです。 git log --follow -- B . これは良いことですが、マージ前の履歴とマージ後の履歴をリンクすることに失敗しているので、あまり良くありません。

M と -C で遊んでみましたが、特定の 1 つのファイルを追跡させることはできませんでした。

ですから、解決策は、サブツリーのマージの一部として行われるリネームについてgitに伝えることだと感じています。 残念ながら、git-read-tree はサブツリーのマージについてかなりうるさいので、一時ディレクトリを通して作業する必要がありますが、コミットする前にそれを取り除くことができます。 その後で、完全な履歴を見ることができます。

まず、"A" リポジトリを作成し、いくつかのコミットを行います。

mkdir -p z/a z/b
cd z/a
git init
echo A>A
git add A
git commit -m A
echo AA>>A
git commit -a -m AA

次に、"B" リポジトリを作成し、いくつかのコミットを行います。

cd ../b
git init
echo B>B
git add B
git commit -m B
echo BB>>B
git commit -a -m BB

そして、これを実現するためのコツは : サブディレクトリを作成し、その中にコンテンツを移動することで、Gitにリネームを認識させることができます。

mkdir bdir
git mv B bdir
git commit -a -m bdir-rename

リポジトリ "A"に戻り、"B"の内容をフェッチしてマージします。

cd ../a
git remote add -f B ../b
git merge -s ours --no-commit B/master
# According to Alex Brown and pjvandehaar, newer versions of git need --allow-unrelated-histories
# git merge -s ours --allow-unrelated-histories --no-commit B/master
git read-tree --prefix= -u B/master
git commit -m "subtree merge B into bdir"

マージされたことを示すために

cd bdir
echo BBB>>B
git commit -a -m BBB

全履歴が連結鎖で保存されていることを証明する。

git log --follow B

このようにすると履歴が残りますが、問題は、古い "b" レポを実際に維持していて、時々そこからマージしている場合(実際には第三者が別途管理しているレポだとします)、その第三者はリネームを行っていないため問題が発生することです。 新しい変更をリネームしたbのバージョンにマージしなければなりませんが、これがスムーズにいかないことが心配です。 しかし、b が消えてしまうのであれば、あなたの勝ちです。