Discussion:
Tree conflict resolution considered harmful
Dag-Erling Smørgrav
2018-08-29 10:54:43 UTC
Permalink
I'm using Subversion 1.10.2 to perform a non-interactive merge with
around 15 tree conflicts (files that exist on the source branch but have
been deleted from the target branch). It spends exactly two hours on
each conflict before the connection is killed and it gives up and moves
on to the next. Here's an excerpt from ktrace showing svn's attempt to
resolve the first conflict:

33821 svn 35.021454 GIO fd 1 wrote 20 bytes
"\rChecking r338344..."
--
33821 svn 40.898214 GIO fd 1 wrote 20 bytes
"\rChecking r338059..."
33821 svn 40.898328 GIO fd 1 wrote 20 bytes
"\rChecking r333678..."
33821 svn 40.898412 GIO fd 1 wrote 20 bytes
"\rChecking r333677..."
--
33821 svn 40.900558 GIO fd 1 wrote 20 bytes
"\rChecking r333490..."
--
33821 svn 77.091446 GIO fd 1 wrote 20 bytes
"\rChecking r333389..."
--
33821 svn 95.000296 GIO fd 1 wrote 20 bytes
"\rChecking r333300..."
--
33821 svn 95.001008 GIO fd 1 wrote 20 bytes
"\rChecking r326169..."
--
33821 svn 671.067538 GIO fd 1 wrote 20 bytes
"\rChecking r322052..."
--
33821 svn 671.337258 GIO fd 1 wrote 20 bytes
"\rChecking r321369..."
--
33821 svn 7240.543297 GIO fd 2 wrote 62 bytes
"svn: warning: W210002: Network connection closed unexpectedly

The third column is the time elapsed since the start of the process.

The actual conflict is in r294466, which removed the file in question
from the target branch. The revision it's stuck on, r321369, only
touched the svn:mergeinfo property on the current directory (propagated
down from a merge higher up in the tree).

Please add an option to disable conflict resolution entirely.

DES
--
Dag-Erling Smørgrav - ***@des.no
Daniel Shahaf
2018-08-29 11:11:29 UTC
Permalink
Post by Dag-Erling Smørgrav
Please add an option to disable conflict resolution entirely.
As a hotfix, I think this will do what you need?

