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 2013/02/04 21:48:13 UTC

svn commit: r1442344 [35/39] - in /subversion/branches/fsfs-format7: ./ build/ build/ac-macros/ build/generator/ build/generator/templates/ build/win32/ contrib/client-side/emacs/ contrib/server-side/fsfsfixer/fixer/ contrib/server-side/svncutter/ doc/...

Modified: subversion/branches/fsfs-format7/subversion/tests/cmdline/update_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-format7/subversion/tests/cmdline/update_tests.py?rev=1442344&r1=1442343&r2=1442344&view=diff
==============================================================================
--- subversion/branches/fsfs-format7/subversion/tests/cmdline/update_tests.py (original)
+++ subversion/branches/fsfs-format7/subversion/tests/cmdline/update_tests.py Mon Feb  4 20:48:05 2013
@@ -417,10 +417,7 @@ def update_missing(sbox):
   svntest.main.safe_rmtree(H_path)
 
   # In single-db mode all missing items will just be restored
-  if svntest.main.wc_is_singledb(wc_dir):
-    A_or_Restored = Item(verb='Restored')
-  else:
-    A_or_Restored = Item(status='A ')
+  A_or_Restored = Item(verb='Restored')
 
   # Create expected output tree for an update of the missing items by name
   expected_output = svntest.wc.State(wc_dir, {
@@ -993,21 +990,16 @@ def update_replace_dir(sbox):
                                         expected_status)
 
   # Update to revision 1 replaces the directory
-  ### I can't get this to work :-(
-  #expected_output = svntest.wc.State(wc_dir, {
-  #  'A/B/F'       : Item(verb='Adding'),
-  #  'A/B/F'       : Item(verb='Deleting'),
-  #  })
-  #expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
-  #svntest.actions.run_and_verify_update(wc_dir,
-  #                                      expected_output,
-  #                                      expected_disk,
-  #                                      expected_status,
-  #                                      None, None, None, None, None, 0,
-  #                                      '-r', '1', wc_dir)
-
-  # Update to revision 1 replaces the directory
-  svntest.actions.run_and_verify_svn(None, None, [], 'up', '-r', '1', wc_dir)
+  expected_output = svntest.wc.State(wc_dir, {
+    'A/B/F' : Item(status='A ', prev_status='D '),
+  })
+  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
+  svntest.actions.run_and_verify_update(wc_dir,
+                                        expected_output,
+                                        expected_disk,
+                                        expected_status,
+                                        None, None, None, None, None, 0,
+                                        '-r', '1', wc_dir)
 
   expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
 
@@ -1167,20 +1159,15 @@ def update_deleted_missing_dir(sbox):
 
   # Create expected output tree for an update of the missing items by name
   expected_output = svntest.wc.State(wc_dir, {
-    'A/B/E' : Item(status='D '),
-    'A/D/H' : Item(status='D '),
-    })
-
-  # In single-db mode the missing items are restored before the update
-  if svntest.main.wc_is_singledb(wc_dir):
-    expected_output.add({
-      'A/D/H/psi'         : Item(verb='Restored'),
-      'A/D/H/omega'       : Item(verb='Restored'),
-      'A/D/H/chi'         : Item(verb='Restored'),
-      'A/B/E/beta'        : Item(verb='Restored'),
-      'A/B/E/alpha'       : Item(verb='Restored')
-      # A/B/E and A/D/H are also restored, but are then overriden by the delete
-    })
+    'A/D/H/psi'         : Item(verb='Restored'),
+    'A/D/H/omega'       : Item(verb='Restored'),
+    'A/D/H/chi'         : Item(verb='Restored'),
+    'A/B/E/beta'        : Item(verb='Restored'),
+    'A/B/E/alpha'       : Item(verb='Restored'),
+    # A/B/E and A/D/H are also restored, but are then overriden by the delete
+    'A/B/E'             : Item(status='D ', prev_verb='Restored'),
+    'A/D/H'             : Item(status='D ', prev_verb='Restored'),
+  })
 
   # Create expected disk tree for the update.
   expected_disk = svntest.main.greek_state.copy()
@@ -1260,20 +1247,14 @@ def another_hudson_problem(sbox):
   # Update missing directory to receive the delete, this should mark G
   # as 'deleted' and should not alter gamma's entry.
 
-  if not svntest.main.wc_is_singledb(wc_dir):
-    expected_output = ["Updating '%s' ...\n" % (G_path),
-                       'D    '+G_path+'\n',
-                       'Updated to revision 3.\n',
-                       ]
-  else:
-    expected_output = ["Updating '%s':\n" % (G_path),
-                       'Restored \'' + G_path + '\'\n',
-                       'Restored \'' + G_path + os.path.sep + 'pi\'\n',
-                       'Restored \'' + G_path + os.path.sep + 'rho\'\n',
-                       'Restored \'' + G_path + os.path.sep + 'tau\'\n',
-                       'D    '+G_path+'\n',
-                       'Updated to revision 3.\n',
-                       ]
+  expected_output = ["Updating '%s':\n" % (G_path),
+                     'Restored \'' + G_path + '\'\n',
+                     'Restored \'' + G_path + os.path.sep + 'pi\'\n',
+                     'Restored \'' + G_path + os.path.sep + 'rho\'\n',
+                     'Restored \'' + G_path + os.path.sep + 'tau\'\n',
+                     'D    '+G_path+'\n',
+                     'Updated to revision 3.\n',
+                    ]
 
   # Sigh, I can't get run_and_verify_update to work (but not because
   # of issue 919 as far as I can tell)
@@ -1675,105 +1656,7 @@ def update_to_future_add(sbox):
                                         expected_disk,
                                         None, None,
                                         None, None, None, None, 0,
-                                        A_path);
-
-#----------------------------------------------------------------------
-
-def nested_in_read_only(sbox):
-  "update a nested wc in a read-only wc"
-
-  sbox.build()
-  wc_dir = sbox.wc_dir
-
-  if svntest.main.wc_is_singledb(wc_dir):
-    raise svntest.Skip('Unsupported in single-db')
-
-  # Delete/commit a file
-  alpha_path = sbox.ospath('A/B/E/alpha')
-  svntest.actions.run_and_verify_svn(None, None, [], 'rm', alpha_path)
-
-  expected_output = svntest.wc.State(wc_dir, {
-    'A/B/E/alpha' : Item(verb='Deleting'),
-    })
-
-  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
-  expected_status.remove('A/B/E/alpha')
-
-  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
-                                        expected_status, None, wc_dir)
-
-  svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
-
-  expected_status.tweak(wc_rev=2)
-
-  svntest.actions.run_and_verify_status(wc_dir, expected_status)
-
-  # Delete/commit a directory that used to contain the deleted file
-  B_path = sbox.ospath('A/B')
-  svntest.actions.run_and_verify_svn(None, None, [], 'rm', B_path)
-
-  expected_output = svntest.wc.State(wc_dir, {
-    'A/B' : Item(verb='Deleting'),
-    })
-
-  expected_status.remove('A/B', 'A/B/lambda', 'A/B/E', 'A/B/E/beta', 'A/B/F')
-
-  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
-                                        expected_status, None, wc_dir)
-
-  svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
-
-  expected_status.tweak(wc_rev=3)
-
-  svntest.actions.run_and_verify_status(wc_dir, expected_status)
-
-  # Replace the deleted directory with a new checkout of an old
-  # version of the directory, this gives it a "plausible" URL that
-  # could be part of the containing wc
-  B_url = sbox.repo_url + '/A/B'
-  svntest.actions.run_and_verify_svn(None, None, [],
-                                     'checkout', '-r', '1', B_url + "@1",
-                                     B_path)
-
-  expected_status = svntest.wc.State(B_path, {
-    ''           : Item(),
-    'lambda'     : Item(),
-    'E'          : Item(),
-    'E/alpha'    : Item(),
-    'E/beta'     : Item(),
-    'F'          : Item(),
-    })
-  expected_status.tweak(wc_rev=1, status='  ')
-
-  svntest.actions.run_and_verify_status(B_path, expected_status)
-
-  # Make enclosing wc read only
-  os.chmod(os.path.join(wc_dir, 'A', svntest.main.get_admin_name()), 0555)
-
-  try:
-    # Update of nested wc should still work
-    expected_output = svntest.wc.State(B_path, {
-      'E/alpha' : Item(status='D '),
-      })
-
-    expected_disk = wc.State('', {
-      'lambda'  : wc.StateItem("This is the file 'lambda'.\n"),
-      'E'       : wc.StateItem(),
-      'E/beta'  : wc.StateItem("This is the file 'beta'.\n"),
-      'F'       : wc.StateItem(),
-      })
-
-    expected_status.remove('E/alpha')
-    expected_status.tweak(wc_rev=2)
-
-    svntest.actions.run_and_verify_update(B_path,
-                                          expected_output,
-                                          expected_disk,
-                                          expected_status,
-                                          None, None, None, None, None, 0,
-                                          '-r', '2', B_path)
-  finally:
-    os.chmod(os.path.join(wc_dir, 'A', svntest.main.get_admin_name()), 0777)
+                                        A_path)
 
 #----------------------------------------------------------------------
 
@@ -1839,6 +1722,8 @@ def conflict_markers_matching_eol(sbox):
 
   mu_path = sbox.ospath('A/mu')
 
+  # CRLF is a string that will match a CRLF sequence read from a text file.
+  # ### On Windows, we assume CRLF will be read as LF, so it's a poor test.
   if os.name == 'nt':
     crlf = '\n'
   else:
@@ -1970,6 +1855,8 @@ def update_eolstyle_handling(sbox):
 
   mu_path = sbox.ospath('A/mu')
 
+  # CRLF is a string that will match a CRLF sequence read from a text file.
+  # ### On Windows, we assume CRLF will be read as LF, so it's a poor test.
   if os.name == 'nt':
     crlf = '\n'
   else:
@@ -2464,9 +2351,9 @@ def update_wc_on_windows_drive(sbox):
                                        sbox.repo_url, wc_dir)
 
     # Make some local modifications
-    mu_path = os.path.join(wc_dir, 'A', 'mu')
+    mu_path = os.path.join(wc_dir, 'A', 'mu').replace(os.sep, '/')
     svntest.main.file_append(mu_path, '\nAppended text for mu')
-    zeta_path = os.path.join(wc_dir, 'zeta')
+    zeta_path = os.path.join(wc_dir, 'zeta').replace(os.sep, '/')
     svntest.main.file_append(zeta_path, "This is the file 'zeta'\n")
     svntest.main.run_svn(None, 'add', zeta_path)
 
@@ -2487,7 +2374,7 @@ def update_wc_on_windows_drive(sbox):
                                           wc_dir, zeta_path)
 
     # Non recursive commit
-    dir1_path = os.path.join(wc_dir, 'dir1')
+    dir1_path = os.path.join(wc_dir, 'dir1').replace(os.sep, '/')
     os.mkdir(dir1_path)
     svntest.main.run_svn(None, 'add', '-N', dir1_path)
     file1_path = os.path.join(dir1_path, 'file1')
@@ -2556,10 +2443,11 @@ def update_wc_on_windows_drive(sbox):
     expected_disk.tweak('A/mu', contents = expected_disk.desc['A/mu'].contents
                         + '\nAppended text for mu')
 
+    # Use .old_tree() for status to avoid the entries validation
     svntest.actions.run_and_verify_update(wc_dir,
                                           expected_output,
                                           expected_disk,
-                                          expected_status)
+                                          expected_status.old_tree())
 
   finally:
     os.chdir(was_cwd)
@@ -3622,12 +3510,12 @@ def update_copied_from_replaced_and_chan
   sbox.build()
   wc_dir = sbox.wc_dir
 
