なんとな~くしあわせ?の日記

「そしてそれゆえ、知識そのものが力である」 (Nam et ipsa scientia potestas est.) 〜 フランシス・ベーコン

Git rebase --onto --root で過去の歴史を改変する

f:id:panzer-jagdironscrap1:20150301001506p:plain

あるリポジトリの最初のコミット以前に歴史を継ぎ足したい、という需要があるかもしれません。
Gitならできます、それ。さっそくやってみましょう。

リポジトリAの歴史
コミットの歴史のイメージ

[ NewDeal first commit ] - [ commit 1 ] - [ commit 2 ] - [ commit 3 ] - [ HEAD ]
↑first commit以前のコミットをNewDealリポジトリに入れ込みたい

リポジトリBの歴史
  • リポジトリBは黒歴史的なコードが入っており、1000回コミットされている
  • これをOldDealリポジトリと呼ぶことにする、コミッターはHooverさん、こいつはまったくフーバーだぜ
コミットの歴史のイメージ

[ OldDeal first commit ] - [ commit 1 ] - [ commit 2 ] - ... - [ commit 999 ] - [ HEAD ]

改変後のコミットのイメージ

[ OldDeal first commit ] - ... - [ commit 999 ] - [ HEAD ] - [ NewDeal first commit ] - [ commit 1 ] - ...

具体的なコマンド

まずは、新しいリポジトリソースコードを取得する

$ git clone https://github.com/Roosevelt/NewDeal.git
$ cd NewDeal/

・新しいリポジトリoldという名前でリモートのURLを登録する、これで古いソースを取得できる
・fetchしてソースを取得
oldという名前でローカルのブランチを作成

$ git remote add old https://github.com/Hoover/OldDeal.git
$ git fetch old
$ git checkout -b old old/master

・今回のメインのオペレーション、rebaseしてOldDealの尻尾とNewDealの頭をくっつける
git push -fはこれまでのリポジトリの歴史を完全に書き換えてしまうので、共同開発者がいる場合は気をつけてください*1

$ git rebase --onto old --root master

・大抵の場合rebaseしたあとにmergeが必要だと促されます
・OldDealの尻尾とNewDealの頭をどうつなぐか、その結果を決めるコミットを作れということです
・ちまちま手で直すのは苦痛なので、便利コマンドを紹介
あと、マージ完了した後にpullとかやるのは絶対にヤメましょう、死にます

$ git checkout --ours (ファイル名) カレントを採用する
$ git checkout --theirs (ファイル名) リモートを採用する


Gitマージ解除 - Qiita

・最後に git push -f
・「世界は、再構成される――!」

$ git push -f

*1:というのは、forkしているソースのpull req & mergeができなくなります。rebaseした前後のコードは全く違う歴史だと判断されるようです。