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

svn commit: r1220893 [11/19] - in /subversion/branches/fs-py: ./ build/ build/ac-macros/ build/generator/ build/generator/templates/ build/win32/ contrib/client-side/emacs/ contrib/server-side/mod_dontdothat/ notes/ subversion/bindings/javahl/native/ s...

Modified: subversion/branches/fs-py/subversion/libsvn_wc/entries.c
URL: http://svn.apache.org/viewvc/subversion/branches/fs-py/subversion/libsvn_wc/entries.c?rev=1220893&r1=1220892&r2=1220893&view=diff
==============================================================================
--- subversion/branches/fs-py/subversion/libsvn_wc/entries.c (original)
+++ subversion/branches/fs-py/subversion/libsvn_wc/entries.c Mon Dec 19 18:49:34 2011
@@ -1466,6 +1466,8 @@ insert_node(svn_sqlite__db_t *sdb,
     SVN_ERR(svn_sqlite__bind_text(stmt, 8, "incomplete"));
   else if (node->presence == svn_wc__db_status_excluded)
     SVN_ERR(svn_sqlite__bind_text(stmt, 8, "excluded"));
+  else if (node->presence == svn_wc__db_status_server_excluded)
+    SVN_ERR(svn_sqlite__bind_text(stmt, 8, "absent"));
 
   if (node->kind == svn_node_none)
     SVN_ERR(svn_sqlite__bind_text(stmt, 10, "unknown"));
@@ -1474,8 +1476,20 @@ insert_node(svn_sqlite__db_t *sdb,
                                   svn_node_kind_to_word(node->kind)));
 
   if (node->kind == svn_node_file)