-  fn1_relpath = os.path.join('A', 'B', 'E', 'aardvark')
-  fn2_relpath = os.path.join('A', 'B', 'E', 'alpha')
-  fn3_relpath = os.path.join('A', 'B', 'E', 'beta')
-  fn1_path = os.path.join(wc_dir, fn1_relpath)
-  fn2_path = os.path.join(wc_dir, fn2_relpath)
-  fn3_path = os.path.join(wc_dir, fn3_relpath)
+  fn1_relpath = 'A/B/E/aardvark'
+  fn2_relpath = 'A/B/E/alpha'
+  fn3_relpath = 'A/B/E/beta'
+  fn1_path = sbox.ospath(fn1_relpath)
+  fn2_path = sbox.ospath(fn2_relpath)
+  fn3_path = sbox.ospath(fn3_relpath)
 
   # Move fn2 to fn1
   svntest.actions.run_and_verify_svn(None, None, [],
@@ -3676,7 +3564,7 @@ def update_copied_from_replaced_and_chan
   # Go back to r1.
   expected_output = svntest.wc.State(wc_dir, {
     fn1_relpath: Item(status='D '),
-    fn2_relpath: Item(status='A '), # though actually should be D and A
+    fn2_relpath: Item(status='A ', prev_status='D '), # D then A
     fn3_relpath: Item(status='A '),
     })
 
@@ -3694,7 +3582,7 @@ def update_copied_from_replaced_and_chan
   # And back up to 3 again.
   expected_output = svntest.wc.State(wc_dir, {
     fn1_relpath: Item(status='A '),
-    fn2_relpath: Item(status='A '), # though actually should be D and A
+    fn2_relpath: Item(status='A ', prev_status='D '), # D then A
     fn3_relpath: Item(status='D '),
     })
 
@@ -3966,6 +3854,7 @@ def update_accept_conflicts(sbox):
                                         % (pi_path_backup)],
                                       "system(.*) returned.*", 0,
                                       'update', '--accept=edit',
+                                      '--force-interactive',
                                       pi_path_backup)
 
   # rho: --accept=launch
@@ -3978,6 +3867,7 @@ def update_accept_conflicts(sbox):
                                       '  Text conflicts: 1\n'],
                                      [],
                                      'update', '--accept=launch',
+                                     '--force-interactive',
                                      rho_path_backup)
 
   # Set the expected disk contents for the test
@@ -4053,6 +3943,11 @@ def update_accept_conflicts(sbox):
 # WC update editor flush and run incomplete logs and lead to WC
 # corruption, detectable by another update command.
 
+# FIXME: With issue #4280 fixed and this test using --force-interactive,
+#        the test driver can no longer redirect terminal input to cause
+#        an EOF. Consequently, skip this test so that it does not hang
+#        the test suite.
+@Skip()
 def eof_in_interactive_conflict_resolver(sbox):
   "eof in interactive resolution can't break wc"
 
@@ -4105,10 +4000,11 @@ interactive-conflicts = true
   # Modify iota differently and try to update *with the interactive
   # resolver*.  ### The parser won't go so well with the output
   svntest.main.file_append(iota_path, "Local mods to r1 text.\n")
-  svntest.actions.run_and_verify_update(wc_dir, None, None, None,
-                                        "Can't read stdin: End of file found",
-                                        None, None, None, None, 1,
-                                        wc_dir, '--config-dir', config_dir)
+  svntest.actions.run_and_verify_update(
+    wc_dir, None, None, None,
+    "End of file while reading from terminal",
+    None, None, None, None, 1,
+    wc_dir, '--force-interactive', '--config-dir', config_dir)
 
   # Now update -r1 again.  Hopefully we don't get a checksum error!
   expected_output = svntest.wc.State(wc_dir, {
@@ -4328,10 +4224,9 @@ def tree_conflicts_on_update_1_1(sbox):
   })
 
   expected_disk = disk_empty_dirs.copy()
-  if svntest.main.wc_is_singledb(sbox.wc_dir):
-    expected_disk.remove('D/D1', 'DF/D1', 'DD/D1', 'DD/D1/D2',
-                         'DDF/D1', 'DDF/D1/D2',
-                         'DDD/D1', 'DDD/D1/D2', 'DDD/D1/D2/D3')
+  expected_disk.remove('D/D1', 'DF/D1', 'DD/D1', 'DD/D1/D2',
+                       'DDF/D1', 'DDF/D1/D2',
+                       'DDD/D1', 'DDD/D1/D2', 'DDD/D1/D2/D3')
 
   # The files delta, epsilon, and zeta are incoming additions, but since
   # they are all within locally deleted trees they should also be schedule
