git subtree のやり方をいつも忘れてググるのを繰り返しているので、備忘録で残します。
似たような概念で git submodule というのがありますが、ここではその違い等には言及しません。
git subtree によるサブリポジトリの切り出し方
ユースケース
- リポジトリAを作成しており、内部に hoge ディレクトリがある
- ある日、別のリポジトリBでも、A内のhogeを流用したくなった
- hoge をライブラリ化して、リポジトリBに取り込む
やることのイメージ
- hoge を別リポジトリに切り出す
- B内に、hoge を取り込む
手順
git subtree split でライブラリを切り出す
リポジトリAにて、以下のコマンドを実行します。
ここでは、hogeを切り出す際に使用するブランチ名を、適当に hoge_work
としてます。
git subtree split --prefix=hoge --rejoin -b hoge_work
上記を実行すると、hoge ディレクトリのみに切り出された hoge_work
ブランチが作成されます。
ここで、--rejoin
はいったんおまじない的なものととらえておきましょう。
(--rejoin
することで、後述する更新時に再度split実行する際の速度が速くなるらしいです。こちらに解説があります)
subtree split はそこそこ時間がかかります。かなり大きめのブランチだと、私の環境では1時間弱かかりました・・・
切り出したライブラリをリモートリポジトリに push する
あらかじめ、リモート側(例えば github とか)に、ベア(空の状態)で hoge リポジトリを作っておきます。
そして、そのリモートのパスを、リポジトリA内に登録します。
tortoise git の場合は以下のような感じになります。
以下の通り、push します。
git push hoge hoge_work
切り出したライブラリを取り込む
切り出した hoge リポジトリを、リポジトリBに取り込みます。
ここからは、リポジトリBで操作します。
取り込む方法としては、subtree で取り込む方法と submodule で取り込む方法があると思いますが、今回は submodule で取り込む方法を記載します。
(ちなみに、経験上、リポジトリAとリポジトリBの両方でsubtree扱いにするとカオスになるのでお勧めしません・・・)
私は普段 tortoise git を使っているので、リポジトリBにて以下のように操作します。
以上で、リポジトリB内にリポジトリAの hoge
ディレクトリを取り込み完了です。
git subtree で運用している際の更新方法
ユースケース
2パターンのユースケースが想定されます。
- リポジトリAで更新した内容を、ライブラリhogeのリポジトリBに反映させる
- リポジトリAをある程度バージョンアップしていった場合、hogeディレクトリも更新されていることが想定される
- リポジトリA内の hoge を再切り出しして、リポジトリBに反映させる
- ライブラリhogeで更新した内容を、リポジトリAに反映させる
手順
私が知りうる限り、ユースケース2の方が断然処理が早いです。というか、ユースケース1のやり方はかなり時間がかかります。
ただし、実際の運用ではユースケース1のパターンの方が多いので、subtree による運用はそこそこ時間的コストがかかるとういことを理解しておく必要があるのかなと思います。(私がちゃんと理解できていないかもですが・・・)
ユースケース1の場合
リポジトリからライブラリを再度切り出す
以下のコマンドを実行し、リポジトリAからライブラリ hoge の再切り出しを行います。
git subtree split --prefix=hoge --rejoin --onto hoge_work -b new_hoge_work
前述の --rejoin
により、前回切り出したときの記録がコミット履歴に残っていて、それを目印に、不要な切り出し処理がスキップされるらしいです。しかし、私が実行してみた限り、処理速度は体感あまり変わらない感じでした。
また、--onto
を付けることにより、前回切り出したときのブランチ(hoge_work
)から派生された履歴が作られます。
(それの何が良いかというと、リモートに切り出し済みのhogeリポジトリと、ブランチの互換性があるのでpushしやすいということです)
切り出したコミットログから、ブランチを更新する
ここがハマりポイントになるのですが、何故か上記の切り出しのままでは、hoge_work
ブランチは更新されていません。
切り出しにより作成されるのは、あくまで hoge_work
から派生されたコミットログだけみたいです。
上述の切り出しが成功した際には、コミットログは下記のようになっているはずです。
ここで、Split ~~ into commit ~
となっているコミットが hoge_work
から派生されたものになります。
なので、このコミットログを明示的に hoge_work
にマージしてやる必要があります。
切り出したライブラリをリモートリポジトリに push する
初回の切り出し字と同じ手順となりますので、詳細は省略します。
git push hoge hoge_work
切り出したライブラリを取り込む(更新する)
更新した hoge リポジトリを、リポジトリBに反映させます。
ここからは、リポジトリBで操作します。
後述した通り、ここでもリポジトリBでは submodule による運用を想定します。
リポジトリB内でサブモジュールとなっている hoge ディレクトリ内で pull を実行すると更新されます。
ユースケース2の場合
ライブラリのリポジトリを取り込む
あらかじめ、リモートのhogeリポジトリが更新済みであるとします。
ここでは、リポジトリAへsubtreeとして更新する場合を説明します。(リポジトリBへsubmoduleとして更新する方法はユースケース1と重複するので省略します)
リポジトリAにて、以下のコマンドを実行します。
git subtree merge --prefix=hoge --squash hoge hoge_work
--squash
はなくてもいいですが、ないとコミット履歴が思わぬ大きさで膨らむことがあるので、とりあえずつけておくこと推奨です。
なお、--squash
の後の hoge はリモートの名称、そのあとの hoge_work はリモート上のブランチ名です。
この方法の場合、これで更新完了で、時間もほとんどかかりません。
コメント