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/08/22 17:21:15 UTC

svn commit: r1160299 [2/3] - in /subversion/branches/fs-py: ./ build/ notes/ subversion/bindings/ctypes-python/test/ subversion/bindings/javahl/native/ subversion/bindings/javahl/src/org/apache/subversion/javahl/ subversion/bindings/javahl/src/org/tigr...

Modified: subversion/branches/fs-py/subversion/libsvn_client/status.c
URL: http://svn.apache.org/viewvc/subversion/branches/fs-py/subversion/libsvn_client/status.c?rev=1160299&r1=1160298&r2=1160299&view=diff
==============================================================================
--- subversion/branches/fs-py/subversion/libsvn_client/status.c (original)
+++ subversion/branches/fs-py/subversion/libsvn_client/status.c Mon Aug 22 15:21:13 2011
@@ -85,12 +85,22 @@ tweak_status(void *baton,
   /* If the status item has an entry, but doesn't belong to one of the
      changelists our caller is interested in, we filter out this status
      transmission.  */
-  if (sb->changelist_hash
-      && (! status->changelist
-          || ! apr_hash_get(sb->changelist_hash, status->changelist,
-                            APR_HASH_KEY_STRING)))
+  if (sb->changelist_hash)
     {
-      return SVN_NO_ERROR;
+      if (status->changelist)
+        {
+          /* Skip unless the caller requested this changelist. */
+          if (! apr_hash_get(sb->changelist_hash, status->changelist,
+                             APR_HASH_KEY_STRING))
+            return SVN_NO_ERROR;
+        }
+      else
+        {
+          /* Skip unless the caller requested changelist-lacking items. */
+          if (! apr_hash_get(sb->changelist_hash, "",
+                             APR_HASH_KEY_STRING))
+            return SVN_NO_ERROR;
+        }
     }
 
   /* If we know that the target was deleted in HEAD of the repository,
@@ -570,10 +580,6 @@ svn_client_status_dup(const svn_client_s
   if (status->moved_to_abspath)
     st->moved_to_abspath = apr_pstrdup(result_pool, status->moved_to_abspath);
 
-  if (status->moved_to_op_root_abspath)
-    st->moved_to_op_root_abspath =
-      apr_pstrdup(result_pool, status->moved_to_op_root_abspath);
-
   return st;
 }
 
@@ -680,7 +686,6 @@ svn_client__create_status(svn_client_sta
 
   (*cst)->moved_from_abspath = status->moved_from_abspath;
   (*cst)->moved_to_abspath = status->moved_to_abspath;
-  (*cst)->moved_to_op_root_abspath = status->moved_to_op_root_abspath;
 
   return SVN_NO_ERROR;
 }

Modified: subversion/branches/fs-py/subversion/libsvn_ra_serf/blame.c
URL: http://svn.apache.org/viewvc/subversion/branches/fs-py/subversion/libsvn_ra_serf/blame.c?rev=1160299&r1=1160298&r2=1160299&view=diff
==============================================================================
--- subversion/branches/fs-py/subversion/libsvn_ra_serf/blame.c (original)
+++ subversion/branches/fs-py/subversion/libsvn_ra_serf/blame.c Mon Aug 22 15:21:13 2011
@@ -159,8 +159,7 @@ create_propval(blame_info_t *info)
                                     info->prop_attr_len + 1);
     }
 
-  /* Include the null term. */
-  s = svn_string_ncreate(info->prop_attr, info->prop_attr_len + 1, info->pool);
+  s = svn_string_ncreate(info->prop_attr, info->prop_attr_len, info->pool);
   if (info->prop_base64)
     {
       s = svn_base64_decode_string(s, info->pool);

Modified: subversion/branches/fs-py/subversion/libsvn_ra_serf/commit.c
URL: http://svn.apache.org/viewvc/subversion/branches/fs-py/subversion/libsvn_ra_serf/commit.c?rev=1160299&r1=1160298&r2=1160299&view=diff
==============================================================================
--- subversion/branches/fs-py/subversion/libsvn_ra_serf/commit.c (original)
+++ subversion/branches/fs-py/subversion/libsvn_ra_serf/commit.c Mon Aug 22 15:21:13 2011
@@ -94,7 +94,7 @@ typedef struct commit_context_t {
 typedef struct proppatch_context_t {
   apr_pool_t *pool;
 
-  const char *name;
+  const char *relpath;
   const char *path;
 
   commit_context_t *commit;
@@ -146,7 +146,10 @@ typedef struct dir_context_t {
   /* Our parent */
   struct dir_context_t *parent_dir;
 
-  /* The directory name; if NULL, we're the 'root' */
+  /* The directory name; if "", we're the 'root' */
+  const char *relpath;
+
+  /* The basename of the directory. "" for the 'root' */
   const char *name;
 
   /* The base revision of the dir. */
@@ -178,6 +181,7 @@ typedef struct file_context_t {
 
   dir_context_t *parent_dir;
 
+  const char *relpath;
   const char *name;
 
   /* The checked out context for this file. */
@@ -311,44 +315,6 @@ handle_checkout(serf_request_t *request,
   return err;
 }
 
-/* Return the relative path from DIR's topmost parent to DIR, in
-   Subversion's internal path style, allocated in POOL.  Use POOL for
-   temporary work as well.  */
-static const char *
-relative_dir_path(dir_context_t *dir, apr_pool_t *pool)
-{
-  const char *rel_path = "";
-  apr_array_header_t *components;
-  dir_context_t *dir_ptr = dir;
-  int i;
-
-  components = apr_array_make(pool, 1, sizeof(const char *));
-
-  for (dir_ptr = dir; dir_ptr; dir_ptr = dir_ptr->parent_dir)
-    APR_ARRAY_PUSH(components, const char *) = dir_ptr->name;
-
-  for (i = 0; i < components->nelts; i++)
-    {
-      rel_path = svn_relpath_join(rel_path,
-                                  APR_ARRAY_IDX(components, i, const char *),
-                                  pool);
-    }
-
-  return rel_path;
-}
-
-
-/* Return the relative path from FILE's topmost parent to FILE, in
-   Subversion's internal path style, allocated in POOL.  Use POOL for
-   temporary work as well.  */
-static const char *
-relative_file_path(file_context_t *f, apr_pool_t *pool)
-{
-  const char *dir_path = relative_dir_path(f->parent_dir, pool);
-  return svn_relpath_join(dir_path, f->name, pool);
-}
-
-
 static svn_error_t *
 checkout_dir(dir_context_t *dir)
 {
@@ -375,8 +341,7 @@ checkout_dir(dir_context_t *dir)
           dir->checkout->activity_url = dir->commit->activity_url;
           dir->checkout->resource_url =
             svn_path_url_add_component2(dir->parent_dir->checkout->resource_url,
-                                        svn_relpath_basename(dir->name, NULL),
-                                        dir->pool);
+                                        dir->name, dir->pool);
 
           return SVN_NO_ERROR;
         }
@@ -428,8 +393,7 @@ checkout_dir(dir_context_t *dir)
       if (err->apr_err == SVN_ERR_FS_CONFLICT)
         SVN_ERR_W(err, apr_psprintf(dir->pool,
                   _("Directory '%s' is out of date; try updating"),
-                  svn_dirent_local_style(relative_dir_path(dir, dir->pool),
-                                         dir->pool)));
+                  svn_dirent_local_style(dir->relpath, dir->pool)));
       return err;
     }
 
@@ -563,8 +527,8 @@ checkout_file(file_context_t *file)
           file->checkout->activity_url = file->commit->activity_url;
           file->checkout->resource_url =
             svn_path_url_add_component2(parent_dir->checkout->resource_url,
-                                        svn_relpath__is_child(parent_dir->name,
-                                                              file->name,
+                                        svn_relpath__is_child(parent_dir->relpath,
+                                                              file->relpath,
                                                               file->pool),
                                         file->pool);
           return SVN_NO_ERROR;
@@ -585,7 +549,7 @@ checkout_file(file_context_t *file)
 
   SVN_ERR(get_version_url(&(file->checkout->checkout_url),
                           file->commit->session, file->commit->conn,
-                          file->name, file->base_revision,
+                          file->relpath, file->base_revision,
                           NULL, file->pool));
 
   handler->body_delegate = create_checkout_body;
@@ -611,8 +575,7 @@ checkout_file(file_context_t *file)
       if (err->apr_err == SVN_ERR_FS_CONFLICT)
         SVN_ERR_W(err, apr_psprintf(file->pool,
                   _("File '%s' is out of date; try updating"),
-                  svn_dirent_local_style(relative_file_path(file, file->pool),
-                                         file->pool)));
+                  svn_dirent_local_style(file->relpath, file->pool)));
       return err;
     }
 
@@ -839,11 +802,11 @@ setup_proppatch_headers(serf_bucket_t *h
                                            proppatch->base_revision));
     }
 
