Git でコンフリクトが起きたときの正しい解決手順と再発防止策

チーム開発を進めていく中で、必ずと言っていいほど遭遇するのがGitコンフリクトです。「またコンフリクトが起きた」「どうやって解決すればいいの?」と頭を抱えた経験をお持ちの方も多いのではないでしょうか。
コンフリクトは確かに面倒ですが、正しい知識と手順さえ身につければ、冷静に対処できるようになります。むしろ、コンフリクトを適切に解決することで、より品質の高いコードベースを維持できるのです。
この記事では、Gitコンフリクトの基礎知識から実際の解決手順、そして二度と同じ問題で悩まないための予防策まで、体系的にお伝えいたします。
Git コンフリクトとは何か
コンフリクトが発生する仕組み
Gitコンフリクトは、複数の人が同じファイルの同じ部分を異なる方法で変更した際に発生します。これは、Gitが「どちらの変更が正しいか判断できない」状況を意味しています。
以下の図で、コンフリクト発生のメカニズムを確認しましょう。
mermaidflowchart TD
main[main ブランチ] --> commit1[コミット A]
main --> feature1[feature ブランチ]
main --> feature2[hotfix ブランチ]
feature1 --> change1[同じファイルを変更]
feature2 --> change2[同じファイルを変更]
change1 --> merge1[マージ試行]
change2 --> merge1
merge1 --> conflict[コンフリクト発生!]
style conflict fill:#ff9999
style change1 fill:#ffeb9c
style change2 fill:#ffeb9c
この図が示すように、異なるブランチで同じファイルに変更が加わると、マージ時にGitは自動的に統合できず、開発者に判断を委ねることになります。
コンフリクトのタイプ別分類
Gitコンフリクトには主に以下の種類があります。
# | タイプ | 説明 | 発生頻度 |
---|---|---|---|
1 | テキストコンフリクト | 同じ行の文字列変更 | 高 |
2 | 削除 vs 変更 | 一方で削除、他方で変更 | 中 |
3 | ファイル名変更 | 同じファイルを異なる名前に変更 | 低 |
4 | バイナリファイル | 画像やドキュメントファイル | 低 |
最も頻繁に発生するのはテキストコンフリクトですので、まずはこちらの解決方法をマスターすることが重要です。
コンフリクトが起きる主な原因
同一ファイル・同一行の変更
最も一般的なコンフリクトの原因は、複数の開発者が同じファイルの同じ行を変更することです。
javascript// 開発者Aの変更
const message = "Hello World";
// 開発者Bの変更
const message = "こんにちは、世界";
このような状況で、GItは自動的にどちらを採用すべきか判断できません。
javascript// ファイル内で実際に表示されるコンフリクトマーカー
<<<<<<< HEAD
const message = "Hello World";
=======
const message = "こんにちは、世界";
>>>>>>> feature-branch
コンフリクトマーカーの意味を理解することで、解決の第一歩となります。
マージ戦略の問題
Gitには複数のマージ戦略があり、不適切な戦略選択がコンフリクトを複雑にする場合があります。
mermaidflowchart LR
A[Fast-forward] --> B{同一直線上?}
C[3-way merge] --> D{複数親コミット}
E[Squash merge] --> F{履歴を圧縮}
B -->|Yes| G[コンフリクトなし]
B -->|No| H[別戦略必要]
D -->|複雑| I[コンフリクト発生しやすい]
style I fill:#ff9999
マージ戦略の理解により、コンフリクトが起きやすい状況を事前に把握できます。
リベースでのコンフリクト
リベース操作は履歴を美しく保てますが、コンフリクトが発生しやすい操作でもあります。
bash# リベース中のコンフリクト例
git rebase main
# Auto-merging src/utils.js
# CONFLICT (content): Merge conflict in src/utils.js
# error: could not apply 7eb2c4a... Update utility function
リベース時のコンフリクトは、一つ一つのコミットに対して解決が必要になるため、特に注意が必要です。
基本的なコンフリクト解決手順
Git ステータスの確認方法
コンフリクトが発生したら、まず現状を正確に把握することが重要です。
bash# 現在の状況を確認
git status
出力例を見てみましょう。
bash# 典型的なコンフリクト状態の出力
On branch feature-login
You have unmerged paths.
(fix conflicts and run "git commit")
(use "git merge --abort" to abort the merge)
Unmerged paths:
(use "git add <file>..." to mark resolution)
both modified: src/auth.js
both modified: src/config.js
この出力から、src/auth.js
とsrc/config.js
でコンフリクトが発生していることがわかります。
コンフリクトファイルの特定
次に、具体的にどの部分でコンフリクトが起きているかを確認します。
bash# コンフリクトの詳細確認
git diff --name-only --diff-filter=U
このコマンドで、コンフリクトが発生しているファイル一覧を取得できます。
bash# 特定ファイルの差分確認
git diff src/auth.js
マージツールを使った解決
Visual Studio Codeを使用している場合、内蔵のマージツールが非常に便利です。
bash# VS Code でコンフリクトファイルを開く
code src/auth.js
VS Codeでは以下のような表示になります。
javascript// VS Code のコンフリクト表示例
<<<<<<< Current Change (HEAD)
function authenticate(user) {
return jwt.sign({id: user.id}, SECRET_KEY);
}
=======
function authenticate(userData) {
return jwt.sign({userId: userData.id}, JWT_SECRET);
}
>>>>>>> Incoming Change
VS Codeでは「Accept Current Change」「Accept Incoming Change」「Accept Both Changes」のボタンが表示され、クリック操作で解決できます。
手動での解決方法
エディタを使わず、手動で解決する場合の手順です。
bash# 1. ファイルを開いてマーカーを確認
cat src/auth.js | grep -A5 -B5 "<<<<<<< HEAD"
マーカーを理解して、適切なコードを残します。
javascript// 解決後のコード例(両方の良い部分を統合)
function authenticate(userData) {
// ユーザーデータの検証
if (!userData || !userData.id) {
throw new Error('Invalid user data');
}
// JWT トークンの生成
return jwt.sign({userId: userData.id}, JWT_SECRET, {expiresIn: '1h'});
}
解決後は、必ずファイルをステージングしてコミットします。
bash# 解決したファイルをステージング
git add src/auth.js
# コンフリクト解決をコミット
git commit -m "Resolve merge conflict in authentication function"
実践的なコンフリクト解決例
ケース1: シンプルなコードの競合
最も基本的なケースから見てみましょう。設定ファイルでの値の競合です。
javascript// config.js でのコンフリクト例
<<<<<<< HEAD
const API_URL = "https://api-prod.example.com";
const TIMEOUT = 5000;
=======
const API_URL = "https://api-staging.example.com";
const TIMEOUT = 3000;
>>>>>>> feature-api-update
このケースでは、本番環境とステージング環境のURLが競合しています。適切な解決方法を考えてみましょう。
javascript// 環境に応じた設定に統合
const API_URL = process.env.NODE_ENV === 'production'
? "https://api-prod.example.com"
: "https://api-staging.example.com";
const TIMEOUT = 5000; // 本番環境の値を採用
このように、単純に片方を選ぶのではなく、より良い解決策を考えることが重要ですね。
ケース2: 複数ファイルでの競合
大きな機能追加で複数ファイルに競合が発生した場合の対処法です。
以下のフローで、効率的に複数ファイルのコンフリクトを解決していきます。
mermaidflowchart TD
start[コンフリクト発生] --> list[コンフリクトファイル一覧取得]
list --> priority[優先順位決定]
priority --> core[コアファイルから解決]
core --> test[テストファイル解決]
test --> config[設定ファイル解決]
config --> verify[動作確認]
verify --> commit[一括コミット]
優先順位に従って解決することで、依存関係によるエラーを避けられます。
bash# 複数ファイルのコンフリクト確認
git status | grep "both modified"
出力例:
bashboth modified: src/user.js
both modified: tests/user.test.js
both modified: config/database.js
ここでは、以下の順序で解決していきます。
src/user.js
(コアロジック)config/database.js
(設定)tests/user.test.js
(テスト)
各ファイルを順番に解決し、一つずつステージングしていきます。
bash# ファイルごとに解決してステージング
git add src/user.js
git add config/database.js
git add tests/user.test.js
# 最後に一括コミット
git commit -m "Resolve conflicts in user management feature"
ケース3: 削除 vs 変更の競合
一方のブランチでファイルを削除し、他方で変更した場合のコンフリクトです。
bash# 削除vs変更のコンフリクト例
CONFLICT (modify/delete): old-utils.js deleted in HEAD and modified in feature-branch.
Version feature-branch of old-utils.js left in tree.
このケースでは、以下の選択肢があります。
# | 選択肢 | 実行コマンド | 適用場面 |
---|---|---|---|
1 | 削除を採用 | git rm old-utils.js | ファイルが不要な場合 |
2 | 変更を採用 | git add old-utils.js | ファイルが必要な場合 |
3 | 新しいファイル名で保存 | git mv old-utils.js new-utils.js | リファクタリング中 |
適切な判断のために、変更内容を必ず確認しましょう。
bash# 変更内容の確認
git show feature-branch:old-utils.js
再発防止のためのベストプラクティス
ブランチ戦略の改善
コンフリクトを減らすためのブランチ管理戦略をご紹介します。
mermaidflowchart LR
main[main] --> develop[develop]
develop --> feature1[feature/user-auth]
develop --> feature2[feature/payment]
develop --> feature3[feature/dashboard]
feature1 --> pr1[PR作成]
feature2 --> pr2[PR作成]
feature3 --> pr3[PR作成]
pr1 --> review[コードレビュー]
pr2 --> review
pr3 --> review
review --> merge[develop へマージ]
merge --> deploy[main へマージ・デプロイ]
style merge fill:#99ff99
style deploy fill:#99ccff
このような Git Flow を採用することで、コンフリクトを最小限に抑えられます。
重要なのは、長期間ブランチを分離させないことです。
bash# 定期的な develop との同期
git checkout feature-branch
git pull origin develop
git rebase develop
この習慣により、大きなコンフリクトを避けられます。
定期的なマージ・リベース
開発中は、定期的にメインブランチの変更を取り込むことが重要です。
bash# 毎日の作業開始時に実行する習慣
git checkout main
git pull origin main
git checkout feature-branch
git rebase main
この手順を毎日実行することで、コンフリクトを小さく分割して対処できます。
# | タイミング | 実行コマンド | 効果 |
---|---|---|---|
1 | 作業開始時 | git pull origin main | 最新状態の確認 |
2 | 機能完成時 | git rebase main | 履歴の整理 |
3 | PR 作成前 | git pull origin develop | 最終チェック |
チーム内のコミュニケーション強化
技術的な対策に加えて、チーム内でのコミュニケーションも重要な要素です。
以下のようなルールを設けることで、コンフリクトの発生を大幅に減らすことができます。
markdown# チーム開発ルール例
## 1. 作業分担の明確化
- 同じファイルを複数人で同時編集しない
- 大きな変更前にはチームで相談する
## 2. コミットメッセージの統一
- [feat] 新機能追加
- [fix] バグ修正
- [refactor] リファクタリング
- [docs] ドキュメント更新
## 3. プルリクエストのサイズ制限
- 変更は 500 行以下に抑制
- 機能単位で細かく分割
チーム全体でこれらのルールを共有し、守ることで、コンフリクトの頻度を大きく減らせます。
基本的なコンフリクト解決手順
Git ステータスの確認方法
コンフリクトが発生したら、慌てずに状況を確認することから始めましょう。
bash# 基本的な状況確認コマンド
git status --porcelain
出力結果の読み方:
bash# コンフリクト状態のファイル表示例
UU src/main.js # 両方で変更されたファイル
AA src/config.js # 両方で追加されたファイル
DD src/old.js # 両方で削除されたファイル
DU src/temp.js # 一方で削除、他方で変更
このように、先頭の文字でコンフリクトの種類を判断できるのです。
コンフリクトファイルの特定
全体状況が把握できたら、具体的なコンフリクト箇所を特定していきます。
bash# コンフリクトマーカーを含む行を検索
git grep -n "<<<<<<< HEAD"
実行例:
bashsrc/auth.js:15:<<<<<<< HEAD
src/utils.js:23:<<<<<<< HEAD
tests/auth.test.js:8:<<<<<<< HEAD
これにより、3つのファイルの具体的な行でコンフリクトが発生していることがわかります。
マージツールを使った解決
Git には様々なマージツールとの連携機能があります。
bash# 利用可能なマージツールの確認
git mergetool --tool-help
推奨ツールの設定:
bash# VS Code をマージツールに設定
git config --global merge.tool vscode
git config --global mergetool.vscode.cmd 'code --wait $MERGED'
# マージツールの起動
git mergetool
マージツールを使用すると、視覚的にコンフリクトを解決できます。
手動での解決方法
コマンドラインで手動解決する場合の詳細手順です。
bash# 1. コンフリクトファイルを編集
vim src/auth.js
# 2. マーカーを削除して適切なコードに修正
# (エディタでマーカーを探して編集)
# 3. 修正結果をステージング
git add src/auth.js
# 4. 全ファイル解決後にコミット
git commit
手動解決のポイント:
<<<<<<<
、=======
、>>>>>>>
マーカーをすべて削除- 必要なコードのみを残す
- コードの動作を確認する
実践的なコンフリクト解決例
ケース1: シンプルなコードの競合
実際のコードで発生したコンフリクトの解決例をご紹介します。
まず、コンフリクトが発生した状況を確認しましょう。
javascript// src/calculator.js でのコンフリクト
function add(a, b) {
<<<<<<< HEAD
// エラーハンドリング追加
if (typeof a !== 'number' || typeof b !== 'number') {
throw new Error('引数は数値である必要があります');
}
return a + b;
=======
// ログ機能追加
console.log(`計算実行: ${a} + ${b}`);
return a + b;
>>>>>>> feature-logging
このケースでは、一方でエラーハンドリング、他方でログ機能が追加されています。
解決策として、両方の機能を統合します。
javascript// 統合された解決案
function add(a, b) {
// エラーハンドリング
if (typeof a !== 'number' || typeof b !== 'number') {
throw new Error('引数は数値である必要があります');
}
// ログ機能
console.log(`計算実行: ${a} + ${b}`);
return a + b;
}
両方の機能を活かした、より良いコードになりました。
ケース2: 複数ファイルでの競合
大きな機能開発で複数ファイルに競合が発生した場合の対処例です。
発生状況の確認:
bash# 競合ファイル一覧
git status --short | grep "^UU"
結果:
bashUU src/user.js
UU src/profile.js
UU tests/user.test.js
解決戦略を図で表すと以下のようになります。
mermaidsequenceDiagram
participant Dev as 開発者
participant Core as コアファイル
participant Test as テストファイル
participant CI as CI/CD
Dev->>Core: 1. user.js 解決
Dev->>Core: 2. profile.js 解決
Dev->>Test: 3. user.test.js 解決
Dev->>CI: 4. テスト実行
CI-->>Dev: 5. 結果確認
Dev->>CI: 6. コミット
この順序で解決することで、依存関係のエラーを避けられます。
ファイルごとの解決手順:
bash# 1. user.js の解決
code src/user.js
git add src/user.js
# 2. profile.js の解決
code src/profile.js
git add src/profile.js
# 3. テストファイルの解決
code tests/user.test.js
git add tests/user.test.js
全ファイル解決後、動作確認を行います。
bash# テストの実行で動作確認
yarn test user
問題なければコミットして完了です。
bashgit commit -m "Resolve conflicts in user profile feature
- Merge user authentication improvements
- Integrate profile update functionality
- Update corresponding test cases"
ケース3: 削除 vs 変更の競合
ファイルのリファクタリング中によく発生するパターンです。
bash# 発生状況の例
CONFLICT (modify/delete): legacy-auth.js deleted in HEAD and modified in feature-oauth.
Version feature-oauth of legacy-auth.js left in tree.
このケースでは、メインブランチで古い認証ファイルを削除し、フィーチャーブランチで OAuth 機能を追加したために競合が発生しています。
まず、変更内容を確認します。
bash# 変更されたファイルの内容確認
git show feature-oauth:legacy-auth.js
確認した内容をもとに、適切な判断を行います。
javascript// legacy-auth.js の変更内容例
// OAuth 2.0 対応が追加されていた場合
export function authenticateWithOAuth(provider, token) {
// 新しい OAuth 認証ロジック
return oauth.validate(provider, token);
}
この場合、新しい認証システムに機能を統合する方針で解決します。
bash# 新しいファイルとして保存
git rm legacy-auth.js
git add src/oauth-auth.js
新しいファイルに機能を移行:
javascript// src/oauth-auth.js
export function authenticateWithOAuth(provider, token) {
// OAuth 2.0 認証処理
const validationResult = oauth.validate(provider, token);
if (!validationResult.success) {
throw new Error(`OAuth認証に失敗しました: ${validationResult.error}`);
}
return {
userId: validationResult.userId,
accessToken: validationResult.accessToken,
expiresAt: validationResult.expiresAt
};
}
トラブルシューティング
よくあるエラーと対処法
コンフリクト解決中に遭遇しやすいエラーとその対処法をまとめました。
エラー1: error: your local changes to the following files would be overwritten by merge
このエラーは、未コミットの変更があることが原因です。
bash# エラーメッセージ例
error: Your local changes to the following files would be overwritten by merge:
src/config.js
Please commit your changes or stash them before you merge.
対処方法:
bash# 変更を一時保存
git stash push -m "作業途中の変更を保存"
# マージを再実行
git merge feature-branch
# 保存した変更を復元
git stash pop
エラー2: fatal: cannot do a partial commit during a merge
マージ中に特定のファイルのみコミットしようとした場合のエラーです。
bash# 間違ったコマンド例
git commit src/auth.js
# fatal: cannot do a partial commit during a merge.
正しい対処法:
bash# 全ファイルを解決してからコミット
git add .
git commit -m "Resolve all merge conflicts"
復旧手順
解決作業中に問題が発生した場合の復旧方法です。
bash# マージを中断して元の状態に戻す
git merge --abort
# リベース中の場合
git rebase --abort
# 特定のコミットまで戻す場合
git reset --hard HEAD~1
ただし、reset --hard
は変更を完全に失うので注意が必要ですね。
安全な復旧のためのバックアップ:
bash# 作業前にバックアップブランチを作成
git checkout -b backup-before-merge
git checkout feature-branch
# マージ実行
git merge main
バックアップがあれば、何か問題が起きても安心して復旧できます。
mermaidstateDiagram-v2
[*] --> Working: 通常の開発
Working --> Backup: バックアップ作成
Backup --> Merge: マージ実行
Merge --> Success: 成功
Merge --> Conflict: コンフリクト発生
Conflict --> Resolve: 解決作業
Conflict --> Abort: 作業中断
Resolve --> Success: 解決完了
Abort --> Working: 元の状態に復旧
Success --> [*]
このフローに従うことで、安全にコンフリクト解決作業を進められます。
まとめ
Gitコンフリクトは避けて通れない開発課題ですが、正しい知識と手順があれば決して恐れる必要はありません。
重要なポイントを改めて整理いたします。
コンフリクト解決の基本原則
- まず現状を正確に把握する
- 一つずつ丁寧に解決する
- 解決後は必ず動作確認を行う
予防策の実践
- 定期的なマージ・リベースの習慣化
- 適切なブランチ戦略の採用
- チーム内でのコミュニケーション強化
トラブル時の対処
- 慌てずにエラーメッセージを確認
- バックアップの活用
- 復旧手順の把握
これらの知識を活用して、より快適で効率的な Git 運用を実現してください。コンフリクトをマスターすることで、チーム開発がさらにスムーズになることでしょう。
関連リンク
- review
今の自分に満足していますか?『持たざる者の逆襲 まだ何者でもない君へ』溝口勇児
- review
ついに語られた業界の裏側!『フジテレビの正体』堀江貴文が描くテレビ局の本当の姿
- review
愛する勇気を持てば人生が変わる!『幸せになる勇気』岸見一郎・古賀史健のアドラー実践編で真の幸福を手に入れる
- review
週末を変えれば年収も変わる!『世界の一流は「休日」に何をしているのか』越川慎司の一流週末メソッド
- review
新しい自分に会いに行こう!『自分の変え方』村岡大樹の認知科学コーチングで人生リセット
- review
科学革命から AI 時代へ!『サピエンス全史 下巻』ユヴァル・ノア・ハラリが予見する人類の未来