TT Lab
はじめる
学ぶ 学習パス コース

Git実戦

同じ衝突を四度目に解いている

TT Labで続きを見る

目標

衝突マーカーで共通祖先を見て、一度解決した内容をgitに記憶させ、-X oursと-s oursが、結果のファイルをどう違う形にするかを、内容で確認します。

なぜ重要なのか

衝突の解決は、技術ではなく判断です。ところが、デフォルトの衝突マーカーは、判断に必要な情報を1つ欠いています。元が何だったかです。それを知らないと、「片側が削除した」場合と「双方が別々に直した」場合を区別できず、間違って選ぶと、他の人の修正が静かに消えます。zdiff3は、その欄を埋めてくれます。そして、同じ判断を繰り返す必要がある状況、つまり、長く生きるブランチをリベースし続けたり、リリースを定期的に戻してマージしたりする状況では、rerereが、その判断を1回だけで済むようにしてくれます。最後の2つのステップは、名前が似ていて取り違えやすい2つのスイッチを、切り分けます。-s oursを内容を取り込む意図で使うと、履歴にはマージされたと書かれ、コードには何も入ってこない状態になります。

ステップ

  1. /root/gitx8/repoに、同じ行を別々に直したブランチを作ります。
  2. デフォルトの衝突マーカーとzdiff3のマーカーを、並べてnotes/zdiff3.txtに残します。
  3. rerereをオンにして、衝突を一度解決し、マージを完了させます。
  4. そのマージを取り消して再びマージし、gitが同じ解決を自分で適用するのを、notes/replay.txtに残します。
  5. sidefixを-X oursでマージして、結果をnotes/xours.txtに残します。
  6. legacyを-s oursでマージして、結果をnotes/sours.txtに残します。
  7. 2つのスイッチの違いを、notes/strategies.txtにまとめます。
  8. notes/report.mdに、繰り返される衝突を減らす方法をまとめます。

参考

同じ行を別々に直したブランチ

/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が記憶するものと記憶しないもの、間違って解決したものを消す方法、そして、そもそも衝突を減らす習慣まで書いてください。