-  if (proppatch->name && proppatch->commit->lock_tokens)
+  if (proppatch->relpath && proppatch->commit->lock_tokens)
     {
       const char *token;
 
-      token = apr_hash_get(proppatch->commit->lock_tokens, proppatch->name,
+      token = apr_hash_get(proppatch->commit->lock_tokens, proppatch->relpath,
                            APR_HASH_KEY_STRING);
 
       if (token)
@@ -1061,7 +1024,7 @@ setup_put_headers(serf_bucket_t *headers
     {
       const char *token;
 
-      token = apr_hash_get(ctx->commit->lock_tokens, ctx->name,
+      token = apr_hash_get(ctx->commit->lock_tokens, ctx->relpath,
                            APR_HASH_KEY_STRING);
 
       if (token)
@@ -1118,9 +1081,8 @@ setup_copy_dir_headers(serf_bucket_t *he
   else
     {
       uri.path = (char *)svn_path_url_add_component2(
-        dir->parent_dir->checkout->resource_url,
-        svn_relpath_basename(dir->name, pool),
-        pool);
+                                    dir->parent_dir->checkout->resource_url,
+                                    dir->name, pool);
     }
   absolute_uri = apr_uri_unparse(pool, &uri, 0);
 
@@ -1398,6 +1360,7 @@ open_root(void *edit_baton,
       dir->pool = dir_pool;
       dir->commit = ctx;
       dir->base_revision = base_revision;
+      dir->relpath = "";
       dir->name = "";
       dir->changed_props = apr_hash_make(dir->pool);
       dir->removed_props = apr_hash_make(dir->pool);
@@ -1479,12 +1442,13 @@ open_root(void *edit_baton,
       dir->pool = dir_pool;
       dir->commit = ctx;
       dir->base_revision = base_revision;
+      dir->relpath = "";
       dir->name = "";
       dir->changed_props = apr_hash_make(dir->pool);
       dir->removed_props = apr_hash_make(dir->pool);
 
       SVN_ERR(get_version_url(&dir->url, dir->commit->session,
-                              dir->commit->conn, dir->name,
+                              dir->commit->conn, dir->relpath,
                               dir->base_revision, ctx->checked_in_url,
                               dir->pool));
       ctx->checked_in_url = dir->url;
@@ -1563,7 +1527,7 @@ delete_entry(const char *path,
       SVN_ERR(checkout_dir(dir));
       delete_target = svn_path_url_add_component2(dir->checkout->resource_url,
                                                   svn_relpath_basename(path,
-                                                                       pool),
+                                                                       NULL),
                                                   pool);
     }
 
@@ -1658,7 +1622,8 @@ add_directory(const char *path,
   dir->base_revision = SVN_INVALID_REVNUM;
   dir->copy_revision = copyfrom_revision;
   dir->copy_path = copyfrom_path;
-  dir->name = apr_pstrdup(dir->pool, path);
+  dir->relpath = apr_pstrdup(dir->pool, path);
+  dir->name = svn_relpath_basename(dir->relpath, NULL);
   dir->changed_props = apr_hash_make(dir->pool);
   dir->removed_props = apr_hash_make(dir->pool);
 
@@ -1674,11 +1639,10 @@ add_directory(const char *path,
       SVN_ERR(checkout_dir(parent));
 
       dir->url = svn_path_url_add_component2(parent->commit->checked_in_url,
-                                             path, dir->pool);
+                                             dir->name, dir->pool);
       mkcol_target = svn_path_url_add_component2(
                                parent->checkout->resource_url,
-                               svn_relpath_basename(path, dir->pool),
-                               dir->pool);
+                               dir->name, dir->pool);
     }
 
   handler = apr_pcalloc(dir->pool, sizeof(*handler));
@@ -1772,7 +1736,8 @@ open_directory(const char *path,
 
   dir->added = FALSE;
   dir->base_revision = base_revision;
-  dir->name = apr_pstrdup(dir->pool, path);
+  dir->relpath = apr_pstrdup(dir->pool, path);
+  dir->name = svn_relpath_basename(dir->relpath, NULL);
   dir->changed_props = apr_hash_make(dir->pool);
   dir->removed_props = apr_hash_make(dir->pool);
 
@@ -1785,7 +1750,7 @@ open_directory(const char *path,
     {
       SVN_ERR(get_version_url(&dir->url,
                               dir->commit->session, dir->commit->conn,
-                              dir->name, dir->base_revision,
+                              dir->relpath, dir->base_revision,
                               dir->commit->checked_in_url, dir->pool));
     }
   *child_baton = dir;
@@ -1863,7 +1828,7 @@ close_directory(void *dir_baton,
       proppatch_ctx->pool = pool;
       proppatch_ctx->progress.pool = pool;
       proppatch_ctx->commit = dir->commit;
-      proppatch_ctx->name = dir->name;
+      proppatch_ctx->relpath = dir->relpath;
       proppatch_ctx->changed_props = dir->changed_props;
       proppatch_ctx->removed_props = dir->removed_props;
       proppatch_ctx->base_revision = dir->base_revision;
@@ -1902,7 +1867,8 @@ add_file(const char *path,
 
   new_file->parent_dir = dir;
   new_file->commit = dir->commit;
-  new_file->name = apr_pstrdup(new_file->pool, path);
+  new_file->relpath = apr_pstrdup(new_file->pool, path);
+  new_file->name = svn_relpath_basename(new_file->relpath, NULL);
   new_file->added = TRUE;
   new_file->base_revision = SVN_INVALID_REVNUM;
   new_file->copy_path = copy_path;
@@ -1925,8 +1891,7 @@ add_file(const char *path,
 
       new_file->url =
         svn_path_url_add_component2(dir->checkout->resource_url,
-                                    svn_relpath_basename(path, new_file->pool),
-                                    new_file->pool);
+                                    new_file->name, new_file->pool);
     }
 
   while (deleted_parent && deleted_parent[0] != '\0')
@@ -1992,7 +1957,8 @@ open_file(const char *path,
 
   new_file->parent_dir = parent;
   new_file->commit = parent->commit;
-  new_file->name = apr_pstrdup(new_file->pool, path); /* TODO: basename? */
+  new_file->relpath = apr_pstrdup(new_file->pool, path);
+  new_file->name = svn_relpath_basename(new_file->relpath, NULL);
   new_file->added = FALSE;
   new_file->base_revision = base_revision;
   new_file->changed_props = apr_hash_make(new_file->pool);
@@ -2213,7 +2179,7 @@ close_file(void *file_baton,
       proppatch = apr_pcalloc(ctx->pool, sizeof(*proppatch));
       proppatch->pool = ctx->pool;
       proppatch->progress.pool = pool;
-      proppatch->name = ctx->name;
+      proppatch->relpath = ctx->relpath;
       proppatch->path = ctx->url;
       proppatch->commit = ctx->commit;
       proppatch->changed_props = ctx->changed_props;

Modified: subversion/branches/fs-py/subversion/libsvn_ra_serf/locks.c
URL: http://svn.apache.org/viewvc/subversion/branches/fs-py/subversion/libsvn_ra_serf/locks.c?rev=1160299&r1=1160298&r2=1160299&view=diff
==============================================================================
--- subversion/branches/fs-py/subversion/libsvn_ra_serf/locks.c (original)
+++ subversion/branches/fs-py/subversion/libsvn_ra_serf/locks.c Mon Aug 22 15:21:13 2011
@@ -580,6 +580,8 @@ svn_ra_serf__lock(svn_ra_session_t *ra_s
 
   subpool = svn_pool_create(pool);
 
+  /* ### TODO for issue 2263: Send all the locks over the wire at once.  This
+     loop is just a temporary shim. */
   for (hi = apr_hash_first(pool, path_revs); hi; hi = apr_hash_next(hi))
     {
       svn_ra_serf__handler_t *handler;
@@ -683,6 +685,8 @@ svn_ra_serf__unlock(svn_ra_session_t *ra
 
   subpool = svn_pool_create(pool);
 
+  /* ### TODO for issue 2263: Send all the locks over the wire at once.  This
+     loop is just a temporary shim. */
   for (hi = apr_hash_first(pool, path_tokens); hi; hi = apr_hash_next(hi))
     {
       svn_ra_serf__handler_t *handler;

Modified: subversion/branches/fs-py/subversion/libsvn_subr/validate.c
URL: http://svn.apache.org/viewvc/subversion/branches/fs-py/subversion/libsvn_subr/validate.c?rev=1160299&r1=1160298&r2=1160299&view=diff
==============================================================================
--- subversion/branches/fs-py/subversion/libsvn_subr/validate.c (original)
+++ subversion/branches/fs-py/subversion/libsvn_subr/validate.c Mon Aug 22 15:21:13 2011
@@ -45,6 +45,7 @@ svn_mime_type_validate(const char *mime_
      specification, e.g., "text/html; charset=UTF-8", make sure we're
      only looking at the media type here. */
   const apr_size_t len = strcspn(mime_type, "; ");
+  const apr_size_t len2 = strlen(mime_type);
   const char *const slash_pos = strchr(mime_type, '/');
   apr_size_t i;
   const char *tspecials = "()<>@,;:\\\"/[]?=";
@@ -69,7 +70,19 @@ svn_mime_type_validate(const char *mime_
             || (strchr(tspecials, mime_type[i]) != NULL)))
         return svn_error_createf
           (SVN_ERR_BAD_MIME_TYPE, NULL,
-           _("MIME type '%s' contains invalid character '%c'"),
+           _("MIME type '%s' contains invalid character '%c' "
+             "in media type"),
+           mime_type, mime_type[i]);
+    }
+
+  /* Check the whole string for unsafe characters. (issue #2872) */
+  for (i = 0; i < len2; i++)
+    {
+      if (svn_ctype_iscntrl(mime_type[i]) && mime_type[i] != '\t')
+        return svn_error_createf(
+           SVN_ERR_BAD_MIME_TYPE, NULL,
+           _("MIME type '%s' contains invalid character '0x%02x' "
+             "in postfix"),
            mime_type, mime_type[i]);
     }
 

Modified: subversion/branches/fs-py/subversion/libsvn_wc/adm_ops.c
URL: http://svn.apache.org/viewvc/subversion/branches/fs-py/subversion/libsvn_wc/adm_ops.c?rev=1160299&r1=1160298&r2=1160299&view=diff
==============================================================================
--- subversion/branches/fs-py/subversion/libsvn_wc/adm_ops.c (original)
+++ subversion/branches/fs-py/subversion/libsvn_wc/adm_ops.c Mon Aug 22 15:21:13 2011
@@ -29,6 +29,7 @@
 
 
 #include <string.h>
+#include <stdlib.h>
 
 #include <apr_pools.h>
 #include <apr_tables.h>
@@ -1291,6 +1292,136 @@ remove_conflict_file(svn_boolean_t *noti
 }
 
 
+/* Sort copied children obtained from the revert list based on
+ * their paths in descending order (longest paths first). */
+static int
+compare_revert_list_copied_children(const void *a, const void *b)
+{
+  const svn_wc__db_revert_list_copied_child_info_t *ca = a;
+  const svn_wc__db_revert_list_copied_child_info_t *cb = b;
+  int i;
+
+  i = svn_path_compare_paths(ca->abspath, cb->abspath);
+
+  /* Reverse the result of svn_path_compare_paths() to achieve
+   * descending order. */
+  return -i;
+}
+
+
+/* Remove all reverted copied children from the directory at LOCAL_ABSPATH.
+ * If REMOVE_SELF is TRUE, try to remove LOCAL_ABSPATH itself (REMOVE_SELF
+ * should be set if LOCAL_ABSPATH is itself a reverted copy).
+ *
+ * If REMOVED_SELF is not NULL, indicate in *REMOVED_SELF whether
+ * LOCAL_ABSPATH itself was removed.
+ *
+ * All reverted copied file children are removed from disk. Reverted copied
+ * directories left empty as a result are also removed from disk.
+ */
+static svn_error_t *
+revert_restore_handle_copied_dirs(svn_boolean_t *removed_self,
+                                  svn_wc__db_t *db,
+                                  const char *local_abspath,
+                                  svn_boolean_t remove_self,
+                                  svn_cancel_func_t cancel_func,
+                                  void *cancel_baton,
+                                  apr_pool_t *scratch_pool)
+{
+  const apr_array_header_t *copied_children;
+  svn_wc__db_revert_list_copied_child_info_t *child_info;
+  int i;
+  svn_node_kind_t on_disk;
+  apr_pool_t *iterpool;
+  svn_error_t *err;
+
+  if (removed_self)
+    *removed_self = FALSE;
+
+  SVN_ERR(svn_wc__db_revert_list_read_copied_children(&copied_children,
+                                                      db, local_abspath,
+                                                      scratch_pool,
+                                                      scratch_pool));
+  iterpool = svn_pool_create(scratch_pool);
+
+  /* Remove all copied file children. */
+  for (i = 0; i < copied_children->nelts; i++)
+    {
+      child_info = APR_ARRAY_IDX(
+                     copied_children, i,
+                     svn_wc__db_revert_list_copied_child_info_t *);
+
+      if (cancel_func)
+        SVN_ERR(cancel_func(cancel_baton));
+
+      if (child_info->kind != svn_wc__db_kind_file)
+        continue;
+
+      svn_pool_clear(iterpool);
+
+      /* Make sure what we delete from disk is really a file. */
+      SVN_ERR(svn_io_check_path(child_info->abspath, &on_disk, iterpool));
+      if (on_disk != svn_node_file)
+        continue;
+
+      SVN_ERR(svn_io_remove_file2(child_info->abspath, TRUE, iterpool));
+    }
+
+  /* Delete every empty child directory.
+   * We cannot delete children recursively since we want to keep any files
+   * that still exist on disk (e.g. unversioned files within the copied tree).
+   * So sort the children list such that longest paths come first and try to
+   * remove each child directory in order. */
+  qsort(copied_children->elts, copied_children->nelts,
+        sizeof(svn_wc__db_revert_list_copied_child_info_t *),
+        compare_revert_list_copied_children);
+  for (i = 0; i < copied_children->nelts; i++)
+    {
+      child_info = APR_ARRAY_IDX(
+                     copied_children, i,
+                     svn_wc__db_revert_list_copied_child_info_t *);
+
+      if (cancel_func)
+        SVN_ERR(cancel_func(cancel_baton));
+
+      if (child_info->kind != svn_wc__db_kind_dir)
+        continue;
+
+      svn_pool_clear(iterpool);
+
+      err = svn_io_dir_remove_nonrecursive(child_info->abspath, iterpool);
+      if (err)
+        {
+          if (APR_STATUS_IS_ENOENT(err->apr_err) ||
+              SVN__APR_STATUS_IS_ENOTDIR(err->apr_err) ||
+              APR_STATUS_IS_ENOTEMPTY(err->apr_err))
+            svn_error_clear(err);
+          else
+            return svn_error_trace(err);
+        }
+    }
+
+  if (remove_self)
+    {
+      /* Delete LOCAL_ABSPATH itself if no children are left. */
+      err = svn_io_dir_remove_nonrecursive(local_abspath, iterpool);
+      if (err)
+       {
+          if (APR_STATUS_IS_ENOTEMPTY(err->apr_err))
+            svn_error_clear(err);
+          else
+            return svn_error_trace(err);
+        }
+      else if (removed_self)
+        *removed_self = TRUE;
+    }
+
+  svn_pool_destroy(iterpool);
+
+  return SVN_NO_ERROR;
+}
+
+
 /* Make the working tree under LOCAL_ABSPATH to depth DEPTH match the
    versioned tree.  This function is called after svn_wc__db_op_revert
    has done the database revert and created the revert list.  Notifies
@@ -1321,6 +1452,8 @@ revert_restore(svn_wc__db_t *db,
 #ifdef HAVE_SYMLINK
   svn_boolean_t special;
 #endif
+  svn_boolean_t copied_here;
+  svn_wc__db_kind_t reverted_kind;
 
   if (cancel_func)
     SVN_ERR(cancel_func(cancel_baton));
@@ -1328,6 +1461,7 @@ revert_restore(svn_wc__db_t *db,
   SVN_ERR(svn_wc__db_revert_list_read(&notify_required,
                                       &conflict_old, &conflict_new,
                                       &conflict_working, &prop_reject,
+                                      &copied_here, &reverted_kind,
                                       db, local_abspath,
                                       scratch_pool, scratch_pool));
 
@@ -1342,17 +1476,31 @@ revert_restore(svn_wc__db_t *db,
     {
       svn_error_clear(err);
 
-      if (notify_func && notify_required)
-        notify_func(notify_baton,
-                    svn_wc_create_notify(local_abspath, svn_wc_notify_revert,
-                                         scratch_pool),
-                    scratch_pool);
-
-      if (notify_func)
-        SVN_ERR(svn_wc__db_revert_list_notify(notify_func, notify_baton,
-                                              db, local_abspath,
-                                              scratch_pool));
-      return SVN_NO_ERROR;
+      if (!copied_here)
+        {
+          if (notify_func && notify_required)
+            notify_func(notify_baton,
+                        svn_wc_create_notify(local_abspath,
+                                             svn_wc_notify_revert,
+                                             scratch_pool),
+                        scratch_pool);
+
+          if (notify_func)
+            SVN_ERR(svn_wc__db_revert_list_notify(notify_func, notify_baton,
+                                                  db, local_abspath,
+                                                  scratch_pool));
+          return SVN_NO_ERROR;
+        }
+      else
+        {
+          /* ### Initialise to values which prevent the code below from
+           * ### trying to restore anything to disk.
+           * ### 'status' should be status_unknown but that doesn't exist. */
+          status = svn_wc__db_status_normal;
+          kind = svn_wc__db_kind_unknown;
+          recorded_size = SVN_INVALID_FILESIZE;
+          recorded_mod_time = 0;
+        }
     }
   else if (err)
     return svn_error_trace(err);
@@ -1387,6 +1535,27 @@ revert_restore(svn_wc__db_t *db,
 #endif
     }
 
+  if (copied_here)
+    {
+      /* The revert target itself is the op-root of a copy. */
+      if (reverted_kind == svn_wc__db_kind_file && on_disk == svn_node_file)
+        {
+          SVN_ERR(svn_io_remove_file2(local_abspath, TRUE, scratch_pool));
+          on_disk = svn_node_none;
+        }
+      else if (reverted_kind == svn_wc__db_kind_dir && on_disk == svn_node_dir)
+        {
+          svn_boolean_t removed;
+
+          SVN_ERR(revert_restore_handle_copied_dirs(&removed, db,
+                                                    local_abspath, TRUE, 
+                                                    cancel_func, cancel_baton,
+                                                    scratch_pool));
+          if (removed)
+            on_disk = svn_node_none;
+        }
+    }
+
   /* If we expect a versioned item to be present then check that any
      item on disk matches the versioned item, if it doesn't match then
      fix it or delete it.  */
@@ -1567,6 +1736,10 @@ revert_restore(svn_wc__db_t *db,
       const apr_array_header_t *children;
       int i;
 
+      SVN_ERR(revert_restore_handle_copied_dirs(NULL, db, local_abspath, FALSE,
+                                                cancel_func, cancel_baton,
+                                                iterpool));
+
       SVN_ERR(svn_wc__db_read_children_of_working_node(&children, db,
                                                        local_abspath,
                                                        scratch_pool,
@@ -2327,9 +2500,11 @@ svn_wc__internal_changelist_match(svn_wc
       return FALSE;
     }
 
+  /* The empty changelist name is special-cased. */
   return (changelist
-            && apr_hash_get((apr_hash_t *)clhash, changelist,
-                            APR_HASH_KEY_STRING) != NULL);
+          ? apr_hash_get((apr_hash_t *)clhash, changelist, APR_HASH_KEY_STRING)
+          : apr_hash_get((apr_hash_t *)clhash, "", APR_HASH_KEY_STRING)
+         ) != NULL;
 }
 
 

Modified: subversion/branches/fs-py/subversion/libsvn_wc/diff_local.c
URL: http://svn.apache.org/viewvc/subversion/branches/fs-py/subversion/libsvn_wc/diff_local.c?rev=1160299&r1=1160298&r2=1160299&view=diff
==============================================================================
--- subversion/branches/fs-py/subversion/libsvn_wc/diff_local.c (original)
+++ subversion/branches/fs-py/subversion/libsvn_wc/diff_local.c Mon Aug 22 15:21:13 2011
@@ -474,8 +474,29 @@ diff_status_callback(void *baton,
           SVN_ERR(file_diff(eb, local_abspath, path, scratch_pool));
         }
     }
-  else
+  else  /* it's a directory */
     {
+      const char *path = svn_dirent_skip_ancestor(eb->anchor_abspath,
+                                                  local_abspath);
+
+      /* Report the directory as deleted and/or opened or added. */
+      if (status->node_status == svn_wc_status_deleted
+          || status->node_status == svn_wc_status_replaced)
+        SVN_ERR(eb->callbacks->dir_deleted(NULL, NULL, path,
+                                           eb->callback_baton, scratch_pool));
+
+      if (status->node_status == svn_wc_status_added
+          || status->node_status == svn_wc_status_replaced)
+        SVN_ERR(eb->callbacks->dir_added(NULL, NULL, NULL, NULL,
+                                         path, status->revision,
+                                         path, status->revision /* ### ? */,
+                                         eb->callback_baton, scratch_pool));
+      else
+        SVN_ERR(eb->callbacks->dir_opened(NULL, NULL, NULL,
+                                          path, status->revision,
+                                          eb->callback_baton, scratch_pool));
+
+      /* Report the prop change. */
       /* ### This case should probably be extended for git-diff, but this
              is what the old diff code provided */
       if (status->node_status == svn_wc_status_deleted
@@ -484,9 +505,6 @@ diff_status_callback(void *baton,
         {
           apr_array_header_t *propchanges;
           apr_hash_t *baseprops;
-          const char *path = svn_dirent_skip_ancestor(eb->anchor_abspath,
-                                                      local_abspath);
-
 
           SVN_ERR(svn_wc__internal_propdiff(&propchanges, &baseprops,
                                             eb->db, local_abspath,
@@ -498,6 +516,15 @@ diff_status_callback(void *baton,
                                                    eb->callback_baton,
                                                    scratch_pool));
         }
+
+      /* Close the dir.
+       * ### This should be done after all children have been processed, not
+       *     yet.  The current Subversion-internal callers don't care. */
+      SVN_ERR(eb->callbacks->dir_closed(
+                        NULL, NULL, NULL, path,
+                        (status->node_status == svn_wc_status_added
+                         || status->node_status == svn_wc_status_replaced),
+                        eb->callback_baton, scratch_pool));
     }
   return SVN_NO_ERROR;
 }

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=1160299&r1=1160298&r2=1160299&view=diff
==============================================================================
--- subversion/branches/fs-py/subversion/libsvn_wc/status.c (original)
+++ subversion/branches/fs-py/subversion/libsvn_wc/status.c Mon Aug 22 15:21:13 2011
@@ -273,6 +273,68 @@ read_info(const struct svn_wc__db_info_t
                                        &mtb->lock, NULL, NULL,
                                        db, local_abspath,
                                        result_pool, scratch_pool));
+
+      if (mtb->status == svn_wc__db_status_deleted)
+        {
+          const char *moved_to_abspath;
+          const char *moved_to_op_root_abspath;
+          
+          /* NOTE: we can't use op-root-ness as a condition here since a base
+           * node can be the root of a move and still not be an explicit
+           * op-root (having a working node with op_depth == pathelements).
+           *
+           * Both these (almost identical) situations showcase this:
+           *   svn mv a/b bb
+           *   svn del a
+           * and
+           *   svn mv a aa  
+           *   svn mv aa/b bb
+           * In both, 'bb' is moved from 'a/b', but 'a/b' has no op_depth>0
+           * node at all, as its parent 'a' is locally deleted. */
+
+          SVN_ERR(svn_wc__db_scan_deletion(NULL,
+                                           &moved_to_abspath,
+                                           NULL,
+                                           &moved_to_op_root_abspath,
+                                           db, local_abspath,
+                                           scratch_pool, scratch_pool));
+          if (moved_to_abspath != NULL
+              && moved_to_op_root_abspath != NULL
+              && strcmp(moved_to_abspath, moved_to_op_root_abspath) == 0)
+            {
+              mtb->moved_to_abspath = apr_pstrdup(result_pool,
+                                                  moved_to_abspath);
+            }
+          /* ### ^^^ THIS SUCKS. For at least two reasons:
+           * 1) We scan the node deletion and that's technically not necessary.
+           *    We'd be fine to know if this is an actual root of a move.
+           * 2) From the elaborately calculated results, we backwards-guess
+           *    whether this is a root.
+           * It works ok, and this code only gets called when a node is an
+           * explicit target of a 'status'. But it would be better to do this
+           * differently.
+           * We could return moved-to via svn_wc__db_base_get_info() (called
+           * just above), but as moved-to is only intended to be returned for
+           * roots of a move, that doesn't fit too well. */
+        }
+    }
+
+  /* ### svn_wc__db_read_info() could easily return the moved-here flag. But
+   * for now... (The per-dir query for recursive status is far more optimal.)
+   * Note that this actually scans around to get the full path, for a bool.
+   * This bool then gets returned, later is evaluated, and if true leads to
+   * the same paths being scanned again. We'd want to obtain this bool here as
+   * cheaply as svn_wc__db_read_children_info() does. */
+  if (mtb->status == svn_wc__db_status_added)
+    {
+      const char *moved_from_abspath = NULL;
+      SVN_ERR(svn_wc__db_scan_addition(NULL, NULL, NULL, NULL, NULL,
+                                       NULL, NULL, NULL, NULL,
+                                       &moved_from_abspath,
+                                       NULL,
+                                       db, local_abspath,
+                                       result_pool, scratch_pool));
+      mtb->moved_here = (moved_from_abspath != NULL);
     }
 
   mtb->has_checksum = (checksum != NULL);
@@ -404,8 +466,6 @@ assemble_status(svn_wc_status3_t **statu
   const char *repos_root_url;
   const char *repos_uuid;
   const char *moved_from_abspath = NULL;
-  const char *moved_to_abspath = NULL;
-  const char *moved_to_op_root_abspath = NULL;
   svn_filesize_t filesize = (dirent && (dirent->kind == svn_node_file))
                                 ? dirent->filesize
                                 : SVN_INVALID_FILESIZE;
@@ -426,17 +486,13 @@ assemble_status(svn_wc_status3_t **statu
       /* A node is switched if it doesn't have the implied repos_relpath */
       const char *name = svn_relpath_skip_ancestor(parent_repos_relpath,
                                                    info->repos_relpath);
-      switched_p = !name || (strcmp(name, svn_dirent_basename(local_abspath, NULL)) != 0);
+      switched_p = !name || (strcmp(name,
+                                    svn_dirent_basename(local_abspath, NULL))
+                             != 0);
     }
 
-  /* Examine whether our target is missing or obstructed.
-
-     While we are not completely in single-db mode yet, data about
-     obstructed or missing nodes might be incomplete here. This is
-     reported by svn_wc_db_status_obstructed_XXXX. In single-db
-     mode these obstructions are no longer reported and we have
-     to detect obstructions by looking at the on disk status in DIRENT.
-     */
+  /* Examine whether our target is missing or obstructed. To detect
+   * obstructions, we have to look at the on-disk status in DIRENT. */
   if (info->kind == svn_wc__db_kind_dir)
     {
       if (info->status == svn_wc__db_status_incomplete)
@@ -608,26 +664,18 @@ assemble_status(svn_wc_status3_t **statu
               else if (schedule == svn_wc_schedule_replace)
                 node_status = svn_wc_status_replaced;
             }
+
+          /* Get moved-from info (only for potential op-roots of a move). */
+          if (info->moved_here && info->op_root)
+            SVN_ERR(svn_wc__db_scan_addition(NULL, NULL, NULL, NULL, NULL,
+                                             NULL, NULL, NULL, NULL,
+                                             &moved_from_abspath,
+                                             NULL,
+                                             db, local_abspath,
+                                             result_pool, scratch_pool));
         }
     }
 
-  /* Get moved-to info. */
-  if (info->status == svn_wc__db_status_deleted)
-    SVN_ERR(svn_wc__db_scan_deletion(NULL,
-                                     &moved_to_abspath,
-                                     NULL,
-                                     &moved_to_op_root_abspath,
-                                     db, local_abspath,
-                                     result_pool, scratch_pool));
-
-  /* Get moved-from info. */
-  if (info->status == svn_wc__db_status_added)
-    SVN_ERR(svn_wc__db_scan_addition(NULL, NULL, NULL, NULL, NULL,
-                                     NULL, NULL, NULL, NULL,
-                                     &moved_from_abspath,
-                                     NULL,
-                                     db, local_abspath,
-                                     result_pool, scratch_pool));
 
   if (node_status == svn_wc_status_normal)
     node_status = text_status;
@@ -722,8 +770,7 @@ assemble_status(svn_wc_status3_t **statu
   stat->repos_uuid = repos_uuid;
 
   stat->moved_from_abspath = moved_from_abspath;
-  stat->moved_to_abspath = moved_to_abspath;
-  stat->moved_to_op_root_abspath = moved_to_op_root_abspath;
+  stat->moved_to_abspath = info->moved_to_abspath;
 
   *status = stat;
 
@@ -1082,8 +1129,8 @@ get_dir_status(const struct walk_status_
          don't all map the same types, but only the keys of the result
          hash are subsequently used. */
       SVN_ERR(svn_wc__db_read_children_info(&nodes, &conflicts,
-                                        wb->db, local_abspath,
-                                        subpool, iterpool));
+                                            wb->db, local_abspath,
+                                            subpool, iterpool));
 
       all_children = apr_hash_overlay(subpool, nodes, dirents);
       if (apr_hash_count(conflicts) > 0)
@@ -2624,10 +2671,6 @@ svn_wc_dup_status3(const svn_wc_status3_
     new_stat->moved_to_abspath
       = apr_pstrdup(pool, orig_stat->moved_to_abspath);
 
-  if (orig_stat->moved_to_op_root_abspath)
-    new_stat->moved_to_op_root_abspath
-      = apr_pstrdup(pool, orig_stat->moved_to_op_root_abspath);
-
   /* Return the new hotness. */
   return new_stat;
 }

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=1160299&r1=1160298&r2=1160299&view=diff
==============================================================================
--- subversion/branches/fs-py/subversion/libsvn_wc/tree_conflicts.c (original)
+++ subversion/branches/fs-py/subversion/libsvn_wc/tree_conflicts.c Mon Aug 22 15:21:13 2011
@@ -76,6 +76,8 @@ 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 },
   { NULL }
 };
 

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=1160299&r1=1160298&r2=1160299&view=diff
==============================================================================
--- subversion/branches/fs-py/subversion/libsvn_wc/update_editor.c (original)
+++ subversion/branches/fs-py/subversion/libsvn_wc/update_editor.c Mon Aug 22 15:21:13 2011
@@ -1272,7 +1272,8 @@ create_tree_conflict(svn_wc_conflict_des
   /* Get the source-left information, i.e. the local state of the node
    * before any changes were made to the working copy, i.e. the state the
    * node would have if it was reverted. */
-  if (reason == svn_wc_conflict_reason_added)
+  if (reason == svn_wc_conflict_reason_added ||
+      reason == svn_wc_conflict_reason_moved_here)
     {
       svn_wc__db_status_t added_status;
 
@@ -1327,6 +1328,7 @@ create_tree_conflict(svn_wc_conflict_des
        * and that other case should also be handled. */
       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_replaced
                      || reason == svn_wc_conflict_reason_obstructed);
 
@@ -1364,7 +1366,8 @@ create_tree_conflict(svn_wc_conflict_des
       /* This is an 'update', so REPOS_RELPATH would be the same as for
        * source-left. However, we don't have a source-left for locally
        * added files. */
-      right_repos_relpath = (reason == svn_wc_conflict_reason_added ?
+      right_repos_relpath = ((reason == svn_wc_conflict_reason_added ||
+                              reason == svn_wc_conflict_reason_moved_here) ?
                              added_repos_relpath : left_repos_relpath);
       if (! right_repos_relpath)
         right_repos_relpath = their_relpath;
@@ -1496,7 +1499,18 @@ check_tree_conflict(svn_wc_conflict_desc
              * would not have been called in the first place. */
             SVN_ERR_ASSERT(action == svn_wc_conflict_action_add);
 
-            reason = svn_wc_conflict_reason_added;
+            /* Scan the addition in case our caller didn't. */
+            if (working_status == svn_wc__db_status_added)
+              SVN_ERR(svn_wc__db_scan_addition(&working_status, NULL, NULL,
+                                               NULL, NULL, NULL, NULL,
+                                               NULL, NULL, NULL, NULL,
+                                               eb->db, local_abspath,
+                                               scratch_pool, scratch_pool));
+
+            if (working_status == svn_wc__db_status_moved_here)
+              reason = svn_wc_conflict_reason_moved_here;
+            else
+              reason = svn_wc_conflict_reason_added;
           }
         else
           {
@@ -1507,8 +1521,20 @@ check_tree_conflict(svn_wc_conflict_desc
 
 
       case svn_wc__db_status_deleted:
-        /* The node is locally deleted. */
-        reason = svn_wc_conflict_reason_deleted;
+        {
+          const char *moved_to_abspath;
+
+          /* The node is locally deleted. Check if it was moved away.
+           * ### should scan_deletion return status_moved_away, like
+           * ### scan_addition returns status_moved_here? */
+          SVN_ERR(svn_wc__db_scan_deletion(NULL, &moved_to_abspath, NULL,
+                                           NULL, eb->db, local_abspath,
+                                           scratch_pool, scratch_pool));
+          if (moved_to_abspath)
+            reason = svn_wc_conflict_reason_moved_away;
+          else
+            reason = svn_wc_conflict_reason_deleted;
+        }
         break;
 
       case svn_wc__db_status_incomplete:
@@ -1573,6 +1599,7 @@ check_tree_conflict(svn_wc_conflict_desc
    * would not have been called in the first place.*/
   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_replaced)
     /* When the node existed before (it was locally deleted, replaced or
      * edited), then 'update' cannot add it "again". So it can only send
@@ -1580,7 +1607,8 @@ check_tree_conflict(svn_wc_conflict_desc
     SVN_ERR_ASSERT(action == svn_wc_conflict_action_edit
                    || action == svn_wc_conflict_action_delete
                    || action == svn_wc_conflict_action_replace);
-  else if (reason == svn_wc_conflict_reason_added)
+  else if (reason == svn_wc_conflict_reason_added ||
+           reason == svn_wc_conflict_reason_moved_here)
     /* When the node did not exist before (it was locally added), then 'update'
      * cannot want to modify it in any way. It can only send _action_add. */
     SVN_ERR_ASSERT(action == svn_wc_conflict_action_add);
@@ -1835,6 +1863,7 @@ delete_entry(const char *path,
              keeping a not-present marker */
         }
       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_replaced)
         {
           /* The item does not exist locally because it was already shadowed.
@@ -2355,6 +2384,7 @@ open_directory(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_replaced);
 
       /* Continue updating BASE */
@@ -3349,6 +3379,7 @@ 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_replaced);
 
       /* Continue updating BASE */

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=1160299&r1=1160298&r2=1160299&view=diff
==============================================================================
--- subversion/branches/fs-py/subversion/libsvn_wc/wc-metadata.sql (original)
+++ subversion/branches/fs-py/subversion/libsvn_wc/wc-metadata.sql Mon Aug 22 15:21:13 2011
@@ -316,8 +316,8 @@ CREATE TABLE NODES (
      BASE node, the location of the initial checkout.
 
      When op_depth != 0, they indicate where this node was copied/moved from.
-     In this case, the fields are set only on the root of the operation,
-     and are NULL for all children. */
+     In this case, the fields are set for the root of the operation and for all
+     children. */
   repos_id  INTEGER REFERENCES REPOSITORY (id),
   repos_path  TEXT,
   revision  INTEGER,
@@ -384,7 +384,8 @@ 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 specified in copyfrom_*.  */
+     copied). The source of the move is implied by a different node with
+     a moved_to column pointing at this node. */
   moved_here  INTEGER,
 
   /* If the underlying node was moved away (rather than just deleted), this
@@ -392,8 +393,8 @@ CREATE TABLE NODES (
      This is set only on the root of a move, and is NULL for all children.
 
      Note that moved_to never refers to *this* node. It always refers
-     to the "underlying" node, whether that is BASE or a child node
-     implied from a parent's move/copy.  */
+     to the "underlying" node in the BASE tree. A non-NULL moved_to column
+     is only valid in rows where op_depth == 0. */
   moved_to  TEXT,
 
 

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=1160299&r1=1160298&r2=1160299&view=diff
==============================================================================
--- subversion/branches/fs-py/subversion/libsvn_wc/wc-queries.sql (original)
+++ subversion/branches/fs-py/subversion/libsvn_wc/wc-queries.sql Mon Aug 22 15:21:13 2011
@@ -37,8 +37,11 @@ ORDER BY op_depth DESC
 -- STMT_SELECT_NODE_INFO_WITH_LOCK
 SELECT op_depth, nodes.repos_id, nodes.repos_path, presence, kind, revision,
   checksum, translated_size, changed_revision, changed_date, changed_author,
-  depth, symlink_target, last_mod_time, properties, lock_token, lock_owner,
-  lock_comment, lock_date, moved_here
+  depth, symlink_target, last_mod_time, properties, moved_here,
+  /* All the columns until now must match those returned by
+     STMT_SELECT_NODE_INFO. The implementation of svn_wc__db_read_info()
+     assumes that these columns are followed by the lock information) */
+  lock_token, lock_owner, lock_comment, lock_date
 FROM nodes
 LEFT OUTER JOIN lock ON nodes.repos_id = lock.repos_id
   AND nodes.repos_path = lock.repos_relpath
@@ -48,7 +51,8 @@ ORDER BY op_depth DESC
 -- STMT_SELECT_BASE_NODE
 SELECT repos_id, repos_path, presence, kind, revision, checksum,
   translated_size, changed_revision, changed_date, changed_author, depth,
-  symlink_target, last_mod_time, properties, file_external IS NOT NULL
+  symlink_target, last_mod_time, properties, file_external IS NOT NULL,
+  moved_to
 FROM nodes
 WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth = 0
 
@@ -56,6 +60,10 @@ WHERE wc_id = ?1 AND local_relpath = ?2 
 SELECT nodes.repos_id, nodes.repos_path, presence, kind, revision,
   checksum, translated_size, changed_revision, changed_date, changed_author,
   depth, symlink_target, last_mod_time, properties, file_external IS NOT NULL,
+  moved_to,
+  /* All the columns until now must match those returned by
+     STMT_SELECT_BASE_NODE. The implementation of svn_wc__db_base_get_info()
+     assumes that these columns are followed by the lock information) */
   lock_token, lock_owner, lock_comment, lock_date
 FROM nodes
 LEFT OUTER JOIN lock ON nodes.repos_id = lock.repos_id
@@ -119,7 +127,7 @@ WHERE wc_id = ?1 AND local_relpath = ?2 
 SELECT op_depth, nodes.repos_id, nodes.repos_path, presence, kind, revision,
   checksum, translated_size, changed_revision, changed_date, changed_author,
   depth, symlink_target, last_mod_time, properties, lock_token, lock_owner,
-  lock_comment, lock_date, local_relpath
+  lock_comment, lock_date, local_relpath, moved_here, moved_to
 FROM nodes
 LEFT OUTER JOIN lock ON nodes.repos_id = lock.repos_id
   AND nodes.repos_path = lock.repos_relpath
@@ -158,9 +166,9 @@ INSERT OR REPLACE INTO nodes (
   wc_id, local_relpath, op_depth, parent_relpath, repos_id, repos_path,
   revision, presence, depth, kind, changed_revision, changed_date,
   changed_author, checksum, properties, translated_size, last_mod_time,
-  dav_cache, symlink_target, file_external)
+  dav_cache, symlink_target, file_external, moved_to)
 VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13, ?14,
-        ?15, ?16, ?17, ?18, ?19, ?20)
+        ?15, ?16, ?17, ?18, ?19, ?20, ?21)
 
 -- STMT_SELECT_OP_DEPTH_CHILDREN
 SELECT local_relpath FROM nodes
@@ -178,7 +186,7 @@ WHERE wc_id = ?1 AND parent_relpath = ?2
 DELETE FROM nodes
 WHERE wc_id = ?1
   AND (local_relpath = ?2
-       OR ((local_relpath > ?2 || '/') AND (local_relpath < ?2 || '0')))
+       OR IS_STRICT_DESCENDANT_OF(local_relpath, ?2))
   AND (op_depth < ?3
        OR (op_depth = ?3 AND presence = 'base-deleted'))
 
@@ -186,7 +194,7 @@ WHERE wc_id = ?1
 SELECT local_relpath FROM nodes
 WHERE wc_id = ?1 AND op_depth = ?3
   AND (parent_relpath = ?2
-       OR ((parent_relpath > ?2 || '/') AND (parent_relpath < ?2 || '0')))
+       OR IS_STRICT_DESCENDANT_OF(parent_relpath, ?2))
   AND presence == 'not-present'
 
 -- STMT_COMMIT_DESCENDANT_TO_BASE
@@ -263,7 +271,7 @@ LEFT JOIN lock ON nodes.repos_id = lock.
 WHERE wc_id = ?1 AND op_depth = 0
   AND (?2 = ''
        OR local_relpath = ?2
-       OR (local_relpath > ?2 || '/' AND local_relpath < ?2 || '0'))
+       OR IS_STRICT_DESCENDANT_OF(local_relpath, ?2))
 
 -- STMT_INSERT_WCROOT
 INSERT INTO wcroot (local_abspath)
@@ -298,7 +306,7 @@ UPDATE nodes SET dav_cache = NULL
 WHERE dav_cache IS NOT NULL AND wc_id = ?1 AND op_depth = 0
   AND (?2 = ''
        OR local_relpath = ?2
-       OR (local_relpath > ?2 || '/' AND local_relpath < ?2 || '0'))
+       OR IS_STRICT_DESCENDANT_OF(local_relpath, ?2))
 
 -- STMT_RECURSIVE_UPDATE_NODE_REPO
 UPDATE nodes SET repos_id = ?4, dav_cache = NULL
@@ -306,7 +314,7 @@ WHERE wc_id = ?1
   AND repos_id = ?3
   AND (?2 = ''
        OR local_relpath = ?2
-       OR (local_relpath > ?2 || '/' AND local_relpath < ?2 || '0'))
+       OR IS_STRICT_DESCENDANT_OF(local_relpath, ?2))
 
 -- STMT_UPDATE_LOCK_REPOS_ID
 UPDATE lock SET repos_id = ?2
@@ -476,7 +484,7 @@ FROM nodes_current
 WHERE wc_id = ?1
        AND (?2 = ''
             OR local_relpath = ?2 
-            OR (local_relpath > ?2 || '/' AND local_relpath < ?2 || '0'))
+            OR IS_STRICT_DESCENDANT_OF(local_relpath, ?2))
 
 -- STMT_INSERT_TARGET_WITH_CHANGELIST
 INSERT INTO targets_list(wc_id, local_relpath, parent_relpath, kind)
@@ -509,7 +517,7 @@ SELECT N.wc_id, N.local_relpath, N.paren
  WHERE N.wc_id = ?1
        AND (?2 = ''
             OR N.local_relpath = ?2 
-            OR (N.local_relpath > ?2 || '/' AND N.local_relpath < ?2 || '0'))
+            OR IS_STRICT_DESCENDANT_OF(N.local_relpath, ?2))
        AND A.changelist = ?3
 
 -- STMT_SELECT_TARGETS
@@ -578,7 +586,7 @@ DELETE FROM nodes
 WHERE wc_id = ?1
   AND (?2 = ''
        OR local_relpath = ?2
-       OR (local_relpath > ?2 || '/' AND local_relpath < ?2 || '0'))
+       OR IS_STRICT_DESCENDANT_OF(local_relpath, ?2))
   AND op_depth >= ?3
 
 -- STMT_DELETE_ACTUAL_NODE
@@ -590,7 +598,7 @@ DELETE FROM actual_node
 WHERE wc_id = ?1
   AND (?2 = ''
        OR local_relpath = ?2
-       OR (local_relpath > ?2 || '/' AND local_relpath < ?2 || '0'))
+       OR IS_STRICT_DESCENDANT_OF(local_relpath, ?2))
 
 -- STMT_DELETE_ACTUAL_NODE_WITHOUT_CONFLICT
 DELETE FROM actual_node
@@ -611,7 +619,7 @@ DELETE FROM actual_node
 WHERE wc_id = ?1
   AND (?2 = ''
        OR local_relpath = ?2
-       OR (local_relpath > ?2 || '/' AND local_relpath < ?2 || '0'))
+       OR IS_STRICT_DESCENDANT_OF(local_relpath, ?2))
   AND (changelist IS NULL
        OR NOT EXISTS (SELECT 1 FROM nodes_current c
                       WHERE c.wc_id = ?1 
@@ -647,7 +655,7 @@ SET properties = NULL,
 WHERE wc_id = ?1
   AND (?2 = ''
        OR local_relpath = ?2
-       OR (local_relpath > ?2 || '/' AND local_relpath < ?2 || '0'))
+       OR IS_STRICT_DESCENDANT_OF(local_relpath, ?2))
 
 -- STMT_UPDATE_NODE_BASE_DEPTH
 UPDATE nodes SET depth = ?3
@@ -779,7 +787,7 @@ DELETE FROM wc_lock
 WHERE wc_id = ?1
   AND (?2 = ''
        OR local_dir_relpath = ?2
-       OR (local_dir_relpath > ?2 || '/' AND local_dir_relpath < ?2 || '0'))
+       OR IS_STRICT_DESCENDANT_OF(local_dir_relpath, ?2))
   AND NOT EXISTS (SELECT 1 FROM nodes
                    WHERE nodes.wc_id = ?1
                      AND nodes.local_relpath = wc_lock.local_dir_relpath)
@@ -812,18 +820,8 @@ SELECT wc_id, local_relpath, ?4 /*op_dep
        kind
 FROM nodes
 WHERE wc_id = ?1
-  AND (local_relpath > ?2 || '/' AND local_relpath < ?2 || '0')
-  AND op_depth = ?3
-  AND presence NOT IN ('base-deleted', 'not-present', 'excluded', 'absent')
-
--- STMT_INSERT_DELETE_NODE
-INSERT INTO nodes (
-    wc_id, local_relpath, op_depth, parent_relpath, presence, kind)
-SELECT wc_id, local_relpath, ?4 /*op_depth*/, parent_relpath, 'base-deleted',
-       kind
-FROM nodes
-WHERE wc_id = ?1
-  AND local_relpath = ?2
+  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')
 
@@ -852,7 +850,7 @@ WHERE wc_id = ?1 AND local_relpath = ?2 
 UPDATE nodes SET op_depth = ?3 + 1
 WHERE wc_id = ?1
   AND (?2 = ''
-       OR (local_relpath > ?2 || '/' AND local_relpath < ?2 || '0'))
+       OR IS_STRICT_DESCENDANT_OF(local_relpath, ?2))
  AND op_depth = ?3
 
 -- STMT_DOES_NODE_EXIST
