git commit --fixup と git rebase --autosquash で簡単に commit が整理できて感動した話
Written by @ryysud Apr 2, 2019 00:07 · 2636 words · 6 minutes read
よくある面倒なシチュエーション
PR を作成した際に、開発中にメインとなる実装の commit とは別に、レビューを受けての修正であったり、ちょっとしたリファクタリングであったり、typo 修正などの細かな commit を、PR の中で粛々と対応するシチュエーションは、どなたにも経験があることだと思います。そして、マージ前に git rebase -i を使って commit を整理するにしても、エディタを開き、どれがどれに紐付く commit なのかをマッピングするのは非常に面倒な作業だと思います。
そこで今回は、そんな面倒なシチュエーションを簡単に突破するために、git commit –fixup と git rebase –autosquash を組み合わせて簡単に commit を整理する方法をまとめてみようと思います。
前置き
commit の整理に使うコマンドである git rebase -i を実行した際に、エディタ内部で使用するコマンド群を把握しておくと、ここから先で説明することがすんなり理解できると思うので、こちら を参考に一部抜粋して先に説明しておきます。
コマンド | 説明 |
---|---|
p, pick | commit を残す。こちらで指定された commit は受け身なイメージ。 |
s, squash | 直前の pick となっている commit に統合する。commit message も統合する。 |
f, fixup | 直前の pick となっている commit に統合する。commit message は統合 “しない” 。 |
git rebase -i では、基本となる commit に pick コマンドを、統合したい commit に squash/fixup コマンドをエディタで付与して、最後に統合する流れとなっております。
今回利用する git のバージョン
$ git --version
git version 2.16.1
git commit の –fixup オプションにはどんな作用があるのか?
–fixup オプションは、git rebase –autosquash で使う commit message を作成するオプションで、commit message には fixup!
という prefix が付与されるとのこと。
$ git commit --help
...
--fixup=<commit>
Construct a commit message for use with rebase --autosquash. The commit message will be the subject
line from the specified commit with a prefix of "fixup! ". See git-rebase(1) for details.
...
簡単に言うと –fixup オプションは、指定した commit の commit message に fixup!
という prefix を付与するオプションです。この付与された fixup!
という prefix を元に、後述の git rebase –autosquash で commit に対して動的にコマンドを付与する形となります。
git rebase の –autosquash オプションにはどんな作用があるのか?
–autosquash オプションは、commit message が squash!
または fixup!
で始まる commit に対して squash/fixup コマンドを付与するもので、git commit の –fixup または –squash オプションとの併用が推奨されているとのこと。もちろん、手動で commit message にそれらの prefix を付与する形でも問題はなさそうです。
$ git rebase --help
...
--autosquash, --no-autosquash
When the commit log message begins with "squash! ..." (or "fixup! ..."), and there is already a commit
in the todo list that matches the same ..., automatically modify the todo list of rebase -i so that the
commit marked for squashing comes right after the commit to be modified, and change the action of the
moved commit from pick to squash (or fixup). A commit matches the ... if the commit subject matches,
or if the ... refers to the commit's hash. As a fall-back, partial matches of the commit subject work,
too. The recommended way to create fixup/squash commits is by using the --fixup/--squash options of
git-commit(1).
This option is only valid when the --interactive option is used.
If the --autosquash option is enabled by default using the configuration variable rebase.autoSquash,
this option can be used to override and disable this setting.
...
今回の文脈で、簡単に言うと –autosquash オプションは、commit message の fixup!
という prefix を元に、commit に対して fixup
コマンドを自動で付与するオプションです。
つまりどういうことか?
git rebase -i などで、それぞれの commit に手動でコマンドを付与せずとも、上記の git commit –fixup と git rebase –autosquash を組み合わせることで、commit に対して自動で fixup
コマンドの付与が可能となり、簡単に commit が整理することが可能になります。
やってみる
適当なリポジトリを作成します。
$ mkdir test
$ cd test
$ git init
$ git commit --allow-empty -m "Initial commit"
master から add-fruits という branch を切った上で、apple というファイルを追加する commit を行います。
$ git checkout -b add-fruits
$ touch apple
$ git add apple
$ git commit -m 'Add fruits'
commit を確認する。
$ git log --oneline
e235daf (HEAD -> add-fruits) Add fruits
7131aaa (master) Initial commit
banana というファイルの commit 忘れに気がついたので、’Add fruits’ の commit に紐づく形で、git commit –fixup を使って、fixup!
という prefix が付与された commit message で commit を行います。引数には紐づけたい commit を識別するための commit hash または HEAD または branch などを渡します。
$ touch banabana
$ git add banabana
$ git commit --fixup e235daf
# 紐づけたい commit を識別できれば良いので以下のパターンでも可
# git commit --fixup HEAD
# git commit --fixup add-fruits
再度 commit を確認すると、想定通りの commit が追加されたことがわかります。
$ git log --oneline
46891da (HEAD -> add-fruits) fixup! Add fruits
e235daf Add fruits
7131aaa (master) Initial commit
誤って banana でなく banabana というファイルを追加してしまったので、同じように git commit –fixup を実行します。
$ mv banabana banana
$ git add .
$ git commit --fixup e235daf
再度 commit を確認すると、想定通りの commit が追加されたことがわかります。
$ git log --oneline
bacee03 (HEAD -> add-fruits) fixup! Add fruits
46891da fixup! Add fruits
e235daf Add fruits
7131aaa (master) Initial commit
最後に git rebase –autosquash を実行してみます。引数には、どこからの commit を autosquash するかを指定する必要があるので、今回は ‘Initial commit’ 以降の commit を対象にするために、その commit を識別できる値を引数として渡します。git rebase –autosquash の実行には -i ( –interactive ) オプションが必須なのでご注意を。
$ git rebase -i --autosquash master
# commit を識別できれば良いので以下のパターンでも可
# git rebase -i --autosquash 7131aaa
エディタが開かれるので、各 commit を確認してみると、fixup!
という prefix が付与された commit message の commit( git commit –fixup で追加した commit )に、想定通り fixup
コマンドが付与されていることがわかります。
1 pick e235daf Add fruits
2 fixup 46891da fixup! Add fruits
3 fixup bacee03 fixup! Add fruits
4
5 # Rebase 7131aaa..bacee03 onto 7131aaa (3 commands)
6 #
7 # Commands:
8 # p, pick = use commit
9 # r, reword = use commit, but edit the commit message
10 # e, edit = use commit, but stop for amending
11 # s, squash = use commit, but meld into previous commit
12 # f, fixup = like "squash", but discard this commit's log message
13 # x, exec = run command (the rest of the line) using shell
14 # d, drop = remove commit
15 #
16 # These lines can be re-ordered; they are executed from top to bottom.
17 #
18 # If you remove a line here THAT COMMIT WILL BE LOST.
19 #
20 # However, if you remove everything, the rebase will be aborted.
21 #
22 # Note that empty commits are commented out
エディタを閉じて、commit を確認してみると git rebase により commit が統合されて整理されたことがわかります。
$ git log --oneline
56aea71 (HEAD -> add-fruits) Add fruits
7131aaa (master) Initial commit
最後に git push -f して commit の整理が完了です。
$ git push -f origin add-fruits
以上です。
Tips のご紹介
その1
毎回 –autosquash オプションを付与するのが面倒であれば、以下のコマンドで git rebase -i 実行時に常に –autosquash オプションを有効にすることが可能です。
# 全てのリポジトリに設定したい場合
$ git config --global rebase.autosquash true
# 特定のリポジトリに限定して設定したい場合
$ git config --local rebase.autosquash true
その2
git commit –fixup で紐付ける commit を、git log を叩いて毎回探すのが面倒であれば、以下の便利スクリプトを導入することで万事解決です。素晴らしすぎますね…!!!
https://qiita.com/uasi/items/57da2e4268d348b371fb
その3
review bot を自ら実装、もしくは既存のものを探して、GitHub に組み込み、マージ前に git rebase –autosquash が自動実行されるようにすると、とても幸せになれそうです。
https://github.com/search?q=autosquash&type=Repositories
さいごに
git commit –fixup と git rebase –autosquash を組み合わせることで、commit の整理が簡単に行えて、とても便利なのでどんどん使っていこうと思います。また、このような git の便利な使い方を他にも知っていきたい気持ちが高まったので、今後新たな発見があったら、今回と同様に理解するがてらまとめていこうと思います。おしまい!