02 Git & GitHub — 変更履歴を管理し、チームで協働する作法
「コミット」「プルリク」「コンフリクト」「マージ」——現場では当たり前に使われるこれらの言葉、正確に説明できますか? この章では、コードの変更を記録し、チームで協働するための作法を根本から理解します。
📑 この章の目次
一言でいうと
Git(ギット) は、コードの変更履歴を記録・管理するバージョン管理システムです。「いつ・誰が・何を変えたか」をすべて保存し、過去の任意の時点に戻れるようにします。GitHub(ギットハブ) は、そのGitの履歴をインターネット上で共有・協働するためのプラットフォームです。Gitが「手元の道具」なら、GitHubは「みんなで使う作業場」と言えます。
1. なぜバージョン管理が必要なのか
Gitが生まれる前、ファイルの変更はこんなふうに管理されていました:
report.docxreport_修正版.docxreport_最終.docxreport_最終_本当に最終.docxreport_本気で最終_先方確認済.docx
笑い話に聞こえますが、コードでも同じことが起きます。そして、ここには致命的な問題が3つあります:
| 問題 | 何が起きるか |
|---|---|
| 「どれが最新?」が分からない | 古い版を編集し続けて作業が無駄になる |
| 「前の状態」に戻せない | 変更で壊れた時、元に戻す手段がない |
| 複数人の変更を合わせられない | 誰かの変更を上書きして消してしまう |
Gitは、これらを**「変更を1本の歴史(履歴)として記録し、自由に行き来・分岐・合流できる仕組み」**で解決します。
💡 「バージョン管理」 = 変更の履歴を管理すること。「第3版」「第4版」と番号を振る代わりに、変更のたびに記録を残して、いつでも過去に戻れるようにする仕組みです。
2. Gitの3つの領域と「コミット」
Gitを理解する鍵は、ファイルが3つの領域(場所)を移動するというモデルです。
[ワークツリー] --add--> [ステージ] --commit--> [リポジトリ]
今編集中の場所 「確定予定」の待合室 履歴として保存
| 領域 | 別名 | 役割 |
|---|---|---|
| ワークツリー(working tree) | 作業ディレクトリ | あなたが今、実際にファイルを編集している場所 |
| ステージ(stage / index) | インデックス | 「次の記録に含める変更」をまとめておく待合室 |
| リポジトリ(repository) | — | 変更が履歴として保存される場所(.git/ 配下) |
コミット(commit)= 変更の1ページを歴史に追加する
git add report.md # ワークツリー → ステージ(「この変更を記録します」と指定)
git commit -m "レポートの誤字を修正" # ステージ → リポジトリ(履歴に1ページ追加)
コミット(commit) は、変更を**「歴史の1ページ」として保存する**ことです。1回のコミットには「いつ・誰が・何を・なぜ変えたか」が記録されます。
💡 なぜ「ワークツリー→ステージ→リポジトリ」と2段階なのか? —— 一度に関連する変更だけをまとめて記録するためです。「機能Aの変更」と「タイポ修正」を別々のコミットに分ければ、後で「機能Aを取り消す」が簡単にできます。ステージは「記録する粒度をコントロールする」ための仕組みです。
3. 保存・共有・取得の5コマンド —— どう違う?
Gitで一番混乱するのが、「保存・共有・取得」系のコマンドです。それぞれ「どこ↔どこ」を動くかで整理します。
| コマンド | 何をするか | 向き |
|---|---|---|
| clone(クローン) | リモート(GitHub)のリポジトリをまるごと手元にコピーして初次取得する | リモート → ローカル(最初の1回) |
| fetch(フェッチ) | リモートの最新情報を取得だけする(手元のファイルは書き換えない) | リモート → ローカル(確認用) |
| pull(プル) | リモートの最新を取得して、手元に取り込む(= fetch + merge) | リモート → ローカル(反映) |
| push(プッシュ) | 手元のコミットをリモートに送る(共有・公開) | ローカル → リモート |
| commit(コミット) | 手元の変更をローカルの履歴に保存する(※まだ共有されない) | ローカル内 |
よくある誤解:commit ≠ push
ここが重要です。commit しても、まだ他の人には見えていません。 push して初めてGitHubに届き、共有されます。
あなたの編集 → [commit] → ローカル履歴(あなただけ)
└→ [push] → GitHub(みんなに共有)
⚠ 「コミットしたのに共有されていない!」という事故は、push 忘れが原因です。逆に、commit せずに push しようとしても「記録がないので送るものがない」と弾かれます。commit(記録)→ push(共有)の2段階を覚えてください。
4. ブランチ —— 別々に進む「平行世界」
ここまでの履歴は「1本の直線」でした。しかし現場では、複数人が同時に別の作業をします。これを可能にするのが**ブランチ(branch)**です。
ブランチとは
ブランチは、履歴を枝分かれさせて、別々に進められる仕組みです。「平行世界」のようなもので、ある世界で機能Aを開発しながら、別の世界でバグ修正を進められます。
┌── (feature/login) ログイン機能を開発 ──┐
main ─────┤ ├── 統合
└── (feature/design) デザイン調整 ────┘
git branch feature/login # ブランチを作る
git switch feature/login # そのブランチに移動(=そちらの世界に入る)
# ...作業・コミット...
git switch main # main(中心の世界)に戻る
💡 main(旧称 master)は、リポジトリの**「中心となる安定した歴史」**を指すブランチ名です。多くの現場では「mainは常に動く・壊れない状態を保つ」という合意があります。
統合(merge)と3つの方法
別ブランチで進めた作業を、mainに**統合する(merge)**方法は、大きく3つあります:
| 方法 | 仕組み | 履歴の見た目 |
|---|---|---|
| fast-forward(早送り) | mainが動いていなければ、ポインタを「前に進める」だけ。枝が無かったことになる | 直線 |
| mergeコミット(通常マージ) | 両方の変更を合わせた「統合コミット」を作る。「どこで合流したか」が残る | 分岐・合流が明示 |
| rebase(リベース) | ブランチの変更を「mainの最新の先」に積み直す。履歴を一直線に整形できる | 直線(綺麗) |
⚠ rebase は履歴を書き換える操作です。綺麗な履歴になる反面、既に共有したコミットをrebaseすると他の人の履歴と矛盾して大混乱を引き起こします。そのため**「push済みのコミットはrebaseしない」「複雑なrebaseは慎重に」**が現場の鉄則です。特に git rebase -i(対話的rebase/コミット履歴を編集する強力な操作)は、取り返しのつかない変更もできるため、個人運用では避ける・チームでは運用ルールを決めるのが普通です。
5. Pull Request —— レビューを経て統合する作法
Pull Request(PR/プルリク) は、「この変更を取り込んでください(pullしてね)」とチームに依頼し、レビューを経て統合する仕組みです。
なぜ直接mainにpushせず、PRを経るのか
個人の一人プロジェクトなら、直接mainにpushしても問題ありません。しかしチーム開発ではPRベースが作法です。理由は:
| 直接pushのリスク | PRが防ぐこと |
|---|---|
| 壊れたコードがいきなりmainに入る | レビューが挟まり、問題を事前に発見できる |
| 「なぜこの変更をしたか」が分からない | PRの説明文に意図と背景を残せる |
| 変更の影響範囲が分からない | 差分(diff) とCI(→第1章)を確認してから統合できる |
| 誰が何をしたか追いにくい | PR単位で「機能追加の一連の作業」が整理される |
PRの流れ
1. トピックブランチを作る(feature/xxx)
2. 作業してコミット → push
3. GitHub上で「Pull Request」を作成
4. CIが自動で回る(テスト・Lint等)← 第1章 CI/CDの出番
5. レビュアーが確認 → コメント・承認
6. 問題なければ統合(merge)
7. ブランチを削除して完了
💡 PRは「完成した変更の提案」です。だからこそ、CIを通す・セルフレビューする・説明文を書くなど、提案する前に品質を整えるのが作法です(→第13章 レビュー文化で詳しく)。
6. コンフリクト —— 同じ場所を別変更した時の解決
複数人が同時に作業していると、同じファイルの同じ行を別の内容で変更してしまうことがあります。これが**コンフリクト(conflict/衝突)**です。
なぜ起きるか
main: 「今日は晴れです」
Aさんの変更:「今日はとても晴れです」 (「とても」を追加)
Bさんの変更:「今日は雨です」 (文全体を書き換え)
AもBも同じ行を変えたため、Gitは「どちらを正とするか」を自動判断できません。これを人間が決める必要があります。
コンフリクトの解決
コンフリクトが起きると、ファイルには両方の変更がマーカ付きで残ります:
<<<<<<< HEAD
今日はとても晴れです (あなたの変更)
=======
今日は雨です (相手の変更)
>>>>>>> feature/other
この部分を編集して「最終的にどうしたいか」1つにまとめ、マーカを消して保存→コミットします。
| 対処 | 意味 |
|---|---|
| 自分のを採用 | Aさんの変更を残す |
| 相手のを採用 | Bさんの変更を残す |
| 両方を組み合わせる | 新しく書き直して両方の意図を取り入れる(一番多い) |
⚠ コンフリクトは「異常」ではありません。並行作業の必然的な結果です。慌てずに「何をどうしたいか」を考え、必要なら相手と相談して解決するのがプロの対応です。mergeでもrebaseでも発生しえます。
7. GitHub回りの協働機能
GitHubは、Gitの履歴を共有するだけでなく、チームで協働するための機能を多数提供しています。
issue(イシュー)
issueは、「やるべきこと・問題・要望」を1件ずつ管理するチケットです。「バグ報告」「この機能を追加したい」「ドキュメントを整備」などをissueとして起票し、議論・対応します。多くのチームは「1issue = 1タスク」で運用します。
ラベル・マイルストーン
| 機能 | 役割 | 例 |
|---|---|---|
| ラベル(label) | issue/PRを分類する色付きタグ | bug(不具報)/ enhancement(機能追加)/ good first issue(初心者向け) |
| マイルストーン(milestone) | 期限・目標でissue/PRを束ねる | 「v1.0リリース」「スプリント7月」 |
fork・upstream・トピックブランチ戦略
他の人のプロジェクトに貢献する(OSS=オープンソースソフトウェアへの参加)時によく使う概念です:
| 用語 | 意味 |
|---|---|
| fork(フォーク) | 他人のリポジトリを自分のアカウントにコピーする(オリジナルとは別物として自由に編集できる) |
| upstream(アップストリーム) | コピー元の**「オリジナルのリポジトリ」**を指す呼び方(川の上流=源流のイメージ) |
| origin(オリジン) | 自分がpush/pullする相手(普通はforkした自分のリポジトリ)のデフォルトの呼び名 |
| トピックブランチ | 1つの目的(issue/機能)ごとに切るブランチ。feature/xxx fix/yyy 等。終わったら消す |
OSS貢献の流れ: fork → clone → トピックブランチ作成 → 作業 → push(自分のforkへ) → GitHub上でオリジナル(upstream)へPull Request → レビュー → 統合。
💡 「ブランチを切る=作る」「PRを出す=作成する」「マージする=統合する」など、現場では独特の言い回しが使われます。最初は用語の壁がありますが、やっていることは「枝を作って作業して、レビューして、本流に戻す」の繰り返しです。
8. コミットメッセージの作法
コミットメーションは**「履歴を読む人(未来の自分・チームメイト)へのメッセージ」**です。いい加減に書くと、後で「この変更、何のためにやったっけ?」と困ります。
Conventional Commits(規約化コミット)
現場で広く使われているコミットメッセージの書き方のルールが Conventional Commits です。冒頭に**変更の種類を表すキーワード(プレフィックス)**を付けます:
| プレフィックス | 意味 | 例 |
|---|---|---|
| feat | 新機能(feature) | feat: ログイン機能を追加 |
| fix | バグ修正 | fix: ログイン時のクラッシュを修正 |
| docs | ドキュメント変更 | docs: READMEを更新 |
| refactor | 挙動を変えない整理(リファクタリング) | refactor: 関数を分割して見やすく |
| test | テスト追加・修正 | test: ログインのテストを追加 |
| chore | 雑務(設定・ビルド・依存更新等) | chore: ライブラリを最新化 |
良いコミットメッセージのポイント
- 1行目は「何をしたか」を簡潔に(日本語で50字程度が目安)
- 1コミット1テーマ(関係ない変更を混ぜない)
- 「なぜ」は本文やPRに(1行目に詰め込まない)
# 良い例
git commit -m "feat: パスワードリセット機能を追加"
# 悪い例(情報がない)
git commit -m "修正"
git commit -m "いろいろ変えた"
💡 コミットメッセージは**「未来の自分への手紙」**です。「なぜこの変更が必要だったか」が履歴から読み取れるようにしておくと、後日のトラブル調査や振り返りが圧倒的に楽になります。
9. よくある失敗と対処
| 失敗 | 何が起きるか | 対処 |
|---|---|---|
| push忘れ | 「直したはず」が共有されておらず、他の人が古い版を見る | commit後はpushまでが1セットと覚える |
| commit粒度が大きすぎる | 「機能Aとタイポ修正と設定変更」が1コミットに混ざり、取り消しや調査が困難に | 1テーマ1コミットを心がける(ステージで取捨選択) |
| mainを直接いじって壊す | 中心の歴史が壊れ、全員に影響 | チーム開発ではトピックブランチ+PRを経る |
| 共有済みコミットをrebase | 他の人の履歴と矛盾し、深刻なコンフリクトが多発 | push済みはrebaseしない(特に rebase -i) |
| コンフリクトを放置 | マージできず開発が止まる | 慌てず両者の意図を読み取り、1つにまとめる |
まとめ
- Git はコードの変更履歴を記録・管理するバージョン管理システム。GitHub はそれを共有・協働するプラットフォーム
- ファイルはワークツリー → ステージ → リポジトリと流れ、
commitで記録・pushで共有(commit ≠ push) - ブランチで平行世界を作り、merge / rebase で統合。push済みのrebaseは厳禁
- Pull Request は、レビューとCIを経て安全に統合する作法。チーム開発の基本単位
- コンフリクトは並行作業の必然。人間が「どうしたいか」を決めて解決する
- コミットメッセージは未来の自分・チームへの手紙。Conventional Commits(feat/fix/docs…)で分かりやすく
💡 次のステップ: Git/GitHubで「コードを管理・共有」する作法が見えました。次は、そのコードの上でプログラム同士が通信する窓口である API を扱います。第3章では、エンドポイント・ステータスコード・REST/GraphQLなど、開発現場で毎日触れるAPIの常識を解説します。