Gitのサブモジュールでは面倒そうな、頻繁に更新される別のリポジトリを取り込む方法としてサブツリーマージを行うラッパーであるgit subtreeコマンドを使う練習を始めた。どちらかというと「参照する」要素の強いサブモジュールに対して、サブツリーは「切り分ける」や「取り込む」という感じなんじゃないかと理解している。全般的に間違ってそうで怖い。
「切り分ける」、つまりリポジトリのサブディレクトリを別のリポジトリにしたい場合は、単純なケースだと親にあたる方で.gitignoreや.git/info/excludeを使ってサブディレクトリを除外してやれば良い。でもこの場合、両方のリポジトリで関連した変更がある時にそれぞれのリポジトリでコミットしてやらないとならないので面倒くさい。
「取り込む」場合はサブモジュールが基本なわけだけど、他で作業して戻ってきてたりする必要があるし、サブモジュールの更新ほど面倒くさい作業はない。理解が足らないとミスもしやすいと思う。
サブツリーを使うと、親となるリポジトリでまとめてコミットしたり、子になるリモート・リポジトリを取り込んだサブディレクトリでシームレスに作業を行ったりしたりできる。そしてその後、サブツリーとして登録したリモート・リポジトリへ、サブディレクトリのファイルへのコミットだけを反映させることもできる。
$ git subtree push --prefix=subtree_dir/ subtree_origin master
まさにこれを目的としていた。つまり作業を一元化し、特定のディレクトリへ加わった変更だけを別のリポジトリに投げる、という感じ。親の方にプライベートなファイルが存在していたりして公開したくない(バージョン管理はしたい)が、サブディレクトリは普通に公開しておきたいとかそういうの。単一のリポジトリで普通に扱うので、ミスはしづらいような気がする。
git remote addとgit subtree addを使う。
$ git remote add -f subtree_origin https://example.com/bob/foo.git $ git subtree add --prefix=foo/ --squash subtree_origin master
取り込みたいリモート・リポジトリをsubtree_originとして登録し、サブディレクトリfoo/として取り込む。リモートに反映させる時に死ぬので必ず--squashを省略すると履歴を継承できるので、サブモジュールや上述のようなリポジトリのネストから移行する場合はつけない方が良さそう。履歴が読みづらくなるのがちょっとアレだけど、logコマンドに--date-orderオプションを付ければなんとかなる気がする(またはSourceTreeで)。--squashを付けるべきとした方が良さそう。
ファイルへ加えた変更のコミットはサブツリーのファイルだろうとそうでなかろうと普通にコミットするだけ。
$ git add file-a.txt $ git add foo/file-b.txt $ git commit -m 'Modified!'
単純にひとつのリポジトリとみなして操作すれば良い。
git pushとgit subtree pushを使う。
$ git push origin master $ git subtree push --prefix=foo/ subtree_origin master
これでoriginにはfile-a.txtとfoo/file-b.txtへ加わった変更が、subtree_originへはfoo/file-b.txtへ加わった変更のみが反映される。
サブツリー関係はウェブで調べてもノイズが多くてよくわからなかった。古い情報も勿論混ざっているし、新しく書かれた記事でも古い情報を元にしていたり、混ぜこぜになっていたり。前提知識がなさすぎて取捨選択できなかったので、一旦全て忘れてとにかくgit subtree -hとcontirib/subtree/git-subtree.txtだけを読んで、色々試してみて覚えることにした。
このウェブサイト全体のリポジトリをBitbucketでプライベートなリポジトリとして作り、ウェブログやドキュメント、CSSのリポジトリをsubtreeとして混ぜるみたいな形で色々試行錯誤してみて、なんとなく理解できたような気がする。git reset --hardとgit push -fを数十回繰り返した価値はあった。
誰かが管理してるリポジトリはサブモジュールで特定のコミットに縛り、自分がメインで管理しているリポジトリはサブツリーでいじりやすいようにしておく、というのが良いのかな。他のリポジトリを取り込むという利用だけでなく、サンプルを置いたディレクトリをgh-pagesブランチを使ったサブツリーにして、GitHub Pagesの生成をシームレスに行うとかも出来そう。