[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
 

Reply via email to