From a929e9cbd00e8d6e43ccc31bf4d406548bede431 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Tue, 7 Oct 2025 09:56:21 +0200 Subject: [PATCH] fix: assure that blob merges recognize conflicts in cojunction with empty bases. Previously, there were special cases that would cause it to auto-merge conflicts even though it shouldn't. This is now fixed just by removing the special case entirely, which seems unexpected in any of the test-suite. --- .../src/blob/builtin_driver/text/function.rs | 95 +++++++++--------- .../src/blob/builtin_driver/text/utils.rs | 9 +- .../generated-archives/text-baseline.tar | Bin 684032 -> 748544 bytes .../generated-archives/tree-baseline.tar | Bin 3385856 -> 3510272 bytes gix-merge/tests/fixtures/text-baseline.sh | 30 +++++- gix-merge/tests/fixtures/tree-baseline.sh | 35 ++++++- gix-merge/tests/merge/blob/builtin_driver.rs | 6 +- gix-merge/tests/merge/tree/baseline.rs | 2 +- gix-merge/tests/merge/tree/mod.rs | 8 +- 9 files changed, 117 insertions(+), 68 deletions(-) diff --git a/gix-merge/src/blob/builtin_driver/text/function.rs b/gix-merge/src/blob/builtin_driver/text/function.rs index d657e3797f5..f37431b4c3b 100644 --- a/gix-merge/src/blob/builtin_driver/text/function.rs +++ b/gix-merge/src/blob/builtin_driver/text/function.rs @@ -50,7 +50,7 @@ pub fn merge<'a>( input, CollectHunks { side: Side::Current, - hunks: Default::default(), + hunks: Vec::new(), }, ); @@ -115,65 +115,60 @@ pub fn merge<'a>( let last_hunk = last_hunk(front_hunks, our_hunks, their_hunks, back_hunks); write_ancestor(input, ancestor_integrated_until, first_hunk.before.start as usize, out); write_hunks(front_hunks, input, ¤t_tokens, out); - if their_hunks.is_empty() { - write_hunks(our_hunks, input, ¤t_tokens, out); - } else if our_hunks.is_empty() { - write_hunks(their_hunks, input, ¤t_tokens, out); - } else { - // DEVIATION: this makes tests (mostly) pass, but probably is very different from what Git does. - let hunk_storage; - let nl = detect_line_ending( - if front_hunks.is_empty() { - hunk_storage = Hunk { - before: ancestor_integrated_until..first_hunk.before.start, - after: Default::default(), - side: Side::Ancestor, - }; - std::slice::from_ref(&hunk_storage) - } else { - front_hunks - }, - input, - ¤t_tokens, - ) - .or_else(|| detect_line_ending(our_hunks, input, ¤t_tokens)) - .unwrap_or(b"\n".into()); - match style { - ConflictStyle::Merge => { - if contains_lines(our_hunks) || contains_lines(their_hunks) { + // DEVIATION: this makes tests (mostly) pass, but probably is very different from what Git does. + let hunk_storage; + let nl = detect_line_ending( + if front_hunks.is_empty() { + hunk_storage = Hunk { + before: ancestor_integrated_until..first_hunk.before.start, + after: Default::default(), + side: Side::Ancestor, + }; + std::slice::from_ref(&hunk_storage) + } else { + front_hunks + }, + input, + ¤t_tokens, + ) + .or_else(|| detect_line_ending(our_hunks, input, ¤t_tokens)) + .unwrap_or(b"\n".into()); + match style { + ConflictStyle::Merge => { + if contains_lines(our_hunks) || contains_lines(their_hunks) { + resolution = Resolution::Conflict; + write_conflict_marker(out, b'<', current_label, marker_size, nl); + write_hunks(our_hunks, input, ¤t_tokens, out); + write_conflict_marker(out, b'=', None, marker_size, nl); + write_hunks(their_hunks, input, ¤t_tokens, out); + write_conflict_marker(out, b'>', other_label, marker_size, nl); + } + } + ConflictStyle::Diff3 | ConflictStyle::ZealousDiff3 => { + if contains_lines(our_hunks) || contains_lines(their_hunks) { + if hunks_differ_in_diff3(style, our_hunks, their_hunks, input, ¤t_tokens) { resolution = Resolution::Conflict; write_conflict_marker(out, b'<', current_label, marker_size, nl); write_hunks(our_hunks, input, ¤t_tokens, out); + let ancestor_hunk = Hunk { + before: first_hunk.before.start..last_hunk.before.end, + after: Default::default(), + side: Side::Ancestor, + }; + let ancestor_hunk = std::slice::from_ref(&ancestor_hunk); + let ancestor_nl = detect_line_ending_or_nl(ancestor_hunk, input, ¤t_tokens); + write_conflict_marker(out, b'|', ancestor_label, marker_size, ancestor_nl); + write_hunks(ancestor_hunk, input, ¤t_tokens, out); write_conflict_marker(out, b'=', None, marker_size, nl); write_hunks(their_hunks, input, ¤t_tokens, out); write_conflict_marker(out, b'>', other_label, marker_size, nl); - } - } - ConflictStyle::Diff3 | ConflictStyle::ZealousDiff3 => { - if contains_lines(our_hunks) || contains_lines(their_hunks) { - if hunks_differ_in_diff3(style, our_hunks, their_hunks, input, ¤t_tokens) { - resolution = Resolution::Conflict; - write_conflict_marker(out, b'<', current_label, marker_size, nl); - write_hunks(our_hunks, input, ¤t_tokens, out); - let ancestor_hunk = Hunk { - before: first_hunk.before.start..last_hunk.before.end, - after: Default::default(), - side: Side::Ancestor, - }; - let ancestor_hunk = std::slice::from_ref(&ancestor_hunk); - let ancestor_nl = detect_line_ending_or_nl(ancestor_hunk, input, ¤t_tokens); - write_conflict_marker(out, b'|', ancestor_label, marker_size, ancestor_nl); - write_hunks(ancestor_hunk, input, ¤t_tokens, out); - write_conflict_marker(out, b'=', None, marker_size, nl); - write_hunks(their_hunks, input, ¤t_tokens, out); - write_conflict_marker(out, b'>', other_label, marker_size, nl); - } else { - write_hunks(our_hunks, input, ¤t_tokens, out); - } + } else { + write_hunks(our_hunks, input, ¤t_tokens, out); } } } } + write_hunks(back_hunks, input, ¤t_tokens, out); ancestor_integrated_until = last_hunk.before.end; } diff --git a/gix-merge/src/blob/builtin_driver/text/utils.rs b/gix-merge/src/blob/builtin_driver/text/utils.rs index 54a6f23a35f..0b1aa7e1dfa 100644 --- a/gix-merge/src/blob/builtin_driver/text/utils.rs +++ b/gix-merge/src/blob/builtin_driver/text/utils.rs @@ -163,10 +163,9 @@ fn ancestor_hunk(start: u32, num_lines: u32) -> Hunk { /// actually different remain. Note that we have to compare the resolved values, not only the tokens, /// so `current_tokens` is expected to be known to the `input` (and its `interner`). /// Hunks from all input arrays maybe removed in the process from the front and back, in case they -/// are entirely equal to what's in `hunk`. Note also that `a_hunks` and `b_hunks` are treated to be consecutive, -/// so [`fill_ancestor()`] must have been called beforehand, and are assumed to covert the same space in the -/// ancestor buffer. -/// Use `mode` to determine how hunks may be handled. +/// are entirely equal to each other. +/// Note also that `a_hunks` and `b_hunks` are treated to be consecutive, so [`fill_ancestor()`] must +/// have been called beforehand, and are assumed to cover the same space in the ancestor buffer. /// /// Return a new vector of all the hunks that were removed from front and back, with partial hunks inserted, /// along with the amount of hunks that go front, with the remaining going towards the back. @@ -418,7 +417,7 @@ fn write_tokens( /// Find all hunks in `iter` which aren't from the same side as `hunk` and intersect with it. /// Also put `hunk` into `input` so it's the first item, and possibly put more hunks of the side of `hunk` so /// `iter` doesn't have any overlapping hunks left. -/// Return `true` if `intersecting` is non-empty after the operation, indicating overlapping hunks were found. +/// Return `Some` if `intersecting` is non-empty after the operation, indicating overlapping hunks were found. pub fn take_intersecting( iter: &mut Peekable>, input: &mut Vec, diff --git a/gix-merge/tests/fixtures/generated-archives/text-baseline.tar b/gix-merge/tests/fixtures/generated-archives/text-baseline.tar index c0601e39edef7336fece921dbe2d5b7d51cafdf1..f018b81162fd63a4056c681768091b9803fad251 100644 GIT binary patch delta 9910 zcmd5?Uu+ab81LM5?{3#hd%aSM6uMVMCD?X%Z+A}{Aygm=K@o#I)P~UZ3KZG`ZHa<} zHpE_yM9g^&Lwq1e42eVVa(ZIe9 z+^U_$=9~(}LaHFChH|AXI)}GiXX_YAmh@fNTNW=Ya36IZd*ON{#>j$VoP^yl=r(V3 zcVEBZ2;H!`xB|Pbup@3w1$dDCta-0n!)az@v6z%AL_tCr4}XkrQ2p+)9$~)16?52V-+;mH@vH_mHeCH ziX@#|R>^a$T@L(s7}Relqx|?0&`?hKHG9EB<&=NB7c48M{ODOG2Cp6hY&j3Pm;!t` z^ ze&LMp@epc8_$c4Zh>%`e;pZQLI^*}Bmf!IUpdKDYv*%K?`qPkJdOwVw2TjHaB~OD# zA&7fl%^zo%z+10@i1#fgPJzd~uO5GuZ6syT`=eD ztCJbN*)jG>#?&tqocblp)SsJnOHTc051RV36Ys_tC24C%hn!ch1%tUMK2>n?w-ivb zZ7n|eYX>fe-g5TaNINq4Plam3Xj+uasRmE}44$7;xz?3)x(i0h>`!*eQoU&nU2bD_N}ma@+!YWfa1cPDTI0|Yic8h$Rr*TYm}P^lCgFAoqk zVUIxm-$*-59gaw0F`1By>VrGNddUCAtjq9~Fm)x^oJ|YSkSxn)KI4*ZX`f;-)N?pr z8X#&{V@r!R7!Y-_3$^DP*h$h`K-|q``Oar@ilPc)ULrKd;pR35+vKo9jQ@d6giupp zyHbJiphBJ!a+dw=uuPQ{)6rJTuyzY`-IV>pCAKZG*yemIAgXDP2{prX zgsElB9?jtNA!>$o`)ig&e zHXYI9XwQiIY_4eBWV5g)j9z3EDv*5+H=cd6O^4UQ6b7cENH#Xn0nEm&M9XaIO1uFr zbh7CRdQP-ZkqhkiB8k5zpIp%4fo80wkY5ctblsAwoa^cIiNvW}m#G_&v~(5sI;f_h zb}>S&ak&))Xifv{YuLnx){_!L%91SGyJ6>j26fyVHgO|qCmTY0%9TQTw%#-xtYVwWDL>xM#>*-H z_Ez@ca>{={$+nayvfsSSE-$C%@wM!Va>~EGiCyWEuT#nvmu&BN-QVy#ES>nTJI;2+qbQqjd|aK`mHCtuQpmB77E*0&ig)e^|pFnjhfFDaO?%v z*{yTOj6Pk3qFrq|M~Ge zdYYWcIcLtCnY?*_@9#Y~96ik3@QYWN->u)2W8q>r)@0#W18Xp{F)VA~EUYO8t6MeG zwjxcCY0SEg*|+HCb8(||PPbiHvG3|FyW+OZD_8mi^0JF}x^&BUmlJcx6WDG0c&}IVcts~^Oi{Z1>DJsbuP7`N+lzaWzK=){t-I!PZ|U$@OB_OyVy(utMMjf~tbxCFmom(Rb24B6pvxjf71e9^Hv+eyD< zlG|K@FMP5-fy{!3%n9#q7Y3cCD0F$~wRPJ`51|*LKnF%9ju!>g?IV-aOFm2|tVjB+ zG?%1tzMXfvef~P>LLlkFOIN!(blyU<+_*M0r*-7ao$XF{CF$zh4{s*DpZ~;+#wiVz zie7rDn+30nx5>kAncXlONiRD*F1sN5q{|bz!sG=&25GZ-L}@{ibLjDO)9d0E1iPNRDYMv+{KdTD=k10$eIAzEKrp}M^q`Md&I zgwpyW*QByqk?a?{1;zCd{e^C%3p;9b`?|@b4_>y2@0Av-e3FCKAZfgvEPJv{bw;Eq zql4C<)vL*s^mmJHI`m5$Ik|U9r%s=|4(aFQbCJ%%%lDG|zS`@GdM@&E4b9qj>rX(- z$>kG5EB<`1OS_%q-fio4y6jbCP1~Kl^jYBVA%{u>A~%QNw^c_DrxcM#nH%Z8uYU@K zliUOXxid9o)0=u*7R_y()zJ9kDNQpMUDh68Xdi74O5I-cAn7tX}|&|@^R{a%IO@(GIMaEC*t=Ue79+)|~& z;iNYOxvK?RhX-i}IvqImIwXjqN325*@`Xqqc;s0@zMH)C0Z9AorO$N@npAEeO{^dj z!_nKh+fARV)0}9ln3$cAO}il=$lWI=L(kFflQiiF?<6fKQ1`c|IQ_zyC$D zTs}21RDVjYZ->gsdBU8+%xx-Kh%V|O)0^^8m2f{>}T4&Q2-fhwz3xkWPukLG!&7kXJlCh zA+lqMfXD!m0U`rL28aw086Yx1WWxjK?;Vj*l+uUJFhO)CR>+S6bvP^Gqb|Zd1s;gi z4uv{|e5#hAP97znhEiHalhG2;C_Jn0qzI5 zAK-p~`vLA}q`9Ak@s${DPK>}ko8yB!Hz)WjXw+7DURDZJ9^`rdY-5);l-hv}pB|#EX%VPR}pRnE^ z1ydQhL2OV|fm7gkf<6k#R51+6L_}0ZAeo4e3ZzdUeFEu|tTY0!1z-!n7J#jh23x74 z%owf7(uYVUeIK@r3Svv*nIB&Wh)B@Qa#6*1U}0j>P(VZkMzzBLBF<=v4CX=94Yc5( z1qUrSXu&}X4q9-~f)6L%|NfOn3lmY0uurGCwb7N9IZ zS%9(tWdX_plm#ejWKmXYQYkJuqMj0_or3tuq;5`Q?fRMfOK$EL-00y)@3_fH<9lgLpe>mos@7y7YSNt5 z94YyZ9^|_@`MaYnBLw-VTLJ|6Ajk(nJ_z!GzX46{2Y&j!xG?;);0zwH4Gr=%ZGXDf{1>gz@B{0nVAq+DE z2U5pVLWzpHgTyhjXf0mfphyn}QY)4)CDino`kHp|k}o##`t5cT+h2dTn^?cPdV=ZI zv2QVfEop_guqCY{*KA>j*f4QsgE5jw%1KXQf~mVTS28Nm?>&8D@!5si7xRzrd*}n} z7X5#|#C&@E7TR90ur)13flD&7l5kx^+_1YzLb<-3VZPCFX0uRhVGUN6 z3|2d5w{yhdyv=5E2?E1(@tL-1v|19 z&Je+%MGr=9NgE~@8S+%G(}0TuxHy1|1GqRqVj(0JLSi8#7LIgc;g+<)F~=Z&CGBUG zm^~0524|xrIP{6C7;m^kH6|E%j$Oh8vSY_#W^q~t{_yKGBfjynxSYV^G_p~~rNTA* z*1^OH7lWYNr_6lpVde7*?ImJ4htz6tT#E8)Cf>-g=3w7UHHn$tmg{1IuF=X@W4~KV zn6y+|d8wTvIs3L%PJ67cm*5TmxW^N@ZUPRzm4d_OVgEAmkez$X|DE7nfZ7K zt*eyL(7M5Fw3s76>!qXhT%~gqUe7AhM{y?0pyY%;b~J;>9E(p%4xN}_r#So#CQzd+ z#l=SxW#M3I9v8e-qx_#ttUXgT5ij0RSc=zuk(M3DQmS-Rp1d*9ztajBTv(<|A`_#( zB+!r5hjVkK)tSnW)v3{B$}NVsI=WRK$MinE5Sm-E@i@Uq~>ch%C?~V32_U}>6o72B%MxPUD?2Z4s$9}r`T)9z| zHs9G_w`KDaX9DLoHmLr(*SYJuwgZRDjjt5mcVLR;=bh`O7k=}p{i(eR^~+v*Z1vMM z?;eUPeWs%7jAGTJy}r(me)&%38~qK}u694WWZwSqhkp5^H$VMy)Zgp=dh+k%+<4U& zad*z1^$O*%=g`S3$3-8d%Q^sw!?q}Ynt-zx=SR@mw~g+gN0yN#t-rKiI_hzG;n^z6 zv08`xnll(phRF31n!I@Rm*-fgsvuTnpCl=Rd>(*YD@g<&n29^>Nn!5QFWW2I28D-Xvr00@r=74{vhoO zV(#~hJaKF3GMibHojds>(W;vl&Aiqy-Y>kEdGPc*SH8!h-ylWPW z4u{2v)#V!bWNwNr&Nd8P1O}`1eE4D?^Jx-;li!R_HP!f6|8?HW=Go6wzq`Bm$(3L2 ze*Wo&>Pfr(S9pA^*H*;Fto^tMr#~}xEb}Uvto{0;NR}7q0aObbc1*x!-``%LHu(CC5t(mW_wQz0bwl?C!Q^WFRtF5M{ z#>Si4Sd0$l=1CKGQ*04lU!}~9C;ULfqph+mk-FY84-i>D{r+Q)DkW=?Vs?#B-I{v?d{o$-{5{MVW`$=adE5{aReR8^2ib9u9S7NQBbyyZ;0rFE!OHQX!>Rfp_K)t~DCNIpZ}{~J zn*T;iS;VyF23+>_=sBvQv#%e;6EEM;xUK8?vG1&L$K<~)<-aXw0;yw49w`~)=!gQR zIFsq(YNgaMV~4^iqE_N$I2DRG#V13=2_jAqae{~wM4TYv1Q92QIE{3~DRoRaF5a9N z&T=Cv8*z;PUW%IP^mEk5!ljIw#9!jF+;qt^yiVV|!7tU4GE{pvb`8`z|8s*>OUh8~ zFYv>;>5?BooxY_&CDoELRC^DO&rA1aR^9#1gR6DPUmu?m`{062pWWQGWH(OV%l7o= zr?2-imf(h!qgF;qyW5nWHxz3T>Z*Pi>WZ}(&Qq&F2B<5bu7J7%>I$eUpss+r0_tjH zsjE%tMVKhV8@B)FsYFbq;njVC9Yh$uBjdwGlGniGs9pm*GluaR2oX#-qIC>b{Dvup z9eH6#Uf7WrcI1T}d0|Ih0&sLvCZwmE8cZ$sh@I4CNrN<^;PB~1q08eJeEKr^4_Z1j z(pIhQPCv4F+zw|uX+adcE~3=BSdw}~`@IUmo@}%mJAc`Kb4mo(IOV)aN$uN0QK(xG? zk=v2?Xa>B6lwq34Rw)*QOPL2+9gE98$!t<>xUb^gFRxi%a^$Wz zW>|aY{9OHB(bXYO0h5a4tewqt%1+-q)PH1xGfy&UgMFAxs$tT-qFMzQOlHtBfmHxl z1%OomSOp;R{SV%g;5`YLsq+W13NWdL=7G6Y-M3K8RIfX+i*Al0l`>|!;WMITQgy1f z`G0foKew-Q+_~TVFV$tuE&JDo+AhZUdH3+RiXbnPZ5Lz3C;*&^5zsP%01tkWHnm8f z2@`;ee1hl{e2VQaJM}c%^rb*c7ICy(lNCg3vV}>}q}*0Zojdw;|M^=EK8jL2D zQYr^`D5}ZZuV;U7dmeF99O_0wY&Y2R+u%|s$iSrRF~Kn_bADQh_uifJ5&fSA|M}jW zWc>C$IqqHye@XTWX4KLlAWE~MF5XW<(!MIxMq<)_@~)EvcUPe00`iI;d$*0e;X`(( z!y$-*+h2{Q_z+KUp_}iLYMkz69@jF#UWJ_g3g0!zM^L1@T|m6MXF1Ar3lPS%f>I?o3p$3 zzcTVmAGrQIjvO!Dos$_n^s5|G33UOP#RN{pWnk52&6V4~ergo%c(a@=fah{fZM@i-2+fyu+<6Kw$V5jy+X_mDq8<|HZ2lI(yfrd% z3L#DiaYBd_LYxrdgb*i$I3dJ2vLViG1r^jmfjU0X5GP48JXFxVM-t3y=dVbnf_Yko z1oI*;TT|GnDqpwi_WsNEpFFYSuIu{_{cPQ?Pt*^~g89IWg=DY3)?~U@AMVI6G6ZkC zq3{Eb6dH4pUE=!Ll&c3P9PTL8ML|xLa%e0nelnQ`d9nk30{jH{3Gfr(C%{jDp8!9N zD1O>-u5iP-q7CPY`_Gl6&Y-YqLfQk{o{-CqsWNQx--umP(OBFx6{X0B9A1_$9dda3 zn9w1c=pm9p=!E2hiauJK{QS}DL$z1nQzA1Kzd99Fsb2o-w}SZi_`iA|I>476Df!vu zUu~R)Nw(s9ffcAISk!1NYbQWs`ippYzVt2(v+7vje(1IftIAVb#7C2&o zBNjMf5x@nGSSyGWnp0e_P;|SA=>eMF)Uv3dsaY!L&u+eAQA=}Uqrzs7Ou?hccM7Ki zwG%xx(k(@r>!?JVOeRg=BYt9KoJK*!+-^nkK!%=|3cckT!PQIczy_*pB*{-)X8-C+e9L{_nVJD)_s zolmMyCr3d%ED_&F?4}qU@!X*vMwlIyeIhET$Q=QJeFF9g*e770fPEVJ^)YSsRkKDDOd^yVBWwI~kQ1?o^;E?R|JfgGR>89U-QSB_7C3nqADQ%q?YYu6pR~K$(M&p!* zi!{$uMZSx5{w}ZGDI%TyJdrTqlUdy=|C2u*nQGsQK+6PLrjgb% z-JvFm?IY@7$q|iXW;6_BGGCnai7y?-4m~OcGH}L7XayJuFc4rMz(9b300Th;4kB>p zfpc}3qM9GF%9qgl_iI!|;7cFUmIjfwOn5vB1~M4gNJpvYJkaB1l~Ghkx?xmEMyn+f zS^)+E3eS&I@CZ+znfR)N~?>#N6 ziPp7KHBos!A0yZAnkAQ>Ux(^DIa;62Zv9QD{#W>(sY!EGk7c&Zs@V9> z2lpMzxc7Sd6FI#PP5zV$kv(H-@qn^Bf&ut_=3?H zrhx(;O>pg%<(sgSLdPjmA0^B%+UB)~7H&c1T&vDObirxNl1 z1W*+UqU@?u%zwY5GK|h4W}183%}7xjyBRFw}Uz(|0Rl?kwr zPv(I_*y9cYi2?)?A&>}xLTbz3p>+A# zBZN7sr=`ps^?riO4(U5HPW=6huy4W(|2Wh%dF$qTN*H|)R_7;93r^mYQpf~e(ia8` z59^&?>NE9BQ8b?^B|FT1FQNF|&Wv9$4PoO_#LK7xX?eF@?-4_Ds>ZrpZc*K}3&Nbn z9wLJhXd9$0|MudIEZOA8ZI=!h*nmJ&ttchEP3=J;2vbFST<#=S&Et0UAX|rE>m(2d z01-idbTcNuhs+7@ZWjjOK6xVwL@p_r%+L$6r-VFl0Ep=BQ^_F60znoCvOtgpf~=7h zWGPt<6{$}R#qRWdcm^B9Gq^MLu@)}acQWrt!YG}S0D~3#uHLdME^r-}IaHEFtTWr9 znLTk<6J+Co+W`O_1!UtvHXdZ-K{g&_AhIn%$wpp4Blv(+5ZP9d=!0Zn)Mb)4B8RIfDHH$@AfS*#bg zH8^T0iUOf(WmR`HAVoI7GlQ)G*ct$11V9Ra6aXmzQUIiWI3RV@pr(w`wDTxRXiqE>XSnI1Rf(8gefnw0EMZ{XnsiKI$ z-1yj2YtdS?Rz(@qQ`T2ot(IC>sYTST-MVUlTHmc`_uiRAQPQ+~_VkZ(PR@^$@8;g` z`+d)QD>k-c6=&M8ot4W&bQ%|m)aofxO{z7d3rVUeJ*jg6vQ6@lHAEDHeW=aAnr2n~ z?AA1E^*e4g1b@vGB_LX(lh>T&0aK%jY_e`NUl8ZMbj6B$4j0-Hb8!)1*swg%RgMh| z%W~nqKfb|^Hu+pmKAV+?Zn||1$r0M*n?OT3MsmA*O>JU-4bdjeN53A27vFhq^h@W* zneQ%AN8H>Pw7_rf)fM2?ULS^DjzusL6-bs^@jT>fl65uXBE41uqs5?H+@l?AFR-tgY_m zXv6@mJp7OKcH|gH_5+G5?kqpFW@*{SNUF(PKEWn^85CS`&&S`nGU=mft;XZT(s|_@ zwXNoI@yU7y@)L|TUv;lEJJ3`^X$;P|SysYtF7c?G*#D=LL9cfjKD5(rrrV@95FF_x2GjJ3&!^^F;2oa}idM`)9mf`;Xu>9|hx_LQ!XwNa<7 z+x86a?2J8cv?@?k+@n`}LJg`svmHi-K}R~XobL4;h&(>j<|7pY0=-HBvIT_XOrK>( zPM0BSplI|;WTleDI~F1-LYuSzm>a$F@sF=5n|4hae&dI0TW4Kxo$*22=@)k*{|GR; z*$b+=-Rsv>b?PMgs@K!1T7cS{0cxL~m)U4Oqxu`xoWY;|B$`uwV8l20FhqlISEvLk zTdO_OIUHd^Lgp$?Y3dUfmnGfcMmZJ*#<%*)@xgi9lT&|LvHoDt~f5BrI%hhcm#jHK(+f~#*Xw;NW@6?p$(Spi1K;<;A0ESlb=3+nzgzZd4 zua6R=aiIiAP6|xm%^Px@r`BrwxUbsR8w=$@1M213$0ys7Gelnwb0>HJ7}q8UM-Hl& zKX1hCXj`%vh1vk{Q>GucMCR@CIa-3ywj|TGq)iZn7^{~Xzu*pnXj@VN8EXKq?M@gB z=G>BJ;=9g$6%wgBdwl8-4|4^hE-$!LvZD- zXyfdpEYhe>(r9yZx-4Dd97>mDP;2JsjkEQM8cLg(m6)y0)-wV7eZNMe$Ur3NGm_n; z%oT2OUC=#~b^FlxXWfnqpNz!Vd={wG)RiO|OESI)TYPBl5z;^H_4cVtZm%bHG|e}5NM)06!fMq(*NpN)kq(e zoVnd0OB%mwq&e>As+QF|hR6jTYcKCnM_)NM`)bpyxl1o4{_^kj<>^WD7uC$j$XYvR z!o1z0l!^0?Rh5)|-|WTN!7dl?U-}l?#{$~AlVG6(2)P0`xE?5){nPOg?;mJQ4Guju zW#=W|>BQi`3yW9JL~dAs(TDvbnaCFZ9FwEsuhlv&{hE*redWK zZBhx~T?i<~*KJ$oGH7ys_t-s4PE!%4ee?J6@!G5huhj(x0r7S5gc`z~4VcDoRJ#MZ z9W*@3KsmR$E-(~C6o-zs?^PnAENhK{%vxhm*DMDd!VzB6ryTYh+M7Fqd~YMhXyUNP zDm!v^C1nCd8$J%-d+Wr#pruF7rv3fg;X^Oiy_*`vJ8Ns??Km7mPT{{Yh8XnDj;0HO zqkyV}4--ki@q)r>N^og48&1@EQ3ccoAmsE(X+@1WA5U54qJ5WcX_?sRosfCLdtl0k z8LN;!GvU}8>}YJI3~I_L3p62$K*L*F?V}TXi>XA!m`XyVLw{wqFAI5kwufQoW(scZ z3|YD%RHxVKp1C+tYLeFq~7huD?;vXGL zsv5-WKa`=d}WMWcYuE`uv%qw~&+ic1!%q8;X5yrfnoa~b9LNnZRPU$P~pDc4; zQ6aG)YaWqPRFYd{HfI+S$trL9s6YgQw}`i<{=Zu>`R|FxMAH7eK!Bf6r8CaSp;4cL{YGzQ>K7Uz=s|_=lqpnS6cRX5!fBw>~M9-{S3^I1#`^`43$tlL2_s z;mUZ6A8c#$W#1QZ^u|3i1}^KBHTOnUdONBvl-jxNTce~FsYK35B?{97cKe>lXSS!c z3I%c=*L0!tETCM$fbs!72OOLT-y)p%1pdGTw432cMR{Rh>34`rYq*)Iv?IsXuy(|j z{TU*>H1RqshiH?3#mTv62$}bRHY7*L$y2?!E9JKl-3#A5v^00~Pt|G9`{&S?hc zqVLf&qD3IA0vpec8TFN|n}2$Sxn<=K)p45Rs9M72n-LQNB-h1b?ahR3Ow(c< zt4Hd8!__g7Y~t4ba>5r3fmlq z>9v}^6I*{}#>a`!pfTpDqGXI?fHA%&y}xyoLHE>cv;T(8unrI>Jh`3^WJ9Az zqDO~tj4n_~8L2{Lau8-}ilUy}=Nvqs!XLx?cDo09LbAXYRL0auO8xs&_?zL?e2VmK zMRovr%=~aWa&`>q1d3HuKK|9=OJ7gQaV<*o>AY9!I;#4?vIA_Y%*5z56nt)2P31YV zfuyufC#gDh6e_> baseline-reversed.cases } +function write_lines () { + printf "%s\n" "$@" +} + mkdir simple (cd simple echo -e "line1-changed-by-both\nline2-to-be-changed-in-incoming" > ours.blob @@ -396,6 +400,27 @@ mkdir no-change-remove cp ours.blob theirs.blob ) +mkdir simple-conflict +(cd simple-conflict + touch base.blob + write_lines a c >ours.blob + write_lines a b c >theirs.blob +) + +mkdir simple-conflict-2 +(cd simple-conflict-2 + touch base.blob + write_lines a b c d >ours.blob + write_lines a b c >theirs.blob +) + +mkdir simple-conflict-3 +(cd simple-conflict-3 + touch base.blob + write_lines b c >ours.blob + write_lines a b c >theirs.blob +) + mkdir complex (cd complex cat <base.blob @@ -627,7 +652,10 @@ mkdir line-ending-change ) -for dir in simple \ +for dir in simple-conflict-3 \ + simple-conflict-2 \ + simple-conflict \ + simple \ multi-change \ clear-ours \ clear-theirs \ diff --git a/gix-merge/tests/fixtures/tree-baseline.sh b/gix-merge/tests/fixtures/tree-baseline.sh index cc3a8ed2471..595598c725a 100755 --- a/gix-merge/tests/fixtures/tree-baseline.sh +++ b/gix-merge/tests/fixtures/tree-baseline.sh @@ -596,13 +596,40 @@ git init rename-within-rename-2 git checkout expected write_lines 1 2 3 4 5 6 >a/x.f - write_lines 1 2 3 4 5 6 >a/sub/y.f +# write_lines 1 2 3 4 5 6 >a/sub/y.f + echo -ne "1\n2\n3\n4\n5\n<<<<<<< A\n=======\n6\n>>>>>>> B\n" >a/sub/y.f git mv a/sub a/sub-renamed git mv a a-renamed git commit -am "tracked both renames, applied all modifications by merge" - # This means there are no conflicts actually. + # In reverse, it finds a rewrite/rewrite case which gives a base to the merge, hence it passes. + # It should of course be reversible… . + git checkout -b expected-reversed B + write_lines 1 2 3 4 5 6 >a/x.f + write_lines 1 2 3 4 5 6 >a/sub-renamed/y.f + git mv a a-renamed + git commit -am "need to hack this unfortunately" + + + rm .git/index + git update-index --index-info < crate::Result { let root = gix_testtools::scripted_fixture_read_only("text-baseline.sh")?; for (baseline, diverging, expected_percentage) in [ - ("baseline-reversed.cases", DIVERGING_REVERSED, 11), - ("baseline.cases", DIVERGING, 11), + ("baseline.cases", DIVERGING, 10), + ("baseline-reversed.cases", DIVERGING_REVERSED, 10), ] { let cases = std::fs::read_to_string(root.join(baseline))?; let mut out = Vec::new(); @@ -392,7 +392,7 @@ mod text { fn read_blob(root: &Path, rela_path: &str) -> BString { std::fs::read(root.join(rela_path)) - .unwrap_or_else(|_| panic!("Failed to read '{rela_path}' in '{}'", root.display())) + .unwrap_or_else(|err| panic!("Failed to read '{rela_path}' in '{}': {err}", root.display())) .into() } } diff --git a/gix-merge/tests/merge/tree/baseline.rs b/gix-merge/tests/merge/tree/baseline.rs index 7401c052e7c..df5c7ce650c 100644 --- a/gix-merge/tests/merge/tree/baseline.rs +++ b/gix-merge/tests/merge/tree/baseline.rs @@ -300,7 +300,7 @@ pub struct DebugIndexEntry<'a> { stage: gix_index::entry::Stage, } -pub fn clear_entries(state: &gix_index::State) -> Vec> { +pub fn debug_entries(state: &gix_index::State) -> Vec> { state .entries() .iter() diff --git a/gix-merge/tests/merge/tree/mod.rs b/gix-merge/tests/merge/tree/mod.rs index 5c66142b48f..0a328bc0f23 100644 --- a/gix-merge/tests/merge/tree/mod.rs +++ b/gix-merge/tests/merge/tree/mod.rs @@ -26,7 +26,7 @@ fn run_baseline() -> crate::Result { let cases = std::fs::read_to_string(root.join("baseline.cases"))?; let mut actual_cases = 0; let mut skipped_tree_resolve_cases = 0; - // let new_test = Some("tree-to-non-tree-with-rename-A-B"); + // let new_test = Some("rename-within-rename-2-A-B-deviates"); let new_test = None; for baseline::Expectation { root, @@ -122,9 +122,9 @@ fn run_baseline() -> crate::Result { actual.index_changed_after_applying_conflicts(&mut actual_index, conflicts_like_in_git, RemovalMode::Prune); pretty_assertions::assert_eq!( - baseline::clear_entries(&actual_index), - baseline::clear_entries(&expected_index), - "{case_name}: index mismatch\n{:#?}\n{:#?}", + baseline::debug_entries(&actual_index), + baseline::debug_entries(&expected_index), + "{case_name}: index mismatch\nOur conflicts {:#?}\nGit conflicts {:#?}", actual.conflicts, merge_info.conflicts );