Git でブランチを切り忘れてコミットした時の戻し方

備忘録。

ブランチを切り忘れて master ブランチにコミットした際に、ブランチを切り直してコミットをそちらへ移動させる方法をいつも忘れるのでメモとして残しておく。

解決したい課題

例:

... - A - B - C - D  (master)

コミット A から foo ブランチを切って、B 以降のコミットをそちらへ移動させたい。

... - A (master)
      |
      + - B - C - D (foo)

解決策1: cherry-pick を使う

まずコミット A から foo ブランチを切る

$ git checkout A
$ git checkout -b foo

cherry-pick でコミット B, C, D をコピーする(foo 上のコミットは ID が変わる)

$ git cherry-pick B
$ git cherry-pick C
$ git cherry-pick D

この時点で foo ブランチはあるべき姿になっているが、master ブランチに B, C, D が残っているのでこれらを消す。

$ git checkout master
$ git reset --hard HEAD~3

cherry-pick するコミット ID を間違えないようにするのと、移動後に master ブランチから消すのを忘れないように注意。

解決策2: stashreset を使う

移動させたいコミットが1つだけの時の場合は、こちらの方が楽。

コミットAからfooブランチを切るはずが、忘れてコミットBのみを行ってしまった場合について考える。

... - A - B  (master)

一旦コミットBを reset して stash に変更を保存し、ブランチを切り直してから復元させればよい。

$ git reset HEAD^
$ git stash
$ git checkout -b foo
$ git stash pop
$ git commit -m 'コミットB'

もちろんこの方法でもコミットIDは変わる。

解決策1と違って、元の master ブランチのコミットを消し忘れることがないのがポイント。