subversion-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From hwri...@apache.org
Subject svn commit: r1231318 [30/35] - in /subversion/branches/revprop-packing: ./ build/ build/ac-macros/ build/generator/ build/generator/templates/ build/win32/ contrib/client-side/emacs/ contrib/server-side/mod_dontdothat/ notes/ notes/http-and-webdav/ not...
Date Fri, 13 Jan 2012 21:40:38 GMT
Modified: subversion/branches/revprop-packing/subversion/tests/cmdline/log_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/revprop-packing/subversion/tests/cmdline/log_tests.py?rev=1231318&r1=1231317&r2=1231318&view=diff
==============================================================================
--- subversion/branches/revprop-packing/subversion/tests/cmdline/log_tests.py (original)
+++ subversion/branches/revprop-packing/subversion/tests/cmdline/log_tests.py Fri Jan 13 21:40:26 2012
@@ -34,6 +34,7 @@ from svntest import wc
 from svntest.main import server_has_mergeinfo
 from svntest.main import SVN_PROP_MERGEINFO
 from merge_tests import set_up_branch
+from diff_tests import make_diff_header, make_no_diff_deleted_header
 
 # (abbreviation)
 Skip = svntest.testcase.Skip_deco
@@ -178,7 +179,8 @@ def guarantee_repos_and_wc(sbox):
   to test the code"""
   svntest.main.file_write(msg_file, msg)
   svntest.main.file_append(iota_path, "8")
-  svntest.main.file_append(rho_path, "8")
+  svntest.main.file_append(rho_path, "88") # More than one char so libmagic
+                                           # treats it as text.
   svntest.main.run_svn(None, 'add', rho_path)
   svntest.main.run_svn(None,
                        'ci', '-F', msg_file)
@@ -424,7 +426,7 @@ class SVNLogParseError(Exception):
   pass
 
 
-def parse_log_output(log_lines):
+def parse_log_output(log_lines, with_diffs=False):
   """Return a log chain derived from LOG_LINES.
   A log chain is a list of hashes; each hash represents one log
   message, in the order it appears in LOG_LINES (the first log
@@ -438,6 +440,7 @@ def parse_log_output(log_lines):
      'date'     ===>  string
      'msg'      ===>  string  (the log message itself)
      'lines'    ===>  number  (so that it may be checked against rev)
+
   If LOG_LINES contains changed-path information, then the hash
   also contains
 
@@ -451,7 +454,11 @@ def parse_log_output(log_lines):
 
      'reverse_merges'   ===> list of reverse-merging revisions that resulted
   in this log being part of the list of messages.
-     """
+
+  If LOG_LINES contains diffs and WITH_DIFFS=True, then the hash also contains
+
+     'diff_lines'  ===> list of strings  (diffs)
+  """
 
   # Here's some log output to look at while writing this function:
 
@@ -566,6 +573,20 @@ def parse_log_output(log_lines):
       for line in log_lines[0:lines]:
         msg += line
       del log_lines[0:lines]
+
+      # Maybe accumulate a diff.
+      # If there is a diff, there is a blank line before and after it.
+      if with_diffs and len(log_lines) >= 2 and log_lines[0] == '\n':
+        log_lines.pop(0)
+        diff_lines = []
+        while len(log_lines) and log_lines[0] != msg_separator:
+          diff_lines.append(log_lines.pop(0))
+        if diff_lines[-1] == '\n':
+          diff_lines.pop()
+        else:
+          raise SVNLogParseError("no blank line after diff in log")
+        this_item['diff_lines'] = diff_lines
+
     elif this_line == msg_separator:
       if this_item:
         this_item['msg'] = msg
@@ -676,6 +697,42 @@ def check_log_chain(chain, revlist, path
                             missing_revs, chain)
 
 
+def parse_diff(output):
+  """Return a set containing the various diff bits, broken up by file."""
+
+  diff_set = []
+  current_diff = []
+  for line in output:
+    if line.startswith('Index: ') and current_diff:
+      diff_set.append(current_diff)
+      current_diff = []
+    current_diff.append(line)
+  diff_set.append(current_diff)
+
+  return diff_set
+
+
+def setify(diff_list):
+  """Take a list of lists and make it a set of tuples."""
+  s = set()
+  for diff in diff_list:
+    s.add(tuple(diff))
+  return s
+
+
+def compare_diff_output(expected_diffs, output):
+  """Compare the diffs in EXPECTED_DIFFS (which is a Python set) with the
+  text in OUTPUT, remembering that there is no canonical ordering for diffs."""
+
+  diffs = parse_diff(output)
+  diffs = setify(diffs)
+  expected_diffs = setify(expected_diffs)
+
+  if diffs.issubset(expected_diffs) and diffs.issuperset(expected_diffs):
+    return
+
+  raise svntest.Failure("Diffs not equal")
+
 
 ######################################################################
 # Tests
@@ -2028,6 +2085,124 @@ def log_on_nonexistent_path_and_valid_re
   svntest.actions.run_and_verify_svn(None, None, expected_error,
                                      'log', '-q', bad_path_default_rev)
 
+#----------------------------------------------------------------------
+# Test for issue #4022 'svn log -g interprets change in inherited mergeinfo
+# due to move as a merge'.
+@Issue(4022)
+def merge_sensitive_log_copied_path_inherited_mergeinfo(sbox):
+  "log -g on copied path with inherited mergeinfo"
+
+  sbox.build()
+  wc_dir = sbox.wc_dir
+  wc_disk, wc_status = set_up_branch(sbox, branch_only=True)
+
+  A_path          = os.path.join(wc_dir, 'A')
+  gamma_COPY_path = os.path.join(wc_dir, 'A_COPY', 'D', 'gamma')
+  old_gamma_path  = os.path.join(wc_dir, 'A', 'D', 'gamma')
+  new_gamma_path  = os.path.join(wc_dir, 'A', 'C', 'gamma')
+
+  # r3 - Modify a file (A_COPY/D/gamma) on the branch
+  svntest.main.file_write(gamma_COPY_path, "Branch edit.\n")
+  svntest.main.run_svn(None, 'ci', '-m', 'Branch edit', wc_dir)
+
+  # r4 - Reintegrate A_COPY to A
+  svntest.main.run_svn(None, 'up', wc_dir)
+  svntest.main.run_svn(None, 'merge', '--reintegrate',
+                       sbox.repo_url + '/A_COPY', A_path)
+  svntest.main.run_svn(None, 'ci', '-m', 'Reintegrate A_COPY to A', wc_dir)
+
+  # r5 - Move file modified by reintegrate (A/D/gamma to A/C/gamma).
+  svntest.main.run_svn(None, 'move', old_gamma_path, new_gamma_path)
+  svntest.main.run_svn(None, 'ci', '-m', 'Move file', wc_dir)
+
+  # 'svn log -g --stop-on-copy ^/A/C/gamma' hould return *only* r5
+  # Previously this test failed because the change in gamma's inherited
+  # mergeinfo between r4 and r5, due to the move, was understood as a merge:
+  #
+  #   >svn log -v -g --stop-on-copy ^^/A/C/gamma
+  #   ------------------------------------------------------------------------
+  #   r5 | jrandom | 2011-10-11 14:37:57 -0700 (Tue, 11 Oct 2011) | 1 line  #
+  #   Changed paths:
+  #      A /A/C/gamma (from /A/D/gamma:4)
+  #      D /A/D/gamma
+  #
+  #   Move file
+  #   ------------------------------------------------------------------------
+  #   r3 | jrandom | 2011-10-11 14:37:56 -0700 (Tue, 11 Oct 2011) | 1 line
+  #   Changed paths:
+  #      M /A_COPY/D/gamma
+  #   Reverse merged via: r5
+  #
+  #   Branch edit
+  #   ------------------------------------------------------------------------
+  #   r2 | jrandom | 2011-10-11 14:37:56 -0700 (Tue, 11 Oct 2011) | 1 line
+  #   Changed paths:
+  #      A /A_COPY (from /A:1)
+  #   Reverse merged via: r5
+  #
+  #   log msg
+  #   ------------------------------------------------------------------------
+  expected_merges = {5  : []}
+  svntest.main.run_svn(None, 'up', wc_dir)
+  exit_code, out, err = svntest.actions.run_and_verify_svn(
+    None, None, [], 'log', '-g', '--stop-on-copy',
+    sbox.repo_url + '/A/C/gamma')
+  log_chain = parse_log_output(out)
+  check_merge_results(log_chain, expected_merges)
+
+#----------------------------------------------------------------------
+def log_diff(sbox):
+  "'svn log --diff'"
+
+  guarantee_repos_and_wc(sbox)
+
+  was_cwd = os.getcwd()
+  os.chdir(sbox.wc_dir)
+
+  exit_code, output, err = svntest.actions.run_and_verify_svn(None, None, [],
+                                                              'log', '--diff')
+  os.chdir(was_cwd)
+
+  for line in output:
+    if line.startswith('Index:'):
+      break
+  else:
+    raise SVNLogParseError("no diffs found in log output")
+
+  # After a copy, a log of the copy destination used to fail because the
+  # diff tried to use the head-revision URL with the old revision numbers
+  # without using the correct peg revision.
+
+  sbox.simple_copy('A', 'A2')
+  sbox.simple_commit()
+
+  os.chdir(sbox.wc_dir)
+  exit_code, output, err = svntest.actions.run_and_verify_svn(None, None, [],
+                                                              'log', '--diff',
+                                                              '-r10:8', 'A2')
+  os.chdir(was_cwd)
+
+  r9diff = [ make_no_diff_deleted_header('A2/B/E/alpha', 8, 9),
+              make_diff_header('A2/B/E/beta', 'revision 8', 'revision 9')
+               + [ "@@ -1 +1,2 @@\n",
+                   " This is the file 'beta'.\n",
+                   "+9\n",
+                   "\ No newline at end of file\n",
+                 ]
+           ]
+  r8diff = [ make_diff_header('A2/D/G/rho', 'revision 0', 'revision 8')
+              + [ "@@ -0,0 +1 @@\n",
+                  "+88\n",
+                  "\ No newline at end of file\n",
+                ]
+           ]
+  log_chain = parse_log_output(output, with_diffs=True)
+  if len(log_chain) != 3:
+    raise SVNLogParseError("%d logs found, 3 expected" % len(log_chain))
+  compare_diff_output(r9diff, log_chain[1]['diff_lines'])
+  compare_diff_output(r8diff, log_chain[2]['diff_lines'])
+
+
 ########################################################################
 # Run the tests
 
@@ -2067,6 +2242,8 @@ test_list = [ None,
               merge_sensitive_log_ignores_cyclic_merges,
               log_with_unrelated_peg_and_operative_revs,
               log_on_nonexistent_path_and_valid_rev,
+              merge_sensitive_log_copied_path_inherited_mergeinfo,
+              log_diff,
              ]
 
 if __name__ == '__main__':

Modified: subversion/branches/revprop-packing/subversion/tests/cmdline/merge_authz_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/revprop-packing/subversion/tests/cmdline/merge_authz_tests.py?rev=1231318&r1=1231317&r2=1231318&view=diff
==============================================================================
--- subversion/branches/revprop-packing/subversion/tests/cmdline/merge_authz_tests.py (original)
+++ subversion/branches/revprop-packing/subversion/tests/cmdline/merge_authz_tests.py Fri Jan 13 21:40:26 2012
@@ -73,7 +73,10 @@ from svntest.actions import inject_confl
 #         This is *not* a full test of issue #2829, see also merge_tests.py,
 #         search for "2829".  This tests the problem where a merge adds a path
 #         with a missing sibling and so needs its own explicit mergeinfo.
-@Issues(2893,2997,2829)
+#
+# #4056 - Don't record non-inheritable mergeinfo if missing subtrees are not
+#         touched by the full-depth diff
+@Issues(2893,2997,2829,4056)
 @SkipUnless(svntest.main.server_has_mergeinfo)
 @Skip(svntest.main.is_ra_type_file)
 def mergeinfo_and_skipped_paths(sbox):
@@ -393,10 +396,54 @@ def mergeinfo_and_skipped_paths(sbox):
 
   # Merge -r7:9 to the restricted WC's A_COPY_2/D/H.
   #
+  # r9 adds a path, 'A_COPY_2/D/H/zeta', which has a missing sibling 'psi',
+  # but since 'psi' is untouched by the merge it isn't skipped, and since it
+  # isn't skipped, its parent 'A_COPY_2/D/H' won't get non-inheritable
+  # mergeinfo set on it to describe the merge, so none of the parent's
+  # children will get explicit mergeinfo -- see issue #4056.
+  expected_output = wc.State(A_COPY_2_H_path, {
+    'omega' : Item(status='U '),
+    'zeta'  : Item(status='A '),
+    })
+  expected_mergeinfo_output = wc.State(A_COPY_2_H_path, {
+    ''      : Item(status=' U'),
+    'omega' : Item(status=' U'),
+    })
+  expected_elision_output = wc.State(A_COPY_2_H_path, {
+    'omega' : Item(status=' U'),
+    })
+  expected_status = wc.State(A_COPY_2_H_path, {
+    ''      : Item(status=' M', wc_rev=8),
+    'chi'   : Item(status='  ', wc_rev=8),
+    'omega' : Item(status='M ', wc_rev=8),
+    'zeta'  : Item(status='A ', copied='+', wc_rev='-'),
+    })
+  expected_disk = wc.State('', {
+    ''      : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:8-9'}),
+    'omega' : Item("New content"),
+    'chi'   : Item("This is the file 'chi'.\n"),
+    'zeta'  : Item("This is the file 'zeta'.\n"),
+    })
+  expected_skip = wc.State(A_COPY_2_H_path, {})
+  svntest.actions.run_and_verify_merge(A_COPY_2_H_path, '7', '9',
+                                       sbox.repo_url + '/A/D/H', None,
+                                       expected_output,
+                                       expected_mergeinfo_output,
+                                       expected_elision_output,
+                                       expected_disk,
+                                       expected_status,
+                                       expected_skip,
+                                       None, None, None, None,
+                                       None, 1, 0)
+
+  # Merge -r4:9 to the restricted WC's A_COPY_2/D/H.
+  #
   # r9 adds a path, 'A_COPY_2/D/H/zeta', which has a parent with
-  # non-inheritable mergeinfo (due to the fact 'A_COPY_2/D/H/psi' is missing).
-  # 'A_COPY_2/D/H/zeta' must therefore get its own explicit mergeinfo from
-  # this merge.
+  # non-inheritable mergeinfo (due to the fact 'A_COPY_2/D/H/psi' is missing
+  # and skipped). 'A_COPY_2/D/H/zeta' must therefore get its own explicit
+  # mergeinfo from this merge.
+  svntest.actions.run_and_verify_svn(None, None, [], 'revert', '--recursive',
+                                     wc_restricted)  
   expected_output = wc.State(A_COPY_2_H_path, {
     'omega' : Item(status='U '),
     'zeta'  : Item(status='A '),
@@ -415,15 +462,17 @@ def mergeinfo_and_skipped_paths(sbox):
     'zeta'  : Item(status='A ', copied='+', wc_rev='-'),
     })
   expected_disk = wc.State('', {
-    ''      : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:8-9*'}),
+    ''      : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:5-9*'}),
     'omega' : Item("New content",
-                   props={SVN_PROP_MERGEINFO : '/A/D/H/omega:8-9'}),
+                   props={SVN_PROP_MERGEINFO : '/A/D/H/omega:5-9'}),
     'chi'   : Item("This is the file 'chi'.\n"),
     'zeta'  : Item("This is the file 'zeta'.\n",
                    props={SVN_PROP_MERGEINFO : '/A/D/H/zeta:9'}),
     })
