前提
parent リポジトリと child リポジトリがある
目標
- parent リポジトリの /parent_child/ フォルダに child リポジトリをsubtreeとして取り込む
- child リポジトリの更新を /parent_child/に反映する (subtree pull)
- parent_child フォルダの更新を child リポジトリに反映する (subtree push)
こんなかんじ
parent--parenttxt (ここはparent repo)
|-parent_child--child.txt (ここのフォルダに child repoを反映)
手順
1. child リポジトリ の取り込み
- parent リポジトリと child リポジトリを作成する(すでにあれば飛ばす)
mkdir parent && pushd parent && git init && popd
mkdir child && pushd child && git init && popd
pushd parent && touch parent.txt && git add . && git commit -m "parent 1st commit" && popd
pushd child && touch child.txt && git add . && git commit -m "child 1st commit" && popd
- subtreeとして取り込む
cd parent
# child リポジトリを remoteとして登録 -f で fetch も行なっておく
git remote add -f child_remote ../child/
# subtreeとして登録 --squashで一つのコミットとして取り込む
# git subtree add --prefix=`subtreeのフォルダ名` `subtreeに取り込むリポジトリ` `取り込むブランチ` --squash
git subtree add --prefix=parent_child child_remote master --squash
# parent_child フォルダ内がchild リポジトリの構成になっていることを確認
ls parent_child/
# 取り込まれたコミットを確認
git log HEAD^2 -1 -p
2. child リポジトリの更新の反映
- child リポジトリの更新
cd ../child/
# child リポジトリの更新
echo "update text after subtree-add" > child.txt
git commit -am "update child #1"
cd ../parent/
- child リポジトリを parent リポジトリの parent_child フォルダに反映
# child リポジトリの反映
# git subtree pull --prefix `subtreeのフォルダ名` child_remote master --squash
# `subtreeのフォルダ名` の最後が / だとうまく動かないので注意
git subtree pull --prefix parent_child child_remote master --squash
# 反映されたコミットを確認
git log HEAD^2 -1 -p
- childリポジトリの複数のコミットがひとつのコミットとしてparentリポジトリに反映されることを確認
# childリポジトリに二つのコミットを作成
cd ../child/
echo "update text after subtree-add #2" for 2 commit pull >> child.txt
git commit -am "update child #2"
echo "update text after subtree-add #3" for 2 commit pull >> child.txt
git commit -am "update child #3"
cd ../parent/
# child リポジトリの反映
git subtree pull --prefix parent_child child_remote master --squash
# ひとつのコミットとして反映されていることを確認
git log HEAD^2 -1 -p
3. parent リポジトリの parent_child内の変更を childリポジトリへ反映
- bareではないリポジトリへのpushでエラーが出ないように下記を設定
# このサンプルでは bare リポジトリを使っていないため下記コマンドを実行する
# bare に対してpushする場合は不要
cd ../child/
git config receive.denyCurrentBranch ignore
cd ../parent/
- parent_child内にファイルを作成するコミットを2回実行
cd parent_child/
touch parent_add1.txt && git add . && git commit -m "add from parent #1"
touch parent_add2.txt && git add . && git commit -m "add from parent #2"
cd ..
- child リポジトリへ subtree push
git subtree push --prefix parent_child child_remote master
# 反映されていることを確認
cd ../child/ && git log -3 -p
- subtree外のファイルがpush で反映されないことを確認
# subtree内にファイルを作成しcommit
cd ../parent/parent_child/
touch parent_add3.txt && git add . && git commit -m "add from parent #3"
# subtree外にファイルを作成しcommit
cd ..
touch parent_add4.txt && git add . && git commit -m "add from parent #4"
# subtree内とsubtree外にファイルを作成しcommit
# 内
cd parent_child && touch parent_add5.txt
# 外
cd .. && touch parent_add6.txt
git add . && git commit -m "add from parent #5 & #6"
# child リポジトリへ subtree push
git subtree push --prefix parent_child child_remote master
# subutree内の変更のみ反映されていることを確認
# parent_add4.txtの追加コミットが反映されないことを確認 および
# parent_add5.txt,parent_add6.txt を追加したコミットに、
# parent_add6.txt がないことを確認
cd ../child/ && git log -3 -p
まとめ
- subtreeの取り込みは git subtree add
- subtreeとして取り込んだリポジトリの変更は
subtree pull
で簡単に取り込める - subtreeとして取り込んだリポジトリへの変更は
subtree push
で簡単に反映させることができる- 注意点としてsubtree外のファイルを同時に変更したコミットがあった場合、subtree外のファイルはcommitから削除された状態でpushされる
- 挙動が理解できたら subtree コマンド便利そう