Gitワークフローパターン
Gitバージョン管理、ブランチ戦略、および協調開発のベストプラクティス。
アクティブ化のタイミング
- 新しいプロジェクトのGitワークフローを設定するとき
- ブランチ戦略(GitFlow、Trunk-Based、GitHub Flow)を決定するとき
- コミットメッセージやプルリクエストの説明を書くとき
- マージコンフリクトを解決するとき
- リリースとバージョンタグを管理するとき
- 新しいチームメンバーをGitのプラクティスにオンボーディングするとき
ブランチ戦略
GitHub Flow(シンプル、ほとんどのチームに推奨)
継続的デプロイメントと小~中規模チームに最適。
main (保護、常にデプロイ可能)
│
├── feature/user-auth → PR → mainにマージ
├── feature/payment-flow → PR → mainにマージ
└── fix/login-bug → PR → mainにマージ
ルール:
mainは常にデプロイ可能であることmainからフィーチャーブランチを作成する- レビューの準備ができたらプルリクエストを開く
- 承認後、CIが合格したら
mainにマージする - マージ後すぐにデプロイする
Trunk-Based Development(高頻度チーム向け)
強力なCI/CDとフィーチャーフラグを持つチームに最適。
main (トランク)
│
├── 短命なフィーチャーブランチ(最大1-2日)
├── 短命なフィーチャーブランチ
└── 短命なフィーチャーブランチ
ルール:
- 全員が
mainまたは非常に短命なブランチにコミットする - フィーチャーフラグで未完成の作業を隠す
- マージ前にCIが合格している必要がある
- 1日に複数回デプロイする
GitFlow(複雑、リリースサイクル駆動)
スケジュールされたリリースとエンタープライズプロジェクトに最適。
main (プロダクションリリース)
│
└── develop (統合ブランチ)
│
├── feature/user-auth
├── feature/payment
│
├── release/1.0.0 → mainとdevelopにマージ
│
└── hotfix/critical → mainとdevelopにマージ
ルール:
mainはプロダクション準備完了のコードのみdevelopは統合ブランチ- フィーチャーブランチは
developから作成し、developにマージする - リリースブランチは
developから作成し、mainとdevelopにマージする - ホットフィックスブランチは
mainから作成し、mainとdevelopの両方にマージする
どの戦略をいつ使うか
| 戦略 | チーム規模 | リリース頻度 | 最適な用途 |
|---|---|---|---|
| GitHub Flow | 任意 | 継続的 | SaaS、Webアプリ、スタートアップ |
| Trunk-Based | 5+ 経験者 | 1日複数回 | 高頻度チーム、フィーチャーフラグ |
| GitFlow | 10+ | スケジュール制 | エンタープライズ、規制業界 |
コミットメッセージ
Conventional Commits形式
<型>(<スコープ>): <件名>
[本文(省略可)]
[フッター(省略可)]
型
| 型 | 使用目的 | 例 |
|---|---|---|
feat | 新機能 | feat(auth): add OAuth2 login |
fix | バグ修正 | fix(api): handle null response in user endpoint |
docs | ドキュメント | docs(readme): update installation instructions |
style | フォーマット(コード変更なし) | style: fix indentation in login component |
refactor | コードリファクタリング | refactor(db): extract connection pool to module |
test | テストの追加・更新 | test(auth): add unit tests for token validation |
chore | メンテナンスタスク | chore(deps): update dependencies |
perf | パフォーマンス改善 | perf(query): add index to users table |
ci | CI/CDの変更 | ci: add PostgreSQL service to test workflow |
revert | 以前のコミットを取り消し | revert: revert "feat(auth): add OAuth2 login" |
良い例と悪い例
# 悪い例: 曖昧、文脈がない
git commit -m "fixed stuff"
git commit -m "updates"
git commit -m "WIP"
# 良い例: 明確で具体的、理由を説明
git commit -m "fix(api): 503 Service Unavailable時にリクエストを再試行
外部APIがピーク時に503エラーを返すことがある。
指数バックオフのリトライロジックを追加し、最大3回試行する。
Closes #123"
コミットメッセージテンプレート
リポジトリルートに.gitmessageを作成:
# <型>(<スコープ>): <件名>
# # 型: feat, fix, docs, style, refactor, test, chore, perf, ci, revert
# スコープ: api, ui, db, auth など
# 件名: 命令形、ピリオドなし、最大50文字
#
# [本文(省略可)] - 何をしたかではなく、なぜしたかを説明
# [フッター(省略可)] - Breaking changes、Closes #issue
有効化: git config commit.template .gitmessage
マージ vs リベース
マージ(履歴を保持)
# マージコミットを作成
git checkout main
git merge feature/user-auth
# 結果:
# * マージコミット
# |\
# | * フィーチャーのコミット
# |/
# * mainのコミット
使用するタイミング:
- フィーチャーブランチを
mainにマージするとき - 正確な履歴を保持したいとき
- 複数人がブランチで作業したとき
- ブランチがプッシュされており、他の人がそのブランチをベースに作業している可能性があるとき
リベース(線形履歴)
# フィーチャーのコミットをターゲットブランチの先端に書き換える
git checkout feature/user-auth
git rebase main
# 結果:
# * フィーチャーのコミット(書き換え済み)
# * mainのコミット
使用するタイミング:
- ローカルのフィーチャーブランチを最新の
mainで更新するとき - 線形でクリーンな履歴にしたいとき
- ブランチがローカル限定(プッシュされていない)のとき
- 自分だけでそのブランチで作業しているとき
リベースワークフロー
# フィーチャーブランチを最新のmainで更新(PR前に)
git checkout feature/user-auth
git fetch origin
git rebase origin/main
# コンフリクトがあれば修正
# テストが通ることを確認
# 強制プッシュ(自分だけが作業している場合)
git push --force-with-lease origin feature/user-auth
リベースしてはいけない場合
# 以下のブランチでは絶対にリベースしない:
- 共有リポジトリにプッシュ済みのブランチ
- 他の人が作業をベースにしているブランチ
- 保護されたブランチ(main, develop)
- すでにマージされたブランチ
# 理由: リベースは履歴を書き換え、他の作業を壊す
プルリクエストワークフロー
PRタイトル形式
<型>(<スコープ>): <説明>
例:
feat(auth): add SSO support for enterprise users
fix(api): resolve race condition in order processing
docs(api): add OpenAPI specification for v2 endpoints
PR説明テンプレート
## 何をしたか
このPRで行った変更の簡単な説明。
## なぜしたか
動機と背景を説明。
## どのようにしたか
強調すべき重要な実装の詳細。
## テスト
- [ ] 単体テストを追加/更新
- [ ] 結合テストを追加/更新
- [ ] 手動テストを実施
## スクリーンショット(該当する場合)
UI変更の場合はBefore/Afterのスクリーンショット。
## チェックリスト
- [ ] コードがプロジェクトのスタイルガイドラインに従っている
- [ ] 自己レビューが完了している
- [ ] 複雑なロジックにはコメントを追加
- [ ] ドキュメントを更新
- [ ] 新しい警告が追加されていない
- [ ] ローカルでテストがパスする
- [ ] 関連するIssueがリンクされている
Closes #123
コードレビューチェックリスト
レビュアー向け:
- コードは問題を解決しているか?
- 未処理のエッジケースはないか?
- コードは読みやすく保守しやすいか?
- 十分なテストがあるか?
- セキュリティ上の懸念はないか?
- コミット履歴はきれいか(必要に応じてスカッシュされているか)?
作成者向け:
- レビュー依頼前に自己レビューが完了している
- CIが通っている(テスト、リント、型チェック)
- PRサイズが妥当(500行未満が理想的)
- 単一の機能/修正に関連している
- 説明が変更内容を明確に説明している
コンフリクト解決
コンフリクトの特定
# マージ前にコンフリクトを確認
git checkout main
git merge feature/user-auth --no-commit --no-ff
# コンフリクトがある場合、Gitが表示:
# CONFLICT (content): Merge conflict in src/auth/login.ts
# Automatic merge failed; fix conflicts and then commit the result.
コンフリクトの解決
# コンフリクトファイルを確認
git status
# ファイル内の競合マーカーを確認
# <<<<<<< HEAD
# mainからの内容
# =======
# フィーチャーブランチからの内容
# >>>>>>> feature/user-auth
# オプション1: 手動解決
# ファイルを編集し、マーカーを削除し、正しい内容を残す
# オプション2: マージツールを使用
git mergetool
# オプション3: 一方のバージョンを採用
git checkout --ours src/auth/login.ts # mainのバージョンを保持
git checkout --theirs src/auth/login.ts # フィーチャーのバージョンを保持
# 解決後、ステージングしてコミット
git add src/auth/login.ts
git commit
コンフリクト防止戦略
# 1. フィーチャーブランチは小さく、短命に保つ
# 2. mainに頻繁にリベースする
git checkout feature/user-auth
git fetch origin
git rebase origin/main
# 3. 共有ファイルを触る場合はチームと連絡を取る
# 4. 長期ブランチの代わりにフィーチャーフラグを使用する
# 5. PRを迅速にレビューしマージする
ブランチ管理
命名規則
# フィーチャーブランチ
feature/user-authentication
feature/JIRA-123-payment-integration
# バグ修正
fix/login-redirect-loop
fix/456-null-pointer-exception
# ホットフィックス(プロダクションの問題)
hotfix/critical-security-patch
hotfix/database-connection-leak
# リリース
release/1.2.0
release/2024-01-hotfix
# 実験/POC
experiment/new-caching-strategy
poc/graphql-migration
ブランチのクリーンアップ
# マージ済みのローカルブランチを削除
git branch --merged main | grep -v "^\*\|main" | xargs -n 1 git branch -d
# リモートブランチが削除されたリモート追跡参照を削除
git fetch -p
# ローカルブランチを削除
git branch -d feature/user-auth # 安全な削除(マージ済みの場合のみ)
git branch -D feature/user-auth # 強制削除
# リモートブランチを削除
git push origin --delete feature/user-auth
Stashワークフロー
# 作業中の内容を保存
git stash push -m "WIP: user authentication"
# スタッシュ一覧
git stash list
# 最新のスタッシュを適用して削除
git stash pop
# 特定のスタッシュを適用(削除なし)
git stash apply stash@{2}
# スタッシュを削除
git stash drop stash@{0}
リリース管理
セマンティックバージョニング
MAJOR.MINOR.PATCH
MAJOR: 互換性を破る変更
MINOR: 新機能、後方互換性あり
PATCH: バグ修正、後方互換性あり
例:
1.0.0 → 1.0.1 (patch: バグ修正)
1.0.1 → 1.1.0 (minor: 新機能)
1.1.0 → 2.0.0 (major: 互換性を破る変更)
リリース作成
# 注釈付きタグを作成
git tag -a v1.2.0 -m "Release v1.2.0
機能:
- ユーザー認証を追加
- パスワードリセットを実装
修正:
- ログインリダイレクトの問題を解決
互換性を破る変更:
- なし"
# タグをリモートにプッシュ
git push origin v1.2.0
# タグ一覧
git tag -l
# タグを削除
git tag -d v1.2.0
git push origin --delete v1.2.0
チェンジログ生成
# コミットからチェンジログを生成
git log v1.1.0..v1.2.0 --oneline --no-merges
# または conventional-changelog を使用
npx conventional-changelog -i CHANGELOG.md -s
Git設定
必須設定
# ユーザー情報
git config --global user.name "Your Name"
git config --global user.email "[email protected]"
# デフォルトブランチ名
git config --global init.defaultBranch main
# プル動作(マージではなくリベース)
git config --global pull.rebase true
# プッシュ動作(現在のブランチのみプッシュ)
git config --global push.default current
# タイプミスの自動修正
git config --global help.autocorrect 1
# より良いdiffアルゴリズム
git config --global diff.algorithm histogram
# カラー出力
git config --global color.ui auto
便利なエイリアス
# ~/.gitconfig に追加
[alias]
co = checkout
br = branch
ci = commit
st = status
unstage = reset HEAD --
last = log -1 HEAD
visual = log --oneline --graph --all
amend = commit --amend --no-edit
wip = commit -m "WIP"
undo = reset --soft HEAD~1
contributors = shortlog -sn
Gitignoreパターン
# 依存関係
node_modules/
vendor/
# ビルド出力
dist/
build/
*.o
*.exe
# 環境ファイル
.env
.env.local
.env.*.local
# IDE
.idea/
.vscode/
*.swp
*.swo
# OSファイル
.DS_Store
Thumbs.db
# ログ
*.log
logs/
# テストカバレッジ
coverage/
# キャッシュ
.cache/
*.tsbuildinfo
一般的なワークフロー
新機能の開始
# 1. mainブランチを更新
git checkout main
git pull origin main
# 2. フィーチャーブランチを作成
git checkout -b feature/user-auth
# 3. 変更を加えてコミット
git add .
git commit -m "feat(auth): implement OAuth2 login"
# 4. リモートにプッシュ
git push -u origin feature/user-auth
# 5. GitHub/GitLabでプルリクエストを作成
PRに新しい変更を反映
# 1. 追加の変更を加える
git add .
git commit -m "feat(auth): add error handling"
# 2. 更新をプッシュ
git push origin feature/user-auth
ForkをUpstreamと同期
# 1. upstreamリモートを追加(一度だけ)
git remote add upstream https://github.com/original/repo.git
# 2. upstreamをフェッチ
git fetch upstream
# 3. upstream/mainを自分のmainにマージ
git checkout main
git merge upstream/main
# 4. 自分のforkにプッシュ
git push origin main
間違いの取り消し
# 最後のコミットを取り消す(変更は保持)
git reset --soft HEAD~1
# 最後のコミットを取り消す(変更を破棄)
git reset --hard HEAD~1
# リモートにプッシュした最後のコミットを取り消す
git revert HEAD
git push origin main
# 特定のファイルの変更を取り消す
git checkout HEAD -- path/to/file
# 最後のコミットメッセージを修正
git commit --amend -m "New message"
# 最後のコミットに忘れたファイルを追加
git add forgotten-file
git commit --amend --no-edit
Gitフック
Pre-Commitフック
#!/bin/bash
# .git/hooks/pre-commit
# リンティングを実行
npm run lint || exit 1
# テストを実行
npm test || exit 1
# シークレットをチェック
if git diff --cached | grep -E '(password|api_key|secret)'; then
echo "シークレットが検出されました。コミットを中止します。"
exit 1
fi
Pre-Pushフック
#!/bin/bash
# .git/hooks/pre-push
# 全テストスイートを実行
npm run test:all || exit 1
# console.logステートメントをチェック
if git diff origin/main | grep -E 'console\.log'; then
echo "プッシュ前にconsole.logを削除してください。"
exit 1
fi
アンチパターン
# BAD: mainに直接コミット
git checkout main
git commit -m "fix bug"
# GOOD: フィーチャーブランチとPRを使用する
# BAD: シークレットをコミット
git add .env # APIキーが含まれている
# GOOD: .gitignoreに追加し、環境変数を使用する
# BAD: 巨大なPR(1000行以上)
# GOOD: 小さく焦点を絞ったPRに分割する
# BAD: 「update」というコミットメッセージ
git commit -m "update"
git commit -m "fix"
# GOOD: 説明的なメッセージ
git commit -m "fix(auth): ログイン後のリダイレクトループを解決"
# BAD: 公開履歴を書き換える
git push --force origin main
# GOOD: 公開ブランチではrevertを使用する
git revert HEAD
# BAD: 長期間のフィーチャーブランチ(数週間~数ヶ月)
# GOOD: ブランチは短期間(数日)に保ち、頻繁にリベースする