@@ -864,7 +862,7 @@ SELECT local_relpath FROM nodes
 WHERE wc_id = ?1
   AND (?2 = ''
        OR local_relpath = ?2
-       OR (local_relpath > ?2 || '/' AND local_relpath < ?2 || '0'))
+       OR IS_STRICT_DESCENDANT_OF(local_relpath, ?2))
   AND op_depth = 0 AND presence = 'absent'
 LIMIT 1
 
@@ -874,7 +872,7 @@ SELECT local_relpath FROM nodes
 WHERE wc_id = ?1
   AND (?2 = ''
        OR local_relpath = ?2
-       OR (local_relpath > ?2 || '/' AND local_relpath < ?2 || '0'))
+       OR IS_STRICT_DESCENDANT_OF(local_relpath, ?2))
   AND op_depth = 0
   AND presence = 'absent'
 
@@ -965,7 +963,7 @@ FROM externals
 WHERE wc_id = ?1 
   AND (?2 = ''
        OR def_local_relpath = ?2
-       OR (def_local_relpath > ?2 || '/' AND def_local_relpath < ?2 || '0'))
+       OR IS_STRICT_DESCENDANT_OF(def_local_relpath, ?2))
 
 -- STMT_UPDATE_EXTERNAL_FILEINFO
 UPDATE externals SET recorded_size = ?3, recorded_mod_time = ?4
