GitHubのブランチはプロジェクト内で複数の作業を同時に行うための機能。ブランチを使うことで、メインのコードベースに影響を与えることなく新しい機能を開発したり、バグを修正したりすることができます。
関連 【初心者向け】GitHubとは?使い方を1からわかりやすく解説
より具体的にざっくり説明すると、ブランチはリポジトリ内に複製される別の作業フォルダのようなものです。別に言い方をするとプログラムに改修をする際に一時的に生み出される別の世界線のようなもので、この世界線で作業することで現実の世界線には影響を与えずに作業することを可能にする仕組み。
このページではブランチの基本的な仕組みや、マージやプルリクエストといったブランチにまつわる基本知識をご説明します。
ブランチとは?
ブランチはプロジェクトの中で独立して作業を進めるための「作業場」や「実験場」のようなものです。「実験場」で作業することで、メインのコードベースに影響を与えずに新しい機能の開発やバグ修正を行うことができます。
例えば、プロジェクトのメインブランチ(通常は「main」や「master」と呼ばれます)は、安定したバージョンを保管しています。これが「完成された場所」だと考えてください。しかし、新しい機能を追加したり、バグを修正したりする場合、直接この「完成された場所」で作業するのはリスクがあります。そこで別の「実験場」(ブランチ)を作成して、その中で自由に作業を進めるということ。
以下がそのイメージ図。リポジトリ内に複製される別の作業場だと理解しましょう。
もし、新たに作成した「実験場」(ブランチ)での改修が成功したら、その資源をメインブランチに合体させます。こうすることで、大事な資源には影響を与えずにプログラムの改修やテストが可能になる、という仕組みです。
- ステップ1ブランチの作成
メインブランチから新しいブランチを作成。これで新しい「実験場」が誕生。
- ステップ2ブランチでの作業
新しいブランチでコードの変更や新機能の開発、バグ修正を実施。
- ステップ3テスト/検証
ブランチ内での変更が正しく動作するかを確認。問題がなければ次のステップに進む。
- ステップ4プルリクエストの作成
変更が完了しテストに合格したら、メインブランチに統合するためのプルリクエストを作成。
- ステップ5統合(マージ)
プルリクエストが承認されるとブランチの変更がメインブランチに統合される。これで「実験場」での成果が正式にプロジェクトの一部となる。
このように、ブランチを使うことで安全かつ効率的にプロジェクトを進めることができます。ブランチは、メインのコードベースを保護しつつ、新しいアイデアや変更を試すための「別の場所」や「実験場」としての役割を果たします。
プルリクエストとは?(PR)
プルリクエスト(Pull Request/PR)は、作業ブランチ(実験場)での作業をメインのブランチに取り込むための「リクエスト(要求)」のこと。ブランチで行った変更が他のチームメンバーによって確認され、承認された後にメインのコードベースに統合されていくための、レビュー機能のようなものです。
プルリクエストは、変更内容をレビューし問題がないか確認するための重要な手続きです。
- ステップ1作業完了後にプルリクエストを作成
ブランチでの作業が完了しすべての変更をコミットした後、そのブランチをメインブランチに統合したい場合にプルリクエストを作成。
- ステップ2GitHubでの操作
GitHub上のリポジトリページにアクセスし「Pull requests」タブをクリック。その後「New pull request」ボタンをクリックして、新しいプルリクエストを作成。
- ステップ3変更内容の確認と説明
プルリクエストを作成する際にはどのブランチからどのブランチに変更を統合するのかを選択します(例えば「feature-branch」から「main」へ)。続いて、プルリクエストのタイトルと説明を書きますが、ここでは、どのような変更を行ったのか、その変更の目的や理由を明確に記載します。
- ステップ4レビューとコメント
プルリクエストを他のチームメンバーがその変更内容をレビュー。レビューアは、コードを確認し、問題がないかチェック。改善点や修正が必要な部分についてコメントを残すこともあります。
- ステップ5修正と更新
レビューアのコメントを受けて、必要に応じて変更を修正。修正後は再度プルリクエストを更新し、再レビューを依頼。
- ステップ5承認とマージ
レビューアが変更を承認すると、プルリクエストをメインブランチにマージすることができます。これで、ブランチで行った変更がメインのコードベースに統合され、プロジェクトに反映されます。
GitHub上でのプルリクエストのリクエスト先
GitHub上でのプルリクエストのリクエスト先(レビューア)は、主に以下の2つの方法で決まります。
1. GitHub上での選択
プルリクエストを作成する際に作成者が特定のレビューアを選択することができます。この方法では、以下の手順でレビューアを指定します。
- プルリクエストの作成
- ブランチでの作業が完了し、GitHub上でプルリクエストを作成。
- レビューアの選択
- プルリクエストの作成ページで、「Reviewers」セクションがあります。このセクションでレビューを依頼したいチームメンバーを選択することができます。選択されたレビューアに通知が送られ、レビューを行う流れ。
2. 事前定義
プロジェクトの設定やリポジトリの管理者によって、特定のファイルやディレクトリに対するコードオーナーを事前に定義することができます。これにより特定の部分に変更があった場合、自動的にレビューアが割り当てるようにすることもできます。
- CODEOWNERSファイルの作成
- リポジトリ内に
.github
ディレクトリを作成し、その中にCODEOWNERS
というファイルを置きます。
- リポジトリ内に
- コードオーナーの定義
CODEOWNERS
ファイル内で、特定のファイルやディレクトリに対して、レビューア(コードオーナー)を指定。例えば、以下のように記述します。
# 全てのファイルに対するコードオーナー * @team-member1 @team-member2 # 特定のディレクトリに対するコードオーナー /src/ @specific-owner # 特定のファイルに対するコードオーナー /README.md @documentation-owner
GitHub上でのプルリクエストのリクエスト先は、作成時に手動で選択するか、事前にCODEOWNERS
ファイルで定義する方法の2つが存在します。手動で選択する方法は柔軟性があり、その都度レビューアを決めることができます。事前定義する方法は、プロジェクトの規模が大きい場合や、特定の部分に対して常に同じ人がレビューを行う必要がある場合に便利です。
どちらの方法も、プロジェクトのレビュー体制を整えるために有効です。
マージとは?(Merge)
プルリクエストはあくまでもレビュー機能。レビュー完了したら自動で「実験場」の資源が本番資源に統合されるわけではありません。(事前に自動でマージされる仕組みを構築することもできますが、これは後述。)
ここで必要となるのがマージです。マージを正式に説明すると、異なるブランチで行われた変更を1つに統合する操作のことです。GitHubでは、この操作をGUI上から簡単に行うことができます。
コンフリクト(conflict)
マージする際の注意点がコンフリクト(conflict)です。コンフリクトは異なるブランチで行われた変更が競合し、マージが自動的に行えない状態のことを指します。これは、同じファイルの同じ部分が異なる方法で変更されたときに発生します。コンフリクトは、手動で解決する必要があります。
コンフリクトは以下のような状況で発生します。
- 同じファイルの同じ行を変更: 異なるブランチで同じファイルの同じ行を異なる内容に変更した場合。
main
ブランチでREADME.md
の1行目を「Welcome to our project」に変更。feature-branch
で同じ行を「Welcome to the best project」に変更。
- ファイルの削除と変更: 一方のブランチでファイルが削除され、他方のブランチで同じファイルが変更された場合。
自動マージの設定方法
GitHubには、特定の条件が満たされた場合にプルリクエストを自動的にマージする機能があります。以下の設定を行うことで自動マージの仕組みを実現することができます。
- 保護されたブランチの設定: リポジトリの設定で、メインブランチに対して保護されたブランチルールを設定します。これにより、特定の条件が満たされるまでマージがブロックされます。
- リポジトリの「Settings」タブを開く。
- 左側のメニューから「Branches」を選択し、「Branch protection rules」セクションで「Add rule」をクリック。
- 保護したいブランチ(例:
main
)を選択し、マージ条件(例:ステータスチェックの合格、レビューの完了)を設定。
- 自動マージの有効化: プルリクエストのページで「Allow auto-merge」オプションを有効にします。このオプションは、保護されたブランチのルールが満たされた場合にプルリクエストを自動的にマージします。
- プルリクエストを作成または開いた状態で、「Allow auto-merge」ボタンをクリック。
【実はここからが重要?】ブランチの本質を探る
最後にブランチについて1歩上の解説をしておきます。
今までの説明を少し否定しまうようですが、ブランチは「フォルダ」や「作業コピー」のイメージで理解すると本質的には誤りです。ブランチは「参照(ポインタ)」のようなものだとイメージするのが正しい理解です。
ブランチをファイルシステム的な発想で捉えない
Git初心者が陥りがちな誤解は「ブランチ=独立したコードの複製」と考えることです。CVSやSubversionのような集中型バージョン管理システム(中央集権型VCS)では、ブランチを作ることはサーバー上のリポジトリに複製的な構造を用意するという意味で大げさでコストのかかる操作でした。結果的にブランチは「重い」ものという感覚をもってしまいがちです。
しかし、Gitではブランチは実は単なる「参照(reference)」であり、中身は「あるコミットを指し示す軽量なポインタ」にすぎません。
サンプル ブランチの実態
e4d1a3f9c03e7dfaf4e18453e8b6d6b5ad5d9263
- 「master」や「main」といったブランチ名は内部的には「
.git/refs/heads/main
」といったファイル名に現れる - 中身は最新コミットのハッシュ値(SHA-1)を記述しただけの存在。
- このファイルはテキスト数十バイト程度で超軽量なので、結果としてリポジトリの容量が倍になるようなことはない。
Gitでブランチを1本作るというのは、「テキストファイル1枚を新たに用意して、そこにコミットIDを書き込む」程度の負荷なのです。
これがGitブランチが「軽い(lightweight)」と言われる所以です。
ブランチは「履歴上の位置」を表すマーカー
ブランチは特定のコミットを指しているだけなので、そのコミットから辿っていくと、そのブランチ上の過去の履歴が「一本道」になっています。コミット同士(参考 コミットの本質を理解する)は親子関係でつながっており、ブランチはその先端(tip)を記録する「札(タグ)」のようなものだと考えてください。
HEAD— ブランチを追跡する特別なポインタ
HEADは「いま自分が立っている地点」を表す
Gitを使っていると頻繁に目にするHEAD
という単語。これは「今まさに自分が操作しているブランチ、またはコミットを指す特別なポインタ」です。HEADはGit操作の基準点と言える重要な存在です。
- 通常、HEADは現在チェックアウトしているブランチ名を指します。
- たとえば
main
ブランチをチェックアウトしているとき、HEADはmain
というブランチを指します。 - この状態で新たにコミットすれば、そのコミットは
main
ブランチに追加され、HEADは新しいコミットを指すmain
ブランチの先端へと「動く」わけです。
デタッチドHEAD状態とは
HEADは通常ブランチを指しますが、特定のコミットIDを直接チェックアウトした場合、HEADはブランチではなく純粋なコミットハッシュを指す「デタッチドHEAD」状態になります。この状態では新たにコミットしても既存のブランチには追加されず、「浮遊した孤立コミット」を生んでしまいます。
デタッチドHEADは初めて見ると混乱しがちですが、「ブランチという名札を経ず、コミットそのものを直に触っている」と考えるとわかりやすいでしょう。後でこのコミットに対してブランチを張れば、その履歴を正式なライン(ブランチ)として再利用できます。
ブランチ操作の実体とその軽さをイメージする
ブランチ作成(git branch)
git branch 新ブランチ名
とすれば、現在のHEADが指すコミットを参照する新しいブランチが作成されます。内部的な処理は極めてシンプルで、「.git/refs/heads/新ブランチ名
というファイルを作って、そこに今指しているコミットのハッシュを書き込む」だけです。大掛かりなコピー操作や複製は行われません。
ブランチ切り替え(git checkout や git switch)
git switch ブランチ名
やgit checkout ブランチ名
でブランチを切り替えると、HEADがそのブランチを指すようになります。同時に、ワーキングディレクトリ(実際のファイル群)は、ブランチが指すコミット内容に応じた状態へと同期されます。
ここで行われる主な処理は以下の通りです。
- HEADを指定ブランチに変更(テキストファイルの中身を書き換える程度)
- 新しいブランチが指すコミットのスナップショットをワーキングディレクトリに展開(必要なファイルの更新や削除)
これらはGitが内部で行うことで、開発者にとっては「簡単に別作業ラインに切り替わった」ように感じられます。
ブランチ削除(git branch -d ブランチ名)
不要になったブランチを削除する場合、Gitは単に対応する参照ファイル(.git/refs/heads/ブランチ名
)を消すだけです。
もともとブランチは「参照」なので、それを消したからといって、そこに紐づくコミットオブジェクトが即座に消えるわけではありません。コミットはGit内部のオブジェクトとして保持され続け、ほかのリファレンス(タグや別ブランチ、リフログ)が何らかの形で参照している限りは残ります。
ブランチが軽量だからこそ可能なワークフロー
小まめなブランチ運用
Gitがブランチを軽量で簡易なものとしているおかげで、開発者は「小さな実験や短いタスクごとにブランチを作成する」というやり方が容易にできます。
- 新機能を試すために
feature/新機能
というブランチを作る - バグ修正用に
hotfix/重大バグ
というブランチを分ける main
やdevelop
などの安定ブランチと、実験的ブランチを並行して持つ
これらすべては「テキストファイルをちょっと書き換える」程度のコストしかないため、ブランチ戦略は非常に柔軟かつ高速です。
一時的な実験と破棄が容易
何かを試したいときに新ブランチを作り、思いきりコードをいじってみて、結果的にうまくいかなかったらブランチごと削除するというワークフローが可能です。サーバーを巻き込んだり、履歴を複雑化したりする必要がありません。
ブランチとコミット履歴の関係
ブランチはコミット履歴を指し示す「道標」
ブランチは常に「どのコミットが先頭なのか」を指すことで、そのブランチ上のコミット列(履歴)を定義しています。
異なるブランチは、同じ祖先コミットを共有しつつ、そこから分岐した異なるストーリーラインを紡ぎます。
main
ブランチは安定した製品版の流れを表すfeature/〇〇
ブランチは新機能開発の流れを表すbugfix/〇〇
ブランチは特定バグ修正の道筋を示す
こうして、ブランチが「異なる未来・過去の系譜」を記録することで、Gitリポジトリ内の変更は有向非巡回グラフ(DAG)という構造をもち、開発履歴をわかりやすく表現できます。