You are viewing a plain text version of this content. The canonical link for it is here.
Posted to users@subversion.apache.org by Daniel Dickison <da...@bandcamp.com> on 2020/02/06 20:37:10 UTC

False conflict with interleaved merge commits

We think we’ve found a bug in Subversion’s conflict detection during merge operations that results in a false conflict. It occurs after two ‘merge’ commands are committed in reverse order between two branches, and then a subsequent merge is attempted. I’ve attached a repro script that illustrates this problem using svn 1.13.0, and goes at least as far back as svn 1.8.19, on MacOS 10.14.6.

To summarize:
1. Create trunk and branch1
2. Commit an edit to file mu in trunk
3. Commit an edit to file iota in branch1
4. Merge trunk -> branch1
5. Merge branch1 -> trunk
6. Commit trunk
7. Commit branch1
8. Commit further edits to mu in trunk
9. Attempt to merge trunk into branch

At step 9, svn appears to have forgotten about the merge from steps 4 and 7 and shows a conflict on mu that has only been edited in trunk.

Strangely, the conflict goes away if you flip the order of steps 2 and 3, or commit the merge from step 4 first. While that order of committing merges is typical, this interleaved ordering can arise pretty naturally between two developers working on the same branch.

Does this sound like a legit bug?

Thanks,
Daniel


Re: False conflict with interleaved merge commits

Posted by Daniel Dickison <da...@bandcamp.com>.
On Feb 7, 2020, at 02:44, Stefan Sperling <st...@elego.de> wrote:
> 
> On Thu, Feb 06, 2020 at 03:37:10PM -0500, Daniel Dickison wrote:
>> Strangely, the conflict goes away if you flip the order of steps 2 and 3,
>> or commit the merge from step 4 first.
> 
> I would say it is working as designed, but the design has its flaws.
> 
> As you have found out, the basic problem is that unlike when developers
> work on the same branch, SVN does not enforce an 'svn update' style
> operation when merges and commits interleave.
> 
> This is indeed a problem but it is not easy to fix in SVN itself.
> If this happens to your team a lot you might want to let your developers
> know about the issue and show them how to work around it.

Thanks for the detailed info and quick response. This is good to know that it’s a fundamental design issue that’s unlikely to be fixed in SVN itself.

As far as I know, it has only happened for us with a single developer merging between trunk and a branch, and accidentally interleaving the commits. We’ve already put in tooling to block that particular scenario in our wrapper scripts, so hopefully that mostly prevents this from occurring in the wild.


> An easy way to work around it is the following, which essentially
> simulates the missing 'svn update' style step. After a merge, and
> before committing this merge, if the other branch has new changes
> which were not yet merged, merge those changes as follows to "catch up":
> 
> 	svn merge -r0:HEAD ^/the/other/branch

I did not know about this method to force a sync merge. I have a feeling this will come in handy!


Re: False conflict with interleaved merge commits

Posted by Stefan Sperling <st...@elego.de>.
On Sat, Feb 08, 2020 at 09:57:47AM +0100, Stefan Sperling wrote:
> So, indeed, if there was a way to extend the heuristic to detect this case
> the problem could be avoided. I suspect the heuristic only ever looks at
> server-side mergeinfo and does not take mergeinfo in the local working copy
> into account? See client_find_automatic_merge() in libsvn_client/merge.c.

I have filed https://issues.apache.org/jira/projects/SVN/issues/SVN-4847

Re: False conflict with interleaved merge commits

Posted by Stefan Sperling <st...@elego.de>.
On Sat, Feb 08, 2020 at 04:01:06AM +0000, Daniel Shahaf wrote:
> That's the desired result, isn't it?  If so, is there an underlying rule
> here that we could teach Subversion to follow automatically?

The underlying issue is a decision made by the "automatic merge" heuristic.

Note the output of the "problematic" merge performed by the script:

--- Merging differences between repository URLs into 'wcb':
C    wcb/A/mu

