You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by st...@apache.org on 2011/12/18 18:36:29 UTC

svn commit: r1220465 [11/13] - in /subversion/branches/file-handle-cache: ./ build/ build/ac-macros/ contrib/client-side/emacs/ contrib/server-side/mod_dontdothat/ notes/ subversion/bindings/javahl/tests/org/apache/subversion/javahl/ subversion/binding...

Modified: subversion/branches/file-handle-cache/subversion/tests/cmdline/dav-mirror-autocheck.sh
URL: http://svn.apache.org/viewvc/subversion/branches/file-handle-cache/subversion/tests/cmdline/dav-mirror-autocheck.sh?rev=1220465&r1=1220464&r2=1220465&view=diff
==============================================================================
--- subversion/branches/file-handle-cache/subversion/tests/cmdline/dav-mirror-autocheck.sh (original)
+++ subversion/branches/file-handle-cache/subversion/tests/cmdline/dav-mirror-autocheck.sh Sun Dec 18 17:36:24 2011
@@ -323,7 +323,7 @@ fi
 [ -r "$MOD_AUTHZ_SVN" ] \
   || fail "authz_svn_module not found, please use '--enable-shared --enable-dso --with-apxs' with your 'configure' script"
 
-export LD_LIBRARY_PATH="$ABS_BUILDDIR/subversion/libsvn_ra_neon/.libs:$ABS_BUILDDIR/subversion/libsvn_ra_local/.libs:$ABS_BUILDDIR/subversion/libsvn_ra_svn/.libs"
+export LD_LIBRARY_PATH="$ABS_BUILDDIR/subversion/libsvn_ra_neon/.libs:$ABS_BUILDDIR/subversion/libsvn_ra_local/.libs:$ABS_BUILDDIR/subversion/libsvn_ra_svn/.libs:$LD_LIBRARY_PATH"
 
 MASTER_REPOS="${MASTER_REPOS:-"$HTTPD_ROOT/master_repos"}"
 SLAVE_REPOS="${SLAVE_REPOS:-"$HTTPD_ROOT/slave_repos"}"

Modified: subversion/branches/file-handle-cache/subversion/tests/cmdline/davautocheck.sh
URL: http://svn.apache.org/viewvc/subversion/branches/file-handle-cache/subversion/tests/cmdline/davautocheck.sh?rev=1220465&r1=1220464&r2=1220465&view=diff
==============================================================================
--- subversion/branches/file-handle-cache/subversion/tests/cmdline/davautocheck.sh (original)
+++ subversion/branches/file-handle-cache/subversion/tests/cmdline/davautocheck.sh Sun Dec 18 17:36:24 2011
@@ -195,7 +195,7 @@ fi
 [ -r "$MOD_AUTHZ_SVN" ] \
   || fail "authz_svn_module not found, please use '--enable-shared --enable-dso --with-apxs' with your 'configure' script"
 
-export LD_LIBRARY_PATH="$ABS_BUILDDIR/subversion/libsvn_ra_neon/.libs:$ABS_BUILDDIR/subversion/libsvn_ra_local/.libs:$ABS_BUILDDIR/subversion/libsvn_ra_svn/.libs"
+export LD_LIBRARY_PATH="$ABS_BUILDDIR/subversion/libsvn_ra_neon/.libs:$ABS_BUILDDIR/subversion/libsvn_ra_local/.libs:$ABS_BUILDDIR/subversion/libsvn_ra_svn/.libs:$LD_LIBRARY_PATH"
 
 case "`uname`" in
   Darwin*) LDD='otool -L'

Modified: subversion/branches/file-handle-cache/subversion/tests/cmdline/depth_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/file-handle-cache/subversion/tests/cmdline/depth_tests.py?rev=1220465&r1=1220464&r2=1220465&view=diff
==============================================================================
--- subversion/branches/file-handle-cache/subversion/tests/cmdline/depth_tests.py (original)
+++ subversion/branches/file-handle-cache/subversion/tests/cmdline/depth_tests.py Sun Dec 18 17:36:24 2011
@@ -1076,7 +1076,8 @@ def diff_in_depthy_wc(sbox):
     "___________________________________________________________________\n",
     "Deleted: foo\n",
     "## -1 +0,0 ##\n",
-    "-foo-val\n"]
+    "-foo-val\n",
+    "\\ No newline at end of property\n"]
 
   os.chdir(wc_empty)
 

Modified: subversion/branches/file-handle-cache/subversion/tests/cmdline/diff_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/file-handle-cache/subversion/tests/cmdline/diff_tests.py?rev=1220465&r1=1220464&r2=1220465&view=diff
==============================================================================
--- subversion/branches/file-handle-cache/subversion/tests/cmdline/diff_tests.py (original)
+++ subversion/branches/file-handle-cache/subversion/tests/cmdline/diff_tests.py Sun Dec 18 17:36:24 2011
@@ -146,15 +146,20 @@ def make_diff_prop_header(path):
     "___________________________________________________________________\n"
   ]
 
+def make_diff_prop_val(plus_minus, pval):
+  "Return diff for prop value PVAL, with leading PLUS_MINUS (+ or -)."
+  if len(pval) > 0 and pval[-1] != '\n':
+    return [plus_minus + pval + "\n","\\ No newline at end of property\n"]
+  return [plus_minus + pval]
+  
 def make_diff_prop_deleted(pname, pval):
   """Return a property diff for deletion of property PNAME, old value PVAL.
      PVAL is a single string with no embedded newlines.  Return the result
      as a list of newline-terminated strings."""
   return [
     "Deleted: " + pname + "\n",
-    "## -1 +0,0 ##\n",
-    "-" + pval + "\n"
-  ]
+    "## -1 +0,0 ##\n"
+  ] + make_diff_prop_val("-", pval)
 
 def make_diff_prop_added(pname, pval):
   """Return a property diff for addition of property PNAME, new value PVAL.
@@ -163,8 +168,7 @@ def make_diff_prop_added(pname, pval):
   return [
     "Added: " + pname + "\n",
     "## -0,0 +1 ##\n",
-    "+" + pval + "\n"
-  ]
+  ] + make_diff_prop_val("+", pval)
 
 def make_diff_prop_modified(pname, pval1, pval2):
   """Return a property diff for modification of property PNAME, old value
