変更を扱う

コンフリクトの解決

マージ、リベース、チェリーピック、リバートが止まったとき、バナーが 何が 止まった のか、何と何の間で 止まったのかを伝えます。単なる「コンフリクト」ではなく、 「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 の提案と同じく、出力ペインに入るだけでステージはしません。保存する前に 自分で読みます。

Xcode プロジェクトファイルで、競合ペインの上に出る構造統合の帯

やらないこと

両者が動かした設定を推測することは決してありません。 あなたが 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 はすべてのコンフリクトを理解するわけではありません。 追加/追加や 削除/変更のコンフリクトには前像が残らないので、毎回そのまま戻ってきます。設定に 出ている件数が実際に抱えている数で、すべて忘れる で空にできます。

関連項目: コンフリクトレーダー · マージとリベース

この翻訳を GitHub で改善