注目の記事

[レポート] 『きれいなcommit, pull requestを知りたい/作りたい方のためのgit勉強会』に参加してきました

3月27日に開催された『きれいなcommit, pull requestを知りたい/作りたい方のためのgit勉強会』の参加レポートです。
2018.04.03

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

はじめに

こんにちは、クラスメソッド最年少らしい黒澤です。
先日、『きれいなcommit, pull requestを知りたい/作りたい方のためのgit勉強会』
というものに参加してきましたので情報を共有します。

情報

  • 日時 : 3月27日(火)20:00-21:30
  • 場所 : 東京都渋谷区道玄坂1-9-5 渋谷スクエアA 11F

【勉強会】きれいなcommit, pull requestを知りたい/作りたい方のためのgit勉強会

スライド

@imaizume さんに講師をしていただきました。
この勉強会ではタイトル通り、きれいな commit, pull request というテーマについてお話をいただき、
そのためのテクニックなどもご紹介いただきました。

きれいな commit を積む目的

  • commit : 変更の塊
    • 変更には必ず意図がある。commit に含まれる変更の意図は見えるべきである。
  • log : 履歴
    • 履歴は後から他人が見るもの。変更の意図を正しく容易に伝えるべきである。

commit log とは変更概要と意図を第三者に伝えるために存在するもの。
きれいであるべき理由は、変更概要と意図を 正確かつ容易に 第三者に伝えるため。

きれいな commit の原則

きれいの価値観/判断基準は人によって違いもあり
完璧な正解といったものは存在しません。
なので今回は @imaizume さん独自の定義をご紹介いただきました。

きれいな commit を構成する3要素

  • 可読性 : commit message の読みやすさ
    • 変更概要を簡単に把握できるようにする
  • 原子性 : commit 粒度の小ささ
    • 変更の粒度を不必要にあげない
  • 論理性 : 変更が意味的にまとまっているか
    • 変更は意味的にまとめて commit する

読みやすい commit message

読みやすい commit message = ひと目で変更を把握できるメッセージ

  • 具体性
  • 人間的意味

の二つを意識する。

具体性がないメッセージ例

  • Fix bugs
  • Add new class

具体性のあるメッセージ例

  • ヘッダーナビゲーションのボタンタップが正しく反応するようjQueryのセレクタ名を修正

5W1Hで指示語や曖昧な言葉を使わないようにする。

人間的意味のないメッセージ例

  • OLD_CONST1, OLD_CONST2を削除
  • MemberModelにsuccessフィールドを追加

人間的意味のあるメッセージ例

  • ver2.0で廃止した登録関連の古い定数を削除
  • API取得が正常に完了したかを保持できるようMemberModelに新規のフィールドを追加

変更の詳細は差分を見ればわかるので人間的な意味を書くようにする。

適切な変更量の commit を作る方法

git add -p コマンドを活用する。

以下で使用例を紹介します。

$ cat memo.txt
aaa
bbb
ccc
ddd

このようなテキストファイルに追記を行ってみます。

$ git diff
diff --git a/memo.txt b/memo.txt
index fdbba2a..f3ea37d 100644
--- a/memo.txt
+++ b/memo.txt
@@ -1,5 +1,4 @@
 aaa
-bbb
-ccc
 ddd
+eee

bbbccc の行を削除し、新たに eee という行を追加しました。
これを普通に git add すると全ての変更がステージされてしまうため、
もし仮に一部の行だけ add したいとなった場合不都合です。
そこで git add -p と入力してみます。

$ git add -p
diff --git a/memo.txt b/memo.txt
index fdbba2a..f3ea37d 100644
--- a/memo.txt
+++ b/memo.txt
@@ -1,5 +1,4 @@
 aaa
-bbb
-ccc
 ddd
+eee
 
Stage this hunk [y,n,q,a,d,/,s,e,?]?

ここで出てくる hunk とは変更の塊のことです。
デフォルトでは7行区切りで hunk が分かれます。
git add -p ではこの hunk 単位で add を操作することができます。
今回は e を押して hunk を編集してみます。

# Manual hunk edit mode -- see bottom for a quick guide.
@@ -1,5 +1,4 @@
 aaa
-bbb
-ccc
 ddd
+eee

# ---
# To remove '-' lines, make them ' ' lines (context).
# To remove '+' lines, delete them.
# Lines starting with # will be removed.
#
# If the patch applies cleanly, the edited hunk will immediately be
# marked for staging.
# If it does not apply cleanly, you will be given an opportunity to
# edit again.  If all lines of the hunk are removed, then the edit is
# aborted and the hunk is left unchanged.

すると vi が起動します。

  • 追加差分を add したくないときは行を削除
  • 削除差分を add したくないときは "-" を削除して空白にする

今回は bbb の削除だけ取り込むために編集してみます。

```
# Manual hunk edit mode -- see bottom for a quick guide.
@@ -1,5 +1,4 @@
 aaa
-bbb
 ccc
 ddd

# ---
# To remove '-' lines, make them ' ' lines (context).
# To remove '+' lines, delete them.
# Lines starting with # will be removed.
#
# If the patch applies cleanly, the edited hunk will immediately be
# marked for staging.
# If it does not apply cleanly, you will be given an opportunity to
# edit again.  If all lines of the hunk are removed, then the edit is
# aborted and the hunk is left unchanged.
```

削除差分 ccc- を空白に変更、追加差分 eee の行を削除してみました。
この状態で vi を保存して終了します。

