git merge時にconflictしたコミットを列挙したい

今年の振り返りを書いてて、そういえばWasmLinuxのrebaseやってなかったなと思い出した。
WasmLinuxの元になったLKLは今年6.xをマージしているので追従したい。

Conflict状態のindexを得る
これは git merge
してconflictした状態で git ls-files -u
すると列挙できる。
% git ls-files -u
100644 cda1f706eaeb1186972eab1ca88e5d8584fcd9e1 1 include/linux/instruction_pointer.h
100644 56bc64cf8d15e1b11cd061676b4dc37a32db2f3d 2 include/linux/instruction_pointer.h
100644 aa0b3ffea93536c7b423742f09efbaee50c7f56e 3 include/linux/instruction_pointer.h
100644 08605ce7379d7b0826cadaff8e6c520292a2fdfd 1 include/linux/rcupdate.h
100644 526d57b311700d4e6f8d29a51a53d41728721e36 2 include/linux/rcupdate.h
100644 5e5f920ade9094bf22222d471883591ebd39d948 3 include/linux/rcupdate.h
100644 da323209f5833183609f08a468b894fca7f6d099 1 kernel/panic.c
100644 8c778fbe35a4ec7234c2d57f1c45912fe96ef100 2 kernel/panic.c
100644 ffa037fa777d5f7f1cb8596893fe90c3fb0a2f46 3 kernel/panic.c
100644 88fa40571d0c773fea3794b65b2c7791a2873411 1 net/core/skbuff.c
100644 cccb62e158545e649d95f21641518a11ef7999e4 2 net/core/skbuff.c
100644 4eaf7ed0d1f44e8305109da2da2835013786a857 3 net/core/skbuff.c
このように1つのファイルに3つのステージがわりあてられている。これはGit(や、通常のRCS)は3 way mergeを行うためで、
-
1
はbase
、履歴上の共通祖先 (git merge-base
コマンド で得られるリビジョンでの内容) -
2
はours
、マージを実行したときのHEADの内容 (abortするとこちらの内容に戻る) -
3
はtheirs
、マージ対象のブランチの内容
をそれぞれ指す。これで挙がるファイルはconflictしている。

git blame -p
でblameを得る
手元で何も変更していない状態で 上記のステージ1のパス名でgit blame -p
すると、マーカーだけがannotateされない状態で git blame
できる。
git blame -p
の各行は3種類に分かれる。
- コミットハッシュのある行。これは更に "ヘッダ" と "継続行" の2つに分かれる。ヘッダには3つの数字
元コミットの行番号
元INDEXの行番号
継続行の数
が含まれる。継続行は2つの行番号だけ。 - コミット情報。ホワイトスペースで始まらない、かつ、40桁のSHA1で始まるわけでもない行はコミットの情報を示す。 ...別に要らないんだけど。。
- 元テキスト。元テキストはtabが前置されて出力される。
で、何も修正が無い状態であればコンフリクトのマーカーは必ず1行を占めるので、
0000000000000000000000000000000000000000 5 5 1
<<<<<<< HEAD
0000000000000000000000000000000000000000 16 16 1
=======
0000000000000000000000000000000000000000 23 23 1
>>>>>>> remotes/upstream/master
のように、 <<<<<<<
=======
>>>>>>>
で始まる各行を区切りとして処理できる。これに囲われたコミット群がconflictに "貢献" したコミットということになる。