@@ -173,9 +177,7 @@ def make_diff_prop_modified(pname, pval1
   return [
     "Modified: " + pname + "\n",
     "## -1 +1 ##\n",
-    "-" + pval1 + "\n",
-    "+" + pval2 + "\n"
-  ]
+  ] + make_diff_prop_val("-", pval1) + make_diff_prop_val("+", pval2)
 
 ######################################################################
 # Diff output checker
@@ -1218,6 +1220,7 @@ def diff_deleted_in_head(sbox):
 
 
 #----------------------------------------------------------------------
+@Issue(2873)
 def diff_targets(sbox):
   "select diff targets"
 
@@ -1263,30 +1266,20 @@ def diff_targets(sbox):
                                                             update_path,
                                                             add_path)
 
-  regex = 'svn: E195012: Unable to find repository location for \'.*\''
-  for line in err_output:
-    if re.match(regex, line):
-      break
-  else:
+  if check_update_a_file(diff_output) or check_add_a_file(diff_output):
     raise svntest.Failure
 
   exit_code, diff_output, err_output = svntest.main.run_svn(1,
                                                             'diff', '-r1:2',
                                                             add_path)
-  for line in err_output:
-    if re.match(regex, line):
-      break
-  else:
+
+  if not check_update_a_file(diff_output) or check_add_a_file(diff_output):
     raise svntest.Failure
 
   exit_code, diff_output, err_output = svntest.main.run_svn(
     1, 'diff', '-r1:2', '--old', parent_path, 'alpha', 'theta')
 
