やりたいこと
コミット履歴の途中にあるバージョン上げコミットを、最新の先頭に持ってきたい。
i7j8k9l バグ修正 (最新の修正)
e4f5g6h 機能Aを追加
a1b2c3d バージョンを1.2.3に上げる ← これを先頭へ
9x8y7z6 その前のコミット
git rebase -i でも並べ替えできるが、エディタ(vim)が開くので操作に迷う。エディタを一切開かずに済ませる方法をまとめる。
前提
- 対象ブランチを他の人が触っていない(force push するため)
- すでに push 済みの履歴を書き換える
手順
1. ハッシュを確認
git log --oneline -10
必要なのは2つ。
- 動かしたいコミットのハッシュ(例:
a1b2c3d) - その1つ前のコミットのハッシュ(例:
9x8y7z6)
2. バックアップブランチを作る
git branch backup/before-rebase
一瞬で終わるので必ずやる。
3. 対象コミットを履歴から外す
git rebase --onto 9x8y7z6 a1b2c3d
結果として a1b2c3d だけが履歴から抜ける。
4. 先頭に付け直す
git cherry-pick a1b2c3d
リベースで履歴から外れても、コミットオブジェクト自体は残っているのでハッシュ指定で拾える。
5. 確認して push
git log --oneline -10
git push --force-with-lease
先頭がバージョン上げコミットになっていること、他のコミットが全部残っていることを確認する。
6. 後片付け
git branch -d backup/before-rebase
数日置いてから消しても構わない。
コマンドまとめ
git log --oneline -10
git branch backup/before-rebase
git rebase --onto <1つ前のハッシュ> <対象ハッシュ>
git cherry-pick <対象ハッシュ>
git log --oneline -10
git push --force-with-lease
失敗したとき
バックアップブランチから戻す。
git reset --hard backup/before-rebase
git push --force-with-lease
バックアップを忘れていても git reflog から元のコミットを辿れる。
注意点
コンフリクトが起きるとエディタが開くことがある
その場合は以下で対応する。
git status # 対象ファイルを確認
# ファイルを修正
git add <ファイル>
git rebase --continue # cherry-pick 中なら git cherry-pick --continue
中断したいときは git rebase --abort / git cherry-pick --abort で元に戻る。
エディタが苦手なら、あらかじめ変えておくと安心。
git config --global core.editor "nano"
# または
git config --global core.editor "code --wait"
--force ではなく --force-with-lease を使う
--force-with-lease は、リモートの先端が自分の認識と一致していればpushし、違っていたら拒否する。他の人のコミットを消す事故を防げる。
他の人が使っているブランチではやらない
main / develop など共有ブランチの場合は、履歴を書き換えず git revert で対応する。