-    SVN_ERR(svn_sqlite__bind_checksum(stmt, 14, node->checksum,
-                                      scratch_pool));
+    {
+      if (!node->checksum
+          && node->op_depth == 0
+          && node->presence != svn_wc__db_status_not_present
+          && node->presence != svn_wc__db_status_excluded
+          && node->presence != svn_wc__db_status_server_excluded)
+        return svn_error_createf(SVN_ERR_WC_CORRUPT, NULL,
+                                 _("The file '%s' has no checksum"),
+                                 svn_dirent_local_style(node->local_relpath,
+                                                        scratch_pool));
+
+      SVN_ERR(svn_sqlite__bind_checksum(stmt, 14, node->checksum,
+                                        scratch_pool));
+    }
 
   if (node->properties) /* ### Never set, props done later */
     SVN_ERR(svn_sqlite__bind_properties(stmt, 15, node->properties,
@@ -1670,6 +1684,13 @@ write_entry(struct write_baton **entry_n
 
       case svn_wc_schedule_add:
         working_node = MAYBE_ALLOC(working_node, result_pool);
+        if (entry->deleted)
+          {
+            if (parent_node->base)
+              base_node = MAYBE_ALLOC(base_node, result_pool);
+            else
+              below_working_node = MAYBE_ALLOC(below_working_node, result_pool);
+          }
         break;
 
       case svn_wc_schedule_delete:
@@ -1693,14 +1714,17 @@ write_entry(struct write_baton **entry_n
      BASE node to indicate the not-present node.  */
   if (entry->deleted)
     {
-      SVN_ERR_ASSERT(base_node && !working_node && !below_working_node);
+      SVN_ERR_ASSERT(base_node || below_working_node);
       SVN_ERR_ASSERT(!entry->incomplete);
-      base_node->presence = svn_wc__db_status_not_present;
+      if (base_node)
+        base_node->presence = svn_wc__db_status_not_present;
+      else
+        below_working_node->presence = svn_wc__db_status_not_present;
     }
-
-  if (entry->absent)
+  else if (entry->absent)
     {
-      SVN_ERR_ASSERT(base_node && !working_node);
+      SVN_ERR_ASSERT(base_node && !working_node && !below_working_node);
+      SVN_ERR_ASSERT(!entry->incomplete);
       base_node->presence = svn_wc__db_status_server_excluded;
     }
 
@@ -1708,16 +1732,10 @@ write_entry(struct write_baton **entry_n
     {
       if (entry->copyfrom_url)
         {
-          const char *relpath;
-
           working_node->repos_id = repos_id;
-          relpath = svn_uri__is_child(this_dir->repos,
-                                      entry->copyfrom_url,
-                                      result_pool);
-          if (relpath == NULL)
-            working_node->repos_relpath = "";
-          else
-            working_node->repos_relpath = relpath;
+          working_node->repos_relpath = svn_uri_skip_ancestor(
+                                          this_dir->repos, entry->copyfrom_url,
+                                          result_pool);
           working_node->revision = entry->copyfrom_rev;
           working_node->op_depth
             = svn_wc__db_op_depth_for_upgrade(local_relpath);
@@ -1873,10 +1891,23 @@ write_entry(struct write_baton **entry_n
 
       if (entry->deleted)
         {
-          base_node->presence = svn_wc__db_status_not_present;
+          SVN_ERR_ASSERT(base_node->presence == svn_wc__db_status_not_present);
           /* ### should be svn_node_unknown, but let's store what we have. */
           base_node->kind = entry->kind;
         }
+      else if (entry->absent)
+        {
+          SVN_ERR_ASSERT(base_node->presence 
+                                == svn_wc__db_status_server_excluded);
+          /* ### should be svn_node_unknown, but let's store what we have. */
+          base_node->kind = entry->kind;
+
+          /* Store the most likely revision in the node to avoid
+             base nodes without a valid revision. Of course
+             we remember that the data is still incomplete. */
+          if (!SVN_IS_VALID_REVNUM(base_node->revision) && parent_node->base)
+            base_node->revision = parent_node->base->revision;
+        }
       else
         {
           base_node->kind = entry->kind;
@@ -1934,9 +1965,20 @@ write_entry(struct write_baton **entry_n
                 found_md5_checksum = text_base_info->normal_base.md5_checksum;
               else
                 found_md5_checksum = NULL;
-              if (entry_md5_checksum && found_md5_checksum)
-                SVN_ERR_ASSERT(svn_checksum_match(entry_md5_checksum,
-                                                  found_md5_checksum));
+              if (entry_md5_checksum && found_md5_checksum &&
+                  !svn_checksum_match(entry_md5_checksum, found_md5_checksum))
+                return svn_error_createf(SVN_ERR_WC_CORRUPT, NULL,
+                                         _("Bad base MD5 checksum for '%s'; "
+                                           "expected: '%s'; found '%s'; "),
+                                       svn_dirent_local_style(
+                                         svn_dirent_join(root_abspath,
+                                                         local_relpath,
+                                                         scratch_pool),
+                                         scratch_pool),
+                                       svn_checksum_to_cstring_display(
+                                         entry_md5_checksum, scratch_pool),
+                                       svn_checksum_to_cstring_display(
+                                         found_md5_checksum, scratch_pool));
               else
                 {
                   /* ### Not sure what conditions this should cover. */
@@ -1951,17 +1993,16 @@ write_entry(struct write_baton **entry_n
 
           if (entry->url != NULL)
             {
-              const char *relpath = svn_uri__is_child(this_dir->repos,
-                                                      entry->url,
-                                                      result_pool);
-              base_node->repos_relpath = relpath ? relpath : "";
+              base_node->repos_relpath = svn_uri_skip_ancestor(
+                                           this_dir->repos, entry->url,
+                                           result_pool);
             }
           else
             {
-              const char *relpath = svn_uri__is_child(this_dir->repos,
-                                                      this_dir->url,
-                                                      scratch_pool);
-              if (relpath == NULL)
+              const char *relpath = svn_uri_skip_ancestor(this_dir->repos,
+                                                          this_dir->url,
+                                                          scratch_pool);
+              if (relpath == NULL || *relpath == '\0')
                 base_node->repos_relpath = entry->name;
               else
                 base_node->repos_relpath =

Modified: subversion/branches/fs-py/subversion/libsvn_wc/externals.c
URL: http://svn.apache.org/viewvc/subversion/branches/fs-py/subversion/libsvn_wc/externals.c?rev=1220893&r1=1220892&r2=1220893&view=diff
==============================================================================
--- subversion/branches/fs-py/subversion/libsvn_wc/externals.c (original)
+++ subversion/branches/fs-py/subversion/libsvn_wc/externals.c Mon Dec 19 18:49:34 2011
@@ -201,8 +201,7 @@ svn_wc_parse_externals_description3(apr_
       for (num_line_parts = 0; line_parts[num_line_parts]; num_line_parts++)
         ;
 
-      SVN_ERR(svn_wc_external_item_create
-              ((const svn_wc_external_item2_t **) &item, pool));
+      SVN_ERR(svn_wc_external_item2_create(&item, pool));
       item->revision.kind = svn_opt_revision_unspecified;
       item->peg_revision.kind = svn_opt_revision_unspecified;
 
@@ -301,8 +300,13 @@ svn_wc_parse_externals_description3(apr_
 
       item->target_dir = svn_dirent_internal_style(item->target_dir, pool);
 
-      if (item->target_dir[0] == '\0' || item->target_dir[0] == '/'
-          || svn_path_is_backpath_present(item->target_dir))
+      if (item->target_dir[0] == '\0'
+          || svn_dirent_is_absolute(item->target_dir)
+          || svn_path_is_backpath_present(item->target_dir)
+          || !svn_dirent_skip_ancestor("dummy",
+                                       svn_dirent_join("dummy",
+                                                       item->target_dir,
+                                                       pool)))
         return svn_error_createf
           (SVN_ERR_CLIENT_INVALID_EXTERNALS_DESCRIPTION, NULL,
            _("Invalid %s property on '%s': "
@@ -610,8 +614,8 @@ close_file(void *file_baton,
     const svn_checksum_t *original_checksum = NULL;
 
     svn_boolean_t added = !SVN_IS_VALID_REVNUM(eb->original_revision);
-    const char *repos_relpath = svn_uri__is_child(eb->repos_root_url,
-                                                  eb->url, pool);
+    const char *repos_relpath = svn_uri_skip_ancestor(eb->repos_root_url,
+                                                      eb->url, pool);
 
     if (! added)
       {
@@ -902,8 +906,8 @@ svn_wc__get_file_external_editor(const s
   eb->diff3cmd = diff3_cmd;
 
   eb->record_ancestor_abspath = apr_pstrdup(edit_pool,record_ancestor_abspath);
-  eb->recorded_repos_relpath = svn_uri__is_child(repos_root_url, recorded_url,
-                                                 edit_pool);
+  eb->recorded_repos_relpath = svn_uri_skip_ancestor(repos_root_url, recorded_url,
+                                                     edit_pool);
 
   eb->changed_rev = SVN_INVALID_REVNUM;
 
@@ -1116,6 +1120,110 @@ svn_wc__read_external_info(svn_node_kind
   return SVN_NO_ERROR;
 }
 
+/* Return TRUE in *IS_ROLLED_OUT iff a node exists at XINFO->LOCAL_ABSPATH and
+ * if that node's origin corresponds with XINFO->REPOS_ROOT_URL and
+ * XINFO->REPOS_RELPATH.  All allocations are made in SCRATCH_POOL. */
+static svn_error_t *
+is_external_rolled_out(svn_boolean_t *is_rolled_out,
+                       svn_wc_context_t *wc_ctx,
+                       svn_wc__committable_external_info_t *xinfo,
+                       apr_pool_t *scratch_pool)
+{
+  const char *x_repos_relpath;
+  const char *x_repos_root_url;
+  svn_error_t *err;
+
+  *is_rolled_out = FALSE;
+
+  err = svn_wc__node_get_origin(NULL, NULL,
+                                &x_repos_relpath,
+                                &x_repos_root_url,
+                                NULL,
+                                wc_ctx, xinfo->local_abspath,
+                                FALSE, /* scan_deleted */
+                                scratch_pool, scratch_pool);
+
+  if (err)
+    {
+      if (err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
+        {
+          svn_error_clear(err);
+          return SVN_NO_ERROR;
+        }
+      SVN_ERR(err);
+    }
+
+  *is_rolled_out = (strcmp(xinfo->repos_root_url, x_repos_root_url) == 0 &&
+                    strcmp(xinfo->repos_relpath, x_repos_relpath) == 0);
+  return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_wc__committable_externals_below(apr_array_header_t **externals,
+                                    svn_wc_context_t *wc_ctx,
+                                    const char *local_abspath,
+                                    svn_depth_t depth,
+                                    apr_pool_t *result_pool,
+                                    apr_pool_t *scratch_pool)
+{
+  apr_array_header_t *orig_externals;
+  int i;
+  apr_pool_t *iterpool;
+
+  /* For svn_depth_files, this also fetches dirs. They are filtered later. */
+  SVN_ERR(svn_wc__db_committable_externals_below(&orig_externals,
+                                                 wc_ctx->db,
+                                                 local_abspath,
+                                                 depth != svn_depth_infinity,
+                                                 result_pool, scratch_pool));
+  
+  if (orig_externals == NULL)
+    return SVN_NO_ERROR;
+
+  iterpool = svn_pool_create(scratch_pool);
+
+  for (i = 0; i < orig_externals->nelts; i++)
+    {
+      svn_boolean_t is_rolled_out;
+
+      svn_wc__committable_external_info_t *xinfo =
+        APR_ARRAY_IDX(orig_externals, i,
+                      svn_wc__committable_external_info_t *);
+
+      /* Discard dirs for svn_depth_files (s.a.). */
+      if (depth == svn_depth_files
+          && xinfo->kind == svn_kind_dir)
+        continue;
+
+      svn_pool_clear(iterpool);
+
+      /* Discard those externals that are not currently checked out. */
+      SVN_ERR(is_external_rolled_out(&is_rolled_out, wc_ctx, xinfo,
+                                     iterpool));
+      if (! is_rolled_out)
+        continue;
+
+      if (*externals == NULL)
+        *externals = apr_array_make(
+                               result_pool, 0,
+                               sizeof(svn_wc__committable_external_info_t *));
+
+      APR_ARRAY_PUSH(*externals,
+                     svn_wc__committable_external_info_t *) = xinfo;
+
+      if (depth != svn_depth_infinity)
+        continue;
+
+      /* Are there any nested externals? */
+      SVN_ERR(svn_wc__committable_externals_below(externals, wc_ctx,
+                                                  xinfo->local_abspath,
+                                                  svn_depth_infinity,
+                                                  result_pool, iterpool));
+    }
+
+  return SVN_NO_ERROR;
+}
+
 svn_error_t *
 svn_wc__externals_defined_below(apr_hash_t **externals,
                                 svn_wc_context_t *wc_ctx,

Modified: subversion/branches/fs-py/subversion/libsvn_wc/node.c
URL: http://svn.apache.org/viewvc/subversion/branches/fs-py/subversion/libsvn_wc/node.c?rev=1220893&r1=1220892&r2=1220893&view=diff
==============================================================================
--- subversion/branches/fs-py/subversion/libsvn_wc/node.c (original)
+++ subversion/branches/fs-py/subversion/libsvn_wc/node.c Mon Dec 19 18:49:34 2011
@@ -214,6 +214,8 @@ svn_wc__internal_get_repos_info(const ch
                                        db, local_abspath,
                                        result_pool, scratch_pool));
 
+  SVN_ERR_ASSERT(repos_root_url == NULL || *repos_root_url != NULL);
+  SVN_ERR_ASSERT(repos_uuid == NULL || *repos_uuid != NULL);
   return SVN_NO_ERROR;
 }
 

Modified: subversion/branches/fs-py/subversion/libsvn_wc/props.c
URL: http://svn.apache.org/viewvc/subversion/branches/fs-py/subversion/libsvn_wc/props.c?rev=1220893&r1=1220892&r2=1220893&view=diff
==============================================================================
--- subversion/branches/fs-py/subversion/libsvn_wc/props.c (original)
+++ subversion/branches/fs-py/subversion/libsvn_wc/props.c Mon Dec 19 18:49:34 2011
@@ -145,8 +145,8 @@ diff_mergeinfo_props(svn_mergeinfo_t *de
       svn_mergeinfo_t from, to;
       SVN_ERR(svn_mergeinfo_parse(&from, from_prop_val->data, pool));
       SVN_ERR(svn_mergeinfo_parse(&to, to_prop_val->data, pool));
-      SVN_ERR(svn_mergeinfo_diff(deleted, added, from, to,
-                                 TRUE, pool));
+      SVN_ERR(svn_mergeinfo_diff2(deleted, added, from, to,
+                                  TRUE, pool, pool));
     }
   return SVN_NO_ERROR;
 }
@@ -595,9 +595,9 @@ prop_conflict_from_skel(const svn_string
                                   incoming_base, scratch_pool);
 
   if (mine == NULL)
-    mine = svn_string_create("", scratch_pool);
+    mine = svn_string_create_empty(scratch_pool);
   if (incoming == NULL)
-    incoming = svn_string_create("", scratch_pool);
+    incoming = svn_string_create_empty(scratch_pool);
 
   /* Pick a suitable base for the conflict diff.
    * The incoming value is always a change,
@@ -607,7 +607,7 @@ prop_conflict_from_skel(const svn_string
       if (incoming_base)
         original = incoming_base;
       else
-        original = svn_string_create("", scratch_pool);
+        original = svn_string_create_empty(scratch_pool);
     }
   else if (incoming_base && svn_string_compare(original, mine))
     original = incoming_base;

Modified: subversion/branches/fs-py/subversion/libsvn_wc/questions.c
URL: http://svn.apache.org/viewvc/subversion/branches/fs-py/subversion/libsvn_wc/questions.c?rev=1220893&r1=1220892&r2=1220893&view=diff
==============================================================================
--- subversion/branches/fs-py/subversion/libsvn_wc/questions.c (original)
+++ subversion/branches/fs-py/subversion/libsvn_wc/questions.c Mon Dec 19 18:49:34 2011
@@ -531,19 +531,6 @@ svn_wc__min_max_revisions(svn_revnum_t *
 
 
 svn_error_t *
-svn_wc__is_sparse_checkout(svn_boolean_t *is_sparse_checkout,
-                           svn_wc_context_t *wc_ctx,
-                           const char *local_abspath,
-                           apr_pool_t *scratch_pool)
-{
-  return svn_error_trace(svn_wc__db_is_sparse_checkout(is_sparse_checkout,
-                                                       wc_ctx->db,
-                                                       local_abspath,
-                                                       scratch_pool));
-}
-
-
-svn_error_t *
 svn_wc__has_switched_subtrees(svn_boolean_t *is_switched,
                               svn_wc_context_t *wc_ctx,
                               const char *local_abspath,

Modified: subversion/branches/fs-py/subversion/libsvn_wc/status.c
URL: http://svn.apache.org/viewvc/subversion/branches/fs-py/subversion/libsvn_wc/status.c?rev=1220893&r1=1220892&r2=1220893&view=diff
==============================================================================
--- subversion/branches/fs-py/subversion/libsvn_wc/status.c (original)
+++ subversion/branches/fs-py/subversion/libsvn_wc/status.c Mon Dec 19 18:49:34 2011
@@ -2455,7 +2455,10 @@ svn_wc_get_status_editor5(const svn_delt
   struct edit_baton *eb;
   svn_delta_editor_t *tree_editor = svn_delta_default_editor(result_pool);
   void *inner_baton;
+  struct svn_wc__shim_fetch_baton_t *sfb;
   const svn_delta_editor_t *inner_editor;
+  svn_delta_shim_callbacks_t *shim_callbacks =
+                                svn_delta_shim_callbacks_default(result_pool);
 
   /* Construct an edit baton. */
   eb = apr_pcalloc(result_pool, sizeof(*eb));
@@ -2543,8 +2546,20 @@ svn_wc_get_status_editor5(const svn_delt
   if (set_locks_baton)
     *set_locks_baton = eb;
 
+  sfb = apr_palloc(result_pool, sizeof(*sfb));
+  sfb->db = wc_ctx->db;
+  sfb->base_abspath = eb->target_abspath;
+  sfb->fetch_base = FALSE;
+
+  shim_callbacks->fetch_kind_func = svn_wc__fetch_kind_func;
+  shim_callbacks->fetch_kind_baton = sfb;
+  shim_callbacks->fetch_props_func = svn_wc__fetch_props_func;
+  shim_callbacks->fetch_props_baton = sfb;
+  shim_callbacks->fetch_base_func = svn_wc__fetch_base_func;
+  shim_callbacks->fetch_base_baton = sfb;
+
   SVN_ERR(svn_editor__insert_shims(editor, edit_baton, *editor, *edit_baton,
-                                   NULL, NULL, NULL, NULL,
+                                   shim_callbacks,
                                    result_pool, scratch_pool));
 
   return SVN_NO_ERROR;

Modified: subversion/branches/fs-py/subversion/libsvn_wc/tree_conflicts.c
URL: http://svn.apache.org/viewvc/subversion/branches/fs-py/subversion/libsvn_wc/tree_conflicts.c?rev=1220893&r1=1220892&r2=1220893&view=diff
==============================================================================
--- subversion/branches/fs-py/subversion/libsvn_wc/tree_conflicts.c (original)
+++ subversion/branches/fs-py/subversion/libsvn_wc/tree_conflicts.c Mon Dec 19 18:49:34 2011
@@ -76,8 +76,9 @@ const svn_token_map_t svn_wc__conflict_r
   { "added",       svn_wc_conflict_reason_added },
   { "replaced",    svn_wc_conflict_reason_replaced },
   { "unversioned", svn_wc_conflict_reason_unversioned },
-  { "moved-here", svn_wc_conflict_reason_moved_here },
   { "moved-away", svn_wc_conflict_reason_moved_away },
+  { "moved-away-and-edited", svn_wc_conflict_reason_moved_away_and_edited },
+  { "moved-here", svn_wc_conflict_reason_moved_here },
   { NULL }
 };
 
@@ -365,7 +366,7 @@ svn_wc__serialize_conflict(svn_skel_t **
 
   /* Victim path (escaping separator chars). */
   victim_basename = svn_dirent_basename(conflict->local_abspath, result_pool);
-  SVN_ERR_ASSERT(strlen(victim_basename) > 0);
+  SVN_ERR_ASSERT(victim_basename[0]);
   svn_skel__prepend(svn_skel__str_atom(victim_basename, result_pool), c_skel);
 
   svn_skel__prepend(svn_skel__str_atom("conflict", result_pool), c_skel);

Modified: subversion/branches/fs-py/subversion/libsvn_wc/update_editor.c
URL: http://svn.apache.org/viewvc/subversion/branches/fs-py/subversion/libsvn_wc/update_editor.c?rev=1220893&r1=1220892&r2=1220893&view=diff
==============================================================================
--- subversion/branches/fs-py/subversion/libsvn_wc/update_editor.c (original)
+++ subversion/branches/fs-py/subversion/libsvn_wc/update_editor.c Mon Dec 19 18:49:34 2011
@@ -35,6 +35,7 @@
 #include "svn_types.h"
 #include "svn_pools.h"
 #include "svn_delta.h"
+#include "svn_hash.h"
 #include "svn_string.h"
 #include "svn_dirent_uri.h"
 #include "svn_path.h"
@@ -283,6 +284,13 @@ struct dir_baton
   /* Absolute path of this directory */
   const char *local_abspath;
 
+  /* Absolute path to the new location of the directory if it was moved away,
+   * and the op-root of the move operation.
+   * This is set on the root of a move operation and all children.
+   * If the directory was not moved away, this is NULL. */
+  const char *moved_to_abspath;
+  const char *moved_to_op_root_abspath;
+
   /* The repository relative path this directory will correspond to. */
   const char *new_relpath;
 
@@ -617,6 +625,8 @@ make_dir_baton(struct dir_baton **d_p,
   d->adding_dir   = adding;
   d->changed_rev  = SVN_INVALID_REVNUM;
   d->not_present_files = apr_hash_make(dir_pool);
+  d->moved_to_abspath = NULL;
+  d->moved_to_op_root_abspath = NULL;
 
   /* Copy some flags from the parent baton */
   if (pb)
@@ -1083,6 +1093,7 @@ open_root(void *edit_baton,
   struct dir_baton *db;
   svn_boolean_t already_conflicted;
   svn_error_t *err;
+  svn_wc__db_status_t status;
 
   /* Note that something interesting is actually happening in this
      edit run. */
@@ -1118,7 +1129,6 @@ open_root(void *edit_baton,
   if (*eb->target_basename == '\0')
     {
       /* For an update with a NULL target, this is equivalent to open_dir(): */
-      svn_wc__db_status_t status;
 
       /* Read the depth from the entry. */
       SVN_ERR(svn_wc__db_base_get_info(&status, NULL, NULL, NULL, NULL, NULL,
@@ -1136,6 +1146,25 @@ open_root(void *edit_baton,
                                                         pool));
     }
 
+  /* Check if this directory was moved away. */
+  err = svn_wc__db_read_info(&status, NULL, NULL, NULL, NULL, NULL, NULL,
+                             NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+                             NULL, NULL, NULL, NULL, NULL, NULL,
+                             NULL, NULL, NULL, NULL,
+                             NULL, NULL, NULL,
+                             eb->db, db->local_abspath, pool, pool);
+  if (err)
+    {
+      if (err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
+        svn_error_clear(err);
+      else
+        return svn_error_trace(err);
+    }
+  else if (status == svn_wc__db_status_deleted)
+    SVN_ERR(svn_wc__db_scan_deletion(NULL, &db->moved_to_abspath, NULL,
+                                     &db->moved_to_op_root_abspath,
+                                     eb->db, db->local_abspath, db->pool, pool));
+
   return SVN_NO_ERROR;
 }
 
@@ -1148,6 +1177,7 @@ typedef struct modcheck_baton_t {
   svn_wc__db_t *db;         /* wc_db to access nodes */
   svn_boolean_t found_mod;  /* whether a modification has been found */
   svn_boolean_t found_not_delete;  /* Found a not-delete modification */
+  svn_boolean_t is_copy; /* check for post-copy modifications only */
 } modcheck_baton_t;
 
 /* An implementation of svn_wc_status_func4_t. */
@@ -1162,6 +1192,7 @@ modcheck_callback(void *baton,
   switch (status->node_status)
     {
       case svn_wc_status_normal:
+      case svn_wc_status_incomplete:
       case svn_wc_status_ignored:
       case svn_wc_status_none:
       case svn_wc_status_unversioned:
@@ -1178,8 +1209,17 @@ modcheck_callback(void *baton,
           break;
         /* Fall through in the found modification case */
 
-      default:
       case svn_wc_status_added:
+        if (!mb->is_copy)
+          {
+            mb->found_mod = TRUE;
+            mb->found_not_delete = TRUE;
+            /* Exit from the status walker: We know what we want to know */
+            return svn_error_create(SVN_ERR_CEASE_INVOCATION, NULL, NULL);
+          }
+        break;
+
+      default:
       case svn_wc_status_replaced:
       case svn_wc_status_modified:
         mb->found_mod = TRUE;
@@ -1196,20 +1236,24 @@ modcheck_callback(void *baton,
  * tree rooted at LOCAL_ABSPATH, using DB. If *MODIFIED
  * is set to true and all the local modifications were deletes then set
  * *ALL_EDITS_ARE_DELETES to true, set it to false otherwise.  LOCAL_ABSPATH
- * may be a file or a directory. */
+ * may be a file or a directory. If IS_COPY is TRUE, LOCAL_ABSPATH is a
+ * copied (or moved) node, and nodes at or within LOCAL_ABSPATH are only
+ * considered modified if they were modified post-copy. */
 static svn_error_t *
 node_has_local_mods(svn_boolean_t *modified,
                     svn_boolean_t *all_edits_are_deletes,
+                    svn_boolean_t is_copy,
                     svn_wc__db_t *db,
                     const char *local_abspath,
                     svn_cancel_func_t cancel_func,
                     void *cancel_baton,
                     apr_pool_t *scratch_pool)
 {
-  modcheck_baton_t modcheck_baton = { NULL, FALSE, FALSE };
+  modcheck_baton_t modcheck_baton = { NULL, FALSE, FALSE, FALSE };
   svn_error_t *err;
 
   modcheck_baton.db = db;
+  modcheck_baton.is_copy = is_copy;
 
   /* Walk the WC tree for status with depth infinity, looking for any local
    * modifications. If it's a "sparse" directory, that's OK: there can be
@@ -1332,6 +1376,7 @@ create_tree_conflict(svn_wc_conflict_des
       SVN_ERR_ASSERT(reason == svn_wc_conflict_reason_edited
                      || reason == svn_wc_conflict_reason_deleted
                      || reason == svn_wc_conflict_reason_moved_away
+                     || reason == svn_wc_conflict_reason_moved_away_and_edited
                      || reason == svn_wc_conflict_reason_replaced
                      || reason == svn_wc_conflict_reason_obstructed);
 
@@ -1527,6 +1572,26 @@ check_tree_conflict(svn_wc_conflict_desc
       case svn_wc__db_status_deleted:
         if (!moved_to_abspath)
           reason = svn_wc_conflict_reason_deleted;
+        else if (action == svn_wc_conflict_action_delete)
+          {
+            svn_boolean_t all_edits_are_deletes = FALSE;
+
+            /* The update wants to delete a node which was locally moved
+             * away. We allow this only if the node wasn't modified post-move.
+             * If the only post-move changes within a subtree are deletions,
+             * allow the update to delete the entire subtree. */
+            if (working_kind == svn_kind_dir)
+              SVN_ERR(node_has_local_mods(&modified, &all_edits_are_deletes,
+                                          TRUE, eb->db, moved_to_abspath,
+                                          eb->cancel_func, eb->cancel_baton,
+                                          scratch_pool));
+            else
+              SVN_ERR(svn_wc__internal_file_modified_p(&modified, eb->db,
+                                                       moved_to_abspath,
+                                                       FALSE, scratch_pool));
+            if (modified && !all_edits_are_deletes)
+              reason = svn_wc_conflict_reason_moved_away_and_edited;
+          }
         break;
 
       case svn_wc__db_status_incomplete:
@@ -1551,7 +1616,7 @@ check_tree_conflict(svn_wc_conflict_desc
          * not visit the subdirectories of a directory that it wants to delete.
          * Therefore, we need to start a separate crawl here. */
 
-        SVN_ERR(node_has_local_mods(&modified, &all_mods_are_deletes,
+        SVN_ERR(node_has_local_mods(&modified, &all_mods_are_deletes, FALSE,
                                     eb->db, local_abspath,
                                     eb->cancel_func, eb->cancel_baton,
                                     scratch_pool));
@@ -1592,6 +1657,7 @@ check_tree_conflict(svn_wc_conflict_desc
   if (reason == svn_wc_conflict_reason_edited
       || reason == svn_wc_conflict_reason_deleted
       || reason == svn_wc_conflict_reason_moved_away
+      || reason == svn_wc_conflict_reason_moved_away_and_edited
       || reason == svn_wc_conflict_reason_replaced)
     /* When the node existed before (it was locally deleted, replaced or
      * edited), then 'update' cannot add it "again". So it can only send
@@ -1706,6 +1772,7 @@ delete_entry(const char *path,
   const char *base = svn_relpath_basename(path, NULL);
   const char *local_abspath;
   const char *repos_relpath;
+  const char *moved_to_abspath = NULL;
   svn_kind_t kind, base_kind;
   svn_boolean_t conflicted;
   svn_boolean_t have_base;
@@ -1805,6 +1872,13 @@ delete_entry(const char *path,
       return SVN_NO_ERROR;
     }
 
+  /* Has the to-be-deleted node been moved away? */
+  if (status == svn_wc__db_status_deleted)
+    SVN_ERR(svn_wc__db_scan_deletion(NULL, &moved_to_abspath, NULL,
+                                     NULL, eb->db, local_abspath,
+                                     pool, pool));
+
+
   /* Is this path the victim of a newly-discovered tree conflict?  If so,
    * remember it and notify the client. Then (if it was existing and
    * modified), re-schedule the node to be added back again, as a (modified)
@@ -1817,7 +1891,8 @@ delete_entry(const char *path,
       SVN_ERR(check_tree_conflict(&tree_conflict, eb, local_abspath,
                                   status, kind, TRUE,
                                   svn_wc_conflict_action_delete, svn_node_none,
-                                  repos_relpath, NULL, pb->pool, scratch_pool));
+                                  repos_relpath, moved_to_abspath,
+                                  pb->pool, scratch_pool));
     }
 
   if (tree_conflict != NULL)
@@ -1856,6 +1931,8 @@ delete_entry(const char *path,
         }
       else if (tree_conflict->reason == svn_wc_conflict_reason_deleted
                || tree_conflict->reason == svn_wc_conflict_reason_moved_away
+               || tree_conflict->reason ==
+                     svn_wc_conflict_reason_moved_away_and_edited
                || tree_conflict->reason == svn_wc_conflict_reason_replaced)
         {
           /* The item does not exist locally because it was already shadowed.
@@ -1867,6 +1944,23 @@ delete_entry(const char *path,
       else
         SVN_ERR_MALFUNCTION();  /* other reasons are not expected here */
     }
+  else if (moved_to_abspath)
+    {
+      /* No tree conflict was flagged, and the node was moved-away.
+       * Automatically merge the incoming deletion with the local move
+       * by deleting the node from the moved-away subtree. */
+      /* ### This should probably use a work queue. */
+      SVN_ERR(svn_wc__db_op_delete(eb->db, moved_to_abspath, NULL,
+                                   NULL, NULL, /* notify below */
+                                   eb->cancel_func, eb->cancel_baton,
+                                   scratch_pool));
+      if (kind == svn_kind_dir)
+        SVN_ERR(svn_io_remove_dir2(moved_to_abspath, TRUE,
+                                   eb->cancel_func, eb->cancel_baton,
+                                   scratch_pool));
+      else
+        SVN_ERR(svn_io_remove_file2(moved_to_abspath, TRUE, scratch_pool));
+    }
 
   /* Issue a wq operation to delete the BASE_NODE data and to delete actual
      nodes based on that from disk, but leave any WORKING_NODEs on disk.
@@ -1917,7 +2011,8 @@ delete_entry(const char *path,
       else
         node_kind = svn_node_file;
 
-      do_notification(eb, local_abspath, node_kind, action, scratch_pool);
+      do_notification(eb, moved_to_abspath ? moved_to_abspath : local_abspath,
+                      node_kind, action, scratch_pool);
     }
 
   svn_pool_destroy(scratch_pool);
@@ -2216,8 +2311,8 @@ add_directory(const char *path,
                                                        pool));
 
   /* Make sure there is a real directory at LOCAL_ABSPATH, unless we are just
-     updating the DB */
-  if (!db->shadowed)
+     updating the DB or the parent was moved away. */
+  if (!db->shadowed && !pb->moved_to_abspath)
     SVN_ERR(svn_wc__ensure_directory(db->local_abspath, pool));
 
   if (!db->shadowed && status == svn_wc__db_status_added)
@@ -2245,6 +2340,16 @@ add_directory(const char *path,
       do_notification(eb, db->local_abspath, svn_node_dir,
                       svn_wc_notify_tree_conflict, pool);
     }
+  else if (wc_kind == svn_kind_unknown &&
+           versioned_locally_and_present == FALSE &&
+           pb->moved_to_abspath)
+    {
+      /* The parent directory of the directory we're adding was moved.
+       * Add the new directory at the new location. */
+      db->moved_to_abspath = svn_dirent_join(pb->moved_to_abspath,
+                                             db->name, db->pool);
+      db->moved_to_op_root_abspath = pb->moved_to_op_root_abspath;
+    }
 
 
 
@@ -2266,7 +2371,9 @@ add_directory(const char *path,
 
       db->already_notified = TRUE;
 
-      do_notification(eb, db->local_abspath, svn_node_dir, action, pool);
+      do_notification(eb, db->moved_to_abspath ? db->moved_to_abspath
+                                               : db->local_abspath,
+                      svn_node_dir, action, pool);
     }
 
   return SVN_NO_ERROR;
@@ -2287,6 +2394,7 @@ open_directory(const char *path,
   svn_wc_conflict_description2_t *tree_conflict = NULL;
   svn_wc__db_status_t status, base_status;
   svn_kind_t wc_kind;
+  svn_error_t *err;
 
   SVN_ERR(make_dir_baton(&db, path, eb, pb, FALSE, pool));
   *child_baton = db;
@@ -2361,13 +2469,34 @@ open_directory(const char *path,
   /* Is this path a fresh tree conflict victim?  If so, skip the tree
      with one notification. */
 
+  /* Check if this directory was moved away. */
+  err = svn_wc__db_read_info(&status, NULL, NULL, NULL, NULL, NULL, NULL,
+                             NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+                             NULL, NULL, NULL, NULL, NULL, NULL,
+                             NULL, NULL, NULL, NULL,
+                             NULL, NULL, NULL,
+                             eb->db, db->local_abspath, pool, pool);
+  if (err)
+    {
+      if (err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
+        svn_error_clear(err);
+      else
+        return svn_error_trace(err);
+    }
+  else if (status == svn_wc__db_status_deleted)
+    SVN_ERR(svn_wc__db_scan_deletion(NULL, &db->moved_to_abspath, NULL,
+                                     &db->moved_to_op_root_abspath,
+                                     eb->db, db->local_abspath,
+                                     db->pool, pool));
+
   /* Check for conflicts only when we haven't already recorded
    * a tree-conflict on a parent node. */
   if (!db->shadowed)
     SVN_ERR(check_tree_conflict(&tree_conflict, eb, db->local_abspath,
                                 status, wc_kind, TRUE,
                                 svn_wc_conflict_action_edit, svn_node_dir,
-                                db->new_relpath, NULL, db->pool, pool));
+                                db->new_relpath, db->moved_to_abspath,
+                                db->pool, pool));
 
   /* Remember the roots of any locally deleted trees. */
   if (tree_conflict != NULL)
@@ -2377,6 +2506,8 @@ open_directory(const char *path,
       SVN_ERR_ASSERT(
                 tree_conflict->reason == svn_wc_conflict_reason_deleted ||
                 tree_conflict->reason == svn_wc_conflict_reason_moved_away ||
+                tree_conflict->reason ==
+                  svn_wc_conflict_reason_moved_away_and_edited ||
                 tree_conflict->reason == svn_wc_conflict_reason_replaced);
 
       /* Continue updating BASE */
@@ -2804,6 +2935,41 @@ close_directory(void *dir_baton,
                 new_actual_props,
                 all_work_items,
                 scratch_pool));
+
+      if (db->moved_to_abspath)
+        {
+          /* Perform another in-DB move of the directory to sync meta-data
+           * of the moved-away node with the new BASE node. */
+          apr_array_header_t *children = NULL;
+          apr_hash_t *children_hash = apr_hash_get(eb->dir_dirents,
+                                                   db->new_relpath,
+                                                   APR_HASH_KEY_STRING);
+          /* The op-root of the move needs to retain its moved-here flag.
+           * Its children are normal copied children. */
+          svn_boolean_t is_move = (strcmp(db->moved_to_op_root_abspath,
+                                          db->moved_to_abspath) == 0);
+
+          /* Add the new directory as a copy and create it on disk. */
+          if (children_hash)
+            SVN_ERR(svn_hash_keys(&children, children_hash, scratch_pool));
+          SVN_ERR(svn_wc__db_op_copy_dir(eb->db, db->moved_to_abspath,
+                                         new_actual_props ? new_actual_props
+                                                          : actual_props,
+                                         db->changed_rev,
+                                         db->changed_date,
+                                         db->changed_author,
+                                         db->new_relpath,
+                                         eb->repos_root,
+                                         eb->repos_uuid,
+                                         *eb->target_revision,
+                                         children,
+                                         is_move,
+                                         db->ambient_depth,
+                                         NULL /* conflict */,
+                                         NULL, /* no work, just modify DB */
+                                         scratch_pool));
+          SVN_ERR(svn_wc__ensure_directory(db->moved_to_abspath, pool));
+        }
     }
 
   /* Process all of the queued work items for this directory.  */
@@ -3256,6 +3422,15 @@ add_file(const char *path,
       do_notification(eb, fb->local_abspath, svn_node_file,
                       svn_wc_notify_tree_conflict, scratch_pool);
     }
+  else if (wc_kind == svn_kind_unknown &&
+           versioned_locally_and_present == FALSE &&
+           pb->moved_to_abspath)
+    {
+      /* The parent directory of the file we're adding was moved.
+       * Add the new file at the new location. */
+      fb->moved_to_abspath = svn_dirent_join(pb->moved_to_abspath,
+                                             fb->name, fb->pool);
+    }
 
   svn_pool_destroy(scratch_pool);
 
@@ -3377,7 +3552,9 @@ open_file(const char *path,
       /* Other modifications wouldn't be a tree conflict */
       SVN_ERR_ASSERT(
                 tree_conflict->reason == svn_wc_conflict_reason_deleted ||
-                tree_conflict->reason == svn_wc_conflict_reason_moved_away||
+                tree_conflict->reason == svn_wc_conflict_reason_moved_away ||
+                tree_conflict->reason ==
+                  svn_wc_conflict_reason_moved_away_and_edited ||
                 tree_conflict->reason == svn_wc_conflict_reason_replaced);
 
       /* Continue updating BASE */
@@ -4085,28 +4262,28 @@ close_file(void *file_baton,
   if (!fb->shadowed
       && (! fb->adding_file || fb->add_existed))
     {
-      svn_boolean_t local_is_link = FALSE;
-      svn_boolean_t incoming_is_link = FALSE;
+      svn_boolean_t local_is_link;
+      svn_boolean_t incoming_is_link;
+      int i;
 
       local_is_link = apr_hash_get(local_actual_props,
                                 SVN_PROP_SPECIAL,
                                 APR_HASH_KEY_STRING) != NULL;
 
-      {
-        int i;
+      incoming_is_link = local_is_link;
 
-        for (i = 0; i < regular_prop_changes->nelts; ++i)
-          {
-            const svn_prop_t *prop = &APR_ARRAY_IDX(regular_prop_changes, i,
-                                                    svn_prop_t);
-
-            if (strcmp(prop->name, SVN_PROP_SPECIAL) == 0)
-              {
-                incoming_is_link = TRUE;
-              }
-          }
-      }
+      /* Does an incoming propchange affect symlink-ness? */
+      for (i = 0; i < regular_prop_changes->nelts; ++i)
+        {
+          const svn_prop_t *prop = &APR_ARRAY_IDX(regular_prop_changes, i,
+                                                  svn_prop_t);
 
+          if (strcmp(prop->name, SVN_PROP_SPECIAL) == 0)
+            {
+              incoming_is_link = (prop->value != NULL);
+              break;
+            }
+        }
 
       if (local_is_link != incoming_is_link)
         {
@@ -4116,15 +4293,12 @@ close_file(void *file_baton,
           fb->obstruction_found = TRUE;
           fb->add_existed = FALSE;
 
-          /* ### Performance: We should just create the conflict here, without
-             ### verifying again */
-          SVN_ERR(check_tree_conflict(&tree_conflict, eb, fb->local_abspath,
-                                      svn_wc__db_status_added,
-                                      svn_kind_file, TRUE,
-                                      svn_wc_conflict_action_add,
-                                      svn_node_file, fb->new_relpath, NULL,
-                                      scratch_pool, scratch_pool));
-          SVN_ERR_ASSERT(tree_conflict != NULL);
+          SVN_ERR(create_tree_conflict(&tree_conflict, eb,
+                                       fb->local_abspath,
+                                       svn_wc_conflict_reason_added,
+                                       svn_wc_conflict_action_add,
+                                       svn_node_file, fb->new_relpath,
+                                       scratch_pool, scratch_pool));
           SVN_ERR(svn_wc__db_op_set_tree_conflict(eb->db,
                                                   fb->local_abspath,
                                                   tree_conflict,
@@ -4545,63 +4719,6 @@ close_edit(void *edit_baton,
 
 /*** Returning editors. ***/
 
-struct fetch_baton
-{
-  svn_wc__db_t *db;
-  const char *target_abspath;
-};
-
-static svn_error_t *
-fetch_props_func(apr_hash_t **props,
-                 void *baton,
-                 const char *path,
-                 apr_pool_t *result_pool,
-                 apr_pool_t *scratch_pool)
-{
-  struct fetch_baton *fpb = baton;
-  const char *local_abspath = svn_dirent_join(fpb->target_abspath, path,
-                                              scratch_pool);
-  svn_error_t *err;
-
-  err = svn_wc__db_read_props(props, fpb->db, local_abspath,
-                              result_pool, scratch_pool);
-
-  /* If the path doesn't exist, just return an empty set of props. */
-  if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
-    {
-      svn_error_clear(err);
-      *props = apr_hash_make(result_pool);
-    }
-  else if (err)
-    return svn_error_trace(err);
-
-  return SVN_NO_ERROR;
-}
-
-static svn_error_t *
-fetch_kind_func(svn_node_kind_t *kind,
-                void *baton,
-                const char *path,
-                apr_pool_t *scratch_pool)
-{
-  struct fetch_baton *fpb = baton;
-  const char *local_abspath = svn_dirent_join(fpb->target_abspath, path,
-                                              scratch_pool);
-  svn_kind_t db_kind;
-
-  SVN_ERR(svn_wc__db_read_kind(&db_kind, fpb->db, local_abspath, FALSE,
-                               scratch_pool));
-
-  if (db_kind == svn_kind_dir)
-    *kind = svn_node_dir;
-  else if (db_kind == svn_kind_file)
-    *kind = svn_node_file;
-  else
-    *kind = svn_node_none;
-  
-  return SVN_NO_ERROR;
-}
-
 /* Helper for the three public editor-supplying functions. */
 static svn_error_t *
 make_editor(svn_revnum_t *target_revision,
@@ -4639,7 +4756,9 @@ make_editor(svn_revnum_t *target_revisio
   svn_delta_editor_t *tree_editor = svn_delta_default_editor(edit_pool);
   const svn_delta_editor_t *inner_editor;
   const char *repos_root, *repos_uuid;
-  struct fetch_baton *fpb;
+  struct svn_wc__shim_fetch_baton_t *sfb;
+  svn_delta_shim_callbacks_t *shim_callbacks =
+                                svn_delta_shim_callbacks_default(edit_pool);
 
   /* An unknown depth can't be sticky. */
   if (depth == svn_depth_unknown)
@@ -4867,12 +4986,20 @@ make_editor(svn_revnum_t *target_revisio
                                             edit_baton,
                                             result_pool));
 
-  fpb = apr_palloc(result_pool, sizeof(*fpb));
-  fpb->db = db;
-  fpb->target_abspath = eb->target_abspath;
+  sfb = apr_palloc(result_pool, sizeof(*sfb));
+  sfb->db = db;
+  sfb->base_abspath = eb->target_abspath;
+  sfb->fetch_base = TRUE;
+
+  shim_callbacks->fetch_kind_func = svn_wc__fetch_kind_func;
+  shim_callbacks->fetch_kind_baton = sfb;
+  shim_callbacks->fetch_props_func = svn_wc__fetch_props_func;
+  shim_callbacks->fetch_props_baton = sfb;
+  shim_callbacks->fetch_base_func = svn_wc__fetch_base_func;
+  shim_callbacks->fetch_base_baton = sfb;
+
   SVN_ERR(svn_editor__insert_shims(editor, edit_baton, *editor, *edit_baton,
-                                   fetch_props_func, fpb, fetch_kind_func, fpb,
-                                   result_pool, scratch_pool));
+                                   shim_callbacks, result_pool, scratch_pool));
 
   return SVN_NO_ERROR;
 }
@@ -5397,14 +5524,14 @@ svn_wc_add_repos_file4(svn_wc_context_t 
                                               dir_abspath,
                                               pool, pool));
 
-      if (!svn_uri__is_ancestor(original_root_url, copyfrom_url))
+      original_repos_relpath =
+        svn_uri_skip_ancestor(original_root_url, copyfrom_url, pool);
+
+      if (!original_repos_relpath)
         return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
                                  _("Copyfrom-url '%s' has different repository"
                                    " root than '%s'"),
                                  copyfrom_url, original_root_url);
-
-      original_repos_relpath =
-        svn_uri_skip_ancestor(original_root_url, copyfrom_url, pool);
     }
   else
     {

Modified: subversion/branches/fs-py/subversion/libsvn_wc/upgrade.c
URL: http://svn.apache.org/viewvc/subversion/branches/fs-py/subversion/libsvn_wc/upgrade.c?rev=1220893&r1=1220892&r2=1220893&view=diff
==============================================================================
--- subversion/branches/fs-py/subversion/libsvn_wc/upgrade.c (original)
+++ subversion/branches/fs-py/subversion/libsvn_wc/upgrade.c Mon Dec 19 18:49:34 2011
@@ -614,8 +614,7 @@ ensure_repos_info(svn_wc_entry_t *entry,
       for (hi = apr_hash_first(scratch_pool, repos_cache);
            hi; hi = apr_hash_next(hi))
         {
-          if (svn_uri__is_child(svn__apr_hash_index_key(hi), entry->url,
-                                scratch_pool))
+          if (svn_uri__is_ancestor(svn__apr_hash_index_key(hi), entry->url))
             {
               if (!entry->repos)
                 entry->repos = svn__apr_hash_index_key(hi);
@@ -1404,6 +1403,7 @@ upgrade_to_wcng(void **dir_baton,
   svn_wc_entry_t *this_dir;
   const char *old_wcroot_abspath, *dir_relpath;
   apr_hash_t *text_bases_info;
+  svn_error_t *err;
 
   /* Don't try to mess with the WC if there are old log files left. */
 
@@ -1475,11 +1475,18 @@ upgrade_to_wcng(void **dir_baton,
                              data->sdb, scratch_pool, scratch_pool));
 
   /***** ENTRIES - WRITE *****/
-  SVN_ERR(svn_wc__write_upgraded_entries(dir_baton, parent_baton, db, data->sdb,
-                                         data->repos_id, data->wc_id,
-                                         dir_abspath, data->root_abspath,
-                                         entries, text_bases_info,
-                                         result_pool, scratch_pool));
+  err = svn_wc__write_upgraded_entries(dir_baton, parent_baton, db, data->sdb,
+                                       data->repos_id, data->wc_id,
+                                       dir_abspath, data->root_abspath,
+                                       entries, text_bases_info,
+                                       result_pool, scratch_pool);
+  if (err && err->apr_err == SVN_ERR_WC_CORRUPT)
+    return svn_error_quick_wrap(err,
+                                _("This working copy is corrupt and "
+                                  "cannot be upgraded. Please check out "
+                                  "a new working copy."));
+  else
+    SVN_ERR(err);
 
   /***** WC PROPS *****/
   /* If we don't know precisely where the wcprops are, ignore them.  */

Modified: subversion/branches/fs-py/subversion/libsvn_wc/util.c
URL: http://svn.apache.org/viewvc/subversion/branches/fs-py/subversion/libsvn_wc/util.c?rev=1220893&r1=1220892&r2=1220893&view=diff
==============================================================================
--- subversion/branches/fs-py/subversion/libsvn_wc/util.c (original)
+++ subversion/branches/fs-py/subversion/libsvn_wc/util.c Mon Dec 19 18:49:34 2011
@@ -150,8 +150,8 @@ svn_wc_dup_notify(const svn_wc_notify_t 
 }
 
 svn_error_t *
-svn_wc_external_item_create(const svn_wc_external_item2_t **item,
-                            apr_pool_t *pool)
+svn_wc_external_item2_create(svn_wc_external_item2_t **item,
+                             apr_pool_t *pool)
 {
   *item = apr_pcalloc(pool, sizeof(svn_wc_external_item2_t));
   return SVN_NO_ERROR;
@@ -533,3 +533,94 @@ svn_wc__status2_from_3(svn_wc_status2_t 
 
   return SVN_NO_ERROR;
 }
+
+
+svn_error_t *
+svn_wc__fetch_kind_func(svn_kind_t *kind,
+                        void *baton,
+                        const char *path,
+                        apr_pool_t *scratch_pool)
+{
+  struct svn_wc__shim_fetch_baton_t *sfb = baton;
+  const char *local_abspath = svn_dirent_join(sfb->base_abspath, path,
+                                              scratch_pool);
+
+  SVN_ERR(svn_wc__db_read_kind(kind, sfb->db, local_abspath, FALSE,
+                               scratch_pool));
+  
+  return SVN_NO_ERROR;
+}
+
+
+svn_error_t *
+svn_wc__fetch_props_func(apr_hash_t **props,
+                         void *baton,
+                         const char *path,
+                         apr_pool_t *result_pool,
+                         apr_pool_t *scratch_pool)
+{
+  struct svn_wc__shim_fetch_baton_t *sfb = baton;
+  const char *local_abspath = svn_dirent_join(sfb->base_abspath, path,
+                                              scratch_pool);
+  svn_error_t *err;
+
+  if (sfb->fetch_base)
+    err = svn_wc__db_base_get_props(props, sfb->db, local_abspath, result_pool,
+                                    scratch_pool);
+  else
+    err = svn_wc__db_read_props(props, sfb->db, local_abspath,
+                                result_pool, scratch_pool);
+
+  /* If the path doesn't exist, just return an empty set of props. */
+  if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
+    {
+      svn_error_clear(err);
+      *props = apr_hash_make(result_pool);
+    }
+  else if (err)
+    return svn_error_trace(err);
+
+  return SVN_NO_ERROR;
+}
+
+
+svn_error_t *
+svn_wc__fetch_base_func(const char **filename,
+                        void *baton,
+                        const char *path,
+                        apr_pool_t *result_pool,
+                        apr_pool_t *scratch_pool)
+{
+  struct svn_wc__shim_fetch_baton_t *sfb = baton;
+  svn_stream_t *contents;
+  svn_stream_t *file_stream;
+  const char *tmp_filename;
+  const svn_checksum_t *checksum;
+  svn_error_t *err;
+  const char *local_abspath = svn_dirent_join(sfb->base_abspath, path,
+                                              scratch_pool);
+
+  err = svn_wc__db_base_get_info(NULL, NULL, NULL, NULL, NULL, NULL,
+                                 NULL, NULL, NULL, NULL, &checksum,
+                                 NULL, NULL, NULL, NULL, sfb->db,
+                                 local_abspath, scratch_pool, scratch_pool);
+  if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
+    {
+      svn_error_clear(err);
+      *filename = NULL;
+      return SVN_NO_ERROR;
+    }
+  else if (err)
+    return svn_error_trace(err);
+  SVN_ERR(svn_wc__db_pristine_read(&contents, NULL, sfb->db, local_abspath,
+                                   checksum, scratch_pool, scratch_pool));
+
+  SVN_ERR(svn_stream_open_unique(&file_stream, &tmp_filename, NULL,
+                                 svn_io_file_del_on_pool_cleanup,
+                                 scratch_pool, scratch_pool));
+  SVN_ERR(svn_stream_copy3(contents, file_stream, NULL, NULL, scratch_pool));
+
+  *filename = apr_pstrdup(result_pool, tmp_filename);
+
+  return SVN_NO_ERROR;
+}

Modified: subversion/branches/fs-py/subversion/libsvn_wc/wc-metadata.sql
URL: http://svn.apache.org/viewvc/subversion/branches/fs-py/subversion/libsvn_wc/wc-metadata.sql?rev=1220893&r1=1220892&r2=1220893&view=diff
==============================================================================
--- subversion/branches/fs-py/subversion/libsvn_wc/wc-metadata.sql (original)
+++ subversion/branches/fs-py/subversion/libsvn_wc/wc-metadata.sql Mon Dec 19 18:49:34 2011
@@ -383,8 +383,9 @@ CREATE TABLE NODES (
      perhaps add a column called "moved_from". */
 
   /* Boolean value, specifying if this node was moved here (rather than just
-     copied). The source of the move is implied by a different node with
-     a moved_to column pointing at this node. */
+     copied). This is set on all the nodes in the moved tree.  The source of
+     the move is implied by a different node with a moved_to column pointing
+     at the root node of the moved tree. */
   moved_here  INTEGER,
 
   /* If the underlying node was moved away (rather than just deleted), this

Modified: subversion/branches/fs-py/subversion/libsvn_wc/wc-queries.sql
URL: http://svn.apache.org/viewvc/subversion/branches/fs-py/subversion/libsvn_wc/wc-queries.sql?rev=1220893&r1=1220892&r2=1220893&view=diff
==============================================================================
--- subversion/branches/fs-py/subversion/libsvn_wc/wc-queries.sql (original)
+++ subversion/branches/fs-py/subversion/libsvn_wc/wc-queries.sql Mon Dec 19 18:49:34 2011
@@ -956,6 +956,40 @@ SELECT presence, kind, def_local_relpath
 FROM externals WHERE wc_id = ?1 AND local_relpath = ?2
 LIMIT 1
 
+/* Select all committable externals, i.e. only unpegged ones on the same
+ * repository as the target path ?2, that are defined by WC ?1 to
+ * live below the target path. It does not matter which ancestor has the
+ * svn:externals definition, only the local path at which the external is
+ * supposed to be checked out is queried.
+ * Arguments:
+ *  ?1: wc_id.
+ *  ?2: the target path, local relpath inside ?1.
+ *  ?3: boolean, if 1 return immediate children of ?2 only.
+ *
+ * ### NOTE: This statement deliberately removes file externals that live
+ * inside an unversioned dir, because commit still breaks on those.
+ * Once that's been fixed, the conditions below "--->8---" become obsolete. */
+-- STMT_SELECT_COMMITTABLE_EXTERNALS_BELOW
+SELECT local_relpath, kind, repos_id, def_repos_relpath, repository.root
+FROM externals
+LEFT OUTER JOIN repository ON repository.id = externals.repos_id
+WHERE wc_id = ?1
+  AND def_revision IS NULL
+  AND repos_id = (SELECT repos_id FROM nodes
+                  WHERE nodes.local_relpath = ?2)
+  AND ( ((NOT ?3)
+         AND (?2 = ''
+              /* Want only the cildren of e.local_relpath;
+               * externals can't have a local_relpath = ''. */
+              OR IS_STRICT_DESCENDANT_OF(local_relpath, ?2)))
+        OR
+        ((?3)
+         AND parent_relpath = ?2) )
+  /* ------>8----- */
+  AND (EXISTS (SELECT 1 FROM nodes
+               WHERE nodes.wc_id = externals.wc_id
+               AND nodes.local_relpath = externals.parent_relpath))
+
 -- STMT_SELECT_EXTERNALS_DEFINED
 SELECT local_relpath, def_local_relpath
 FROM externals
@@ -1193,21 +1227,18 @@ DROP TABLE IF EXISTS delete_list;
 CREATE TEMPORARY TABLE delete_list (
 /* ### we should put the wc_id in here in case a delete spans multiple
    ### working copies. queries, etc will need to be adjusted.  */
-   local_relpath TEXT PRIMARY KEY NOT NULL
+   local_relpath TEXT PRIMARY KEY NOT NULL UNIQUE
    )
 
 /* This matches the selection in STMT_INSERT_DELETE_FROM_NODE_RECURSIVE */
 -- STMT_INSERT_DELETE_LIST
 INSERT INTO delete_list(local_relpath)
-SELECT local_relpath FROM nodes n
+SELECT local_relpath FROM nodes_current
 WHERE wc_id = ?1
   AND (local_relpath = ?2
        OR IS_STRICT_DESCENDANT_OF(local_relpath, ?2))
   AND op_depth >= ?3
   AND presence NOT IN ('base-deleted', 'not-present', 'excluded', 'absent')
-  AND op_depth = (SELECT MAX(op_depth) FROM nodes s
-                  WHERE s.wc_id = n.wc_id 
-                    AND s.local_relpath = n.local_relpath)
 
 -- STMT_SELECT_DELETE_LIST
 SELECT local_relpath FROM delete_list

Modified: subversion/branches/fs-py/subversion/libsvn_wc/wc.h
URL: http://svn.apache.org/viewvc/subversion/branches/fs-py/subversion/libsvn_wc/wc.h?rev=1220893&r1=1220892&r2=1220893&view=diff
==============================================================================
--- subversion/branches/fs-py/subversion/libsvn_wc/wc.h (original)
+++ subversion/branches/fs-py/subversion/libsvn_wc/wc.h Mon Dec 19 18:49:34 2011
@@ -149,7 +149,7 @@ extern "C" {
  * The bump to 29 renamed the pristine files from '<SHA1>' to '<SHA1>.svn-base'
  * and introduced the EXTERNALS store. Bumped in r1129286.
  *
- * == 1.7.x shipped with format ???
+ * == 1.7.x shipped with format 29
  *
  * Please document any further format changes here.
  */
@@ -350,10 +350,8 @@ svn_wc__prop_array_to_hash(const apr_arr
  *
  * If EXACT_COMPARISON is FALSE, translate LOCAL_ABSPATH's EOL
  * style and keywords to repository-normal form according to its properties,
- * and compare the result with the text base.  If COMPARE_TEXTBASES is
- * TRUE, translate the text base's EOL style and keywords to working-copy
- * form according to LOCAL_ABSPATH's properties, and compare the
- * result with LOCAL_ABSPATH.  Usually, EXACT_COMPARISON should be FALSE.
+ * and compare the result with the text base.
+ * Usually, EXACT_COMPARISON should be FALSE.
  *
  * If LOCAL_ABSPATH does not exist, consider it unmodified.  If it exists
  * but is not under revision control (not even scheduled for
@@ -726,6 +724,38 @@ svn_wc__perform_file_merge(svn_skel_t **
                            apr_pool_t *scratch_pool);
 
 
+/* Couple of random helpers for the Ev2 shims.
+   ### These will eventually be obsoleted and removed. */
+struct svn_wc__shim_fetch_baton_t
+{
+  svn_wc__db_t *db;
+  const char *base_abspath;
+  svn_boolean_t fetch_base;
+};
+
+/* Using a BATON of struct shim_fetch_baton, return KIND for PATH. */
+svn_error_t *
+svn_wc__fetch_kind_func(svn_kind_t *kind,
+                        void *baton,
+                        const char *path,
+                        apr_pool_t *scratch_pool);
+
+/* Using a BATON of struct shim_fetch_baton, return PROPS for PATH. */
+svn_error_t *
+svn_wc__fetch_props_func(apr_hash_t **props,
+                         void *baton,
+                         const char *path,
+                         apr_pool_t *result_pool,
+                         apr_pool_t *scratch_pool);
+
+/* Using a BATON of struct shim_fetch_baton, return a delta base for PATH. */
+svn_error_t *
+svn_wc__fetch_base_func(const char **filename,
+                        void *baton,
+                        const char *path,
+                        apr_pool_t *result_pool,
+                        apr_pool_t *scratch_pool);
+
 #ifdef __cplusplus
 }
 #endif /* __cplusplus */

Modified: subversion/branches/fs-py/subversion/libsvn_wc/wc_db.c
URL: http://svn.apache.org/viewvc/subversion/branches/fs-py/subversion/libsvn_wc/wc_db.c?rev=1220893&r1=1220892&r2=1220893&view=diff
==============================================================================
--- subversion/branches/fs-py/subversion/libsvn_wc/wc_db.c (original)
+++ subversion/branches/fs-py/subversion/libsvn_wc/wc_db.c Mon Dec 19 18:49:34 2011
@@ -842,6 +842,15 @@ insert_base_node(void *baton,
                             moved_to_relpath /* 21 */));
   if (pibb->kind == svn_kind_file)
     {
+      if (!pibb->checksum
+          && pibb->status != svn_wc__db_status_not_present
+          && pibb->status != svn_wc__db_status_excluded
+          && pibb->status != svn_wc__db_status_server_excluded)
+        return svn_error_createf(SVN_ERR_WC_CORRUPT, svn_sqlite__reset(stmt),
+                                 _("The file '%s' has no checksum."),
+                                 path_for_error_message(wcroot, local_relpath,
+                                                        scratch_pool));
+
       SVN_ERR(svn_sqlite__bind_checksum(stmt, 14, pibb->checksum,
                                         scratch_pool));
 
@@ -3059,6 +3068,64 @@ svn_wc__db_external_read(svn_wc__db_stat
 }
 
 svn_error_t *
+svn_wc__db_committable_externals_below(apr_array_header_t **externals,
+                                       svn_wc__db_t *db,
+                                       const char *local_abspath,
+                                       svn_boolean_t immediates_only,
+                                       apr_pool_t *result_pool,
+                                       apr_pool_t *scratch_pool)
+{
+  svn_wc__db_wcroot_t *wcroot;
+  svn_sqlite__stmt_t *stmt;
+  const char *local_relpath;
+  svn_boolean_t have_row;
+  svn_wc__committable_external_info_t *info;
+  svn_kind_t db_kind;
+  apr_array_header_t *result = NULL;
+
+  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
+
+  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
+                              local_abspath, scratch_pool, scratch_pool));
+  VERIFY_USABLE_WCROOT(wcroot);
+
+  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
+                                    STMT_SELECT_COMMITTABLE_EXTERNALS_BELOW));
+
+  SVN_ERR(svn_sqlite__bindf(stmt, "isi", wcroot->wc_id, local_relpath,
+                            (apr_int64_t)(immediates_only ? 1 : 0)));
+
+  SVN_ERR(svn_sqlite__step(&have_row, stmt));
+
+  if (have_row)
+    result = apr_array_make(result_pool, 0,
+                            sizeof(svn_wc__committable_external_info_t *));
+
+  while (have_row)
+    {
+      info = apr_palloc(result_pool, sizeof(*info));
+
+      local_relpath = svn_sqlite__column_text(stmt, 0, NULL);
+      info->local_abspath = svn_dirent_join(wcroot->abspath, local_relpath,
+                                            result_pool);
+
+      db_kind = svn_sqlite__column_token(stmt, 1, kind_map);
+      SVN_ERR_ASSERT(db_kind == svn_kind_file || db_kind == svn_kind_dir);
+      info->kind = db_kind;
+
+      info->repos_relpath = svn_sqlite__column_text(stmt, 3, result_pool);
+      info->repos_root_url = svn_sqlite__column_text(stmt, 4, result_pool);
+
+      APR_ARRAY_PUSH(result, svn_wc__committable_external_info_t *) = info;
+
+      SVN_ERR(svn_sqlite__step(&have_row, stmt));
+    }
+
+  *externals = result;
+  return svn_error_trace(svn_sqlite__reset(stmt));
+}
+
+svn_error_t *
 svn_wc__db_externals_defined_below(apr_hash_t **externals,
                                    svn_wc__db_t *db,
                                    const char *local_abspath,
@@ -3525,6 +3592,9 @@ db_op_copy(svn_wc__db_wcroot_t *src_wcro
                                                       src_relpath,
                                                       scratch_pool));
     default:
+      /* Perhaps we should allow incomplete to incomplete? We can't
+         avoid incomplete working nodes as one step in copying a
+         directory is to add incomplete children. */
       return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
                                _("Cannot handle status of '%s'"),
                                path_for_error_message(src_wcroot,
@@ -3711,6 +3781,7 @@ db_op_copy_shadowed_layer(svn_wc__db_wcr
                           apr_int64_t repos_id,
                           const char *repos_relpath,
                           svn_revnum_t revision,
+                          svn_boolean_t is_move,
                           apr_pool_t *scratch_pool)
 {
   const apr_array_header_t *children;
@@ -3828,13 +3899,15 @@ db_op_copy_shadowed_layer(svn_wc__db_wcr
         SVN_ERR(svn_sqlite__get_statement(&stmt, src_wcroot->sdb,
                              STMT_INSERT_WORKING_NODE_COPY_FROM_BASE));
 
+      /* Perhaps we should avoid setting moved_here to 0 and leave it
+         null instead? */
       SVN_ERR(svn_sqlite__bindf(stmt, "issisti",
                         src_wcroot->wc_id, src_relpath,
                         dst_relpath,
                         dst_op_depth,
                         svn_relpath_dirname(dst_relpath, iterpool),
                         presence_map, dst_presence,
-                        (apr_int64_t)0));
+                        (apr_int64_t)(is_move ? 1 : 0)));
 
       if (src_op_depth > 0)
         SVN_ERR(svn_sqlite__bind_int64(stmt, 8, src_op_depth));
@@ -3895,7 +3968,7 @@ db_op_copy_shadowed_layer(svn_wc__db_wcr
                          src_wcroot, child_src_relpath, src_op_depth,
                          dst_wcroot, child_dst_relpath, dst_op_depth,
                          del_op_depth,
-                         repos_id, child_repos_relpath, revision,
+                         repos_id, child_repos_relpath, revision, is_move,
                          scratch_pool));
     }
 
@@ -3970,7 +4043,7 @@ op_copy_shadowed_layer_txn(void * baton,
                         ocb->src_wcroot, ocb->src_relpath, src_op_depth,
                         ocb->dst_wcroot, ocb->dst_relpath, dst_op_depth,
                         del_op_depth,
-                        repos_id, repos_relpath, revision,
+                        repos_id, repos_relpath, revision, ocb->is_move,
                         scratch_pool));
 
   return SVN_NO_ERROR;
@@ -3980,6 +4053,7 @@ svn_error_t *
 svn_wc__db_op_copy_shadowed_layer(svn_wc__db_t *db,
                                   const char *src_abspath,
                                   const char *dst_abspath,
+                                  svn_boolean_t is_move,
                                   apr_pool_t *scratch_pool)
 {
   struct op_copy_baton ocb = {0};
@@ -3999,6 +4073,7 @@ svn_wc__db_op_copy_shadowed_layer(svn_wc
                                                 scratch_pool, scratch_pool));
   VERIFY_USABLE_WCROOT(ocb.dst_wcroot);
 
+  ocb.is_move = is_move;
   ocb.work_items = NULL;
 
   /* Call with the sdb in src_wcroot. It might call itself again to
@@ -4178,6 +4253,7 @@ svn_wc__db_op_copy_dir(svn_wc__db_t *db,
                        const char *original_uuid,
                        svn_revnum_t original_revision,
                        const apr_array_header_t *children,
+                       svn_boolean_t is_move,
                        svn_depth_t depth,
                        const svn_skel_t *conflict,
                        const svn_skel_t *work_items,
@@ -4209,7 +4285,7 @@ svn_wc__db_op_copy_dir(svn_wc__db_t *db,
   iwb.changed_rev = changed_rev;
   iwb.changed_date = changed_date;
   iwb.changed_author = changed_author;
-  iwb.moved_here = FALSE;
+  iwb.moved_here = is_move;
 
   if (original_root_url != NULL)
     {
@@ -6222,10 +6298,10 @@ struct op_delete_baton_t {
 };
 
 static svn_error_t *
-op_delete_txn(void *baton,
-              svn_wc__db_wcroot_t *wcroot,
-              const char *local_relpath,
-              apr_pool_t *scratch_pool)
+delete_node(void *baton,
+            svn_wc__db_wcroot_t *wcroot,
+            const char *local_relpath,
+            apr_pool_t *scratch_pool)
 {
   struct op_delete_baton_t *b = baton;
   svn_wc__db_status_t status;
@@ -6236,8 +6312,6 @@ op_delete_txn(void *baton,
   svn_boolean_t refetch_depth = FALSE;
   svn_kind_t kind;
 
-  SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb, STMT_CREATE_DELETE_LIST));
-
   SVN_ERR(read_info(&status,
                     &kind, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
                     NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
@@ -6403,6 +6477,33 @@ op_delete_txn(void *baton,
         }
 
       select_depth = relpath_depth(local_relpath);
+
+      /* When deleting a moved-here op-root, clear moved-to data at the
+       * pre-move location, transforming the move into a normal delete.
+       * This way, deleting the copied half of a move has the same effect
+       * as reverting it. */
+      if (status == svn_wc__db_status_added ||
+          status == svn_wc__db_status_moved_here)
+        {
+          const char *moved_from_relpath;
+          const char *moved_from_op_root_relpath;
+
+          SVN_ERR(scan_addition(&status, NULL, NULL, NULL, NULL, NULL, NULL,
+                                &moved_from_relpath,
+                                &moved_from_op_root_relpath,
+                                wcroot, local_relpath,
+                                scratch_pool, scratch_pool));
+          if (status == svn_wc__db_status_moved_here &&
+              moved_from_relpath && moved_from_op_root_relpath &&
+              strcmp(moved_from_relpath, moved_from_op_root_relpath) == 0)
+            {
+              SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
+                                                STMT_CLEAR_MOVED_TO_RELPATH));
+              SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
+                                        moved_from_op_root_relpath));
+              SVN_ERR(svn_sqlite__step_done(stmt));
+            }
+        }
     }
   else
     {
@@ -6472,6 +6573,51 @@ op_delete_txn(void *baton,
   return SVN_NO_ERROR;
 }
 
+static svn_error_t *
+op_delete_txn(void *baton,
+              svn_wc__db_wcroot_t *wcroot,
+              const char *local_relpath,
+              apr_pool_t *scratch_pool)
+{
+
+  SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb, STMT_CREATE_DELETE_LIST));
+  SVN_ERR(delete_node(baton, wcroot, local_relpath, scratch_pool));
+  return SVN_NO_ERROR;
+}
+
+
+struct op_delete_many_baton_t {
+  apr_array_header_t *rel_targets;
+} op_delete_many_baton_t;
+
+static svn_error_t *
+op_delete_many_txn(void *baton,
+                   svn_wc__db_wcroot_t *wcroot,
+                   const char *local_relpath,
+                   apr_pool_t *scratch_pool)
+{
+  struct op_delete_many_baton_t *odmb = baton;
+  int i;
+  apr_pool_t *iterpool;
+
+  SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb, STMT_CREATE_DELETE_LIST));
+  iterpool = svn_pool_create(scratch_pool);
+  for (i = 0; i < odmb->rel_targets->nelts; i++)
+    {
+      const char *target_relpath = APR_ARRAY_IDX(odmb->rel_targets, i,
+                                                 const char *);
+      struct op_delete_baton_t odb;
+      
+      svn_pool_clear(iterpool);
+      odb.delete_depth = relpath_depth(target_relpath);
+      odb.moved_to_relpath = NULL;
+      SVN_ERR(delete_node(&odb, wcroot, target_relpath, iterpool));
+    }
+  svn_pool_destroy(iterpool);
+
+  return SVN_NO_ERROR;
+}
+
 
 static svn_error_t *
 do_delete_notify(void *baton,
@@ -6498,9 +6644,6 @@ do_delete_notify(void *baton,
 
       svn_pool_clear(iterpool);
 
-      if (cancel_func)
-        SVN_ERR(cancel_func(cancel_baton));
-
       notify_relpath = svn_sqlite__column_text(stmt, 0, NULL);
       notify_abspath = svn_dirent_join(wcroot->abspath,
                                        notify_relpath,
@@ -6516,7 +6659,15 @@ do_delete_notify(void *baton,
     }
   svn_pool_destroy(iterpool);
 
-  return svn_error_trace(svn_sqlite__reset(stmt));
+  SVN_ERR(svn_sqlite__reset(stmt));
+
+  /* We only allow cancellation after notification for all deleted nodes
+   * has happened. The nodes are already deleted so we should notify for
+   * all of them. */
+  if (cancel_func)
+    SVN_ERR(cancel_func(cancel_baton));
+
+  return SVN_NO_ERROR;
 }
 
 
@@ -6582,6 +6733,67 @@ svn_wc__db_op_delete(svn_wc__db_t *db,
 }
 
 
+svn_error_t *
+svn_wc__db_op_delete_many(svn_wc__db_t *db,
+                          apr_array_header_t *targets,
+                          svn_cancel_func_t cancel_func,
+                          void *cancel_baton,
+                          svn_wc_notify_func2_t notify_func,
+                          void *notify_baton,
+                          apr_pool_t *scratch_pool)
+{
+  svn_wc__db_wcroot_t *wcroot;
+  const char *local_relpath;
+  struct op_delete_many_baton_t odmb;
+  int i;
+  apr_pool_t *iterpool;
+
+  odmb.rel_targets = apr_array_make(scratch_pool, targets->nelts,
+                                    sizeof(const char *));
+  iterpool = svn_pool_create(scratch_pool);
+  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
+                                                db,
+                                                APR_ARRAY_IDX(targets, 0,
+                                                              const char *),
+                                                scratch_pool, iterpool));
+  VERIFY_USABLE_WCROOT(wcroot);
+  for (i = 0; i < targets->nelts; i++)
+    {
+      const char *local_abspath = APR_ARRAY_IDX(targets, i, const char*);
+      svn_wc__db_wcroot_t *target_wcroot;
+
+      svn_pool_clear(iterpool);
+
+      SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&target_wcroot,
+                                                    &local_relpath, db,
+                                                    APR_ARRAY_IDX(targets, i,
+                                                                  const char *),
+                                                    scratch_pool, iterpool));
+      VERIFY_USABLE_WCROOT(target_wcroot);
+      SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
+
+      /* Assert that all targets are within the same working copy. */
+      SVN_ERR_ASSERT(wcroot->wc_id == target_wcroot->wc_id);
+
+      APR_ARRAY_PUSH(odmb.rel_targets, const char *) = local_relpath;
+      SVN_ERR(flush_entries(target_wcroot, local_abspath, svn_depth_infinity,
+                            iterpool));
+
+    }
+  svn_pool_destroy(iterpool);
+  
+  /* Perform the deletion operation (transactionally), perform any
+     notifications necessary, and then clean out our temporary tables.  */
+  return svn_error_trace(with_finalization(wcroot, wcroot->abspath,
+                                           op_delete_many_txn, &odmb,
+                                           do_delete_notify, NULL,
+                                           cancel_func, cancel_baton,
+                                           notify_func, notify_baton,
+                                           STMT_FINALIZE_DELETE,
+                                           scratch_pool));
+}
+
+
 /* Like svn_wc__db_read_info(), but taking WCROOT+LOCAL_RELPATH instead of
    DB+LOCAL_ABSPATH, and outputting repos ids instead of URL+UUID. */
 static svn_error_t *
@@ -6728,18 +6940,10 @@ read_info(svn_wc__db_status_t *status,
             }
           else
             {
-              svn_error_t *err2;
-              err2 = svn_sqlite__column_checksum(checksum, stmt_info, 6,
-                                                 result_pool);
 
-              if (err2 != NULL)
-                err = svn_error_compose_create(
-                         err,
-                         svn_error_createf(
-                               err->apr_err, err2,
-                              _("The node '%s' has a corrupt checksum value."),
-                              path_for_error_message(wcroot, local_relpath,
-                                                     scratch_pool)));
+              err = svn_error_compose_create(
+                        err, svn_sqlite__column_checksum(checksum, stmt_info, 6,
+                                                         result_pool));
             }
         }
       if (recorded_size)
@@ -6922,6 +7126,12 @@ read_info(svn_wc__db_status_t *status,
   if (stmt_act != NULL)
     err = svn_error_compose_create(err, svn_sqlite__reset(stmt_act));
 
+  if (err && err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
+    err = svn_error_quick_wrap(err, 
+                               apr_psprintf(scratch_pool,
+                                            "Error reading node '%s'",
+                                            local_relpath));
+
   SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt_info)));
 
   return SVN_NO_ERROR;
@@ -7186,9 +7396,7 @@ read_children_info(void *baton,
           child_item->info.moved_here = svn_sqlite__column_boolean(stmt, 20);
         }
 
-      err = svn_sqlite__step(&have_row, stmt);
-      if (err)
-        SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
+      SVN_ERR(svn_sqlite__step(&have_row, stmt));
     }
 
   SVN_ERR(svn_sqlite__reset(stmt));
@@ -7242,9 +7450,7 @@ read_children_info(void *baton,
         apr_hash_set(conflicts, apr_pstrdup(result_pool, name),
                      APR_HASH_KEY_STRING, "");
 
-      err = svn_sqlite__step(&have_row, stmt);
-      if (err)
-        SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
+      SVN_ERR(svn_sqlite__step(&have_row, stmt));
     }
 
   SVN_ERR(svn_sqlite__reset(stmt));
@@ -7479,9 +7685,7 @@ svn_wc__db_read_children_walker_info(apr
       apr_hash_set(*nodes, apr_pstrdup(result_pool, name),
                    APR_HASH_KEY_STRING, child);
 
-      err = svn_sqlite__step(&have_row, stmt);
-      if (err)
-        SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
+      SVN_ERR(svn_sqlite__step(&have_row, stmt));
     }
 
   SVN_ERR(svn_sqlite__reset(stmt));
@@ -8541,6 +8745,7 @@ commit_node(void *baton,
   apr_int64_t repos_id;
   const char *repos_relpath;
   apr_int64_t op_depth;
+  svn_wc__db_status_t old_presence;
 
     /* If we are adding a file or directory, then we need to get
      repository information from the parent node since "this node" does
@@ -8603,6 +8808,8 @@ commit_node(void *baton,
   if (cb->keep_changelist && have_act)
     changelist = svn_sqlite__column_text(stmt_act, 1, scratch_pool);
 
+  old_presence = svn_sqlite__column_token(stmt_info, 3, presence_map);
+
   /* ### other stuff?  */
 
   SVN_ERR(svn_sqlite__reset(stmt_info));
@@ -8658,8 +8865,10 @@ commit_node(void *baton,
   else
     parent_relpath = svn_relpath_dirname(local_relpath, scratch_pool);
 
-  /* ### other presences? or reserve that for separate functions?  */
-  new_presence = svn_wc__db_status_normal;
+  /* Preserve any incomplete status */
+  new_presence = (old_presence == svn_wc__db_status_incomplete
+                  ? svn_wc__db_status_incomplete
+                  : svn_wc__db_status_normal);
 
   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
                                     STMT_APPLY_CHANGES_TO_BASE_NODE));
@@ -9639,6 +9848,7 @@ scan_addition_txn(void *baton,
     {
       SVN_ERR_ASSERT(*sab->status == svn_wc__db_status_added
                      || *sab->status == svn_wc__db_status_copied
+                     || *sab->status == svn_wc__db_status_incomplete
                      || *sab->status == svn_wc__db_status_moved_here);
       if (*sab->status == svn_wc__db_status_added)
         {
@@ -9892,8 +10102,8 @@ scan_deletion_txn(void *baton,
                                                         local_relpath,
                                                         scratch_pool));
 
-      /* ### incomplete not handled */
       SVN_ERR_ASSERT(work_presence == svn_wc__db_status_normal
+                     || work_presence == svn_wc__db_status_incomplete
                      || work_presence == svn_wc__db_status_not_present
                      || work_presence == svn_wc__db_status_base_deleted);
 
@@ -9958,9 +10168,6 @@ scan_deletion_txn(void *baton,
           else
             {
               const char *moved_child_relpath;
-              svn_wc__db_status_t moved_child_status;
-              svn_boolean_t found_child;
-              svn_error_t *err;
 
               /* The CURRENT_RELPATH is the op_root of the delete-half of
                * the move. LOCAL_RELPATH is a child that was moved along.
@@ -9973,65 +10180,19 @@ scan_deletion_txn(void *baton,
                                                   moved_child_relpath,
                                                   scratch_pool);
               
-              /* Figure out what happened to the child after it was moved
-               * along. Maybe the child was moved-away further, either by
-               * itself, or along with some intermediate parent node.
-               *
-               * If the child was deleted instead of moved-away,
-               * the resulting MOVED_TO_RELPATH is NULL. */
-              err = read_info(&moved_child_status, NULL, NULL, NULL, NULL,
-                              NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-                              NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-                              NULL, NULL, NULL, NULL, NULL, NULL, wcroot,
-                              moved_to_relpath, scratch_pool, scratch_pool);
-              if (err)
-                {
-                  if (err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
-                    {
-                      /* Tolerate missing children. A likely cause is that
-                       * the moved-to information in BASE is incorrect.
-                       * Just treat this as a normal deletion. */
-                      svn_error_clear(err); 
-                      moved_to_relpath = NULL;
-                      moved_to_op_root_relpath = NULL;
-                      found_child = FALSE;
-                    }
-                  else
-                    return svn_error_compose_create(err,
-                                                    svn_sqlite__reset(stmt));
-                }
-              else
-                found_child = TRUE;
-
-              if (found_child &&
-                  moved_child_status == svn_wc__db_status_deleted)
-                {
-                  err = scan_deletion(NULL, &moved_to_relpath, NULL,
-                                      NULL, wcroot, moved_to_relpath,
-                                      scratch_pool, scratch_pool);
-                  if (err)
-                   return svn_error_compose_create(err,
-                                                   svn_sqlite__reset(stmt));
-                }
               if (sd_baton->moved_to_relpath)
                 *sd_baton->moved_to_relpath = moved_to_relpath ? 
                   apr_pstrdup(sd_baton->result_pool, moved_to_relpath) : NULL;
             }
 
-          /* CURRENT_RELPATH is the op_root of the delete-half of the move,
-           * so it's the BASE_DEL_RELPATH. */
-          if (sd_baton->base_del_relpath)
-            *sd_baton->base_del_relpath =
-              apr_pstrdup(sd_baton->result_pool, current_relpath);
-
           if (sd_baton->moved_to_op_root_relpath)
             *sd_baton->moved_to_op_root_relpath = moved_to_op_root_relpath ?
               apr_pstrdup(sd_baton->result_pool, moved_to_op_root_relpath)
               : NULL;
 
-          /* If all other out parameters are irrelevant, stop scanning.
-           * Happens to be only WORK_DEL_RELPATH. */
-          if (sd_baton->work_del_relpath == NULL)
+          /* If all other out parameters are irrelevant, stop scanning. */
+          if (sd_baton->work_del_relpath == NULL
+              && sd_baton->base_del_relpath == NULL)
             break;
 
           found_moved_to = TRUE;
@@ -11334,7 +11495,7 @@ svn_wc__db_wclock_obtain(svn_wc__db_t *d
           svn_wc__db_wclock_t* lock = &APR_ARRAY_IDX(wcroot->owned_locks,
                                                      i, svn_wc__db_wclock_t);
 
-          if (svn_relpath__is_ancestor(lock->local_relpath, local_relpath)
+          if (svn_relpath_skip_ancestor(lock->local_relpath, local_relpath)
               && (lock->levels == -1
                   || (lock->levels + relpath_depth(lock->local_relpath))
                             >= depth))
@@ -11399,7 +11560,7 @@ is_wclocked(void *baton,
     {
       const char *relpath = svn_sqlite__column_text(stmt, 0, NULL);
 
-      if (svn_relpath__is_ancestor(relpath, dir_relpath))
+      if (svn_relpath_skip_ancestor(relpath, dir_relpath))
         {
           /* Any row here means there can be no locks closer to root
              that extend past here. */
@@ -11534,7 +11695,7 @@ wclock_owns_lock(svn_boolean_t *own_lock
           svn_wc__db_wclock_t *lock = &APR_ARRAY_IDX(owned_locks, i,
                                                      svn_wc__db_wclock_t);
 
-          if (svn_relpath__is_ancestor(lock->local_relpath, local_relpath)
+          if (svn_relpath_skip_ancestor(lock->local_relpath, local_relpath)
               && (lock->levels == -1
                   || ((relpath_depth(lock->local_relpath) + lock->levels)
                       >= lock_level)))
@@ -11590,6 +11751,9 @@ end_directory_update(void *baton,
                         NULL, NULL, NULL, NULL, NULL, NULL,
                         wcroot, local_relpath, scratch_pool, scratch_pool));
 
+  if (base_status == svn_wc__db_status_normal)
+    return SVN_NO_ERROR;
+
   SVN_ERR_ASSERT(base_status == svn_wc__db_status_incomplete);
 
   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
@@ -12136,8 +12300,8 @@ svn_wc__db_get_not_present_descendants(c
           const char *found_relpath = svn_sqlite__column_text(stmt, 0, NULL);
 
           APR_ARRAY_PUSH(paths, const char *)
-              = svn_relpath__is_child(local_relpath, found_relpath,
-                                      result_pool);
+              = apr_pstrdup(result_pool, svn_relpath_skip_ancestor(
+                                           local_relpath, found_relpath));
 
           SVN_ERR(svn_sqlite__step(&have_row, stmt));
         }
@@ -12226,8 +12390,8 @@ svn_wc__db_min_max_revisions(svn_revnum_
 }
 
 
-/* Like svn_wc__db_is_sparse_checkout,
- * but accepts a WCROOT/LOCAL_RELPATH pair. */
+/* Set *IS_SPARSE_CHECKOUT TRUE if LOCAL_RELPATH or any of the nodes
+ * within LOCAL_RELPATH is sparse, FALSE otherwise. */
 static svn_error_t *
 is_sparse_checkout_internal(svn_boolean_t *is_sparse_checkout,
                             svn_wc__db_wcroot_t *wcroot,
@@ -12251,28 +12415,6 @@ is_sparse_checkout_internal(svn_boolean_
 }
 
 
-svn_error_t *
-svn_wc__db_is_sparse_checkout(svn_boolean_t *is_sparse_checkout,
-                              svn_wc__db_t *db,
-                              const char *local_abspath,
-                              apr_pool_t *scratch_pool)
-{
-  svn_wc__db_wcroot_t *wcroot;
-  const char *local_relpath;
-
-  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
-
-  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
-                                                db, local_abspath,
-                                                scratch_pool, scratch_pool));
-  VERIFY_USABLE_WCROOT(wcroot);
-
-  return svn_error_trace(is_sparse_checkout_internal(is_sparse_checkout,
-                                                     wcroot, local_relpath,
-                                                     scratch_pool));
-}
-
-
 /* Like svn_wc__db_has_switched_subtrees(),
  * but accepts a WCROOT/LOCAL_RELPATH pair. */
 static svn_error_t *