gitコンフリクト、両方残す積み上げマージ

一致行も含まれてる積み上げ

gitでコンフリクトしたときのマージ、時に両方の修正を "単に積み上げたい" ってこともあります。

別に簡単なマージじゃん?と思われるかもですが...
一致行が含まれてるケース だとちょいコツが必要です。

例えば...

↓元々これだけのテキスト

sea:{
}

↓Aさんがland:{}を追加、Bさんがpiari:{}を追加

   Aさん      Bさん
sea:{     |  sea:{
  land:{  |    piari:{  // 追加行1
  }       |    }        // 追加行2 (一致行)
}         |  }

↓これをこうしたい (積み上げ)

sea:{
  land:{
  }
  piari:{
  }
}

↓でもコンフリクトするとこうなる

sea:{
<<<<<<< HEAD
  land:{          // Aさんの追加行1
=======
  piari:{         // Bさんの追加行1
>>>>>>> refs/heads/...
  }               // 追加行2 (ひとまとめ一致行)
}

(追加行2は変更なし扱いとして "ひとまとめ" になる)

↓単純にコンフリクトマークを消すだけだと括弧閉じが足らない

sea:{
  land:{          // Aさんの追加行1
  piari:{         // Bさんの追加行1
  }               // 追加行2 (ひとまとめ一致行)
}

↓コンフリクト対象から外れた括弧閉じの追加行2を補完する必要がある

sea:{
  land:{          // Aさんの追加行1
  }               // Aさんの追加行2 (補完行)
  piari:{         // Bさんの追加行1
  }               // Bさんの追加行2 (ひとまとめそのまま行)
}

landが先なのか?piariが先なのか?どっちでもいいのか?
それはファイルの特性によるので人間が判断。

一致している端の行がひとまとめになる

というように、(たまたま)一致している端の行がひとまとめになるので、積み上げマージのときに片方の分の一致行を足して補完してあげる必要があります。

先の例では、最後の括弧閉じがひとまとめになりました。
ですが、先頭行が一致することもありますので、後ろだけでなく前も補完するケースもあります。

sea:{           | sea:{
  land:{        |   land:{        // 追加行1 (一致行)
    showbase:{  |      orleans:{  // 追加行2 (コンフリクト)
    }           |      }          // 追加行3 (一致行)
  }             |   }             // 追加行4 (一致行)
}               | }

↓コンフリクトするとこうなる (landはひとまとめになる)

sea:{
  land:{           // 追加行1 (ひとまとめ一致行)
<<<<<<< HEAD
    showbase:{     // 追加行2 (コンフリクト)
=======
    orleans:{      // 追加行2 (コンフリクト)
>>>>>>> refs/heads/...
    }              // 追加行3 (ひとまとめ一致行)
  }                // 追加行4 (ひとまとめ一致行)
}

↓補完してあげるとこんな感じ

sea:{
  land:{       // ひとまとめそのまま行
    showbase:{
    }          // 後ろの補完行
  }            // 後ろの補完行
  land:{       // 前の補完行
    orleans:{
    }          // ひとまとめそのまま行
  }            // ひとまとめそのまま行
}

↓まあ、landを2つ並べたいのか、1つのlandの中に集約させたいのかは状況に寄るかも。

sea:{
  land:{
    showbase:{
    }
    orleans:{
    }
  }
}

途中の一致行はコンフリクトに囲まれる

sea:{             | sea:{
  land:{          |   piari:{       // 追加行1 (コンフリクト)
    stage:{       |      stage:{    // 追加行2 (一致フリクト)
      showbase:{  |        plaza:{  // 追加行3 (コンフリクト)
      }           |        }        // 追加行4 (ひとまとめ)
    }             |      }          // 追加行5 (ひとまとめ)
  }               |   }             // 追加行6 (ひとまとめ)
}                 | }

↓コンフリクトするとこうなる (stageの行も囲まれる)

sea:{
<<<<<<< HEAD
  land:{
    stage:{        // 追加行2 (一致フリクト)
      showbase:{
=======
  piari:{
    stage:{        // 追加行2 (一致フリクト)
      plaza:{
>>>>>>> refs/heads/Btest
      }
    }
  }
}

なので積み上げマージのときは逆に気にする必要はないという感じですね。
両方の修正でそれぞれ必要なものなので、分身のままマージすればOKと。

sea:{
  land:{
    stage:{       // 一致フリクトそのまま行
      showbase:{
      }
    }
  }
  piari:{
    stage:{       // 一致フリクトそのまま行
      plaza:{
      }
    }
  }
}

同じ場所に追加コンフリクト

この場合、gitからすると...

o どちらか一方にしないといけないのか?
o 両方とも追加しないといけないのか?

の判断がつきません。
なのでコンフリクトさせて人間に任せるしかないのです。

要は "同じ場所に追加コンフリクト" ということで、内容がコンフリクトしてるというよりも、空間がコンフリクトしてるようなイメージでしょうか?

しかも、一致してる行に関しては、修正が入ってないと判断されてコンフリクトの囲いから外れるわけです。
パット見でそこはマージしなくていいのかな?と思うわけですが、積み上げマージの場合はその一致行が減ってるので複製してあげないといけないのです。

ERFluteでの積み上げマージ

ERFluteは "マージできるER図ツール" ということを特徴としていますが...
https://dbflute.seasar.org/ja/manual/function/helper/erflute/index.html

互いにテーブル追加した場合、この積み上げマージが必要になることがあります。

(ただし、必ずしもではないです。どちらかというと互いにテーブル追加してしても自動マージできることの方が多いです。アルファベット順的にたまたま追加する位置が一致してしまったときにコンフリクトするようですね)

</table>                         // 既存行
<table>                          // 追加行 (ひとまとめ)
<<<<<<< HEAD
    <physical_name>SEA</physical_name>
    ...(略)
    <compound_unique_key_list>
        <compound_unique_key>
        ...(略)
        </compound_unique_key>
=======
    <physical_name>SAR</physical_name>
    ...(略)
    <compound_unique_key_list>
>>>>>>> refs/heads/...
    </compound_unique_key_list>  // 追加行 (ひとまとめ)
    <table_properties>           // 追加行 (ひとまとめ)
    </table_properties>          // 追加行 (ひとまとめ)
</table>                         // 追加行 (ひとまとめ)
<table>                          // 既存行

これも同じ論理で積み上げマージできます。
というかGUI上でもっかい同じことやるのは辛いですから、頑張って積み上げマージしましょう。

</table>                         // 既存行
<table>                          // ひとまとめそのまま行
    <physical_name>SEA</physical_name>
    ...(略)
    <compound_unique_key_list>
        <compound_unique_key>
        ...(略)
        </compound_unique_key>
    </compound_unique_key_list>  // 後ろの補完行
    <table_properties>           // 後ろの補完行
    </table_properties>          // 後ろの補完行
</table>                         // 後ろの補完行
<table>                          // 前の補完行
    <physical_name>SAR</physical_name>
    ...(略)
    <compound_unique_key_list>
    </compound_unique_key_list>  // ひとまとめそのまま行
    <table_properties>           // ひとまとめそのまま行
    </table_properties>          // ひとまとめそのまま行
</table>                         // ひとまとめそのまま行
<table>                          // 既存行

ひとまとめ行はDB変更の修正内容によって変わると思うので、↑のexampleはあくまでマージセオリーの参考までにということで。

DBFluteのdiffmapの積み上げマージ

DBFluteのschemaディレクトリ配下のdiffmapファイルが同じような感じでコンフリクトすることがあります。
こちらも同様に積み上げマージをしましょう。

map:{
<<<<<<< HEAD
    ; 2024/07/25 17:58:06 = map:{
        ; diffDate = 2024/07/25 17:58:06
        ; diffAuthor = sea
        ; tableDiff = map:{
        ...(略)
=======
    ; 2024/05/22 23:24:21 = map:{
        ; diffDate = 2024/05/22 23:24:21
        ; tableCount = map:{
            ; next = 16
            ; previous = 13
        }
        ; diffAuthor = land
        ; tableDiff = map:{
        ...(略)
>>>>>>> refs/heads/...
                ; diffType = ADD  // 追加行 (ひとまとめ)
            }                     // 追加行 (ひとまとめ)
        }                         // 追加行 (ひとまとめ)
    }                             // 追加行 (ひとまとめ)
    ; 2024/05/09 18:38:31 = map:{
    ...(略)
}

↓積み上げマージすると...

map:{
    ; 2024/07/25 17:58:06 = map:{
        ; diffDate = 2024/07/25 17:58:06
        ; diffAuthor = sea
        ; tableDiff = map:{
        ...(略)
                ; diffType = ADD // 補完行
            }                    // 補完行
        }                        // 補完行
    }                            // 補完行
    ; 2024/05/22 23:24:21 = map:{
        ; diffDate = 2024/05/22 23:24:21
        ; tableCount = map:{
            ; next = 16
            ; previous = 13
        }
        ; diffAuthor = land
        ; tableDiff = map:{
        ...(略)
                ; diffType = ADD // ひとまとめそのまま行
            }                    // ひとまとめそのまま行
        }                        // ひとまとめそのまま行
    }                            // ひとまとめそのまま行
    ; 2024/05/09 18:38:31 = map:{
    ...(略)
}

こちらも同じく、ひとまとめ行はDB変更の修正内容によって変わると思うので、↑のexampleはあくまでマージセオリーの参考までにということで。

diffmapコンフリクトのちょい補足

日時が前後してる場合は入れ替えてあげてください。HistoryHTML上で順番が逆転してしまわないように。

一方で、シリアルにDB変更してたら発生することはないので、コンフリクトしたらそもそもDB変更運用を確認した方が良いです。そもそも手順が間違ってる可能性があります。とはいえコンフリクトしちゃったら先に進むしかないので、そのときの参考のために。

ちなみに、DBFlute-1.2.6 からはdiffmapがコンフリクトしないように工夫されています。
ぜひDBFluteをアップグレードしてみてください。

おまけ: 自動生成ファイルは自動生成し直し

そもそもコンフリクトファイルが自動生成ファイルの場合は、マージ自体をやる必要はありません。自動生成の元になっているファイルがマージされていれば、自動生成し直せば良いだけなので。
(そもそも自動生成ファイルはgitignoreされていることが多いですが、利便性のためにコミットしているものもあります)

DBFluteでは、以下のようなファイルたちです。

A. Javaの自動生成クラス (Exクラスは除く)
B. SchemaHTML (output/doc/schema-[schema].html)
C. HistoryHTML (output/doc/history-[schema].html)
D. SchemaXML (schema/project-schema-[schema].html)
E. tsv-data-result.dfmark (playsql/...)
F. などなど、こういった類のファイルたち

これらファイルがコンフリクトした場合は、以下のような手順で作り直しましょう。

1. 自動生成の元になっているファイルをマージ
2. 自動生成を実施 (JDBC, Doc, Generateなど)
3. 自動生成ファイルのマージ完了

"自動生成し直しマージ" って名付けていいかな?(^^

コンフリクトは優しさ

gitでコンフリクトが起きると "うげー" と思われる方も多いのではないでしょうか?
まあ me too なんですけど、コンフリクトの指摘ルールを把握すると、gitの優しさを感じられるようになるかもしれません。
(ぼくも頑張ります)