Git入門:バージョン管理のきほん
プルリクエストを出す
このレッスンで分かること
- プルリクエストが「変更を取り込んでほしい」という依頼であること
- ブラウザと
gh pr createの両方で作る手順- レビューされやすい粒度と説明文の書き方、Draft の使いどころ
プルリクエストは変更の取り込み依頼
前のレッスンまでで、ローカルのブランチを GitHub 上のリモートリポジトリに push できるようになりました。リモートリポジトリとは、GitHub 側に置かれたリポジトリの実体のことです。
ただし push しただけでは、その変更は本流のブランチに入りません。main は多くのチームで直接触らないよう保護されていて、変更を入れるには「このブランチの変更を main に取り込んでください」と申請する必要があります。この申請がプルリクエスト(Pull Request、略して PR)です。
プルリクエストは Git の機能ではなく GitHub の機能です。ローカルの Git だけを使っている限り出てきません。役割は次の 3 つです。
| 役割 | 何が起きるか |
|---|---|
| 差分の提示 | 取り込み先との差分がファイル単位で並ぶ |
| 議論の場 | 行ごとにコメントを付けて相談できる |
| 実行の入口 | 承認後にボタン 1 つでマージできる |
つまりプルリクエストは、マージという操作の前に「見せて、話して、決める」時間を挟むための仕組みです。
プルリクエストを作る
作り方は 2 通りあります。ブラウザから作る方法と、ターミナルから gh コマンドで作る方法です。できあがるものは同じなので、好きなほうを使ってください。その前に、まずブランチを push しておきます。
まずブランチを push する
プルリクエストを作るには、先に作業ブランチをリモートへ送っておきます。ここでは feature/login-form というブランチで作業していたとします。
ターミナル
$ git switch -c feature/login-form
$ git add .
$ git commit -m "ログインフォームの見た目を追加"
$ git push -u origin feature/login-formorigin は、clone したときに自動で付くリモートリポジトリの名前です。-u は「このローカルブランチは origin の同名ブランチと対応します」と覚えさせるオプションで、次からは git push だけで送れます。
push が成功すると、ターミナルに次のような案内が出ます。
プレーンテキスト
remote: Create a pull request for 'feature/login-form' on GitHub by visiting:
remote: https://github.com/your-name/your-repo/pull/new/feature/login-formこの URL をそのまま開けば、プルリクエストの作成画面に飛べます。
push する前に
git statusで、意図しないファイルが混ざっていないか確認してください。設定ファイルや個人用のメモが一緒に入ってしまうと、レビューする側は本題の差分を見つけられなくなります。
ブラウザから作る
GitHub のリポジトリページを開くと、最近 push したブランチについて Compare & pull request という緑のボタンが上部に出ます。これを押すと作成画面が開きます。
ボタンが見当たらないときは、リポジトリの Pull requests タブから New pull request を押し、比較するブランチを自分で選びます。画面の上部に取り込み先と取り込み元の 2 つが並ぶので、左が main、右が feature/login-form になっているか必ず確認してください。この 2 つは逆向きにも作れてしまいます。
その下に差分が表示されます。ここで自分の変更をひととおり読み返します。他人にレビューを頼む前の自己レビューで、書きかけのコードや消し忘れたデバッグ用の出力はたいてい見つかります。
タイトルと説明を書いて Create pull request を押せば完成です。
gh pr create で作る
ターミナルから離れずに作りたいときは GitHub CLI(gh コマンド)を使います。ブラウザで作るのと結果は同じです。
インストールは Mac なら Homebrew、Windows なら winget が手軽です。
ターミナル
# Mac
$ brew install gh
# Windows (PowerShell)
> winget install --id GitHub.cli初回だけログインします。
ターミナル
$ gh auth loginあとは作業ブランチにいる状態で次を実行します。
ターミナル
$ gh pr create --base main --title "ログインフォームを追加" --body "ログイン画面の入力フォームを追加しました。バリデーションは次のPRで対応します。"--base が取り込み先のブランチです。省略するとリポジトリの既定ブランチが使われます。オプションを付けずに gh pr create だけを打つと、対話形式でタイトルと本文を順に聞かれます。
作成後によく使うコマンドをまとめます。
| コマンド | 何をするか |
|---|---|
gh pr create | プルリクエストを作る |
gh pr view --web | 作った PR をブラウザで開く |
gh pr status | 自分に関係する PR の状態を一覧する |
gh pr checks | CI の実行結果を見る |
gh pr ready | Draft を通常の PR に切り替える |
レビュアーを指名する
PR を作っただけでは、誰も気づかないことがあります。右側のサイドバーにある Reviewers から、見てほしい人を指名してください。指名するとその人に通知が届きます。
CLI からも指定できます。
ターミナル
$ gh pr create --reviewer taro,hanako --assignee @me--assignee は担当者、つまりこの PR を進める責任がある人です。自分を指す @me を入れておくと、あとから一覧で自分の PR を探しやすくなります。
Labels でラベルを付けておくのも役に立ちます。bug や documentation のような分類のほか、WIP(作業中)や needs-discussion(相談したい)を用意しているチームもあります。
指名は 1〜2 人に絞ってください。5 人に頼むと、全員が「他の誰かが見るだろう」と思って誰も見ません。責任の所在をはっきりさせたほうが早く返ってきます。
粒度は「1 つの PR で 1 つの目的」
プルリクエストで最も差が出るのは、コマンドではなく大きさです。
レビューできる差分の量には限界があります。数百行を超えたあたりから、読む側は 1 行ずつ追うのをやめて雰囲気で承認しはじめます。そうなった PR はレビューを通ったように見えて、実際には誰も検証していません。
目安として、次の状態を目指します。
- 変更が 1 つの目的に絞られている
- 差分が 400 行を大きく超えない
- タイトルを「〜と〜をやりました」と書かずに済む
タイトルに「と」が 2 回出てきたら、PR を分ける合図です。
よくある失敗
ここで実際にありがちな失敗を 1 つ挙げます。ログインフォームを作っている途中で、既存コードのインデントがそろっていないのが気になり、ついでにフォーマッタを全ファイルにかけてしまうケースです。
その結果、差分は 2,000 行になります。本題であるログインフォームの 80 行は、書式だけの変更 1,900 行に埋もれて誰にも読まれません。
こうなったときは、いったん機能側だけを取り出します。
ターミナル
$ git switch main
$ git switch -c feature/login-form-only
$ git cherry-pick a1b2c3dcherry-pick は特定のコミットだけを別のブランチに移す操作です。詳しくは 必要なコミットだけ取り込む(cherry-pick) で扱っています。書式だけの変更は別 PR に切り出し、「これは自動整形のみで挙動の変更なし」と説明を添えれば、レビューは一瞬で終わります。
混ぜないための一番の対策は、作業を始める前にブランチを分けることです。整形したくなったら、その場でやらずにメモしておき、あとで別ブランチを切ります。
説明文に何を書くか
説明文の目的は、レビュアーが差分を読む前に文脈をそろえることです。コードを見れば分かることは書かず、コードを見ても分からないことを書きます。
書くべき項目は次の 4 つです。
- 何のための変更か — 対応する Issue 番号やチケットへのリンク
- 何を変えたか — 主要な変更点を 2〜4 行の箇条書きで
- どう確認したか — 動かした手順、確認した画面、通したテスト
- レビューで見てほしい点 — 迷った設計判断、意見が欲しい箇所
逆に書かなくていいのは、変更したファイル名の羅列です。それは GitHub が勝手に表示します。
実際の例を挙げます。
プレーンテキスト
## 目的
Issue #142 のログイン画面を実装する
## 変更点
- LoginForm コンポーネントを追加
- メールアドレスとパスワードの入力欄、送信ボタンを配置
- 送信処理はまだ未接続(次のPRで対応)
## 確認したこと
- ローカルで /login を開き、入力と送信ボタンの活性状態を確認
- 既存のテストが全て通ることを確認
## 見てほしい点
- フォームの状態管理を useState でやっていますが、
他画面と合わせるなら別の方法のほうがよいでしょうかチームによっては、この形をテンプレートとして .github/pull_request_template.md に置いています。そのファイルがあると、新規 PR の説明欄に自動で内容が入ります。
Draft はいつ使うか
Draft pull request は「作業途中なのでまだレビューしないでください」という状態の PR です。作成画面の Create pull request ボタンの横にある下向き矢印から Create draft pull request を選ぶと作れます。CLI なら --draft を付けます。
ターミナル
$ gh pr create --draft --title "検索機能の下書き"Draft には次の性質があります。
- マージボタンが押せない(誤マージを防げる)
- レビュアーへの通知が飛ばない
- CI は通常どおり動く
使いどころは主に 3 つです。1 つ目は、方向性が合っているか早めに相談したいとき。骨組みだけ作って Draft で出し、「この設計で進めていいですか」と聞けば、完成後に全部書き直す事故を防げます。
2 つ目は、CI を回したいだけのとき。手元では通るのに CI では落ちる、という状況を調べたいときに便利です。
3 つ目は、大きな作業の進捗を見えるようにしておきたいときです。
準備ができたら Ready for review ボタン、または gh pr ready で通常の PR に切り替えます。
作業が終わっているのに Draft のまま放置すると、誰も気づかないまま止まります。Draft は「今は見なくていい」という意思表示なので、終わったら必ず切り替えてください。
マージの 3 つの方式
レビューが通ると、Merge pull request ボタンが押せるようになります。このボタンには下向き矢印が付いていて、3 つの方式から選べます。履歴の残り方が変わるので、違いを押さえておきます。
| 方式 | 履歴への入り方 |
|---|---|
| Create a merge commit | ブランチのコミットがそのまま入り、マージコミットが 1 つ足される |
| Squash and merge | ブランチのコミットが 1 つにまとめられて入る |
| Rebase and merge | ブランチのコミットが main の先端に並べ直されて入る |
多くのチームが Squash and merge を既定にしています。作業中の「typo 修正」「レビュー指摘対応」といった細かいコミットが main の履歴に残らず、PR 1 つが 1 コミットとして並ぶからです。あとから履歴を追うとき、PR 単位で読めるのは大きな利点です。
逆に、1 つの PR の中でコミットを意味のある単位に分けて作ってあるなら、Create a merge commit でそのまま残す価値があります。マージの仕組みそのものは マージの仕組み(fast-forward と マージコミット) で扱いました。
マージが終わったら、画面に出る Delete branch ボタンでリモートのブランチを消します。役目を終えたブランチを残しておくと、ブランチ一覧が読めなくなります。手元のブランチも掃除しておきます。
ターミナル
$ git switch main
$ git pull
$ git branch -d feature/login-form-d はマージ済みのブランチだけを消すオプションです。まだマージされていないブランチを指定すると、Git が警告して止めてくれます。
- プルリクエストは GitHub の機能で、ブランチの変更を取り込んでもらうための依頼
- ブラウザでも
gh pr createでも作れる。取り込み先と取り込み元の向きだけは必ず確認する - 1 つの PR に 1 つの目的だけを入れる。書式の一括変更は必ず別 PR にする
- 説明文には目的・変更点・確認方法・見てほしい点を書く。相談したい段階なら Draft で出す