$ git status
On branch master
Your branch is ahead of 'origin/master' by 1 commit.
  (use "git push" to publish your local commits)

Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

	modified:   memo.txt

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

	modified:   memo.txt

git status してみると同一ファイルがステージにいるものといないもので分かれています。

$ git diff --cached 
diff --git a/memo.txt b/memo.txt
index fdbba2a..d95e448 100644
--- a/memo.txt
+++ b/memo.txt
@@ -1,5 +1,4 @@
 aaa
-bbb
 ccc
 ddd

git diff --cached でステージの差分をみてみると
bbb だけが add されていることを確認できました。

長くなりましたがこのような感じで git add -p を使用すると
commit の変更量を調整できるので活用しましょう。

git add -p の各コマンド

コマンド 説明
y hunk を stage
n hunk を stage しない
q 終了する(以降の全 hunk を stage しない)
a 以降の全 hunk を stage
d 以降の全 hunk を stage しない
g 指定した hunk に移動
/ 正規表現で hunk を検索
j hunk を保留にし、次の保留されている hunk に移動
J hunk を保留にし、次の hunk に移動
k hunk を保留にし、ひとつ前の保留されている hunk に移動
K hunk を保留にし、ひとつ前の hunk に移動
s hunk をさらに小さく分割
e hunk を手動で編集
? ヘルプ

論理性の高い commit を作る方法

git rebase -i コマンドを活用する。
rebase -i を使用することで
commit の 結合/分割/書き換え/順序入れ替え が可能。

以下では一番使うであろう結合(squash)について紹介します。

$ git log --oneline
bfc2a57 (HEAD -> master) TOP画面の実装
e84e22e エラーメッセージのtypo修正
ba76419 エラーメッセージの追加

こんな感じの commit log があったとします。
よくありがちなケースだと思いますが
typo修正だけのコミットなんてまとめてしまいたいですよね。
そこで git rebase -i の出番です。

$ git rebase -i HEAD~3

# HEAD は @ とも書ける、便利
$ git rebase -i @~3

上のコマンドでHEADから3番目までの commit を対象にします。

pick ba76419 エラーメッセージの追加
pick e84e22e エラーメッセージのtypo修正
pick bfc2a57 TOP画面の実装

# Rebase 6a5094a..bfc2a57 onto 6a5094a (3 commands)
#
# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message
# x, exec = run command (the rest of the line) using shell
# d, drop = remove commit
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
#
# Note that empty commits are commented out

すると vi が起動して対象とした commit が表示されます。
今回は
e84e22e エラーメッセージのtypo修正ba76419 エラーメッセージの追加
にまとめましょう。

pick ba76419 エラーメッセージの追加
s e84e22e エラーメッセージのtypo修正   
pick bfc2a57 TOP画面の実装

# Rebase 6a5094a..bfc2a57 onto 6a5094a (3 commands)
...

typo修正の commit の pickssquash に書き換えます。
そして vi を保存して終了します。

# This is a combination of 2 commits.
# This is the 1st commit message:

エラーメッセージの追加

# This is the commit message #2:

エラーメッセージのtypo修正

# Please enter the commit message for your changes. Lines starting
...

今度は commit message を統合するための vi が立ち上がるので
今回は エラーメッセージの追加 だけ残して終了しましょう。

# This is a combination of 2 commits.
# This is the 1st commit message:

エラーメッセージの追加
#...

すると Successfully とか言われると思うので
もう一度 commit log を確認してみます。

$ git log --oneline 
deac135 (HEAD -> master) TOP画面の実装
8ff0103 エラーメッセージの追加

typo修正の commit が消えたのを確認できました。
これで 8ff0103 エラーメッセージの追加 に統合されています。

このように git rebase -i は非常に便利なコマンドです。
でも一つ注意点として変更された commit は hash が変わるので
push 済の commit を変更してもう一度 push しようとすると怒られます。
自己責任で push -f するか push 前の commit に対して rebase -i するようにしましょう。

弊社ブログには既にこんな記事もあるのでこちらもよろしかったらどうぞ。

[Git] rebase コマンドで複数のコミットを 1 つにまとめる

git rebase -i の各コマンド

コマンド 説明
(p)pick commit をそのまま残す
(r)reword commit message を変更
(e)edit commit を編集
(s)squash 直前の pick を指定した commit に統合。commit message も統合
(f)fixup 直前の pick を指定した commit に統合。commit message は破棄
(e)exec 行の末尾に指定したシェルコマンドを実行
(d)drop commit を破棄

commit の順序を入れ替えたい場合は pick とかの vi 画面で
commit 順序を上下入れ替えてあげるとできます。

きれいな pull request を作る方法

各 commit だけでなく pull request 全体も俯瞰する。

  • commit log を見返してやったことを思い出せるか
  • 順序や commit 粒度は適切か
  • トータルの commit 数や差分が大きすぎないか
  • pull request のコメントは適切か

まとめ

  • きれいにする目的は 他人=未来の自分 に変更の内容と意図を正しく容易に伝えるため
  • commit の可読性 : 変更概要を簡単に把握できるようにする
  • commit の原子性 : 変更の粒度を不必要にあげない
  • commit の論理性 : 変更は意味的にまとめて commit する

感想

とてもためになる勉強会でした。
私も普段から Git を使用していますが、
改めて意識してみるとここはもっとこうしようみたいな気づきも得られたので
どんどん改善してきれいな commit を作っていけるようになっていきたいと思います。
発表スライドの方も図などがありとてもわかりやすのでみなさんみてみてください!