Index: subversion/libsvn_client/conflicts.c
===================================================================
--- subversion/libsvn_client/conflicts.c (revision 1838961)
+++ subversion/libsvn_client/conflicts.c (working copy)
@@ -11366,6 +11366,7 @@ conflict_type_specific_setup(svn_client_conflict_t
svn_boolean_t tree_conflicted;
svn_wc_conflict_action_t incoming_change;
svn_wc_conflict_reason_t local_change;
+ return SVN_NO_ERROR;

/* For now, we only deal with tree conflicts here. */
SVN_ERR(svn_client_conflict_get_conflicted(NULL, NULL, &tree_conflicted,
Dag-Erling Smørgrav
2018-08-29 11:49:31 UTC
Permalink
Post by Daniel Shahaf
Post by Dag-Erling Smørgrav
Please add an option to disable conflict resolution entirely.
As a hotfix, I think this will do what you need?
Thanks, I will test this later.

DES
--
Dag-Erling Smørgrav - ***@des.no
Branko Čibej
2018-08-29 12:29:16 UTC
Permalink
Post by Dag-Erling Smørgrav
I'm using Subversion 1.10.2 to perform a non-interactive merge with
around 15 tree conflicts (files that exist on the source branch but have
been deleted from the target branch). It spends exactly two hours on
each conflict before the connection is killed and it gives up and moves
on to the next. Here's an excerpt from ktrace showing svn's attempt to
33821 svn 35.021454 GIO fd 1 wrote 20 bytes
"\rChecking r338344..."
--
33821 svn 40.898214 GIO fd 1 wrote 20 bytes
"\rChecking r338059..."
33821 svn 40.898328 GIO fd 1 wrote 20 bytes
"\rChecking r333678..."
33821 svn 40.898412 GIO fd 1 wrote 20 bytes
"\rChecking r333677..."
--
33821 svn 40.900558 GIO fd 1 wrote 20 bytes
"\rChecking r333490..."
--
33821 svn 77.091446 GIO fd 1 wrote 20 bytes
"\rChecking r333389..."
--
33821 svn 95.000296 GIO fd 1 wrote 20 bytes
"\rChecking r333300..."
--
33821 svn 95.001008 GIO fd 1 wrote 20 bytes
"\rChecking r326169..."
--
33821 svn 671.067538 GIO fd 1 wrote 20 bytes
"\rChecking r322052..."
--
33821 svn 671.337258 GIO fd 1 wrote 20 bytes
"\rChecking r321369..."
--
33821 svn 7240.543297 GIO fd 2 wrote 62 bytes
"svn: warning: W210002: Network connection closed unexpectedly
The third column is the time elapsed since the start of the process.
The actual conflict is in r294466, which removed the file in question
from the target branch. The revision it's stuck on, r321369, only
touched the svn:mergeinfo property on the current directory (propagated
down from a merge higher up in the tree).
Please add an option to disable conflict resolution entirely.
Would not one of the --accept option values work? E.g.,
`--accept=theirs-conflict', though that would also affect how the file
contents are merged.

-- Brane
Stefan Sperling
2018-08-29 15:25:37 UTC
Permalink
Post by Branko Čibej
Post by Dag-Erling Smørgrav
Please add an option to disable conflict resolution entirely.
Would not one of the --accept option values work? E.g.,
`--accept=theirs-conflict', though that would also affect how the file
contents are merged.
-- Brane
Either --accept postpone or --non-interactive should prevent
the interactive resolver from running.

Then resolve tree conflicts manually as with SVN 1.9.
Stefan Sperling
2018-08-29 15:39:37 UTC
Permalink
Post by Dag-Erling Smørgrav
I'm using Subversion 1.10.2 to perform a non-interactive merge with
around 15 tree conflicts (files that exist on the source branch but have
been deleted from the target branch). It spends exactly two hours on
each conflict before the connection is killed and it gives up and moves
on to the next. Here's an excerpt from ktrace showing svn's attempt to
33821 svn 35.021454 GIO fd 1 wrote 20 bytes
"\rChecking r338344..."
--
33821 svn 40.898214 GIO fd 1 wrote 20 bytes
"\rChecking r338059..."
33821 svn 40.898328 GIO fd 1 wrote 20 bytes
"\rChecking r333678..."
33821 svn 40.898412 GIO fd 1 wrote 20 bytes
"\rChecking r333677..."
--
33821 svn 40.900558 GIO fd 1 wrote 20 bytes
"\rChecking r333490..."
--
33821 svn 77.091446 GIO fd 1 wrote 20 bytes
"\rChecking r333389..."
--
33821 svn 95.000296 GIO fd 1 wrote 20 bytes
"\rChecking r333300..."
--
33821 svn 95.001008 GIO fd 1 wrote 20 bytes
"\rChecking r326169..."
--
33821 svn 671.067538 GIO fd 1 wrote 20 bytes
"\rChecking r322052..."
--
33821 svn 671.337258 GIO fd 1 wrote 20 bytes
"\rChecking r321369..."
--
33821 svn 7240.543297 GIO fd 2 wrote 62 bytes
"svn: warning: W210002: Network connection closed unexpectedly
The third column is the time elapsed since the start of the process.
The actual conflict is in r294466, which removed the file in question
from the target branch. The revision it's stuck on, r321369, only
touched the svn:mergeinfo property on the current directory (propagated
down from a merge higher up in the tree).
This does indeed look like a problem.

Unfortunately, you're not providing sufficient information. Ktrace tells
us that time was spent somewhere. But it does not tell us what this time
was actually spent on.

Could you provide a shell script I can use to actually run the problematic
merge myself in a working copy of the appropriate subdirectory of the FreeBSD
repository? Ideally, such a script would assume nothing beyond a new empty
directory and an 'svn' binary in $PATH, and then allow me to proceed to the
actual problem you were seeing. I'll run this script on OpenBSD, so a ksh
compatible script would be nice.

If you could take the time to provide that, then I will take the time to look
into the problem. But your report so far doesn't provide enough material for
me to chew on.

Thanks!
Dag-Erling Smørgrav
2018-08-30 09:34:18 UTC
Permalink
Post by Stefan Sperling
Could you provide a shell script I can use to actually run the problematic
merge myself in a working copy of the appropriate subdirectory of the FreeBSD
repository?
svn co -q svn://svn.freebsd.org/base/***@338344
cd head/crypto/openssh
svn merge --non-interactive -c338344 \^/vendor-crypto/openssh/dist .

DES
--
Dag-Erling Smørgrav - ***@des.no
Stefan Sperling
2018-08-30 11:49:35 UTC
Permalink
Post by Dag-Erling Smørgrav
Post by Stefan Sperling
Could you provide a shell script I can use to actually run the problematic
merge myself in a working copy of the appropriate subdirectory of the FreeBSD
repository?
cd head/crypto/openssh
svn merge --non-interactive -c338344 \^/vendor-crypto/openssh/dist .
Thanks. I understand the problem now and have found a solution.

First, let me explain the problem:

A tree conflict occurs because config.h.in was deleted from head
in r294466:

------------------------------------------------------------------------
r294466 | des | 2016-01-21 00:08:57 +0100 (Thu, 21 Jan 2016) | 4 lines
Changed paths:
D /head/crypto/openssh/config.h.in
D /head/crypto/openssh/configure
D /head/crypto/openssh/moduli.0
D /head/crypto/openssh/scp.0
D /head/crypto/openssh/sftp-server.0
D /head/crypto/openssh/sftp.0
D /head/crypto/openssh/ssh-add.0
D /head/crypto/openssh/ssh-agent.0
D /head/crypto/openssh/ssh-keygen.0
D /head/crypto/openssh/ssh-keyscan.0
D /head/crypto/openssh/ssh-keysign.0
D /head/crypto/openssh/ssh-pkcs11-helper.0
D /head/crypto/openssh/ssh.0
D /head/crypto/openssh/ssh_config.0
D /head/crypto/openssh/sshd.0
D /head/crypto/openssh/sshd_config.0

Remove a number of generated files which are either out-of-date (because
they are never regenerated to reflect our changes) or in the way of
freebsd-configure.sh.

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


However, config.h.in still exists on the vendor-branch, and during the
merge of r338344 we get an edit for this file:

------------------------------------------------------------------------
r338344 | des | 2018-08-28 12:47:58 +0200 (Tue, 28 Aug 2018) | 2 lines

Vendor import of OpenSSH 7.8p1.

Changed paths:
[...]
M /vendor-crypto/openssh/dist/config.h.in
[...]
------------------------------------------------------------------------


Obviously, what we want to resolver to do here is to discard
the incoming edit and mark the tree conflict as resolved.

Your problem appears when the resolver tries to determine whether
the 'locally missing' config.h.in has ever existed in head.
The resolver does this because when describing the conflict it
wants to tell the user which revision deleted the file, to save
the user the trouble of figuring this out by themselves.
And of course, knowing whether the file did exist in the past is
vital information when determining applicable resolution options.

To find the missing file, the resolver first attempts to find a
youngest common ancestor between the paths vendor-crypto/openssh/dist
and head/crypto/openssh. It does this because if a youngest common
ancestor exists, our search back through history can stop there.

And now starts our first bit of trouble.
There is no youngest common ancestor:

533 err = find_yca(&yca_loc, p1, peg_rev1, p2, peg_rev2,
(gdb) p p1
$13 = 0x557687f79a0 "vendor-crypto/openssh/dist"
(gdb) p p2
$14 = 0x55707cc5140 "head/crypto/openssh"
(gdb) n
536 if (err)
(gdb) p err
$15 = (svn_error_t *) 0x0
(gdb) p yca_loc
$16 = (svn_client__pathrev_t *) 0x0

This is because FreeBSD's history did not actually follow the vendor-branch
pattern with openssh, at least not in the way SVN would expect this pattern.
This goes back all the way to CVS days, so the commits below where generated
by cvs2svn. In 1999 the openssh directory was first created in head:

The directory /head/crypto/openssh
------------------------------------------------------------------------
r53874 | green | 1999-11-29 08:09:44 +0100 (Mon, 29 Nov 1999)
Changed paths:
A /head/crypto/openssh
A /head/crypto/openssh/pam_ssh
A /head/crypto/openssh/pam_ssh/pam_ssh.c
A /head/lib/libpam/modules/pam_ssh
A /head/lib/libpam/modules/pam_ssh/pam_ssh.c

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


Later on, the history of vendor-crypto/openssh/dist starts in r57429:
------------------------------------------------------------------------
r57429 | markm | 2000-02-24 15:29:47 +0100 (Thu, 24 Feb 2000) | 2 lines
Changed paths:
A /vendor-crypto/openssh
A /vendor-crypto/openssh/dist
[...]
------------------------------------------------------------------------

If ^/head/crypto/openssh had been made a copy of ^/vendor-crypto/openssh
during the cvs2svn conversion, you would not have found this problem today.
Merges from vendor to head would probably just work as expected.

Of course, this is all water under the bridge now, so let's see what we
can do to make the resolver cope with this.

Why is this such a problem for the resolver anyway?
People have somehow been merging from ^/vendor-crypto/openssh to
^/head/openssh. This probably works fine when merging with a diff+patch
mentality. However, when resolving tree conflicts in an automated way we
have to rely on ancestry information to guide decisions during the
resolution process. If path ancestry doesn't match expected branching
and merging patterns then the resolver's heuristics can get thrown off
the rails.

It is also clear now why the resolver spends so much time on this merge.
Lacking a common ancestor, it uses revision zero as a lower bound in its
search for the deleted config.h.in file.
Scanning the log back all the way to revision zero will take some time
but it should not take 2 hours. And in this case, we know that the search
should stop in r294466. Unfortunately, the problem is exacerbated by the
resolver's move detection logic. While scanning history the resolver
also scans for moves in each revision. The rationale being that while we're
already walking the log we might as well collect as much information as
possible. The problem with this approach is that scanning for moves does
not come for free in a repository of FreeBSD's dimensions. The problematic
revision r321369 is a revision which upgrades the clang compiler and
moves many files files around:

------------------------------------------------------------------------
r321369 | dim | 2017-07-22 13:08:25 +0200 (Sat, 22 Jul 2017) | 10 lines

Upgrade our copies of clang, llvm, lld, lldb, compiler-rt and libc++ to
5.0.0 (trunk r308421).
[...]
------------------------------------------------------------------------

While scanning for moves in this revision the client sends many
'get-location-segments' requests to the server, and this is where
all the extra time is spent.

The conclusion I am drawing from this is that asking the resolver scan
for moves unconditionally was a mistake. It is better to only do so if
a youngest common ancestor is known since it will act as a bound for
our search through history.

If I adjust resolver accordingly, it determines the revision in which
config.h.in was deleted within a couple of seconds:

$ svn resolve
Merge conflict discovered in file 'INSTALL'.
Select: (p) Postpone, (df) Show diff, (e) Edit file, (m) Merge,
(s) Show all options: p
Merge conflict discovered in file 'auth2.c'.
Select: (p) Postpone, (df) Show diff, (e) Edit file, (m) Merge,
(s) Show all options: p
Searching tree conflict details for 'config.h.in' in repository:
Checking r294466... done
Tree conflict on 'config.h.in':
Changes destined for a file arrived during merge of
'^/vendor-crypto/openssh/dist/config.h.in:338344'.
No such file or directory was found in the merge target working copy.
'^/head/crypto/openssh/config.h.in' was deleted in r294466 by des.

Subversion is not smart enough to resolve this tree conflict automatically!
See 'svn help resolve' for more information.

Select: (p) Postpone, (r) Mark as resolved, (h) Help, (q) Quit resolution:

No resolution handler has been implementation for this particular case
so the resolver won't suggest a "smart" resolution option yet.
Ideally, it would offer an option to "discard the incoming edit".
However, just using the 'r' option will lead to the desired outcome
in this case so you should now be able to perform such merges.

I've committed my fix to trunk in https://svn.apache.org/r1839662

Below is a patch which applies to 1.10:

Index: subversion/libsvn_client/conflicts.c
===================================================================
--- subversion/libsvn_client/conflicts.c (revision 1839662)
+++ subversion/libsvn_client/conflicts.c (working copy)
@@ -1059,6 +1059,9 @@ find_deleted_rev(void *baton,
{
apr_array_header_t *moves;

+ if (b->moves_table == NULL)
+ return SVN_NO_ERROR;
+
moves = apr_hash_get(b->moves_table, &log_entry->revision,
sizeof(svn_revnum_t));
if (moves)
@@ -2223,8 +2226,8 @@ find_operative_moves(apr_array_header_t **moves,
* If the node was replaced rather than deleted, set *REPLACING_NODE_KIND to
* the node kind of the replacing node. Else, set it to svn_node_unknown.
* Only request the log for revisions up to END_REV from the server.
- * If the deleted node was moved, provide heads of move chains in *MOVES.
- * If the node was not moved,set *MOVES to NULL.
+ * If MOVES it not NULL, and the deleted node was moved, provide heads of
+ * move chains in *MOVES, or, if the node was not moved, set *MOVES to NULL.
*/
static svn_error_t *
find_revision_for_suspected_deletion(svn_revnum_t *deleted_rev,
@@ -2261,10 +2264,11 @@ find_revision_for_suspected_deletion(svn_revnum_t
scratch_pool));
victim_abspath = svn_client_conflict_get_local_abspath(conflict);

- SVN_ERR(find_moves_in_revision_range(&moves_table, parent_repos_relpath,
- repos_root_url, repos_uuid,
- victim_abspath, start_rev, end_rev,
- ctx, result_pool, scratch_pool));
+ if (moves)
+ SVN_ERR(find_moves_in_revision_range(&moves_table, parent_repos_relpath,
+ repos_root_url, repos_uuid,
+ victim_abspath, start_rev, end_rev,
+ ctx, result_pool, scratch_pool));

url = svn_path_url_add_component2(repos_root_url, parent_repos_relpath,
scratch_pool);
@@ -2289,7 +2293,8 @@ find_revision_for_suspected_deletion(svn_revnum_t
b.repos_root_url = repos_root_url;
b.repos_uuid = repos_uuid;
b.ctx = ctx;
- b.moves_table = moves_table;
+ if (moves)
+ b.moves_table = moves_table;
b.result_pool = result_pool;
SVN_ERR(svn_ra__dup_session(&b.extra_ra_session, ra_session, NULL,
scratch_pool, scratch_pool));
@@ -2319,7 +2324,7 @@ find_revision_for_suspected_deletion(svn_revnum_t
{
struct repos_move_info *move = b.move;

- if (move)
+ if (moves && move)
{
*deleted_rev = move->rev;
*deleted_rev_author = move->rev_author;
@@ -2337,7 +2342,8 @@ find_revision_for_suspected_deletion(svn_revnum_t
*deleted_rev = SVN_INVALID_REVNUM;
*deleted_rev_author = NULL;
*replacing_node_kind = svn_node_unknown;
- *moves = NULL;
+ if (moves)
+ *moves = NULL;
}
return SVN_NO_ERROR;
}
@@ -2346,10 +2352,11 @@ find_revision_for_suspected_deletion(svn_revnum_t
*deleted_rev = b.deleted_rev;
*deleted_rev_author = b.deleted_rev_author;
*replacing_node_kind = b.replacing_node_kind;
- SVN_ERR(find_operative_moves(moves, moves_table,
- b.deleted_repos_relpath, b.deleted_rev,
- ra_session, repos_root_url,
- result_pool, scratch_pool));
+ if (moves)
+ SVN_ERR(find_operative_moves(moves, moves_table,
+ b.deleted_repos_relpath, b.deleted_rev,
+ ra_session, repos_root_url,
+ result_pool, scratch_pool));
}

return SVN_NO_ERROR;
@@ -2693,7 +2700,8 @@ conflict_tree_get_details_local_missing(svn_client
end_rev = 0; /* ### We might walk through all of history... */

SVN_ERR(find_revision_for_suspected_deletion(
- &deleted_rev, &deleted_rev_author, &replacing_node_kind, &moves,
+ &deleted_rev, &deleted_rev_author, &replacing_node_kind,
+ yca_loc ? &moves : NULL,
conflict, deleted_basename, parent_repos_relpath,
parent_peg_rev, end_rev, related_repos_relpath, related_peg_rev,
ctx, conflict->pool, scratch_pool));
Index: .
===================================================================
--- . (revision 1839662)
+++ . (working copy)

Property changes on: .
___________________________________________________________________
Modified: svn:mergeinfo
## -0,0 +0,1 ##
Merged /subversion/trunk:r1839662
Dag-Erling Smørgrav
2018-08-30 12:49:55 UTC
Permalink
A tree conflict occurs because config.h.in was deleted from head in
r294466: [...] However, config.h.in still exists on the
vendor-branch, and during the merge of r338344 we get an edit for this
Correct, same goes for all the .0 files (which are prerendered versions
of the man pages). I removed them from the target branch because they
are not needed and are sometimes overwritten during the porting process,
resulting in further text conflicts down the line.
And now starts our first bit of trouble. There is no youngest common
ancestor: [...] This is because FreeBSD's history did not actually
follow the vendor-branch pattern with openssh, at least not in the way
SVN would expect this pattern. This goes back all the way to CVS
days, so the commits below where generated by cvs2svn.
We didn't have a consistent procedure for vendor imports back then, and
CVS is pretty shit at branches. I vaguely recall that OpenSSH actually
had two separate vendor branches in CVS and required manual intervention
during the transition to Subversion.
Obviously, what we want to resolver to do here is to discard
the incoming edit and mark the tree conflict as resolved.
Yes. With --accept=postpone (which I had forgotten about), I just do:

% svn resolved $(svn stat | awk '$2 == "C" { print $3 }')
The conclusion I am drawing from this is that asking the resolver scan
for moves unconditionally was a mistake. It is better to only do so if
a youngest common ancestor is known since it will act as a bound for
our search through history. [...] I've committed my fix to trunk in
https://svn.apache.org/r1839662
Thank you very much. I really didn't expect a solution so soon!

DES
--
Dag-Erling Smørgrav - ***@des.no
Daniel Shahaf
2018-08-30 21:21:58 UTC
Permalink
Post by Dag-Erling Smørgrav
% svn resolved $(svn stat | awk '$2 == "C" { print $3 }')
'svn resolved -R ./' is equivalent and more robust.
Dag-Erling Smørgrav
2018-08-30 21:49:57 UTC
Permalink
Post by Daniel Shahaf
Post by Dag-Erling Smørgrav
% svn resolved $(svn stat | awk '$2 == "C" { print $3 }')
'svn resolved -R ./' is equivalent and more robust.
No. My command line only resolves tree conflicts. Yours also resolves
text conflicts.

DES
--
Dag-Erling Smørgrav - ***@des.no
Daniel Shahaf
2018-08-30 22:31:33 UTC
Permalink
Post by Dag-Erling Smørgrav
Post by Daniel Shahaf
Post by Dag-Erling Smørgrav
% svn resolved $(svn stat | awk '$2 == "C" { print $3 }')
'svn resolved -R ./' is equivalent and more robust.
No. My command line only resolves tree conflicts. Yours also resolves
text conflicts.
Fair enough. I don't think we have an easy syntax for resolving only
tree conflicts.

You probably know the following, but for the list's benefit I'll say it:
awk(1) is not a robust way to parse `svn status` output; the output is
columns-oriented, not whitespace-separated-fields oriented. `svn st |
cut` or `svn st --xml` are the recommended idioms.

Cheers,

Daniel
Dag-Erling Smørgrav
2018-08-31 09:06:07 UTC
Permalink
Post by Daniel Shahaf
awk(1) is not a robust way to parse `svn status` output; the output is
columns-oriented, not whitespace-separated-fields oriented. `svn st |
cut` or `svn st --xml` are the recommended idioms.
Now that you mention it, /^...C/ would have been much simpler.

DES
--
Dag-Erling Smørgrav - ***@des.no
Stefan Sperling
2018-08-31 09:44:34 UTC
Permalink
Post by Dag-Erling Smørgrav
Post by Daniel Shahaf
awk(1) is not a robust way to parse `svn status` output; the output is
columns-oriented, not whitespace-separated-fields oriented. `svn st |
cut` or `svn st --xml` are the recommended idioms.
Now that you mention it, /^...C/ would have been much simpler.
Note also the 'svnconflict' tool which allows you to drive the
conflict resolver from scripts.


usage: svnconflict <subcommand> [args]
Type 'svnconflict --version' to see the program version and RA modules,

svnconflict provides a non-interactive conflict resolution interface.
It is intended for use by non-interactive scripts which cannot make
use of interactive conflict resolution provided by 'svn resolve'.

svnconflict operates on a single working copy path only. It is assumed that
scripts are able to discover conflicted paths in the working copy via other
means, such as 'svn status'.
Some advanced operations offered by 'svn resolve' are not supported.

svnconflict may contact the repository to obtain information about a conflict.
It will never modify the repository, but only read information from it.
svnconflict will not prompt for credentials. If read-access to the repository
requires credentials but no suitable credentials are stored in Subversion's
authentication cache or provided on the command line, the operation may fail.

Available subcommands:
help (?, h)
list (ls)
options-text
options-prop
options-tree
resolve-text
resolve-prop
resolve-tree

Subversion is a tool for version control.
For additional information, see http://subversion.apache.org/

Chris
2018-08-30 06:15:58 UTC
Permalink
Hi,

this seems to be the same issue that I reported back here (https://mail-archives.apache.org/mod_mbox/subversion-users/201804.mbox/browser). The bug fix that Stefan Sperling did for 1.10.2 has helped in some of my merges by breaking off earlier, but it is still equally slow at checking each revision. The case reported in this thread seems to have the conflict far back so I guess that's why the mitigation doesn't work here.
Unfortunately, I can't provide any more data on what happens (company-internal server), but I might be able to test run something if you have some theories on what to test.

By the way, if I remember correctly, --quiet works to stop the resolver, but --non-interactive did not.

/Chris

--------------------------------------------
On Wed, 8/29/18, Stefan Sperling <***@elego.de> wrote:

Subject: Re: Tree conflict resolution considered harmful
To: "Dag-Erling Smørgrav" <***@des.no>
Cc: ***@subversion.apache.org
Date: Wednesday, August 29, 2018, 5:39 PM

On Wed, Aug 29, 2018 at 12:54:43PM +0200,
Post by Dag-Erling Smørgrav
I'm using
Subversion 1.10.2 to perform a non-interactive merge with
Post by Dag-Erling Smørgrav
around 15 tree conflicts (files that exist
on the source branch but have
Post by Dag-Erling Smørgrav
been
deleted from the target branch).  It spends exactly two
hours on
Post by Dag-Erling Smørgrav
each conflict before the
connection is killed and it gives up and moves
Post by Dag-Erling Smørgrav
on to the next.  Here's an excerpt
from ktrace showing svn's attempt to
Post by Dag-Erling Smørgrav
  33821 svn     
35.021454 GIO  fd 1 wrote 20 bytes
Post by Dag-Erling Smørgrav
 
      "\rChecking r338344..."
Post by Dag-Erling Smørgrav
--
  33821 svn   
  40.898214 GIO  fd 1 wrote 20 bytes
Post by Dag-Erling Smørgrav
        "\rChecking
r338059..."
Post by Dag-Erling Smørgrav
  33821 svn     
40.898328 GIO  fd 1 wrote 20 bytes
Post by Dag-Erling Smørgrav
 
      "\rChecking r333678..."
Post by Dag-Erling Smørgrav
  33821 svn      40.898412 GIO  fd 1
wrote 20 bytes
Post by Dag-Erling Smørgrav
       
"\rChecking r333677..."
Post by Dag-Erling Smørgrav
--
  33821 svn      40.900558 GIO  fd 1
wrote 20 bytes
Post by Dag-Erling Smørgrav
       
"\rChecking r333490..."
Post by Dag-Erling Smørgrav
--
  33821 svn      77.091446 GIO  fd 1
wrote 20 bytes
Post by Dag-Erling Smørgrav
       
"\rChecking r333389..."
Post by Dag-Erling Smørgrav
--
  33821 svn      95.000296 GIO  fd 1
wrote 20 bytes
Post by Dag-Erling Smørgrav
       
"\rChecking r333300..."
Post by Dag-Erling Smørgrav
--
  33821 svn      95.001008 GIO  fd 1
wrote 20 bytes
Post by Dag-Erling Smørgrav
       
"\rChecking r326169..."
Post by Dag-Erling Smørgrav
--
  33821 svn      671.067538 GIO  fd 1
wrote 20 bytes
Post by Dag-Erling Smørgrav
       
"\rChecking r322052..."
Post by Dag-Erling Smørgrav
--
  33821 svn      671.337258 GIO  fd 1
wrote 20 bytes
Post by Dag-Erling Smørgrav
       
"\rChecking r321369..."
Post by Dag-Erling Smørgrav
--
  33821 svn      7240.543297 GIO  fd
2 wrote 62 bytes
warning: W210002: Network connection closed unexpectedly
Post by Dag-Erling Smørgrav
The third column is
the time elapsed since the start of the process.
Post by Dag-Erling Smørgrav
The actual conflict
is in r294466, which removed the file in question
Post by Dag-Erling Smørgrav
from the target branch.  The revision
it's stuck on, r321369, only
touched the svn:mergeinfo property on the current directory
(propagated
Post by Dag-Erling Smørgrav
down from a merge higher up
in the tree).

This
does indeed look like a problem.

Unfortunately, you're not providing
sufficient information. Ktrace tells
us that
time was spent somewhere. But it does not tell us what this
time
was actually spent on.

Could you provide a shell
script I can use to actually run the problematic
merge myself in a working copy of the
appropriate subdirectory of the FreeBSD
repository? Ideally, such a script would assume
nothing beyond a new empty
directory and an
'svn' binary in $PATH, and then allow me to proceed
to the
actual problem you were seeing.
I'll run this script on OpenBSD, so a ksh
compatible script would be nice.

If you could take the time to
provide that, then I will take the time to look
into the problem. But your report so far
doesn't provide enough material for
me
to chew on.

Thanks!

-----Inline Attachment Follows-----
Dag-Erling Smørgrav
2018-08-30 07:44:46 UTC
Permalink
[...] The case reported in this thread seems to have the conflict far
back so I guess that's why the mitigation doesn't work here.
No, it's stuck on a single revision. Look at the timings from the
trace:

35.021454 r338344
40.898214 r338059
40.898328 r333678
40.898412 r333677
40.900558 r333490
77.091446 r333389
95.000296 r333300
95.001008 r326169
671.067538 r322052
671.337258 r321369
7240.543297 disconnected

DES
--
Dag-Erling Smørgrav - ***@des.no
Stefan Sperling
2018-08-30 12:06:28 UTC
Permalink
Post by Chris
By the way, if I remember correctly, --quiet works to stop the resolver, but --non-interactive did not.
This is correct. I misremembered how this was implemented.

The reason that the resolver runs in --non-interactive mode is that we
want it to resolve tree conflicts during automated merges.
In --non-interactive mode the default value for --accept is 'recommended'.
This allows users to run test merges on CI infrastructure and
have some tree conflicts resolved automatically. Conflicts for
which automated resolution is impossible will be postponed.

The reason --quiet suppresses the resolver is an unintended side-effect.
In --quiet mode no notification callback is passed to libsvn_client.
However, the 'svn' client relies on notifications to obtain a list of
conflicted paths after an update/merge/switch operation, and it won't
invoke the resolver if there are no conflicted paths.

The "official" way to prevent the resolver from running is --accept=postpone
Daniel Shahaf
2018-08-30 22:08:36 UTC
Permalink
Post by Stefan Sperling
In --non-interactive mode the default value for --accept is 'recommended'.
This is a backwards incompatible change to the semantics of `svn merge
--non-interactive` (with no other --option flags): A workflow designed
under 1.9 and trusting svn to obey PEP 20's "In the face of ambiguity,
refuse the temptation to guess" will find that 1.10 no longer obeys
that.

I don't know how I missed this before the release, and I'm not sure
what's best to do now, but I wanted to point this out anyway.

Cheers,

Daniel

P.S. If I'd noticed this ahead of the release, I'd probably have suggested
leaving accept=postpone the default and having the output of 'merge' say
"You may want to run 'svn resolve --accept=recommended -R ./' now" at
the end. (Come to think of it, accept=recommended could/should be the
default for 'svn resolve', couldn't it?)
Stefan Sperling
2018-08-31 09:41:16 UTC
Permalink
Post by Daniel Shahaf
Post by Stefan Sperling
In --non-interactive mode the default value for --accept is 'recommended'.
This is a backwards incompatible change to the semantics of `svn merge
--non-interactive` (with no other --option flags): A workflow designed
under 1.9 and trusting svn to obey PEP 20's "In the face of ambiguity,
refuse the temptation to guess" will find that 1.10 no longer obeys
that.
Which facts lead you to this conclusion?

The resolver never makes guesses about moves and renames.
It either detects an ancestral relationship between two nodes
in the repository, or it does not. It will only make guesses
where doing so helps with seeding a search for potential
ancestral relationships.

I'm not sure which ambiguity you are talking about exactly.
If you're referring to ambiguous moves (two ancestral copyfrom
connections exist after svn cp A B; svn mv A C; svn commit), then
don't worry. We're quite careful about applying 'recommended' options.
In the case under discussion the resolver would postpone a conflict
when there is ambiguity. The resolver will only recommend a "follow
move + merge" resolution option when only one move is detected.

(I'll note that popular distributed version control systems lack explicit
ancestry links between paths in their data models which forces them to
make guesses about node ancestry; in our case, users stay in control
as long as they use 'svn copy' and 'svn move').
Post by Daniel Shahaf
I don't know how I missed this before the release, and I'm not sure
what's best to do now, but I wanted to point this out anyway.
I had to adjust some old tree conflict tests to use --accept=postpone
because some merges would suddently produce text conflicts or even
clean merges where the test expected a tree conflict. These tests
are still useful because they verify our ability to detect such
conflicts. But in the real world, what people care about is that
'svn merge' does as much as it can to produce a useful result.

I think your argument about backwards compat falls short because
it ignores the fact that the mere introduction of tree conflicts
in 1.6 was a much more drastic change. Merges which appeared to
"just work" in 1.5 (but were in fact discarding changes) were suddently
flagging conflicts all over place. I would argue that this change in 1.10
is a long overdue step back in the user-friendly direction by making some
merge cases conflict-free which silently discarded changes in 1.5 and
haven't been conflict-free since 1.6.

Granted, we could have required everyone to add --accept=recommended to
their non-interactive merge command lines, but 99% of our users are simply
not going to do that. And then they would see no moves being followed at
all in non-interactive merges.

And most merges are interactive anyway. Many of our users run continuous
integration but only very few of them run non-interactive test merges.
Post by Daniel Shahaf
P.S. If I'd noticed this ahead of the release, I'd probably have suggested
leaving accept=postpone the default and having the output of 'merge' say
"You may want to run 'svn resolve --accept=recommended -R ./' now" at
the end.
In the --non-interactive case I wouldn't assume anyone is paying
enough attention to read such a message.
Loading...