On Thu, Jan 24, 2013 at 5:16 PM, Julian Foad <julianf...@btopenworld.com> wrote:
> Summary: I plan to make 'svn merge' act on --accept=theirs|mine|etc. per 
> file, rather than (as it does now) postponing all conflicts until after the 
> whole merge and then resolving them.
>
>
> I have been investigating the way "--accept=foo" resolves conflicts, for 
> blocker issue #4238 "merge -cA,B with --accept option aborts if rA conflicts" 
> [1].  Postponing all resolution until the end of the whole merge (as we do 
> now) is not good enough in this scenario.  A similar scenario can occur 
> during an automatic merge when the merge code internally decides to do a 
> multi-revision-range merge.

Minor nit: The internal split you speak of here isn't tied solely to
automatic merges.  Cherry-picks can provoke that behavior just as
easily.  For example:

svn merge  SRC TGT -cY
svn merge SRC TGT -rX:Z

Where X < (Y-1) < Y < Z

The second merge will be split into two parts: X:(Y-1), Y:Z

I mention this only because the issue #4238 scenario of

  svn merge -cM,N

Could just as easily be,

  svn merge -rW:X,Y:Z

and either one of those ranges could be split into multiple merges.

> We need to be able to resolve conflicts at least after each phase (revision 
> range) of merge.
>
> For the manual '-cA,B' example the client ('svn') could simply call 
> svn_client_merge once for each specified revision or range, resolving any 
> conflicts after each.  That is not feasible for the ranges decided internally 
> during an automatic merge, because the client doesn't have a way to discover 
> those ranges in advance.  (And the merge code architecture is such that it 
> discovers those ranges incrementally as it goes.)

I just realized another problem for automatic (implied) or cherry-pick
(explicit) ranges in which a given range is split into multiple merges
under the covers: We record mergeinfo based on the requested merge
range, even if only part of the range is merged before we hit a
conflict and abort.  This prevents the remainder of the range from
being merged if the conflict is resolved and the merge repeated.

A simple example (borrowing the WC from the issue #4238 test prior to
the merge):

### Copy a file:

>svn copy iota@1 iota-new
A         iota-new

>svn ci -m "" -q

### Merge a rev from the middle of all eligible revs and commit:

>svn mergeinfo --show-revs eligible ^^/iota iota-new
r2
r3
r4

>svn merge ^^/iota iota-new -c3 --accept theirs-conflict
--- Merging r3 into 'iota-new':
C    iota-new
--- Recording mergeinfo for merge of r3 into 'iota-new':
 U   iota-new
Summary of conflicts:
  Text conflicts: 1
Resolved conflicted state of 'iota-new'

>svn ci -m ""
Sending        iota-new
Transmitting file data .
Committed revision 6.

>svn up -q

### The expected mergeinfo:

>svn pl -vR
Properties on 'iota-new':
  svn:mergeinfo
    /iota:3

### Do a merge which covers a range of revs which the previously merged
### rev is a proper subset of (nothing new here, this is just the "internally
### decided" range split Julian spoke of.

>svn merge ^^/iota iota-new
--- Merging r2 into 'iota-new':
C    iota-new
--- Recording mergeinfo for merge of r2 through r6 into 'iota-new':
 U   iota-new
Summary of conflicts:
  Text conflicts: 1
Conflict discovered in file 'iota-new'.
Select: (p) postpone, (df) diff-full, (e) edit, (m) merge,
        (mc) mine-conflict, (tc) theirs-conflict, (s) show all options: p
..\..\..\subversion\svn\util.c:553: (apr_err=155015)
..\..\..\subversion\svn\merge-cmd.c:138: (apr_err=155015)
..\..\..\subversion\libsvn_client\merge.c:11611: (apr_err=155015)
..\..\..\subversion\libsvn_client\merge.c:11582: (apr_err=155015)
..\..\..\subversion\libsvn_client\merge.c:9057: (apr_err=155015)
..\..\..\subversion\libsvn_client\merge.c:4715: (apr_err=155015)
svn: E155015: One or more conflicts were produced while merging r1:2 into
'\iota-new' --
resolve all conflicts and rerun the merge to apply the remaining
unmerged revisions