@@ -984,7 +982,7 @@ FROM nodes n
 WHERE wc_id = ?1
   AND (?2 = ''
        OR local_relpath = ?2
-       OR (local_relpath > ?2 || '/' AND local_relpath < ?2 || '0'))
+       OR IS_STRICT_DESCENDANT_OF(local_relpath, ?2))
   AND kind = 'dir' AND presence='normal'
   AND op_depth=(SELECT MAX(op_depth) FROM nodes o
                 WHERE o.wc_id = ?1 AND o.local_relpath = n.local_relpath)
@@ -1112,14 +1110,18 @@ CREATE TEMPORARY TABLE revert_list (
    conflict_working TEXT,
    prop_reject TEXT,
    notify INTEGER,         /* 1 if an actual row had props or tree conflict */
+   op_depth INTEGER,
+   repos_id INTEGER,
+   kind TEXT,
    PRIMARY KEY (local_relpath, actual)
    );
 DROP TRIGGER IF EXISTS   trigger_revert_list_nodes;
 CREATE TEMPORARY TRIGGER trigger_revert_list_nodes
 BEFORE DELETE ON nodes
 BEGIN
-   INSERT OR REPLACE INTO revert_list(local_relpath, actual)
-   SELECT OLD.local_relpath, 0;
+   INSERT OR REPLACE INTO revert_list(local_relpath, actual, op_depth,
+                                      repos_id, kind)
+   SELECT OLD.local_relpath, 0, OLD.op_depth, OLD.repos_id, OLD.kind;
 END;
 DROP TRIGGER IF EXISTS   trigger_revert_list_actual_delete;
 CREATE TEMPORARY TRIGGER trigger_revert_list_actual_delete