-  regex = 'svn: E160013: \'.*\' was not found in the repository'
-  for line in err_output:
-    if re.match(regex, line):
-      break
-  else:
+  if check_update_a_file(diff_output) or check_add_a_file(diff_output):
     raise svntest.Failure
 
   exit_code, diff_output, err_output = svntest.main.run_svn(
@@ -2863,18 +2856,18 @@ def diff_with_depth(sbox):
   A_header = make_diff_header('A', "revision 1", "working copy")
   B_header = make_diff_header(B_path, "revision 1", "working copy")
 
-  expected_empty = svntest.verify.UnorderedOutput(dot_header + diff[:6])
-  expected_files = svntest.verify.UnorderedOutput(dot_header + diff[:6]
-                                                  + iota_header + diff[7:12])
-  expected_immediates = svntest.verify.UnorderedOutput(dot_header + diff[:6]
+  expected_empty = svntest.verify.UnorderedOutput(dot_header + diff[:7])
+  expected_files = svntest.verify.UnorderedOutput(dot_header + diff[:7]
+                                                  + iota_header + diff[8:14])
+  expected_immediates = svntest.verify.UnorderedOutput(dot_header + diff[:7]
                                                        + iota_header
-                                                       + diff[7:12]
-                                                       +  A_header + diff[8:18])
-  expected_infinity = svntest.verify.UnorderedOutput(dot_header + diff[:6]
+                                                       + diff[8:14]
+                                                       + A_header + diff[15:21])
+  expected_infinity = svntest.verify.UnorderedOutput(dot_header + diff[:7]
                                                        + iota_header
-                                                       + diff[7:12]
-                                                       +  A_header + diff[8:18]
-                                                       + B_header + diff[12:])
+                                                       + diff[8:14]
+                                                       + A_header + diff[15:21]
+                                                       + B_header + diff[22:])
 
   os.chdir(sbox.wc_dir)
 
@@ -2910,18 +2903,18 @@ def diff_with_depth(sbox):
   A_header = make_diff_header('A', "revision 1", "revision 2")
   B_header = make_diff_header(B_path, "revision 1", "revision 2")
 
-  expected_empty = svntest.verify.UnorderedOutput(dot_header + diff[:6])
-  expected_files = svntest.verify.UnorderedOutput(dot_header + diff[:6]
-                                                  + iota_header + diff[7:12])
-  expected_immediates = svntest.verify.UnorderedOutput(dot_header + diff[:6]
+  expected_empty = svntest.verify.UnorderedOutput(dot_header + diff[:7])
+  expected_files = svntest.verify.UnorderedOutput(dot_header + diff[:7]
+                                                  + iota_header + diff[8:14])
+  expected_immediates = svntest.verify.UnorderedOutput(dot_header + diff[:7]
                                                        + iota_header
-                                                       + diff[7:12]
-                                                       +  A_header + diff[8:18])
+                                                       + diff[8:14]
+                                                       + A_header + diff[15:21])
   expected_infinity = svntest.verify.UnorderedOutput(dot_header + diff[:6]
                                                        + iota_header
-                                                       + diff[7:12]
-                                                       +  A_header + diff[8:18]
-                                                       + B_header + diff[12:])
+                                                       + diff[8:14]
+                                                       + A_header + diff[15:21]
+                                                       + B_header + diff[22:])
 
   # Test repos-repos diff.
   svntest.actions.run_and_verify_svn(None, expected_empty, [],
@@ -2954,10 +2947,10 @@ def diff_with_depth(sbox):
     make_diff_prop_header(".") + \
     make_diff_prop_modified("foo1", "bar1", "baz1")
 
-  expected_empty = svntest.verify.UnorderedOutput(diff_wc_repos[43:])
-  expected_files = svntest.verify.UnorderedOutput(diff_wc_repos[29:])
-  expected_immediates = svntest.verify.UnorderedOutput(diff_wc_repos[11:22]
-                                                       +diff_wc_repos[29:])
+  expected_empty = svntest.verify.UnorderedOutput(diff_wc_repos[49:])
+  expected_files = svntest.verify.UnorderedOutput(diff_wc_repos[33:])
+  expected_immediates = svntest.verify.UnorderedOutput(diff_wc_repos[13:26]
+                                                       +diff_wc_repos[33:])
   expected_infinity = svntest.verify.UnorderedOutput(diff_wc_repos[:])
 
   svntest.actions.run_and_verify_svn(None, None, [],

Modified: subversion/branches/file-handle-cache/subversion/tests/cmdline/externals_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/file-handle-cache/subversion/tests/cmdline/externals_tests.py?rev=1220465&r1=1220464&r2=1220465&view=diff
==============================================================================
--- subversion/branches/file-handle-cache/subversion/tests/cmdline/externals_tests.py (original)
+++ subversion/branches/file-handle-cache/subversion/tests/cmdline/externals_tests.py Sun Dec 18 17:36:24 2011
@@ -225,7 +225,7 @@ def change_external_expect_error(path, n
   but expect to get an error message that matches EXPECTED_ERR."""
 
   svntest.actions.set_prop('svn:externals', new_val, path,
-                           expected_err=expected_err)
+                           expected_re_string=expected_err)
 
 
 def probe_paths_exist(paths):
@@ -706,6 +706,13 @@ def disallow_dot_or_dotdot_directory_ref
   if not external_urls: external_urls = list(external_url_for.values())
   externals_value_8 = external_urls.pop() + " /foo \n"
   if not external_urls: external_urls = list(external_url_for.values())
+  if svntest.main.is_os_windows():
+    externals_value_9 = external_urls.pop() + " D:/foo\n"
+    if not external_urls: external_urls = list(external_url_for.values())
+    externals_value_10 = external_urls.pop() + " D:\\foo\n"
+    if not external_urls: external_urls = list(external_url_for.values())
+    externals_value_11 = external_urls.pop() + " D:foo\n"
+    if not external_urls: external_urls = list(external_url_for.values())
 
   set_externals_for_path_expect_error(B_path, externals_value_1)
   set_externals_for_path_expect_error(G_path, externals_value_2)
@@ -715,6 +722,10 @@ def disallow_dot_or_dotdot_directory_ref
   set_externals_for_path_expect_error(B_path, externals_value_6)
   set_externals_for_path_expect_error(G_path, externals_value_7)
   set_externals_for_path_expect_error(H_path, externals_value_8)
+  if svntest.main.is_os_windows():
+    set_externals_for_path_expect_error(B_path, externals_value_9)
+    set_externals_for_path_expect_error(B_path, externals_value_10)
+    set_externals_for_path_expect_error(B_path, externals_value_11)
 
 
 #----------------------------------------------------------------------
@@ -2098,6 +2109,547 @@ def copy_file_externals(sbox):
   actions.run_and_verify_update(wc_dir, expected_output, expected_disk,
     expected_status, None, None, None, None, None, True, wc_dir)
 
+def include_externals(sbox):
+  "commit --include-externals"
+  # svntest.factory.make(sbox, """
+  #   mkdir Z
+  #   echo 'This is the file zeta.' > Z/zeta
+  #   svn add Z
+  #   svn mkdir --parents Xpegged X/Y
+  #   svn ci
+  #   svn up
+  #   svn ps svn:externals "^/Z xZ" A/D/H
+  #   svn ps svn:externals "^/iota@1 Xpegged/xiota" wc_dir
+  #   # ^^^ manually set externals to:
+  #   #  ^/iota@1 Xpegged/xiota
+  #   #  -r1 ^/A/B/E Xpegged/xE
+  #   #  ^/A/mu X/xmu
+  #   #  ^/A/B/lambda X/Y/xlambda
+  #   #  ^/A/D/G X/xG
+  #   #  ^/A/D/H X/Y/xH
+  #   """)
+  # exit(0)
+
+  sbox.build()
+  wc_dir = sbox.wc_dir
+
+  A_D_H = os.path.join(wc_dir, 'A', 'D', 'H')
+  X = os.path.join(wc_dir, 'X')
+  X_Y = os.path.join(wc_dir, 'X', 'Y')
+  Xpegged = os.path.join(wc_dir, 'Xpegged')
+  Z = os.path.join(wc_dir, 'Z')
+  Z_zeta = os.path.join(wc_dir, 'Z', 'zeta')
+
+  # mkdir Z
+  os.makedirs(Z)
+
+  # echo 'This is the file zeta.' > Z/zeta
+  main.file_write(Z_zeta, 'This is the file zeta.\n')
+
+  # svn add Z
+  expected_stdout = verify.UnorderedOutput([
+    'A         ' + Z + '\n',
+    'A         ' + Z_zeta + '\n',
+  ])
+
+  actions.run_and_verify_svn2('OUTPUT', expected_stdout, [], 0, 'add', Z)
+
+  # svn mkdir --parents Xpegged X/Y
+  expected_stdout = verify.UnorderedOutput([
+    'A         ' + Xpegged + '\n',
+    'A         ' + X + '\n',
+    'A         ' + X_Y + '\n',
+  ])
+
+  actions.run_and_verify_svn2('OUTPUT', expected_stdout, [], 0, 'mkdir',
+    '--parents', Xpegged, X_Y)
+
+  # svn ci
+  expected_output = svntest.wc.State(wc_dir, {
+    'Z'                 : Item(verb='Adding'),
+    'Z/zeta'            : Item(verb='Adding'),
+    'X'                 : Item(verb='Adding'),
+    'X/Y'               : Item(verb='Adding'),
+    'Xpegged'           : Item(verb='Adding'),
+  })
+
+  expected_status = actions.get_virginal_state(wc_dir, 1)
+  expected_status.add({
+    'Z'                 : Item(status='  ', wc_rev='2'),
+    'Z/zeta'            : Item(status='  ', wc_rev='2'),
+    'X'                 : Item(status='  ', wc_rev='2'),
+    'X/Y'               : Item(status='  ', wc_rev='2'),
+    'Xpegged'           : Item(status='  ', wc_rev='2'),
+  })
+
+  actions.run_and_verify_commit(wc_dir, expected_output, expected_status,
+    None, wc_dir)
+
+  # svn up
+  expected_output = svntest.wc.State(wc_dir, {})
+
+  expected_disk = svntest.main.greek_state.copy()
+  expected_disk.add({
+    'Z'                 : Item(),
+    'Z/zeta'            : Item(contents="This is the file zeta.\n"),
+    'Xpegged'           : Item(),
+    'X'                 : Item(),
+    'X/Y'               : Item(),
+  })
+
+  expected_status.tweak(wc_rev='2')
+
+  actions.run_and_verify_update(wc_dir, expected_output, expected_disk,
+    expected_status, None, None, None, None, None, False, wc_dir)
+
+  # svn ps svn:externals "^/Z xZ" A/D/H
+  expected_stdout = ["property 'svn:externals' set on '" + A_D_H + "'\n"]
+
+  actions.run_and_verify_svn2('OUTPUT', expected_stdout, [], 0, 'ps',
+    'svn:externals', '^/Z xZ', A_D_H)
+
+  # svn ps svn:externals "^/iota@1 Xpegged/xiota" wc_dir
+  expected_stdout = ["property 'svn:externals' set on '" + wc_dir + "'\n"]
+
+  actions.run_and_verify_svn2('OUTPUT', expected_stdout, [], 0, 'ps',
+    'svn:externals',
+    '''
+      ^/iota@1 Xpegged/xiota
+      -r1 ^/A/B/E Xpegged/xE
+      ^/A/mu X/xmu
+      ^/A/B/lambda X/Y/xlambda
+      ^/A/D/G X/xG
+      ^/A/D/H X/Y/xH
+    ''', wc_dir)
+
+  # svntest.factory.make(sbox, prev_disk=expected_disk,
+  #                      prev_status=expected_status,
+  #                      commands = """
+  #   svn ci
+  #   svn up
+  #   echo mod >> Xpegged/xE/alpha
+  #   echo mod >> X/xmu
+  #   echo mod >> X/Y/xlambda
+  #   echo mod >> X/xG/pi
+  #   echo mod >> X/Y/xH/chi
+  #   echo mod >> X/Y/xH/xZ/zeta
+  #   svn status
+  #   # Expect no externals to be committed
+  #   svn ci
+  #   # Expect no externals to be committed, because pegged
+  #   svn ci --include-externals Xpegged
+  #   # Expect no externals to be committed, because of depth
+  #   svn ci --depth=immediates --include-externals
+  #   # Expect only unpegged externals to be committed (those in X/)
+  #   svn ci --include-externals
+  #   # ### Below, manually add:
+  #   # expected_status.tweak('A/D/H/xZ', 'Xpegged/xE', 'X/Y/xH', 'X/xG',
+  #   #                       wc_rev=None)
+  #   svn up
+  #   # new mods to check more cases
+  #   echo mod >> X/xmu
+  #   echo mod >> X/Y/xlambda
+  #   echo mod >> X/xG/pi
+  #   echo mod >> X/Y/xH/chi
+  #   echo mod >> X/Y/xH/xZ/zeta
+  #   svn status
+  #   # Expect no externals to be committed, because of depth
+  #   svn ci --include-externals --depth=empty X
+  #   # Expect only file external xmu to be committed, because of depth
+  #   svn ci --include-externals --depth=files X
+  #   svn status
+  #   # ### Below, manually add:
+  #   # expected_status.tweak('A/D/H/xZ', 'Xpegged/xE', 'X/Y/xH', 'X/xG',
+  #   #                       wc_rev=None)
+  #   svn up
+  #   echo mod >> X/xG/pi
+  #   svn status
+  #   # Expect explicit targets to be committed
+  #   svn ci X/Y/xlambda X/xG
+  #   svn status
+  #   """)
+
+  X = os.path.join(wc_dir, 'X')
+  X_xG = os.path.join(wc_dir, 'X', 'xG')
+  X_xG_pi = os.path.join(wc_dir, 'X', 'xG', 'pi')
+  X_xmu = os.path.join(wc_dir, 'X', 'xmu')
+  X_Y_xH_chi = os.path.join(wc_dir, 'X', 'Y', 'xH', 'chi')
+  X_Y_xH_xZ_zeta = os.path.join(wc_dir, 'X', 'Y', 'xH', 'xZ', 'zeta')
+  X_Y_xlambda = os.path.join(wc_dir, 'X', 'Y', 'xlambda')
+  Xpegged = os.path.join(wc_dir, 'Xpegged')
+  Xpegged_xE_alpha = os.path.join(wc_dir, 'Xpegged', 'xE', 'alpha')
+
+  # svn ci
+  expected_output = svntest.wc.State(wc_dir, {
+    ''                  : Item(verb='Sending'),
+    'A/D/H'             : Item(verb='Sending'),
+  })
+
+  expected_status.tweak('', 'A/D/H', wc_rev='3')
+
+  actions.run_and_verify_commit(wc_dir, expected_output, expected_status,
+    None, wc_dir)
+
+  # svn up
+  expected_output = svntest.wc.State(wc_dir, {
+    'X/xmu'             : Item(status='A '),
+    'X/xG/tau'          : Item(status='A '),
+    'X/xG/rho'          : Item(status='A '),
+    'X/xG/pi'           : Item(status='A '),
+    'X/Y/xH'            : Item(status=' U'),
+    'X/Y/xH/psi'        : Item(status='A '),
+    'X/Y/xH/xZ/zeta'    : Item(status='A '),
+    'X/Y/xH/chi'        : Item(status='A '),
+    'X/Y/xH/omega'      : Item(status='A '),
+    'X/Y/xlambda'       : Item(status='A '),
+    'A/D/H/xZ/zeta'     : Item(status='A '),
+    'Xpegged/xiota'     : Item(status='A '),
+    'Xpegged/xE/alpha'  : Item(status='A '),
+    'Xpegged/xE/beta'   : Item(status='A '),
+  })
+
+  expected_disk.add({
+    'Xpegged/xE'        : Item(),
+    'Xpegged/xE/beta'   : Item(contents="This is the file 'beta'.\n"),
+    'Xpegged/xE/alpha'  : Item(contents="This is the file 'alpha'.\n"),
+    'Xpegged/xiota'     : Item(contents="This is the file 'iota'.\n"),
+    'A/D/H/xZ'          : Item(),
+    'A/D/H/xZ/zeta'     : Item(contents="This is the file zeta.\n"),
+    'X/Y/xlambda'       : Item(contents="This is the file 'lambda'.\n"),
+    'X/Y/xH'            : Item(),
+    'X/Y/xH/chi'        : Item(contents="This is the file 'chi'.\n"),
+    'X/Y/xH/xZ'         : Item(),
+    'X/Y/xH/xZ/zeta'    : Item(contents="This is the file zeta.\n"),
+    'X/Y/xH/psi'        : Item(contents="This is the file 'psi'.\n"),
+    'X/Y/xH/omega'      : Item(contents="This is the file 'omega'.\n"),
+    'X/xmu'             : Item(contents="This is the file 'mu'.\n"),
+    'X/xG'              : Item(),
+    'X/xG/tau'          : Item(contents="This is the file 'tau'.\n"),
+    'X/xG/rho'          : Item(contents="This is the file 'rho'.\n"),
+    'X/xG/pi'           : Item(contents="This is the file 'pi'.\n"),
+  })
+
+  expected_status.tweak(wc_rev='3')
+  expected_status.add({
+    'A/D/H/xZ'          : Item(status='X '),
+    'Xpegged/xiota'     : Item(status='  ', wc_rev='1', switched='X'),
+    'Xpegged/xE'        : Item(status='X '),
+    'X/Y/xH'            : Item(status='X '),
+    'X/Y/xlambda'       : Item(status='  ', wc_rev='3', switched='X'),
+    'X/xmu'             : Item(status='  ', wc_rev='3', switched='X'),
+    'X/xG'              : Item(status='X '),
+  })
+  expected_status.tweak('Xpegged/xiota', wc_rev='1')
+
+  actions.run_and_verify_update(wc_dir, expected_output, expected_disk,
+    expected_status, None, None, None, None, None, False, wc_dir)
+
+  # echo mod >> Xpegged/xE/alpha
+  main.file_append(Xpegged_xE_alpha, 'mod\n')
+
+  # echo mod >> X/xmu
+  main.file_append(X_xmu, 'mod\n')
+
+  # echo mod >> X/Y/xlambda
+  main.file_append(X_Y_xlambda, 'mod\n')
+
+  # echo mod >> X/xG/pi
+  main.file_append(X_xG_pi, 'mod\n')
+
+  # echo mod >> X/Y/xH/chi
+  main.file_append(X_Y_xH_chi, 'mod\n')
+
+  # echo mod >> X/Y/xH/xZ/zeta
+  main.file_append(X_Y_xH_xZ_zeta, 'mod\n')
+
+  # svn status
+  expected_status.tweak('X/Y/xlambda', 'X/xmu', status='M ')
+
+  actions.run_and_verify_unquiet_status(wc_dir, expected_status)
+
+  # Expect no externals to be committed
+  # svn ci
+  expected_output = svntest.wc.State(wc_dir, {})
+
+  actions.run_and_verify_commit(wc_dir, expected_output, expected_status,
+    None, wc_dir)
+
+  # Expect no externals to be committed, because pegged
+  # svn ci --include-externals Xpegged
+  expected_output = svntest.wc.State(wc_dir, {})
+
+  actions.run_and_verify_commit(wc_dir, expected_output, expected_status,
+    None, '--include-externals', Xpegged)
+
+  # Expect no externals to be committed, because of depth
+  # svn ci --depth=immediates --include-externals
+  expected_output = svntest.wc.State(wc_dir, {})
+
+  actions.run_and_verify_commit(wc_dir, expected_output, expected_status,
+    None, '--depth=immediates', '--include-externals', wc_dir)
+
+  # Expect only unpegged externals to be committed (those in X/)
+  # svn ci --include-externals
+  expected_output = svntest.wc.State(wc_dir, {
+    'X/xmu'             : Item(verb='Sending'),
+    'X/Y/xlambda'       : Item(verb='Sending'),
+    'X/Y/xH/xZ/zeta'    : Item(verb='Sending'),
+    'X/Y/xH/chi'        : Item(verb='Sending'),
+    'X/xG/pi'           : Item(verb='Sending'),
+  })
+
+  expected_status.tweak(status='  ')
+  expected_status.tweak('X/Y/xlambda', 'X/xmu', wc_rev='4')
+  expected_status.tweak('X/Y/xH', 'X/xG', 'A/D/H/xZ', 'Xpegged/xE',
+    status='X ')
+
+  actions.run_and_verify_commit(wc_dir, expected_output, expected_status,
+    None, '--include-externals', wc_dir)
+
+  # svn up
+  expected_output = svntest.wc.State(wc_dir, {
+    'A/mu'              : Item(status='U '),
+    'A/D/H/chi'         : Item(status='U '),
+    'A/D/H/xZ/zeta'     : Item(status='U '),
+    'A/D/G/pi'          : Item(status='U '),
+    'A/B/lambda'        : Item(status='U '),
+    'Z/zeta'            : Item(status='U '),
+  })
+
+  expected_disk.tweak('Xpegged/xE/alpha',
+    contents="This is the file 'alpha'.\nmod\n")
+  expected_disk.tweak('A/D/H/chi', 'X/Y/xH/chi',
+    contents="This is the file 'chi'.\nmod\n")
+  expected_disk.tweak('A/D/H/xZ/zeta', 'X/Y/xH/xZ/zeta', 'Z/zeta',
+    contents='This is the file zeta.\nmod\n')
+  expected_disk.tweak('A/D/G/pi', 'X/xG/pi',
+    contents="This is the file 'pi'.\nmod\n")
+  expected_disk.tweak('A/mu', 'X/xmu',
+    contents="This is the file 'mu'.\nmod\n")
+  expected_disk.tweak('A/B/lambda', 'X/Y/xlambda',
+    contents="This is the file 'lambda'.\nmod\n")
+
+  expected_status.tweak(wc_rev='4')
+  expected_status.tweak('Xpegged/xiota', wc_rev='1')
+  expected_status.tweak('A/D/H/xZ', 'Xpegged/xE', 'X/Y/xH', 'X/xG',
+                        wc_rev=None)
+
+  actions.run_and_verify_update(wc_dir, expected_output, expected_disk,
+    expected_status, None, None, None, None, None, False, wc_dir)
+
+  # new mods to check more cases
+  # echo mod >> X/xmu
+  main.file_append(X_xmu, 'mod\n')
+
+  # echo mod >> X/Y/xlambda
+  main.file_append(X_Y_xlambda, 'mod\n')
+
+  # echo mod >> X/xG/pi
+  main.file_append(X_xG_pi, 'mod\n')
+
+  # echo mod >> X/Y/xH/chi
+  main.file_append(X_Y_xH_chi, 'mod\n')
+
+  # echo mod >> X/Y/xH/xZ/zeta
+  main.file_append(X_Y_xH_xZ_zeta, 'mod\n')
+
+  # svn status
+  expected_status.tweak('X/Y/xlambda', 'X/xmu', status='M ')
+
+  actions.run_and_verify_unquiet_status(wc_dir, expected_status)
+
+  # Expect no externals to be committed, because of depth
+  # svn ci --include-externals --depth=empty X
+  expected_output = svntest.wc.State(wc_dir, {})
+
+  actions.run_and_verify_commit(wc_dir, expected_output, expected_status,
+    None, '--include-externals', '--depth=empty', X)
+
+  # Expect only file external xmu to be committed, because of depth
+  # svn ci --include-externals --depth=files X
+  expected_output = svntest.wc.State(wc_dir, {
+    'X/xmu'             : Item(verb='Sending'),
+  })
+
+  expected_status.tweak(status='  ')
+  expected_status.tweak('X/xmu', wc_rev='5')
+  expected_status.tweak('X/Y/xlambda', status='M ')
+  expected_status.tweak('X/Y/xH', 'X/xG', 'A/D/H/xZ', 'Xpegged/xE',
+    status='X ')
+
+  actions.run_and_verify_commit(wc_dir, expected_output, expected_status,
+    None, '--include-externals', '--depth=files', X)
+
+  # svn status
+  actions.run_and_verify_unquiet_status(wc_dir, expected_status)
+
+  # svn up
+  expected_output = svntest.wc.State(wc_dir, {
+    'A/mu'              : Item(status='U '),
+  })
+
+  expected_disk.tweak('A/mu', 'X/xmu',
+    contents="This is the file 'mu'.\nmod\nmod\n")
+  expected_disk.tweak('X/Y/xlambda',
+    contents="This is the file 'lambda'.\nmod\nmod\n")
+  expected_disk.tweak('X/Y/xH/chi',
+    contents="This is the file 'chi'.\nmod\nmod\n")
+  expected_disk.tweak('X/Y/xH/xZ/zeta',
+    contents='This is the file zeta.\nmod\nmod\n')
+  expected_disk.tweak('X/xG/pi',
+    contents="This is the file 'pi'.\nmod\nmod\n")
+
+  expected_status.tweak(wc_rev='5')
+  expected_status.tweak('Xpegged/xiota', wc_rev='1')
+  expected_status.tweak('A/D/H/xZ', 'Xpegged/xE', 'X/Y/xH', 'X/xG',
+                        wc_rev=None)
+
+  actions.run_and_verify_update(wc_dir, expected_output, expected_disk,
+    expected_status, None, None, None, None, None, False, wc_dir)
+
+  # echo mod >> X/xG/pi
+  main.file_append(X_xG_pi, 'mod\n')
+
+  # svn status
+  actions.run_and_verify_unquiet_status(wc_dir, expected_status)
+
+  # Expect explicit targets to be committed
+  # svn ci X/Y/xlambda X/xG
+  expected_output = svntest.wc.State(wc_dir, {
+    'X/Y/xlambda'       : Item(verb='Sending'),
+    'X/xG/pi'           : Item(verb='Sending'),
+  })
+
+  expected_status.tweak(status='  ')
+  expected_status.tweak('X/Y/xlambda', wc_rev='6')
+  expected_status.tweak('X/Y/xH', 'X/xG', 'A/D/H/xZ', 'Xpegged/xE',
+    status='X ')
+
+  actions.run_and_verify_commit(wc_dir, expected_output, expected_status,
+    None, X_Y_xlambda, X_xG)
+
+  # svn status
+  actions.run_and_verify_unquiet_status(wc_dir, expected_status)
+
+
+@XFail()
+def include_immediate_dir_externals(sbox):
+  "commit --include-externals --depth=immediates"
+  # See also comment inside svn_client_commit6().
+
+  #   svntest.factory.make(sbox,"""
+  #     svn mkdir X
+  #     svn ci
+  #     svn up
+  #     svn ps svn:externals "^/A/B/E X/XE" wc_dir
+  #     svn ci
+  #     svn up
+  # 
+  #     svn ps some change X/XE
+  #     echo mod >> X/XE/alpha
+  # 
+  #     svn st X/XE
+  #     # Expect only the propset on X/XE to be committed.
+  #     # Should be like 'svn commit --include-externals --depth=empty X/XE'.
+  #     svn commit --include-externals --depth=immediates X
+  #     """)
+
+  sbox.build()
+  wc_dir = sbox.wc_dir
+
+  X = os.path.join(wc_dir, 'X')
+  X_XE = os.path.join(wc_dir, 'X', 'XE')
+  X_XE_alpha = os.path.join(wc_dir, 'X', 'XE', 'alpha')
+
+  # svn mkdir X
+  expected_stdout = ['A         ' + X + '\n']
+
+  actions.run_and_verify_svn2('OUTPUT', expected_stdout, [], 0, 'mkdir', X)
+
+  # svn ci
+  expected_output = svntest.wc.State(wc_dir, {
+    'X'                 : Item(verb='Adding'),
+  })
+
+  expected_status = actions.get_virginal_state(wc_dir, 1)
+  expected_status.add({
+    'X'                 : Item(status='  ', wc_rev='2'),
+  })
+
+  actions.run_and_verify_commit(wc_dir, expected_output, expected_status,
+    None, wc_dir)
+
+  # svn up
+  expected_output = svntest.wc.State(wc_dir, {})
+
+  expected_disk = svntest.main.greek_state.copy()
+  expected_disk.add({
+    'X'                 : Item(),
+  })
+
+  expected_status.tweak(wc_rev='2')
+
+  actions.run_and_verify_update(wc_dir, expected_output, expected_disk,
+    expected_status, None, None, None, None, None, False, wc_dir)
+
+  # svn ps svn:externals "^/A/B/E X/XE" wc_dir
+  expected_stdout = ["property 'svn:externals' set on '" + wc_dir + "'\n"]
+
+  actions.run_and_verify_svn2('OUTPUT', expected_stdout, [], 0, 'ps',
+    'svn:externals', '^/A/B/E X/XE', wc_dir)
+
+  # svn ci
+  expected_output = svntest.wc.State(wc_dir, {
+    ''                  : Item(verb='Sending'),
+  })
+
+  expected_status.tweak('', wc_rev='3')
+
+  actions.run_and_verify_commit(wc_dir, expected_output, expected_status,
+    None, wc_dir)
+
+  # svn up
+  expected_output = svntest.wc.State(wc_dir, {
+    'X/XE/alpha'        : Item(status='A '),
+    'X/XE/beta'         : Item(status='A '),
+  })
+
+  expected_disk.add({
+    'X/XE'              : Item(),
+    'X/XE/alpha'        : Item(contents="This is the file 'alpha'.\n"),
+    'X/XE/beta'         : Item(contents="This is the file 'beta'.\n"),
+  })
+
+  expected_status.tweak(wc_rev='3')
+  expected_status.add({
+    'X/XE'              : Item(status='X '),
+  })
+
+  actions.run_and_verify_update(wc_dir, expected_output, expected_disk,
+    expected_status, None, None, None, None, None, False, wc_dir)
+
+  # svn ps some change X/XE
+  expected_stdout = ["property 'some' set on '" + X_XE + "'\n"]
+
+  actions.run_and_verify_svn2('OUTPUT', expected_stdout, [], 0, 'ps', 'some',
+    'change', X_XE)
+
+  # echo mod >> X/XE/alpha
+  main.file_append(X_XE_alpha, 'mod\n')
+
+  # svn st X/XE
+  actions.run_and_verify_unquiet_status(wc_dir, expected_status)
+
+  # Expect only the propset on X/XE to be committed.
+  # Should be like 'svn commit --include-externals --depth=empty X/XE'.
+  # svn commit --include-externals --depth=immediates X
+  expected_output = svntest.wc.State(wc_dir, {
+    'X/XE'              : Item(verb='Sending'),
+  })
+
+  actions.run_and_verify_commit(wc_dir, expected_output, expected_status,
+    None, '--include-externals', '--depth=immediates', X)
+
 
 ########################################################################
 # Run the tests
@@ -2140,6 +2692,8 @@ test_list = [ None,
               file_externals_different_repos,
               file_external_in_unversioned,
               copy_file_externals,
+              include_externals,
+              include_immediate_dir_externals,
              ]
 
 if __name__ == '__main__':

Modified: subversion/branches/file-handle-cache/subversion/tests/cmdline/getopt_tests_data/svn_help_log_switch_stdout
URL: http://svn.apache.org/viewvc/subversion/branches/file-handle-cache/subversion/tests/cmdline/getopt_tests_data/svn_help_log_switch_stdout?rev=1220465&r1=1220464&r2=1220465&view=diff
==============================================================================
--- subversion/branches/file-handle-cache/subversion/tests/cmdline/getopt_tests_data/svn_help_log_switch_stdout (original)
+++ subversion/branches/file-handle-cache/subversion/tests/cmdline/getopt_tests_data/svn_help_log_switch_stdout Sun Dec 18 17:36:24 2011
@@ -38,6 +38,10 @@ usage: 1. log [PATH][@REV]
     svn log http://www.example.com/repo/project foo.c bar.c
     svn log http://www.example.com/repo/project@50 foo.c bar.c
 
+    This command shows the log entry for the revision the branch
+    ^/branches/foo was created in:
+      svn log --stop-on-copy --limit 1 -r0:HEAD ^/branches/foo
+
 Valid options:
   -r [--revision] ARG      : ARG (some commands also take ARG1:ARG2 range)
                              A revision argument can be one of:
@@ -157,9 +161,10 @@ Valid options:
   --ignore-ancestry        : ignore ancestry when calculating merges
   --force                  : force operation to run
   --accept ARG             : specify automatic conflict resolution action
-                             ('postpone', 'base', 'mine-conflict',
+                             ('postpone', 'working', 'base', 'mine-conflict',
                              'theirs-conflict', 'mine-full', 'theirs-full',
                              'edit', 'launch')
+                             (shorthand: 'p', 'mc', 'tc', 'mf', 'tf', 'e', 'l')
 
 Global options:
   --username ARG           : specify a username ARG

Modified: subversion/branches/file-handle-cache/subversion/tests/cmdline/input_validation_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/file-handle-cache/subversion/tests/cmdline/input_validation_tests.py?rev=1220465&r1=1220464&r2=1220465&view=diff
==============================================================================
--- subversion/branches/file-handle-cache/subversion/tests/cmdline/input_validation_tests.py (original)
+++ subversion/branches/file-handle-cache/subversion/tests/cmdline/input_validation_tests.py Sun Dec 18 17:36:24 2011
@@ -151,13 +151,18 @@ def invalid_log_targets(sbox):
 def invalid_merge_args(sbox):
   "invalid arguments for 'merge'"
   sbox.build(read_only=True)
-  run_and_verify_svn_in_wc(sbox, "svn: E195002: A working copy merge source needs "
-                           "an explicit revision", 'merge', 'iota', '^/')
-  for (src, target) in [('iota@HEAD', '^/'), ('iota@BASE', 'file://')]:
-    run_and_verify_svn_in_wc(sbox, "svn: E205000: Merge sources must both be either "
-                             "paths or URLs", 'merge', src, target)
+  for args in [('iota', 'A/mu@HEAD'),
+               ('iota@BASE', 'A/mu@HEAD')]:
+    run_and_verify_svn_in_wc(sbox, "svn: E195002: .* working copy .* revision",
+                             'merge', *args)
+  for args in [(sbox.repo_url, 'A@1', 'A'),
+               ('^/A', 'A@HEAD', 'A'),
+               ('A@HEAD', '^/A', 'A'),
+               ('A@HEAD', '^/A')]:
+    run_and_verify_svn_in_wc(sbox, "svn: E205000: Merge sources must both "
+                             "be either paths or URLs", 'merge', *args)
   run_and_verify_svn_in_wc(sbox, "svn: E155010: Path '.*' does not exist",
-                           'merge', 'iota@BASE', 'iota@HEAD', 'nonexistent')
+                           'merge', '^/@0', '^/@1', 'nonexistent')
   run_and_verify_svn_in_wc(sbox, "svn: E205000: Too many arguments given",
                           'merge', '-c42', '^/A/B', '^/A/C', 'iota')
   run_and_verify_svn_in_wc(sbox, "svn: E205000: Cannot specify a revision range with" +

Modified: subversion/branches/file-handle-cache/subversion/tests/cmdline/log_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/file-handle-cache/subversion/tests/cmdline/log_tests.py?rev=1220465&r1=1220464&r2=1220465&view=diff
==============================================================================
--- subversion/branches/file-handle-cache/subversion/tests/cmdline/log_tests.py (original)
+++ subversion/branches/file-handle-cache/subversion/tests/cmdline/log_tests.py Sun Dec 18 17:36:24 2011
@@ -2093,6 +2093,45 @@ def merge_sensitive_log_copied_path_inhe
   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)
+
+  for line in output:
+    if line.startswith('Index:'):
+      break
+  else:
+    raise SVNLogParseError("no diffs found in log output")
+
+
 ########################################################################
 # Run the tests
 
@@ -2133,6 +2172,7 @@ test_list = [ None,
               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/file-handle-cache/subversion/tests/cmdline/merge_authz_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/file-handle-cache/subversion/tests/cmdline/merge_authz_tests.py?rev=1220465&r1=1220464&r2=1220465&view=diff
==============================================================================
--- subversion/branches/file-handle-cache/subversion/tests/cmdline/merge_authz_tests.py (original)
+++ subversion/branches/file-handle-cache/subversion/tests/cmdline/merge_authz_tests.py Sun Dec 18 17:36:24 2011
@@ -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/file-handle-cache/subversion/tests/cmdline/merge_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/file-handle-cache/subversion/tests/cmdline/merge_tests.py?rev=1220465&r1=1220464&r2=1220465&view=diff
==============================================================================
--- subversion/branches/file-handle-cache/subversion/tests/cmdline/merge_tests.py (original)
+++ subversion/branches/file-handle-cache/subversion/tests/cmdline/merge_tests.py Sun Dec 18 17:36:24 2011
@@ -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")
@@ -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"),
@@ -12509,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
@@ -12558,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.
@@ -12567,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)
 
@@ -12594,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)
 
@@ -13052,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, [],
@@ -13068,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)
 
@@ -13131,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.
@@ -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',
@@ -16822,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"
@@ -16849,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',
@@ -16886,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 '),
     })
@@ -16935,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,
@@ -17111,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
 
@@ -17239,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/file-handle-cache/subversion/tests/cmdline/merge_tree_conflict_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/file-handle-cache/subversion/tests/cmdline/merge_tree_conflict_tests.py?rev=1220465&r1=1220464&r2=1220465&view=diff
==============================================================================
--- subversion/branches/file-handle-cache/subversion/tests/cmdline/merge_tree_conflict_tests.py (original)
+++ subversion/branches/file-handle-cache/subversion/tests/cmdline/merge_tree_conflict_tests.py Sun Dec 18 17:36:24 2011
@@ -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)
@@ -1975,15 +1929,10 @@ def merge_replace_on_del_fails(sbox):
 
   # Sync merge ^/A to branch
   svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
-  expected_stdout = verify.UnorderedOutput([
-    "--- Merging r2 through r4 into '" + branch_path + "':\n",
-    '   C ' + C_branch_path + '\n',
-    "--- Recording mergeinfo for merge of r2 through r4 into '" \
-    + branch_path + "':\n",
-    ' U   ' + branch_path + '\n',
-    'Summary of conflicts:\n',
-    '  Tree conflicts: 1\n',
-    ])
+  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