同じ衝突を四度目に解いている
目標
衝突マーカーで共通祖先を見て、一度解決した内容をgitに記憶させ、-X oursと-s oursが、結果のファイルをどう違う形にするかを、内容で確認します。
なぜ重要なのか
衝突の解決は、技術ではなく判断です。ところが、デフォルトの衝突マーカーは、判断に必要な情報を1つ欠いています。元が何だったかです。それを知らないと、「片側が削除した」場合と「双方が別々に直した」場合を区別できず、間違って選ぶと、他の人の修正が静かに消えます。zdiff3は、その欄を埋めてくれます。そして、同じ判断を繰り返す必要がある状況、つまり、長く生きるブランチをリベースし続けたり、リリースを定期的に戻してマージしたりする状況では、rerereが、その判断を1回だけで済むようにしてくれます。最後の2つのステップは、名前が似ていて取り違えやすい2つのスイッチを、切り分けます。-s oursを内容を取り込む意図で使うと、履歴にはマージされたと書かれ、コードには何も入ってこない状態になります。
ステップ
/root/gitx8/repoに、同じ行を別々に直したブランチを作ります。- デフォルトの衝突マーカーと
zdiff3のマーカーを、並べてnotes/zdiff3.txtに残します。 rerereをオンにして、衝突を一度解決し、マージを完了させます。- そのマージを取り消して再びマージし、gitが同じ解決を自分で適用するのを、
notes/replay.txtに残します。 sidefixを-X oursでマージして、結果をnotes/xours.txtに残します。legacyを-s oursでマージして、結果をnotes/sours.txtに残します。- 2つのスイッチの違いを、
notes/strategies.txtにまとめます。 notes/report.mdに、繰り返される衝突を減らす方法をまとめます。
参考
- リポジトリごとに、
git config user.emailとuser.nameを指定しないと、コミットできません。コミットの時刻は、GIT_AUTHOR_DATEとGIT_COMMITTER_DATEで固定してください。 - 衝突を見たあとは、
git merge --abortで、きれいに引き返せます。 - よくある間違い: ステップ4で、rerereがファイルを直してくれたのを見て、コミットまでされたと思うことです。rerereは適用するだけで、ステージングはしません。
- よくある間違い: ステップ6のあとで、
legacy.txtがないことを失敗とみなすことです。それが-s oursの行うことであり、このステップの要点です。
同じ行を別々に直したブランチ
/root/gitx8/repoに、同じ行を別々に直したブランチを作ります。
handler.txtをaからgまでの7行で作り、topic・sidefix・legacyの3つのブランチを、最初のコミットから作ります。topicとmainは2行目を別々に直し、sidefixは2行目と6行目を直します。6行目が離れているので、ステップ5で、衝突しない変更が別に見えます。
元が何だったかを示す欄
デフォルトの衝突マーカーとzdiff3のマーカーを、並べてnotes/zdiff3.txtに残します。
git merge topicで衝突を起こしたあと、handler.txtをそのまま書き写して、git merge --abortします。そのあと、git -c merge.conflictStyle=zdiff3 merge topicで同じ衝突をもう一度起こすと、欄が1つ増えます。その欄が何かを、1行付け足してください。
解決を一度記録させる
rerereをオンにして、衝突を一度解決し、マージを完了させます。
git config rerere.enabled trueでオンにしてから、git merge topicを行います。衝突したhandler.txtの2行目を、双方を生かしたb by main and topicに直し、git addしてからコミットしてください。コミットするとき、gitが何を記録したと言うのか、そして.git/rr-cacheに何ができたかを見てください。
2回目からは、gitが代わりに解決する
そのマージを取り消して再びマージし、gitが同じ解決を自分で適用するのを、notes/replay.txtに残します。
git reset --hard HEAD~1でマージコミットを消し、git merge topicをもう一度行います。出力に1行が増え、handler.txtはすでに解決された状態です。ところが、git statusを見ると、まだUUです。適用するだけで、ステージングはしないためです。git addしてコミットして、完了させてください。
衝突した箇所だけを自分たちのものに
sidefixを-X oursでマージして、結果をnotes/xours.txtに残します。
git merge -X ours -m '<메시지>' sidefixを実行します(プレースホルダーはメッセージです)。2行目は衝突したので自分たちのものが残り、6行目は衝突しなかったので、sidefixの変更がそのまま入ってきます。sidefix-only.txtも入ってきます。結果のファイルをそのまま書き写し、何が残り、何が入ってきたかを、1行付け足してください。
マージしたと書くだけで、何も取り込まない
legacyを-s oursでマージして、結果をnotes/sours.txtに残します。
git merge -s ours -m '<메시지>' legacyを実行します(プレースホルダーはメッセージです)。終わってからlegacy.txtを探してみると、ありません。git diff --name-only HEAD^1 HEADが何も出力しないことも見てください。結果のツリーが、マージ前と1文字も違わないという意味です。ところが、git merge-base --is-ancestor legacy mainは真です。
名前だけが似ている2つのスイッチ
2つのスイッチの違いを、notes/strategies.txtにまとめます。
表で書いてください。何か(オプション対戦略)、衝突しなかった相手の変更はどうなるか、相手が追加した新しいファイルはどうなるか、いつ使うか、の4行で構いません。ステップ5・6で自分で見たファイル名を、根拠に挙げる必要があります。
繰り返される衝突を減らす方法
notes/report.mdに、繰り返される衝突を減らす方法をまとめます。
設定2行(rerere.enabled、merge.conflictStyle)が、なぜデフォルトでオンにしておく価値があるのか、rerereが記憶するものと記憶しないもの、間違って解決したものを消す方法、そして、そもそも衝突を減らす習慣まで書いてください。