実務でgit rebase -i を使う必要がありそうだったので、予習のために使ってみました。
シチュエーション
- リモートにpush済み
- コードレビューをしてもらった後に修正コミットがたくさんできている
この状況でコミットをまとめていきます。
ただ、リモートにpush済みのコミットをgit rebase -iでまとめるのは本来NGです。しかし、自分が作業をしているブランチが「絶対に自分しかいじらない」のであればコミットをまとめても大丈夫な場合があります。例えば私の職場では、コードレビュー後にコミットを1つにまとめるという文化がありますが、コミットをまとめないで欲しいというところもあるようです。コミットをまとめたいと思った時は、チームに確認してからにしましょう。
それでは、実際にやっていきたいと思います。
コミットlogを確認
まずはコミットlogを確認します。
$ git log --oneline
2fd949a (HEAD -> create_list, origin/create_list) リスト1を修正
968362c リスト2を修正
d196455 リスト1、リスト2を作成
f6bd217 (origin/title) titleを記載
「リスト2を修正」と「リスト1を修正」が修正コミットです。この2つの修正コミットと「リスト1、リスト2を作成」を1つにまとめます。
git rebase -i でまとめたいコミットを指定
git rebase -i でまとめたいコミットを指定します。その際二つの指定方法があるので紹介します。
git rebase -i HEAD~<数字>
数字の部分にはまとめたいコミットの数を指定します。今回であれば3つのコミットをまとめたいので、3を指定します。
git rebase -i <コミットのハッシュ値>
こちらはコミットのハッシュ値を指定します。しかし、この指定方法には少し癖があります。HEADを使って指定する場合は「まとめたいコミットの数」とわかりやすいですが、コミットのハッシュ値を指定する場合は「まとめたいコミットの一つ前のハッシュ値」を指定します。例えば今回の場合だと、「リスト1を修正」〜「リスト1、リスト2を作成」までをまとめたいので、その前のコミットである「titleを記載」のハッシュ値を指定します。
git rebase -i を実行してコミットをまとめる
git rebase -i を実行します。
$ git rebase -i HEAD~3 もしくは git rabase -i f6bd217
すると、以下のようにエディタが起動します。
pick d196455 リスト1、リスト2を作成
pick 968362c リスト2を修正
pick d196455 リスト1を修正
# Rebase 3c1cd16..d196455 onto 3c1cd16 (3 commands)
#
# Commands:
# p, pick <commit> = use commit
# r, reword <commit> = use commit, but edit the commit message
# e, edit <commit> = use commit, but stop for amending
# s, squash <commit> = use commit, but meld into previous commit
# f, fixup <commit> = like "squash", but discard this commit's log message
# x, exec <command> = run command (the rest of the line) using shell
# b, break = stop here (continue rebase later with 'git rebase --continue')
# d, drop <commit> = remove commit
# l, label <label> = label current HEAD with a name
# t, reset <label> = reset HEAD to a label
# m, merge [-C <commit> | -c <commit>] <label> [# <oneline>]
# . create a merge commit using the original merge commit's
# . message (or the oneline, if no original merge commit was
# . specified). Use -c <commit> to reword the commit message.
#
# These lines can be re-ordered; they are executed from top to botto
インサートモードに切り替えて、修正コミットの「pick」を「s」に変更・保存します。エディタにも書いてありますが、この「s」はsquashのsです。
pick d196455 リスト1、リスト2を作成
s 968362c リスト2を修正 ⬅︎ 変更
s d196455 リスト1を修正 ⬅︎ 変更
# Rebase 3c1cd16..d196455 onto 3c1cd16 (3 commands)
#
# Commands:
# p, pick <commit> = use commit
# r, reword <commit> = use commit, but edit the commit message
# e, edit <commit> = use commit, but stop for amending
# s, squash <commit> = use commit, but meld into previous commit
# f, fixup <commit> = like "squash", but discard this commit's log message
# x, exec <command> = run command (the rest of the line) using shell
# b, break = stop here (continue rebase later with 'git rebase --continue')
# d, drop <commit> = remove commit
# l, label <label> = label current HEAD with a name
# t, reset <label> = reset HEAD to a label
# m, merge [-C <commit> | -c <commit>] <label> [# <oneline>]
# . create a merge commit using the original merge commit's
# . message (or the oneline, if no original merge commit was
# . specified). Use -c <commit> to reword the commit message.
#
# These lines can be re-ordered; they are executed from top to botto
するとコミットメッセージを編集するためのエディタが開きます。
# This is a combination of 3 commits.
# This is the 1st commit message:
リスト1、リスト2を作成
# This is the commit message #2:
リスト2を修正
# This is the commit message #3:
リスト1を修正
# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
#
# Date: Wed Dec 11 23:52:55 2019 +0900
#
# interactive rebase in progress; onto f53176d
# Last commands done (3 commands done):
# squash 968362c リスト2を修正
# squash d196455 リスト1を修正
# No commands remaining.
コミットメッセージは「リスト1、リスト2を作成」だけあればいいので、他の2つは削除します。
# This is a combination of 3 commits.
# This is the 1st commit message:
リスト1、リスト2を作成
# This is the commit message #2:
⬅︎ 削除
# This is the commit message #3:
⬅︎ 削除
# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
#
# Date: Wed Dec 11 23:52:55 2019 +0900
#
# interactive rebase in progress; onto f53176d
# Last commands done (3 commands done):
# squash 968362c リスト2を修正
# squash d196455 リスト1を修正
# No commands remaining.
この変更を保存するとrebaseが完了です。以下のように表示されれば問題なくrebaseできています。
Successfully rebased and updated refs/heads/create_list.
git log --oneline でlogを見てみると、コミットが1つにまとまっているのがわかると思います。
最後に、リモートにpushをします。ここで要注意なのが、rebaseをしたことでコミットのハッシュ値が変わっているので、pushができなくなっています。この場合、git push に-f オプションを付けて、強制的にpushすることができます。
$ git push -f origin create_list:create_list
これでGitHubを確認すると、コミットが1つにまとまっていると思います。
補足
git push -fなのですが、リモートの内容を破壊的に書き換えてしまうため、実はあまり多用することはおすすめできません。冒頭で、リモートのコミットをまとめるのが本来NGと書いたのは、実はこのgit push -f をする必要があるからでした。なので、git push -f を実行する時は、「絶対に自分以外いじらないブランチ」で作業をしているという確証のもと、実行するようにしてください。