gitコミットしぐさ

はいどうも。最近気をつけているコミットのやり方について備忘録的に書きます。1

前提として、現在はある程度成熟しているアプリケーションについての話です。新アプリの新画面ばーんってつくっていくぞみたいなときはもっと雑にコミットしてもいいんじゃないでしょうか。不具合修正であったりちょこちょこした機能追加だったりがメインな文脈です。

commit を verbose にする

いいコミットの第一歩はたぶんこれです。git CLI使っている人は git config --global commit.verbose true でOKです。あるいは git commit -v って打つか。2 これはどういうオプションかというと、コミットメッセージを書くときに下にdiffが出ます。

-v, --verbose
    Show unified diff between the HEAD commit and what would be committed at the bottom of the commit message template to help the user describe the
    commit by reminding what changes the commit has. Note that this diff output doesn't have its lines prefixed with #. This diff will not be a part
    of the commit message. See the commit.verbose configuration variable in git-config(1).

    If specified twice, show in addition the unified diff between what would be committed and the worktree files, i.e. the unstaged changes to tracked
    files.

説明にあるように、なんの変更をコミットするのかを見せてもらえるのですごくいいです。ざっと眺めることで「これってどういうタイトルつけよう?」とか「あれ、これ入れたくなかったんだけど」みたいなことが起きます。おすすめ。

リファクタリングと変更を分ける

たとえば以下のようなものを考えましょう。

SELECT  * from users WHERE foo = 1 LIMIT 100;

「普通の select 文ですね。でも order by がないですね。あと from は予約語なのに小文字ですね。直しちゃいましょう。」

$ git diff
diff --git a/tekitou.sql b/tekitou.sql
index a9a1810..f852f3c 100644
--- a/tekitou.sql
+++ b/tekitou.sql
@@ -1 +1 @@
-SELECT  * from users WHERE foo = 1 LIMIT 100;
+SELECT  * FROM users WHERE foo = 1 LIMIT 100 ORDER BY id;

はいアウトー3 from -> FROM は動作を変更しませんが、 ORDER BY の追加は動作を変更するので分けたほうがいいと思います。

まあこの例はさすがに極端なのですが、例えば以下のような変更をするときも2コミットに分けます。

class Foo
  def foo
    obj.some_method(another_obj.some_method)
  end
end

ここで another_obj.some_method を更に加工する必要が出てきたとき

まずは「リファクタリング: ローカル変数にとるようにする」のようなコミットを積んでから変更を積みます。

$ git show
commit 786849cff921477a463fe7fcd59000d3ce84c0a3 (HEAD -> master)
Author: hkdnet <xxx>
Date:   Sun Jan 28 14:12:18 2018 +0900

    リファクタリング: ローカル変数にとるようにする

diff --git a/tekitou.rb b/tekitou.rb
index c910df5..faa7b7b 100644
--- a/tekitou.rb
+++ b/tekitou.rb
@@ -1,5 +1,6 @@
 class Foo
   def foo
-    obj.some_method(another_obj.some_method)
+    tmp = another_obj.some_method
+    obj.some_method(tmp)
   end
 end

これの何がいいかっていうと、レビュアーが楽なんですよね。GitHubのPRにはコミットを順番に見る機能があるので4リファクタリングって書いてあるコミットは軽く見れます。機能が変わったコミットに集中できます。

コマンドを打ったときにコミットしておく(例外あり)

たとえばこんな感じ

$ npm install foo
$ git add .
$ git commit -m '$ npm install foo'

npm installpackage.jsonpackage-lock.json が更新されるのでそれだけでコミットします。

Gemfile の更新は、Gemfile.lock の更新を伴うのでだいたい一緒にしてます(例外)

$ vim Gemfile
# ここではコミットしない
$ bundle install
$ git add .
$ git commit -m 'Install awesome_gem'

Rails で generate したときもそれだけでコミットしてます。db:migrate とかも。

$ bin/rails g model User name:string
$ git add .
$ git commit -m '$ bin/rails g model User name:string'
$ bin/rails db:migrate
$ git add .
$ git commit -m '$ bin/rails db:migrate'

モデルとかマイグレいじるときもコミット後に積んだほうが無難かなー。

コミットメッセージにいろいろ書く

書いておくとGitHubのPRを作るときにもコピペできて便利です。

最近だと国内向けのハイフン区切り電話番号の検証をやったのですが、こんな感じのコミットメッセージになりました。

意識してるのは、正しい仕様の根拠を示すこと。なにか機能を落としてるなら明記すること。5

$ git show
commit 9a2b1b3c57308adbb2c08daaaff1459d60c2ea55 (HEAD -> master)
Author: hkdnet <xxx>
Date:   Sun Jan 28 15:02:04 2018 +0900

    xx画面での電話番号のjs検証を追加

    ref: http://www.soumu.go.jp/main_sosiki/joho_tsusin/top/tel_number/q_and_a.html#q2

    電話番号は以下の仕様となる。
      国内プレフィックス: 1桁(0固定)
      市外局番          : 1〜4桁
      市内局番          : 1〜4桁
      加入者番号        : 4桁
        ただし市外局番と市内局番は合計で5桁となる
    ハイフンは市外局番と市内局番の間、市内局番と加入者番号の間にいれる。
    そのため (2-5)-(1-4)-(4) が正しいフォーマットであるといえる。

    「ハイフン区切りの第1項と第2項の桁数の和が必ず6桁である」
    という検証は今回は行わない
...

ハイフン区切りの第1項と第2項の桁数の和が必ず6桁である

やらなかったことが明記されてるとあとで助かります。

コミットは rebase で整形しとく

PR出す前に Fix a typo とかは rebase で何事もなかったかのように消し去ってます。

$ git commit -m 'fix a typo on xxxx'
$ git rebase -i origin/master
# さっきのコミットを xxxx のコミットの下の行にもってきて fixup しとく

これは好みだから別にどっちでも。僕が間違えたことやメソッドの使い方を勘違いしていたことも1つの情報だから、コミットとして残っててもいいのかもなーと思いつつ、blameで遡っていくときの邪魔になるので消しちゃうほうが最近の好みです。


  1. 1年前だと、「まあ適当で。GitHubのPRに書いてあればなんとかなるし」派だったことを思い出すと、こういう dump が重要な気がした

  2. GUIは知らない

  3. 「あと」とか思った時点でだいたいアウト

  4. 余談だが該当画面で n を押すと次のコミットにいく、というのを結構知らない人がいたので書いておく

  5. 今回のコミットメッセージを書く上で、あとで総務省のページがリンク切れになってたらどうしよう……という不安はある