- はじめに
- 1. Gitの内部構造を理解する
- 2. 必須コマンドチートシート
- 3. ブランチ戦略の比較
- 4. Merge vs Rebase
- 5. 高度なコマンドをマスターする
- 6. .gitconfigの最適化
- 7. GitHub PRレビューのベストプラクティス
- 8. コミットメッセージのコンベンション
- 9. モノレポ管理
- 10. 危機対応プレイブック
- まとめ
はじめに
Gitは開発者が毎日使うツールだが、ほとんどの人はadd-commit-pushのループから抜け出せない。mergeコンフリクトが発生するとパニックになり、rebaseは怖く、bisectやworktreeの存在すら知らないことが多い。
この記事では、Gitの内部構造から始めて、実務で必ず知っておくべき高度なコマンド、ブランチ戦略、PRレビュー文化、モノレポ管理、危機対応まで、Gitのすべてを徹底的に解説する。
1. Gitの内部構造を理解する
Gitを正しく使うには、内部構造の理解が不可欠だ。Gitの本質はコンテンツアドレス可能なファイルシステム(content-addressable filesystem)、つまりキーバリューストアだ。
1.1 4つのコアオブジェクト
Gitのすべてのデータは4種類のオブジェクトとして保存される。
| オブジェクト | 役割 | 説明 |
|---|---|---|
| blob | ファイル内容 | ファイルのスナップショット(名前なし、内容のみ) |
| tree | ディレクトリ | blobや他のtreeを参照するリスト |
| commit | スナップショット | tree + 親コミット + メタデータ |
| tag | ラベル | 特定のコミットに付けるアノテーションタグ |
各オブジェクトはSHA-1ハッシュで識別される。同じ内容であれば同じハッシュになるため、Gitは自然に重複を排除する。
# オブジェクトタイプの確認
git cat-file -t HEAD
# コミットオブジェクトの内容を表示
git cat-file -p HEAD
# treeオブジェクトの探索
git ls-tree HEAD
1.2 3つの領域
Gitには3つの核心的な領域がある。
- Working Directory -- 実際のファイルが存在するディレクトリ。編集作業を行う空間だ。
- Staging Area(Index) -- 次のコミットに含める変更を準備する領域だ。
- Repository(.git) -- コミット履歴とすべてのオブジェクトが格納される空間だ。
# 3つの領域間のファイル移動フロー
# Working -> Staging
git add file.txt
# Staging -> Repository
git commit -m "feat: add file"
# Repository -> Working(特定ファイルの復元)
git checkout HEAD -- file.txt
1.3 HEADとref
- HEAD -- 現在チェックアウトされているコミットを指すポインタ。通常はブランチを指す。
- branch -- 特定のコミットを指す移動可能なポインタ。
- tag -- 特定のコミットを指す固定ポインタ。
# HEADの参照先を確認
cat .git/HEAD
# ref: refs/heads/main
# ブランチが指すコミットを確認
cat .git/refs/heads/main
# Detached HEAD状態にする
git checkout HEAD~2
2. 必須コマンドチートシート
日常的に使うコマンドをシーン別に整理した。
2.1 基本ワークフロー
# リポジトリの初期化・クローン
git init
git clone https://github.com/user/repo.git
# 変更の確認
git status
git diff # unstaged変更
git diff --staged # staged変更
git diff HEAD # すべての変更
# ステージング&コミット
git add -p # インタラクティブな部分ステージング
git commit -m "feat: login" # コミット
git commit --amend # 最後のコミットを修正
# リモート同期
git fetch origin # リモートの変更のみ取得
git pull --rebase origin main # fetch + rebase
git push origin feature/login # プッシュ
2.2 ブランチ管理
# ブランチの作成&切り替え
git branch feature/auth
git checkout -b feature/auth # 作成 + 切り替え
git switch -c feature/auth # Git 2.23以降の推奨方法
# ブランチ一覧
git branch -a # ローカル+リモート
git branch --merged # マージ済みブランチ
git branch -d feature/old # 安全な削除
git branch -D feature/old # 強制削除
# リモートブランチの整理
git remote prune origin
git fetch -p
2.3 stash -- 作業の一時保存
作業中に急いで別のブランチに切り替える必要がある時にstashを使う。
# 現在の変更を保存
git stash
git stash push -m "WIP: login form"
# stash一覧の確認
git stash list
# 復元
git stash pop # 取り出して削除
git stash apply stash@{0} # 取り出すが保持
# 特定のファイルだけstash
git stash push -m "partial" -- src/auth.ts
# stashをブランチに変換
git stash branch new-branch stash@{0}
3. ブランチ戦略の比較
3.1 Git Flow
最も伝統的なブランチモデルで、2010年にVincent Driessenが提案した。
構造:
main-- プロダクションコードdevelop-- 次のリリースの開発feature/*-- 機能開発release/*-- リリース準備hotfix/*-- 緊急バグ修正
適したチーム: リリースサイクルが長いプロジェクト(月1-2回)、複数バージョンを同時に維持する必要がある場合。
欠点: ブランチが多くて複雑になり、CI/CDとの相性が良くない。
3.2 GitHub Flow
GitHubが実際に使っているシンプルなモデル。
構造:
main-- 常にデプロイ可能な状態feature/*-- mainから分岐し、PRでマージ
適したチーム: 継続的デプロイ環境、小規模チーム、Webサービス。
ルール: mainは常にデプロイ可能であること。featureブランチで作業し、PRを通じてマージする。
3.3 Trunk-Based Development
Google、Metaなど大手IT企業が採用している戦略。
構造:
main(trunk)-- すべての開発者が直接コミットするか、short-livedブランチで作業- ブランチの寿命:最大1-2日
適したチーム: Feature Flagインフラがあるチーム、大規模組織、高速フィードバックループ。
3.4 比較まとめ
| 項目 | Git Flow | GitHub Flow | Trunk-Based |
|---|---|---|---|
| 複雑さ | 高い | 低い | 中程度 |
| デプロイ頻度 | 月1-2回 | 毎日 | 1日数回 |
| チーム規模 | 大規模 | 小〜中規模 | すべての規模 |
| Feature Flag必要 | いいえ | いいえ | はい |
| CI/CD相性 | 普通 | 良い | 非常に良い |
4. Merge vs Rebase
Gitで最も議論になるテーマの一つだ。
4.1 Merge -- 履歴の保存
# featureブランチをmainにマージ
git checkout main
git merge feature/auth
mergeは2つのブランチの共通祖先を見つけて3-way mergeを行い、マージコミットを生成する。履歴がそのまま保存されるため、「いつどのブランチで作業したか」を追跡できる。
4.2 Rebase -- きれいな履歴
# featureブランチでmainの最新変更を取り込む
git checkout feature/auth
git rebase main
rebaseはfeatureブランチのコミットをmainの先端に一つずつ再適用する。結果として直線的な履歴が作られる。
4.3 Interactive Rebase -- コミット整理の要
# 直近5つのコミットを整理
git rebase -i HEAD~5
インタラクティブリベースエディタで使えるコマンドは以下の通り。
- pick -- コミットを維持
- reword -- コミットメッセージを修正
- edit -- コミット内容を修正
- squash -- 前のコミットと統合(メッセージも統合)
- fixup -- 前のコミットと統合(メッセージは破棄)
- drop -- コミットを削除
# 実用例:PR前のコミット整理
# エディタで以下のように修正
# pick abc1234 feat: add login page
# squash def5678 fix: typo in login
# squash ghi9012 style: adjust padding
# -> 3つのコミットが1つに統合される
4.4 使い分けの指針
| シーン | 推奨 |
|---|---|
| PRマージ | Squash MergeまたはRebase Merge |
| ローカルコミット整理 | Interactive Rebase |
| 共有ブランチ | Merge(rebase禁止) |
| 個人featureブランチ | Rebaseでmainと同期 |
ゴールデンルール: すでにpushしたコミットはrebaseしてはならない。他の人が参照している可能性がある。
5. 高度なコマンドをマスターする
5.1 cherry-pick -- 特定のコミットだけ取得
別のブランチの特定のコミット1つを現在のブランチにコピーする。
# 特定のコミットを取得
git cherry-pick abc1234
# 複数のコミットを一度に
git cherry-pick abc1234 def5678
# 範囲で取得(abcは含まず、defは含む)
git cherry-pick abc1234..def5678
# コミットせずに変更だけ適用
git cherry-pick --no-commit abc1234
ユースケース:
- hotfixブランチのバグ修正をdevelopにも適用
- 間違ったブランチにコミットしたものを正しいブランチに移動
- リリースブランチに特定の機能だけ選択的に含める
5.2 bisect -- 二分探索でバグを見つける
数百のコミットの中からバグが発生したコミットを二分探索で特定する。
# bisect開始
git bisect start
# 現在(バグあり)をbadとしてマーク
git bisect bad
# 正常だったコミットをgoodとしてマーク
git bisect good v2.0.0
# Gitが中間のコミットをチェックアウト -> テスト -> 判定
git bisect good # または git bisect bad
# 原因コミットが見つかったらリセット
git bisect reset
自動化も可能だ。
# テストスクリプトによる自動bisect
git bisect start HEAD v2.0.0
git bisect run npm test
Gitが自動的に各コミットでテストを実行し、最初に失敗するコミットを特定してくれる。
5.3 worktree -- 複数ブランチの同時作業
1つのリポジトリから複数の作業ディレクトリを作成し、異なるブランチを同時に作業できる。
# 新しいworktreeを作成
git worktree add ../project-hotfix hotfix/critical-bug
# worktree一覧の確認
git worktree list
# worktreeの削除
git worktree remove ../project-hotfix
# 新しいブランチを作りながらworktreeを作成
git worktree add -b feature/new-ui ../project-ui
ユースケース:
- mainでコードレビューしながらfeatureで開発を続ける
- 緊急hotfixと現在の作業を同時に進行
- CIビルドを待つ間に別の作業を開始
5.4 reflog -- ミス復旧の最後の砦
reflogはHEADが指したすべての場所の記録だ。誤ってコミットを失っても、reflogで復旧できる。
# reflogの確認
git reflog
# 誤ってreset --hardした時の復旧
git reflog
# HEAD@{2}: commit: important feature を見つけたら
git reset --hard HEAD@{2}
# 削除されたブランチの復旧
git reflog
git checkout -b recovered-branch HEAD@{5}
# 特定期間の記録
git reflog --since="2 days ago"
注意: reflogはローカルにのみ存在し、デフォルトで90日後に期限切れとなる。
6. .gitconfigの最適化
生産性を高めるGit設定を紹介する。
6.1 便利なエイリアス
[alias]
# ステータス&ログ
st = status -sb
lg = log --oneline --graph --decorate --all
last = log -1 HEAD --stat
# ブランチ
co = checkout
sw = switch
br = branch -vv
brd = branch -d
# コミット
cm = commit -m
ca = commit --amend --no-edit
undo = reset HEAD~1 --mixed
# diff
df = diff --stat
dfc = diff --cached
# stash
sl = stash list
sp = stash pop
ss = stash push -m
# クリーンアップ
cleanup = "!git branch --merged | grep -v '\\*\\|main\\|develop' | xargs -n 1 git branch -d"
6.2 コア設定
[core]
editor = code --wait
autocrlf = input # macOS/Linux
ignorecase = false
pager = delta # deltaペイジャーを使用
[pull]
rebase = true # pull時に常にrebase
[push]
default = current # 現在のブランチのみpush
autoSetupRemote = true # push時にupstreamを自動設定
[init]
defaultBranch = main
[diff]
tool = vscode
colorMoved = default
[merge]
conflictstyle = diff3 # 3-wayコンフリクト表示
tool = vscode
[rerere]
enabled = true # コンフリクト解決を記憶
6.3 delta -- より良いdiffツール
deltaはGit diffの出力を見やすく変換するツールだ。
# インストール
brew install git-delta
# .gitconfigに追加
[core]
pager = delta
[interactive]
diffFilter = delta --color-only
[delta]
navigate = true
line-numbers = true
side-by-side = true
7. GitHub PRレビューのベストプラクティス
7.1 良いPRの条件
- サイズ: 200〜400行以下。それ以上なら分割する。
- 単一目的: 1つのPRは1つの変更のみ。
- 説明: なぜこの変更が必要か、どうテストしたかを記述。
- セルフレビュー: 提出前に自分でレビュー。
7.2 PRテンプレート
## 変更内容
- 何をなぜ変更したか
## テスト
- [ ] ユニットテストの追加・修正
- [ ] ローカルで動作確認
- [ ] エッジケースの検証
## スクリーンショット(UI変更時)
## 関連Issue
- closes #123
7.3 レビューエチケット
レビュアーとして:
- コードの「意図」をまず理解する。スタイルよりロジックに集中。
- 質問形式でフィードバックする:「この部分はなぜこうしたのですか?」は「これは間違いです」よりも効果的。
- nit、suggestion、question、blockerなどコメントタイプを区別する。
- Approve、Request Changes、Commentを明確に使い分ける。
作成者として:
- レビュアーの時間を尊重する。PRを小さく保つ。
- すべてのコメントに返答する(最低でもリアクション)。
- force push後にはレビュアーに通知する。
7.4 CODEOWNERS
# .github/CODEOWNERS
# コードベース全体
* @team-lead
# フロントエンド
/src/components/ @frontend-team
/src/pages/ @frontend-team
# バックエンドAPI
/src/api/ @backend-team
# インフラ
/terraform/ @devops-team
/k8s/ @devops-team
# セキュリティ上重要なファイル
/src/auth/ @security-team @team-lead
8. コミットメッセージのコンベンション
8.1 Conventional Commits
type(scope): description
body(オプション)
footer(オプション)
typeリファレンス:
| type | 説明 |
|---|---|
| feat | 新機能 |
| fix | バグ修正 |
| docs | ドキュメント変更 |
| style | コードフォーマット(機能変更なし) |
| refactor | リファクタリング |
| perf | パフォーマンス改善 |
| test | テストの追加・修正 |
| chore | ビルド・設定変更 |
| ci | CI設定変更 |
# 例
git commit -m "feat(auth): add Google OAuth login"
git commit -m "fix(api): handle null response from payment gateway"
git commit -m "docs(readme): update installation instructions"
# Breaking Change
git commit -m "feat(api)!: change response format to JSON:API"
8.2 gitmoji
絵文字でコミットタイプを視覚的に区別する。
git commit -m ":sparkles: add user profile page"
git commit -m ":bug: fix login redirect loop"
git commit -m ":recycle: refactor database connection pool"
git commit -m ":white_check_mark: add unit tests for auth module"
8.3 commitlintで強制する
# インストール
npm install -D @commitlint/cli @commitlint/config-conventional
# commitlint.config.js
module.exports = {
extends: ['@commitlint/config-conventional'],
rules: {
'type-enum': [2, 'always', [
'feat', 'fix', 'docs', 'style',
'refactor', 'perf', 'test', 'chore', 'ci'
]],
'subject-max-length': [2, 'always', 72],
},
};
9. モノレポ管理
9.1 git sparse-checkout
大規模モノレポで必要なディレクトリだけチェックアウトする。
# sparse-checkoutを有効化
git sparse-checkout init --cone
# 必要なディレクトリのみ指定
git sparse-checkout set packages/frontend packages/shared
# ディレクトリを追加
git sparse-checkout add packages/backend
# 設定の確認
git sparse-checkout list
# 全体を復元
git sparse-checkout disable
9.2 git subtree
外部リポジトリをサブディレクトリとして統合管理する。
# 外部リポジトリを追加
git subtree add --prefix=libs/shared-utils \
https://github.com/org/shared-utils.git main --squash
# 変更を取り込む
git subtree pull --prefix=libs/shared-utils \
https://github.com/org/shared-utils.git main --squash
# 変更をプッシュバック
git subtree push --prefix=libs/shared-utils \
https://github.com/org/shared-utils.git main
9.3 Nx / Turborepoとの連携
モノレポビルドツールとGitを連携させると、変更されたパッケージのみビルド・テストできる。
# Nx:影響を受けたプロジェクトのみテスト
npx nx affected --target=test --base=main --head=HEAD
# Turborepo:変更されたパッケージのみビルド
npx turbo run build --filter=...[HEAD~1]
# GitHub Actionsでの変更検知
# CIでmainと比較し、変更されたパッケージのみ処理
10. 危機対応プレイブック
10.1 force pushからの復旧
誰かが誤ってforce pushしてしまった場合の対処法。
# 1. reflogで元のコミットを見つける
git reflog show origin/main
# 2. 元のコミットに復旧
git push origin HEAD@{1}:main --force
# 3. チームに通知
# 「mainブランチが復旧されました。git pull --rebaseを実行してください」
予防策:
# mainブランチのforce pushをブロック(GitHub Settings)
# Settings -> Branches -> Branch protection rules
# 「Restrict force pushes」を有効化
10.2 機密情報の削除(BFG Repo-Cleaner)
パスワードやAPIキーなどを誤ってコミットしてしまった場合。
# BFGのインストール
brew install bfg
# 特定ファイルを履歴から完全に削除
bfg --delete-files secrets.env
# 特定テキストの置換
bfg --replace-text passwords.txt
# クリーンアップ
git reflog expire --expire=now --all
git gc --prune=now --aggressive
# 強制プッシュ
git push --force
重要な注意事項:
- BFG実行前に必ずリポジトリをバックアップする。
- すでにcloneしたチームメンバーにはfresh cloneを依頼する必要がある。
- GitHubにキャッシュのクリアを依頼する必要がある場合もある。
10.3 大容量ファイルの管理(Git LFS)
バイナリファイル、メディアファイルなどの大容量ファイルにはGit LFSを使用する。
# Git LFSのインストールと初期化
brew install git-lfs
git lfs install
# 追跡するファイルパターンを指定
git lfs track "*.psd"
git lfs track "*.zip"
git lfs track "assets/videos/*"
# .gitattributesが自動生成される -- コミットが必要
git add .gitattributes
git commit -m "chore: configure Git LFS tracking"
# LFS追跡ファイルの一覧
git lfs ls-files
# すでにコミットされた大容量ファイルをLFSに移行
git lfs migrate import --include="*.psd" --everything
10.4 間違ったmergeを取り消す
# マージコミット自体をrevert
git revert -m 1 MERGE_COMMIT_SHA
# -m 1:第1の親(main)を基準に戻す
# -m 2:第2の親(feature)を基準に戻す
10.5 すべてを失った時の最終手段
# 1. reflogを確認(90日以内)
git reflog
# 2. danglingオブジェクトから復旧
git fsck --lost-found
# 3. 最悪の場合:チームメンバーのローカルリポジトリから復旧
# pushを依頼するか、bundleで受け取る
git bundle create backup.bundle --all
まとめ
Gitは単なるバージョン管理ツールではない。チームの協業方式を定義し、コード品質を保証し、デプロイパイプラインの基盤となるコアインフラだ。
この記事で扱った内容を要約する。
- 内部構造を理解すれば、コマンドが直感的に理解できる。
- ブランチ戦略はチームの状況に合わせて選択する -- 正解はない。
- rebaseはローカルで、mergeは共有ブランチで使う。
- cherry-pick、bisect、worktree、reflogは実戦で威力を発揮する。
- PRレビュー文化がコード品質を決定する。
- Conventional Commitsでコミット履歴をきれいに維持する。
- 危機的状況では、reflogとBFGが命綱となる。
Gitをマスターすることは一朝一夕にはできない。しかし、毎日1つずつ新しいコマンドを試し、内部構造の理解を深めていけば、いつの間にかどんな状況でも動じないGitマスターになっているはずだ。
현재 단락 (1/305)
Gitは開発者が毎日使うツールだが、ほとんどの人はadd-commit-pushのループから抜け出せない。mergeコンフリクトが発生するとパニックになり、rebaseは怖く、bisectやworkt...