Gitのプルリクエストとは: GitHubでPRを作成する方法
プルリクエスト(PR)は、あるブランチの変更を別のブランチへ取り込むことを提案する、GitHubなどのプラットフォーム上の依頼です。変更を示し、チームがコメントや検証を行い、承認後にマージします。GitLabでは同じ仕組みをマージリクエスト(MR)と呼びます。これはGitのコマンドではなく、git pullとは関係ありません。
プルリクエスト(PR)は、あるブランチの変更を別のブランチに取り込んでほしいと提案する仕組みです。GitHubなどのプラットフォーム上で行います。ブランチをサーバーに送信し、PRを開くと、チームは差分を確認し、行ごとにコメントし、自動チェックを実行します。そのうえでマージボタンを押します。GitLabでは同じ仕組みをマージリクエスト(MR)と呼びます。「git pull requestとは」と聞かれた場合は、「コードを書いた」段階と「コードがmainに入った」段階の間にある確認の場だと答えられます。
プルリクエストという名前の由来
この名前は歴史的な経緯から付きました。変更を加えた人が、リポジトリの持ち主に対して「あなたのブランチを自分の方へpull(取り込み)してください」と依頼します。そこからpull requestという名前が生まれました。GitLabは結果に注目して、マージリクエスト(merge request)と呼ぶことにしました。BitbucketとAzure DevOpsは「pull request」と呼びます。実質的には同じもので、元になるブランチ、マージ先のブランチ、説明、議論、チェック、マージボタンで構成されています。
重要なのは、プルリクエストがGitの一部ではないという点です。Gitそのものには「PR」というオブジェクトはなく、コミットや .git フォルダでPRを見つけることもできません。これはプラットフォームの機能です。GitHubは説明、コメント、チェックの状態を保存します。Gitが担当するのは、PRに含まれるブランチとコミットだけです。
開発者の会話で「PR」と言えば、ほぼ必ずプルリクエストを指します。「PRを出して」「PRをレビューして」「PRをマージした」などと使います。GitLabでは「MR」と言います。
pull requestとgit pullの違い
似た言葉のため、この2つは混同されがちです。git pull はリモートリポジトリの新しいコミットをダウンロードし、現在のブランチにすぐに取り込むコマンドです。ターミナルでのローカル操作です。一方、プルリクエストは、あるブランチを別のブランチにマージしてほしいと依頼するための、GitHubなどのWebページです。
| git pull | プルリクエスト | |
|---|---|---|
| どこにあるか | ターミナルのGitコマンド | GitHub、GitLab、Bitbucketのページ |
| 何をするか | サーバーの変更を手元に取り込む | 自分のブランチを別のブランチへマージするよう依頼する |
| 誰が関わるか | 自分だけ | 作成者、レビュアー、CI |
| プラットフォームが必要か | 不要。remoteがあれば十分 | 必要 |
Gitには「git pull request」という独立したコマンドはありません。古いユーティリティの git request-pull はありますが、これは変更の要約メールの文面を生成するだけで、GitHubのPRとは関係ありません。
プルリクエストが必要な理由
プロジェクトを一人で進めているうちは、mainに直接コミットしても問題ありません。しかし、リポジトリに2人以上が関わるようになると、メインブランチへの直接のpushが、ビルドの破損や作業の消失につながります。プルリクエストは、次のような課題をまとめて解決します。
- コードレビュー: 変更がmainに入る前に別の人が確認し、作成者が見落としたミスを見つけられます。
- 自動チェック: GitHub ActionsなどのCIが、PRごとにテストやリンターを実行します。赤い状態のままでは、壊れたコードをマージできません。
- 意思決定の記録: 1年後でも、PRを見れば何が変わったかだけでなく、なぜ変わったかも議論を含めて分かります。
- 他人のプロジェクトへの貢献: オープンソースでは書き込み権限がないため、フォークからのPRが修正を提案する唯一の方法になります。
- ブランチの保護: GitHubのブランチ保護ルールで、mainへの直接pushを禁止し、承認済みのPRを必須にできます。
GitHubでブランチからプルリクエストを作成する方法
これは、リポジトリへの書き込み権限を全員が持っているチームでの基本的な流れです。作業はfeatureブランチで行い、mainへの変更はPRを通してのみ行います。
手順1. 最新のmainからブランチを作成する
git switch main
git pull
git switch -c feature/login-form
ブランチ名は内容が分かるように付けましょう。feature/…、fix/…、タスク番号などが一般的です。レビュアーが、そのブランチが何についてなのかをすぐ理解できます。
手順2. コミットを作成する
git add src/login.js
git commit -m "ログインフォームを追加"
1つのPRには1つのタスクを含めます。100〜300行程度の小さなPRは素早く丁寧にレビューできますが、3000行もあるPRだと、レビュアーは中身を確認せずに承認しがちです。
手順3. ブランチをサーバーに送信する(PRのためのgit push)
git push -u origin feature/login-form
-u オプションは、ローカルブランチとリモートブランチを紐付けます。以後は git push だけで送信できます。GitHubは応答の中で、PRを作成するためのリンクを案内してくれます。
remote: Create a pull request for 'feature/login-form' on GitHub by visiting:
remote: https://github.com/team/app/pull/new/feature/login-form
手順4. Webインターフェースでプルリクエストを開く
ターミナルに表示されたリンクを開くか、GitHubでリポジトリを開きます。新しくpushしたブランチがあれば、「Compare & pull request」というボタンが表示されます。別の方法としては、「Pull requests」タブから「New pull request」を選ぶこともできます。
- ブランチの組み合わせを確認します。base はマージ先(通常は
main)、compare は自分のブランチです。 - タイトルを入力します。何をしたかを、命令形または簡潔な説明で書きます。
- 説明欄に、目的、確認方法、今回の範囲外の内容を書きます。リポジトリにPRのテンプレートがあれば、自動的に入力されます。
- 右側で、レビュアー(Reviewers)、担当者(Assignees)、ラベルを設定します。チームが使っている場合に限ります。
- 「Create pull request」を押すか、下書きとして作成します(後述)。
ターミナルからPRを作成する: GitHub CLI
ブラウザーに切り替えたくない場合は、公式ツールの gh を使えます。これはGitのコマンドではなく、GitHubが提供する別のプログラムですが、PRの操作にはとても便利です。
gh pr create --base main --title "ログインフォームを追加" --body "Closes #42"
gh pr list
gh pr checkout 57
gh pr merge 57 --squash
「git pull requestのコマンド」を正直に説明すると、次のようになります。Gitはブランチ、コミット、pushを扱います。PRそのものは、ブラウザー、gh、またはAPIを使ってプラットフォーム上で作成されます。
フォークを使って他人のリポジトリにプルリクエストを送る方法
オープンソースのプロジェクトでは、書き込み権限がないのが普通です。そのため、まずフォークを作成します。フォークとは、GitHub上にリポジトリのコピーを作ることです。そのコピーで作業し、元のプロジェクトに変更を提案します。
- プロジェクトのページで「Fork」を押します。
github.com/you/projectが作成されます。 - フォークをcloneし、元のリポジトリを2つ目のremoteとして追加します。名前は通常
upstreamです。 - ブランチを作成し、コミットを行い、フォークにブランチを送信します。
- GitHubでPRを開きます。base repositoryは元のプロジェクト、head repositoryは自分のフォークと自分のブランチです。
git clone git@github.com:you/project.git
cd project
git remote add upstream https://github.com/owner/project.git
git switch -c fix/typo-readme
git commit -am "READMEの誤字を修正"
git push -u origin fix/typo-readme
作業を始める前に CONTRIBUTING.md を確認してください。コミットメッセージの書き方、テストの実行方法、事前にissueを作る必要があるかどうかが書かれていることが多いです。フォークを最新に保つには、git fetch upstream を実行し、自分のブランチで git rebase upstream/main を行います。そうすれば、PRが元のプロジェクトから遅れることはありません。
GitLabのマージリクエスト: 意味と作り方
GitLabのマージリクエストは、GitHubのプルリクエストに相当する機能です。名前やインターフェースの細部は異なりますが、流れは同じです。ブランチ、push、マージ依頼、レビュー、マージという順番で進みます。
MRは次のいずれかの方法で作成できます。
- 新しいブランチを
git pushした後にターミナルに表示されるリンクから。 - プロジェクトの Merge requests セクションで「New merge request」を押し、元のブランチと先のブランチを選ぶ。
- push時にGitLabのpush optionsを指定して、ブラウザーを開かずに作成する。
git push -o merge_request.create -o merge_request.target=main origin feature/login-form
GitLabの用語は少し違います。承認はapprove、マージの責任者はassignee、GitHub Actionsの代わりに .gitlab-ci.yml を使うGitLab CI/CDが動きます。下書きのMRもあり、Draftと呼ばれます。
プルリクエストのレビュー
レビュアーは「Files changed」タブを開き、差分を確認します。どの行にもコメントを残せますし、修正案(suggestion)を提示して、作成者がワンクリックで適用することもできます。コメントはまとめて送信され、次のいずれかの判定を付けます。
- Comment: 判定を伴わない指摘だけを行う。
- Approve: 変更を承認する。
- Request changes: 修正が必要で、マージしてほしくない。
良いレビューは、ロジック、読みやすさ、テスト、境界条件に注目します。好みの問題で争うのは避けましょう。書式はCIのリンターに任せるのが良い方法です。作成者は、コメントにはすべて返信するべきです。修正した、議論した、説明を添えて現状のままにした、のいずれかを書きます。
他人のPRをローカルで確認する方法(git checkout pull request)
コードを実際に動かして確認したいこともあります。ブランチが同じリポジトリにあるなら、git fetch と git switch で十分です。フォークからのPRについては、GitHubが pull/<番号>/head という特別な参照を提供しています。
git fetch origin pull/57/head:pr-57
git switch pr-57
GitLabでは同じ目的の参照が merge-requests/<番号>/head と呼ばれます。GitHub CLIを使っている場合は、gh pr checkout 57 と入力するだけです。
レビューの指摘を受けてプルリクエストを更新する方法
PRはコミット単位ではなく、ブランチに紐付いています。同じブランチに新しいコミットを作ってpushすれば、PRは自動的に更新されます。新しくPRを作り直す必要はありません。
git commit -am "レビューの指摘に対応"
git push
git rebase pull request: 最新のmainを取り込む
PRがレビュー中にも、mainは先に進みます。GitHubがブランチの古さやコンフリクトを報告したら、ブランチを更新します。rebaseを使うと、履歴を直線的に保てます。
git fetch origin
git rebase origin/main
# コンフリクトを解決し、git add の後、git rebase --continue
git push --force-with-lease
rebaseするとコミットのハッシュが変わるため、通常のpushは拒否されます。強制的なpushが必要です。--force-with-lease を使ってください。このオプションは、ブランチに他の人のコミットが追加されていた場合に上書きを拒否します。そのブランチで他の人も作業しているなら、git merge origin/main でmainを取り込むほうが安全です。履歴を書き換えずに済みます。
プルリクエストのマージ: merge、squash、rebase
レビューで承認され、チェックがすべて緑になったら、PRをマージできます。GitHubは3つの方法を提供しています。リポジトリの管理者は、そのうちの一部だけを許可することもできます。
| 方法 | mainに入る内容 | 向いている場合 |
|---|---|---|
| Create a merge commit | ブランチのすべてのコミットとマージコミット | 完全な履歴が必要で、どのコミットが一緒に入ったかを残したい |
| Squash and merge | PRの全変更を含む1つのコミット | ブランチに「fix typo」のような細かいコミットが多く、mainのログを綺麗に保ちたい |
| Rebase and merge | ブランチのコミットを1つずつ、マージコミットなしで | コミットが整理されていて、それぞれに意味がある |
インターフェースから行うPRのマージは、基本的にはサーバー上で実行される git merge と同じです。マージ後、GitHubはブランチの削除を提案します。不要になったブランチなので、削除して構いません。ローカルも整理しましょう。
git switch main
git pull
git branch -d feature/login-form
ターミナルで手動マージすることもできます。ローカルでブランチをmainにマージして、pushします。GitHubは、ブランチのコミットがbaseに入ったことを認識し、PRを自動的にmergedとして扱います。ただし、この方法ではチェックやブランチ保護ルールが適用されないため、通常はボタンでマージします。
Auto-mergeとmerge queue
チェックに時間がかかる場合は、自動マージを有効にできます。すべての条件を満たした時点で、PRが自動的にマージされます。大規模なリポジトリでは、merge queueを使います。これは、キューにある他のPRを先に適用した状態でPRを検証し、互換性のない変更によってmainが壊れるのを防ぎます。
プルリクエストの下書き(Draft)
下書きとは、まだレビューの準備ができていないPRです。マージはできず、レビュアーにはレビュー依頼が届きません。下書きは、方向性を早めに共有したい場合や、CIを早く動かしたい場合、設計についての意見を集めたい場合に便利です。GitHubでPRを作成するときに、作成ボタンのドロップダウンから下書きを選ぶか、gh pr create --draft を実行します。作業が完了したら「Ready for review」を押します。GitLabでは、MRのタイトルの先頭に Draft: を付けることで下書きにします。
プルリクエストとissueの関連付け
PRの説明にキーワードとissue番号を書くと、マージ後にGitHubが自動的にissueを閉じてくれます。
Closes #42
Fixes #17
Resolves #8
close、fix、resolveとその活用形(closes、fixed、resolvesなど)が使えます。キーワードなしで #42 と書くだけではリンクになりますが、issueは閉じられません。GitLabでも、Closes #42 などの似た表現が使えます。
プルリクエストやマージリクエストは削除できるか
GitHubの一般ユーザーはPRを削除できません。「Close pull request」ボタンで閉じることしかできません。閉じたPRは履歴に残り、再度開くこともできます。閉じた後のブランチは、別途削除してください。PRを完全に削除できるのは、GitHubのサポートに依頼した場合だけです。たとえばシークレットが混入してしまった場合などです。
GitLabでは、作成者または十分な権限を持つメンバーがMRを閉じることができます。削除できるのはプロジェクトのオーナーか管理者です。PRにパスワードやトークンが含まれていた場合は、閉じるだけでは不十分です。コミットはすでにサーバーにあるため、すぐにそのシークレットを無効化してください。
プルリクエストでよくある間違い
- mainからPRを出す: 自分のmainにコミットしてから、そのmainを元にPRを開いてしまう。次のPRには古い変更も含まれてしまいます。作業は必ず別のブランチで行いましょう。
- baseブランチの誤り: PRが
mainではなくdevelopに向いている、またはその逆。PRのタイトル横の Edit ボタンでbaseを変更できます。 - 不要なファイルが差分に入る: ビルド成果物、
node_modules、.envなど。.gitignoreに追加し、git rm --cachedでインデックスから外してください。 - 巨大なPR: 作業を複数の連続したPRに分割してください。
- rebase後の通常のpush:
rejected (non-fast-forward)エラーが出ます。--forceではなくgit push --force-with-leaseを使ってください。 - コンフリクトをWebエディタで安易に解決する: ローカルでmainを取り込み、解決してテストを確認してください。
git flowや他のプロセスでのプルリクエスト
プルリクエストは、どのチームの開発プロセスにも組み込めます。GitHub Flowでは、mainからブランチを作り、PRを出し、レビューしてマージし、デプロイするという単純な流れです。git flowでは、featureブランチからの PR は develop に向かい、リリースブランチやhotfixブランチも main へのPRを通ります。trunk-based開発では、PRは短命で、数日ではなく数時間で完結します。どのプロセスでも、ルールは1つです。共有ブランチへの変更は、必ずプルリクエストを通して行います。
まとめ
プルリクエストは、Gitのコマンドではなく、プラットフォームの仕組みです。サーバーに送信されたブランチに、議論、レビュー、チェックのためのページを組み合わせたものです。GitLabではマージリクエストと呼ばれ、会話の中ではPRやMRと言います。PRは別のブランチから作成し、小さく保ち、同じブランチへのpushで更新し、プロジェクトの履歴に合った方法でマージしましょう。フォーク、featureブランチ、プルリクエストの実践は、Gitのコースで扱っています。
よくある質問
プルリクエストのレビュアーは何人必要ですか?
通常は1〜2人で十分です。必要な承認数はブランチ保護ルールで設定され、それを満たさないとマージボタンは使えません。
フォークを使わずにプルリクエストを作れますか?
はい。リポジトリへの書き込み権限があれば、そのリポジトリ内にブランチを作り、そこからPRを開けます。フォークが必要になるのは、書き込み権限がない場合だけです。
PRがなかなか見てもらえないときはどうすればいいですか?
レビュアーにコメントやチームのチャットで丁寧にリマインドしましょう。オープンソースでは数週間かかることもあります。PRがCONTRIBUTING.mdのルールに合っているかも確認してください。
自分のフォーク内の2つのブランチの間でプルリクエストを作れますか?
はい。比較ページで、baseとheadの両方に自分のフォークを選びます。元のプロジェクトに送る前に、自分の変更をレビューするのに便利です。
自分が作成していないコミットがPRに表示されるのはなぜですか?
ブランチが最新のmainから作られていないか、baseブランチが誤って選ばれている可能性が高いです。最新のmainにrebaseするか、PRの設定でbaseを変更してください。