@@ -1156,11 +1158,20 @@ DROP TRIGGER IF EXISTS trigger_revert_li
 DROP TRIGGER IF EXISTS trigger_revert_list_actual_update
 
 -- STMT_SELECT_REVERT_LIST
-SELECT conflict_old, conflict_new, conflict_working, prop_reject, notify, actual
+SELECT conflict_old, conflict_new, conflict_working, prop_reject, notify,
+       actual, op_depth, repos_id, kind
 FROM revert_list
 WHERE local_relpath = ?1
 ORDER BY actual DESC
 
+-- STMT_SELECT_REVERT_LIST_COPIED_CHILDREN
+SELECT local_relpath, kind
+FROM revert_list
+WHERE local_relpath LIKE ?1 ESCAPE '#'
+  AND op_depth >= ?2
+  AND repos_id IS NOT NULL
+ORDER BY local_relpath
+
 -- STMT_DELETE_REVERT_LIST
 DELETE FROM revert_list WHERE local_relpath = ?1
 
@@ -1192,7 +1203,7 @@ INSERT INTO delete_list(local_relpath)
 SELECT local_relpath FROM nodes n
 WHERE wc_id = ?1
   AND (local_relpath = ?2
-       OR (local_relpath > ?2 || '/' AND local_relpath < ?2 || '0'))
+       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
@@ -1217,7 +1228,7 @@ SELECT MIN(revision), MAX(revision),
   WHERE wc_id = ?1
     AND (?2 = ''
        OR local_relpath = ?2
-       OR (local_relpath > ?2 || '/' AND local_relpath < ?2 || '0'))
+       OR IS_STRICT_DESCENDANT_OF(local_relpath, ?2))
     AND presence IN ('normal', 'incomplete')
     AND file_external IS NULL
     AND op_depth = 0