-  expected_skip = wc.State(A_COPY_2_H_path, {})
-  svntest.actions.run_and_verify_merge(A_COPY_2_H_path, '7', '9',
+  expected_skip = wc.State(A_COPY_2_H_path, {
+    'psi' : Item(),
+    })
+  svntest.actions.run_and_verify_merge(A_COPY_2_H_path, '4', '9',
                                        sbox.repo_url + '/A/D/H', None,
                                        expected_output,
                                        expected_mergeinfo_output,

Modified: subversion/branches/revprop-packing/subversion/tests/cmdline/merge_reintegrate_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/revprop-packing/subversion/tests/cmdline/merge_reintegrate_tests.py?rev=1231318&r1=1231317&r2=1231318&view=diff
==============================================================================
--- subversion/branches/revprop-packing/subversion/tests/cmdline/merge_reintegrate_tests.py (original)
+++ subversion/branches/revprop-packing/subversion/tests/cmdline/merge_reintegrate_tests.py Fri Jan 13 21:40:26 2012
@@ -2435,7 +2435,7 @@ def reintegrate_replaced_source(sbox):
   # Using cherrypick merges, simulate a series of sync merges from A to
   # A_COPY with a replace of A_COPY along the way.
   #
-  # r6 - Merge r3 from A to A_COPY
+  # r7 - Merge r3 from A to A_COPY
   svntest.main.run_svn(None, 'up', wc_dir)
   svntest.main.run_svn(None, 'merge', sbox.repo_url + '/A', A_COPY_path,
                        '-c3')

Modified: subversion/branches/revprop-packing/subversion/tests/cmdline/merge_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/revprop-packing/subversion/tests/cmdline/merge_tests.py?rev=1231318&r1=1231317&r2=1231318&view=diff
==============================================================================
--- subversion/branches/revprop-packing/subversion/tests/cmdline/merge_tests.py (original)
+++ subversion/branches/revprop-packing/subversion/tests/cmdline/merge_tests.py Fri Jan 13 21:40:26 2012
@@ -49,15 +49,31 @@ from svntest.actions import make_conflic
 from svntest.actions import inject_conflict_into_expected_state
 
 def expected_merge_output(rev_ranges, additional_lines=None, foreign=False,
-                          elides=False, two_url=False):
+                          elides=False, two_url=False, target=None,
+                          text_conflicts=0, prop_conflicts=0, tree_conflicts=0):
   """Generate an (inefficient) regex representing the expected merge
-  output and mergeinfo notifications from REV_RANGES (a list of 'range' lists
-  of the form [start, end] or [single_rev] --> [single_rev - 1, single_rev]),
-  and ADDITIONAL_LINES (a list of strings).  If REV_RANGES is None then only
-  the standard notification for a 3-way merge is expected.  If ELIDES is true
-  add to the regex an expression representing elision notification.  If TWO_URL
-  us true tweak the regex to expect the appropriate mergeinfo notification
-  for a 3-way merge."""
+  output and mergeinfo notifications from REV_RANGES and ADDITIONAL_LINES.
+
+  REV_RANGES is a list of revision ranges for which mergeinfo is being
+  recorded.  Each range is of the form [start, end] (where both START and
+  END are inclusive, unlike in '-rX:Y') or the form [single_rev] (which is
+  like '-c SINGLE_REV').  If REV_RANGES is None then only the standard
+  notification for a 3-way merge is expected.
+
+  ADDITIONAL_LINES is a list of strings to match the other lines of output;
+  these are basically regular expressions except that backslashes will be
+  escaped herein.
+
+  If ELIDES is true, add to the regex an expression representing elision
+  notification.  If TWO_URL is true, tweak the regex to expect the
+  appropriate mergeinfo notification for a 3-way merge.
+
+  TARGET is the local path to the target, as it should appear in
+  notifications; if None, it is not checked.
+
+  TEXT_CONFLICTS, PROP_CONFLICTS and TREE_CONFLICTS specify the number of
+  each kind of conflict to expect."""
+
   if rev_ranges is None:
     lines = [svntest.main.merge_notify_line(None, None, False, foreign)]
   else:
@@ -69,8 +85,8 @@ def expected_merge_output(rev_ranges, ad
       else:
         end_rev = None
       lines += [svntest.main.merge_notify_line(start_rev, end_rev,
-                                               True, foreign)]
-      lines += [svntest.main.mergeinfo_notify_line(start_rev, end_rev)]
+                                               True, foreign, target)]
+      lines += [svntest.main.mergeinfo_notify_line(start_rev, end_rev, target)]
 
   if (elides):
     lines += ["--- Eliding mergeinfo from .*\n"]
@@ -94,6 +110,16 @@ def expected_merge_output(rev_ranges, ad
     if sys.platform == 'win32' and additional_lines != None:
       additional_lines = additional_lines.replace("\\", "\\\\")
     lines.append(str(additional_lines))
+
+  if text_conflicts or prop_conflicts or tree_conflicts:
+    lines.append("Summary of conflicts:\n")
+    if text_conflicts:
+      lines.append("  Text conflicts: %d\n" % text_conflicts)
+    if prop_conflicts:
+      lines.append("  Property conflicts: %d\n" % prop_conflicts)
+    if tree_conflicts:
+      lines.append("  Tree conflicts: %d\n" % tree_conflicts)
+
   return "|".join(lines)
 
 def check_mergeinfo_recursively(root_path, subpaths_mergeinfo):
@@ -835,7 +861,8 @@ def merge_similar_unrelated_trees(sbox):
 
 #----------------------------------------------------------------------
 def merge_one_file_helper(sbox, arg_flav, record_only = 0):
-  "ARG_FLAV is one of 'r' (revision range) or 'c' (single change)."
+  """ARG_FLAV is one of 'r' (revision range) or 'c' (single change) or
+  '*' (no revision specified)."""
 
   if arg_flav not in ('r', 'c', '*'):
     raise svntest.Failure("Unrecognized flavor of merge argument")
@@ -972,9 +999,13 @@ def merge_record_only(sbox):
   merge_one_file_helper(sbox, 'r', 1)
 
 #----------------------------------------------------------------------
-# This is a regression for the enhancement added in issue #785.
+# This is a regression test for the enhancement added in issue #785 "add
+# friendly enhancement to 'svn merge'", which is about inferring that
+# the default target of "svn merge [-r...] FILE" should not be "." but
+# rather should be "FILE".
 def merge_with_implicit_target_helper(sbox, arg_flav):
-  "ARG_FLAV is one of 'r' (revision range) or 'c' (single change)."
+  """ARG_FLAV is one of 'r' (revision range) or 'c' (single change) or
+  '*' (no revision specified)."""
 
   if arg_flav not in ('r', 'c', '*'):
     raise svntest.Failure("Unrecognized flavor of merge argument")
@@ -2439,7 +2470,7 @@ def set_up_dir_replace(sbox):
 # A merge that replaces a directory
 # Tests for Issue #2144 and Issue #2607
 @SkipUnless(server_has_mergeinfo)
-@Issue(2144)
+@Issue(2144,2607)
 def merge_dir_replace(sbox):
   "merge a replacement of a directory"
 
@@ -5573,7 +5604,7 @@ def merge_to_switched_path(sbox):
 #   3188: Mergeinfo on switched targets/subtrees should
 #         elide to repos
 @SkipUnless(server_has_mergeinfo)
-@Issue(2823,2839,3187,3188)
+@Issue(2823,2839,3187,3188,4056)
 def merge_to_path_with_switched_children(sbox):
   "merge to path with switched children"
 
@@ -5668,18 +5699,18 @@ def merge_to_path_with_switched_children
     'omega' : Item(status=' U')
     })
   expected_elision_output = wc.State(A_COPY_H_path, {
+    'omega' : Item(status=' U')
     })
   expected_status = wc.State(A_COPY_H_path, {
     ''      : Item(status=' M', wc_rev=8),
     'psi'   : Item(status='  ', wc_rev=8, switched='S'),
-    'omega' : Item(status='MM', wc_rev=8),
+    'omega' : Item(status='M ', wc_rev=8),
     'chi'   : Item(status='  ', wc_rev=8),
     })
   expected_disk = wc.State('', {
-    ''      : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:8*'}),
+    ''      : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:8'}),
     'psi'   : Item("This is the file 'psi'.\n"),
-    'omega' : Item("New content",
-                   props={SVN_PROP_MERGEINFO : '/A/D/H/omega:8'}),
+    'omega' : Item("New content"),
     'chi'   : Item("This is the file 'chi'.\n"),
     })
   expected_skip = wc.State(A_COPY_H_path, { })
@@ -5713,7 +5744,7 @@ def merge_to_path_with_switched_children
     ''        : Item(status=' M', wc_rev=8),
     'H'       : Item(status=' M', wc_rev=8),
     'H/chi'   : Item(status='  ', wc_rev=8),
-    'H/omega' : Item(status='MM', wc_rev=8),
+    'H/omega' : Item(status='M ', wc_rev=8),
     'H/psi'   : Item(status='  ', wc_rev=8, switched='S'),
     'G'       : Item(status=' M', wc_rev=8, switched='S'),
     'G/pi'    : Item(status='  ', wc_rev=8),
@@ -5723,10 +5754,9 @@ def merge_to_path_with_switched_children
     })
   expected_disk_D = wc.State('', {
     ''        : Item(props={SVN_PROP_MERGEINFO : '/A/D:6*'}),
-    'H'       : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:8*'}),
+    'H'       : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:8'}),
     'H/chi'   : Item("This is the file 'chi'.\n"),
-    'H/omega' : Item("New content",
-                     props={SVN_PROP_MERGEINFO : '/A/D/H/omega:8'}),
+    'H/omega' : Item("New content"),
     'H/psi'   : Item("This is the file 'psi'.\n",),
     'G'       : Item(props={SVN_PROP_MERGEINFO : '/A/D/G:6*'}),
     'G/pi'    : Item("This is the file 'pi'.\n"),
@@ -5760,10 +5790,10 @@ def merge_to_path_with_switched_children
     })
   expected_elision_output = wc.State(A_COPY_D_path, {
     })
-  expected_disk_D.tweak('', props={SVN_PROP_MERGEINFO : '/A/D:5-6*'})
-  expected_disk_D.tweak('H', props={SVN_PROP_MERGEINFO : '/A/D/H:5*,8*'})
+  expected_disk_D.tweak('', props={SVN_PROP_MERGEINFO : '/A/D:5,6*'})
+  expected_disk_D.tweak('H', props={SVN_PROP_MERGEINFO : '/A/D/H:5*,8'})
   expected_disk_D.tweak('H/psi', contents="New content",
-                        props={SVN_PROP_MERGEINFO :'/A/D/H/psi:5'})
+                        props={SVN_PROP_MERGEINFO :'/A/D/H/psi:5,8'})
   expected_status_D.tweak('H/psi', status='MM')
   svntest.actions.run_and_verify_merge(A_COPY_D_path, '4', '5',
                                        sbox.repo_url + '/A/D', None,
@@ -5804,7 +5834,7 @@ def merge_to_path_with_switched_children
     'D/H'       : Item(status=' M', wc_rev=8),
     'D/H/chi'   : Item(status='  ', wc_rev=8),
     'D/H/psi'   : Item(status='MM', wc_rev=8, switched='S'),
-    'D/H/omega' : Item(status='MM', wc_rev=8),
+    'D/H/omega' : Item(status='M ', wc_rev=8),
     })
   expected_disk = wc.State('', {
     ''          : Item(props={SVN_PROP_MERGEINFO : '/A:5-8'}),
@@ -5816,19 +5846,18 @@ def merge_to_path_with_switched_children
     'B/lambda'  : Item("This is the file 'lambda'.\n"),
     'B/F'       : Item(),
     'C'         : Item(),
-    'D'         : Item(props={SVN_PROP_MERGEINFO : '/A/D:5-6*'}),
+    'D'         : Item(props={SVN_PROP_MERGEINFO : '/A/D:5,6*'}),
     'D/G'       : Item(props={SVN_PROP_MERGEINFO : '/A/D/G:6*'}),
     'D/G/pi'    : Item("This is the file 'pi'.\n"),
     'D/G/rho'   : Item("New content",
                        props={SVN_PROP_MERGEINFO : '/A/D/G/rho:6'}),
     'D/G/tau'   : Item("This is the file 'tau'.\n"),
     'D/gamma'   : Item("This is the file 'gamma'.\n"),
-    'D/H'       : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:5*,8*'}),
+    'D/H'       : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:5*,8'}),
     'D/H/chi'   : Item("This is the file 'chi'.\n"),
     'D/H/psi'   : Item("New content",
-                       props={SVN_PROP_MERGEINFO : '/A/D/H/psi:5'}),
-    'D/H/omega' : Item("New content",
-                       props={SVN_PROP_MERGEINFO : '/A/D/H/omega:8'}),
+                       props={SVN_PROP_MERGEINFO : '/A/D/H/psi:5,8'}),
+    'D/H/omega' : Item("New content"),
     })
   expected_skip = wc.State(A_COPY_path, { })
   svntest.actions.run_and_verify_merge(A_COPY_path, '4', '8',
@@ -5839,7 +5868,6 @@ def merge_to_path_with_switched_children
                                        expected_disk,
                                        expected_status, expected_skip,
                                        None, None, None, None, None, 1)
-
   # Commit changes thus far.
   expected_output = svntest.wc.State(wc_dir, {
     'A_COPY'           : Item(verb='Sending'),
@@ -5866,17 +5894,16 @@ def merge_to_path_with_switched_children
   wc_disk.tweak("A_COPY/B/E/beta",
                 contents="New content")
   wc_disk.tweak("A_COPY/D",
-                props={SVN_PROP_MERGEINFO : '/A/D:5-6*'})
+                props={SVN_PROP_MERGEINFO : '/A/D:5,6*'})
   wc_disk.tweak("A_COPY/D/G",
                 props={SVN_PROP_MERGEINFO : '/A/D/G:6*'})
   wc_disk.tweak("A_COPY/D/G/rho",
                 contents="New content",
                 props={SVN_PROP_MERGEINFO : '/A/D/G/rho:6'})
   wc_disk.tweak("A_COPY/D/H",
-                props={SVN_PROP_MERGEINFO : '/A/D/H:5*,8*'})
+                props={SVN_PROP_MERGEINFO : '/A/D/H:5*,8'})
   wc_disk.tweak("A_COPY/D/H/omega",
-                contents="New content",
-                props={SVN_PROP_MERGEINFO : '/A/D/H/omega:8'})
+                contents="New content")
   wc_disk.tweak("A_COPY_2", props={})
   svntest.actions.run_and_verify_switch(sbox.wc_dir, A_COPY_psi_path,
                                         sbox.repo_url + "/A_COPY/D/H/psi",
@@ -5915,8 +5942,7 @@ def merge_to_path_with_switched_children
   expected_disk = wc.State('', {
     ''      : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:5-8'}),
     'psi'   : Item("New content"),
-    'omega' : Item("New content",
-                   props={SVN_PROP_MERGEINFO : '/A/D/H/omega:8'}),
+    'omega' : Item("New content"),
     'chi'   : Item("This is the file 'chi'.\n"),
     })
   expected_skip = wc.State(A_COPY_H_path, { })
@@ -5966,14 +5992,11 @@ def merge_to_path_with_switched_children
   expected_status_D.tweak('H/psi', wc_rev=10, switched=None)
   expected_status_D.tweak('H/omega', wc_rev=9)
   expected_status_D.tweak('G', 'G/rho', switched='S', wc_rev=9)
-  expected_disk_D.tweak('', props={SVN_PROP_MERGEINFO : '/A/D:5-6*,10*',
+  expected_disk_D.tweak('', props={SVN_PROP_MERGEINFO : '/A/D:5,6*,10',
                                    "prop:name" : "propval"})
   expected_disk_D.tweak('G/rho',
                         props={SVN_PROP_MERGEINFO : '/A/D/G/rho:6'})
   expected_disk_D.tweak('H', props={SVN_PROP_MERGEINFO : '/A/D/H:5-8'})
-
-  expected_disk_D.tweak('H/omega',
-                        props={SVN_PROP_MERGEINFO : '/A/D/H/omega:8'})
   expected_disk_D.tweak('H/psi', contents="New content", props={})
   svntest.actions.run_and_verify_merge(A_COPY_D_path, '9', '10',
                                        sbox.repo_url + '/A/D', None,
@@ -6034,7 +6057,6 @@ def merge_to_path_with_switched_children
     'D/G'       : Item(status=' U'),
     'D/G/rho'   : Item(status=' U'),
     'D/H'       : Item(status=' U'),
-    'D/H/omega' : Item(status=' U'),
     })
   expected_elision_output = wc.State(A_COPY_path, {
     ''          : Item(status=' U'),
@@ -6042,7 +6064,6 @@ def merge_to_path_with_switched_children
     'D/G'       : Item(status=' U'),
     'D/G/rho'   : Item(status=' U'),
     'D/H'       : Item(status=' U'),
-    'D/H/omega' : Item(status=' U'),
     })
   expected_status = wc.State(A_COPY_path, {
     ''          : Item(status=' M', wc_rev=10),
@@ -6063,7 +6084,7 @@ def merge_to_path_with_switched_children
     'D/H'       : Item(status=' M', wc_rev=10),
     'D/H/chi'   : Item(status='  ', wc_rev=10),
     'D/H/psi'   : Item(status='M ', wc_rev=10),
-    'D/H/omega' : Item(status='MM', wc_rev=10),
+    'D/H/omega' : Item(status='M ', wc_rev=10),
     })
   expected_disk = wc.State('', {
     'B'         : Item(),
@@ -7260,7 +7281,7 @@ def merge_with_depth_files(sbox):
 #
 # Test issue #3407 'Shallow merges incorrectly set mergeinfo on children'.
 @SkipUnless(server_has_mergeinfo)
-@Issues(2976,3392,3407)
+@Issues(2976,3392,3407,4057)
 def merge_away_subtrees_noninheritable_ranges(sbox):
   "subtrees can lose non-inheritable ranges"
 
@@ -7579,8 +7600,9 @@ def merge_away_subtrees_noninheritable_r
   svntest.actions.run_and_verify_svn(None, None, [], 'revert', '-R', wc_dir)
   svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
 
-  # Merge r8 from A/D/H to A_COPY_D/H at depth empty, creating non-inheritable
-  # mergeinfo on the target.  Commit this merge as r13.
+  # Merge r8 from A/D/H to A_COPY_D/H at depth empty.  Since r8 affects only
+  # A_COPY/D/H itself, the resulting mergeinfo is inheritabled.  Commit this
+  # merge as r13.
   expected_output = wc.State(H_COPY_2_path, {
     ''    : Item(status=' U'),
     })
@@ -7596,7 +7618,7 @@ def merge_away_subtrees_noninheritable_r
     'chi'   : Item(status='  ', wc_rev=12),
     })
   expected_disk = wc.State('', {
-    ''      : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:8*',
+    ''      : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:8',
                           "prop:name" : "propval"}),
     'psi'   : Item("This is the file 'psi'.\n"),
     'omega' : Item("This is the file 'omega'.\n"),
@@ -9308,6 +9330,7 @@ def ignore_ancestry_and_mergeinfo(sbox):
 
 #----------------------------------------------------------------------
 @SkipUnless(server_has_mergeinfo)
+@Issue(3032)
 def merge_from_renamed_branch_fails_while_avoiding_repeat_merge(sbox):
   "merge from renamed branch"
   #Copy A/C to A/COPY_C results in r2.
@@ -9693,7 +9716,7 @@ def new_subtrees_should_not_break_merge(
     'D/H/psi'   : Item("This is the file 'psi'.\n"),
     'D/H/omega' : Item("New content"),
     'D/H/nu'    : Item("New content",
-                       props={SVN_PROP_MERGEINFO : '/A/D/H/nu:8'}),
+                       props={SVN_PROP_MERGEINFO : '/A/D/H/nu:7-8'}),
     })
   expected_skip = wc.State(A_COPY_path, { })
   svntest.actions.run_and_verify_merge(A_COPY_path, '4', '6',
@@ -9739,7 +9762,7 @@ def new_subtrees_should_not_break_merge(
     'H/psi'   : Item("This is the file 'psi'.\n"),
     'H/omega' : Item("This is the file 'omega'.\n"),
     'H/nu'    : Item("New content",
-                     props={SVN_PROP_MERGEINFO : '/A/D/H/nu:8'}),
+                     props={SVN_PROP_MERGEINFO : '/A/D/H/nu:7-8'}),
     })
   expected_skip = wc.State(D_COPY_path, { })
   svntest.actions.run_and_verify_merge(D_COPY_path, '6', '5',
@@ -9811,7 +9834,7 @@ def new_subtrees_should_not_break_merge(
     'D/H/psi'   : Item("This is the file 'psi'.\n"),
     'D/H/omega' : Item("New content"),
     'D/H/nu'    : Item("New content",
-                       props={SVN_PROP_MERGEINFO : '/A/D/H/nu:8'}),
+                       props={SVN_PROP_MERGEINFO : '/A/D/H/nu:7-8'}),
     })
   expected_skip = wc.State(A_COPY_path, { })
   svntest.actions.run_and_verify_merge(A_COPY_path, '5', '6',
@@ -12508,24 +12531,46 @@ def svn_copy(s_rev, path1, path2):
   svntest.actions.run_and_verify_svn(None, None, [], 'copy', '--parents',
                                      '-r', s_rev, path1, path2)
 
-def svn_merge(rev_spec, source, target, exp_out=None, *args):
-  """Merge a single change from path 'source' to path 'target'.
-  SRC_CHANGE_NUM is either a number (to cherry-pick that specific change)
-  or a command-line option revision range string such as '-r10:20'.
-  *ARGS are additional arguments passed to svn merge."""
+def svn_merge(rev_range, source, target, lines=None, elides=[],
+              text_conflicts=0, prop_conflicts=0, tree_conflicts=0, args=[]):
+  """Merge a single change from path SOURCE to path TARGET and verify the
+  output and that there is no error.  (The changes made are not verified.)
+
+  REV_RANGE is either a number (to cherry-pick that specific change) or a
+  two-element list [X,Y] to pick the revision range '-r(X-1):Y'.
+
+  LINES is a list of regular expressions to match other lines of output; if
+  LINES is 'None' then match all normal (non-conflicting) merges.
+
+  ELIDES is a list of paths on which mergeinfo elision should be reported.
+
+  TEXT_CONFLICTS, PROP_CONFLICTS and TREE_CONFLICTS specify the number of
+  each kind of conflict to expect.
+
+  ARGS are additional arguments passed to svn merge."""
+
   source = local_path(source)
   target = local_path(target)
-  if isinstance(rev_spec, int):
-    rev_spec = '-c' + str(rev_spec)
-  if exp_out is None:
-    target_re = re.escape(target)
-    exp_1 = "--- Merging r.* into '" + target_re + ".*':"
-    exp_2 = "(A |D |[UG] | [UG]|[UG][UG])   " + target_re + ".*"
-    exp_3 = "--- Recording mergeinfo for merge of r.* into '" + \
-            target_re + ".*':"
-    exp_out = svntest.verify.RegexOutput(exp_1 + "|" + exp_2 + "|" + exp_3)
+  elides = [local_path(p) for p in elides]
+  if isinstance(rev_range, int):
+    mi_rev_range = [rev_range]
+    rev_arg = '-c' + str(rev_range)
+  else:
+    mi_rev_range = rev_range
+    rev_arg = '-r' + str(rev_range[0] - 1) + ':' + str(rev_range[1])
+  if lines is None:
+    lines = ["(A |D |[UG] | [UG]|[UG][UG])   " + target + ".*\n"]
+  else:
+    # Expect mergeinfo on the target; caller must supply matches for any
+    # subtree mergeinfo paths.
+    lines.append(" [UG]   " + target + "\n")
+  exp_out = expected_merge_output([mi_rev_range], lines, target=target,
+                                  elides=elides,
+                                  text_conflicts=text_conflicts,
+                                  prop_conflicts=prop_conflicts,
+                                  tree_conflicts=tree_conflicts)
   svntest.actions.run_and_verify_svn(None, exp_out, [],
-                                     'merge', rev_spec, source, target, *args)
+                                     'merge', rev_arg, source, target, *args)
 
 #----------------------------------------------------------------------
 # Tests for merging the deletion of a node, where the node to be deleted
@@ -12557,7 +12602,8 @@ def del_identical_file(sbox):
   svn_copy(s_rev_mod, source, target)
   sbox.simple_commit(target)
   # Should be deleted quietly.
-  svn_merge(s_rev_del, source, target, '--- Merging|D |--- Recording| U')
+  svn_merge(s_rev_del, source, target,
+            ['D    %s\n' % local_path('A/D/G2/tau')])
 
   # Make a differing copy, locally modify it so it's the same,
   # and merge a deletion to it.
@@ -12566,7 +12612,8 @@ def del_identical_file(sbox):
   sbox.simple_commit(target)
   svn_modfile(target+"/tau")
   # Should be deleted quietly.
-  svn_merge(s_rev_del, source, target, '--- Merging|D |--- Recording| U')
+  svn_merge(s_rev_del, source, target,
+            ['D    %s\n' % local_path('A/D/G3/tau')])
 
   os.chdir(saved_cwd)
 
@@ -12593,10 +12640,11 @@ def del_sched_add_hist_file(sbox):
   svn_copy(s_rev_orig, source, target)
   sbox.simple_commit(target)
   s_rev = 3
-  svn_merge(s_rev_add, source, target, '--- Merging|A |--- Recording| U')
+  svn_merge(s_rev_add, source, target,
+            ['A    %s\n' % local_path('A/D/G2/file')])
   # Should be deleted quietly.
   svn_merge(-s_rev_add, source, target,
-            '--- Reverse-merging|D |--- Recording| U| G|--- Eliding')
+            ['D    %s\n' % local_path('A/D/G2/file')], elides=['A/D/G2'])
 
   os.chdir(saved_cwd)
 
@@ -12890,12 +12938,6 @@ def merge_target_and_subtrees_need_nonin
     None, expected_merge_output([[8]], ['U    ' + nu_COPY_path    + '\n',
                                         ' G   ' + nu_COPY_path    + '\n']),
     [], 'merge', '-c8', sbox.repo_url + '/A/D/G/nu', nu_COPY_path)
-  # Replicate pre 1.7 merge behavior where self-referential mergeinfo
-  # could be inherited, this keeps the original intent of this test intact,
-  # see http://subversion.tigris.org/issues/show_bug.cgi?id=3669#desc8
-  svntest.actions.run_and_verify_svn(None, None, [], 'ps',
-                                     SVN_PROP_MERGEINFO, '/A/D/G/nu:2-8',
-                                     nu_COPY_path)
 
   svntest.actions.run_and_verify_svn(
     None, expected_merge_output([[-6]], ['G    ' + omega_COPY_path    + '\n',
@@ -13057,15 +13099,9 @@ def merge_two_edits_to_same_prop(sbox):
   rev4 = initial_rev + 4
 
   # Merge the two changes together to source.
-  svn_merge('-r'+str(rev3-1)+':'+str(rev4), A_COPY_path, A_path, [
-      "--- Merging r9 through r10 into '%s':\n" % A_path,
+  svn_merge([rev3, rev4], A_COPY_path, A_path, [
       " C   %s\n" % mu_path,
-      "--- Recording mergeinfo for merge of r9 through r10 into '%s':\n" \
-      % A_path,
-      " U   A\n",
-      "Summary of conflicts:\n",
-      "  Property conflicts: 1\n"],
-      '--allow-mixed-revisions')
+      ], prop_conflicts=1, args=['--allow-mixed-revisions'])
 
   # Revert changes to source wc, to test next scenario of #3250
   svntest.actions.run_and_verify_svn(None, None, [],
@@ -13073,21 +13109,11 @@ def merge_two_edits_to_same_prop(sbox):
 
   # Merge the first change, then the second, to source.
   svn_merge(rev3, A_COPY_path, A_path, [
-      "--- Merging r9 into '%s':\n" % A_path,
       " C   %s\n" % mu_path,
-      "--- Recording mergeinfo for merge of r9 into '%s':\n" % A_path,
-      " U   A\n",
-      "Summary of conflicts:\n",
-      "  Property conflicts: 1\n"],
-      '--allow-mixed-revisions')
+      ], prop_conflicts=1, args=['--allow-mixed-revisions'])
   svn_merge(rev4, A_COPY_path, A_path, [
-      "--- Merging r10 into '%s':\n" % A_path,
       " C   %s\n" % mu_path,
-      "--- Recording mergeinfo for merge of r10 into '%s':\n" % A_path,
-      " G   A\n",
-      "Summary of conflicts:\n",
-      "  Property conflicts: 1\n"],
-      '--allow-mixed-revisions')
+      ], prop_conflicts=1, args=['--allow-mixed-revisions'])
 
   os.chdir(was_cwd)
 
@@ -13136,8 +13162,8 @@ def merge_an_eol_unification_and_set_svn
   sbox.simple_commit('A_COPY')
 
   # Merge the two changes together to the target branch.
-  svn_merge('-r'+str(rev1)+':'+str(rev3), 'A', 'A_COPY', None,
-            '--allow-mixed-revisions')
+  svn_merge([rev2, rev3], 'A', 'A_COPY',
+            args=['--allow-mixed-revisions'])
 
   # That merge should succeed.
   # Surprise: setting svn:eol-style='LF' instead of 'native' doesn't fail.
@@ -16242,7 +16268,8 @@ def merge_with_os_deleted_subtrees(sbox)
 # Test for issue #3668 'inheritance can result in self-referential
 # mergeinfo' and issue #3669 'inheritance can result in mergeinfo
 # describing nonexistent sources'
-@Issue(3668)
+@Issue(3668,3669)
+@XFail()
 def no_self_referential_or_nonexistent_inherited_mergeinfo(sbox):
   "don't inherit bogus mergeinfo"
 
@@ -16303,6 +16330,10 @@ def no_self_referential_or_nonexistent_i
   # Update the WC in preparation for merges.
   svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
 
+  # This test is marked as XFail because the following two merges
+  # create mergeinfo with both non-existent path-revs and self-referential
+  # mergeinfo.
+  #
   # Merge all available revisions from A/C/nu to A_COPY/C/nu.
   # The target has no explicit mergeinfo of its own but inherits mergeinfo
   # from A_COPY.  A_COPY has the mergeinfo '/A:2-9' so the naive mergeinfo
@@ -16680,11 +16711,9 @@ def foreign_repos_prop_conflict(sbox):
 
   # Now, merge the propchange to the *second* working copy.
   expected_output = [' C   %s\n' % (os.path.join(other_wc_dir,
-                                                 "A", "D", "G")),
-                     'Summary of conflicts:\n',
-                     '  Property conflicts: 1\n',
-                     ]
-  expected_output = expected_merge_output([[3]], expected_output, True)
+                                                 "A", "D", "G"))]
+  expected_output = expected_merge_output([[3]], expected_output, True,
+                                          prop_conflicts=1)
   svntest.actions.run_and_verify_svn(None,
                                      expected_output,
                                      [], 'merge', '-c3',
@@ -16788,11 +16817,15 @@ def merge_adds_subtree_with_mergeinfo(sb
     'B/lambda'  : Item("This is the file 'lambda'.\n"),
     'B/F'       : Item(),
     'C'         : Item(),
+    # C/nu will pick up the mergeinfo A_COPY/C/nu:8 which is self-referential.
+    # This is issue #3668 'inheritance can result in self-referential
+    # mergeinfo', but we'll allow it in this test since issue #3668 is
+    # tested elsewhere and is not the point of *this* test.
     'C/nu'      : Item("This is the file 'nu'.\n" \
                        "More work on the A_COPY branch.\n" \
                        "A faux conflict resolution.\n",
                        props={SVN_PROP_MERGEINFO :
-                              '/A/C/nu:9-11\n/A_COPY/C/nu:10'}),
+                              '/A/C/nu:9-11\n/A_COPY/C/nu:8,10'}),
     'D'         : Item(),
     'D/G'       : Item(),
     'D/G/pi'    : Item("This is the file 'pi'.\n"),
@@ -16818,7 +16851,7 @@ def merge_adds_subtree_with_mergeinfo(sb
 
 #----------------------------------------------------------------------
 # A test for issue #3978 'reverse merge which adds subtree fails'.
-@Issue(3978)
+@Issue(3978,4057)
 @SkipUnless(server_has_mergeinfo)
 def reverse_merge_adds_subtree(sbox):
   "reverse merge adds subtree"
@@ -16845,6 +16878,9 @@ def reverse_merge_adds_subtree(sbox):
                                      'Cherry-pick r7 from A to A_COPY', wc_dir)
 
   # r9 - File depth sync merge from A/D/H to A_COPY/D/H/
+  # This shallow merge does not create non-inheritable mergeinfo because of
+  # the issue #4057 fix; all subtrees affected by the diff are present, so
+  # non-inheritable mergeinfo is not required.
   svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
   svntest.actions.run_and_verify_svn(None, None, [], 'merge',
                                      sbox.repo_url + '/A/D/H',
@@ -16882,7 +16918,6 @@ def reverse_merge_adds_subtree(sbox):
   # ..\..\..\subversion\libsvn_subr\kitchensink.c:57: (apr_err=200022)
   # svn: E200022: Negative revision number found parsing '-7'
   svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
-  svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
   expected_output = wc.State(A_COPY_path, {
     'D/H/chi' : Item(status='A '),
     })
@@ -16931,12 +16966,10 @@ def reverse_merge_adds_subtree(sbox):
     'D/G/rho'   : Item("This is the file 'rho'.\n"),
     'D/G/tau'   : Item("This is the file 'tau'.\n"),
     'D/gamma'   : Item("This is the file 'gamma'.\n"),
-    'D/H'       : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:2-6*,8*'}),
+    'D/H'       : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:2-6,8'}),
     'D/H/chi'   : Item("This is the file 'chi'.\n"),
-    'D/H/psi'   : Item("New content",
-                       props={SVN_PROP_MERGEINFO : '/A/D/H/psi:2-8'}),
-    'D/H/omega' : Item("New content",
-                       props={SVN_PROP_MERGEINFO : '/A/D/H/omega:2-8'}),
+    'D/H/psi'   : Item("New content"),
+    'D/H/omega' : Item("New content"),
     })
   expected_skip = wc.State('.', { })
   svntest.actions.run_and_verify_merge(A_COPY_path, 7, 6,
@@ -17107,6 +17140,215 @@ def record_only_merge_adds_new_subtree_m
                                        None, None, None, None,
                                        None, 1, False)
 
+#----------------------------------------------------------------------
+# Setup helper for issue #4056 and issue #4057 tests.
+def noninheritable_mergeinfo_test_set_up(sbox):
+  '''Starting with standard greek tree, copy 'A' to 'branch' in r2 and
+  then made a file edit to A/B/lambda in r3.
+  Return (expected_output, expected_mergeinfo_output, expected_elision_output,
+          expected_status, expected_disk, expected_skip) for a merge of
+  r3 from ^/A/B to branch/B.'''
+
+  sbox.build()
+  wc_dir = sbox.wc_dir
+
+  lambda_path   = os.path.join(wc_dir, 'A', 'B', 'lambda')
+  B_branch_path = os.path.join(wc_dir, 'branch', 'B')
+
+  # r2 - Branch ^/A to ^/branch.
+  svntest.main.run_svn(None, 'copy', sbox.repo_url + '/A',
+                       sbox.repo_url + '/branch', '-m', 'make a branch')
+
+  # r3 - Make an edit to A/B/lambda.
+  svntest.main.file_write(lambda_path, "trunk edit.\n")
+  svntest.main.run_svn(None, 'commit', '-m', 'file edit', wc_dir)
+  svntest.main.run_svn(None, 'up', wc_dir)
+
+  expected_output = wc.State(B_branch_path, {
+    'lambda' : Item(status='U '),
+    })
+  expected_mergeinfo_output = wc.State(B_branch_path, {
+    ''       : Item(status=' U'),
+    'lambda' : Item(status=' U'),
+    })
+  expected_elision_output = wc.State(B_branch_path, {
+    'lambda' : Item(status=' U'),
+    })
+  expected_status = wc.State(B_branch_path, {
+    ''        : Item(status=' M'),
+    'lambda'  : Item(status='M '),
+    'E'       : Item(status='  '),
+    'E/alpha' : Item(status='  '),
+    'E/beta'  : Item(status='  '),
+    'F'       : Item(status='  '),
+    })
+  expected_status.tweak(wc_rev='3')
+  expected_disk = wc.State('', {
+    ''          : Item(props={SVN_PROP_MERGEINFO : '/A/B:3'}),
+    'lambda'  : Item("trunk edit.\n"),
+    'E'       : Item(),
+    'E/alpha' : Item("This is the file 'alpha'.\n"),
+    'E/beta'  : Item("This is the file 'beta'.\n"),
+    'F'       : Item(),
+    })
+  expected_skip = wc.State(B_branch_path, {})
+
+  return expected_output, expected_mergeinfo_output, expected_elision_output, \
+    expected_status, expected_disk, expected_skip
+
+
+#----------------------------------------------------------------------
+# Test for issue #4056 "don't record non-inheritable mergeinfo if missing
+# subtrees are not touched by the full-depth diff".
+@Issue(4056)
+@SkipUnless(server_has_mergeinfo)
+def unnecessary_noninheritable_mergeinfo_missing_subtrees(sbox):
+  "missing subtrees untouched by infinite depth merge"
+
+  B_branch_path = os.path.join(sbox.wc_dir, 'branch', 'B')
+
+  # Setup a simple branch to which 
+  expected_output, expected_mergeinfo_output, expected_elision_output, \
+    expected_status, expected_disk, expected_skip = \
+    noninheritable_mergeinfo_test_set_up(sbox)
+
+  # Create a shallow merge target; set depth of branch/B to files.
+  svntest.main.run_svn(None, 'up', '--set-depth=files', B_branch_path)
+  expected_status.remove('E', 'E/alpha', 'E/beta', 'F')
+  expected_disk.remove('E', 'E/alpha', 'E/beta', 'F')
+
+  # Merge r3 from ^/A/B to branch/B
+  #
+  # Merge is smart enough to realize that despite the shallow merge target,
+  # the diff can only affect branch/B/lambda, which is still present, so there
+  # is no need to record non-inheritable mergeinfo on the target
+  # or any subtree mergeinfo whatsoever:
+  #
+  #   >svn pg svn:mergeinfo -vR
+  #   Properties on 'branch\B':
+  #     svn:mergeinfo
+  #       /A/B:3 <-- Nothing was skipped, so doesn't need
+  #                  to be non-inheritable.
+  svntest.actions.run_and_verify_merge(B_branch_path,
+                                       '2', '3',
+                                       sbox.repo_url + '/A/B', None,
+                                       expected_output,
+                                       expected_mergeinfo_output,
+                                       expected_elision_output,
+                                       expected_disk,
+                                       expected_status,
+                                       expected_skip,
+                                       None, None, None, None, None, 1, 1,
+                                       B_branch_path)
+
+#----------------------------------------------------------------------
+# Test for issue #4057 "don't record non-inheritable mergeinfo in shallow
+# merge if entire diff is within requested depth".
+@Issue(4057)
+@SkipUnless(server_has_mergeinfo)
+def unnecessary_noninheritable_mergeinfo_shallow_merge(sbox):
+  "shallow merge reaches all necessary subtrees"
+
+  B_branch_path = os.path.join(sbox.wc_dir, 'branch', 'B')
+  E_path        = os.path.join(sbox.wc_dir, 'A', 'B', 'E')
+
+  # Setup a simple branch to which 
+  expected_output, expected_mergeinfo_output, expected_elision_output, \
+    expected_status, expected_disk, expected_skip = \
+    noninheritable_mergeinfo_test_set_up(sbox)
+
+  # Merge r3 from ^/A/B to branch/B at operational depth=files
+  #
+  # Previously this failed because merge wasn't smart enough to
+  # realize that despite being a shallow merge, the diff can
+  # only affect branch/B/lambda, which is within the specified
+  # depth, so there is no need to record non-inheritable mergeinfo
+  # or subtree mergeinfo:
+  #
+  #   >svn pg svn:mergeinfo -vR
+  #   Properties on 'branch\B':
+  #     svn:mergeinfo
+  #       /A/B:3* <-- Should be inheritable
+  #   Properties on 'branch\B\lambda':
+  #     svn:mergeinfo
+  #       /A/B/lambda:3 <-- Not necessary
+  expected_skip = wc.State(B_branch_path, {})
+  svntest.actions.run_and_verify_merge(B_branch_path, '2', '3',
+                                       sbox.repo_url + '/A/B', None,
+                                       expected_output,
+                                       expected_mergeinfo_output,
+                                       expected_elision_output,
+                                       expected_disk,
+                                       expected_status,
+                                       expected_skip,
+                                       None, None, None, None, None, 1, 1,
+                                       '--depth', 'files', B_branch_path)
+
+  # Revert the merge and then make a prop change to A/B/E in r4.
+  svntest.actions.run_and_verify_svn(None, None, [],
+                                     'revert', '--recursive', sbox.wc_dir)
+  svntest.actions.run_and_verify_svn(None,
+                                     ["property 'prop:name' set on '" +
+                                      E_path + "'\n"], [], 'ps',
+                                     'prop:name', 'propval', E_path)
+  svntest.actions.run_and_verify_svn(None, None, [],
+                                     'ci', '-m', 'A new property on a dir',
+                                     sbox.wc_dir)
+  svntest.actions.run_and_verify_svn(None, None, [],
+                                     'up', sbox.wc_dir)
+
+  # Merge r4 from ^/A/B to branch/B at operational depth=immediates
+  #
+  # Previously this failed because the mergetracking logic didn't realize
+  # that despite being a shallow merge, the diff only affected branch/B/E,
+  # which was within the specified depth, so there was no need to record
+  # non-inheritable mergeinfo or subtree mergeinfo:
+  #
+  #   >svn pg svn:mergeinfo -vR
+  #   Properties on 'branch\B':
+  #     svn:mergeinfo
+  #       /A/B:4* <-- Should be inheritable
+  #   Properties on 'branch\B\E':
+  #     svn:mergeinfo
+  #       /A/B/E:4 <-- Not necessary 
+  expected_output = wc.State(B_branch_path, {
+    'E' : Item(status=' U'),
+    })
+  expected_mergeinfo_output = wc.State(B_branch_path, {
+    ''  : Item(status=' U'),
+    'E' : Item(status=' U'),
+    })
+  expected_elision_output = wc.State(B_branch_path, {
+    'E' : Item(status=' U'),
+    })
+  expected_status = wc.State(B_branch_path, {
+    ''        : Item(status=' M'),
+    'lambda'  : Item(status='  '),
+    'E'       : Item(status=' M'),
+    'E/alpha' : Item(status='  '),
+    'E/beta'  : Item(status='  '),
+    'F'       : Item(status='  '),
+    })
+  expected_status.tweak(wc_rev='4')
+  expected_disk = wc.State('', {
+    ''          : Item(props={SVN_PROP_MERGEINFO : '/A/B:4'}),
+    'lambda'  : Item("This is the file 'lambda'.\n"),
+    'E'       : Item(props={'prop:name' : 'propval'}),
+    'E/alpha' : Item("This is the file 'alpha'.\n"),
+    'E/beta'  : Item("This is the file 'beta'.\n"),
+    'F'       : Item(),
+    })
+  svntest.actions.run_and_verify_merge(B_branch_path, '3', '4',
+                                       sbox.repo_url + '/A/B', None,
+                                       expected_output,
+                                       expected_mergeinfo_output,
+                                       expected_elision_output,
+                                       expected_disk,
+                                       expected_status,
+                                       expected_skip,
+                                       None, None, None, None, None, 1, 1,
+                                       '--depth', 'immediates', B_branch_path)
+
 ########################################################################
 # Run the tests
 
@@ -17235,6 +17477,8 @@ test_list = [ None,
               reverse_merge_adds_subtree,
               merged_deletion_causes_tree_conflict,
               record_only_merge_adds_new_subtree_mergeinfo,
+              unnecessary_noninheritable_mergeinfo_missing_subtrees,
+              unnecessary_noninheritable_mergeinfo_shallow_merge,
              ]
 
 if __name__ == '__main__':

Modified: subversion/branches/revprop-packing/subversion/tests/cmdline/merge_tree_conflict_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/revprop-packing/subversion/tests/cmdline/merge_tree_conflict_tests.py?rev=1231318&r1=1231317&r2=1231318&view=diff
==============================================================================
--- subversion/branches/revprop-packing/subversion/tests/cmdline/merge_tree_conflict_tests.py (original)
+++ subversion/branches/revprop-packing/subversion/tests/cmdline/merge_tree_conflict_tests.py Fri Jan 13 21:40:26 2012
@@ -46,6 +46,7 @@ from svntest.main import server_has_merg
 from merge_tests import set_up_branch
 from merge_tests import svn_copy
 from merge_tests import svn_merge
+from merge_tests import expected_merge_output
 
 #----------------------------------------------------------------------
 @SkipUnless(server_has_mergeinfo)
@@ -680,25 +681,16 @@ def del_differing_file(sbox):
                                      'newprop', 'v', target+"/pi")
 
   dir_D = os.path.join('A','D')
-  dir_G2 = os.path.join(dir_D, 'G2')
   tau = os.path.join(dir_D,'G2','tau')
   pi = os.path.join(dir_D, 'G2', 'pi')
   # Should complain and "skip" it.
   svn_merge(s_rev_tau, source, target, [
-      "--- Merging r2 into '%s':\n" % dir_G2,
-      "   C %s\n" % tau,
-      "--- Recording mergeinfo for merge of r2 into '%s':\n" % (dir_G2),
-      " U   %s\n" % (dir_G2),
-      "Summary of conflicts:\n",
-      "  Tree conflicts: 1\n"])
+      "   C %s\n" % tau,       # merge
+      ], tree_conflicts=1)
 
   svn_merge(s_rev_pi, source, target, [
-      "--- Merging r3 into '%s':\n" % dir_G2,
-      "   C %s\n" % pi,
-      "--- Recording mergeinfo for merge of r3 into '%s':\n" % (dir_G2),
-      " G   %s\n" % (dir_G2),
-      "Summary of conflicts:\n",
-      "  Tree conflicts: 1\n"])
+      "   C %s\n" % pi,        # merge
+      ], tree_conflicts=1)
 
 
   # Copy a file, modify it, commit, and merge a deletion to it.
@@ -710,26 +702,17 @@ def del_differing_file(sbox):
   sbox.simple_commit(target)
 
 
-  dir_G3 = os.path.join(dir_D, 'G3')
   tau = os.path.join(dir_D,'G3','tau')
   pi = os.path.join(dir_D, 'G3', 'pi')
 
   # Should complain and "skip" it.
   svn_merge(s_rev_tau, source, target, [
-      "--- Merging r2 into '%s':\n" % dir_G3,
       "   C %s\n" % tau,
-      "--- Recording mergeinfo for merge of r2 into '%s':\n" % (dir_G3),
-      " U   %s\n" % (dir_G3),
-      "Summary of conflicts:\n",
-      "  Tree conflicts: 1\n"])
+      ], tree_conflicts=1)
 
   svn_merge(s_rev_pi, source, target, [
-      "--- Merging r3 into '%s':\n" % dir_G3,
       "   C %s\n" % pi,
-      "--- Recording mergeinfo for merge of r3 into '%s':\n" % (dir_G3),
-      " G   %s\n" % (dir_G3),
-      "Summary of conflicts:\n",
-      "  Tree conflicts: 1\n"])
+      ], tree_conflicts=1)
 
   os.chdir(saved_cwd)
 
@@ -1738,18 +1721,15 @@ def merge_replace_causes_tree_conflict(s
     'propname', 'propval', A_D_H)
 
   # svn merge $URL/A $URL/branch A
-  expected_stdout = verify.UnorderedOutput([
-    "--- Merging differences between repository URLs into '" + A + "':\n",
+  expected_stdout = expected_merge_output(None, [
+    # merge
     '   C ' + A_B_E + '\n',
     '   C ' + A_mu + '\n',
     '   C ' + A_D_G_pi + '\n',
     '   C ' + A_D_H + '\n',
-    "--- Recording mergeinfo for merge between repository URLs into '" \
-    + A + "':\n",
+    # mergeinfo
     ' U   ' + A + '\n',
-    'Summary of conflicts:\n',
-    '  Tree conflicts: 4\n',
-  ])
+  ], target=A, two_url=True, tree_conflicts=4)
 
   actions.run_and_verify_svn2('OUTPUT', expected_stdout, [], 0, 'merge',
     url_A, url_branch, A)
@@ -1829,15 +1809,10 @@ def merge_replace_causes_tree_conflict2(
 
   ### A file-with-file replacement onto a deleted file.
   # svn merge $URL/A/mu $URL/branch/mu A/mu
-  expected_stdout = verify.UnorderedOutput([
-    "--- Merging differences between repository URLs into '" + A + "':\n",
-    '   C ' + A_mu + '\n',
-    "--- Recording mergeinfo for merge between repository URLs into '" +
-      A + "':\n",
-    " U   " + A + "\n",
-    'Summary of conflicts:\n',
-    '  Tree conflicts: 1\n',
-  ])
+  expected_stdout = expected_merge_output(None, [
+    '   C ' + A_mu + '\n',  # merge
+    " U   " + A + "\n",     # mergeinfo
+  ], target=A, two_url=True, tree_conflicts=1)
 
   actions.run_and_verify_svn2('OUTPUT', expected_stdout, [], 0, 'merge',
     url_A, url_branch, A, '--depth=files')
@@ -1853,15 +1828,10 @@ def merge_replace_causes_tree_conflict2(
 
   ### A dir-with-dir replacement onto a deleted directory.
   # svn merge $URL/A/B $URL/branch/B A/B
-  expected_stdout = verify.UnorderedOutput([
-    "--- Merging differences between repository URLs into '" + A_B + "':\n",
-    '   C ' + A_B_E + '\n',
-    "--- Recording mergeinfo for merge between repository URLs into '" +
-      A_B + "':\n",
-    " U   " + A_B + "\n",
-    'Summary of conflicts:\n',
-    '  Tree conflicts: 1\n',
-  ])
+  expected_stdout = expected_merge_output(None, [
+    '   C ' + A_B_E + '\n',   # merge
+    " U   " + A_B + "\n",     # mergeinfo
+  ], target=A_B, two_url=True, tree_conflicts=1)
 
   actions.run_and_verify_svn2('OUTPUT', expected_stdout, [], 0, 'merge',
     url_A_B, url_branch_B, A_B)
@@ -1877,16 +1847,11 @@ def merge_replace_causes_tree_conflict2(
 
   ### A dir-with-file replacement onto a deleted directory.
   # svn merge --depth=immediates $URL/A/D $URL/branch/D A/D
-  expected_stdout = verify.UnorderedOutput([
-    "--- Merging differences between repository URLs into '" + A_D + "':\n",
-    '   C ' + A_D_H + '\n',
-    "--- Recording mergeinfo for merge between repository URLs into '" +
-      A_D + "':\n",
-    " U   " + A_D + "\n",
+  expected_stdout = expected_merge_output(None, [
+    '   C ' + A_D_H + '\n',   # merge
+    " U   " + A_D + "\n",     # mergeinfo
     " U   " + A_D_G + "\n",
-    'Summary of conflicts:\n',
-    '  Tree conflicts: 1\n',
-  ])
+  ], target=A_D, two_url=True, tree_conflicts=1)
 
   actions.run_and_verify_svn2('OUTPUT', expected_stdout, [], 0, 'merge',
     '--depth=immediates', url_A_D, url_branch_D, A_D)
@@ -1902,20 +1867,9 @@ def merge_replace_causes_tree_conflict2(
 
   ### A file-with-dir replacement onto a deleted file.
   # svn merge $URL/A/D/G $URL/branch/D/G A/D/G
-  expected_stdout = verify.UnorderedOutput([
-    "--- Merging differences between repository URLs into '" + A_D_G +
-    "':\n",
-    '   C ' + A_D_G_pi + '\n',
-    "--- Recording mergeinfo for merge between repository URLs into '" +
-      A_D_G + "':\n",
-    "--- Eliding mergeinfo from '" + A_D_G_pi + "':\n",
-    " U   " + A_D_G_pi + "\n",
-    "--- Eliding mergeinfo from '" + A_D_G_pi + "':\n",
-    " U   " + A_D_G_pi + "\n",
-    " G   " + A_D_G + "\n",
-    'Summary of conflicts:\n',
-    '  Tree conflicts: 1\n',
-  ])
+  expected_stdout = expected_merge_output(None, [
+    '   C ' + A_D_G_pi + '\n',  # merge
+  ], target=A_D_G, elides=[A_D_G_pi, A_D_G], two_url=True, tree_conflicts=1)
 
   actions.run_and_verify_svn2('OUTPUT', expected_stdout, [], 0, 'merge',
     url_A_D_G, url_branch_D_G, A_D_G)
@@ -1940,6 +1894,77 @@ def merge_replace_causes_tree_conflict2(
     actions.run_and_verify_svn2('OUTPUT', expected_stdout, [], 0, 'st',
                               '--depth=empty', path)
 
+#----------------------------------------------------------------------
+# Test for issue #4011 'merge of replacement on local delete fails'
+@SkipUnless(server_has_mergeinfo)
+@Issue(4011)
+@XFail()
+def merge_replace_on_del_fails(sbox):
+  "merge replace on local delete fails"
+
+  sbox.build()
+  wc_dir = sbox.wc_dir
+
+  C_path        = os.path.join(wc_dir, 'A', 'C')
+  branch_path   = os.path.join(wc_dir, 'branch')
+  C_branch_path = os.path.join(wc_dir, 'branch', 'C')
+
+  # r2 - Copy ^/A to ^/branch
+  svntest.actions.run_and_verify_svn(None, None, [], 'copy',
+                                     sbox.repo_url + '/A',
+                                     sbox.repo_url + '/branch',
+                                     '-m', 'Create a branch')
+
+  # r3 - Replace A/C
+  svntest.actions.run_and_verify_svn(None, None, [], 'del', C_path)
+  svntest.actions.run_and_verify_svn(None, None, [], 'mkdir', C_path)
+  svntest.actions.run_and_verify_svn(None, None, [], 'ci',
+                                     '-m', 'Replace A/C', wc_dir)
+
+  # r4 - Delete branch/C
+  svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
+  svntest.actions.run_and_verify_svn(None, None, [], 'del', C_branch_path)
+  svntest.actions.run_and_verify_svn(None, None, [], 'ci',
+                                     '-m', 'Delete branch/C', wc_dir)
+
+  # Sync merge ^/A to branch
+  svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
+  expected_stdout = expected_merge_output([[2,4]], [
+    '   C ' + C_branch_path + '\n',  # merge
+    ' U   ' + branch_path + '\n',    # mergeinfo
+  ], target=branch_path, tree_conflicts=1)
+  # This currently fails with:
+  #
+  #   >svn merge ^/A branch
+  #   ..\..\..\subversion\svn\util.c:913: (apr_err=155010)
+  #   ..\..\..\subversion\libsvn_client\merge.c:11349: (apr_err=155010)
+  #   ..\..\..\subversion\libsvn_client\merge.c:11303: (apr_err=155010)
+  #   ..\..\..\subversion\libsvn_client\merge.c:11303: (apr_err=155010)
+  #   ..\..\..\subversion\libsvn_client\merge.c:11273: (apr_err=155010)
+  #   ..\..\..\subversion\libsvn_client\merge.c:9287: (apr_err=155010)
+  #   ..\..\..\subversion\libsvn_client\merge.c:8870: (apr_err=155010)
+  #   ..\..\..\subversion\libsvn_client\merge.c:5349: (apr_err=155010)
+  #   ..\..\..\subversion\libsvn_repos\reporter.c:1430: (apr_err=155010)
+  #   ..\..\..\subversion\libsvn_client\ra.c:247: (apr_err=155010)
+  #   ..\..\..\subversion\libsvn_repos\reporter.c:1269: (apr_err=155010)
+  #   ..\..\..\subversion\libsvn_repos\reporter.c:1205: (apr_err=155010)
+  #   ..\..\..\subversion\libsvn_repos\reporter.c:920: (apr_err=155010)
+  #   ..\..\..\subversion\libsvn_delta\cancel.c:120: (apr_err=155010)
+  #   ..\..\..\subversion\libsvn_delta\cancel.c:120: (apr_err=155010)
+  #   ..\..\..\subversion\libsvn_client\repos_diff.c:710: (apr_err=155010)
+  #   ..\..\..\subversion\libsvn_client\merge.c:2234: (apr_err=155010)
+  #   ..\..\..\subversion\libsvn_wc\adm_ops.c:1069: (apr_err=155010)
+  #   ..\..\..\subversion\libsvn_wc\adm_ops.c:956: (apr_err=155010)
+  #   ..\..\..\subversion\libsvn_wc\update_editor.c:5036: (apr_err=155010)
+  #   ..\..\..\subversion\libsvn_wc\wc_db.c:6985: (apr_err=155010)
+  #   ..\..\..\subversion\libsvn_wc\wc_db.c:6929: (apr_err=155010)
+  #   ..\..\..\subversion\libsvn_wc\wc_db.c:6920: (apr_err=155010)
+  #   svn: E155010: The node 'C:\SVN\src-trunk\Debug\subversion\tests\
+  #     cmdline\svn-test-work\working_copies\merge_tree_conflict_tests-24\
+  #     branch\C' was not found.
+  actions.run_and_verify_svn2('OUTPUT', expected_stdout, [], 0, 'merge',
+    sbox.repo_url + '/A', branch_path)
+
 ########################################################################
 # Run the tests
 
@@ -1969,6 +1994,7 @@ test_list = [ None,
               tree_conflicts_merge_del_onto_missing,
               merge_replace_causes_tree_conflict,
               merge_replace_causes_tree_conflict2,
+              merge_replace_on_del_fails,
              ]
 
 if __name__ == '__main__':

Modified: subversion/branches/revprop-packing/subversion/tests/cmdline/mergeinfo_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/revprop-packing/subversion/tests/cmdline/mergeinfo_tests.py?rev=1231318&r1=1231317&r2=1231318&view=diff
==============================================================================
--- subversion/branches/revprop-packing/subversion/tests/cmdline/mergeinfo_tests.py (original)
+++ subversion/branches/revprop-packing/subversion/tests/cmdline/mergeinfo_tests.py Fri Jan 13 21:40:26 2012
@@ -68,8 +68,11 @@ def no_mergeinfo(sbox):
   "'mergeinfo' on a URL that lacks mergeinfo"
 
   sbox.build(create_wc=False)
+  sbox.simple_repo_copy('A', 'A2')
   svntest.actions.run_and_verify_mergeinfo(adjust_error_for_server_version(""),
-                                           [], sbox.repo_url, sbox.repo_url)
+                                           [],
+                                           sbox.repo_url + '/A',
+                                           sbox.repo_url + '/A2')
 
 def mergeinfo(sbox):
   "'mergeinfo' on a path with mergeinfo"
@@ -77,41 +80,65 @@ def mergeinfo(sbox):
   sbox.build()
   wc_dir = sbox.wc_dir
 
+  # make a branch 'A2'
+  sbox.simple_repo_copy('A', 'A2')  # r2
+  # make a change in branch 'A'
+  sbox.simple_mkdir('A/newdir')
+  sbox.simple_commit()  # r3
+  sbox.simple_update()
+
   # Dummy up some mergeinfo.
-  svntest.actions.run_and_verify_svn(None, None, [], 'ps', SVN_PROP_MERGEINFO,
-                                     '/:1', wc_dir)
+  svntest.actions.run_and_verify_svn(None, None, [],
+                                     'ps', SVN_PROP_MERGEINFO, '/A:3',
+                                     sbox.ospath('A2'))
   svntest.actions.run_and_verify_mergeinfo(adjust_error_for_server_version(""),
-                                           ['1'], sbox.repo_url, wc_dir)
+                                           ['3'],
+                                           sbox.repo_url + '/A',
+                                           sbox.ospath('A2'))
 
 @SkipUnless(server_has_mergeinfo)
 def explicit_mergeinfo_source(sbox):
   "'mergeinfo' with source selection"
 
+  # The idea is the target has mergeinfo pertaining to two or more different
+  # source branches and we're asking about just one of them.
+
   sbox.build()
-  wc_dir = sbox.wc_dir
-  H_path = os.path.join(wc_dir, 'A', 'D', 'H')
-  H2_path = os.path.join(wc_dir, 'A', 'D', 'H2')
-  B_url = sbox.repo_url + '/A/B'
-  B_path = os.path.join(wc_dir, 'A', 'B')
-  G_url = sbox.repo_url + '/A/D/G'
-  G_path = os.path.join(wc_dir, 'A', 'D', 'G')
-  H2_url = sbox.repo_url + '/A/D/H2'
 
-  # Make a copy, and dummy up some mergeinfo.
-  mergeinfo = '/A/B:1\n/A/D/G:1\n'
-  svntest.actions.set_prop(SVN_PROP_MERGEINFO, mergeinfo, H_path)
-  svntest.main.run_svn(None, "cp", H_path, H2_path)
-  svntest.main.run_svn(None, "ci", "-m", "r2", wc_dir)
+  def url(relpath):
+    return sbox.repo_url + '/' + relpath
+  def path(relpath):
+    return sbox.ospath(relpath)
+
+  B = 'A/B'
+
+  # make some branches
+  B2 = 'A/B2'
+  B3 = 'A/B3'
+  sbox.simple_repo_copy(B, B2)  # r2
+  sbox.simple_repo_copy(B, B3)  # r3
+  sbox.simple_update()
+
+  # make changes in the branches
+  sbox.simple_mkdir('A/B2/newdir')
+  sbox.simple_commit()  # r4
+  sbox.simple_mkdir('A/B3/newdir')
+  sbox.simple_commit()  # r5
+
+  # Put dummy mergeinfo on branch root
+  mergeinfo = '/A/B2:2-5\n/A/B3:2-5\n'
+  sbox.simple_propset(SVN_PROP_MERGEINFO, mergeinfo, B)
+  sbox.simple_commit()
 
   # Check using each of our recorded merge sources (as paths and URLs).
   svntest.actions.run_and_verify_mergeinfo(adjust_error_for_server_version(""),
-                                           ['1'], B_url, H_path)
+                                           ['2', '4'], url(B2), path(B))
   svntest.actions.run_and_verify_mergeinfo(adjust_error_for_server_version(""),
-                                           ['1'], B_path, H_path)
+                                           ['2', '4'], path(B2), path(B))
   svntest.actions.run_and_verify_mergeinfo(adjust_error_for_server_version(""),
-                                           ['1'], G_url, H_path)
+                                           ['3', '5'], url(B3), path(B))
   svntest.actions.run_and_verify_mergeinfo(adjust_error_for_server_version(""),
-                                           ['1'], G_path, H_path)
+                                           ['3', '5'], path(B3), path(B))
 
 @SkipUnless(server_has_mergeinfo)
 def mergeinfo_non_source(sbox):
@@ -624,6 +651,109 @@ def wc_target_inherits_mergeinfo_from_re
   test_svn_mergeinfo_4_way(D_COPY_path)
   test_svn_mergeinfo_4_way(subtree_wc)
 
+#----------------------------------------------------------------------
+# A test for issue 3791 'svn mergeinfo shows natural history of added
+# subtrees as eligible'.
+@Issue(3791)
+@SkipUnless(server_has_mergeinfo)
+def natural_history_is_not_eligible_nor_merged(sbox):
+  "natural history is not eligible nor merged"
+
+  sbox.build()
+  wc_dir = sbox.wc_dir
+  wc_disk, wc_status = set_up_branch(sbox)
+
+  nu_path      = os.path.join(wc_dir, 'A', 'C', 'nu')
+  A_COPY_path  = os.path.join(wc_dir, 'A_COPY')
+  nu_COPY_path = os.path.join(wc_dir, 'A_COPY', 'C', 'nu')
+
+  # r7 - Add a new file A/C/nu
+  svntest.main.file_write(nu_path, "This is the file 'nu'.\n")
+  svntest.actions.run_and_verify_svn(None, None, [], 'add', nu_path)
+  svntest.actions.run_and_verify_svn(None, None, [], 'ci',
+                                     '-m', 'Add a file', wc_dir)
+
+  # r8 - Sync merge ^/A to A_COPY
+  svntest.actions.run_and_verify_svn(None, None, [], 'merge',
+                                     sbox.repo_url + '/A', A_COPY_path)
+  svntest.actions.run_and_verify_svn(None, None, [], 'ci',
+                                     '-m', 'Add a file', wc_dir)
+
+  # r9 - Modify the file added in r7
+  svntest.main.file_write(nu_path, "Modification to file 'nu'.\n")
+  svntest.actions.run_and_verify_svn(None, None, [], 'ci',
+                                     '-m', 'Modify added file', wc_dir)
+
+  # r10 - Merge ^/A/C/nu to A_COPY/C/nu, creating subtree mergeinfo.
+  svntest.actions.run_and_verify_svn(None, None, [], 'merge',
+                                     sbox.repo_url + '/A/C/nu', nu_COPY_path)
+  svntest.actions.run_and_verify_svn(None, None, [], 'ci',
+                                     '-m', 'Add a file', wc_dir)
+  svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
+
+  # We've effectively merged everything from ^/A to A_COPY, check
+  # that svn mergeinfo -R agrees.
+  #
+  # First check if there are eligible revisions, there should be none.
+  svntest.actions.run_and_verify_mergeinfo(
+    adjust_error_for_server_version(''),
+    [], sbox.repo_url + '/A',
+    A_COPY_path, '--show-revs', 'eligible', '-R')
+
+  # Now check that all operative revisions show as merged.
+  svntest.actions.run_and_verify_mergeinfo(
+    adjust_error_for_server_version(''),
+    ['3','4','5','6','7','9'], sbox.repo_url + '/A',
+    A_COPY_path, '--show-revs', 'merged', '-R')
+
+#----------------------------------------------------------------------
+# A test for issue 4050 "'svn mergeinfo' always considers non-inheritable
+# ranges as partially merged".
+@Issue(4050)
+@SkipUnless(server_has_mergeinfo)
+def noninheritabled_mergeinfo_not_always_eligible(sbox):
+  "noninheritabled mergeinfo not always eligible"
+
+  sbox.build()
+  wc_dir = sbox.wc_dir
+
+  A_path      = os.path.join(wc_dir, 'A')
+  branch_path = os.path.join(wc_dir, 'branch')
+
+  # r2 - Branch ^/A to ^/branch.
+  svntest.main.run_svn(None, 'copy', sbox.repo_url + '/A',
+                       sbox.repo_url + '/branch', '-m', 'make a branch')
+
+  # r3 - Make prop edit to A.
+  svntest.main.run_svn(None, 'ps', 'prop', 'val', A_path)
+  svntest.main.run_svn(None, 'commit', '-m', 'file edit', wc_dir)
+  svntest.main.run_svn(None, 'up', wc_dir)
+
+  # r4 - Merge r3 from ^/A to branch at depth=empty.
+  svntest.actions.run_and_verify_svn(None, None, [], 'merge',
+                                     sbox.repo_url + '/A', branch_path,
+                                     '-c3', '--depth=empty')
+  # Forcibly set non-inheritable mergeinfo to replicate the pre-1.8 behavior,
+  # where prior to the fix for issue #4057, non-inheritable mergeinfo was
+  # unconditionally set for merges with shallow operational depths.
+  svntest.actions.run_and_verify_svn(None, None, [],
+                                     'propset', SVN_PROP_MERGEINFO,
+                                     '/A:3*\n', branch_path)  
+  svntest.main.run_svn(None, 'commit', '-m', 'shallow merge', wc_dir)
+
+  # Now check that r3 is reported as fully merged from ^/A to ^/branch
+  # and does not show up all when asking for eligible revs.
+  svntest.actions.run_and_verify_mergeinfo(
+    adjust_error_for_server_version(''),
+    ['3'], sbox.repo_url + '/A', sbox.repo_url + '/branch',
+    '--show-revs', 'merged', '-R')
+  # Likewise r3 shows up as partially eligible when asking about
+  # for --show-revs=eligible.
+  svntest.actions.run_and_verify_mergeinfo(
+    adjust_error_for_server_version(''),
+    [], sbox.repo_url + '/A', sbox.repo_url + '/branch',
+    '--show-revs', 'eligible', '-R')
+
 ########################################################################
 # Run the tests
 
@@ -639,6 +769,8 @@ test_list = [ None,
               recursive_mergeinfo,
               mergeinfo_on_pegged_wc_path,
               wc_target_inherits_mergeinfo_from_repos,
+              natural_history_is_not_eligible_nor_merged,
+              noninheritabled_mergeinfo_not_always_eligible,
              ]
 
 if __name__ == '__main__':



Mime
View raw message