コンフリクトの解決
マージ、リベース、チェリーピック、リバートが止まったとき、バナーが 何が 止まった
のか、何と何の間で 止まったのかを伝えます。単なる「コンフリクト」ではなく、
「feature/x を main にマージ中」と表示します。

なぜここが衝突するのか
ヘッダーの なぜここが衝突するのか は、ブランチが分かれてからこのファイルに触れた
コミットを、側ごとに一覧します。中身は git log --merge — git がずっと前から積んで
いるのに、誰も見つけないコマンドです。

マーカーが言うのは 何が ぶつかっているかです。こちらが言うのは 誰が、なぜ 変えた のかで、たいてい解決を決めるのはそちらです。何も出てこなければ、どちらの側もこの パスそのものへの変更をコミットしていません。衝突は名前の変更か移動から来ています。
3 つのペイン
| ペイン | 中身 |
|---|---|
| 左 | こちら側(ours) — いたほうの側。そのコミットのラベル付き |
| 右 | あちら側(theirs) — 入ってくる側。そのコミットのラベル付き |
| 中央 | 出力 — 編集でき、行番号があり、実際にステージされるもの |
3 つのペインはいずれもサイズを変えられます。さらに出力ペインのヘッダーには、表示の トグルが 2 つあります。
| 切り替え | 何をするか |
|---|---|
| 折り返し | A と B のペイン内で長い行を折り返し、横スクロールをなくします。出力ペインは 1 行 1 段のまま — 側面のマーカーがそれを前提にしています — なので常にスクロールします |
| 連動 | A、B、出力を縦・横ともに一緒にスクロールします。行数が互いに違うため、縦位置は割合で合わせます |
折り返しは最初オフ、連動は最初オンで、どちらも状態を覚えます。
移動する
ファイルを開くと、ファイルの先頭ではなく最初のコンフリクトに着地します。 出力ヘッダーの ⌃ / ⌄ 矢印 — または Alt+↑ / Alt+↓ — で 残りを順に辿り、そのたびに 3 つのペインすべてがそこへスクロールします。
選ぶ
行 単位、チャンク 単位、あるいは 片側まるごと を一度に。答えが「両方 残す」のときは、チャンクの両側を採用することもできます。コンフリクトを 1 つずつ辿る ナビゲータが残りを案内するので、マーカーを置き去りにしてしまうことはありません。
AI の補助
AI を有効にしていると、AI で解決 が出力ペインにマージ案を提示します。自分から 何かを適用することは決してありません。読んで、直して、ステージするのはあなたです。 AI 機能 を参照してください。
Xcode プロジェクトファイル
project.pbxproj は iOS リポジトリのどのファイルよりも競合します。しかも、
誰かが意見を違えたからであることはほとんどありません。24 桁の 16 進 ID を
キーにした平坦なオブジェクト辞書ひとつなので、ファイルを 1 つ追加すると 4 つの
エントリが書かれます — PBXBuildFile、PBXFileReference、所属グループの
children の 1 行、ターゲットのビルドフェーズの 1 行。2 人がそれぞれ 1 つずつ
追加すれば、同じ数行に落ちる 8 つのエントリが書かれます。Git は衝突と見ますが、
解決すべきものは何もありません。
競合したファイルが project.pbxproj のとき、リゾルバは 3 つの版をテキストでは
なくプロジェクトとして読み、構造で統合することを提案します。ID でオブジェクト
を対応づけ、両側のすべての追加を取り込み、children と files の配列を統合し、
本当に食い違ったところで止まります。ペインの上の帯が、各側が何を追加したか、
そして — もしあれば — あなたに何が残っているかを伝えます。
AI の提案と同じく、出力ペインに入るだけでステージはしません。保存する前に 自分で読みます。

やらないこと
両者が動かした設定を推測することは決してありません。 あなたが
MARKETING_VERSION を 1.1 に、相手が 2.0 にしたなら、それは判断です。
背後で解決するのではなく、設定名・あなたの値・相手の値として帯に出ます。決着を
つけられなかったオブジェクトは あなたの 版をそのまま保ちます。中途半端に適用
された統合がディスクに届くことはありません。
3 つの版のいずれかが解析できなければ、ファイル全体を断ります。 Xcode が
開けない project.pbxproj は手作業の統合より高くつくので、確実に読めないものは
通常のテキスト競合のまま残り、そのことを明示します。
異なるオブジェクトに同じ ID が振られた場合は検出しません。 Xcode は ID を 無作為に選ぶため稀ですが、起きたときはどちらを取っても誰かのファイルを黙って 落とすことになるため、統合せずに報告します。
merge=union ではなく
これに対して出回っている対処は .gitattributes の
*.pbxproj merge=union です。やめましょう。union は変更が独立した追加だけの
うちは動きますが、2 人が同じビルド設定を編集した瞬間に両方の行を出力し、Xcode が
開けないファイルを作ります — しかも差分を丁寧に読む可能性が最も低いその瞬間に。
構造による統合は、その破綻なしに同じ手軽さを与えます。
ロックファイル
Podfile.lock、Package.resolved、yarn.lock とその仲間は、誰かのリゾルバが
すでに解いた依存グラフを記録したものです。片方の解の半分をもう片方の解の半分に
縫い合わせても、誰も解いていないグラフができるだけです。インストールできないかも
しれませんし、できたとしても、どちらのブランチもテストしていないものが入ります。
そこで競合したファイルがロックファイルのときは、帯がそれを持つツールの名前を出し、 こちらを採用 と 相手を採用 をその場で提供し、あとで再生成するコマンドを 示します。ここで片側を選ぶのは妥協ではありません — それが方法のすべてで、 正しさを与えるのは再生成のほうです。

3 つのペインはそのまま使えます。見覚えのあるチェックサム、予期していたバージョン など、何が変わったのか読みたいことはたまにあるからです。思いとどまってほしいのは 手で編集することのほうです。
そもそも起こさない
コンフリクトレーダー は、マージする前に、どのブランチが衝突する のかを教えてくれます。
git に覚えさせる(rerere)
長く生きているブランチをリベースすると、毎回同じコンフリクトに出会います。rerere
— reuse recorded resolution(記録した解決の再利用)— が git の答えです。コンフリクトを
どう決着させたかを覚えておき、まったく同じものが次に現れたときにその答えを再生します。
設定 → 一般 → コンフリクトの解決を記憶する。 グローバルの git 設定に
rerere.enabled を書き込むので、コマンドラインでも同じように振る舞います。
git があなたの代わりに答えたとき、リゾルバは「コンフリクトマーカーがありません」という 空の画面を見せる代わりにその旨を伝え、この解決を忘れる を提示します。これは記憶を 捨てると 同時に コンフリクトを呼び戻すので、違う決着をつけられます。
知っておくとよいことが 2 つあります。
再生された解決はステージされません — 再生された解決を自動でステージする を 有効にしない限りは。オフのままにしておいてください。ここで一度止まる意味は、 記憶された答えがこのマージでは間違っているかもしれない、という点にあります。見ずに ステージすることが、それをコミットまで運んでしまう経路です。
だから再生されたファイルは コンフリクトしたファイル に残り続けます。git は中身を 書いたものの、インデックスは未マージのまま抱えており、それを片付けるのはステージ だけだからです。動かすのは、リゾルバの このままステージ、または一覧の すべて解決済みにする です。
rerere はすべてのコンフリクトを理解するわけではありません。 追加/追加や 削除/変更のコンフリクトには前像が残らないので、毎回そのまま戻ってきます。設定に 出ている件数が実際に抱えている数で、すべて忘れる で空にできます。
関連項目: コンフリクトレーダー · マージとリベース