はじめに
Gitを使う上でコミットメッセージは避けて通れない。適当に fix や 修正 と書いていると、後から追跡するのが大変です。
ルールを決めておくだけで、後からログを追いやすくなり、レビューもスムーズになる。私が採用しているフォーマットを紹介します。
※コミットメッセージの書き方に正解はありません。あくまで一例として参考にしてください。
書き方のフォーマット
以下のフォーマットで書いています。
<種別>: <要約>
例:
feat: 検索機能を追加
fix: ログイン時のエラーを修正
1. Prefixをつける
コミットの種別を先頭につけます。Semantic Commit Message や Angular.js のルール がよく参照されているので、これらを参考にしています。
| Prefix | 用途 |
|---|---|
feat |
新機能の追加 |
fix |
バグ修正 |
refactor |
リファクタリング(機能変更なし) |
chore |
ビルド・設定関連 |
docs |
ドキュメントのみの変更 |
style |
フォーマット修正(空白、セミコロン等) |
test |
テストの追加・修正 |
remove |
削除 |
Prefixがあると、履歴を眺めたときにどんな変更があったか一目でわかります。また、Prefixに合わせてコミットを分けることで、適切な粒度になりやすいです。
2. SubjectとBodyの使い分け
1行で収まらない場合は、SubjectとBodyを分けて書きます。
フォーマット
<種別>: <Subject(要約)>
← 空行(必須)
<Body(詳細説明)>
Subject:何をしたか(50文字以内が目安)
Body:なぜ・どのように(必要に応じて)
書き方
-m を付けずに git commit を実行するとエディタが開き、複数行で書けます。
git commit
実例1:バグ修正の詳細を残す
fix: ログイン時にエラーが発生する問題を修正
原因:
- セッションの有効期限チェックが漏れていた
対応:
- トークン検証ロジックを追加
- リフレッシュトークンの再取得処理を実装
関連: #456
実例2:設計判断を残す
refactor: 検索ロジックをRepositoryパターンに変更
従来はControllerに直接SQLを書いていたが、以下の理由で分離:
- テストが書きづらい
- 同じクエリが複数箇所に散らばっていた
今後の検索機能追加に備えて、Repository層を導入した。
実例3:複数ファイルの変更意図を説明
feat: #123 ユーザー認証機能を追加
追加したファイル:
- AuthController.ts: 認証エンドポイント
- AuthService.ts: トークン生成・検証ロジック
- auth.middleware.ts: 認証ミドルウェア
JWTを使用し、アクセストークンの有効期限は1時間に設定。
いつBodyを書くか
| 状況 | Bodyの要否 |
|---|---|
| 変更理由が自明(typo修正など) | 不要 |
| 原因と対応を残したい(バグ修正) | 書く |
| 設計判断や選定理由がある | 書く |
| 後で「なぜこうした?」と聞かれそう | 書く |
3. 区分が混じるときの対応
実際の開発では「新機能のためにライブラリを追加する」など、複数の区分にまたがる変更が発生します。このときの対応方法を整理します。
パターン1:独立したPRにする
Node.jsバージョンアップやCI設定の変更など、機能開発と関係なく行う変更は独立したPRにします。
# 変更するファイル
- package.json
- .nvmrc
- Dockerfile
# コミット
chore: Node.jsのバージョンを20に更新
理由:
- 影響範囲が明確になる
- 問題が起きたときに切り戻しやすい
- レビューが軽い
パターン2:同じPR内で別コミットにする
「新機能のためにライブラリを追加する」ような場合は、同じPR内でコミットを分けます。
# 1つ目のコミット
chore: axiosを追加
# 2つ目のコミット
feat: #123 API通信機能を追加
こうしておくと、後からログを見たときに「何のためにaxiosを入れたか」がわかります。
パターン3:まとめてしまう
変更が小さければ、まとめてしまうこともあります。
feat: #123 API通信機能を追加(axios導入)
判断基準
| 観点 | 対応 |
|---|---|
| 他の変更と混ぜると影響範囲がわかりにくい | 独立PRにする |
| その機能と一緒にレビューした方が文脈がわかりやすい | 同じPR内で別コミット |
| 変更が小さく、分けるほどでもない | まとめる |
変更内容別の目安
| 変更内容 | 対応 |
|---|---|
| Node.js / 言語バージョンアップ | 独立PR |
| CI設定の変更 | 独立PR |
| 新機能に必要なライブラリ追加 | 同じPR内で別コミット or まとめる |
| Linter設定の微調整 | 独立コミット(PRは状況次第) |
4. レビュー指摘を受けた時の対応フロー
レビューで修正を求められた場合の、修正から再レビューまでの流れを説明します。
全体の流れ
1. PRを作成してレビュー依頼
2. レビュー指摘を受ける
3. 指摘内容を修正してコミット
4. プッシュして再レビュー依頼
5. (必要に応じて)コミットを整理してマージ
Step 1:レビュー指摘を受ける
例として、以下のような指摘を受けたとします。
□ user が null の場合のハンドリングがない
□ 変数名 u は userId にしてください
□ 不要なコメントが残っています
Step 2:修正してコミット
指摘ごとにコミットを分けるか、まとめるかは状況次第です。
方法A:fixupコミットを使う(おすすめ)
fixupとは「この修正は、指定したコミットの一部として扱ってほしい」と印をつける機能です。
マージ前にsquash(複数のコミットを1つにまとめる操作)して、きれいな履歴にする前提で、修正対象のコミットに紐付けます。
こうすることで、最終的には「最初から完璧だったかのような履歴」になります。
# 元のコミットを確認
git log --oneline
# abc1234 feat: #123 検索機能を追加
# def5678 chore: axiosを追加
# 修正してfixupコミット
git add .
git commit --fixup=abc1234
自動で fixup! feat: #123 検索機能を追加 というメッセージが付きます。
方法B:明示的にメッセージを書く
git commit -m "fix: #123 PR指摘対応 - nullチェックを追加"
方法C:複数の指摘をまとめて対応
git commit -m "fix: #123 PR指摘対応" -m "- nullチェックを追加" -m "- 変数名をuserIdに変更" -m "- 不要なコメントを削除"
または、エディタで書く場合:
fix: #123 PR指摘対応
- nullチェックを追加
- 変数名をuserIdに変更
- 不要なコメントを削除
Step 3:プッシュして再レビュー依頼
git push origin feature/search
PRのコメントで再レビューを依頼します。
ご指摘いただいた点を修正しました。
- nullチェック追加: abc1234
- 変数名修正: def5678
ご確認お願いします。
ポイント:どのコミットで何を直したか書くと、レビュアーが差分を追いやすい。
Step 4:Approveされたらコミットを整理(任意)
fixupコミットを使った場合は、マージ前に整理します。
# autosquashオプションでfixupコミットを自動で統合
git rebase -i --autosquash main
実行すると、エディタが開きます:
pick abc1234 feat: #123 検索機能を追加
fixup def5678 fixup! feat: #123 検索機能を追加 ← 自動で並び替えられている
pick ghi9012 chore: axiosを追加
保存して閉じると、fixupコミットが元のコミットに統合されます。
# 整理後のログ
git log --oneline
# abc1234 feat: #123 検索機能を追加 ← 修正が含まれた状態
# ghi9012 chore: axiosを追加
Step 5:プッシュしてマージ
# rebaseしたのでforce pushが必要
git push --force-with-lease origin feature/search
その後、PRをマージします。
フロー全体のコミット履歴イメージ
# レビュー前
abc1234 feat: #123 検索機能を追加
def5678 chore: axiosを追加
# レビュー指摘後(修正コミット追加)
abc1234 feat: #123 検索機能を追加
def5678 chore: axiosを追加
111aaaa fixup! feat: #123 検索機能を追加 ← 指摘対応1
222bbbb fixup! feat: #123 検索機能を追加 ← 指摘対応2
# rebase --autosquash 後(マージ直前)
abc1234 feat: #123 検索機能を追加 ← 修正が統合されてスッキリ
def5678 chore: axiosを追加
避けたいパターン
# ❌ 何を直したかわからない
fix: レビュー対応
fix: 修正
fix: PR指摘
# ❌ 履歴が汚れたままマージ
abc1234 feat: #123 検索機能を追加
111aaaa fix: 修正
222bbbb fix: 修正2
333cccc fix: やっぱりこっち
5. WhyはIssueに書く
「コミットメッセージにはWhyを書くべき」という意見もありますが、ワンライナーでWhyまで書くのは難しいです。
僕はコミットメッセージにIssue番号を紐付け、詳細はIssueやPull Request側で担保しています。
feat: #123 検索機能を追加
こうしておけば、後から経緯を追いたいときはIssueを見ればわかります。
サンプル
# 新機能
feat: 検索機能を追加
feat: #123 ユーザー認証機能を追加
# バグ修正
fix: 検索結果が表示されない問題を修正
fix: #456 ログイン時のエラーを修正
# リファクタリング
refactor: 検索ロジックを共通関数に集約
# 削除
remove: 使用していないコンポーネントを削除
# 設定変更
chore: Node.jsのバージョンを20に更新
chore: axiosを追加
# レビュー指摘対応
fix: #123 PR指摘対応 - nullチェックを追加
おわりに
コミットメッセージにこだわる、最初に決め事とすることで、レビューも不具合対応もスムーズになります。