@@ -1227,7 +1238,7 @@ SELECT 1 FROM nodes
 WHERE wc_id = ?1
   AND (?2 = ''
        OR local_relpath = ?2
-       OR (local_relpath > ?2 || '/' AND local_relpath < ?2 || '0'))
+       OR IS_STRICT_DESCENDANT_OF(local_relpath, ?2))
   AND op_depth = 0
   AND (presence IN ('absent', 'excluded')
         OR depth NOT IN ('infinity', 'unknown'))
@@ -1239,7 +1250,7 @@ SELECT 1 FROM nodes
 WHERE wc_id = ?1
   AND (?2 = ''
        OR local_relpath = ?2
-       OR (local_relpath > ?2 || '/' AND local_relpath < ?2 || '0'))
+       OR IS_STRICT_DESCENDANT_OF(local_relpath, ?2))
   AND op_depth > 0
 LIMIT 1
 
@@ -1248,7 +1259,7 @@ SELECT 1 FROM actual_node
 WHERE wc_id = ?1
   AND (?2 = ''
        OR local_relpath = ?2
-       OR (local_relpath > ?2 || '/' AND local_relpath < ?2 || '0'))
+       OR IS_STRICT_DESCENDANT_OF(local_relpath, ?2))
   AND properties IS NOT NULL
 LIMIT 1
 
@@ -1271,7 +1282,7 @@ SELECT o.repos_path || '/' || SUBSTR(s.l
 FROM nodes AS o
 LEFT JOIN nodes AS s
 ON o.wc_id = s.wc_id
-   AND s.local_relpath > ?2 || '/' AND s.local_relpath < ?2 || '0'
+   AND IS_STRICT_DESCENDANT_OF(s.local_relpath, ?2)
    AND s.op_depth = 0
    AND s.repos_id = o.repos_id
    AND s.file_external IS NULL
@@ -1285,7 +1296,7 @@ SELECT SUBSTR(s.local_relpath, LENGTH(?2
 FROM nodes AS o
 LEFT JOIN nodes AS s
 ON o.wc_id = s.wc_id
-   AND s.local_relpath > ?2 || '/' AND s.local_relpath < ?2 || '0'
+   AND IS_STRICT_DESCENDANT_OF(s.local_relpath, ?2)
    AND s.op_depth = 0
    AND s.repos_id = o.repos_id
    AND s.file_external IS NULL
@@ -1326,7 +1337,7 @@ SELECT local_relpath, translated_size, l
 WHERE wc_id = ?1
   AND (?2 = ''
        OR local_relpath = ?2
-       OR (local_relpath > ?2 || '/' AND local_relpath < ?2 || '0'))
+       OR IS_STRICT_DESCENDANT_OF(local_relpath, ?2))
   AND op_depth = 0
   AND kind='file'
   AND presence='normal'
@@ -1349,13 +1360,16 @@ UPDATE nodes SET moved_to = NULL
 WHERE wc_id = ?1
   AND (?2 = ''
        OR local_relpath = ?2
-       OR (local_relpath > ?2 || '/' AND local_relpath < ?2 || '0'))
+       OR IS_STRICT_DESCENDANT_OF(local_relpath, ?2))
   AND op_depth = 0
 
+/* This statement returns pairs of move-roots below the path ?2 in WC_ID ?1.
+ * Each row returns a moved-here path (always a child of ?2) in the first
+ * column, and its matching moved-away (deleted) path in the second column. */
 -- STMT_SELECT_MOVED_HERE_CHILDREN
 SELECT moved_to, local_relpath FROM nodes
 WHERE wc_id = ?1 AND op_depth = 0
-  AND (moved_to > ?2 || '/' AND moved_to < ?2 || '0')
+  AND IS_STRICT_DESCENDANT_OF(moved_to, ?2)
 
 /* ------------------------------------------------------------------------- */
 

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=1160299&r1=1160298&r2=1160299&view=diff
==============================================================================
--- subversion/branches/fs-py/subversion/libsvn_wc/wc_db.c (original)
+++ subversion/branches/fs-py/subversion/libsvn_wc/wc_db.c Mon Aug 22 15:21:13 2011
@@ -781,6 +781,8 @@ insert_base_node(void *baton,
   svn_sqlite__stmt_t *stmt;
   svn_filesize_t recorded_size = SVN_INVALID_FILESIZE;
   apr_int64_t recorded_mod_time;
+  const char *moved_to_relpath = NULL;
+  svn_boolean_t have_row;
 
   /* The directory at the WCROOT has a NULL parent_relpath. Otherwise,
      bind the appropriate parent_relpath. */
@@ -798,24 +800,28 @@ insert_base_node(void *baton,
   /* ### we can't handle this right now  */
   SVN_ERR_ASSERT(pibb->conflict == NULL);
 
-  if (pibb->keep_recorded_info)
+  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
+                                    STMT_SELECT_BASE_NODE));
+  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
+  SVN_ERR(svn_sqlite__step(&have_row, stmt));
+  if (have_row)
     {
-      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
-                                        STMT_SELECT_BASE_NODE));
-
-      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
-      SVN_ERR(svn_sqlite__step_row(stmt));
-
-      recorded_size = get_recorded_size(stmt, 6);
-      recorded_mod_time = svn_sqlite__column_int64(stmt, 12);
-
-      SVN_ERR(svn_sqlite__reset(stmt));
+      /* A BASE node already exists. */
+      if (pibb->keep_recorded_info)
+        {
+          /* Preserve size and modification time if caller asked us to. */
+          recorded_size = get_recorded_size(stmt, 6);
+          recorded_mod_time = svn_sqlite__column_int64(stmt, 12);
+        }
+      /* Always preserve moved-to info. */
+      moved_to_relpath = svn_sqlite__column_text(stmt, 15, scratch_pool);
     }