@@ -4349,37 +4244,37 @@ def tree_conflicts_on_update_1_1(sbox):
   expected_info = {
     'F/alpha' : {
       'Tree conflict' :
-        '^local delete, incoming edit upon update'
+        '^local file delete, incoming file edit upon update'
         + ' Source  left: .file.*/F/alpha@2'
         + ' Source right: .file.*/F/alpha@3$',
     },
     'DF/D1' : {
       'Tree conflict' :
-        '^local delete, incoming edit upon update'
+        '^local dir delete, incoming dir edit upon update'
         + ' Source  left: .dir.*/DF/D1@2'
         + ' Source right: .dir.*/DF/D1@3$',
     },
     'DDF/D1' : {
       'Tree conflict' :
-        '^local delete, incoming edit upon update'
+        '^local dir delete, incoming dir edit upon update'
         + ' Source  left: .dir.*/DDF/D1@2'
         + ' Source right: .dir.*/DDF/D1@3$',
     },
     'D/D1' : {
       'Tree conflict' :
-        '^local delete, incoming edit upon update'
+        '^local dir delete, incoming dir edit upon update'
         + ' Source  left: .dir.*/D/D1@2'
         + ' Source right: .dir.*/D/D1@3$',
     },
     'DD/D1' : {
       'Tree conflict' :
-        '^local delete, incoming edit upon update'
+        '^local dir delete, incoming dir edit upon update'
         + ' Source  left: .dir.*/DD/D1@2'
         + ' Source right: .dir.*/DD/D1@3$',
     },
     'DDD/D1' : {
       'Tree conflict' :
-        '^local delete, incoming edit upon update'
+        '^local dir delete, incoming dir edit upon update'
         + ' Source  left: .dir.*/DDD/D1@2'
         + ' Source right: .dir.*/DDD/D1@3$',
     },
@@ -4438,46 +4333,45 @@ def tree_conflicts_on_update_1_2(sbox):
   ### Why does the deep trees state not include files?
   expected_disk.remove('D/D1',
                        'DD/D1/D2',
-                       'DDD/D1/D2/D3')
-  if svntest.main.wc_is_singledb(sbox.wc_dir):
-    expected_disk.remove('DF/D1', 'DD/D1',
-                         'DDF/D1', 'DDF/D1/D2',
-                         'DDD/D1', 'DDD/D1/D2')
+                       'DDD/D1/D2/D3',
+                       'DF/D1', 'DD/D1',
+                       'DDF/D1', 'DDF/D1/D2',
+                       'DDD/D1', 'DDD/D1/D2')
 
   expected_info = {
     'F/alpha' : {
       'Tree conflict' :
-        '^local delete, incoming delete upon update'
+        '^local file delete, incoming file delete upon update'
         + ' Source  left: .file.*/F/alpha@2'
         + ' Source right: .none.*(/F/alpha@3)?$',
     },
     'DF/D1' : {
       'Tree conflict' :
-        '^local delete, incoming edit upon update'
+        '^local dir delete, incoming dir edit upon update'
         + ' Source  left: .dir.*/DF/D1@2'
         + ' Source right: .dir.*/DF/D1@3$',
     },
     'DDF/D1' : {
       'Tree conflict' :
-        '^local delete, incoming edit upon update'
+        '^local dir delete, incoming dir edit upon update'
         + ' Source  left: .dir.*/DDF/D1@2'
         + ' Source right: .dir.*/DDF/D1@3$',
     },
     'D/D1' : {
       'Tree conflict' :
-        '^local delete, incoming delete upon update'
+        '^local dir delete, incoming dir delete upon update'
         + ' Source  left: .dir.*/D/D1@2'
         + ' Source right: .none.*(/D/D1@3)?$',
     },
     'DD/D1' : {
       'Tree conflict' :
-        '^local delete, incoming edit upon update'
+        '^local dir delete, incoming dir edit upon update'
         + ' Source  left: .dir.*/DD/D1@2'
         + ' Source right: .dir.*/DD/D1@3$',
     },
     'DDD/D1' : {
       'Tree conflict' :
-        '^local delete, incoming edit upon update'
+        '^local dir delete, incoming dir edit upon update'
         + ' Source  left: .dir.*/DDD/D1@2'
         + ' Source right: .dir.*/DDD/D1@3$',
     },
@@ -4530,37 +4424,37 @@ def tree_conflicts_on_update_2_1(sbox):
   expected_info = {
     'F/alpha' : {
       'Tree conflict' :
-        '^local edit, incoming delete upon update'
+        '^local file edit, incoming file delete upon update'
         + ' Source  left: .file.*/F/alpha@2'
         + ' Source right: .none.*(/F/alpha@3)?$',
     },
     'DF/D1' : {
       'Tree conflict' :
-        '^local edit, incoming delete upon update'
+        '^local dir edit, incoming dir delete upon update'
         + ' Source  left: .dir.*/DF/D1@2'
         + ' Source right: .none.*(/DF/D1@3)?$',
     },
     'DDF/D1' : {
       'Tree conflict' :
-        '^local edit, incoming delete upon update'
+        '^local dir edit, incoming dir delete upon update'
         + ' Source  left: .dir.*/DDF/D1@2'
         + ' Source right: .none.*(/DDF/D1@3)?$',
     },
     'D/D1' : {
       'Tree conflict' :
-        '^local edit, incoming delete upon update'
+        '^local dir edit, incoming dir delete upon update'
         + ' Source  left: .dir.*/D/D1@2'
         + ' Source right: .none.*(/D/D1@3)?$',
     },
     'DD/D1' : {
       'Tree conflict' :
-        '^local edit, incoming delete upon update'
+        '^local dir edit, incoming dir delete upon update'
         + ' Source  left: .dir.*/DD/D1@2'
         + ' Source right: .none.*(/DD/D1@3)?$',
     },
     'DDD/D1' : {
       'Tree conflict' :
-        '^local edit, incoming delete upon update'
+        '^local dir edit, incoming dir delete upon update'
         + ' Source  left: .dir.*/DDD/D1@2'
         + ' Source right: .none.*(/DDD/D1@3)?$',
     },
@@ -4638,37 +4532,37 @@ def tree_conflicts_on_update_2_2(sbox):
   expected_info = {
     'F/alpha' : {
       'Tree conflict' :
-        '^local delete, incoming delete upon update'
+        '^local file delete, incoming file delete upon update'
         + ' Source  left: .file.*/F/alpha@2'
         + ' Source right: .none.*(/F/alpha@3)?$',
     },
     'DF/D1' : {
       'Tree conflict' :
-        '^local delete, incoming delete upon update'
+        '^local dir delete, incoming dir delete upon update'
         + ' Source  left: .dir.*/DF/D1@2'
         + ' Source right: .none.*(/DF/D1@3)?$',
     },
     'DDF/D1' : {
       'Tree conflict' :
-        '^local delete, incoming delete upon update'
+        '^local dir delete, incoming dir delete upon update'
         + ' Source  left: .dir.*/DDF/D1@2'
         + ' Source right: .none.*(/DDF/D1@3)?$',
     },
     'D/D1' : {
       'Tree conflict' :
-        '^local delete, incoming delete upon update'
+        '^local dir delete, incoming dir delete upon update'
         + ' Source  left: .dir.*/D/D1@2'
         + ' Source right: .none.*(/D/D1@3)?$',
     },
     'DD/D1' : {
       'Tree conflict' :
-        '^local delete, incoming delete upon update'
+        '^local dir delete, incoming dir delete upon update'
         + ' Source  left: .dir.*/DD/D1@2'
         + ' Source right: .none.*(/DD/D1@3)?$',
     },
     'DDD/D1' : {
       'Tree conflict' :
-        '^local delete, incoming delete upon update'
+        '^local dir delete, incoming dir delete upon update'
         + ' Source  left: .dir.*/DDD/D1@2'
         + ' Source right: .none.*(/DDD/D1@3)?$',
     },
@@ -4800,37 +4694,37 @@ def tree_conflicts_on_update_3(sbox):
   expected_info = {
     'F/alpha' : {
       'Tree conflict' :
-        '^local delete, incoming delete upon update'
+        '^local file delete, incoming file delete upon update'
         + ' Source  left: .file.*/F/alpha@2'
         + ' Source right: .none.*(/F/alpha@3)?$',
     },
     'DF/D1' : {
       'Tree conflict' :
-        '^local delete, incoming delete upon update'
+        '^local dir delete, incoming dir delete upon update'
         + ' Source  left: .dir.*/DF/D1@2'
         + ' Source right: .none.*(/DF/D1@3)?$',
     },
     'DDF/D1' : {
       'Tree conflict' :
-        '^local delete, incoming delete upon update'
+        '^local dir delete, incoming dir delete upon update'
         + ' Source  left: .dir.*/DDF/D1@2'
         + ' Source right: .none.*(/DDF/D1@3)?$',
     },
     'D/D1' : {
       'Tree conflict' :
-        '^local delete, incoming delete upon update'
+        '^local dir delete, incoming dir delete upon update'
         + ' Source  left: .dir.*/D/D1@2'
         + ' Source right: .none.*(/D/D1@3)?$',
     },
     'DD/D1' : {
       'Tree conflict' :
-        '^local delete, incoming delete upon update'
+        '^local dir delete, incoming dir delete upon update'
         + ' Source  left: .dir.*/DD/D1@2'
         + ' Source right: .none.*(/DD/D1@3)?$',
     },
     'DDD/D1' : {
       'Tree conflict' :
-        '^local delete, incoming delete upon update'
+        '^local dir delete, incoming dir delete upon update'
         + ' Source  left: .dir.*/DDD/D1@2'
         + ' Source right: .none.*(/DDD/D1@3)?$',
     },
@@ -5533,7 +5427,6 @@ def update_to_HEAD_plus_1(sbox):
                                         None, None,
                                         None, None, None, other_wc, '-r', '2')
 
-@XFail()
 def update_moved_dir_leaf_del(sbox):
   "update locally moved dir with leaf del"
   sbox.build()
@@ -5543,24 +5436,31 @@ def update_moved_dir_leaf_del(sbox):
                        sbox.repo_url + "/A/B/E/alpha")
   sbox.simple_move("A/B/E", "A/B/E2")
 
-  # since alpha isn't locally modified, the incoming delete should auto-merge
+  # Produce a tree conflict by updating the working copy to the
+  # revision which removed A/B/E/alpha. The deletion collides with
+  # the local move of A/B/E to A/B/E2.
   expected_output = svntest.wc.State(wc_dir, {
-    'A/B/E2/alpha' : Item(status='D '),
+    'A/B/E'       : Item(status='  ', treeconflict='C'),
+    'A/B/E/alpha' : Item(status='  ', treeconflict='D'),
   })
   expected_disk = svntest.main.greek_state.copy()
   expected_disk.remove('A/B/E/alpha', 'A/B/E/beta', 'A/B/E')
   expected_disk.add({
     'A/B/E2'           : Item(),
+    'A/B/E2/alpha'     : Item(contents="This is the file 'alpha'.\n"),
     'A/B/E2/beta'      : Item(contents="This is the file 'beta'.\n"),
   })
   expected_status = svntest.actions.get_virginal_state(wc_dir, 2)
   expected_status.add({
-    'A/B/E2'            : Item(status='A ', copied='+', wc_rev='-'),
+    'A/B/E2'            : Item(status='A ', copied='+', wc_rev='-',
+                               moved_from='A/B/E'),
     'A/B/E2/beta'       : Item(status='  ', copied='+', wc_rev='-'),
-    'A/B/E2/alpha'      : Item(status='D ', copied='+', wc_rev='-'),
+    'A/B/E2/alpha'      : Item(status='  ', copied='+', wc_rev='-'),
   })
   expected_status.remove('A/B/E/alpha')
-  expected_status.tweak('A/B/E', 'A/B/E/beta', status='D ')
+  expected_status.tweak('A/B/E', status='D ', treeconflict='C',
+                        moved_to='A/B/E2')
+  expected_status.tweak('A/B/E/beta', status='D ')
   svntest.actions.run_and_verify_update(wc_dir,
                                         expected_output,
                                         expected_disk,
@@ -5568,7 +5468,18 @@ def update_moved_dir_leaf_del(sbox):
                                         None, None, None,
                                         None, None, 1)
 
-@XFail()
+  # Now resolve the conflict, using --accept=mine-conflict applying
+  # the update to A/B/E2
+  svntest.actions.run_and_verify_svn("resolve failed", None, [],
+                                     'resolve',
+                                     '--accept=mine-conflict',
+                                     sbox.ospath('A/B/E'))
+  expected_status.tweak('A/B/E', treeconflict=None)
+  expected_status.remove('A/B/E2/alpha')
+  svntest.actions.run_and_verify_status(wc_dir, expected_status)
+
+@Issue(3144,3630)
+# Like break_moved_dir_edited_leaf_del, but with --accept=mine-conflict
 def update_moved_dir_edited_leaf_del(sbox):
   "update locally moved dir with edited leaf del"
   sbox.build()
@@ -5580,9 +5491,12 @@ def update_moved_dir_edited_leaf_del(sbo
   svntest.main.file_write(sbox.ospath('A/B/E2/alpha'),
                           "This is a changed 'alpha'.\n")
 
-  # since alpha was modified post-move, the incoming delete should conflict
+  # Produce a tree conflict by updating the working copy to the
+  # revision which removed A/B/E/alpha. The deletion collides with
+  # the local move of A/B/E to A/B/E2.
   expected_output = svntest.wc.State(wc_dir, {
-    'A/B/E/alpha'       : Item(status='  ', treeconflict='C'),
+    'A/B/E'       : Item(status='  ', treeconflict='C'),
+    'A/B/E/alpha' : Item(status='  ', treeconflict='D'),
   })
   expected_disk = svntest.main.greek_state.copy()
   expected_disk.remove('A/B/E/alpha', 'A/B/E/beta', 'A/B/E')
@@ -5592,14 +5506,16 @@ def update_moved_dir_edited_leaf_del(sbo
     'A/B/E2/beta'      : Item(contents="This is the file 'beta'.\n"),
   })
   expected_status = svntest.actions.get_virginal_state(wc_dir, 2)
-  expected_status.tweak('A/B/E', 'A/B/E/beta', status='D ')
-  expected_status.remove('A/B/E/alpha')
   expected_status.add({
-    'A/B/E/alpha'       : Item(status='! ', treeconflict='C'),
-    'A/B/E2'            : Item(status='A ', copied='+', wc_rev='-'),
+    'A/B/E2'            : Item(status='A ', copied='+', wc_rev='-',
+                               moved_from='A/B/E'),
     'A/B/E2/beta'       : Item(status='  ', copied='+', wc_rev='-'),
     'A/B/E2/alpha'      : Item(status='M ', copied='+', wc_rev='-'),
   })
+  expected_status.remove('A/B/E/alpha')
+  expected_status.tweak('A/B/E', status='D ', treeconflict='C',
+                        moved_to='A/B/E2')
+  expected_status.tweak('A/B/E/beta', status='D ')
   svntest.actions.run_and_verify_update(wc_dir,
                                         expected_output,
                                         expected_disk,
@@ -5607,7 +5523,18 @@ def update_moved_dir_edited_leaf_del(sbo
                                         None, None, None,
                                         None, None, 1)
 
-@XFail()
+  # Now resolve the conflict, using --accept=mine-conflict.
+  # This should apply the update to A/B/E2, and flag a tree
+  # conflict on A/B/E2/alpha (incoming delete vs. local edit)
+  svntest.actions.run_and_verify_svn("resolve failed", None, [],
+                                     'resolve',
+                                     '--accept=mine-conflict',
+                                     sbox.ospath('A/B/E'))
+  expected_status.tweak('A/B/E', treeconflict=None)
+  expected_status.tweak('A/B/E2/alpha', status='A ', copied='+', wc_rev='-',
+                        entry_status='  ', treeconflict='C')
+  svntest.actions.run_and_verify_status(wc_dir, expected_status)
+
 def update_moved_dir_file_add(sbox):
   "update locally moved dir with incoming file"
   sbox.build()
@@ -5622,9 +5549,12 @@ def update_moved_dir_file_add(sbox):
   svntest.main.run_svn(False, 'update', '-r', '1', wc_dir)
   sbox.simple_move("A/B/E", "A/B/E2")
 
-  # the incoming file should auto-merge
+  # Produce a tree conflict by updating the working copy to the
+  # revision which created A/B/E/foo. The addition collides with
+  # the local move of A/B/E to A/B/E2.
   expected_output = svntest.wc.State(wc_dir, {
-    'A/B/E2/foo' : Item(status='A '),
+    'A/B/E'       : Item(status='  ', treeconflict='C'),
+    'A/B/E/foo'   : Item(status='  ', treeconflict='A'),
   })
   expected_disk = svntest.main.greek_state.copy()
   expected_disk.remove('A/B/E/alpha', 'A/B/E/beta', 'A/B/E')
@@ -5632,17 +5562,19 @@ def update_moved_dir_file_add(sbox):
     'A/B/E2'           : Item(),
     'A/B/E2/alpha'     : Item(contents="This is the file 'alpha'.\n"),
     'A/B/E2/beta'      : Item(contents="This is the file 'beta'.\n"),
-    'A/B/E2/foo'       : Item(contents=foo_content),
   })
   expected_status = svntest.actions.get_virginal_state(wc_dir, 2)
-  expected_status.tweak('A/B/E', 'A/B/E/alpha', 'A/B/E/beta', status='D ')
   expected_status.add({
     'A/B/E/foo'         : Item(status='D ', wc_rev='2'),
-    'A/B/E2'            : Item(status='A ', copied='+', wc_rev='-'),
+    'A/B/E2'            : Item(status='A ', copied='+', wc_rev='-',
+                               moved_from='A/B/E'),
     'A/B/E2/beta'       : Item(status='  ', copied='+', wc_rev='-'),
     'A/B/E2/alpha'      : Item(status='  ', copied='+', wc_rev='-'),
-    'A/B/E2/foo'        : Item(status='A ', copied='+', wc_rev='-'),
   })
+  expected_status.tweak('A/B/E', status='D ', treeconflict='C',
+                        moved_to='A/B/E2')
+  expected_status.tweak('A/B/E/alpha', status='D ')
+  expected_status.tweak('A/B/E/beta', status='D ')
   svntest.actions.run_and_verify_update(wc_dir,
                                         expected_output,
                                         expected_disk,
@@ -5650,7 +5582,20 @@ def update_moved_dir_file_add(sbox):
                                         None, None, None,
                                         None, None, 1)
 
-@XFail()
+  # Now resolve the conflict, using --accept=mine-conflict.
+  # This should apply the update to A/B/E2, adding A/B/E2/foo.
+  svntest.actions.run_and_verify_svn("resolve failed", None, [],
+                                     'resolve',
+                                     '--accept=mine-conflict',
+                                     sbox.ospath('A/B/E'))
+  # the incoming file should auto-merge
+  expected_status.tweak('A/B/E', treeconflict=None)
+  expected_status.add({
+    'A/B/E2/foo'        : Item(status='  ', copied='+', wc_rev='-'),
+  })
+  svntest.actions.run_and_verify_status(wc_dir, expected_status)
+
+
 def update_moved_dir_dir_add(sbox):
   "update locally moved dir with incoming dir"
   sbox.build()
@@ -5669,8 +5614,9 @@ def update_moved_dir_dir_add(sbox):
 
   # the incoming file should auto-merge
   expected_output = svntest.wc.State(wc_dir, {
-    'A/B/E2/foo'      : Item(status='A '),
-    'A/B/E2/foo/bar'  : Item(status='A '),
+      'A/B/E'         : Item(status='  ', treeconflict='C'),
+      'A/B/E/foo'     : Item(status='  ', treeconflict='A'),
+      'A/B/E/foo/bar' : Item(status='  ', treeconflict='A'),
   })
   expected_disk = svntest.main.greek_state.copy()
   expected_disk.remove('A/B/E/alpha', 'A/B/E/beta', 'A/B/E')
@@ -5678,19 +5624,17 @@ def update_moved_dir_dir_add(sbox):
     'A/B/E2'           : Item(),
     'A/B/E2/alpha'     : Item(contents="This is the file 'alpha'.\n"),
     'A/B/E2/beta'      : Item(contents="This is the file 'beta'.\n"),
-    'A/B/E2/foo'       : Item(),
-    'A/B/E2/foo/bar'   : Item(contents=bar_content),
   })
   expected_status = svntest.actions.get_virginal_state(wc_dir, 2)
   expected_status.tweak('A/B/E', 'A/B/E/alpha', 'A/B/E/beta', status='D ')
