[Second attempt to attach the patch.]
I (Julian Foad) wrote:
>> I'll come to 'svnsync' later, but basically my current thought is it
>> should 'correct' the mergeinfo by removing the r0 reference.
>
> I have written a test for this, and hacked up code to do this by textual
> substitution. It isn't quite right -- for example it would go wrong if a
> path contained the character sequence ":0", among other issues. I
> ought to rewrite it in the form of a proper mergeinfo parser but one that is
> more lenient than the regular parser.
Here's the patch in progress...
- Julian
Fix the 'svnsync' part of issue #4476 "Mergeinfo containing r0 makes
svnsync and svnadmin dump fail".
Make 'svnsync' adjust mergeinfo containing r0 to remove the r0 before trying
to commit it to the target repository.
### TODO: Give a warning or notification?
### TODO:
Revert svnsync_tests.py r1230676 "re-dump", at it is no longer necessary
since r1292507 "Use a simple dump file parser to compare dumps ...", and
as re-loading-dumping hides errors introduced by 'load'.
### TODO: Implement fix_mergeinfo() as a more 'proper' parser. The current
hack has at least two flaws: goes wrong if a path contains ':0', and
doesn't elide the whole source path reference if '0' was the only
revision mentioned.
* subversion/svnsync/sync.c
(fix_mergeinfo): New function -- a bit of a hack at the moment.
(change_file_prop,
change_dir_prop): Use it.
* subversion/tests/cmdline/svnsync_tests_data/mergeinfo-contains-r0.dump
subversion/tests/cmdline/svnsync_tests_data/mergeinfo-contains-r0.expected.dump
New files.
* subversion/tests/cmdline/svnsync_tests.py
(setup_and_sync,
verify_mirror): Tweak to avoid passing the expected results through
'svnadmin load' before comparison, because that can alter mergeinfo.
(mergeinfo_contains_r0): New test.
(test_list): Run it.
--This line, and those below, will be ignored--
Index: subversion/svnsync/sync.c
===================================================================
--- subversion/svnsync/sync.c (revision 1643101)
+++ subversion/svnsync/sync.c (working copy)
@@ -30,12 +30,13 @@
#include "svn_auth.h"
#include "svn_opt.h"
#include "svn_ra.h"
#include "svn_utf.h"
#include "svn_subst.h"
#include "svn_string.h"
+#include "svn_ctype.h"
#include "sync.h"
#include "svn_private_config.h"
#include <apr_network_io.h>
@@ -372,12 +373,58 @@ absent_directory(const char *path,
{
node_baton_t *db = dir_baton;
edit_baton_t *eb = db->edit_baton;
return eb->wrapped_editor->absent_directory(path, db->wrapped_node_baton,
pool);
}
+/* Adjust the mergeinfo VALUE to remove any references to r0. */
+static svn_error_t *
+fix_mergeinfo(const svn_string_t **value_p,
+ const svn_string_t *old_value,
+ apr_pool_t *result_pool)
+{
+ svn_string_t *value = svn_string_dup(old_value, result_pool);
+ char *colon_pos;
+
+ /* remove r0 references by textual substitution */
+ /* ### Goes wrong if a path contains ':0'; should instead search for
+ the *last* colon on each line. */
+ while ((colon_pos = strstr(value->data, ":0")))
+ {
+ char *r0_pos = colon_pos + 1;
+
+ SVN_DBG(("fix mergeinfo r0: was ...%.30s", colon_pos));
+
+ if (r0_pos[1] == '*' && r0_pos[2] == ',')
+ {
+ value->len -= 3;
+ memmove(r0_pos, r0_pos + 3, value->len + 1);
+ }
+ else if (r0_pos[1] == ',' || r0_pos[1] == '*' /* at end of line */
+ || (r0_pos[1] == '-' && r0_pos[2] == '1' && ! svn_ctype_isdigit(r0_pos[3])))
+ {
+ value->len -= 2;
+ memmove(r0_pos, r0_pos + 2, value->len + 1);
+ }
+ else if (r0_pos[1] == '\n' || r0_pos[1] == '\r' || r0_pos[1] == '\0')
+ {
+ value->len -= 1;
+ memmove(r0_pos, r0_pos + 1, value->len + 1);
+ }
+ else if (r0_pos[1] == '-')
+ {
+ /* '0-5' should become '1-5'.
+ '0-1' should become '1' but '1-1' is probably good enough. */
+ r0_pos[0] = '1';
+ }
+ SVN_DBG(("fix mergeinfo r0: now ...%.30s", colon_pos));
+ }
+
+ *value_p = value;
+ return SVN_NO_ERROR;
+}
static svn_error_t *
change_file_prop(void *file_baton,
const char *name,
const svn_string_t *value,
apr_pool_t *pool)
@@ -417,12 +464,18 @@ change_file_prop(void *file_baton,
SVN_ERR(normalize_string(&value, &was_normalized,
eb->source_prop_encoding, pool, pool));
if (was_normalized)
(*(eb->normalized_node_props_counter))++;
}
+ /* */
+ if (strcmp(name, SVN_PROP_MERGEINFO) == 0)
+ {
+ SVN_ERR(fix_mergeinfo(&value, value, pool));
+ }
+
return eb->wrapped_editor->change_file_prop(fb->wrapped_node_baton,
name, value, pool);
}
static svn_error_t *
change_dir_prop(void *dir_baton,
@@ -516,12 +569,18 @@ change_dir_prop(void *dir_baton,
SVN_ERR(normalize_string(&value, &was_normalized, eb->source_prop_encoding,
pool, pool));
if (was_normalized)
(*(eb->normalized_node_props_counter))++;
}
+ /* */
+ if (strcmp(name, SVN_PROP_MERGEINFO) == 0)
+ {
+ SVN_ERR(fix_mergeinfo(&value, value, pool));
+ }
+
return eb->wrapped_editor->change_dir_prop(db->wrapped_node_baton,
name, value, pool);
}
static svn_error_t *
close_edit(void *edit_baton,
Index: subversion/tests/cmdline/svnsync_tests_data/mergeinfo-contains-r0.dump
===================================================================
--- subversion/tests/cmdline/svnsync_tests_data/mergeinfo-contains-r0.dump (revision 0)
+++ subversion/tests/cmdline/svnsync_tests_data/mergeinfo-contains-r0.dump (working copy)
@@ -0,0 +1,85 @@
+SVN-fs-dump-format-version: 2
+
+UUID: 6ad9f820-0205-0410-94a2-c8cf366bb2b3
+
+Revision-number: 0
+Prop-content-length: 56
+Content-length: 56
+
+K 8
+svn:date
+V 27
+2005-11-07T23:36:48.095832Z
+PROPS-END
+
+Revision-number: 1
+Prop-content-length: 101
+Content-length: 101
+
+K 10
+svn:author
+V 7
+jrandom
+K 8
+svn:date
+V 27
+2000-01-01T00:00:00.000000Z
+K 7
+svn:log
+V 0
+
+PROPS-END
+
+Node-path:
+Node-kind: dir
+Node-action: change
+Prop-content-length: 22
+Content-length: 22
+
+K 1
+p
+V 1
+v
+PROPS-END
+
+
+Revision-number: 2
+Prop-content-length: 113
+Content-length: 113
+
+K 10
+svn:author
+V 7
+jrandom
+K 8
+svn:date
+V 27
+2005-11-07T23:37:17.705159Z
+K 7
+svn:log
+V 11
+add foo.txt
+PROPS-END
+
+Node-path: foo.txt
+Node-kind: file
+Node-action: add
+Prop-content-length: 86
+Text-content-length: 0
+Text-content-md5: d41d8cd98f00b204e9800998ecf8427e
+Text-content-sha1: da39a3ee5e6b4b0d3255bfef95601890afd80709
+Content-length: 86
+
+K 13
+svn:mergeinfo
+V 51
+/a:0,1
+/b:0*,1
+/c:0,2
+/d:0-1
+/e:0-1,3
+/f:0-2
+/g:0-3
+PROPS-END
+
+
Index: subversion/tests/cmdline/svnsync_tests_data/mergeinfo-contains-r0.expected.dump
===================================================================
--- subversion/tests/cmdline/svnsync_tests_data/mergeinfo-contains-r0.expected.dump (revision 0)
+++ subversion/tests/cmdline/svnsync_tests_data/mergeinfo-contains-r0.expected.dump (working copy)
@@ -0,0 +1,85 @@
+SVN-fs-dump-format-version: 2
+
+UUID: 6ad9f820-0205-0410-94a2-c8cf366bb2b3
+
+Revision-number: 0
+Prop-content-length: 56
+Content-length: 56
+
+K 8
+svn:date
+V 27
+2005-11-07T23:36:48.095832Z
+PROPS-END
+
+Revision-number: 1
+Prop-content-length: 101
+Content-length: 101
+
+K 10
+svn:author
+V 7
+jrandom
+K 8
+svn:date
+V 27
+2000-01-01T00:00:00.000000Z
+K 7
+svn:log
+V 0
+
+PROPS-END
+
+Node-path:
+Node-kind: dir
+Node-action: change
+Prop-content-length: 22
+Content-length: 22
+
+K 1
+p
+V 1
+v
+PROPS-END
+
+
+Revision-number: 2
+Prop-content-length: 113
+Content-length: 113
+
+K 10
+svn:author
+V 7
+jrandom
+K 8
+svn:date
+V 27
+2005-11-07T23:37:17.705159Z
+K 7
+svn:log
+V 11
+add foo.txt
+PROPS-END
+
+Node-path: foo.txt
+Node-kind: file
+Node-action: add
+Prop-content-length: 75
+Text-content-length: 0
+Text-content-md5: d41d8cd98f00b204e9800998ecf8427e
+Text-content-sha1: da39a3ee5e6b4b0d3255bfef95601890afd80709
+Content-length: 75
+
+K 13
+svn:mergeinfo
+V 40
+/a:1
+/b:1
+/c:2
+/d:1
+/e:1,3
+/f:1-2
+/g:1-3
+PROPS-END
+
+
* subversion/tests/cmdline/svnsync_tests_data/mergeinfo-contains-r0.dump
* subversion/tests/cmdline/svnsync_tests_data/mergeinfo-contains-r0.expected.dump
* subversion/tests/cmdline/svnsync_tests.py
(setup_and_sync):
(verify_mirror):
(fd_leak_sync_from_serf_to_local):
--This line, and those below, will be ignored--
Index: subversion/tests/cmdline/svnsync_tests_data/mergeinfo-contains-r0.dump
===================================================================
--- subversion/tests/cmdline/svnsync_tests_data/mergeinfo-contains-r0.dump (revision 0)
+++ subversion/tests/cmdline/svnsync_tests_data/mergeinfo-contains-r0.dump (working copy)
@@ -0,0 +1,85 @@
+SVN-fs-dump-format-version: 2
+
+UUID: 6ad9f820-0205-0410-94a2-c8cf366bb2b3
+
+Revision-number: 0
+Prop-content-length: 56
+Content-length: 56
+
+K 8
+svn:date
+V 27
+2005-11-07T23:36:48.095832Z
+PROPS-END
+
+Revision-number: 1
+Prop-content-length: 101
+Content-length: 101
+
+K 10
+svn:author
+V 7
+jrandom
+K 8
+svn:date
+V 27
+2000-01-01T00:00:00.000000Z
+K 7
+svn:log
+V 0
+
+PROPS-END
+
+Node-path:
+Node-kind: dir
+Node-action: change
+Prop-content-length: 22
+Content-length: 22
+
+K 1
+p
+V 1
+v
+PROPS-END
+
+
+Revision-number: 2
+Prop-content-length: 113
+Content-length: 113
+
+K 10
+svn:author
+V 7
+jrandom
+K 8
+svn:date
+V 27
+2005-11-07T23:37:17.705159Z
+K 7
+svn:log
+V 11
+add foo.txt
+PROPS-END
+
+Node-path: foo.txt
+Node-kind: file
+Node-action: add
+Prop-content-length: 86
+Text-content-length: 0
+Text-content-md5: d41d8cd98f00b204e9800998ecf8427e
+Text-content-sha1: da39a3ee5e6b4b0d3255bfef95601890afd80709
+Content-length: 86
+
+K 13
+svn:mergeinfo
+V 51
+/a:0,1
+/b:0*,1
+/c:0,2
+/d:0-1
+/e:0-1,3
+/f:0-2
+/g:0-3
+PROPS-END
+
+
Index: subversion/tests/cmdline/svnsync_tests_data/mergeinfo-contains-r0.expected.dump
===================================================================
--- subversion/tests/cmdline/svnsync_tests_data/mergeinfo-contains-r0.expected.dump (revision 0)
+++ subversion/tests/cmdline/svnsync_tests_data/mergeinfo-contains-r0.expected.dump (working copy)
@@ -0,0 +1,85 @@
+SVN-fs-dump-format-version: 2
+
+UUID: 6ad9f820-0205-0410-94a2-c8cf366bb2b3
+
+Revision-number: 0
+Prop-content-length: 56
+Content-length: 56
+
+K 8
+svn:date
+V 27
+2005-11-07T23:36:48.095832Z
+PROPS-END
+
+Revision-number: 1
+Prop-content-length: 101
+Content-length: 101
+
+K 10
+svn:author
+V 7
+jrandom
+K 8
+svn:date
+V 27
+2000-01-01T00:00:00.000000Z
+K 7
+svn:log
+V 0
+
+PROPS-END
+
+Node-path:
+Node-kind: dir
+Node-action: change
+Prop-content-length: 22
+Content-length: 22
+
+K 1
+p
+V 1
+v
+PROPS-END
+
+
+Revision-number: 2
+Prop-content-length: 113
+Content-length: 113
+
+K 10
+svn:author
+V 7
+jrandom
+K 8
+svn:date
+V 27
+2005-11-07T23:37:17.705159Z
+K 7
+svn:log
+V 11
+add foo.txt
+PROPS-END
+
+Node-path: foo.txt
+Node-kind: file
+Node-action: add
+Prop-content-length: 75
+Text-content-length: 0
+Text-content-md5: d41d8cd98f00b204e9800998ecf8427e
+Text-content-sha1: da39a3ee5e6b4b0d3255bfef95601890afd80709
+Content-length: 75
+
+K 13
+svn:mergeinfo
+V 40
+/a:1
+/b:1
+/c:2
+/d:1
+/e:1,3
+/f:1-2
+/g:1-3
+PROPS-END
+
+
Index: subversion/tests/cmdline/svnsync_tests.py
===================================================================
--- subversion/tests/cmdline/svnsync_tests.py (revision 1643074)
+++ subversion/tests/cmdline/svnsync_tests.py (working copy)
@@ -206,26 +206,33 @@ def setup_and_sync(sbox, dump_file_conte
source_prop_encoding=source_prop_encoding)
run_copy_revprops(dest_repo_url, repo_url,
source_prop_encoding=source_prop_encoding)
return dest_sbox
-def verify_mirror(dest_sbox, src_sbox):
- """Compare the contents of the DEST_SBOX repository with EXP_DUMP_FILE_CONTENTS."""
+def verify_mirror(dest_sbox, src_sbox=None, src_dump=None):
+ """Compare the contents of the mirror repository in DEST_SBOX with the
+ reference repository in SRC_SBOX or SRC_DUMP,
+ by comparing the parsed dump stream content.
+ First remove svnsync rev-props from the DEST_SBOX repository.
+ """
# Remove some SVNSync-specific housekeeping properties from the
# mirror repository in preparation for the comparison dump.
for prop_name in ("svn:sync-from-url", "svn:sync-from-uuid",
"svn:sync-last-merged-rev"):
svntest.actions.run_and_verify_svn(
None, None, [], "propdel", "--revprop", "-r", "0",
prop_name, dest_sbox.repo_url)
# Create a dump file from the mirror repository.
dest_dump = svntest.actions.run_and_verify_dump(dest_sbox.repo_dir)
- src_dump = svntest.actions.run_and_verify_dump(src_sbox.repo_dir)
+ # Create a dump file from the reference repository if needed.
+ if src_sbox:
+ assert src_dump is None
+ src_dump = svntest.actions.run_and_verify_dump(src_sbox.repo_dir)
svntest.verify.compare_dump_files(
"Dump files", "DUMP", src_dump, dest_dump)
def run_test(sbox, dump_file_name, subdir=None, exp_dump_file_name=None,
bypass_prop_validation=False, source_prop_encoding=None,
@@ -248,22 +255,17 @@ or another dump file."""
is_src_ra_local, is_dest_ra_local)
# Compare the dump produced by the mirror repository with either the original
# dump file (used to create the master repository) or another specified dump
# file.
if exp_dump_file_name:
- build_repos(sbox)
- svntest.actions.run_and_verify_load(sbox.repo_dir,
- open(os.path.join(svnsync_tests_dir,
- exp_dump_file_name),
- 'rb').readlines())
- src_sbox = sbox
+ src_dump_path = os.path.join(svnsync_tests_dir, exp_dump_file_name)
+ src_dump = open(src_dump_path, 'rb').readlines()
+ verify_mirror(dest_sbox, src_dump=src_dump)
else:
- src_sbox = sbox
-
- verify_mirror(dest_sbox, sbox)
+ verify_mirror(dest_sbox, sbox)
######################################################################
# Tests
@@ -573,12 +575,20 @@ def delete_revprops(sbox):
def fd_leak_sync_from_serf_to_local(sbox):
"fd leak during sync from serf to local"
import resource
resource.setrlimit(resource.RLIMIT_NOFILE, (128, 128))
run_test(sbox, "largemods.dump", is_src_ra_local=None, is_dest_ra_local=True)
+#----------------------------------------------------------------------
+
+def mergeinfo_contains_r0(sbox):
+ "mergeinfo contains r0"
+ run_test(sbox, "mergeinfo-contains-r0.dump",
+ exp_dump_file_name="mergeinfo-contains-r0.expected.dump",
+ bypass_prop_validation=True)
+
########################################################################
# Run the tests
# list all tests here, starting with None:
@@ -609,12 +619,13 @@ test_list = [ None,
copy_bad_encoding,
delete_svn_props,
commit_a_copy_of_root,
descend_into_replace,
delete_revprops,
fd_leak_sync_from_serf_to_local, # calls setrlimit
+ mergeinfo_contains_r0,
]
if __name__ == '__main__':
svntest.main.run_tests(test_list)
# NOTREACHED