"Merging differences between repository URls" means that the automatic
merge heuristic has decided to run a "reintegrate merge", rather than a
"sync merge". This decision is consistent with the state of mergeinfo in
the repository at the time the merge command is executed.

Changing the last line in the script as follows to force a sync merge
has the same result as your manual cherry-picking merges:

--- Merging r5 through r7 into 'wcb':
U    wcb/A/mu

So, indeed, if there was a way to extend the heuristic to detect this case
the problem could be avoided. I suspect the heuristic only ever looks at
server-side mergeinfo and does not take mergeinfo in the local working copy
into account? See client_find_automatic_merge() in libsvn_client/merge.c.

--- merge-interleave-3.sh.orig	Sat Feb  8 09:47:55 2020
+++ merge-interleave-3.sh	Sat Feb  8 09:46:37 2020
@@ -118,7 +118,7 @@
 # Step 9, the bug.
 # The following trunk->branch1 merge should complete successfully, as A/mu has only been edited on trunk and the first edit has already been merged to branch1, but instead it shows a conflict on A/mu as if the first merge never took place and as if A/mu was edited directly in branch1.
 ${SVN} up wc wcb
-${SVN} merge ${URL}/trunk wcb
+${SVN} merge -r0:HEAD ${URL}/trunk wcb
 
 # Put kill command in a file, in case need to run it manually.
 # echo "kill -9 `cat svnserve-pid`" > k

Re: False conflict with interleaved merge commits

Posted by Daniel Shahaf <d....@daniel.shahaf.name>.
Stefan Sperling wrote on Fri, 07 Feb 2020 08:44 +0100:
> Which is why I see no way to fix this in SVN itself.
> If we made SVN record r5 during the original merge (before r5 existed),
> that would be wrong.
> And if we made SVN skip A/mu edits from r5 during the conflicting merge,
> that would be wrong, too.
> 
> So to me it looks like this needs to be handled by synchronizing
> your developers. Sorry :-/

Quick question.  I commented out the last merge in Daniel's script (line 121),
ran the script, then ran the following by hand:

[[[
% cd wcb
% svn mergeinfo --show-revs=eligible ../wc
r5
r7
% svn merge -c 5 ../wc
--- Recording mergeinfo for merge of r5 into '.':
 U   .
% svn merge -c 7 ../wc
--- Merging r7 into '.':
U    A/mu
--- Recording mergeinfo for merge of r7 into '.':
 G   .
% svn di
Index: A/mu
===================================================================
--- A/mu        (revision 7)
+++ A/mu        (working copy)
@@ -1 +1 @@
-Edit 1 of A/mu in trunk
+Edit 2 of A/mu in trunk
Index: .
===================================================================
--- .   (revision 7)
+++ .   (working copy)

Property changes on: .
___________________________________________________________________
Modified: svn:mergeinfo
## -0,0 +0,1 ##
   Merged /trunk:r5,7
%
]]]

That's the desired result, isn't it?  If so, is there an underlying rule
here that we could teach Subversion to follow automatically?

Cheers,

Daniel

Re: False conflict with interleaved merge commits

Posted by Stefan Sperling <st...@elego.de>.
On Thu, Feb 06, 2020 at 03:37:10PM -0500, Daniel Dickison wrote:
> Strangely, the conflict goes away if you flip the order of steps 2 and 3,
> or commit the merge from step 4 first.

I would say it is working as designed, but the design has its flaws.

As you have found out, the basic problem is that unlike when developers
work on the same branch, SVN does not enforce an 'svn update' style
operation when merges and commits interleave.

This is indeed a problem but it is not easy to fix in SVN itself.
If this happens to your team a lot you might want to let your developers
know about the issue and show them how to work around it.

An easy way to work around it is the following, which essentially
simulates the missing 'svn update' style step. After a merge, and
before committing this merge, if the other branch has new changes
which were not yet merged, merge those changes as follows to "catch up":

	svn merge -r0:HEAD ^/the/other/branch

Patch for your script:

--- merge-interleave-3.sh	Fri Feb  7 08:19:28 2020
+++ merge-interleave-4.sh	Fri Feb  7 08:29:59 2020
@@ -111,6 +111,7 @@
 ${SVN} merge ${URL}/branches/branch1 wc         # Step 5
 ${SVN} pl -v -R wc
 ${SVN} commit wc -m 'Merge branch1 into trunk'  # Step 6
+${SVN} merge -r0:HEAD ${URL}/trunk wcb
 ${SVN} commit wcb -m 'Merge trunk into branch1' # Step 7
 
 # Step 8. edit A/mu one more time in trunk.

Note that I'm using -r0:HEAD to force a "sync merge" at that point.
Once the merge to the other branch has been committed, Subversion will
try to run the 'reintegrate' strategy which will refuse to operate on
a working copy which already contains local modifications.

To understand why your problem is happening, look at the generated mergeinfo.

With your script, the mergeinfo generated by the merge into 'wc' lacks r5:

Properties on 'wcb':
  svn:mergeinfo
    /trunk:2-4

Properties on 'wc':
  svn:mergeinfo
    /branches/branch1:2-4

As you point out, when merges and commits are done sequentually it works
as expected. Mergeinfo contains this instead:

Properties on 'wcb':
  svn:mergeinfo
    /trunk:2-4

Properties on 'wc':
  svn:mergeinfo
    /branches/branch1:2-5

r5 is the revision which records the merge from trunk to the branch.
Without r5 in trunk's mergeinfo, 'svn merge' will pick a merge strategy
that includes r5, which includes 'Edit 1'.

Which is why I see no way to fix this in SVN itself.
If we made SVN record r5 during the original merge (before r5 existed),
that would be wrong.
And if we made SVN skip A/mu edits from r5 during the conflicting merge,
that would be wrong, too.

So to me it looks like this needs to be handled by synchronizing
your developers. Sorry :-/


$ svn log -r5 --diff ^/
[...]
Index: branches/branch1/A/mu
===================================================================
--- branches/branch1/A/mu       (revision 4)
+++ branches/branch1/A/mu       (revision 5)
@@ -1 +1 @@
-This is the file 'A/mu'.
+Edit 1 of A/mu in trunk
Index: branches/branch1
===================================================================
--- branches/branch1    (revision 4)
+++ branches/branch1    (revision 5)

Property changes on: branches/branch1
___________________________________________________________________
Added: svn:mergeinfo
## -0,0 +0,1 ##
   Merged /trunk:r2-4

------------------------------------------------------------------------


Re: False conflict with interleaved merge commits

Posted by Paul Hammant <pa...@hammant.org>.
Here’s a similar one -
https://paulhammant.com/2015/10/05/subversion-merge-limitations-not-in-perforce/

On Fri, Feb 7, 2020 at 6:31 AM Daniel Dickison <da...@bandcamp.com> wrote:

> We think we’ve found a bug in Subversion’s conflict detection during merge
> operations that results in a false conflict. It occurs after two ‘merge’
> commands are committed in reverse order between two branches, and then a
> subsequent merge is attempted. I’ve attached a repro script that
> illustrates this problem using svn 1.13.0, and goes at least as far back as
> svn 1.8.19, on MacOS 10.14.6.
>
> To summarize:
> 1. Create trunk and branch1
> 2. Commit an edit to file mu in trunk
> 3. Commit an edit to file iota in branch1
> 4. Merge trunk -> branch1
> 5. Merge branch1 -> trunk
> 6. Commit trunk
> 7. Commit branch1
> 8. Commit further edits to mu in trunk
> 9. Attempt to merge trunk into branch
>
> At step 9, svn appears to have forgotten about the merge from steps 4 and
> 7 and shows a conflict on mu that has only been edited in trunk.
>
> Strangely, the conflict goes away if you flip the order of steps 2 and 3,
> or commit the merge from step 4 first. While that order of committing
> merges is typical, this interleaved ordering can arise pretty naturally
> between two developers working on the same branch.
>
> Does this sound like a legit bug?
>
> Thanks,
> Daniel
>
>
>