スクリプト化する
とりあえずスクリプト化してみた:
-- include/linux/instruction_pointer.h:
-- A 0cb256b6923684cb5b94d8ce9f040c6baa95870f Yuki Okumura <mjt@cltn.org>
-- A e52340de11d8bca3ba35e5a72fea4f2a5b6abbbb Stephen Rothwell <sfr@canb.auug.org.au>
-- B 25e73b7e3f72a25aa30cbb2eecb49036e0acf066 Peter Zijlstra <peterz@infradead.org>
-- B e52340de11d8bca3ba35e5a72fea4f2a5b6abbbb Stephen Rothwell <sfr@canb.auug.org.au>
-- include/linux/rcupdate.h:
-- A 3520da9491b96bb54c73b79b0a95f575f149d565 okuoku <mjt@cltn.org>
-- A 5ea5d1ed572cb5ac173674fe770252253d2d9e27 Uladzislau Rezki (Sony) <urezki@gmail.com>
-- B 5ea5d1ed572cb5ac173674fe770252253d2d9e27 Uladzislau Rezki (Sony) <urezki@gmail.com>
-- B 04a522b7da3dbc083f8ae0aa1a6184b959a8f81c Uladzislau Rezki (Sony) <urezki@gmail.com>
-- kernel/panic.c:
-- A 0cb256b6923684cb5b94d8ce9f040c6baa95870f Yuki Okumura <mjt@cltn.org>
-- B cccd32816506cbac3a4c65d9dff51b3125ef1a03 Lukas Wunner <lukas@wunner.de>
-- net/core/skbuff.c:
-- A c205cc7534a97f2d6fbd2a23a94ed7c036c6e2aa Menglong Dong <imagedong@tencent.com>
-- A 231d06ae826664b83369166449144304859a62fa J?rn Engel <joern@wohnheim.fh-wedel.de>
-- A 3889a803e1da9bd7cd10d6504bf281ee7e55dfd6 Paolo Abeni <pabeni@redhat.com>
-- A 20bbcd0a94c6686c2692e6f7081163c233d7ce40 Menglong Dong <imagedong@tencent.com>
-- A 0cb256b6923684cb5b94d8ce9f040c6baa95870f Yuki Okumura <mjt@cltn.org>
-- A c504e5c2f9648a1e5c2be01e8c3f59d394192bd3 Menglong Dong <imagedong@tencent.com>
-- B a4650da2a2d6150a8ff1ea36fde9f6a26cf5fda3 Jesper Dangaard Brouer <brouer@redhat.com>
Conflictは複数行になるから、 A (ours) と B (theirs) 側で被るコミットが出てくるのは仕方ない。。AとBで同じコミットが出てきた場合は消し込んでしまって良いはず。
WasmLinuxの開発者は現在自分だけだから、 skbuff.c
みたいに、 A
側が自分のコミットだけにならないのは怪しいな。。何か間違っている。。?
0000000000000 (Not Committed Yet 2024-12-31 15:18:17 +0900 1102) <<<<<<< HEAD
c205cc7534a97 (Menglong Dong 2022-08-21 13:18:58 +0800 1103) if (unlikely(!skb_unref(skb)))
231d06ae82666 (J<F6>rn Engel 2006-03-20 21:28:35 -0800 1104) return;
3889a803e1da9 (Paolo Abeni 2017-06-12 11:23:41 +0200 1105)
20bbcd0a94c66 (Menglong Dong 2022-05-13 11:03:37 +0800 1106) DEBUG_NET_WARN_ON_ONCE(reason <= 0 || reason >= SKB_DROP_REASON_MAX);
20bbcd0a94c66 (Menglong Dong 2022-05-13 11:03:37 +0800 1107)
0cb256b692368 (Yuki Okumura 2023-10-11 01:05:17 +0900 1108) #ifndef __wasm__
c504e5c2f9648 (Menglong Dong 2022-01-09 14:36:26 +0800 1109) trace_kfree_skb(skb, __builtin_return_address(0), reason);
0cb256b692368 (Yuki Okumura 2023-10-11 01:05:17 +0900 1110) #else
0cb256b692368 (Yuki Okumura 2023-10-11 01:05:17 +0900 1111) trace_kfree_skb(skb, (void*)0xdeadcafe, reason);
0cb256b692368 (Yuki Okumura 2023-10-11 01:05:17 +0900 1112) #endif
231d06ae82666 (J<F6>rn Engel 2006-03-20 21:28:35 -0800 1113) __kfree_skb(skb);
0000000000000 (Not Committed Yet 2024-12-31 15:18:17 +0900 1114) =======
a4650da2a2d61 (Jesper Dangaard Brouer 2023-01-13 14:51:59 +0100 1115) if (__kfree_skb_reason(skb, reason))
a4650da2a2d61 (Jesper Dangaard Brouer 2023-01-13 14:51:59 +0100 1116) __kfree_skb(skb);
0000000000000 (Not Committed Yet 2024-12-31 15:18:17 +0900 1117) >>>>>>> remotes/upstream/master
正解は、これらの行は競合するコミットにより削除されている(移動している)ため。
merge-baseからマージ先HEADに無いコミットはfilter outしてしまっても良いのかも。