昨日のDay 12では、「Fast-Forward(早送り)」と「3-Way Merge(3方向マージ)」の仕組みを学び、Gitが共通の祖先(Base)を自動探索してスマートにコードを統合してくれる頼もしさを実感していただきました。
しかし、チーム開発やAIを活用した並行開発を進める中で、誰もが必ず直面する壁があります。それが「コンフリクト(競合・衝突)」です。
Subversion (SVN) や Visual SourceSafe (VSS) を長く使ってきた方にとって、競合は「誰かがファイルをロックし忘れて上書き事故を起こした!」「謎の .mine や .r123 ファイルが勝手に生成されてフォルダが散らかった!」という悪夢の記憶かもしれません。
しかし、断言します。Gitにおけるコンフリクトは、決して「失敗」でも「バグ」でもありません!
「同じファイルの同じ箇所を同時に変更したため、Gitが勝手にどちらかを捨てず、人間の知性に判断を委ねてくれた安全ストッパー」なのです。
本日は第13回として、コンフリクトマーカー(
<<<<<<<)の正確な読み方、Windows開発でお馴染みのTortoiseMergeによる3画面(3ペイン)GUI結合、そして現代最強の切り札であるAI(Claude Code / ChatGPT)を用いたスマートな競合解消テクニックを余すところなく伝授します!
① 導入・本日の達成目標
「マージしたら赤文字で CONFLICT と怒られた……どうしよう、コードが壊れた!?」
Gitを使い始めた初心者が最もパニックに陥りやすい瞬間が、このコンフリクト発生時です。
しかし、そのメカニズムと解決ツールを正しく知っていれば、コンフリクトはわずか数十秒〜数分で冷静に片付けられるルーティンワークに変わります。
本日の講義でマスターする達成目標は以下の3点です!
- コンフリクトが発生する原理とGitの安全思想: なぜ衝突が起きるのか、そしてGitがなぜ自動マージをストップさせるのかを理解する。
- コンフリクトマーカーの構造とTortoiseMergeの3画面操作:
<<<<<<< HEADの意味を正確に読み取り、3ペイン比較GUIで視覚的・確実に統合する。 - AIアシスタントを活用したC#コードのスマート競合解消: 複雑なビジネスロジックや
.csprojの衝突を、AIプロンプトで意図を崩さずに統合するプロの技を習得する。
② 【メカニズム】なぜコンフリクト(競合)が発生するのか?
Gitは非常に賢いバージョン管理システムです。同じファイルであっても、ブランチAが「ファイルの10行目」を変更し、ブランチBが「ファイルの50行目」を変更した場合、Gitは何も文句を言わずに両方の変更を自動で綺麗に合成(自動マージ)してくれます。
では、一体どんな時に「コンフリクト」が起きるのでしょうか?
答えはシンプルで、「同じファイルの同じ行(または極めて隣接する行)を、別々のブランチで同時に変更したとき」です。
例えば上記の図のように、共通の先祖(Base)にある CalculateTotal() メソッドに対して:
- Branch A(開発機能): 新税率(10%)の計算ロジックを追加した
- Branch B(緊急修正): 特別優待割引の計算ロジックを追加した
この2つのブランチをマージしようとしたとき、Gitは「どちらのコードを残すべきか?」「税率を先に掛けるのか、割引を先にするのか?」という人間のビジネス仕様上の意図までは判断できません。
もしGitが「適当にBranch Aを残してBranch Bを捨てておきました!」と勝手に判断したら、大事故(データ消失やバグ混入)になりますよね。
だからこそ、Gitは「ここはお互いに変更がぶつかっているので、人間が確認して正しい形に決めてください!」と安全に作業を一時停止してくれるのです。
③ 【ソースコードの読み方】コンフリクトマーカーの3分割構造
コンフリクトが発生すると、該当のファイル(例: OrderService.cs)を開いたときに、見慣れない記号が書き込まれています。これを「コンフリクトマーカー」と呼びます。
マーカーの構造は極めて論理的で、必ず以下の3つの区切り記号で構成されています:
decimal taxRate = 0.10m; // [現在チェックアウトしているブランチ(HEAD)の変更]
=======
decimal discount = 0.05m; // [取り込もうとしている相手ブランチの変更]
>>>>>>> feature-discount
<<<<<<< HEADから=======まで:
あなたが現在作業しているブランチ(通常はmainなど)の既存コードです。=======:
競合部分の真ん中を分ける区切り境界線です。=======から>>>>>>> feature-xxxまで:
マージによって取り込もうとしている相手ブランチのコードです。
SVNの場合、競合が起きると
OrderService.cs.mine、OrderService.cs.r102、OrderService.cs.r103 といったファイルが作業フォルダ内に散乱し、どれがどれだか分からなくなりがちでした。Gitは余計なゴミファイルを一切作らず、対象ファイルそのものの衝突箇所にだけマーカーを刻みます。記号を消して意図したコードに整えたら、
git add するだけで解消完了です!
④ 【GUIで直感解決】TortoiseMergeによる3ペイン(3画面)比較
テキストエディタで直接マーカーを手動削除しても良いですが、コード量が多い場合や複雑な差分がある場合は、TortoiseGit付属の強力なマージ専用ツール「TortoiseMerge」を使うのが圧倒的に安全です!
TortoiseMergeの画面は、見通しの良い「3ペイン構成」になっています:
- 左上ペイン(Theirs / 相手の変更): マージしようとしているブランチ側のコード
- 右上ペイン(Mine / 自分の変更): 現在のブランチ側のコード
- 下段ペイン(Merged / 最終解決結果): ここに最終的に保存される合成コード
競合している行を右クリックすると、以下の選択肢が表示されます:
- 「'Mine'のブロックを使う」: 自分の変更を優先して採用する
- 「'Theirs'のブロックを使う」: 相手の変更を優先して採用する
- 「両方のブロックを使う(Mineの後にTheirs / またはその逆)」: 2つの変更を並べて下段に出力し、下段で手動微調整する
すべての競合箇所を解消したら、上部ツールバーの「保存」アイコンをクリックして「解決済みとしてマーク(Mark as Resolved)」を押すだけで、TortoiseGitが自動でインデックスを更新してくれます。
⑤ 【AI時代の最新手法】Claude Code / ChatGPT による競合解消
ビジネスロジックが入り組んだC#クラスや、複雑なXML構造を持つ .csproj の競合では、人間が見比べても「両方の意図をどう組み合わせるのが正解か」迷うことがあります。
ここで絶大な威力を発揮するのが、生成AI(Claude Code や ChatGPT)のコード理解力です!
やり方は至極簡単です。コンフリクトマーカーが含まれたコードブロックをコピーし、AIに次のようにプロンプトを投げるだけです:
以下のC#コードでGitマージコンフリクトが発生しました。
HEAD側は「税率10%への改定対応」、相手ブランチ側は「優待割引ロジックの追加」です。
両方の変更意図を完全に活かし、文法エラーのないクリーンなC#コードとして1つに統合してください。
マージマーカー(<<<<<<< や ======= 等)はすべて取り除き、完成したコードのみを出力してください。
[ここにコンフリクトマーカー付きのコードを貼り付け]
AIはC#の構文木とビジネス意図を深く理解しているため、変数名の重複をリファクタリングし、両方の計算ロジックが正しく順序立てて実行される完璧なコードを瞬時に提案してくれます。
※ 注意: AIが統合したコードを貼り付けた後は、必ずVisual Studioで Ctrl + Shift + B(または dotnet build)を実行し、ビルドと単体テストが通ることを確認しましょう!
⑥ 【万が一の救命索】どうしても収拾がつかないときは?
「競合の箇所が多すぎて頭がパニックになった……」「間違えて重要なコードを消してしまったかもしれない……」
そんな時でも、絶対に焦る必要はありません! Gitにはマージ作業を瞬時に巻き戻す「緊急脱出コマンド」が標準装備されています。
git merge --abort
このコマンドを実行するだけで、マージ作業を開始する前の完全な状態に一瞬で巻き戻ります。
壊れたファイルや中途半端な変更もすべて綺麗に消去されるため、体制を立て直してもう一度最初から落ち着いてマージをやり直すことができます!
⑦ 【C#現場のリアル】.csproj と Program.cs の競合あるある
C#(.NET)の現場で最も頻出するコンフリクト箇所が、プロジェクト定義ファイル(.csproj)とスタートアップファイル(Program.cs)です。
💡 C#エンジニアのための実践的競合対策
- .csproj の PackageReference 競合: 2つのブランチで別々のNuGetパッケージを追加した際、XML行が衝突することがあります。この場合は単純に「両方の
<PackageReference>行をそのまま並べて残す」のが正解です。同じパッケージでバージョンが異なる場合は、チームと相談して新しいバージョン側に統一しましょう。 - Program.cs の DIコンテナ登録:
builder.Services.AddScoped<...>()が末尾に並ぶと競合しやすくなります。これも「両方の登録行を残す」のが基本です。大規模プロジェクトでは機能ごとにservices.AddOrderServices()などの拡張メソッドに分割しておくと衝突を根本から予防できます。
⑧ 📌 本日のまとめ・理解したい最重要ポイント
第13回の講義、お疲れ様でした! 本日の最重要ポイントを振り返りましょう。
- コンフリクトは「安全装置」: 同じ箇所の同時変更をGitが勝手に捨てず、人間の判断に委ねてくれた証拠。慌てる必要は一切なし!
- マーカーの構造を覚える:
<<<<<<< HEAD(自分の変更)〜=======(境界線)〜>>>>>>>(相手の変更)。 - TortoiseMergeの3画面で視覚的に解消: 左(相手)と右(自分)を見比べ、ワンクリックで採否を選んで下段に合成。
- 困ったらAIに相談&最後は
git merge --abort: 複雑なロジックはClaudeやChatGPTに統合を依頼。やり直したい時はいつでも巻き戻せる!
コメント
コメントを投稿