Open4

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

okuokuokuoku

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を行うためで、

  • 1base 、履歴上の共通祖先 (git merge-base コマンド で得られるリビジョンでの内容)
  • 2ours 、マージを実行したときのHEADの内容 (abortするとこちらの内容に戻る)
  • 3theirs 、マージ対象のブランチの内容

をそれぞれ指す。これで挙がるファイルはconflictしている。

okuokuokuoku

git blame -p でblameを得る

手元で何も変更していない状態で 上記のステージ1のパス名でgit blame -p すると、マーカーだけがannotateされない状態で git blame できる。

https://gist.github.com/okuoku/7429551a3609ff4def855e3408e5673d

git blame -p の各行は3種類に分かれる。

  1. コミットハッシュのある行。これは更に "ヘッダ" と "継続行" の2つに分かれる。ヘッダには3つの数字 元コミットの行番号 元INDEXの行番号 継続行の数 が含まれる。継続行は2つの行番号だけ。
  2. コミット情報。ホワイトスペースで始まらない、かつ、40桁のSHA1で始まるわけでもない行はコミットの情報を示す。 ...別に要らないんだけど。。
  3. 元テキスト。元テキストはtabが前置されて出力される。

で、何も修正が無い状態であればコンフリクトのマーカーは必ず1行を占めるので、

0000000000000000000000000000000000000000 5 5 1
	<<<<<<< HEAD
0000000000000000000000000000000000000000 16 16 1
	=======
0000000000000000000000000000000000000000 23 23 1
	>>>>>>> remotes/upstream/master

のように、 <<<<<<< ======= >>>>>>> で始まる各行を区切りとして処理できる。これに囲われたコミット群がconflictに "貢献" したコミットということになる。

okuokuokuoku

スクリプト化する

とりあえずスクリプト化してみた:

https://github.com/okuoku/mergecol-proto/commit/92102a2b38e6d6d5e3470d9c5e61b949eb6996c3

-- 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

正解は、これらの行は競合するコミットにより削除されている(移動している)ため。

https://github.com/lkl/linux/commit/a4650da2a2d61

merge-baseからマージ先HEADに無いコミットはfilter outしてしまっても良いのかも。