git/list[1] front-page[2] threads[3] people[4] search[5] about
 

Re: [PATCH v3 10/11] rerere: teach rerere to handle nested conflicts

From
Thomas Gummerer <t.gummerer@gmail.com>
Date
Jul 30, 2018, 20:20 UTC
Message-ID
<20180730202037.GF9955@hank.intra.tgummerer.com>
In-Reply-To
<xmqqzhy8hb2s.fsf@gitster-ct.c.googlers.com>
On 07/30, Junio C Hamano wrote:
Show 42 quoted lines
> Thomas Gummerer <t.gummerer@gmail.com> writes:
> 
> > Currently rerere can't handle nested conflicts and will error out when
> > it encounters such conflicts.  Do that by recursively calling the
> > 'handle_conflict' function to normalize the conflict.
> >
> > The conflict ID calculation here deserves some explanation:
> >
> > As we are using the same handle_conflict function, the nested conflict
> > is normalized the same way as for non-nested conflicts, which means
> > the ancestor in the diff3 case is stripped out, and the parts of the
> > conflict are ordered alphabetically.
> >
> > The conflict ID is however is only calculated in the top level
> > handle_conflict call, so it will include the markers that 'rerere'
> > adds to the output.  e.g. say there's the following conflict:
> >
> >     <<<<<<< HEAD
> >     1
> >     =======
> >     <<<<<<< HEAD
> >     3
> >     =======
> >     2
> >     >>>>>>> branch-2
> >     >>>>>>> branch-3~
> 
> Hmph, I vaguely recall that I made inner merges to use the conflict
> markers automatically lengthened (by two, if I recall correctly)
> than its immediate outer merge.  Wouldn't the above look more like
> 
>      <<<<<<< HEAD
>      1
>      =======
>      <<<<<<<<< HEAD
>      3
>      =========
>      2
>      >>>>>>>>> branch-2
>      >>>>>>> branch-3~
>     
> Perhaps I am not recalling it correctly.

The only way I could reproduce this is by not resolving a conflict (just leaving the conflict markers in place, but running 'git add conflicted'), and then merging something else, which produces another conflict, where one of the sides was the one with conflict markers already in the file, same as what I did in the test.

So in that case, the conflict markers of the already existing conflict would just be treated as normal text during the merge I believe, and thus the new conflict markers would be the same length.

The usage of git is really a bit wrong here, so I don't know if it's actually worth helping the users at this point. But trying to understand how rerere exactly works, I had this written up already, so I thought I would include it in this series anyway in case it helps somebody :)