+  expected_status.tweak('A/B/E', treeconflict='C', moved_to='A/B/E2')
   expected_status.add({
     'A/B/E/foo'         : Item(status='D ', wc_rev='2'),
     'A/B/E/foo/bar'     : Item(status='D ', wc_rev='2'),
-    'A/B/E2'            : Item(status='A ', copied='+', wc_rev='-'),
+    'A/B/E2'            : Item(status='A ', copied='+', wc_rev='-',
+                               moved_from='A/B/E'),
     'A/B/E2/beta'       : Item(status='  ', copied='+', wc_rev='-'),
     'A/B/E2/alpha'      : Item(status='  ', copied='+', wc_rev='-'),
-    'A/B/E2/foo'        : Item(status='A ', copied='+', wc_rev='-'),
-    'A/B/E2/foo/bar'    : Item(status='A ', copied='+', wc_rev='-'),
   })
   svntest.actions.run_and_verify_update(wc_dir,
                                         expected_output,
@@ -5698,8 +5642,17 @@ def update_moved_dir_dir_add(sbox):
                                         expected_status,
                                         None, None, None,
                                         None, None, 1)
+  svntest.actions.run_and_verify_svn("resolve failed", None, [],
+                                     'resolve',
+                                     '--recursive',
+                                     '--accept=mine-conflict', wc_dir)
+  expected_status.tweak(treeconflict=None)
+  expected_status.add({
+    'A/B/E2/foo'        : Item(status='  ', copied='+', wc_rev='-'),
+    'A/B/E2/foo/bar'    : Item(status='  ', copied='+', wc_rev='-'),
+  })
+  svntest.actions.run_and_verify_status(wc_dir, expected_status)
 
-@XFail()
 @Issue(4037)
 def update_moved_dir_file_move(sbox):
   "update locally moved dir with incoming file move"
@@ -5712,15 +5665,13 @@ def update_moved_dir_file_move(sbox):
   svntest.main.run_svn(False, 'update', '-r', '1', wc_dir)
   sbox.simple_move("A/B/E", "A/B/E2")
 
-  # The incoming move should auto-merge such that A/B/F/alpha appears
-  # as moved to A/B/E2/alpha -- this strategy prefers the local user's
-  # change as the solution to the conflict.
-  # ### Ideally, the user should be offered a set of alternative solutions.
-  # ### E.g. the user might prefer if A/B/E2/alpha disappeared and A/B/E/alpha
-  # ### appeared as moved to A/B/F/alpha. But the --accept option does not yet
-  # ### support tree conflicts.
-  expected_output = svntest.wc.State(wc_dir, {
-    'A/B/E2/alpha' : Item(status='A '),
+  # The incoming "move" creates a tree-conflict as an incoming change
+  # in a local move.  We don't yet track moves on the server so we
+  # don't recognise the incoming change as a move.
+  expected_output = svntest.wc.State(wc_dir, {
+    'A/B/E'       : Item(status='  ', treeconflict='C'),
+    'A/B/E/alpha' : Item(status='  ', treeconflict='D'),
+    'A/B/F/alpha' : Item(status='A '),
   })
   expected_disk = svntest.main.greek_state.copy()
   expected_disk.remove('A/B/E/alpha', 'A/B/E/beta', 'A/B/E')
@@ -5728,13 +5679,18 @@ def update_moved_dir_file_move(sbox):
     'A/B/E2'           : Item(),
     'A/B/E2/alpha'     : Item(contents="This is the file 'alpha'.\n"),
     'A/B/E2/beta'      : Item(contents="This is the file 'beta'.\n"),
+    'A/B/F/alpha'      : Item(contents="This is the file 'alpha'.\n"),
   })
   expected_status = svntest.actions.get_virginal_state(wc_dir, 2)
-  expected_status.tweak('A/B/E', 'A/B/E/alpha', 'A/B/E/beta', status='D ')
+  expected_status.remove('A/B/E/alpha')
+  expected_status.tweak('A/B/E', status='D ', treeconflict='C',
+                        moved_to='A/B/E2')
+  expected_status.tweak('A/B/E/beta', status='D ')
   expected_status.add({
-    'A/B/F/alpha'       : Item(status='D '),
-    'A/B/E2'            : Item(status='A ', copied='+', wc_rev='-'),
-    'A/B/E2/alpha'      : Item(status='A ', copied='+', wc_rev='-'),
+    'A/B/F/alpha'       : Item(status='  ', wc_rev='2'),
+    'A/B/E2'            : Item(status='A ', copied='+', wc_rev='-',
+                               moved_from='A/B/E'),
+    'A/B/E2/alpha'      : Item(status='  ', copied='+', wc_rev='-'),
     'A/B/E2/beta'       : Item(status='  ', copied='+', wc_rev='-'),
   })
   svntest.actions.run_and_verify_update(wc_dir,
@@ -5744,7 +5700,19 @@ def update_moved_dir_file_move(sbox):
                                         None, None, None,
                                         None, None, 1)
 
-@XFail()
+  # The incoming change is a delete as we don't yet track server-side
+  # moves.  Resolving the tree-conflict as "mine-conflict" applies the
+  # delete to the move destination.
+  svntest.actions.run_and_verify_svn("resolve failed", None, [],
+                                     'resolve',
+                                     '--accept=mine-conflict',
+                                     sbox.ospath('A/B/E'))
+
+  expected_status.tweak('A/B/E', treeconflict=None)
+  expected_status.remove('A/B/E2/alpha')
+  svntest.actions.run_and_verify_status(wc_dir, expected_status)
+
+
 @Issue(3144,3630)
 def update_move_text_mod(sbox):
   "text mod to moved files"
@@ -5762,31 +5730,36 @@ def update_move_text_mod(sbox):
   expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
   expected_status.tweak('A/B/E', 'A/B/E/alpha', 'A/B/E/beta', 'A/B/lambda',
                         status='D ')
+  expected_status.tweak('A/B/E',  moved_to='A/E2')
+  expected_status.tweak('A/B/lambda', moved_to='A/lambda2')
   expected_status.add({
-      'A/E2'        : Item(status='A ', copied='+', wc_rev='-'),
+      'A/E2'        : Item(status='A ', copied='+', wc_rev='-',
+                           moved_from='A/B/E'),
       'A/E2/alpha'  : Item(status='  ', copied='+', wc_rev='-'),
       'A/E2/beta'   : Item(status='  ', copied='+', wc_rev='-'),
-      'A/lambda2'   : Item(status='A ', copied='+', wc_rev='-'),
+      'A/lambda2'   : Item(status='A ', copied='+', wc_rev='-',
+                           moved_from='A/B/lambda'),
       })
 
   svntest.actions.run_and_verify_status(wc_dir, expected_status)
 
   expected_output = svntest.wc.State(wc_dir, {
-    'A/lambda2' : Item(status='U '),
-    'A/E2/beta' : Item(status='U '),
+    'A/B/lambda' : Item(status='  ', treeconflict='C'),
+    'A/B/E'      : Item(status='  ', treeconflict='C'),
+    'A/B/E/beta' : Item(status='  ', treeconflict='U'),
   })
   expected_disk = svntest.main.greek_state.copy()
   expected_disk.remove('A/B/E/alpha', 'A/B/E/beta', 'A/B/E', 'A/B/lambda')
   expected_disk.add({
     'A/E2'        : Item(),
     'A/E2/alpha'  : Item(contents="This is the file 'alpha'.\n"),
-    'A/E2/beta'   : Item(contents="This is the file 'beta'.\nmodified\n"),
-    'A/lambda2'   : Item(contents="This is the file 'lambda'.\nmodified\n"),
+    'A/E2/beta'   : Item(contents="This is the file 'beta'.\n"),
+    'A/lambda2'   : Item(contents="This is the file 'lambda'.\n"),
   })
   expected_status.tweak(wc_rev=2)
+  expected_status.tweak('A/B/E', 'A/B/lambda', treeconflict='C')
   expected_status.tweak('A/E2', 'A/E2/alpha', 'A/E2/beta', 'A/lambda2',
                         wc_rev='-')
-  ### XFAIL 'A/E2/beta' is status R but should be ' '
   svntest.actions.run_and_verify_update(wc_dir,
                                         expected_output,
                                         expected_disk,
@@ -5794,7 +5767,22 @@ def update_move_text_mod(sbox):
                                         None, None, None,
                                         None, None, 1)
 
-@XFail()
+  svntest.actions.run_and_verify_svn("resolve failed", None, [],
+                                     'resolve',
+                                     '--recursive',
+                                     '--accept=mine-conflict',
+                                     wc_dir)
+
+  expected_status.tweak('A/B/E', 'A/B/lambda', treeconflict=None)
+  svntest.actions.run_and_verify_status(wc_dir, expected_status)
+
+  expected_disk.tweak('A/E2/beta',
+                      contents="This is the file 'beta'.\nmodified\n"),
+  expected_disk.tweak('A/lambda2',
+                      contents="This is the file 'lambda'.\nmodified\n"),
+  svntest.actions.verify_disk(wc_dir, expected_disk, check_props = True)
+
+
 @Issue(3144,3630)
 def update_nested_move_text_mod(sbox):
   "text mod to moved file in moved dir"
@@ -5810,29 +5798,34 @@ def update_nested_move_text_mod(sbox):
 
   expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
   expected_status.tweak('A/B/E', 'A/B/E/alpha', 'A/B/E/beta', status='D ')
+  expected_status.tweak('A/B/E', moved_to='A/E2')
   expected_status.add({
-      'A/E2'        : Item(status='A ', copied='+', wc_rev='-'),
-      'A/E2/alpha'  : Item(status='D ', copied='+', wc_rev='-'),
+      'A/E2'        : Item(status='A ', copied='+', wc_rev='-',
+                           moved_from='A/B/E'),
+      'A/E2/alpha'  : Item(status='D ', copied='+', wc_rev='-',
+                           moved_to='A/alpha2'),
       'A/E2/beta'   : Item(status='  ', copied='+', wc_rev='-'),
-      'A/alpha2'    : Item(status='A ', copied='+', wc_rev='-'),
+      'A/alpha2'    : Item(status='A ', copied='+', wc_rev='-',
+                           moved_from='A/E2/alpha'),
       })
 
   svntest.actions.run_and_verify_status(wc_dir, expected_status)
 
   expected_output = svntest.wc.State(wc_dir, {
-    'A/alpha2' : Item(status='U '),
+    'A/B/E'       : Item(status='  ', treeconflict='C'),
+    'A/B/E/alpha' : Item(status='  ', treeconflict='U'),
   })
   expected_disk = svntest.main.greek_state.copy()
   expected_disk.remove('A/B/E/alpha', 'A/B/E/beta', 'A/B/E')
   expected_disk.add({
     'A/E2'        : Item(),
     'A/E2/beta'   : Item(contents="This is the file 'beta'.\n"),
-    'A/alpha2'    : Item(contents="This is the file 'alpha'.\nmodified\n"),
+    'A/alpha2'    : Item(contents="This is the file 'alpha'.\n"),
   })
   expected_status.tweak(wc_rev=2)
