Gitの履歴を"切断" と "接続" する
他所のリポジトリに一連のコミットを持っていくために履歴を"切断"したいことがある。
もっと簡単な方法もあるかもしれないが、とりあえず filter-branch
ではこれができる。
まとめ
WORKING
ブランチが処理後の HEAD
を指しているとして、
- 切断
FILTER_BRANCH_SQUELCH_WARNING=1 git filter-branch --parent-filter \
'test $GIT_COMMIT = <ルートにしたいコミット> && echo "" || cat' \
<ルートにしたいコミット>^..WORKING WORKING
- 接続
FILTER_BRANCH_SQUELCH_WARNING=1 git filter-branch --parent-filter \
'test $GIT_COMMIT = <ルート> && echo "-p <接続先>" || cat' WORKING
要は、 parent-filter
で GIT_COMMIT
が一致したときに特定の parent を echo
するようなスクリプトを渡せば良い。
filter-branch
での履歴の切断
あるリポジトリの 22a55b9c403fc3f929cc39727a06e44a7db19715^..58f57cda5a1f5be5de34355764263d3c4bea84fe
だけを含む履歴を生成するには:
git branch WORKING 22a55b9c403fc3f929cc39727a06e44a7db19715
FILTER_BRANCH_SQUELCH_WARNING=1 git filter-branch --parent-filter 'test $GIT_COMMIT = 58f57cda5a1f5be5de34355764263d3c4bea84fe && echo "" || cat' 58f57cda5a1f5be5de34355764263d3c4bea84fe^..WORKING WORKING
git update-ref -d refs/original/refs/heads/WORKING
のようにできる。これにより、ブランチ WORKING
が 22a55b9c403fc3f929cc39727a06e44a7db19715
をroot(最初のコミット)とした履歴に書き換えられる。
(警告 WARNING: Ref 'refs/heads/WORKING' is unchanged
は安全に無視できる)
接続
履歴の切断のユースケースとしては、ブランチに対する破壊的な操作 -- git lfs migrate
とか git filter-branch
とかをインクリメンタルに行うことにある。
例えば、↑で切断した 58f57cda5a1f5be5de34355764263d3c4bea84fe
の直前のコミット 58f57cda5a1f5be5de34355764263d3c4bea84fe^
には git lfs migrate
を掛けたブランチ FIRST
が別に存在したとする:
git branch FIRST 58f57cda5a1f5be5de34355764263d3c4bea84fe^
git lfs migrate import --include=*.sls FIRST
↑ の手順でブランチ WORKING
は FIRST..
の履歴のみを含むので、
git lfs migrate import --include=*.sls WORKING
として git lfs migrate
を掛けた後、
git rev-list --reverse --topo-order WORKING | head -1
git rev-list --max-count=1 FIRST
として書き換え後のcommitを求め、
FILTER_BRANCH_SQUELCH_WARNING=1 git filter-branch --parent-filter 'test $GIT_COMMIT = 89f3eb81836abc61220f5735ab7bc82af30e7961 && echo "-p 34a3949c688b39c3a923a3369591d7e4af2214db" || cat' WORKING
のようにしてくっつけることができる。