Show 58 quoted lines
> > it would be recorde as follows in the preimage:
> >
> >     <<<<<<<
> >     1
> >     =======
> >     <<<<<<<
> >     2
> >     =======
> >     3
> >     >>>>>>>
> >     >>>>>>>
> >
> > and the conflict ID would be calculated as
> >
> >     sha1(1<NUL><<<<<<<
> >     2
> >     =======
> >     3
> >     >>>>>>><NUL>)
> >
> > Stripping out vs. leaving the conflict markers in place in the inner
> > conflict should have no practical impact, but it simplifies the
> > implementation.
> >
> > Signed-off-by: Thomas Gummerer <t.gummerer@gmail.com>
> > ---
> >  Documentation/technical/rerere.txt | 42 ++++++++++++++++++++++++++++++
> >  rerere.c                           | 10 +++++--
> >  t/t4200-rerere.sh                  | 37 ++++++++++++++++++++++++++
> >  3 files changed, 87 insertions(+), 2 deletions(-)
> >
> > [..snip..]
> > 
> > diff --git a/rerere.c b/rerere.c
> > index a35b88916c..f78bef80b1 100644
> > --- a/rerere.c
> > +++ b/rerere.c
> > @@ -365,12 +365,18 @@ static int handle_conflict(struct strbuf *out, struct rerere_io *io,
> >  		RR_SIDE_1 = 0, RR_SIDE_2, RR_ORIGINAL
> >  	} hunk = RR_SIDE_1;
> >  	struct strbuf one = STRBUF_INIT, two = STRBUF_INIT;
> > -	struct strbuf buf = STRBUF_INIT;
> > +	struct strbuf buf = STRBUF_INIT, conflict = STRBUF_INIT;
> >  	int has_conflicts = -1;
> >  
> >  	while (!io->getline(&buf, io)) {
> >  		if (is_cmarker(buf.buf, '<', marker_size)) {
> > -			break;
> > +			if (handle_conflict(&conflict, io, marker_size, NULL) < 0)
> > +				break;
> > +			if (hunk == RR_SIDE_1)
> > +				strbuf_addbuf(&one, &conflict);
> > +			else
> > +				strbuf_addbuf(&two, &conflict);
> 
> Hmph, do we ever see the inner conflict block while we are skipping
> and ignoring the common ancestor version, or it is impossible that
> we see '<' only while processing either our or their side?

As mentioned above, I haven't been able to reproduce creating an inner conflict block outside of the case mentioned above, where the user committed conflict markers, and then did another merge.

I don't think it can appear outside of that case in "normal" operation.

Show 50 quoted lines
> > +			strbuf_release(&conflict);
> >  		} else if (is_cmarker(buf.buf, '|', marker_size)) {
> >  			if (hunk != RR_SIDE_1)
> >  				break;
> > diff --git a/t/t4200-rerere.sh b/t/t4200-rerere.sh
> > index 34f0518a5e..d63fe2b33b 100755
> > --- a/t/t4200-rerere.sh
> > +++ b/t/t4200-rerere.sh
> > @@ -602,4 +602,41 @@ test_expect_success 'rerere with unexpected conflict markers does not crash' '
> >  	git rerere clear
> >  '
> >  
> > +test_expect_success 'rerere with inner conflict markers' '
> > +	git reset --hard &&
> > +
> > +	git checkout -b A master &&
> > +	echo "bar" >test &&
> > +	git add test &&
> > +	git commit -q -m two &&
> > +	echo "baz" >test &&
> > +	git add test &&
> > +	git commit -q -m three &&
> > +
> > +	git reset --hard &&
> > +	git checkout -b B master &&
> > +	echo "foo" >test &&
> > +	git add test &&
> > +	git commit -q -a -m one &&
> > +
> > +	test_must_fail git merge A~ &&
> > +	git add test &&
> > +	git commit -q -m "will solve conflicts later" &&
> > +	test_must_fail git merge A &&
> > +
> > +	echo "resolved" >test &&
> > +	git add test &&
> > +	git commit -q -m "solved conflict" &&
> > +
> > +	echo "resolved" >expect &&
> > +
> > +	git reset --hard HEAD~~ &&
> > +	test_must_fail git merge A~ &&
> > +	git add test &&
> > +	git commit -q -m "will solve conflicts later" &&
> > +	test_must_fail git merge A &&
> > +	cat test >actual &&
> > +	test_cmp expect actual
> > +'
> > +
> >  test_done
Previous: Junio C HamanoNext: Thomas Gummerer
Message 55 of 84 in “rerere: handle nested conflicts”
  1. 0/7 rerere: handle nested conflictsThomas Gummerer, May 20, 2018
  2. 1/7 rerere: unify error message when read_cache failsThomas Gummerer, May 20, 2018
  3. Stefan BellerMay 21, 2018
  4. 2/7 rerere: mark strings for translationThomas Gummerer, May 20, 2018
  5. Junio C HamanoMay 24, 2018
  6. 3/7 rerere: add some documentationThomas Gummerer, May 20, 2018
  7. Junio C HamanoMay 24, 2018
  8. Thomas GummererJun 3, 2018
  9. 4/7 rerere: fix crash when conflict goes unresolvedThomas Gummerer, May 20, 2018
  10. Junio C HamanoMay 24, 2018
  11. Thomas GummererMay 24, 2018
  12. Junio C HamanoMay 25, 2018
  13. 5/7 rerere: only return whether a path has conflicts or notThomas Gummerer, May 20, 2018
  14. Junio C HamanoMay 24, 2018
  15. 6/7 rerere: factor out handle_conflict functionThomas Gummerer, May 20, 2018
  16. 7/7 rerere: teach rerere to handle nested conflictsThomas Gummerer, May 20, 2018
  17. Junio C HamanoMay 24, 2018
  18. Thomas GummererMay 24, 2018
  19. 00/10 rerere: handle nested conflictsThomas Gummerer, Jun 5, 2018
  20. 01/10 rerere: unify error messages when read_cache failsThomas Gummerer, Jun 5, 2018
  21. 03/10 rerere: wrap paths in output in sqThomas Gummerer, Jun 5, 2018
  22. 05/10 rerere: add some documentationThomas Gummerer, Jun 5, 2018
  23. 07/10 rerere: only return whether a path has conflicts or notThomas Gummerer, Jun 5, 2018
  24. 09/10 rerere: teach rerere to handle nested conflictsThomas Gummerer, Jun 5, 2018
  25. 10/10 rerere: recalculate conflict ID when unresolved conflict is committedThomas Gummerer, Jun 5, 2018
  26. 02/10 rerere: lowercase error messagesThomas Gummerer, Jun 5, 2018
  27. 06/10 rerere: fix crash when conflict goes unresolvedThomas Gummerer, Jun 5, 2018
  28. 04/10 rerere: mark strings for translationThomas Gummerer, Jun 5, 2018
  29. 08/10 rerere: factor out handle_conflict functionThomas Gummerer, Jun 5, 2018
  30. Thomas GummererJul 3, 2018
  31. Junio C HamanoJul 6, 2018
  32. Thomas GummererJul 10, 2018
  33. 00/11 rerere: handle nested conflictsThomas Gummerer, Jul 14, 2018
  34. 01/11 rerere: unify error messages when read_cache failsThomas Gummerer, Jul 14, 2018
  35. 03/11 rerere: wrap paths in output in sqThomas Gummerer, Jul 14, 2018
  36. 02/11 rerere: lowercase error messagesThomas Gummerer, Jul 14, 2018
  37. 04/11 rerere: mark strings for translationThomas Gummerer, Jul 14, 2018
  38. Simon RuderichJul 15, 2018
  39. Thomas GummererJul 16, 2018
  40. 06/11 rerere: fix crash when conflict goes unresolvedThomas Gummerer, Jul 14, 2018
  41. Junio C HamanoJul 30, 2018
  42. Thomas GummererJul 30, 2018
  43. 05/11 rerere: add documentation for conflict normalizationThomas Gummerer, Jul 14, 2018
  44. Junio C HamanoJul 30, 2018
  45. Thomas GummererJul 30, 2018
  46. 07/11 rerere: only return whether a path has conflicts or notThomas Gummerer, Jul 14, 2018
  47. Junio C HamanoJul 30, 2018
  48. Thomas GummererJul 30, 2018
  49. 08/11 rerere: factor out handle_conflict functionThomas Gummerer, Jul 14, 2018
  50. Junio C HamanoJul 30, 2018
  51. 09/11 rerere: return strbuf from handle pathThomas Gummerer, Jul 14, 2018
  52. Junio C HamanoJul 30, 2018
  53. 10/11 rerere: teach rerere to handle nested conflictsThomas Gummerer, Jul 14, 2018
  54. Junio C HamanoJul 30, 2018
  55. Thomas GummererJul 30, 2018
  56. 11/11 rerere: recalculate conflict ID when unresolved conflict is committedThomas Gummerer, Jul 14, 2018
  57. Junio C HamanoJul 30, 2018
  58. Thomas GummererJul 30, 2018
  59. 00/11 rerere: handle nested conflictsThomas Gummerer, Aug 5, 2018
  60. 01/11 rerere: unify error messages when read_cache failsThomas Gummerer, Aug 5, 2018
  61. 02/11 rerere: lowercase error messagesThomas Gummerer, Aug 5, 2018
  62. 03/11 rerere: wrap paths in output in sqThomas Gummerer, Aug 5, 2018
  63. 04/11 rerere: mark strings for translationThomas Gummerer, Aug 5, 2018
  64. 05/11 rerere: add documentation for conflict normalizationThomas Gummerer, Aug 5, 2018
  65. 06/11 rerere: fix crash with files rerere can't handleThomas Gummerer, Aug 5, 2018
  66. 07/11 rerere: only return whether a path has conflicts or notThomas Gummerer, Aug 5, 2018
  67. 08/11 rerere: factor out handle_conflict functionThomas Gummerer, Aug 5, 2018
  68. 09/11 rerere: return strbuf from handle pathThomas Gummerer, Aug 5, 2018
  69. 10/11 rerere: teach rerere to handle nested conflictsThomas Gummerer, Aug 5, 2018
  70. Ævar Arnfjörð BjarmasonAug 22, 2018
  71. Junio C HamanoAug 22, 2018
  72. Thomas GummererAug 22, 2018
  73. Junio C HamanoAug 22, 2018
  74. Thomas GummererAug 24, 2018
  75. 1/2 rerere: remove documentation for "nested conflicts"Thomas Gummerer, Aug 24, 2018
  76. 2/2 rerere: add not about files with existing conflict markersThomas Gummerer, Aug 24, 2018
  77. 1/2 rerere: mention caveat about unmatched conflict markersThomas Gummerer, Aug 28, 2018
  78. 2/2 rerere: add note about files with existing conflict markersThomas Gummerer, Aug 28, 2018
  79. Junio C HamanoAug 29, 2018
  80. Thomas GummererSep 1, 2018
  81. Junio C HamanoAug 27, 2018
  82. Thomas GummererAug 28, 2018
  83. Junio C HamanoAug 27, 2018
  84. 11/11 rerere: recalculate conflict ID when unresolved conflict is committedThomas Gummerer, Aug 5, 2018

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.