+  expected_status.tweak('A/B/E', treeconflict='C')
   expected_status.tweak('A/E2', 'A/E2/alpha', 'A/E2/beta', 'A/alpha2',
                         wc_rev='-')
-  ### XFAIL update fails 'No such file'
   svntest.actions.run_and_verify_update(wc_dir,
                                         expected_output,
                                         expected_disk,
@@ -5840,6 +5833,19 @@ def update_nested_move_text_mod(sbox):
                                         None, None, None,
                                         None, None, 1)
 
+  svntest.actions.run_and_verify_svn("resolve failed", None, [],
+                                     'resolve',
+                                     '--recursive',
+                                     '--accept=mine-conflict',
+                                     wc_dir)
+
+  expected_status.tweak('A/B/E', treeconflict=None)
+  svntest.actions.run_and_verify_status(wc_dir, expected_status)
+
+  expected_disk.tweak('A/alpha2',
+                      contents="This is the file 'alpha'.\nmodified\n"),
+  svntest.actions.verify_disk(wc_dir, expected_disk, check_props = True)
+
 def update_with_parents_and_exclude(sbox):
   "bring a subtree in over an excluded path"
 
@@ -5896,6 +5902,672 @@ def update_with_parents_and_exclude(sbox
                                         '--parents',
                                         sbox.ospath('A/B'))
 
