チーム開発をしているプロダクトで タイポを修正しただけとか、コミットログが本当にただの履歴になっているままPRをだしたりしてmasterにマージされてしまうとmasterブランチに本質ではないコミットというノイズが混ざり、後から遡って見づらくなったりしてしまいます。
PRの際にブランチのコミットを整理するルールや方法はチームやプロダクトによって違うと思いますが、 PRを出してコードレビューで指摘を受けた箇所を直し、最終的に修正部分のコミットを元のコミットに結合してコミットログを修正する方法を例にメモ
e.g.
例えば次のようなジャパリバスを直したPRのコミットログがあるとします
$ git log --oneline ca284e8 (HEAD -> master) しうんてんをしたよ 30c99a8 バス的なものをボスとつないだよ bb971d8 いすを取り付けたよ 6d8ee79 ハンドルを取り付けたよ 4917aae でんちを取り付けたよ 4d5b197 でんちを見つけてきたよ 332eb07 タイヤをさがしたよ cdbe6ff バス的なものの足りないパーツを調べるよ
4917aae
で"でんち"を取り付けたつもりだったけどコードを繋ぎ忘れてたので修正したとします。
そのまま普通に修正コミットをすると
XXXXXX (HEAD -> master) でんちのコードつけわすれてた ca284e8 しうんてんをしたよ 30c99a8 バス的なものをボスとつないだよ bb971d8 いすを取り付けたよ 6d8ee79 ハンドルを取り付けたよ 4917aae でんちを取り付けたよ 4d5b197 でんちを見つけてきたよ 332eb07 タイヤをさがしたよ cdbe6ff バス的なものの足りないパーツを調べるよ
みたいな感じになってしまうので、この修正は4917aae でんちを取り付けたよ
と一緒にしたい。
--fixup ・rebaseで修正のコミットをまとめる
fixupコミット(修正コミット)を作成する
git commit --fixup <target hash>
修正のコミットする際に、--fixup
か --fixup=
に続けて一緒にしてしまいたいターゲットコミットのハッシュを指定します。
今回の例では4917aae でんちを取り付けたよ
と一緒にしたいので次のような感じでコミット
git commit --fixup 4917aae
コミットメッセージの入力はなく次のようなコミットが作成されました
(HEAD -> master) fixup! でんちを取り付けたよ
rebase -i --autosquash でコミットをまとめる
--autosquash
オプションを付けてrebaseすると先のfixup!
な修正コミットが自動的に合体させたいコミットと統合される。rebaseする時は修正するコミットの1つ前のコミットをターゲットに指定するので、今回の場合は4917aae でんちを取り付けたよ
にコミットを統合するので1つ前の4d5b197 でんちを見つけてきたよ
を指定する
$ git rebase -i --autosquash 4d5b197
エディタが開き、fixup!
コミットがターゲットの下に自動的に移動している事を確認
pick 4917aae でんちを取り付けたよ fixup 50689a2 fixup! でんちを取り付けたよ pick 6d8ee79 ハンドルを取り付けたよ pick bb971d8 いすを取り付けたよ pick 30c99a8 バス的なものをボスとつないだよ pick ca284e8 しうんてんをしたよ
:wq
でファイルを保存して特に問題がなければrebaseが完了します。
コミットログを確認すると
$ git log --onelin 68155df (HEAD -> master) しうんてんをしたよ c3fa632 バス的なものをボスとつないだよ a81f850 いすを取り付けたよ 5e6545e ハンドルを取り付けたよ 9bc5ea3 でんちを取り付けたよ 4d5b197 でんちを見つけてきたよ 332eb07 タイヤをさがしたよ cdbe6ff バス的なものの足りないパーツを調べるよ
fixup!
コミットが消え、変更内容がターゲットにしていた でんちを取り付けたよ
にまとめられました。
既にコミットされている場合
既に修正コミットがコミットされてしまっている場合は
$ git log --oneline 5eb844c (HEAD -> master) 修正 でんちコードつなぎわすれ 68155df しうんてんをしたよ c3fa632 バス的なものをボスとつないだよ a81f850 いすを取り付けたよ 5e6545e ハンドルを取り付けたよ 9bc5ea3 でんちを取り付けたよ 4d5b197 でんちを見つけてきたよ 332eb07 タイヤをさがしたよ cdbe6ff バス的なものの足りないパーツを調べるよ
rebase -i
で修正コミットをfixup
コミットに変更してコミットログをまとめます。
まとめたいターゲットの1つまえのコミットを指定して
$ git rebase -i 4d5b197
- エディタが開くので修正するコミットの
pick
をfixup
(f
)に変更
pick 9bc5ea3 でんちを取り付けたよ pick 5e6545e ハンドルを取り付けたよ pick a81f850 いすを取り付けたよ pick c3fa632 バス的なものをボスとつないだよ pick 68155df しうんてんをしたよ fixup 5eb844c 修正 コードつなぎわすれ
fixup
にしたコミットの行を合体させたいコミットの下に移動 ( Viならdd
で1行カット、p
でペースト )pick 9bc5ea3 でんちを取り付けたよ fixup 5eb844c 修正 コードつなぎわすれ pick 5e6545e ハンドルを取り付けたよ pick a81f850 いすを取り付けたよ pick c3fa632 バス的なものをボスとつないだよ pick 68155df しうんてんをしたよ
- ファイルを保存。rebaseが実行されコミットがマージされる
ログを確認するとfixup
コミットの変更内容がfixup
にしたコミットを移動させた上のコミットにマージされ、fixup
にしたコミットメッセージは消え、元のコミットメッセージだけが残った状態になりコミットをまとめることが出来ました。
fixup と squash の違い
fixup
に似たコミットをまとめられるものにsquash
があります。
rebase
時に開くエディタの説明を引用すると、違いはコミットメッセージを残すかどうかということのようです。
squash (s): use commit, but meld into previous commit
... コミットメッセージを残し直前のコミットとまとめるfixup (f): like "squash", but discard this commit's log message
... コミットメッセージを削除して直前のコミットとまとめる
e.g.
$ git log --oneline a19959c (HEAD -> master) 修正 でんちの充電 3609605 しうんてんをしたよ 4a12fdd バス的なものをボスとつないだよ 24961f5 いすを取り付けたよ 6aa20fb ハンドルを取り付けたよ 56de548 でんちを取り付けたよ 4d5b197 でんちを見つけてきたよ 332eb07 タイヤをさがしたよ cdbe6ff バス的なものの足りないパーツを調べるよ
a19959c
のコミットを56de548
のコミットにまとめたいと思います。
56de548
の1つ前のコミットを指定してrebase
$ git rebase -i 4d5b197
エディタが開くのでa19959c
のpick
をsquash
(s
)に変更して、56de548
の下に移動
pick 56de548 でんちを取り付けたよ s a19959c 修正 でんちの充電 pick 6aa20fb ハンドルを取り付けたよ pick 24961f5 いすを取り付けたよ pick 4a12fdd バス的なものをボスとつないだよ pick 3609605 しうんてんをしたよ
ファイルを保存してrebaseを完了させるとsquash
にしたコミットが消えて、修正内容が56de548
のコミットに合算されています。
コミットメッセージは
$ git log ... commit 4d39d57aca4c7ae1a3ba48cc426b506e022db442 Author: KiKiKi Date: Mon Feb 25 15:05:17 2019 +0900 でんちを取り付けたよ 修正 でんちの充電 ...
fixup
と異なり、元のコミットメッセージが合体させたコミットメッセージに追加され残っています。
squash
でコミットする場合
fixup
と同じようにsquash
も--squash
オプションでコミットすることが出来ます。
$ git log --oneline 3609605 しうんてんをしたよ 4a12fdd バス的なものをボスとつないだよ 24961f5 いすを取り付けたよ 6aa20fb ハンドルを取り付けたよ 56de548 でんちを取り付けたよ 4d5b197 でんちを見つけてきたよ 332eb07 タイヤをさがしたよ cdbe6ff バス的なものの足りないパーツを調べるよ
$ git commit --squash <target hash>
エディタが開くのでそのまま保存すると、squash! <ターゲットのコミットメッセージ>
というコミットが作成されます。
3e22c20 (HEAD -> master) squash! でんちを取り付けたよ
--autosquash
オプションを付けてrebaseの実行
$ git rebase -i --autosquash <まとめるコミットの1つ前のコミット>
エディタが開きsquash!
のコミットが自動的にターゲットのコミットの下に移動しています
pick 56de548 でんちを取り付けたよ squash 3e22c20 squash! でんちを取り付けたよ pick 6aa20fb ハンドルを取り付けたよ pick 24961f5 いすを取り付けたよ pick 4a12fdd バス的なものをボスとつないだよ pick 3609605 しうんてんをしたよ
ファイルを保存するとrebaseが実行される前に、squash
で統合されるコミットのメッセージを修正するエディタが起動します。
This is a combination of 2 commits. # This is the 1st commit message: でんちを取り付けたよ # This is the commit message #2: squash! でんちを取り付けたよ
squash!
で始まるメッセージ部分を修正内容に変更して、でファイルを保存すればrebaseが実行され、先程変更したメッセージのコミットに変更内容が統合されました。
--squash
でのrebaseはコミットが移動している所でメッセージをへんこうしても何故か、メッセージの修正エディタが開いて再度メッセージの編集を強いられてしまってメンドーだったので、コミットしてしまってたものをrebaseで手動で移動させるほうが楽な感じでした。
rebase時に自動的に--autosquash
を付ける
--autosquash
オプションとして長かったり、付けわすれてあれ?ってなったりしがちなので、
rebase -i
の時に自動的に--autosquash
オプションが付くようにしておくと楽です。
$ git config --global --add rebase.autosquash true
まとめと感想
--squash
オプションでのコミットでの挙動的にgit rebase -i --autosquash
はfixup!
、squash!
キーワードから始まるコミットを、キーワード以降の文字列が一致するコミットの下に移動させているだけなんじゃないかなという印象でした。
fixup
なら修正コミットのメッセージが残らないので、--fixup
オプションでコミットしてしまいrebaseするのが簡単で良さそうです。コミットメッセージを残すsquash
なら残すべきメッセージでコミットしてしまってrebaseの時にpick
をs
に変更し、統合したいコミットの下に移動させる方が楽かなという肌感でした。
git rebase
でコミットログを綺麗にした場合pushするには-f
でforce pushしなければならなくなるので、チーム開発のPRなどの修正でも場合複数人がそのPRのブランチに関わっているとか、ステージングにそのブランチpullしたとかあると、rebaseしてのforce pushがあると結構メンドーな事にもなりかねないので、PR出した人がmasterにマージする前にrebaseするとかチームでルールの認識合わせが必要だなーと思いました。
個人的にはミス含め過去の履歴修正主義には否定的だったのですが、チーム開発でコミットの粒度が細かくコミット数の多いプロジェクトなら確かに遡って調べやすくなるように、ノイズになるコミットはrebaseでまとめてしまい読みやすいコミットログを作るってのは確かにアリだなと思いました。個人開発だとコミット量もそんなに増えないし、自分のやったことのログなのであまり他人が見てもわかりやすいログって今まで意識がなかったなーという気づきがありました。
[参考]
- git commit --fixup とは何か - 詩と創作・思索のひろば
- git rebaseについてのtips | けーこ in サンフランシスコ
- git rebaseでsquashした場合とfixupした場合の違い - Qiita
- git rebase -i のための rebase.autosquash オプション - Qiita
Gitが、おもしろいほどわかる基本の使い方33〈バージョン管理、SourceTree、Bitbucket〉
- 作者:大串 肇,久保靖資,豊沢泰尚
- 出版社/メーカー: エムディエヌコーポレーション
- 発売日: 2015/05/26
- メディア: 単行本