+  SVN_ERR(svn_sqlite__reset(stmt));
 
   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_INSERT_NODE));
   SVN_ERR(svn_sqlite__bindf(stmt, "isisisr"
                             "tstr"               /* 8 - 11 */
-                            "isnnnnns",          /* 12 - 19 */
+                            "isnnnnnsss",          /* 12 - 21 */
                             wcroot->wc_id,       /* 1 */
                             local_relpath,       /* 2 */
                             (apr_int64_t)0, /* op_depth is 0 for base */
@@ -831,8 +837,9 @@ insert_base_node(void *baton,
                             pibb->changed_date,   /* 12 */
                             pibb->changed_author, /* 13 */
                             (pibb->kind == svn_wc__db_kind_symlink) ?
-                                pibb->target : NULL)); /* 19 */
-
+                                pibb->target : NULL, /* 19 */
+                            NULL /* 20 */,
+                            moved_to_relpath /* 21 */));
   if (pibb->kind == svn_wc__db_kind_file)
     {
       SVN_ERR(svn_sqlite__bind_checksum(stmt, 14, pibb->checksum,
@@ -2022,7 +2029,7 @@ base_get_info(svn_wc__db_status_t *statu
       SVN_ERR_ASSERT(!repos_relpath || *repos_relpath);
       if (lock)
         {
-          *lock = lock_from_columns(stmt, 15, 16, 17, 18, result_pool);
+          *lock = lock_from_columns(stmt, 16, 17, 18, 19, result_pool);
         }
       if (changed_rev)
         {
@@ -5325,6 +5332,8 @@ op_revert_recursive_txn(void *baton,
   svn_sqlite__stmt_t *stmt;
   svn_boolean_t have_row;
   apr_int64_t op_depth;
+  apr_int64_t select_op_depth;
+  svn_boolean_t moved_here;
   int affected_rows;
   apr_pool_t *iterpool;
 
@@ -5356,6 +5365,7 @@ op_revert_recursive_txn(void *baton,
     }
 
   op_depth = svn_sqlite__column_int64(stmt, 0);
+  moved_here = svn_sqlite__column_boolean(stmt, 15);
   SVN_ERR(svn_sqlite__reset(stmt));
 
   if (op_depth > 0 && op_depth != relpath_depth(local_relpath))
@@ -5366,13 +5376,13 @@ op_revert_recursive_txn(void *baton,
                                                     local_relpath,
                                                     scratch_pool));
 
-  if (!op_depth)
-    op_depth = 1; /* Don't delete BASE nodes */
+  /* Don't delete BASE nodes */
+  select_op_depth = op_depth ? op_depth : 1;
 
   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
                                     STMT_DELETE_NODES_RECURSIVE));
   SVN_ERR(svn_sqlite__bindf(stmt, "isi", wcroot->wc_id,
-                            local_relpath, op_depth));
+                            local_relpath, select_op_depth));
   SVN_ERR(svn_sqlite__step_done(stmt));
 
   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
@@ -5413,7 +5423,12 @@ op_revert_recursive_txn(void *baton,
     }
   svn_pool_destroy(iterpool);
 
-  /* Clear any moved-to paths of the BASE nodes. */
+  /* Clear potential moved-to pointing at the target node itself. */
+  if (op_depth > 0 && op_depth == relpath_depth(local_relpath)
+      && moved_here)
+    SVN_ERR(clear_moved_to(local_relpath, wcroot, scratch_pool));
+
+  /* Clear any moved-to paths of potentially 'moved-away' nodes. */
   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
                                     STMT_CLEAR_MOVED_TO_RELPATH_RECURSIVE));
   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
@@ -5471,6 +5486,8 @@ struct revert_list_read_baton {
   const char **conflict_new;
   const char **conflict_working;
   const char **prop_reject;
+  svn_boolean_t *copied_here;
+  svn_wc__db_kind_t *kind;
   apr_pool_t *result_pool;
 };
 
@@ -5487,6 +5504,8 @@ revert_list_read(void *baton,
   *(b->reverted) = FALSE;
   *(b->conflict_new) = *(b->conflict_old) = *(b->conflict_working) = NULL;
   *(b->prop_reject) = NULL;
+  *(b->copied_here) = FALSE;
+  *(b->kind) = svn_wc__db_kind_unknown;
 
   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
                                     STMT_SELECT_REVERT_LIST));
@@ -5494,9 +5513,10 @@ revert_list_read(void *baton,
   SVN_ERR(svn_sqlite__step(&have_row, stmt));
   if (have_row)
     {
-      svn_boolean_t another_row;
+      svn_boolean_t is_actual = (svn_sqlite__column_int64(stmt, 5) != 0);
+      svn_boolean_t another_row = FALSE;
 
-      if (svn_sqlite__column_int64(stmt, 5))
+      if (is_actual)
         {
           if (!svn_sqlite__column_is_null(stmt, 4))
             *(b->reverted) = TRUE;
@@ -5526,11 +5546,19 @@ revert_list_read(void *baton,
                                 b->result_pool);
 
           SVN_ERR(svn_sqlite__step(&another_row, stmt));
-          if (another_row)
-            *(b->reverted) = TRUE;
         }
-      else
-        *(b->reverted) = TRUE;
+
+      if (!is_actual || another_row)
+        {
+          *(b->reverted) = TRUE;
+          if (!svn_sqlite__column_is_null(stmt, 7))
+            {
+              apr_int64_t op_depth = svn_sqlite__column_int64(stmt, 6);
+              *(b->copied_here) = (op_depth == relpath_depth(local_relpath));
+            }
+          *(b->kind) = svn_sqlite__column_token(stmt, 8, kind_map);
+        }
+
     }
   SVN_ERR(svn_sqlite__reset(stmt));
 
@@ -5551,6 +5579,8 @@ svn_wc__db_revert_list_read(svn_boolean_
                             const char **conflict_new,
                             const char **conflict_working,
                             const char **prop_reject,
+                            svn_boolean_t *copied_here,
+                            svn_wc__db_kind_t *kind,
                             svn_wc__db_t *db,
                             const char *local_abspath,
                             apr_pool_t *result_pool,
@@ -5560,7 +5590,7 @@ svn_wc__db_revert_list_read(svn_boolean_
   const char *local_relpath;
   struct revert_list_read_baton b = {reverted, conflict_old, conflict_new,
                                      conflict_working, prop_reject,
-                                     result_pool};
+                                     copied_here, kind, result_pool};
 
   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
                               db, local_abspath, scratch_pool, scratch_pool));
@@ -5571,6 +5601,80 @@ svn_wc__db_revert_list_read(svn_boolean_
   return SVN_NO_ERROR;
 }
 