+@Issue(4288)
+def update_edit_delete_obstruction(sbox):
+  "obstructions shouldn't cause update failures"
+
+  sbox.build()
+  wc_dir = sbox.wc_dir
+
+  # r2
+  sbox.simple_rm('A/B','iota')
+  svntest.main.file_append(sbox.ospath('A/mu'), "File change")
+  sbox.simple_propset('key', 'value', 'A/D', 'A/D/G')
+  sbox.simple_commit()
+
+  # r3
+  sbox.simple_mkdir('iota')
+  sbox.simple_copy('A/D/gamma', 'A/B')
+  sbox.simple_rm('A/D/H/chi')
+  sbox.simple_commit()
+
+  sbox.simple_update('', 1)
+
+  # Create obstructions
+  svntest.main.safe_rmtree(sbox.ospath('A/B'))
+  svntest.main.file_append(sbox.ospath('A/B'), "Obstruction")
+
+  svntest.main.safe_rmtree(sbox.ospath('A/D'))
+  svntest.main.file_append(sbox.ospath('A/D'), "Obstruction")
+
+  os.remove(sbox.ospath('iota'))
+  os.mkdir(sbox.ospath('iota'))
+
+  os.remove(sbox.ospath('A/mu'))
+  os.mkdir(sbox.ospath('A/mu'))
+
+  expected_status = svntest.wc.State(wc_dir, {
+    ''                  : Item(status='  ', wc_rev='2'),
+    'A'                 : Item(status='  ', wc_rev='2'),
+    'A/mu'              : Item(status='~ ', treeconflict='C', wc_rev='2'),
+    'A/D'               : Item(status='~ ', treeconflict='C', wc_rev='2'),
+    'A/D/G'             : Item(status='! ', wc_rev='2'),
+    'A/D/G/pi'          : Item(status='! ', wc_rev='2'),
+    'A/D/G/tau'         : Item(status='! ', wc_rev='2'),
+    'A/D/G/rho'         : Item(status='! ', wc_rev='2'),
+    'A/D/H'             : Item(status='! ', wc_rev='2'),
+    'A/D/H/omega'       : Item(status='! ', wc_rev='2'),
+    'A/D/H/chi'         : Item(status='! ', wc_rev='2'),
+    'A/D/H/psi'         : Item(status='! ', wc_rev='2'),
+    'A/D/gamma'         : Item(status='! ', wc_rev='2'),
+    'A/C'               : Item(status='  ', wc_rev='2'),
+    'A/B'               : Item(status='~ ', treeconflict='C', wc_rev='-',
+                               entry_status='A ', entry_copied='+'),
+    'A/B/F'             : Item(status='! ', wc_rev='-', entry_copied='+'),
+    'A/B/E'             : Item(status='! ', wc_rev='-', entry_copied='+'),
+    'A/B/E/beta'        : Item(status='! ', wc_rev='-', entry_copied='+'),
+    'A/B/E/alpha'       : Item(status='! ', wc_rev='-', entry_copied='+'),
+    'A/B/lambda'        : Item(status='! ', wc_rev='-', entry_copied='+'),
+    'iota'              : Item(status='~ ', treeconflict='C', wc_rev='-',
+                               entry_status='A ', entry_copied='+'),
+  })
+  expected_disk = svntest.wc.State('', {
+    'A/D'               : Item(contents="Obstruction", props={'key':'value'}),
+    'A/C'               : Item(),
+    'A/B'               : Item(contents="Obstruction"),
+    'A/mu'              : Item(),
+    'iota'              : Item(),
+  })
+
+  expected_output = svntest.wc.State(wc_dir, {
+    'iota'    : Item(status='  ', treeconflict='C'),
+    'A/mu'    : Item(status='  ', treeconflict='C'),
+    'A/D'     : Item(status='  ', treeconflict='C'),
+    'A/D/G'   : Item(status='  ', treeconflict='U'),
+    'A/B'     : Item(status='  ', treeconflict='C'),
+  })
+
+  # And now update to delete B and iota
+  svntest.actions.run_and_verify_update(wc_dir,
+                                        expected_output,
+                                        expected_disk,
+                                        expected_status,
+                                        None, None, None,
+                                        None, None, 1,
+                                        '-r', '2', wc_dir)
+
+  # Cleanup obstructions
+  os.remove(sbox.ospath('A/B'))
+  os.remove(sbox.ospath('A/D'))
+  os.rmdir(sbox.ospath('iota'))
+  os.rmdir(sbox.ospath('A/mu'))
+
+  # Revert to remove working nodes and tree conflicts
+  svntest.actions.run_and_verify_svn('Reverting', None, [],
+                                     'revert', '-R',
+                                     sbox.ospath('A/B'),
+                                     sbox.ospath('A/mu'),
+                                     sbox.ospath('A/D'),
+                                     sbox.ospath('iota'))
+  sbox.simple_update('', 1)
+
+  # Now obstruct A (as parent of the changed node), and retry
+  svntest.main.safe_rmtree(sbox.ospath('A'))
+  svntest.main.file_append(sbox.ospath('A'), "Obstruction")
+
+  # And now update to delete B and iota
+
+  expected_output = svntest.wc.State(wc_dir, {
+    'A'         : Item(status='  ', treeconflict='C'),
+    'A/mu'      : Item(status='  ', treeconflict='U'),
+    'A/D'       : Item(status='  ', treeconflict='U'),
+    'A/D/G'     : Item(status='  ', treeconflict='U'),
+    'A/D/H'     : Item(status='  ', treeconflict='U'),
+    'A/D/H/chi' : Item(status='  ', treeconflict='D'),
+    'A/B'       : Item(prev_status='  ', prev_treeconflict='D', # Replacement
+                       status='  ', treeconflict='A'), 
+    'iota'      : Item(status='A ', prev_status='D '), # Replacement
+  })
+
+  expected_disk = svntest.wc.State('', {
+    'A'                 : Item(contents="Obstruction"),
+    'iota'              : Item(),
+  })
+
+  expected_status = svntest.wc.State(wc_dir, {
+    ''            : Item(status='  ', wc_rev='3'),
+    'A'           : Item(status='~ ', treeconflict='C', wc_rev='3'),
+    'A/mu'        : Item(status='! ', wc_rev='3'),
+    'A/D'         : Item(status='! ', wc_rev='3'),
+    'A/D/G'       : Item(status='! ', wc_rev='3'),
+    'A/D/G/rho'   : Item(status='! ', wc_rev='3'),
+    'A/D/G/pi'    : Item(status='! ', wc_rev='3'),
+    'A/D/G/tau'   : Item(status='! ', wc_rev='3'),
+    'A/D/gamma'   : Item(status='! ', wc_rev='3'),
+    'A/D/H'       : Item(status='! ', wc_rev='3'),
+    'A/D/H/psi'   : Item(status='! ', wc_rev='3'),
+    'A/D/H/omega' : Item(status='! ', wc_rev='3'),
+    'A/C'         : Item(status='! ', wc_rev='3'),
+    'A/B'         : Item(status='! ', wc_rev='3'),
+    'iota'        : Item(status='  ', wc_rev='3'),
+  })
+
+  svntest.actions.run_and_verify_update(wc_dir,
+                                        expected_output,
+                                        expected_disk,
+                                        expected_status,
+                                        None, None, None,
+                                        None, None, 1,
+                                        '-r', '3', wc_dir)
+
+def update_deleted(sbox):
+  "update a deleted tree"
+
+  sbox.build(read_only = True)
+  wc_dir = sbox.wc_dir
+  sbox.simple_rm('A')
+
+  expected_output = svntest.wc.State(wc_dir, {
+  })
+
+  expected_status = svntest.wc.State(wc_dir, {
+  })
+
+  # This runs an update anchored on A, which is deleted. The update editor
+  # shouldn't look at the ACTUAL/WORKING data in this case, but in 1.7 it did.
+  svntest.actions.run_and_verify_update(wc_dir,
+                                        expected_output,
+                                        None,
+                                        None,
+                                        None, None, None,
+                                        None, None, 1,
+                                        sbox.ospath('A/B'))
+
+@Issue(3144,3630)
+# Like update_moved_dir_edited_leaf_del, but with --accept=theirs-conflict
+def break_moved_dir_edited_leaf_del(sbox):
+  "break local move of dir with edited leaf del"
+  sbox.build()
+  wc_dir = sbox.wc_dir
+
+  svntest.main.run_svn(False, 'rm', '-m', 'remove /A/B/E/alpha',
+                       sbox.repo_url + "/A/B/E/alpha")
+  sbox.simple_move("A/B/E", "A/B/E2")
+  svntest.main.file_write(sbox.ospath('A/B/E2/alpha'),
+                          "This is a changed 'alpha'.\n")
+
+  # Produce a tree conflict by updating the working copy to the
+  # revision which removed A/B/E/alpha. The deletion collides with
+  # the local move of A/B/E to A/B/E2.
+  expected_output = svntest.wc.State(wc_dir, {
+    'A/B/E'       : Item(status='  ', treeconflict='C'),
+    'A/B/E/alpha' : Item(status='  ', treeconflict='D'),
+  })
+  expected_disk = svntest.main.greek_state.copy()
+  expected_disk.remove('A/B/E/alpha', 'A/B/E/beta', 'A/B/E')
+  expected_disk.add({
+    'A/B/E2'           : Item(),
+    'A/B/E2/alpha'     : Item(contents="This is a changed 'alpha'.\n"),
+    'A/B/E2/beta'      : Item(contents="This is the file 'beta'.\n"),
+  })
+  expected_status = svntest.actions.get_virginal_state(wc_dir, 2)
+  expected_status.add({
+    'A/B/E2'            : Item(status='A ', copied='+', wc_rev='-',
+                               moved_from='A/B/E'),
+    'A/B/E2/beta'       : Item(status='  ', copied='+', wc_rev='-'),
+    'A/B/E2/alpha'      : Item(status='M ', copied='+', wc_rev='-'),
+  })
+  expected_status.remove('A/B/E/alpha')
+  expected_status.tweak('A/B/E', status='D ', treeconflict='C',
+                        moved_to='A/B/E2')
+  expected_status.tweak('A/B/E/beta', status='D ')
+  svntest.actions.run_and_verify_update(wc_dir,
+                                        expected_output,
+                                        expected_disk,
+                                        expected_status,
+                                        None, None, None,
+                                        None, None, 1)
+
+  # Now resolve the conflict, using --accept=theirs-conflict.
+  # This should break the move of A/B/E to A/B/E2, leaving A/B/E2
+  # as a copy. The deletion of A/B/E is reverted (unless it has been
+  # replaced by a new A/B/E, which is a different test case).
+  # XFAIL: Currently the move is still recorded after 'svn resolve'.
+  svntest.actions.run_and_verify_svn("resolve failed", None, [],
+                                     'resolve', '--recursive',
+                                     '--accept=theirs-conflict', wc_dir)
+  expected_status.tweak('A/B/E', status='  ', treeconflict=None, moved_to=None)
+  expected_status.tweak('A/B/E/beta', status='  ')
+  expected_status.tweak('A/B/E2', moved_from=None)
+  svntest.actions.run_and_verify_status(wc_dir, expected_status)
+
+@XFail()
+@Issue(3144,3630)
+def break_moved_replaced_dir(sbox):
+  "break local move of dir plus replace"
+  sbox.build()
+  wc_dir = sbox.wc_dir
+
+  svntest.main.run_svn(False, 'rm', '-m', 'remove /A/B/E/alpha',
+                       sbox.repo_url + "/A/B/E/alpha")
+  sbox.simple_move("A/B/E", "A/B/E2")
+  svntest.main.file_write(sbox.ospath('A/B/E2/alpha'),
+                          "This is a changed 'alpha'.\n")
+
+  # Locally replace A/B/E with something else
+  sbox.simple_copy('A/D/H', 'A/B/E')
+
+  # Produce a tree conflict by updating the working copy to the
+  # revision which removed A/B/E/alpha. The deletion collides with
+  # the local move of A/B/E to A/B/E2.
+  expected_output = svntest.wc.State(wc_dir, {
+    'A/B/E'       : Item(status='  ', treeconflict='C'),
+    'A/B/E/alpha' : Item(status='  ', treeconflict='D'),
+  })
+  expected_disk = svntest.main.greek_state.copy()
+  expected_disk.remove('A/B/E/alpha', 'A/B/E/beta')
+  expected_disk.add({
+    'A/B/E/chi'        : Item(contents="This is the file 'chi'.\n"),
+    'A/B/E/psi'        : Item(contents="This is the file 'psi'.\n"),
+    'A/B/E/omega'      : Item(contents="This is the file 'omega'.\n"),
+    'A/B/E2'           : Item(),
+    'A/B/E2/alpha'     : Item(contents="This is a changed 'alpha'.\n"),
+    'A/B/E2/beta'      : Item(contents="This is the file 'beta'.\n"),
+  })
+  expected_status = svntest.actions.get_virginal_state(wc_dir, 2)
+  expected_status.add({
+    'A/B/E/chi'         : Item(status='  ', copied='+', wc_rev='-'),
+    'A/B/E/psi'         : Item(status='  ', copied='+', wc_rev='-'),
+    'A/B/E/omega'       : Item(status='  ', copied='+', wc_rev='-'),
+    'A/B/E2'            : Item(status='A ', copied='+', wc_rev='-',
+                               moved_from='A/B/E'),
+    'A/B/E2/beta'       : Item(status='  ', copied='+', wc_rev='-'),
+    'A/B/E2/alpha'      : Item(status='M ', copied='+', wc_rev='-'),
+  })
+  expected_status.remove('A/B/E/alpha')
+  expected_status.tweak('A/B/E', status='R ', copied='+', wc_rev='-',
+                        treeconflict='C', moved_to='A/B/E2')
+  expected_status.tweak('A/B/E/beta', status='D ')
+  svntest.actions.run_and_verify_update(wc_dir,
+                                        expected_output,
+                                        expected_disk,
+                                        expected_status,
+                                        None, None, None,
+                                        None, None, 1)
+
+  # Now resolve the conflict, using --accept=theirs-conflict.
+  # This should break the move of A/B/E to A/B/E2, leaving A/B/E2
+  # as a copy. A/B/E is not reverted since it has been replaced
+  # by a new A/B/E.
+  svntest.actions.run_and_verify_svn("resolve failed", None, [],
+                                     'resolve', '--recursive',
+                                     '--accept=theirs-conflict', wc_dir)
+  expected_status.tweak('A/B/E2', moved_from=None)
+  svntest.actions.run_and_verify_status(wc_dir, expected_status)
+
+@Issue(4295)
+def update_removes_switched(sbox):
+  "update completely removes switched node"
+
+  sbox.build(create_wc = False)
+
+  wc_dir = sbox.wc_dir
+  repo_url = sbox.repo_url
+
+  svntest.actions.run_and_verify_svn(None, None, [],
+                                     'cp', repo_url + '/A',
+                                           repo_url + '/AA', '-m', 'Q')
+
+  svntest.actions.run_and_verify_svn(None, None, [],
+                                     'co', repo_url + '/A', sbox.wc_dir)
+  svntest.actions.run_and_verify_svn(None, None, [],
+                                     'switch', repo_url + '/AA/B',
+                                               wc_dir + '/B')
+
+  svntest.actions.run_and_verify_svn(None, None, [],
+                                     'rm', repo_url + '/AA/B', '-m', 'Q')
+
+  expected_output = svntest.wc.State(wc_dir, {
+    'B'                 : Item(status='D '),
+  })
+  expected_status = svntest.wc.State(wc_dir, {
+    ''                  : Item(status='  ', wc_rev='3'),
+    'D'                 : Item(status='  ', wc_rev='3'),
+    'D/G'               : Item(status='  ', wc_rev='3'),
+    'D/G/rho'           : Item(status='  ', wc_rev='3'),
+    'D/G/pi'            : Item(status='  ', wc_rev='3'),
+    'D/G/tau'           : Item(status='  ', wc_rev='3'),
+    'D/H'               : Item(status='  ', wc_rev='3'),
+    'D/H/omega'         : Item(status='  ', wc_rev='3'),
+    'D/H/chi'           : Item(status='  ', wc_rev='3'),
+    'D/H/psi'           : Item(status='  ', wc_rev='3'),
+    'D/gamma'           : Item(status='  ', wc_rev='3'),
+    'C'                 : Item(status='  ', wc_rev='3'),
+    'mu'                : Item(status='  ', wc_rev='3'),
+  })
+
+  # Before r1435684 the inherited properties code would try to fetch
+  # inherited properties for ^/AA/B and fail.
+  #
+  # The inherited properties fetch code would then bail and forget to reset
+  # the ra-session URL back to its original value.
+  #
+  # After that the update code (which ignored the specific error code) was
+  # continued the update against /AA/B (url of missing switched path)
+  # instead of against A (the working copy url).
+
+  # This update removes 'A/B', since its in-repository location is removed.
+  svntest.actions.run_and_verify_update(wc_dir,
+                                        expected_output,
+                                        None,
+                                        expected_status)
+
+  expected_output = svntest.wc.State(wc_dir, {
+    'B'          : Item(status='A '),
+    'B/lambda'   : Item(status='A '),
+    'B/E'        : Item(status='A '),
+    'B/E/alpha'  : Item(status='A '),
+    'B/E/beta'   : Item(status='A '),
+    'B/F'        : Item(status='A '),
+  })
+  expected_status = svntest.wc.State(wc_dir, {
+    ''                  : Item(status='  ', wc_rev='3'),
+    'D'                 : Item(status='  ', wc_rev='3'),
+    'D/G'               : Item(status='  ', wc_rev='3'),
+    'D/G/rho'           : Item(status='  ', wc_rev='3'),
+    'D/G/pi'            : Item(status='  ', wc_rev='3'),
+    'D/G/tau'           : Item(status='  ', wc_rev='3'),
+    'D/H'               : Item(status='  ', wc_rev='3'),
+    'D/H/omega'         : Item(status='  ', wc_rev='3'),
+    'D/H/chi'           : Item(status='  ', wc_rev='3'),
+    'D/H/psi'           : Item(status='  ', wc_rev='3'),
+    'D/gamma'           : Item(status='  ', wc_rev='3'),
+    'B'                 : Item(status='  ', wc_rev='3'),
+    'B/E'               : Item(status='  ', wc_rev='3'),
+    'B/E/alpha'         : Item(status='  ', wc_rev='3'),
+    'B/E/beta'          : Item(status='  ', wc_rev='3'),
+    'B/F'               : Item(status='  ', wc_rev='3'),
+    'B/lambda'          : Item(status='  ', wc_rev='3'),
+    'C'                 : Item(status='  ', wc_rev='3'),
+    'mu'                : Item(status='  ', wc_rev='3'),
+  })
+  
+  # And this final update brings back the node, as it was before switching.
+  svntest.actions.run_and_verify_update(wc_dir,
+                                       expected_output,
+                                       None,
+                                       expected_status)
+
+@Issue(3192)
+def incomplete_overcomplete(sbox):
+  "verify editor v1 incomplete behavior"
+
+  sbox.build()
+
+  wc_dir = sbox.wc_dir
+  repo_dir = sbox.repo_dir
+  repo_url = sbox.repo_url
+
+  # r2 - Make sure we have some dir properties in a clean wc
+  sbox.simple_rm('A', 'iota')
+  sbox.simple_propset('keep', 'keep-value', '')
+  sbox.simple_propset('del', 'del-value', '')
+  sbox.simple_commit()
+
+  # r3 -  Perform some changes that will be undone later
+  sbox.simple_mkdir('ADDED-dir')
+  sbox.simple_add_text('The added file', 'added-file')
+  sbox.simple_propset('prop-added', 'value', '')
+  sbox.simple_commit('')
+  sbox.simple_update('')
+
+  r3_disk = svntest.wc.State('', {
+    'added-file'        : Item(contents="The added file"),
+    '.'                 : Item(props={'prop-added':'value', 'del':'del-value', 'keep':'keep-value'}),
+    'ADDED-dir'         : Item(),
+  })
+
+  r3_status = svntest.wc.State(wc_dir, {
+    ''                  : Item(status='  ', wc_rev='3'),
+    'ADDED-dir'         : Item(status='  ', wc_rev='3'),
+    'added-file'        : Item(status='  ', wc_rev='3'),
+  })
+
+  # Verify assumptions for later check
+  svntest.actions.run_and_verify_status(wc_dir, r3_status)
+  svntest.actions.verify_disk(wc_dir, r3_disk, check_props = True)
+
+
+  # r4 - And we undo r3
+  sbox.simple_rm('ADDED-dir', 'added-file')
+  sbox.simple_propdel('prop-added', '')
+  sbox.simple_commit('')
+
+  # r5 - Create some alternate changes
+  sbox.simple_mkdir('NOT-ADDED-dir')
+  sbox.simple_add_text('The not added file', 'not-added-file')
+  sbox.simple_propset('prop-not-added', 'value', '')
+  sbox.simple_commit('')
+
+  # Nothing to do to bring the wc to single revision
+  expected_output = svntest.wc.State(wc_dir, {
+  })
+
+  r5_disk = svntest.wc.State('', {
+    ''                  : Item(props={'prop-not-added':'value',
+                                      'del':'del-value',
+                                      'keep':'keep-value'}),
+    'NOT-ADDED-dir'     : Item(),
+    'not-added-file'    : Item(contents="The not added file"),
+  })
+
+  expected_status = svntest.wc.State(wc_dir, {
+    ''                  : Item(status='  ', wc_rev='5'),
+    'NOT-ADDED-dir'     : Item(status='  ', wc_rev='5'),
+    'not-added-file'    : Item(status='  ', wc_rev='5'),
+  })
+
+
+  svntest.actions.run_and_verify_update(wc_dir,
+                                        expected_output,
+                                        r5_disk,
+                                        expected_status,
+                                        None, None, None, None, None,
+                                        True)
+
+  # And now we mark the directory incomplete, as if the update had failed
+  # half-way through an update to r3
+  svntest.actions.set_incomplete(wc_dir, 3)
+
+  # Tweak status to verify us breaking the wc
+  expected_status.tweak('', status='! ', wc_rev=3)
+  svntest.actions.run_and_verify_status(wc_dir, expected_status)
+
+  # But the working copy is still 100% at r5
+  svntest.actions.verify_disk(wc_dir, r5_disk, check_props = True)
+
+  # And expect update to do the right thing even though r3 is already encoded
+  # in the parent. This includes fixing the list of children (reported to the
+  # server, which will report adds and deletes) and fixing the property list
+  # (received all; client should delete properties that shouldn't be here)
+
+  expected_output = svntest.wc.State(wc_dir, {
+    ''                  : Item(status=' U'),
+    'not-added-file'    : Item(status='D '),
+    'ADDED-dir'         : Item(status='A '),
+    'added-file'        : Item(status='A '),
+    'NOT-ADDED-dir'     : Item(status='D '),
+  })
+
+  svntest.actions.run_and_verify_update(wc_dir,
+                                        expected_output,
+                                        r3_disk,
+                                        r3_status,
+                                        None, None, None, None, None,
+                                        True,
+                                        wc_dir, '-r', 3)
+
+@Issue(4300)
+def update_swapped_depth_dirs(sbox):
+  "text mod to file in swapped depth dir"
+
+  sbox.build()
+  wc_dir = sbox.wc_dir
+  sbox.build()
+  wc_dir = sbox.wc_dir
+  svntest.main.file_append(sbox.ospath('A/B/E/alpha'), "modified\n")
+  sbox.simple_commit()
+  sbox.simple_update(revision=1)
+
+  sbox.simple_move("A/B/E", "A/E")
+  sbox.simple_move("A/B", "A/E/B")
+  # This is almost certainly not the right status but it's what
+  # is currently being output so we're using it here so we
+  # can get to the deeper problem.
+  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
+  expected_status.tweak("A/B", "A/B/lambda", "A/B/F", "A/B/E",
+                        "A/B/E/alpha", "A/B/E/beta", status="D ")
+  expected_status.tweak("A/B", moved_to="A/E/B")
+  expected_status.add({
+      'A/E'          : Item(status='A ', copied='+', wc_rev='-',
+                            moved_from='A/E/B/E'),
+      'A/E/B'        : Item(status='A ', copied='+', wc_rev='-',
+                            moved_from='A/B'),
+      'A/E/B/E'      : Item(status='D ', copied='+', wc_rev='-',
+                            moved_to='A/E'),
+      'A/E/B/F'      : Item(status='  ', copied='+', wc_rev='-'),
+      'A/E/B/lambda' : Item(status='  ', copied='+', wc_rev='-'),
+      'A/E/alpha'    : Item(status='  ', copied='+', wc_rev='-'),
+      'A/E/beta'     : Item(status='  ', copied='+', wc_rev='-'),
+      'A/E/B/E/alpha': Item(status='D ', copied='+', wc_rev='-'),
+      'A/E/B/E/beta' : Item(status='D ', copied='+', wc_rev='-'),
+      })
+
+  svntest.actions.run_and_verify_status(wc_dir, expected_status)
+
+  expected_output = svntest.wc.State(wc_dir, {
+    'A/B'         : Item(status='  ', treeconflict='C'),
+    'A/B/E'       : Item(status='  ', treeconflict='U'),
+    'A/B/E/alpha' : Item(status='  ', treeconflict='U'),
+  })
+  expected_disk = svntest.main.greek_state.copy()
+  expected_disk.remove('A/B', 'A/B/lambda', 'A/B/F', 'A/B/E',
+                       'A/B/E/alpha', 'A/B/E/beta')
+  expected_disk.add({
+    'A/E'          : Item(),
+    'A/E/alpha'    : Item(contents="This is the file 'alpha'.\n"),
+    'A/E/beta'     : Item(contents="This is the file 'beta'.\n"),
+    'A/E/B'        : Item(),
+    'A/E/B/lambda' : Item(contents="This is the file 'lambda'.\n"),
+    'A/E/B/F'      : Item(),
+  })
+  expected_status.tweak(wc_rev=2)
+  expected_status.tweak('A/B', treeconflict='C')
+  expected_status.tweak('A/E', 'A/E/alpha', 'A/E/beta', 'A/E/B',
+                        'A/E/B/E', 'A/E/B/E/alpha', 'A/E/B/E/beta',
+                        'A/E/B/lambda', 'A/E/B/F', wc_rev='-')
+  svntest.actions.run_and_verify_update(wc_dir,
+                                        expected_output,
+                                        expected_disk,
+                                        expected_status,
+                                        None, None, None,
+                                        None, None, 1)
+
+def move_update_props(sbox):
+  "move-update with property mods"
+
+  sbox.build()
+  wc_dir = sbox.wc_dir
+
+  # Commit some 'future' property changes
+  sbox.simple_propset('propertyA', 'value1',
+                      'A/B', 'A/B/E', 'A/B/E/alpha', 'A/B/E/beta')
+  sbox.simple_commit()
+  sbox.simple_propset('propertyB', 'value2',
+                      'A/B', 'A/B/E', 'A/B/E/alpha', 'A/B/E/beta')
+  sbox.simple_commit()
+  sbox.simple_update(revision=1)
+
+  # Make some local property changes
+  sbox.simple_propset('propertyB', 'value3',
+                      'A/B/E', 'A/B/E/beta')
+
+  sbox.simple_move("A/B", "A/B2")
+
+  # Update and expect a conflict
+  expected_output = svntest.wc.State(wc_dir, {
+      'A/B'         : Item(status='  ', treeconflict='C'),
+      'A/B/E'       : Item(status='  ', treeconflict='U'),
+      'A/B/E/alpha' : Item(status='  ', treeconflict='U'),
+      'A/B/E/beta'  : Item(status='  ', treeconflict='U'),
+      })
+  expected_disk = svntest.main.greek_state.copy()
+  expected_disk.remove('A/B/E/alpha', 'A/B/E/beta', 'A/B/E',
+                       'A/B/lambda', 'A/B/F', 'A/B')
+  expected_disk.add({
+      'A/B2'         : Item(),
+      'A/B2/E'       : Item(),
+      'A/B2/E/alpha' : Item(contents="This is the file 'alpha'.\n"),
+      'A/B2/E/beta'  : Item(contents="This is the file 'beta'.\n"),
+      'A/B2/F'       : Item(),
+      'A/B2/lambda'  : Item(contents="This is the file 'lambda'.\n"),
+      })
+  expected_disk.tweak('A/B2/E', 'A/B2/E/beta', props={'propertyB':'value3'})
+  expected_status = svntest.actions.get_virginal_state(wc_dir, 2)
+  expected_status.tweak('A/B', status='D ', treeconflict='C', moved_to='A/B2')
+  expected_status.tweak('A/B/E', 'A/B/E/alpha', 'A/B/E/beta',
+                        'A/B/F', 'A/B/lambda', status='D ')
+  expected_status.add({
+      'A/B2'         : Item(status='A ', copied='+', wc_rev='-',
+                            moved_from='A/B'),
+      'A/B2/E'       : Item(status=' M', copied='+', wc_rev='-'),
+      'A/B2/E/beta'  : Item(status=' M', copied='+', wc_rev='-'),
+      'A/B2/E/alpha' : Item(status='  ', copied='+', wc_rev='-'),
+      'A/B2/F'       : Item(status='  ', copied='+', wc_rev='-'),
+      'A/B2/lambda'  : Item(status='  ', copied='+', wc_rev='-'),
+      })
+  svntest.actions.run_and_verify_update(wc_dir,
+                                        expected_output,
+                                        expected_disk,
+                                        expected_status,
+                                        None, None, None,
+                                        None, None, 1,
+                                        '-r', '2', wc_dir)
+
+  # Resolve conflict moving changes to destination without conflict
+  svntest.actions.run_and_verify_svn("resolve failed", None, [],
+                                     'resolve',
+                                     '--accept=mine-conflict',
+                                     sbox.ospath('A/B'))
+
+  expected_status.tweak('A/B', treeconflict=None)
+  svntest.actions.run_and_verify_status(wc_dir, expected_status)
+
+  expected_disk.tweak('A/B2', 'A/B2/E/alpha', props={'propertyA' : 'value1'})
+  expected_disk.tweak('A/B2/E', 'A/B2/E/beta', props={'propertyA' : 'value1',
+                                                      'propertyB':'value3'})
+  svntest.actions.verify_disk(wc_dir, expected_disk, check_props = True)
+
+  # Further update and expect a conflict.
+  expected_status.tweak('A/B', status='D ', treeconflict='C', moved_to='A/B2')
+  expected_status.tweak(wc_rev=3)
+  expected_status.tweak( 'A/B2', 'A/B2/E', 'A/B2/E/beta', 'A/B2/E/alpha',
+                         'A/B2/F', 'A/B2/lambda', wc_rev='-')
+  svntest.actions.run_and_verify_update(wc_dir,
+                                        expected_output,
+                                        expected_disk,
+                                        expected_status,
+                                        None, None, None,
+                                        None, None, 1,
+                                        '-r', '3', wc_dir)
+
+  # Resolve conflict moving changes and raising property conflicts
+  svntest.actions.run_and_verify_svn("resolve failed", None, [],
+                                     'resolve',
+                                     '--accept=mine-conflict',
+                                     sbox.ospath('A/B'))
+
+  expected_status.tweak('A/B', treeconflict=None)
+  expected_status.tweak('A/B2/E', 'A/B2/E/beta', status=' C')
+  svntest.actions.run_and_verify_status(wc_dir, expected_status)
+
+  expected_disk.tweak('A/B2', 'A/B2/E/alpha', props={'propertyA' : 'value1',
+                                                     'propertyB' : 'value2'})
+  expected_disk.tweak('A/B2/E', 'A/B2/E/beta', props={'propertyA' : 'value1',
+                                                      'propertyB' : 'value3'})
+  extra_files = ['dir_conflicts.prej', 'beta.prej']
+  svntest.actions.verify_disk(wc_dir, expected_disk, True,
+                              svntest.tree.detect_conflict_files, extra_files)
 
 #######################################################################
 # Run the tests
