gitでは様々な方法でコミットログを書き換えることができます。
その一例として誤ったブランチに対して行った変更を正しいブランチへ移す方法を紹介します。
これまで「新機能Xを追加する」という設定で以下のトピックについて解説していました:
これにはまず
$ git branch topic-x master
$ git checkout topic-x
としてこの作業用のトピックブランチを作成してそちらで作業を行うのが普通です。
しかし git branch
を実行したところで安心してしまい、git checkout
を忘れて全く違うブランチで作業を行ってしまう
というミスは時々やってしまいます
(git checkout -b
という方法もありますがここではそれも忘れていたとしましょう)。
例えば以下のような状況だったとしましょう:
$ git branch
master
topic-x
* topic-y
$ git log --oneline --decorate --graph
* c000022 (HEAD, topic-y) Update to use X
* c000012 Refactor - Sort using statements
* b000001 Add a neat feature X into the library
* 0100002 Update to use Y
* 0100001 Add a great feature Y in to the library
* 0000000 (master, topic-x) Initial import
日本語に訳すと以下の通りです:
本来ならば以下のような形なっているところです:
$ git branch
master
まだまだtopic-xの作業内容はどこにも公開されていないので、
いまのうちにコミットログを綺麗な形に書き換えるとしましょう。
しかし具体的にはどうすればよいでしょうか。
今回の場合、作業内容は既にtopic-yに対してコミットしていますので、以下の手順で修正することにします:
git cherry-pick
というコマンドがあり、これを使うと既存のコミットで行った変更と同じことを行うことができます。
今回の場合は以下のコマンドでtopic-yで行ってしまった変更をtopic-xに「コピー」することができます:
$ git checkout topic-x
$ git cherry-pick b000001
Finished one cherry-pick.
[topic-x d000001] Add a neat feature X into the library
1 files changed, 21 insertions(+), 0 deletions(-)
$ git cherry-pick c000012
Finished one cherry-pick.
[topic-x d000012] Refactor - Sort using statements
1 files changed, 5 insertions(+), 3 deletions(-)
$ git cherry-pick c000022
Finished one cherry-pick.
[topic-x d000022] Update to use X
2 files changed, 10 insertions(+), 5 deletions(-)
$ git log --oneline --decorate --graph master topic-x topic-y
* d000022 (HEAD, topic-x) Update to use X
* d000012 Refactor - Sort using statements
* d000001 Add a neat feature X into the library
| * c000022 (topic-y) Update to use X
| * c000012 Refactor - Sort using statements
| * b000001 Add a neat feature X into the library
| * 0100002 Update to use Y
| * 0100001 Add a great feature Y in to the library
|/
* 0000000 (master) Initial import
git cherry-pick
は「コピー」元のコミットのIDを引数に取りますが、
いちいち各コミットのIDを指定するのは面倒ですから、
以下のようにしてブランチを起点に「コピー」するコミットを指定することもできます:
$ git cherry-pick topic-y~2 # topic-yの2個前のコミット。この場合はb000001と同じ。
$ git cherry-pick topic-y~1 # topic-yの1個前のコミット。この場合はc000012と同じ。
$ git cherry-pick topic-y~0 # topic-yの0個前のコミット。topic-yと同じ。
$ git cherry-pick topic-y # topic-yの指すコミット。この場合はc000022と同じ。
gitで1つのコミットを複数のコミットに分割する
でも紹介したgit reset
コマンドを使えば可能です。
今回の場合は以下のコマンドでtopic-yから意図せず行ってしまったコミットを取り除くことができます:
$ git checkout topic-y
$ git reset --hard HEAD~3
HEAD is now at 0100002 Update to use Y
$ git log --oneline --decorate --graph master topic-x topic-y
* d000022 (topic-x) Update to use X
* d000012 Refactor - Sort using statements
* d000001 Add a neat feature X into the library
| * 0100002 (HEAD, topic-y) Update to use Y
| * 0100001 Add a great feature Y in to the library
|/
* 0000000 (master) Initial import
コミットの分割の例では作業ディレクトリの内容はそのまま残しておきたかったのでgit reset HEAD~1
などを使いましたが、
今回の場合は作業ディレクトリの内容も含めて全て捨て去りたいのでgit reset --hard HEAD~3
のように --hard
オプションが必要です。
これで理想的な形にコミットログを書き換えることができました。
やりましたね。
(続く)