+
+struct revert_list_read_copied_children_baton {
+  const apr_array_header_t **children;
+  apr_pool_t *result_pool;
+};
+
+static svn_error_t *
+revert_list_read_copied_children(void *baton,
+                                 svn_wc__db_wcroot_t *wcroot,
+                                 const char *local_relpath,
+                                 apr_pool_t *scratch_pool)
+{
+  struct revert_list_read_copied_children_baton *b = baton;
+  svn_sqlite__stmt_t *stmt;
+  svn_boolean_t have_row;
+  apr_array_header_t *children;
+
+  children =
+    apr_array_make(b->result_pool, 0,
+                  sizeof(svn_wc__db_revert_list_copied_child_info_t *));
+
+  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
+                                    STMT_SELECT_REVERT_LIST_COPIED_CHILDREN));
+  SVN_ERR(svn_sqlite__bindf(stmt, "si",
+                            construct_like_arg(local_relpath, scratch_pool),
+                            relpath_depth(local_relpath)));
+  SVN_ERR(svn_sqlite__step(&have_row, stmt));
+  while (have_row)
+    {
+      svn_wc__db_revert_list_copied_child_info_t *child_info;
+      const char *child_relpath;
+
+      child_info = apr_palloc(b->result_pool, sizeof(*child_info));
+
+      child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
+      child_info->abspath = svn_dirent_join(wcroot->abspath, child_relpath,
+                                            b->result_pool);
+      child_info->kind = svn_sqlite__column_token(stmt, 1, kind_map);
+      APR_ARRAY_PUSH(
+        children,
+        svn_wc__db_revert_list_copied_child_info_t *) = child_info;
+
+      SVN_ERR(svn_sqlite__step(&have_row, stmt));
+    }
+   SVN_ERR(svn_sqlite__reset(stmt));
+
+  *b->children = children;
+
+  return SVN_NO_ERROR;
+}
+
+
+svn_error_t *
+svn_wc__db_revert_list_read_copied_children(const apr_array_header_t **children,
+                                            svn_wc__db_t *db,
+                                            const char *local_abspath,
+                                            apr_pool_t *result_pool,
+                                            apr_pool_t *scratch_pool)
+{
+  svn_wc__db_wcroot_t *wcroot;
+  const char *local_relpath;
+  struct revert_list_read_copied_children_baton b = {children, result_pool};
+
+  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_wc__db_with_txn(wcroot, local_relpath,
+                              revert_list_read_copied_children, &b,
+                              scratch_pool));
+  return SVN_NO_ERROR;
+}
+
+
 svn_error_t *
 svn_wc__db_revert_list_notify(svn_wc_notify_func2_t notify_func,
                               void *notify_baton,
@@ -6324,13 +6428,6 @@ op_delete_txn(void *baton,
   if (add_work)
     {
       /* Delete the node at LOCAL_RELPATH, and possibly mark it as moved. */
-      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
-                                 STMT_INSERT_DELETE_NODE));
-      SVN_ERR(svn_sqlite__bindf(stmt, "isii",
-                                wcroot->wc_id, local_relpath,
-                                select_depth, b->delete_depth));
-      SVN_ERR(svn_sqlite__step_done(stmt));
-
       if (b->moved_to_relpath)
         {
           /* Record moved-to relpath in BASE. */
@@ -6342,7 +6439,7 @@ op_delete_txn(void *baton,
           SVN_ERR(svn_sqlite__step_done(stmt));
         }
 
-      /* Delete children, if any. */
+      /* Delete the node and possible descendants. */
       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
                                  STMT_INSERT_DELETE_FROM_NODE_RECURSIVE));
       SVN_ERR(svn_sqlite__bindf(stmt, "isii",
@@ -6687,7 +6784,7 @@ read_info(svn_wc__db_status_t *status,
           if (op_depth != 0)
             *lock = NULL;
           else
-            *lock = lock_from_columns(stmt_info, 15, 16, 17, 18, result_pool);
+            *lock = lock_from_columns(stmt_info, 16, 17, 18, 19, result_pool);
         }
 
       if (have_work)
@@ -7038,18 +7135,27 @@ read_children_info(void *baton,
 
       if (op_depth == 0)
         {
+          const char *moved_to_relpath;
+
           child_item->info.have_base = TRUE;
 
-          /* Get the lock info. The query only reports lock info in the row at
-            * op_depth 0. */
-          if (op_depth == 0)
-            child_item->info.lock = lock_from_columns(stmt, 15, 16, 17, 18,
-                                                      result_pool);
+          /* Get the lock info, available only at op_depth 0. */
+          child_item->info.lock = lock_from_columns(stmt, 15, 16, 17, 18,
+                                                    result_pool);
+
+          /* Moved-to is only stored at op_depth 0. */
+          moved_to_relpath = svn_sqlite__column_text(stmt, 21, NULL); 
+          if (moved_to_relpath)
+            child_item->info.moved_to_abspath =
+              svn_dirent_join(wcroot->abspath, moved_to_relpath, result_pool);
         }
       else
         {
           child_item->nr_layers++;
           child_item->info.have_more_work = (child_item->nr_layers > 1);
+
+          /* Moved-here can only exist at op_depth > 0. */
+          child_item->info.moved_here = svn_sqlite__column_boolean(stmt, 20);
         }
 
       err = svn_sqlite__step(&have_row, stmt);
@@ -9669,6 +9775,7 @@ scan_deletion_txn(void *baton,
   svn_wc__db_status_t child_presence;
   svn_boolean_t child_has_base = FALSE;
   apr_int64_t local_op_depth, op_depth;
+  svn_boolean_t found_moved_to = FALSE;
 
   /* Initialize all the OUT parameters.  */
   if (sd_baton->base_del_relpath != NULL)
@@ -9795,9 +9902,12 @@ scan_deletion_txn(void *baton,
              gimmick, not a real node that may have been deleted.  */
         }
 
-      if ((sd_baton->moved_to_relpath != NULL
-                || sd_baton->moved_to_op_root_relpath != NULL
-                || sd_baton->base_del_relpath != NULL)
+      /* Evaluate moved-to information. Once moved-to info has been found, it
+       * must not be overwritten with ancestors' moved-to info. */
+      if ((! found_moved_to)
+          && (sd_baton->moved_to_relpath != NULL
+              || sd_baton->moved_to_op_root_relpath != NULL
+              || sd_baton->base_del_relpath != NULL)
           && !svn_sqlite__column_is_null(stmt, 2 /* moved_to */))
         {
           const char *moved_to_op_root_relpath;
@@ -9888,6 +9998,13 @@ scan_deletion_txn(void *baton,
             *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)
+            break;
+
+          found_moved_to = TRUE;
         }
 
       op_depth = svn_sqlite__column_int64(stmt, 3);

Modified: subversion/branches/fs-py/subversion/libsvn_wc/wc_db.h
URL: http://svn.apache.org/viewvc/subversion/branches/fs-py/subversion/libsvn_wc/wc_db.h?rev=1160299&r1=1160298&r2=1160299&view=diff
==============================================================================
--- subversion/branches/fs-py/subversion/libsvn_wc/wc_db.h (original)
+++ subversion/branches/fs-py/subversion/libsvn_wc/wc_db.h Mon Aug 22 15:21:13 2011
@@ -1506,6 +1506,10 @@ svn_wc__db_op_revert(svn_wc__db_t *db,
  * path was reverted.  Set *CONFLICT_OLD, *CONFLICT_NEW,
  * *CONFLICT_WORKING and *PROP_REJECT to the names of the conflict
  * files, or NULL if the names are not stored.
+ * 
+ * Set *COPIED_HERE if the reverted node was copied here and is the
+ * operation root of the copy.
+ * Set *KIND to the node kind of the reverted node.
  *
  * Removes the row for LOCAL_ABSPATH from the revert list.
  */
@@ -1515,11 +1519,32 @@ svn_wc__db_revert_list_read(svn_boolean_
                             const char **conflict_new,
                             const char **conflict_working,
                             const char **prop_reject,
+                            svn_boolean_t *copied_here,
+                            svn_wc__db_kind_t *kind,
                             svn_wc__db_t *db,
                             const char *local_abspath,
                             apr_pool_t *result_pool,
                             apr_pool_t *scratch_pool);
 
+/* The type of elements in the array returned by
+ * svn_wc__db_revert_list_read_copied_children(). */
+typedef struct svn_wc__db_revert_list_copied_child_info_t {
+  const char *abspath;
+  svn_wc__db_kind_t kind;
+} svn_wc__db_revert_list_copied_child_info_t ;
+
+/* Return in *CHILDREN a list of reverted copied nodes at or within
+ * LOCAL_ABSPATH (which is a reverted file or a reverted directory).
+ * Allocate *COPIED_CHILDREN and its elements in RESULT_POOL.
+ * The elements are of type svn_wc__db_revert_list_copied_child_info_t. */
+svn_error_t *
+svn_wc__db_revert_list_read_copied_children(const apr_array_header_t **children,
+                                            svn_wc__db_t *db,
+                                            const char *local_abspath,
+                                            apr_pool_t *result_pool,
+                                            apr_pool_t *scratch_pool);
+
+
 /* Make revert notifications for all paths in the revert list that are
  * equal to LOCAL_ABSPATH or below LOCAL_ABSPATH.
  *
@@ -1842,6 +1867,9 @@ struct svn_wc__db_info_t {
 
   svn_boolean_t locked;     /* WC directory lock */
   svn_wc__db_lock_t *lock;  /* Repository file lock */
+
+  const char *moved_to_abspath; /* Only on op-roots. See svn_wc_status3_t. */
+  svn_boolean_t moved_here;     /* On both op-roots and children. */
 };
 
 /* Return in *NODES a hash mapping name->struct svn_wc__db_info_t for

Modified: subversion/branches/fs-py/subversion/libsvn_wc/wc_db_wcroot.c
URL: http://svn.apache.org/viewvc/subversion/branches/fs-py/subversion/libsvn_wc/wc_db_wcroot.c?rev=1160299&r1=1160298&r2=1160299&view=diff
==============================================================================
--- subversion/branches/fs-py/subversion/libsvn_wc/wc_db_wcroot.c (original)
+++ subversion/branches/fs-py/subversion/libsvn_wc/wc_db_wcroot.c Mon Aug 22 15:21:13 2011
@@ -100,11 +100,13 @@ get_old_version(int *version,
    of LOCAL_ABSPATH, using DB and SCRATCH_POOL as needed.
 
    This function may do strange things, but at long as it comes up with the
-   Right Answer, we should be happy.  */
+   Right Answer, we should be happy.
+
+   Sets *KIND to svn_node_dir for symlinks. */
 static svn_error_t *
-get_path_kind(svn_wc__db_t *db,
+get_path_kind(svn_node_kind_t *kind,
+              svn_wc__db_t *db,
               const char *local_abspath,
-              svn_node_kind_t *kind,
               apr_pool_t *scratch_pool)
 {
   svn_boolean_t special;
@@ -132,7 +134,11 @@ get_path_kind(svn_wc__db_t *db,
     }
 
   SVN_ERR(svn_io_check_special_path(local_abspath, &db->parse_cache.kind,
-                                    &special /* unused */, scratch_pool));
+                                    &special, scratch_pool));
+
+  /* The wcroot could be a symlink to a directory. (Issue #2557, #3987) */
+  if (special)
+    db->parse_cache.kind = svn_node_dir;
   *kind = db->parse_cache.kind;
 
   return SVN_NO_ERROR;
@@ -395,7 +401,7 @@ svn_wc__db_wcroot_parse_local_abspath(sv
      ### rid of this stat() call. it is going to happen for EVERY call
      ### into wc_db which references a file. calls for directories could
      ### get an early-exit in the hash lookup just above.  */
-  SVN_ERR(get_path_kind(db, local_abspath, &kind, scratch_pool));
+  SVN_ERR(get_path_kind(&kind, db, local_abspath, scratch_pool));
   if (kind != svn_node_dir)
     {
       /* If the node specified by the path is NOT present, then it cannot

Modified: subversion/branches/fs-py/subversion/libsvn_wc/workqueue.c
URL: http://svn.apache.org/viewvc/subversion/branches/fs-py/subversion/libsvn_wc/workqueue.c?rev=1160299&r1=1160298&r2=1160299&view=diff
==============================================================================
--- subversion/branches/fs-py/subversion/libsvn_wc/workqueue.c (original)
+++ subversion/branches/fs-py/subversion/libsvn_wc/workqueue.c Mon Aug 22 15:21:13 2011
@@ -733,10 +733,7 @@ run_file_install(svn_wc__db_t *db,
                            cancel_func, cancel_baton,
                            scratch_pool));
 
-  /* ### post-commit feature: avoid overwrite if same as working file.  */
-
   /* All done. Move the file into place.  */
-  /* ### fix this. we should delay the rename.  */
 
   {
     svn_error_t *err;
@@ -750,7 +747,7 @@ run_file_install(svn_wc__db_t *db,
       {
         svn_error_t *err2;
 
-        err2 = svn_io_make_dir_recursively(svn_dirent_dirname(dst_abspath,
+        err2 = svn_io_make_dir_recursively(svn_dirent_dirname(local_abspath,
                                                               scratch_pool),
                                            scratch_pool);
 

Modified: subversion/branches/fs-py/subversion/mod_dav_svn/liveprops.c
URL: http://svn.apache.org/viewvc/subversion/branches/fs-py/subversion/mod_dav_svn/liveprops.c?rev=1160299&r1=1160298&r2=1160299&view=diff
==============================================================================
--- subversion/branches/fs-py/subversion/mod_dav_svn/liveprops.c (original)
+++ subversion/branches/fs-py/subversion/mod_dav_svn/liveprops.c Mon Aug 22 15:21:13 2011
@@ -483,6 +483,8 @@ insert_prop_internal(const dav_resource 
                    there's no point even checking.  No matter what the
                    error is, we can't claim to have a mime type for
                    this resource. */
+                ap_log_rerror(APLOG_MARK, APLOG_WARNING, serr->apr_err, 
+                              resource->info->r, "%s", serr->message);
                 svn_error_clear(serr);
                 return DAV_PROP_INSERT_NOTDEF;
               }

Modified: subversion/branches/fs-py/subversion/mod_dav_svn/mod_dav_svn.c
URL: http://svn.apache.org/viewvc/subversion/branches/fs-py/subversion/mod_dav_svn/mod_dav_svn.c?rev=1160299&r1=1160298&r2=1160299&view=diff
==============================================================================
--- subversion/branches/fs-py/subversion/mod_dav_svn/mod_dav_svn.c (original)
+++ subversion/branches/fs-py/subversion/mod_dav_svn/mod_dav_svn.c Mon Aug 22 15:21:13 2011
@@ -1000,7 +1000,7 @@ static const command_rec cmds[] =
   /* per server */
   AP_INIT_TAKE1("SVNInMemoryCacheSize", SVNInMemoryCacheSize_cmd, NULL,
                 RSRC_CONF,
-                "specifies the maximum size im kB per process of Subversion's "
+                "specifies the maximum size in kB per process of Subversion's "
                 "in-memory object cache (default value is 16384; 0 deactivates "
                 "the cache)."),
   /* per server */

Modified: subversion/branches/fs-py/subversion/svn/main.c
URL: http://svn.apache.org/viewvc/subversion/branches/fs-py/subversion/svn/main.c?rev=1160299&r1=1160298&r2=1160299&view=diff
==============================================================================
--- subversion/branches/fs-py/subversion/svn/main.c (original)
+++ subversion/branches/fs-py/subversion/svn/main.c Mon Aug 22 15:21:13 2011
@@ -1006,6 +1006,13 @@ const svn_opt_subcommand_desc2_t svn_cl_
      "  for deletion. If the patch creates a new file, that file is scheduled\n"
      "  for addition. Use 'svn revert' to undo deletions and additions you\n"
      "  do not agree with.\n"
+     "\n"
+     "  Hint: If the patch file was created with Subversion, it will contain\n"
+     "        the number of a revision N the patch will cleanly apply to\n"
+     "        (look for lines like \"--- foo/bar.txt        (revision N)\").\n"
+     "        To avoid rejects, first update to the revision N using \n"
+     "        'svn update -r N', apply the patch, and then update back to the\n"
+     "        HEAD revision. This way, conflicts can be resolved interactively.\n"
      ),
     {'q', opt_dry_run, opt_strip, opt_reverse_diff,
      opt_ignore_whitespace} },
@@ -1184,7 +1191,7 @@ const svn_opt_subcommand_desc2_t svn_cl_
      "usage: revert PATH...\n"
      "\n"
      "  Note:  this subcommand does not require network access, and resolves\n"
-     "  any conflicted states.  However, it does not restore removed directories.\n"),
+     "  any conflicted states.\n"),
     {opt_targets, 'R', opt_depth, 'q', opt_changelist} },
 
   { "status", svn_cl__status, {"stat", "st"}, N_
@@ -1936,12 +1943,6 @@ main(int argc, const char *argv[])
         break;
       case opt_changelist:
         opt_state.changelist = apr_pstrdup(pool, opt_arg);
-        if (opt_state.changelist[0] == '\0')
-          {
-            err = svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
-                                   _("Changelist names must not be empty"));
-            return svn_cmdline_handle_exit_error(err, pool, "svn: ");
-          }
         apr_hash_set(changelists, opt_state.changelist,
                      APR_HASH_KEY_STRING, (void *)1);
         break;