git rebase -i で何ができるの?
- 複数のコミットを1つにまとめられる
- コミットの履歴を変更できる
-i は --interactive の略で、「(CLIと)対話的に rebase をできる」といった感じ。
なんのために使うの?
- 「コミット細かくしすぎたからまとめたい(慎重にいきすぎた!)」
- 「コミットログをもうちょっとまとめてきれいにしたい!」
てなとき。
少々クセはあるものの、使い慣れたらとても便利。
git rebase -i 手順例
rebase -i
を実行するため、適当なディレクトリとファイルを作成する。その後git init
し、コミットを重ねる。(コピペしやすいようにシェルの$
は抜いています)
とりあえず以下コマンドをすべてコピペしてターミナルで実行しましょう。
mkdir git-rebase-i cd git-rebase-i git init echo "Python" > sample.txt git add . git commit -m "commit 1" echo "Java" >> sample.txt git add . git commit -m "commit 2" echo "Go" >> sample.txt git add . git commit -m "commit 3" echo "Javascript" >> sample.txt git add . git commit -m "commit 4" echo "Ruby" >> sample.txt git add . git commit -m "commit 5"
いちおうファイルの中身を確認しておく。
cat sample.txt Python Java Go Javascript Ruby
コミットログを確認する。
git log --graph --oneline * a3764df (HEAD -> master) commit 5 * 4eea250 commit 4 * 890a236 commit 3 * 5bea072 commit 2 * bd2030a commit 1
今回は、コミット4と5をまとめてみる。ここで rebase -i を使うのだが、コミット3のハッシュを指定する点に注意する。
#コミット3を指定している git rebase -i 890a236
すると、次のようにエディタが開く。
1 pick 4eea250 commit 4 2 pick a3764df commit 5 3 4 # Rebase 890a236..a3764df onto 890a236 (2 commands) 5 # 6 # Commands: 7 # p, pick <commit> = use commit 8 # r, reword <commit> = use commit, but edit the commit message 9 # e, edit <commit> = use commit, but stop for amending 10 # s, squash <commit> = use commit, but meld into previous commit 11 # f, fixup <commit> = like "squash", but discard this commit's log message 12 # x, exec <command> = run command (the rest of the line) using shell 13 # b, break = stop here (continue rebase later with 'git rebase --continue') 14 # d, drop <commit> = remove commit 15 # l, label <label> = label current HEAD with a name 16 # t, reset <label> = reset HEAD to a label 17 # m, merge [-C <commit> | -c <commit>] <label> [# <oneline>] 18 # . create a merge commit using the original merge commit's 19 # . message (or the oneline, if no original merge commit was 20 # . specified). Use -c <commit> to reword the commit message. 21 # 22 # These lines can be re-ordered; they are executed from top to bottom. 23 # 24 # If you remove a line here THAT COMMIT WILL BE LOST. 25 # 26 # However, if you remove everything, the rebase will be aborted. 27 # 28 # Note that empty commits are commented out ~
今回は4と5をまとめたいので、2行目(コミット5)の先頭のpick
をsquash
に変更する。※s
だけでもOK。
1 pick 4eea250 commit 4 2 squash 3764df commit 5
コミットメッセージを書くようにエディタが再び開くので、適当にメッセージをつける。
#コミットメッセージを書く rebase test #ここから 1 # This is a combination of 2 commits. 2 # This is the 1st commit message: 3 4 commit 4 5 6 # This is the commit message #2: 7 8 commit 5 9 #ここまでは削除してOK 10 # Please enter the commit message for your changes. Lines starting 11 # with '#' will be ignored, and an empty message aborts the commit. 12 # 13 # Date: Sun Aug 9 09:04:30 2020 +0900 ~
Successfully rebased
がでるはず。
[detached HEAD d37bfd3] rebase test Date: Sun Aug 9 09:04:30 2020 +0900 1 file changed, 2 insertions(+) Successfully rebased and updated refs/heads/master.
ログを確認してみよう。
$ git log --graph --oneline #4と5が合体している * d37bfd3 (HEAD -> master) rebase test * 414f7bc commit 3 * a46265f commit 2 * fed9aef commit 1
ファイルを確認してみると、内容自体は変更されていない。rebase -i は成功している。
cat sample.txt Python Java Go Javascript Ruby
応用編
次はもうちょっと実践的にしてみる。挙動をわかりやすくするため、今使っていたディレクトリを削除し、また同じ準備をする。
cd .. rm -rf git-rebase-i
mkdir git-rebase-i cd git-rebase-i git init echo "Python" > sample.txt git add . git commit -m "commit 1" echo "Java" >> sample.txt git add . git commit -m "commit 2" echo "Go" >> sample.txt git add . git commit -m "commit 3" echo "Javascript" >> sample.txt git add . git commit -m "commit 4" echo "Ruby" >> sample.txt git add . git commit -m "commit 5"
$ git log --graph --oneline * 0731f42 (HEAD -> master) commit 5 * a8e0dd3 commit 4 * deb6b4e commit 3 * 0f3b62e commit 2 * aa6bf82 commit 1
今回はつぎのようにコミットをまとめてみる。
- 2と3をまとめる
- 4と5をまとめる
#コミット1を指定する $ git rebase -i aa6bf82
エディタが開く。
1 pick 0f3b62e commit 2 2 pick deb6b4e commit 3 3 pick a8e0dd3 commit 4 4 pick 0731f42 commit 5 5 6 # Rebase aa6bf82..0731f42 onto aa6bf82 (4 commands) 7 # 8 # Commands: 9 # p, pick <commit> = use commi ~
2行目と、4行目の先頭にあるpickを削除し、squashを意味する「s」を記入する。
1 pick 0f3b62e commit 2 2 s deb6b4e commit 3 3 pick a8e0dd3 commit 4 4 s 0731f42 commit 5
この場合、コミットをまとめる作業が2回発生するので、コミットメッセージを書くエディタも2回開く。
1 2&3 #記入 2 3 # This is a combination of 2 commits. 4 # This is the 1st commit message: 5 6 commit 2 7 8 # This is the commit message #2: 9 10 commit 3
1 4&5 #記入 2 3 # This is a combination of 2 commits. 4 # This is the 1st commit message: 5 6 commit 4 7 8 # This is the commit message #2: 9 10 commit 5
保存、終了する。そしてログを確認すると・・・
$ git log --graph --oneline * 1ca741d (HEAD -> master) 4&5 * b1aecb9 2&3 * aa6bf82 commit 1
結果:
- コミット4と5が合体した
- コミット2と3が合体した
これで複数のコミットの固まりをつくることができた。こちらのほうがより実践的かもしれない。
push するときは
今回の作業をするとコミットハッシュが変わるため、普通にpushしてもエラーが出る。
そのため、--force-with-lease origin
オプションをつけてpushする。
git push --force-with-lease origin "branch"
rebase -i を中止したい
CLIにブランチ名を表示するよう設定している場合、rebase の実行中は(master|REBASE-i)
と表示される。
rebase を中止するにはgit rebase --abort
を実行する。
:~/dev/git-rebase-i (master|REBASE-i) $ git rebase --abort ↓ :~/dev/git-rebase-i (master)
感想
これで「あかん、コミット乱発しすぎた・・・」なんてことを心配する必要はなくなりますね👍
参考
今回使った squash 以外にも edit などいろんな方法があるのですが、以下の記事が学習に有効でした。感謝🙏