>svn st
CM      iota-new
?       iota-new.merge-left.r1
?       iota-new.merge-right.r2
?       iota-new.working
Summary of conflicts:
  Text conflicts: 1

### Obviously we only merged r2 before things blew up, but
### not according to the resulting mergeinfo :

>svn pl -vR
Properties on 'iota-new':
  svn:mergeinfo
    /iota:2-6

### Obviously this means if we resolve the conflict...

>svn resolved -R .
Resolved conflicted state of 'iota-new'

### ...and repeat the merge, then r4 stubbornly remains un-merged:

>svn merge ^^/iota iota-new

### Even if we explicitly ask for r4:

>svn merge ^^/iota iota-new -c4

### We have to disable merge tracking and use a cherry-pick to DTRT
### (i.e. get r4 applied) which is, to put it charitably, less than optimal:

>svn merge ^^/iota iota-new -c4 --ignore-ancestry
--- Merging r4 into 'iota-new':
C    iota-new
Summary of conflicts:
  Text conflicts: 1
Conflict discovered in file 'iota-new'.
Select: (p) postpone, (df) diff-full, (e) edit, (m) merge,
        (mc) mine-conflict, (tc) theirs-conflict, (s) show all options: p

>

FWIW this isn't a regression from 1.7.  I'll file an issue and add a
test shortly.

> I suppose the client could run the merge, let the merge abort if it finds any 
> conflicts during a phase, resolve those conflicts, and then run the same 
> merge again, repeating until the merge is complete.  That may work if the 
> merge code can be run again and again... but it isn't set up to do so in 
> general.  (For one thing, local mods aren't tolerated in all cases, and 
> obviously there will be local mods after the first phase has run.  I bet 
> there would be other problems too.)

Look no further than the above example :-\

> I think a good enough solution for 1.8 is this:
>
>   * With --accept=postpone, if any conflicts are raised during a merge phase 
> (one revision range), then abort the merge after that phase.  (The details 
> are not relevant to this mail thread.)
>
>   * Otherwise, the user requested a specific conflict resolution.  At the 
> moment, we run the merge with a per-node resolver callback installed that 
> always chooses 'postpone' but also collects a list of the conflicted paths, 
> and then we run 'resolve' on those paths afterwards.  Instead, let
> the resolver callback be active during the merge, resolving each
> conflict on a node immediately after that conflict has been raised, according 
> to '--accept'.
>
>   * By resolving a conflict so soon, we can avoid notifying the merge result 
> as 'C' for conflict and instead notify 'U' or 'G' for that node, and avoid 
> "Resolved conflict on 'foo'" lines.  I think that is better.
>
>   * After the merge, there might still be some postponed conflicts, depending 
> on whether the implementation of the chosen --accept was capable of handling 
> every conflict that occurred.  We might want to consider doing something like 
> running the interactive resolver afterwards if this happens, but I think not.
>
> The main alternative I can think of to this plan would be to design a way to 
> store multiple
> conflicts (of the same kind) per node and so be able to postpone all 
> conflicts from multiple phases of merge.  We have decided before that that
>  is too difficult and I still think so.
>
> So, can you see any problems with the above approach?

Other than noted above no.

-- 
Paul T. Burba
CollabNet, Inc. -- www.collab.net -- Enterprise Cloud Development
Skype: ptburba

> I think we were doing this at one stage during development.  I assume we 
> stopped because (a) for interactive resolution, postponing that stage until 
> after the merge avoids network timeouts due to waiting for the user; and (b) 
> we wanted to test the ability to postpone all conflicts and/or there seemed 
> to be no reason to make the '--accept=' case work differently from the 
> interactive case.
>
> - Julian
>
>
> [1] <http://subversion.tigris.org/issues/show_bug.cgi?id=4238>
>
> --
> Certified & Supported Apache Subversion Downloads: 
> http://www.wandisco.com/subversion/download

Reply via email to