Git実務ワークフロー #5 PR運用 — レビュー単位・コミットメッセージ・draft PR

初めてのPRを作ってマージするまでの流れはGit基礎 #6 GitHubと初めてのPull Requestで扱いました。PRを作れることと、チームでPRがうまく回ることは別の問題です。レビューが2日ずつ滞るチーム、500行のPRに「LGTM」の一言だけが付くチーム、コミットメッセージが全部「修正」のチームに共通するのは、ツールではなく運用です。この記事はPRを運用の観点から扱います。

全7回で構成します。

  • #1 ブランチ戦略 — GitHub Flowとtrunk-based
  • #2 rebase vs merge — 判断基準と禁則
  • #3 interactive rebase — squash・fixupでコミット整理
  • #4 コンフリクト解決 — 構造とmergetool・rerere
  • #5 PR運用 — レビュー単位・コミットメッセージ・draft PR ← この記事
  • #6 stash・cherry-pick・bisect — 日常ツールセット
  • #7 モノレポとGit — sparse-checkout・サブモジュール・LFS

この記事では、レビュー単位を設計する基準から、コミットメッセージの規約、draft PRとセルフレビュー、そしてチームレベルの仕組みであるPRテンプレートとbranch protectionまでを順に整理します。

小さなPR — レビュー単位を設計する #

PR運用で最も効果の大きい原則をひとつ挙げるなら、PRを小さく保つことです。基準は2つあります。

  • 目的がひとつ — PRはひとつの文で説明できる必要があります。「ログインバグ修正 + ボタン色変更 + 依存関係更新」のように目的が列挙され始めたら、すでに分割すべき時点を過ぎています。
  • レビュー可能なサイズ — 変更が数百行を超えるとレビューの質は急激に落ちます。レビュアーがdiffを最後まで読むのを諦めて承認ボタンを押した瞬間、レビューは形式的な手続きになります。

小さなPRはレビューが早く終わり、コメントが具体的に付き、問題が起きたときのrevert範囲も狭くて済みます。逆に大きなPRはレビュー待ちが長くなり、その間にmainが先に進んでコンフリクトが積もり、最終的には「まずマージして後で直そう」に流れていきます。

大きな作業を分割する方法 #

小さく保つという原則を知っていても、作業自体が大きい場合があります。そのときは、PRを時間の順序ではなく依存の順序で分割します。

  • 先行リファクタリングを分離します。 機能を入れる前に必要な構造変更(関数の分離、リネーム、ファイル移動)を、動作変更のない別PRとして先に送ります。レビュアーは「動作が同じか」だけを確認すればよいので、大きなdiffでもレビューは速く進みます。その上に載る機能PRは、純粋に新しい動作だけを含むことになります。
  • 機能を縦に切ります。 画面全体を一度に作る代わりに、データモデルPR、API PR、UI PRのように積み上げられる単位に分けます。
  • スタックを積む方法もあります。 前のPRのブランチから次のブランチを続けて作り、PRを連鎖させる方式です。前のPRがマージされたら次のPRのbaseをmainに切り替えて、順に流していきます。ツールなしでも可能ですが、スタックが深くなるほど管理の負担が大きくなるので、2〜3段程度が現実的です。

コミットメッセージ — Conventional Commits #

コミットメッセージの基本原則(件名1行、1コミット1変更)は基礎シリーズで扱いました。チームではその上に形式をもう一段重ねることが多く、最も広く使われている形式がConventional Commitsです。

Conventional Commitsの形式
type(scope): subject

feat(auth): ソーシャルログインボタンを追加
fix(cart): 数量0のときの合計エラーを修正
docs(readme): ローカル実行手順を更新

typeが変更の性質を宣言し、括弧内のscopeが影響範囲を絞り込みます。代表的なtypeは次のとおりです。

type用途
featユーザーに見える新機能
fixバグ修正
refactor動作変更のない構造改善
docsドキュメントのみの変更
testテストの追加・修正
choreビルド、設定、依存関係などの付随作業

この形式の価値は読みやすさで終わりません。形式が機械的にパース可能なので、チェンジログの自動生成バージョンの自動決定(featはminor、fixはpatch)にそのままつながります。コミットメッセージがリリース自動化の入力になるということです。

件名が「何を」を担うなら、本文には「なぜ」を書きます。コードからは読み取れない背景、検討して捨てた代替案、副作用への警告は本文に残しておきます。半年後にgit logを掘り返す人にとって、最も価値のある情報はたいてい本文にあります。

draft PR — まだレビューを受ける段階ではないとき #