@@ -5926,7 +6598,6 @@ test_list = [ None,
               update_deletion_inside_out,
               update_schedule_add_dir,
               update_to_future_add,
-              nested_in_read_only,
               obstructed_update_alters_wc_props,
               update_xml_unsafe_dir,
               conflict_markers_matching_eol,
@@ -5972,6 +6643,14 @@ test_list = [ None,
               update_move_text_mod,
               update_nested_move_text_mod,
               update_with_parents_and_exclude,
+              update_edit_delete_obstruction,
+              update_deleted,
+              break_moved_dir_edited_leaf_del,
+              break_moved_replaced_dir,
+              update_removes_switched,
+              incomplete_overcomplete,
+              update_swapped_depth_dirs,
+              move_update_props,
              ]
 
 if __name__ == '__main__':

Modified: subversion/branches/fsfs-format7/subversion/tests/cmdline/upgrade_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-format7/subversion/tests/cmdline/upgrade_tests.py?rev=1442344&r1=1442343&r2=1442344&view=diff
==============================================================================
--- subversion/branches/fsfs-format7/subversion/tests/cmdline/upgrade_tests.py (original)
+++ subversion/branches/fsfs-format7/subversion/tests/cmdline/upgrade_tests.py Mon Feb  4 20:48:05 2013
@@ -1143,18 +1143,21 @@ def upgrade_file_externals(sbox):
 
   expected_output = svntest.verify.RegexOutput('r2 committed.*')
   svntest.actions.run_and_verify_svnmucc(None, expected_output, [],
+                                         '-m', 'r2',
                                          'propset', 'svn:externals',
                                          '^/A/B/E EX\n^/A/mu muX',
                                          sbox.repo_url + '/A/B/F')
 
   expected_output = svntest.verify.RegexOutput('r3 committed.*')
   svntest.actions.run_and_verify_svnmucc(None, expected_output, [],
+                                         '-m', 'r3',
                                          'propset', 'svn:externals',
                                          '^/A/B/F FX\n^/A/B/lambda lambdaX',
                                          sbox.repo_url + '/A/C')
 
   expected_output = svntest.verify.RegexOutput('r4 committed.*')
   svntest.actions.run_and_verify_svnmucc(None, expected_output, [],
+                                         '-m', 'r4',
                                          'propset', 'pname1', 'pvalue1',
                                          sbox.repo_url + '/A/mu',
                                          'propset', 'pname2', 'pvalue2',
@@ -1201,7 +1204,6 @@ def upgrade_file_externals(sbox):
       'alpha' : {'pname3' : 'pvalue3' },
       })
 
