git-svn で標準レイアウトではない Subversion リポジトリを管理しているとき、サブディレクトリ同士をマージしたくなることがあるのでメモしておく。
簡単に言えば、「2つのサブディレクトリ a と b をマージ」するためには、「『サブディレクトリ a の中身を b の中身で置き換えたブランチ』を作って元々のブランチとマージ」すればよい。
リポジトリに以下のディレクトリがあるとする。ディレクトリの中には適当にいくつかファイルがあると考える。
$REPO/hoge_a/
$REPO/hoge_b/
hoge_b/ は、ある時点で hoge_a/ からコピーして作られたとする。その後 hoge_a/ に変更 A が、 hoge_b/ には別の変更 B が加えられてそれぞれコミットされた。この hoge_a/ に hoge_b/ をマージしたい。どうすればいいか?
このようなときは、歴史を遡って、 hoge_a/ に変更 A ではなく変更 B を加えたブランチを作ればいい。そうすれば「hoge_a/ に変更 A を加えたブランチ」と「hoge_a/ に変更 B を加えたブランチ」のマージに持ち込める。
1. まず hoge_b/ が作られたコミットを探してチェックアウトする。この時点では hoge_a/ と hoge_b/ の中身は同じ。
git checkout -b hoge_a+B ${hoge_b が作られたコミット}
2. master の hoge_b/ の中身を hoge_a+B の hoge_a/ にコピーしてコミットする。
# インデックスの hoge_a/ を消す
git rm -r --cached hoge_a/
# インデックスの hoge_a/ に master の hoge_b/ の中身を読み込む
# つまり、 hoge_a/ に変更 B を適用する
git read-tree --prefix=hoge_a/ master:hoge_b
# インデックスだけが更新された状態なので、インデックスをワーキングツリーにチェックアウトする
git checkout .
# コミット
git commit
これで hoge_a/ に変更 B を加えたブランチが作れた。あとは普通にマージすればいい。
git checkout master
git merge --squash hoge_a+B