PRを開くタイミングは、作業が終わった後である必要はありません。GitHubのdraft PRは「まだレビューしないでください」という状態表示が付いたPRです。2つの用途で役に立ちます。

  • 方向性の共有 — 実装の序盤で構造が固まった時点でdraftとして開いておくと、チームがdiffを見ながら方向性への異論を早めに出せます。全部作り終えた後に「構造が間違っている」というコメントを受けるより、修正コストがはるかに小さくて済みます。
  • CIの先行確認 — draft状態でもCIは動くので、レビュアーの時間を使う前にテストとリントを先に通しておけます。

Ready for reviewに切り替える時点は明確です。自分でマージしてよいと判断できる状態になったときです。「とりあえず開いておいてレビューを受けながら作り続けよう」というアプローチは、レビュアーのコメントと自分の修正がずれ続ける原因になるので避けるのが無難です。

セルフレビュー — レビュアーより先にdiffを読む #

Ready for reviewを押す前に、最後の習慣がひとつあります。自分のPRのFiles changedタブを最初から最後まで読むことです。エディタでコードを書いているときとdiffで見るときでは、目に入るものが違います。セルフレビューで引っかかる定番は決まっています。

  • 消し忘れたデバッグ出力とコメントアウトされたコード
  • 意図せず紛れ込んだファイル(ローカル設定、実験スクリプト)
  • このPRの目的と無関係な細かい修正

こうした指摘をレビュアーにさせてしまうと、本来設計レビューに使うべきエネルギーが消耗されます。機械的な問題はセルフレビューとCIが濾し取り、人は判断が必要な問題に集中する。それが良い分業です。

レビューコメント — 文化の最小合意 #

レビュー文化はチームごとに異なりますが、摩擦を減らす最小限の合意は共通しています。

  • 意図を添えて書きます。 「こう直してください」より「同時リクエストが重なるとこのカウンターがずれる可能性があるので、アトミックな操作への変更を提案します」のほうが受け入れやすくなります。
  • suggested changesを活用します。 GitHubのコメントの提案機能を使えば、作成者はボタンひとつで修正をコミットできます。タイポや1行修正を往復なしで終わらせる道具です。
  • 承認基準を合意しておきます。 「すべてのコメント解決後に承認」なのか「軽微なコメントは承認後に反映」なのかをチームとして決めておけば、PRごとに基準が揺れません。

チームの仕組み — PRテンプレートとbranch protection #

個人の習慣だけに頼らず、リポジトリのレベルで強制できる仕組みが2つあります。

PRテンプレートは、リポジトリに.github/pull_request_template.mdファイルを置くと、すべてのPRの説明欄に自動で挿入される雛形です。変更の要約、変更の理由、テスト方法程度の項目があるだけでも、説明が空のPRはなくなります。

.github/pull_request_template.mdの例
## 何を変えたか

## なぜ変えたか

## どう確認したか
- [ ] テストを追加または更新
- [ ] ローカルで動作確認

branch protectionはmainブランチに対するルールです。リポジトリ設定で次のことを強制できます。

  • PRなしでのmainへの直接push禁止
  • 承認レビュー最低1件以上
  • 指定したCIチェック(テスト、リント)の通過までマージボタンを無効化

ルールが人の記憶力の代わりを務めるようになれば、「急いでいたのでそのままpushした」という事故自体が構造的に不可能になります。

マージ方式のポリシーとコミット整理の関係 #

基礎 #6で見たマージボタン3種(merge commit、squash、rebase)は、チームのポリシーとコミット整理の負担が交わる地点です。

Squash and mergeをチームポリシーにすると、PR内のコミットはマージ時点でひとつにまとまるので、「レビュー反映」「タイポ修正」のような中間コミットを前回扱ったinteractive rebaseで整理する負担が大きく減ります。その代わりPRひとつがコミットひとつになるため、PRが大きいほど履歴の解像度も下がります。小さなPRの原則とsquashポリシーはセットで動かすときに履歴が最も読みやすくなります。

逆にコミット単位を履歴にそのまま残すチーム(merge commitまたはrebase and merge)なら、PRを開く前にコミットを意味単位に整理する習慣が、そのまま履歴の品質になります。

まとめ #

この記事の要点は4つです。

  • PRはひとつの目的とレビュー可能なサイズに保ち、大きな作業は依存の順序で分割します。
  • Conventional Commitsは読みやすい形式にとどまらず、チェンジログとリリース自動化の入力になります。
  • draft PRで方向性とCIを先に確認し、Readyの前にセルフレビューで機械的な問題を濾し取っておきます。
  • PRテンプレートとbranch protectionで、習慣をリポジトリレベルのルールに変えます。

次回の「Git実務ワークフロー #6 stash・cherry-pick・bisect — 日常ツールセット」では、作業中の急な切り替え、特定コミットだけの移動、バグ混入コミットの特定のように、必要な瞬間にないと困る3つのツールを扱います。

X