-
 @Issue(4035)
 def upgrade_missing_replaced(sbox):
   "upgrade with missing replaced dir"
@@ -1217,19 +1219,28 @@ def upgrade_missing_replaced(sbox):
                                      sbox.wc_dir)
 
   expected_output = svntest.wc.State(sbox.wc_dir, {
-      'A/B/E'         : Item(status='  ', treeconflict='C'),
+      'A/B/E'         : Item(status='  ', treeconflict='C',
+                             prev_verb='Restored'),
       'A/B/E/alpha'   : Item(status='  ', treeconflict='A'),
       'A/B/E/beta'    : Item(status='  ', treeconflict='A'),
       })
   expected_status = svntest.actions.get_virginal_state(sbox.wc_dir, 1)
-  expected_status.tweak('A/B/E', status='! ', treeconflict='C', wc_rev='-')
+  expected_status.tweak('A/B/E', status='! ', treeconflict='C', wc_rev='-',
+                        entry_status='R ', entry_rev='1')
   expected_status.tweak('A/B/E/alpha', 'A/B/E/beta', status='D ')
+
+  # This upgrade installs an INCOMPLETE node in WORKING for E, which makes the
+  # database technically invalid... but we did that for 1.7 and nobody noticed.
+
+  # Pass the old status tree to avoid testing via entries-dump
+  # as fetching the entries crashes on the invalid db state.
   svntest.actions.run_and_verify_update(sbox.wc_dir, expected_output,
                                         None, expected_status)
 
   svntest.actions.run_and_verify_svn(None, 'Reverted.*', [], 'revert', '-R',
                                      sbox.wc_dir)
   expected_status = svntest.actions.get_virginal_state(sbox.wc_dir, 1)
+  # And verify that the state is now valid in both the entries an status world.
   svntest.actions.run_and_verify_status(sbox.wc_dir, expected_status)
 
 @Issue(4033)
@@ -1256,6 +1267,17 @@ def upgrade_not_present_replaced(sbox):
   svntest.actions.run_and_verify_update(sbox.wc_dir, expected_output,
                                         None, expected_status)
 
+@Issue(4307)
+def upgrade_from_1_7_conflict(sbox):
+  "upgrade from 1.7 WC with conflict (format 29)"
+
+  sbox.build(create_wc=False)
+  replace_sbox_with_tarfile(sbox, 'upgrade_from_1_7_wc.tar.bz2')
+
+  # The working copy contains a text conflict, and upgrading such
+  # a working copy used to cause a pointless 'upgrade required' error.
+  svntest.actions.run_and_verify_svn(None, None, [], 'upgrade', sbox.wc_dir)
+
 ########################################################################
 # Run the tests
 
@@ -1307,6 +1329,7 @@ test_list = [ None,
               upgrade_file_externals,
               upgrade_missing_replaced,
               upgrade_not_present_replaced,
+              upgrade_from_1_7_conflict,
              ]
 
 

Modified: subversion/branches/fsfs-format7/subversion/tests/cmdline/wc_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-format7/subversion/tests/cmdline/wc_tests.py?rev=1442344&r1=1442343&r2=1442344&view=diff
==============================================================================
--- subversion/branches/fsfs-format7/subversion/tests/cmdline/wc_tests.py (original)
+++ subversion/branches/fsfs-format7/subversion/tests/cmdline/wc_tests.py Mon Feb  4 20:48:05 2013
@@ -25,6 +25,7 @@
 ######################################################################
 
 # General modules
+from __future__ import with_statement
 import shutil, stat, re, os, logging
 
 logger = logging.getLogger()
@@ -117,6 +118,87 @@ def add_with_symlink_in_path(sbox):
   sbox.simple_append('A/B/kappa', 'xyz', True)
   sbox.simple_add('Z/B/kappa')
 
+@Issue(4118)
+@SkipUnless(svntest.main.is_posix_os)
+def status_with_inaccessible_wc_db(sbox):
+  """inaccessible .svn/wc.db"""
+
+  sbox.build(read_only = True)
+  os.chmod(sbox.ospath(".svn/wc.db"), 0)
+  svntest.actions.run_and_verify_svn(
+    "Status when wc.db is not accessible", None,
+    r"[^ ]+ E155016: The working copy database at '.*' is corrupt",
+    "st", sbox.wc_dir)
+
+@Issue(4118)
+def status_with_corrupt_wc_db(sbox):
+  """corrupt .svn/wc.db"""
+
+  sbox.build(read_only = True)
+  with open(sbox.ospath(".svn/wc.db"), 'wb') as fd:
+    fd.write('\0' * 17)
+  svntest.actions.run_and_verify_svn(
+    "Status when wc.db is corrupt", None,
+    r"[^ ]+ E155016: The working copy database at '.*' is corrupt",
+    "st", sbox.wc_dir)
+
+@Issue(4118)
+def status_with_zero_length_wc_db(sbox):
+  """zero-length .svn/wc.db"""
+
+  sbox.build(read_only = True)
+  os.close(os.open(sbox.ospath(".svn/wc.db"), os.O_RDWR | os.O_TRUNC))
+  svntest.actions.run_and_verify_svn(
+    "Status when wc.db has zero length", None,
+    r"[^ ]+ E200030:",                    # SVN_ERR_SQLITE_ERROR
+    "st", sbox.wc_dir)
+
+@Issue(4118)
+def status_without_wc_db(sbox):
+  """missing .svn/wc.db"""
+
+  sbox.build(read_only = True)
+  os.remove(sbox.ospath(".svn/wc.db"))
+  svntest.actions.run_and_verify_svn(
+    "Status when wc.db is missing", None,
+    r"[^ ]+ E155016: The working copy database at '.*' is missing",
+    "st", sbox.wc_dir)
+
+@Issue(4118)
+@Skip()      # FIXME: Test fails in-tree because it finds the source WC root
+def status_without_wc_db_and_entries(sbox):
+  """missing .svn/wc.db and .svn/entries"""
+
+  sbox.build(read_only = True)
+  os.remove(sbox.ospath(".svn/wc.db"))
+  os.remove(sbox.ospath(".svn/entries"))
+  svntest.actions.run_and_verify_svn2(
+    "Status when wc.db and entries are missing", None,
+    r"[^ ]+ warning: W155007: '.*' is not a working copy",
+    0, "st", sbox.wc_dir)
+
+@Issue(4118)
+def status_with_missing_wc_db_and_maybe_valid_entries(sbox):
+  """missing .svn/wc.db, maybe valid .svn/entries"""
+
+  sbox.build(read_only = True)
+  with open(sbox.ospath(".svn/entries"), 'ab') as fd:
+    fd.write('something\n')
+    os.remove(sbox.ospath(".svn/wc.db"))
+  svntest.actions.run_and_verify_svn(
+    "Status when wc.db is missing and .svn/entries might be valid", None,
+    r"[^ ]+ E155036:",                    # SVN_ERR_WC_UPGRADE_REQUIRED
+    "st", sbox.wc_dir)
+
+
+@Issue(4267)
+def cleanup_below_wc_root(sbox):
+  """cleanup from directory below WC root"""
+
+  sbox.build(read_only = True)
+  svntest.actions.lock_admin_dir(sbox.ospath(""), True)
+  svntest.actions.run_and_verify_svn("Cleanup below wc root", None, [],
+                                     "cleanup", sbox.ospath("A"))
 
 ########################################################################
 # Run the tests
@@ -129,6 +211,13 @@ test_list = [ None,
               add_through_unversioned_symlink,
               add_through_versioned_symlink,
               add_with_symlink_in_path,
+              status_with_inaccessible_wc_db,
+              status_with_corrupt_wc_db,
+              status_with_zero_length_wc_db,
+              status_without_wc_db,
+              status_without_wc_db_and_entries,
+              status_with_missing_wc_db_and_maybe_valid_entries,
+              cleanup_below_wc_root,
              ]
 
 if __name__ == '__main__':

Modified: subversion/branches/fsfs-format7/subversion/tests/libsvn_client/client-test.c
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-format7/subversion/tests/libsvn_client/client-test.c?rev=1442344&r1=1442343&r2=1442344&view=diff
==============================================================================
--- subversion/branches/fsfs-format7/subversion/tests/libsvn_client/client-test.c (original)
+++ subversion/branches/fsfs-format7/subversion/tests/libsvn_client/client-test.c Mon Feb  4 20:48:05 2013
@@ -721,6 +721,53 @@ test_youngest_common_ancestor(const svn_
   return SVN_NO_ERROR;
 }
 
+static svn_error_t *
+test_foreign_repos_copy(const svn_test_opts_t *opts,
+                        apr_pool_t *pool)
+{
+  svn_opt_revision_t rev;
+  svn_opt_revision_t peg_rev;
+  const char *repos_url;
+  const char *repos2_url;
+  const char *wc_path;
+  svn_client_ctx_t *ctx;
+/* Create a filesytem and repository containing the Greek tree. */
+  SVN_ERR(create_greek_repos(&repos_url, "foreign-copy1", opts, pool));
+  SVN_ERR(create_greek_repos(&repos2_url, "foreign-copy2", opts, pool));
+
+  SVN_ERR(svn_dirent_get_absolute(&wc_path, "test-wc-add", pool));
+
+  wc_path = svn_dirent_join(wc_path, "foreign-wc", pool);
+
+  /* Remove old test data from the previous run */
+  SVN_ERR(svn_io_remove_dir2(wc_path, TRUE, NULL, NULL, pool));
+
+  SVN_ERR(svn_io_make_dir_recursively(wc_path, pool));
+  svn_test_add_dir_cleanup(wc_path);
+
+  rev.kind = svn_opt_revision_head;
+  peg_rev.kind = svn_opt_revision_unspecified;
+  SVN_ERR(svn_client_create_context(&ctx, pool));
+  /* Checkout greek tree as wc_path */
+  SVN_ERR(svn_client_checkout3(NULL, repos_url, wc_path, &peg_rev, &rev,
+                               svn_depth_infinity, FALSE, FALSE, ctx, pool));
+
+  SVN_ERR(svn_client__copy_foreign(svn_path_url_add_component2(repos2_url, "A",
+                                                               pool),
+                                   svn_dirent_join(wc_path, "A-copied", pool),
+                                   &peg_rev, &rev, svn_depth_infinity, FALSE, FALSE,
+                                   ctx, pool));
+
+
+  SVN_ERR(svn_client__copy_foreign(svn_path_url_add_component2(repos2_url,
+                                                               "iota",
+                                                               pool),
+                                   svn_dirent_join(wc_path, "iota-copied", pool),
+                                   &peg_rev, &rev, svn_depth_infinity, FALSE, FALSE,
+                                   ctx, pool));
+
+  return SVN_NO_ERROR;
+}
 
 /* ========================================================================== */
 
@@ -738,5 +785,6 @@ struct svn_test_descriptor_t test_funcs[
     SVN_TEST_OPTS_PASS(test_16k_add, "test adding 16k files"),
 #endif
     SVN_TEST_OPTS_PASS(test_youngest_common_ancestor, "test youngest_common_ancestor"),
+    SVN_TEST_OPTS_PASS(test_foreign_repos_copy, "test foreign repository copy"),
     SVN_TEST_NULL
   };