You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by pb...@apache.org on 2012/10/06 18:40:58 UTC

svn commit: r1395109 [2/3] - in /subversion/trunk: ./ subversion/include/ subversion/include/private/ subversion/libsvn_client/ subversion/libsvn_ra/ subversion/libsvn_ra_local/ subversion/libsvn_ra_serf/ subversion/libsvn_ra_svn/ subversion/libsvn_rep...

Modified: subversion/trunk/subversion/libsvn_client/update.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_client/update.c?rev=1395109&r1=1395108&r2=1395109&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_client/update.c (original)
+++ subversion/trunk/subversion/libsvn_client/update.c Sat Oct  6 16:40:55 2012
@@ -200,7 +200,10 @@ update_internal(svn_revnum_t *result_rev
   svn_boolean_t sleep_here = FALSE;
   svn_boolean_t *use_sleep = timestamp_sleep ? timestamp_sleep : &sleep_here;
   svn_boolean_t clean_checkout = FALSE;
+  svn_boolean_t is_not_present;
   const char *diff3_cmd;
+  apr_hash_t *wcroot_iprops;
+  svn_opt_revision_t opt_rev;
   svn_ra_session_t *ra_session;
   const char *preserved_exts_str;
   apr_array_header_t *preserved_exts;
@@ -353,10 +356,18 @@ update_internal(svn_revnum_t *result_rev
       anchor_loc->url = corrected_url;
     }
 
+  /* Resolve unspecified REVISION now, because we need to retrieve the
+     correct inherited props prior to the editor drive and we need to
+     use the same value of HEAD for both. */
+  opt_rev.kind = revision->kind;
+  opt_rev.value = revision->value;
+  if (opt_rev.kind == svn_opt_revision_unspecified)
+    opt_rev.kind = svn_opt_revision_head;
+
   /* ### todo: shouldn't svn_client__get_revision_number be able
      to take a URL as easily as a local path?  */
   SVN_ERR(svn_client__get_revision_number(&revnum, NULL, ctx->wc_ctx,
-                                          local_abspath, ra_session, revision,
+                                          local_abspath, ra_session, &opt_rev,
                                           pool));
 
   SVN_ERR(svn_ra_has_capability(ra_session, &server_supports_depth,
@@ -366,12 +377,31 @@ update_internal(svn_revnum_t *result_rev
   dfb.target_revision = revnum;
   dfb.anchor_url = anchor_loc->url;
 
+  err = svn_client__get_inheritable_props(&wcroot_iprops, local_abspath,
+                                          revnum, depth, ra_session, ctx,
+                                          pool, pool);
+
+  /* We might be trying to update to a non-existant path-rev. */
+  if (err)
+    {
+      if (err->apr_err == SVN_ERR_FS_NOT_FOUND)
+        {
+          svn_error_clear(err);
+          err = NULL;
+        }
+      else
+        {
+          return svn_error_trace(err);
+        }
+    }
+
   /* Fetch the update editor.  If REVISION is invalid, that's okay;
      the RA driver will call editor->set_target_revision later on. */
   SVN_ERR(svn_wc__get_update_editor(&update_editor, &update_edit_baton,
                                     &revnum, ctx->wc_ctx, anchor_abspath,
-                                    target, use_commit_times, depth,
-                                    depth_is_sticky, allow_unver_obstructions,
+                                    target, wcroot_iprops, use_commit_times,
+                                    depth, depth_is_sticky,
+                                    allow_unver_obstructions,
                                     adds_as_modification,
                                     server_supports_depth,
                                     clean_checkout,
@@ -431,6 +461,19 @@ update_internal(svn_revnum_t *result_rev
                                            ctx, pool));
     }
 
+  /* Cache inherited props. */
+  err = svn_wc__node_is_status_not_present(&is_not_present, ctx->wc_ctx,
+                                           local_abspath, pool);
+  if (err)
+    {
+      if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
+        return svn_error_trace(err);
+
+      svn_error_clear(err);
+      err = SVN_NO_ERROR;
+      is_not_present = TRUE;
+    }
+
   if (sleep_here)
     svn_io_sleep_for_timestamps(local_abspath, pool);
 

Modified: subversion/trunk/subversion/libsvn_ra/compat.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_ra/compat.c?rev=1395109&r1=1395108&r2=1395109&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_ra/compat.c (original)
+++ subversion/trunk/subversion/libsvn_ra/compat.c Sat Oct  6 16:40:55 2012
@@ -871,3 +871,90 @@ svn_ra__get_deleted_rev_from_log(svn_ra_
   *revision_deleted = log_path_deleted_baton.revision_deleted;
   return SVN_NO_ERROR;
 }
+
+
+svn_error_t *
+svn_ra__get_inherited_props_walk(svn_ra_session_t *session,
+                                 const char *path,
+                                 svn_revnum_t revision,
+                                 apr_array_header_t **inherited_props,
+                                 apr_pool_t *result_pool,
+                                 apr_pool_t *scratch_pool)
+{
+  const char *repos_root_url;
+  const char *session_url;
+  const char *parent_url;
+  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
+
+  *inherited_props =
+    apr_array_make(result_pool, 1, sizeof(svn_prop_inherited_item_t *));
+
+  /* Walk to the root of the repository getting inherited
+     props for PATH. */
+  SVN_ERR(svn_ra_get_repos_root2(session, &repos_root_url, scratch_pool));
+  SVN_ERR(svn_ra_get_session_url(session, &session_url, scratch_pool));
+  parent_url = session_url;
+
+  while (strcmp(repos_root_url, parent_url))
+    {
+      apr_hash_index_t *hi;
+      apr_hash_t *parent_props;
+      apr_hash_t *final_hash = apr_hash_make(result_pool);
+      svn_error_t *err;
+
+      svn_pool_clear(iterpool);
+      parent_url = svn_uri_dirname(parent_url, iterpool);
+      SVN_ERR(svn_ra_reparent(session, parent_url, iterpool));
+      err = session->vtable->get_dir(session, NULL, NULL,
+                                     &parent_props, "",
+                                     revision, SVN_DIRENT_ALL,
+                                     iterpool);
+
+      /* If the user doesn't have read access to a parent path then
+         skip, but allow them to inherit from further up. */
+      if (err)
+        {
+          if ((err->apr_err == SVN_ERR_RA_NOT_AUTHORIZED)
+              || (err->apr_err == SVN_ERR_RA_DAV_FORBIDDEN))
+            {
+              svn_error_clear(err);
+              continue;
+            }
+          else
+            {
+              return svn_error_trace(err);
+            }
+        }
+
+      for (hi = apr_hash_first(scratch_pool, parent_props);
+           hi;
+           hi = apr_hash_next(hi))
+        {
+          const char *name = svn__apr_hash_index_key(hi);
+          apr_ssize_t klen = svn__apr_hash_index_klen(hi);
+          svn_string_t *value = svn__apr_hash_index_val(hi);
+
+          if (svn_property_kind(NULL, name) == svn_prop_regular_kind)
+            {
+              name = apr_pstrdup(result_pool, name);
+              value = svn_string_dup(value, result_pool);
+              apr_hash_set(final_hash, name, klen, value);
+            }
+        }
+
+      if (apr_hash_count(final_hash))
+        {
+          svn_prop_inherited_item_t *new_iprop =
+            apr_palloc(result_pool, sizeof(*new_iprop));
+          new_iprop->path_or_url = apr_pstrdup(result_pool, parent_url);
+          new_iprop->prop_hash = final_hash;
+          svn_sort__array_insert(&new_iprop, *inherited_props, 0);
+        }
+    }
+
+  /* Reparent session back to original URL. */
+  SVN_ERR(svn_ra_reparent(session, session_url, scratch_pool));
+
+  svn_pool_destroy(iterpool);
+  return SVN_NO_ERROR;
+}

Modified: subversion/trunk/subversion/libsvn_ra/deprecated.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_ra/deprecated.c?rev=1395109&r1=1395108&r2=1395109&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_ra/deprecated.c (original)
+++ subversion/trunk/subversion/libsvn_ra/deprecated.c Sat Oct  6 16:40:55 2012
@@ -417,3 +417,16 @@ svn_error_t *svn_ra_do_status(svn_ra_ses
                                     SVN_DEPTH_INFINITY_OR_IMMEDIATES(recurse),
                                     status_editor, status_baton, pool);
 }
+
+svn_error_t *svn_ra_get_dir(svn_ra_session_t *session,
+                            const char *path,
+                            svn_revnum_t revision,
+                            apr_hash_t **dirents,
+                            svn_revnum_t *fetched_rev,
+                            apr_hash_t **props,
+                            apr_pool_t *pool)
+{
+  SVN_ERR_ASSERT(*path != '/');
+  return session->vtable->get_dir(session, dirents, fetched_rev, props,
+                                  path, revision, SVN_DIRENT_ALL, pool);
+}

Modified: subversion/trunk/subversion/libsvn_ra/ra_loader.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_ra/ra_loader.c?rev=1395109&r1=1395108&r2=1395109&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_ra/ra_loader.c (original)
+++ subversion/trunk/subversion/libsvn_ra/ra_loader.c Sat Oct  6 16:40:55 2012
@@ -43,6 +43,9 @@
 #include "svn_xml.h"
 #include "svn_path.h"
 #include "svn_dso.h"
+#include "svn_props.h"
+#include "svn_sorts.h"
+
 #include "svn_config.h"
 #include "ra_loader.h"
 
@@ -748,19 +751,6 @@ svn_error_t *svn_ra_get_file(svn_ra_sess
                                    fetched_rev, props, pool);
 }
 
-svn_error_t *svn_ra_get_dir(svn_ra_session_t *session,
-                            const char *path,
-                            svn_revnum_t revision,
-                            apr_hash_t **dirents,
-                            svn_revnum_t *fetched_rev,
-                            apr_hash_t **props,
-                            apr_pool_t *pool)
-{
-  SVN_ERR_ASSERT(*path != '/');
-  return session->vtable->get_dir(session, dirents, fetched_rev, props,
-                                  path, revision, SVN_DIRENT_ALL, pool);
-}
-
 svn_error_t *svn_ra_get_dir2(svn_ra_session_t *session,
                              apr_hash_t **dirents,
                              svn_revnum_t *fetched_rev,
@@ -1269,6 +1259,38 @@ svn_ra_get_deleted_rev(svn_ra_session_t 
   return err;
 }
 
+svn_error_t *
+svn_ra_get_inherited_props(svn_ra_session_t *session,
+                           apr_array_header_t **iprops,
+                           const char *path,
+                           svn_revnum_t revision,
+                           apr_pool_t *result_pool,
+                           apr_pool_t *scratch_pool)
+{
+  svn_boolean_t iprop_capable;
+
+  /* Path must be relative. */
+  SVN_ERR_ASSERT(*path != '/');
+
+  SVN_ERR(svn_ra_has_capability(session, &iprop_capable,
+                                SVN_RA_CAPABILITY_INHERITED_PROPS,
+                                scratch_pool));
+
+  if (iprop_capable)
+    {
+      SVN_ERR(session->vtable->get_inherited_props(session, iprops, path,
+                                                   revision, result_pool,
+                                                   scratch_pool));
+    }
+  else
+    {
+      /* Fallback for legacy servers. */
+      SVN_ERR(svn_ra__get_inherited_props_walk(session, path, revision, iprops,
+                                               result_pool, scratch_pool));
+    }
+
+  return SVN_NO_ERROR;
+}
 
 svn_error_t *
 svn_ra__get_commit_ev2(svn_editor_t **editor,
@@ -1329,7 +1351,6 @@ svn_ra__get_commit_ev2(svn_editor_t **ed
                            cancel_func, cancel_baton,
                            result_pool, scratch_pool));
 }
-
 
 
 svn_error_t *

Modified: subversion/trunk/subversion/libsvn_ra/ra_loader.h
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_ra/ra_loader.h?rev=1395109&r1=1395108&r2=1395109&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_ra/ra_loader.h (original)
+++ subversion/trunk/subversion/libsvn_ra/ra_loader.h Sat Oct  6 16:40:55 2012
@@ -299,7 +299,13 @@ typedef struct svn_ra__vtable_t {
   /* See svn_ra__register_editor_shim_callbacks() */
   svn_error_t *(*register_editor_shim_callbacks)(svn_ra_session_t *session,
                                     svn_delta_shim_callbacks_t *callbacks);
-
+  /* See svn_ra_get_inherited_props(). */
+  svn_error_t *(*get_inherited_props)(svn_ra_session_t *session,
+                                      apr_array_header_t **iprops,
+                                      const char *path,
+                                      svn_revnum_t revision,
+                                      apr_pool_t *result_pool,
+                                      apr_pool_t *scratch_pool);
   /* See svn_ra__get_commit_ev2()  */
   svn_error_t *(*get_commit_ev2)(
     svn_editor_t **editor,
@@ -473,6 +479,21 @@ svn_ra__get_deleted_rev_from_log(svn_ra_
                                  apr_pool_t *pool);
 
 
+/**
+ * Fallback logic for svn_ra_get_fileX and svn_ra_get_dirX when those APIs
+ * need to find PATH's inherited properties on a legacy server that
+ * doesn't have the SVN_RA_CAPABILITY_INHERITED_PROPS capability.
+ *
+ * All arguments are as per the two aforementioned APIs.
+ */
+svn_error_t *
+svn_ra__get_inherited_props_walk(svn_ra_session_t *session,
+                                 const char *path,
+                                 svn_revnum_t revision,
+                                 apr_array_header_t **inherited_props,
+                                 apr_pool_t *result_pool,
+                                 apr_pool_t *scratch_pool);
+
 /* Utility function to provide a shim between a returned Ev2 and an RA
    provider's Ev1-based commit editor.
 

Modified: subversion/trunk/subversion/libsvn_ra_local/ra_plugin.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_ra_local/ra_plugin.c?rev=1395109&r1=1395108&r2=1395109&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_ra_local/ra_plugin.c (original)
+++ subversion/trunk/subversion/libsvn_ra_local/ra_plugin.c Sat Oct  6 16:40:55 2012
@@ -1042,41 +1042,70 @@ svn_ra_local__stat(svn_ra_session_t *ses
 
 static svn_error_t *
 get_node_props(apr_hash_t **props,
+               apr_array_header_t **inherited_props,
                svn_ra_local__session_baton_t *sess,
                svn_fs_root_t *root,
                const char *path,
-               apr_pool_t *pool)
+               apr_pool_t *result_pool,
+               apr_pool_t *scratch_pool)
 {
   svn_revnum_t cmt_rev;
   const char *cmt_date, *cmt_author;
 
   /* Create a hash with props attached to the fs node. */
-  SVN_ERR(svn_fs_node_proplist(props, root, path, pool));
+  if (props)
+    {
+      SVN_ERR(svn_fs_node_proplist(props, root, path, result_pool));
+    }
+
+  /* Turn FS-path keys into URLs. */
+  if (inherited_props)
+    {
+      int i;
+
+      SVN_ERR(svn_repos_fs_get_inherited_props(inherited_props, root, path,
+                                               NULL, NULL,
+                                               result_pool, scratch_pool));
+
+      for (i = 0; i < (*inherited_props)->nelts; i++)
+        {
+          svn_prop_inherited_item_t *i_props =
+            APR_ARRAY_IDX(*inherited_props, i, svn_prop_inherited_item_t *);
+          i_props->path_or_url = svn_path_url_add_component2(
+            sess->repos_url, i_props->path_or_url, result_pool);
+        }
+    }
 
   /* Now add some non-tweakable metadata to the hash as well... */
 
-  /* The so-called 'entryprops' with info about CR & friends. */
-  SVN_ERR(svn_repos_get_committed_info(&cmt_rev, &cmt_date,
-                                       &cmt_author, root, path, pool));
-
-  apr_hash_set(*props,
-               SVN_PROP_ENTRY_COMMITTED_REV,
-               APR_HASH_KEY_STRING,
-               svn_string_createf(pool, "%ld", cmt_rev));
-  apr_hash_set(*props,
-               SVN_PROP_ENTRY_COMMITTED_DATE,
-               APR_HASH_KEY_STRING,
-               cmt_date ? svn_string_create(cmt_date, pool) : NULL);
-  apr_hash_set(*props,
-               SVN_PROP_ENTRY_LAST_AUTHOR,
-               APR_HASH_KEY_STRING,
-               cmt_author ? svn_string_create(cmt_author, pool) : NULL);
-  apr_hash_set(*props,
-               SVN_PROP_ENTRY_UUID,
-               APR_HASH_KEY_STRING,
-               svn_string_create(sess->uuid, pool));
+  if (props)
+    {
+      /* The so-called 'entryprops' with info about CR & friends. */
+      SVN_ERR(svn_repos_get_committed_info(&cmt_rev, &cmt_date,
+                                           &cmt_author, root, path,
+                                           scratch_pool));
+
+      apr_hash_set(*props,
+                   SVN_PROP_ENTRY_COMMITTED_REV,
+                   APR_HASH_KEY_STRING,
+                   svn_string_createf(result_pool, "%ld", cmt_rev));
+      apr_hash_set(*props,
+                   SVN_PROP_ENTRY_COMMITTED_DATE,
+                   APR_HASH_KEY_STRING,
+                   cmt_date ? svn_string_create(cmt_date,
+                                                result_pool) : NULL);
+      apr_hash_set(*props,
+                   SVN_PROP_ENTRY_LAST_AUTHOR,
+                   APR_HASH_KEY_STRING,
+                   cmt_author ? svn_string_create(cmt_author,
+                                                  result_pool) : NULL);
+      apr_hash_set(*props,
+                   SVN_PROP_ENTRY_UUID,
+                   APR_HASH_KEY_STRING,
+                   svn_string_create(sess->uuid, result_pool));
 
-  /* We have no 'wcprops' in ra_local, but might someday. */
+      /* We have no 'wcprops' in ra_local, but might someday. */
+    }
 
   return SVN_NO_ERROR;
 }
@@ -1148,7 +1177,7 @@ svn_ra_local__get_file(svn_ra_session_t 
 
   /* Handle props if requested. */
   if (props)
-    SVN_ERR(get_node_props(props, sess, root, abs_path, pool));
+    SVN_ERR(get_node_props(props, NULL, sess, root, abs_path, pool, pool));
 
   return SVN_NO_ERROR;
 }
@@ -1259,7 +1288,7 @@ svn_ra_local__get_dir(svn_ra_session_t *
 
   /* Handle props if requested. */
   if (props)
-    SVN_ERR(get_node_props(props, sess, root, abs_path, pool));
+    SVN_ERR(get_node_props(props, NULL, sess, root, abs_path, pool, pool));
 
   return SVN_NO_ERROR;
 }
@@ -1493,7 +1522,8 @@ svn_ra_local__has_capability(svn_ra_sess
       || strcmp(capability, SVN_RA_CAPABILITY_LOG_REVPROPS) == 0
       || strcmp(capability, SVN_RA_CAPABILITY_PARTIAL_REPLAY) == 0
       || strcmp(capability, SVN_RA_CAPABILITY_COMMIT_REVPROPS) == 0
-      || strcmp(capability, SVN_RA_CAPABILITY_ATOMIC_REVPROPS) == 0)
+      || strcmp(capability, SVN_RA_CAPABILITY_ATOMIC_REVPROPS) == 0
+      || strcmp(capability, SVN_RA_CAPABILITY_INHERITED_PROPS) == 0)
     {
       *has = TRUE;
     }
@@ -1537,6 +1567,44 @@ svn_ra_local__get_deleted_rev(svn_ra_ses
 }
 
 static svn_error_t *
+svn_ra_local__get_inherited_props(svn_ra_session_t *session,
+                                  apr_array_header_t **iprops,
+                                  const char *path,
+                                  svn_revnum_t revision,
+                                  apr_pool_t *result_pool,
+                                  apr_pool_t *scratch_pool)
+{
+  svn_fs_root_t *root;
+  svn_revnum_t youngest_rev;
+  svn_ra_local__session_baton_t *sess = session->priv;
+  const char *abs_path = svn_fspath__join(sess->fs_path->data, path,
+                                          scratch_pool);
+  svn_node_kind_t node_kind;
+
+  /* Open the revision's root. */
+  if (! SVN_IS_VALID_REVNUM(revision))
+    {
+      SVN_ERR(svn_fs_youngest_rev(&youngest_rev, sess->fs, scratch_pool));
+      SVN_ERR(svn_fs_revision_root(&root, sess->fs, youngest_rev,
+                                   scratch_pool));
+    }
+  else
+    {
+      SVN_ERR(svn_fs_revision_root(&root, sess->fs, revision, scratch_pool));
+    }
+
+  SVN_ERR(svn_fs_check_path(&node_kind, root, abs_path, scratch_pool));
+  if (node_kind == svn_node_none)
+    {
+      return svn_error_createf(SVN_ERR_FS_NOT_FOUND, NULL,
+                               _("'%s' path not found"), abs_path);
+    }
+
+  return svn_error_trace(get_node_props(NULL, iprops, sess, root, abs_path,
+                                        result_pool, scratch_pool));
+}
+
+static svn_error_t *
 svn_ra_local__register_editor_shim_callbacks(svn_ra_session_t *session,
                                     svn_delta_shim_callbacks_t *callbacks)
 {
@@ -1649,6 +1717,7 @@ static const svn_ra__vtable_t ra_local_v
   svn_ra_local__replay_range,
   svn_ra_local__get_deleted_rev,
   svn_ra_local__register_editor_shim_callbacks,
+  svn_ra_local__get_inherited_props,
   svn_ra_local__get_commit_ev2
 };
 

Modified: subversion/trunk/subversion/libsvn_ra_serf/options.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_ra_serf/options.c?rev=1395109&r1=1395108&r2=1395109&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_ra_serf/options.c (original)
+++ subversion/trunk/subversion/libsvn_ra_serf/options.c Sat Oct  6 16:40:55 2012
@@ -199,6 +199,12 @@ capabilities_headers_iterator_callback(v
                        SVN_RA_CAPABILITY_PARTIAL_REPLAY, APR_HASH_KEY_STRING,
                        capability_yes);
         }
+      if (svn_cstring_match_list(SVN_DAV_NS_DAV_SVN_INHERITED_PROPS, vals))
+        {
+          apr_hash_set(session->capabilities,
+                       SVN_RA_CAPABILITY_INHERITED_PROPS,
+                       APR_HASH_KEY_STRING, capability_yes);
+        }
       if (svn_cstring_match_list(SVN_DAV_NS_DAV_SVN_EPHEMERAL_TXNPROPS, vals))
         {
           apr_hash_set(session->capabilities,
@@ -323,6 +329,8 @@ options_response_handler(serf_request_t 
                    APR_HASH_KEY_STRING, capability_no);
       apr_hash_set(session->capabilities, SVN_RA_CAPABILITY_ATOMIC_REVPROPS,
                    APR_HASH_KEY_STRING, capability_no);
+      apr_hash_set(session->capabilities, SVN_RA_CAPABILITY_INHERITED_PROPS,
+                   APR_HASH_KEY_STRING, capability_no);
       apr_hash_set(session->capabilities, SVN_RA_CAPABILITY_EPHEMERAL_TXNPROPS,
                    APR_HASH_KEY_STRING, capability_no);
 

Modified: subversion/trunk/subversion/libsvn_ra_serf/ra_serf.h
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_ra_serf/ra_serf.h?rev=1395109&r1=1395108&r2=1395109&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_ra_serf/ra_serf.h (original)
+++ subversion/trunk/subversion/libsvn_ra_serf/ra_serf.h Sat Oct  6 16:40:55 2012
@@ -1657,6 +1657,14 @@ svn_ra_serf__get_deleted_rev(svn_ra_sess
                              svn_revnum_t *revision_deleted,
                              apr_pool_t *pool);
 
+/* Implements the get_inherited_props RA layer function. */
+svn_error_t * svn_ra_serf__get_inherited_props(svn_ra_session_t *session,
+                                               apr_array_header_t **iprops,
+                                               const char *path,
+                                               svn_revnum_t revision,
+                                               apr_pool_t *result_pool,
+                                               apr_pool_t *scratch_pool);
+
 /* Implements svn_ra__vtable_t.get_repos_root(). */
 svn_error_t *
 svn_ra_serf__get_repos_root(svn_ra_session_t *ra_session,
@@ -1668,7 +1676,6 @@ svn_error_t *
 svn_ra_serf__register_editor_shim_callbacks(svn_ra_session_t *session,
                                     svn_delta_shim_callbacks_t *callbacks);
 
-
 /*** Authentication handler declarations ***/
 
 /**

Modified: subversion/trunk/subversion/libsvn_ra_serf/serf.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_ra_serf/serf.c?rev=1395109&r1=1395108&r2=1395109&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_ra_serf/serf.c (original)
+++ subversion/trunk/subversion/libsvn_ra_serf/serf.c Sat Oct  6 16:40:55 2012
@@ -1149,7 +1149,8 @@ static const svn_ra__vtable_t serf_vtabl
   svn_ra_serf__has_capability,
   svn_ra_serf__replay_range,
   svn_ra_serf__get_deleted_rev,
-  svn_ra_serf__register_editor_shim_callbacks
+  svn_ra_serf__register_editor_shim_callbacks,
+  svn_ra_serf__get_inherited_props
 };
 
 svn_error_t *

Modified: subversion/trunk/subversion/libsvn_ra_svn/client.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_ra_svn/client.c?rev=1395109&r1=1395108&r2=1395109&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_ra_svn/client.c (original)
+++ subversion/trunk/subversion/libsvn_ra_svn/client.c Sat Oct  6 16:40:55 2012
@@ -1056,6 +1056,80 @@ static svn_error_t *ra_svn_commit(svn_ra
   return SVN_NO_ERROR;
 }
 
+/* Parse IPROPLIST, an array of svn_ra_svn_item_t structures, as a list of
+   const char * repos relative paths and properties for those paths, storing
+   the result as an array of svn_prop_inherited_item_t *items. */
+static svn_error_t *
+parse_iproplist(apr_array_header_t **inherited_props,
+                const apr_array_header_t *iproplist,
+                svn_ra_session_t *session,
+                apr_pool_t *result_pool,
+                apr_pool_t *scratch_pool)
+
+{
+  int i;
+  const char *repos_root_url;
+  apr_pool_t *iterpool;
+
+  if (iproplist == NULL)
+    {
+      /* If the server doesn't have the SVN_RA_CAPABILITY_INHERITED_PROPS
+         capability we shouldn't be asking for inherited props, but if we
+         did and the server sent back nothing then we'll want to handle
+         that. */
+      *inherited_props = NULL;
+      return SVN_NO_ERROR;
+    }
+
+  SVN_ERR(ra_svn_get_repos_root(session, &repos_root_url, scratch_pool));
+
+  *inherited_props = apr_array_make(
+    result_pool, iproplist->nelts, sizeof(svn_prop_inherited_item_t *));
+
+  iterpool = svn_pool_create(scratch_pool);
+
+  for (i = 0; i < iproplist->nelts; i++)
+    {
+      apr_array_header_t *iprop_list;
+      char *parent_rel_path;
+      apr_hash_t *iprops;
+      apr_hash_index_t *hi;
+      svn_prop_inherited_item_t *new_iprop =
+        apr_palloc(result_pool, sizeof(*new_iprop));
+      svn_ra_svn_item_t *elt = &APR_ARRAY_IDX(iproplist, i,
+                                              svn_ra_svn_item_t);
+      if (elt->kind != SVN_RA_SVN_LIST)
+        return svn_error_create(
+          SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
+          _("Inherited proplist element not a list"));
+
+      svn_pool_clear(iterpool);
+
+      SVN_ERR(svn_ra_svn_parse_tuple(elt->u.list, iterpool, "cl",
+                                     &parent_rel_path, &iprop_list));
+      SVN_ERR(svn_ra_svn_parse_proplist(iprop_list, iterpool, &iprops));
+      new_iprop->path_or_url = svn_path_url_add_component2(repos_root_url,
+                                                           parent_rel_path,
+                                                           result_pool);
+      new_iprop->prop_hash = apr_hash_make(result_pool);
+      for (hi = apr_hash_first(iterpool, iprops);
+           hi;
+           hi = apr_hash_next(hi))
+        {
+          const char *name = svn__apr_hash_index_key(hi);
+          svn_string_t *value = svn__apr_hash_index_val(hi);
+          apr_hash_set(new_iprop->prop_hash,
+                       apr_pstrdup(result_pool, name),
+                       APR_HASH_KEY_STRING,
+                       svn_string_dup(value, result_pool));
+        }
+      APR_ARRAY_PUSH(*inherited_props, svn_prop_inherited_item_t *) =
+        new_iprop;
+    }
+  svn_pool_destroy(iterpool);
+  return SVN_NO_ERROR;
+}
+
 static svn_error_t *ra_svn_get_file(svn_ra_session_t *session, const char *path,
                                     svn_revnum_t rev, svn_stream_t *stream,
                                     svn_revnum_t *fetched_rev,
@@ -2533,6 +2607,9 @@ static svn_error_t *ra_svn_has_capabilit
   else if (strcmp(capability, SVN_RA_CAPABILITY_ATOMIC_REVPROPS) == 0)
     *has = svn_ra_svn_has_capability(sess->conn,
                                      SVN_RA_SVN_CAP_ATOMIC_REVPROPS);
+  else if (strcmp(capability, SVN_RA_CAPABILITY_INHERITED_PROPS) == 0)
+    *has = svn_ra_svn_has_capability(sess->conn,
+                                     SVN_RA_SVN_CAP_INHERITED_PROPS);
   else if (strcmp(capability, SVN_RA_CAPABILITY_EPHEMERAL_TXNPROPS) == 0)
     *has = svn_ra_svn_has_capability(sess->conn,
                                      SVN_RA_SVN_CAP_EPHEMERAL_TXNPROPS);
@@ -2582,6 +2659,27 @@ ra_svn_register_editor_shim_callbacks(sv
   return SVN_NO_ERROR;
 }
 
+static svn_error_t *
+ra_svn_get_inherited_props(svn_ra_session_t *session,
+                           apr_array_header_t **iprops,
+                           const char *path,
+                           svn_revnum_t revision,
+                           apr_pool_t *result_pool,
+                           apr_pool_t *scratch_pool)
+{
+  svn_ra_svn__session_baton_t *sess_baton = session->priv;
+  svn_ra_svn_conn_t *conn = sess_baton->conn;
+  apr_array_header_t *iproplist;
+
+  SVN_ERR(svn_ra_svn_write_cmd(conn, scratch_pool, "get-iprops", "c(?r)",
+                               path, revision));
+  SVN_ERR(handle_auth_request(sess_baton, scratch_pool));
+  SVN_ERR(svn_ra_svn_read_cmd_response(conn, scratch_pool, "l", &iproplist));
+  SVN_ERR(parse_iproplist(iprops, iproplist, session, result_pool,
+                          scratch_pool));
+
+  return SVN_NO_ERROR;
+}
 
 static const svn_ra__vtable_t ra_svn_vtable = {
   svn_ra_svn_version,
@@ -2619,7 +2717,8 @@ static const svn_ra__vtable_t ra_svn_vta
   ra_svn_has_capability,
   ra_svn_replay_range,
   ra_svn_get_deleted_rev,
-  ra_svn_register_editor_shim_callbacks
+  ra_svn_register_editor_shim_callbacks,
+  ra_svn_get_inherited_props
 };
 
 svn_error_t *

Modified: subversion/trunk/subversion/libsvn_ra_svn/protocol
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_ra_svn/protocol?rev=1395109&r1=1395108&r2=1395109&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_ra_svn/protocol (original)
+++ subversion/trunk/subversion/libsvn_ra_svn/protocol Sat Oct  6 16:40:55 2012
@@ -202,6 +202,10 @@ capability and C indicates a client capa
 [S]  atomic-revprops   If the server presents this capability, it
                        supports the change-rev-prop2 command.
                        See section 3.1.1.
+[S]  inherited-props   If the server presents this capability, it supports the
+                       retrieval of inherited properties via the get-dir and
+                       get-file commands and also supports the get-iprops
+                       command (see section 3.1.1).
 
 3. Commands
 -----------
@@ -222,6 +226,7 @@ responds.
 Here are some miscellaneous prototypes used by the command sets:
 
   proplist:  ( ( name:string value:string ) ... )
+  iproplist: ( ( name:string proplist ) ... )
   propdelta: ( ( name:string [ value:string ] ) ... )
   node-kind: none|file|dir|unknown
   bool:      true|false
@@ -293,8 +298,10 @@ second place for auth-request point as n
                    ? ( post-commit-err:string ) )
 
   get-file
-    params:   ( path:string [ rev:number ] want-props:bool want-contents:bool )
-    response: ( [ checksum:string ] rev:number props:proplist )
+    params:   ( path:string [ rev:number ] want-props:bool want-contents:bool
+                [ want-iprops:bool ] )
+    response: ( [ checksum:string ] rev:number props:proplist
+                [ inherited-props:iproplist ] )
     If want-contents is specified, then after sending response, server
      sends file contents as a series of strings, terminated by the empty
      string, followed by a second empty command response to indicate
@@ -302,8 +309,9 @@ second place for auth-request point as n
 
   get-dir
     params:   ( path:string [ rev:number ] want-props:bool want-contents:bool
-                ? ( field:dirent-field ... ) )
-    response: ( rev:number props:proplist ( entry:dirent ... ) )]
+                ? ( field:dirent-field ... ) [ want-iprops:bool ] )
+    response: ( rev:number props:proplist ( entry:dirent ... )
+                [ inherited-props:iproplist ] )]
     dirent:   ( name:string kind:node-kind size:number has-props:bool
                 created-rev:number [ created-date:string ]
                 [ last-author:string ] )
@@ -464,6 +472,11 @@ second place for auth-request point as n
     params:   ( path:string peg-rev:number end-rev:number )
     response: ( deleted-rev:number )
 
+  get-iprops
+    params:   ( path:string [ rev:number ] )
+    response: ( inherited-props:iproplist )
+    New in svn 1.8.  If rev is not specified, the youngest revision is used.
+
 3.1.2. Editor Command Set
 
 An edit operation produces only one response, at close-edit or

Modified: subversion/trunk/subversion/libsvn_repos/fs-wrap.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_repos/fs-wrap.c?rev=1395109&r1=1395108&r2=1395109&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_repos/fs-wrap.c (original)
+++ subversion/trunk/subversion/libsvn_repos/fs-wrap.c Sat Oct  6 16:40:55 2012
@@ -31,10 +31,12 @@
 #include "svn_props.h"
 #include "svn_repos.h"
 #include "svn_time.h"
+#include "svn_sorts.h"
 #include "repos.h"
 #include "svn_private_config.h"
 #include "private/svn_repos_private.h"
 #include "private/svn_utf_private.h"
+#include "private/svn_fspath.h"
 
 
 /*** Commit wrappers ***/
@@ -737,7 +739,54 @@ svn_repos_fs_pack2(svn_repos_t *repos,
                      cancel_func, cancel_baton, pool);
 }
 
+svn_error_t *
+svn_repos_fs_get_inherited_props(apr_array_header_t **inherited_props_p,
+                                 svn_fs_root_t *root,
+                                 const char *path,
+                                 svn_repos_authz_func_t authz_read_func,
+                                 void *authz_read_baton,
+                                 apr_pool_t *result_pool,
+                                 apr_pool_t *scratch_pool)
+{
+  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
+  apr_array_header_t *inherited_props;
+  const char *parent_path = path;
+
+  inherited_props = apr_array_make(result_pool, 1,
+                                   sizeof(svn_prop_inherited_item_t *));
+  while (!(parent_path[0] == '/' && parent_path[1] == '\0'))
+    {
+      svn_boolean_t allowed = TRUE;
+      apr_hash_t *parent_properties;
+
+      svn_pool_clear(iterpool);
+      parent_path = svn_fspath__dirname(parent_path, iterpool);
+
+      if (authz_read_func)
+        SVN_ERR(authz_read_func(&allowed, root, parent_path,
+                                authz_read_baton, iterpool));
+      if (allowed)
+        {
+          SVN_ERR(svn_fs_node_proplist(&parent_properties, root,
+                                       parent_path, result_pool));
+          if (parent_properties && apr_hash_count(parent_properties))
+            {
+              svn_prop_inherited_item_t *i_props =
+                apr_pcalloc(result_pool, sizeof(*i_props));
+              i_props->path_or_url =
+                apr_pstrdup(result_pool, parent_path + 1);
+              i_props->prop_hash = parent_properties;
+              /* Build the output array in depth-first order. */
+              svn_sort__array_insert(&i_props, inherited_props, 0);
+            }
+        }
+    }
 
+  svn_pool_destroy(iterpool);
+
+  *inherited_props_p = inherited_props;
+  return SVN_NO_ERROR;
+}
 
 /*
  * vim:ts=4:sw=2:expandtab:tw=80:fo=tcroq

Modified: subversion/trunk/subversion/libsvn_subr/cmdline.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_subr/cmdline.c?rev=1395109&r1=1395108&r2=1395109&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_subr/cmdline.c (original)
+++ subversion/trunk/subversion/libsvn_subr/cmdline.c Sat Oct  6 16:40:55 2012
@@ -642,6 +642,7 @@ void
 svn_cmdline__print_xml_prop(svn_stringbuf_t **outstr,
                             const char* propname,
                             svn_string_t *propval,
+                            svn_boolean_t inherited_prop,
                             apr_pool_t *pool)
 {
   const char *xml_safe;
@@ -665,16 +666,22 @@ svn_cmdline__print_xml_prop(svn_stringbu
     }
 
   if (encoding)
-    svn_xml_make_open_tag(outstr, pool, svn_xml_protect_pcdata,
-                          "property", "name", propname,
-                          "encoding", encoding, NULL);
+    svn_xml_make_open_tag(
+      outstr, pool, svn_xml_protect_pcdata,
+      inherited_prop ? "inherited_property" : "property",
+      "name", propname,
+      "encoding", encoding, NULL);
   else
-    svn_xml_make_open_tag(outstr, pool, svn_xml_protect_pcdata,
-                          "property", "name", propname, NULL);
+    svn_xml_make_open_tag(
+      outstr, pool, svn_xml_protect_pcdata,
+      inherited_prop ? "inherited_property" : "property",
+      "name", propname, NULL);
 
   svn_stringbuf_appendcstr(*outstr, xml_safe);
 
-  svn_xml_make_close_tag(outstr, pool, "property");
+  svn_xml_make_close_tag(
+    outstr, pool,
+    inherited_prop ? "inherited_property" : "property");
 
   return;
 }

Modified: subversion/trunk/subversion/libsvn_subr/log.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_subr/log.c?rev=1395109&r1=1395108&r2=1395109&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_subr/log.c (original)
+++ subversion/trunk/subversion/libsvn_subr/log.c Sat Oct  6 16:40:55 2012
@@ -380,3 +380,17 @@ svn_log__replay(const char *path, svn_re
     log_path = "/";
   return apr_psprintf(pool, "replay %s r%ld", log_path, rev);
 }
+
+const char *
+svn_log__get_inherited_props(const char *path,
+                             svn_revnum_t rev,
+                             apr_pool_t *pool)
+{
+  const char *log_path;
+
+  if (path && path[0] != '\0')
+    log_path = svn_path_uri_encode(path, pool);
+  else
+    log_path = "/";
+  return apr_psprintf(pool, "get-inherited-props %s r%ld", log_path, rev);
+}

Modified: subversion/trunk/subversion/libsvn_subr/skel.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_subr/skel.c?rev=1395109&r1=1395108&r2=1395109&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_subr/skel.c (original)
+++ subversion/trunk/subversion/libsvn_subr/skel.c Sat Oct  6 16:40:55 2012
@@ -23,6 +23,8 @@
 #include <string.h>
 #include "svn_string.h"
 #include "svn_error.h"
+#include "svn_props.h"
+#include "svn_pools.h"
 #include "private/svn_skel.h"
 #include "private/svn_string_private.h"
 
@@ -167,6 +169,35 @@ is_valid_proplist_skel(const svn_skel_t 
   return FALSE;
 }
 
+static svn_boolean_t
+is_valid_iproplist_skel(const svn_skel_t *skel)
+{
+  int len = svn_skel__list_length(skel);
+
+  if ((len >= 0) && (len & 1) == 0)
+    {
+      svn_skel_t *elt;
+
+      for (elt = skel->children; elt; elt = elt->next)
+        {
+          if (!elt->is_atom)
+            return FALSE;
+
+          if (elt->next == NULL)
+            return FALSE;
+
+          elt = elt->next;
+
+          if (! is_valid_proplist_skel(elt))
+            return FALSE;
+        }
+
+      return TRUE;
+    }
+
+  return FALSE;
+}
+
 
 static svn_skel_t *parse(const char *data, apr_size_t len,
                          apr_pool_t *pool);
@@ -689,6 +720,34 @@ svn_skel__parse_proplist(apr_hash_t **pr
   return SVN_NO_ERROR;
 }
 
+svn_error_t *
+svn_skel__parse_iprops(apr_array_header_t **iprops,
+                       const svn_skel_t *skel,
+                       apr_pool_t *result_pool)
+{
+  svn_skel_t *elt;
+
+  /* Validate the skel. */
+  if (! is_valid_iproplist_skel(skel))
+    return skel_err("iprops");
+
+  /* Create the returned structure */
+  *iprops = apr_array_make(result_pool, 1,
+                           sizeof(svn_prop_inherited_item_t *));
+
+  for (elt = skel->children; elt; elt = elt->next->next)
+    {
+      svn_prop_inherited_item_t *new_iprop = apr_palloc(result_pool,
+                                                        sizeof(*new_iprop));
+      svn_string_t *repos_parent = svn_string_ncreate(elt->data, elt->len,
+                                                      result_pool);
+      SVN_ERR(svn_skel__parse_proplist(&(new_iprop->prop_hash), elt->next,
+                                       result_pool));
+      new_iprop->path_or_url = repos_parent->data;
+      APR_ARRAY_PUSH(*iprops, svn_prop_inherited_item_t *) = new_iprop;
+    }
+  return SVN_NO_ERROR;
+}
 
 svn_error_t *
 svn_skel__parse_prop(svn_string_t **propval,
@@ -760,3 +819,65 @@ svn_skel__unparse_proplist(svn_skel_t **
   *skel_p = skel;
   return SVN_NO_ERROR;
 }
+
+svn_error_t *
+svn_skel__unparse_iproplist(svn_skel_t **skel_p,
+                            const apr_array_header_t *inherited_props,
+                            apr_pool_t *result_pool)
+{
+  svn_skel_t *skel = svn_skel__make_empty_list(result_pool);
+
+  /* Create the skel. */
+  if (inherited_props)
+    {
+      int i;
+      apr_hash_index_t *hi;
+      apr_pool_t *subpool = svn_pool_create(result_pool);
+
+      for (i = 0; i < inherited_props->nelts; i++)
+        {
+          svn_prop_inherited_item_t *iprop =
+            APR_ARRAY_IDX(inherited_props, i, svn_prop_inherited_item_t *);
+
+          svn_skel_t *skel_list = svn_skel__make_empty_list(result_pool);
+          svn_skel_t *skel_atom;
+
+          svn_pool_clear(subpool);
+
+          /* Loop over hash entries */
+          for (hi = apr_hash_first(subpool, iprop->prop_hash);
+               hi;
+               hi = apr_hash_next(hi))
+            {
+              const void *key;
+              void *val;
+              apr_ssize_t klen;
+              svn_string_t *value;
+
+              apr_hash_this(hi, &key, &klen, &val);
+              value = val;
+
+              /* VALUE */
+              svn_skel__prepend(svn_skel__mem_atom(value->data, value->len,
+                                                   result_pool), skel_list);
+
+              /* NAME */
+              svn_skel__prepend(svn_skel__mem_atom(key, klen, result_pool),
+                                skel_list);
+            }
+
+          skel_atom = svn_skel__str_atom(
+            apr_pstrdup(result_pool, iprop->path_or_url), result_pool);
+          svn_skel__append(skel, skel_atom);
+          svn_skel__append(skel, skel_list);
+        }
+      svn_pool_destroy(subpool);
+    }
+
+  /* Validate and return the skel. */
+  if (! is_valid_iproplist_skel(skel))
+    return skel_err("iproplist");
+
+  *skel_p = skel;
+  return SVN_NO_ERROR;
+}

Modified: subversion/trunk/subversion/libsvn_subr/sqlite.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_subr/sqlite.c?rev=1395109&r1=1395108&r2=1395109&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_subr/sqlite.c (original)
+++ subversion/trunk/subversion/libsvn_subr/sqlite.c Sat Oct  6 16:40:55 2012
@@ -476,6 +476,27 @@ svn_sqlite__bind_properties(svn_sqlite__
 }
 
 svn_error_t *
+svn_sqlite__bind_iprops(svn_sqlite__stmt_t *stmt,
+                        int slot,
+                        const apr_array_header_t *inherited_props,
+                        apr_pool_t *scratch_pool)
+{
+  svn_skel_t *skel;
+  svn_stringbuf_t *properties;
+
+  if (inherited_props == NULL)
+    return svn_error_trace(svn_sqlite__bind_blob(stmt, slot, NULL, 0));
+
+  SVN_ERR(svn_skel__unparse_iproplist(&skel, inherited_props,
+                                      scratch_pool));
+  properties = svn_skel__unparse(skel, scratch_pool);
+  return svn_error_trace(svn_sqlite__bind_blob(stmt,
+                                               slot,
+                                               properties->data,
+                                               properties->len));
+}
+
+svn_error_t *
 svn_sqlite__bind_checksum(svn_sqlite__stmt_t *stmt,
                           int slot,
                           const svn_checksum_t *checksum,
@@ -581,6 +602,31 @@ svn_sqlite__column_properties(apr_hash_t
 }
 
 svn_error_t *
+svn_sqlite__column_iprops(apr_array_header_t **iprops,
+                          svn_sqlite__stmt_t *stmt,
+                          int column,
+                          apr_pool_t *result_pool,
+                          apr_pool_t *scratch_pool)
+{
+  apr_size_t len;
+  const void *val;
+
+  /* svn_skel__parse_iprops copies everything needed to result_pool */
+  val = svn_sqlite__column_blob(stmt, column, &len, NULL);
+  if (val == NULL)
+    {
+      *iprops = NULL;
+      return SVN_NO_ERROR;
+    }
+
+  SVN_ERR(svn_skel__parse_iprops(iprops,
+                                 svn_skel__parse(val, len, scratch_pool),
+                                 result_pool));
+
+  return SVN_NO_ERROR;
+}
+
+svn_error_t *
 svn_sqlite__column_checksum(const svn_checksum_t **checksum,
                             svn_sqlite__stmt_t *stmt, int column,
                             apr_pool_t *result_pool)

Modified: subversion/trunk/subversion/libsvn_wc/deprecated.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_wc/deprecated.c?rev=1395109&r1=1395108&r2=1395109&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_wc/deprecated.c (original)
+++ subversion/trunk/subversion/libsvn_wc/deprecated.c Sat Oct  6 16:40:55 2012
@@ -3269,7 +3269,7 @@ svn_wc_get_update_editor4(const svn_delt
                               target_revision,
                               wc_ctx,
                               anchor_abspath,
-                              target_basename,
+                              target_basename, NULL,
                               use_commit_times,
                               depth, depth_is_sticky,
                               allow_unver_obstructions,
@@ -3455,7 +3455,7 @@ svn_wc_get_switch_editor4(const svn_delt
                               target_revision,
                               wc_ctx,
                               anchor_abspath, target_basename,
-                              switch_url,
+                              switch_url, NULL,
                               use_commit_times,
                               depth, depth_is_sticky,
                               allow_unver_obstructions,

Modified: subversion/trunk/subversion/libsvn_wc/externals.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_wc/externals.c?rev=1395109&r1=1395108&r2=1395109&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_wc/externals.c (original)
+++ subversion/trunk/subversion/libsvn_wc/externals.c Sat Oct  6 16:40:55 2012
@@ -381,6 +381,10 @@ struct edit_baton
   /* List of incoming propchanges */
   apr_array_header_t *propchanges;
 
+  /* Array of svn_prop_inherited_item_t * structures representing the
+     properties inherited by the base node at LOCAL_ABSPATH. */
+  apr_array_header_t *iprops;
+
   /* The last change information */
   svn_revnum_t changed_rev;
   apr_time_t changed_date;
@@ -810,6 +814,7 @@ close_file(void *file_baton,
                         eb->repos_uuid,
                         *eb->target_revision,
                         new_pristine_props,
+                        eb->iprops,
                         eb->changed_rev,
                         eb->changed_date,
                         eb->changed_author,
@@ -825,6 +830,12 @@ close_file(void *file_baton,
                         all_work_items,
                         pool));
 
+    /* close_edit may also update iprops for switched files, catching
+       those for which close_file is never called (e.g. an update of a
+       file external with no changes).  So as a minor optimization we
+       clear the iprops so as not to set them again in close_edit. */
+    eb->iprops = NULL;
+
     SVN_ERR(svn_wc__wq_run(eb->db, eb->wri_abspath,
                            eb->cancel_func, eb->cancel_baton, pool));
   }
@@ -864,8 +875,18 @@ close_edit(void *edit_baton,
 {
   struct edit_baton *eb = edit_baton;
 
-  if (!eb->file_closed)
+  if (!eb->file_closed
+      || eb->iprops)
     {
+      apr_hash_t *wcroot_iprops = NULL;
+
+      if (eb->iprops)
+        {
+          wcroot_iprops = apr_hash_make(pool);
+          apr_hash_set(wcroot_iprops, eb->local_abspath, APR_HASH_KEY_STRING,
+                       eb->iprops);
+        }
+
       /* The node wasn't updated, so we just have to bump its revision */
       SVN_ERR(svn_wc__db_op_bump_revisions_post_update(eb->db,
                                                        eb->local_abspath,
@@ -873,6 +894,7 @@ close_edit(void *edit_baton,
                                                        NULL, NULL, NULL,
                                                        *eb->target_revision,
                                                        apr_hash_make(pool),
+                                                       wcroot_iprops,
                                                        pool));
     }
 
@@ -889,6 +911,7 @@ svn_wc__get_file_external_editor(const s
                                  const char *url,
                                  const char *repos_root_url,
                                  const char *repos_uuid,
+                                 apr_array_header_t *iprops,
                                  svn_boolean_t use_commit_times,
                                  const char *diff3_cmd,
                                  const apr_array_header_t *preserved_exts,
@@ -924,6 +947,8 @@ svn_wc__get_file_external_editor(const s
   eb->repos_root_url = apr_pstrdup(edit_pool, repos_root_url);
   eb->repos_uuid = apr_pstrdup(edit_pool, repos_uuid);
 
+  eb->iprops = iprops;
+
   eb->use_commit_times = use_commit_times;
   eb->ext_patterns = preserved_exts;
   eb->diff3cmd = diff3_cmd;

Modified: subversion/trunk/subversion/libsvn_wc/props.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_wc/props.c?rev=1395109&r1=1395108&r2=1395109&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_wc/props.c (original)
+++ subversion/trunk/subversion/libsvn_wc/props.c Sat Oct  6 16:40:55 2012
@@ -46,6 +46,7 @@
 #include "svn_wc.h"
 #include "svn_utf.h"
 #include "svn_diff.h"
+#include "svn_sorts.h"
 
 #include "private/svn_wc_private.h"
 #include "private/svn_mergeinfo_private.h"
@@ -2332,3 +2333,151 @@ svn_wc__has_magic_property(const apr_arr
     }
   return FALSE;
 }
+
+/* Remove all prop name value pairs from PROP_HASH where the property
+   name is not PROPNAME. */
+static void
+filter_unwanted_props(apr_hash_t *prop_hash,
+                      const char * propname,
+                      apr_pool_t *scratch_pool)
+{
+  apr_hash_index_t *hi;
+
+  for (hi = apr_hash_first(scratch_pool, prop_hash);
+       hi;
+       hi = apr_hash_next(hi))
+    {
+      const char *ipropname = svn__apr_hash_index_key(hi);
+
+      if (strcmp(ipropname, propname) != 0)
+        apr_hash_set(prop_hash, ipropname, APR_HASH_KEY_STRING, NULL);
+    }
+  return;
+}
+
+svn_error_t *
+svn_wc__get_iprops(apr_array_header_t **inherited_props,
+                   svn_wc_context_t *wc_ctx,
+                   const char *local_abspath,
+                   const char *propname,
+                   apr_pool_t *result_pool,
+                   apr_pool_t *scratch_pool)
+{
+  int i;
+  apr_array_header_t *cached_iprops = NULL;
+  const char *parent_abspath = local_abspath;
+  svn_boolean_t is_wc_root = FALSE;
+  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
+
+  SVN_ERR_ASSERT(inherited_props);
+  *inherited_props = apr_array_make(result_pool, 1,
+                                    sizeof(svn_prop_inherited_item_t *));
+
+  /* Walk up to the root of the WC looking for inherited properties.  When we
+     reach the WC root also check for cached inherited properties. */
+  while (TRUE)
+    {
+      apr_hash_t *actual_props;
+
+      svn_pool_clear(iterpool);
+
+      SVN_ERR(svn_wc_is_wc_root2(&is_wc_root, wc_ctx, parent_abspath,
+                                 iterpool));
+      if (is_wc_root)
+        {
+          const char *child_repos_relpath;
+
+          SVN_ERR(svn_wc__node_get_repos_relpath(&child_repos_relpath,
+                                                 wc_ctx, parent_abspath,
+                                                 iterpool, iterpool));
+
+          /* If the WC root is also the root of the repository then by
+             definition there are no inheritable properties to be had. */
+          if (child_repos_relpath[0] != '\0')
+            {
+              /* Grab the cached inherited properties for the WC root. */
+              SVN_ERR(svn_wc__db_read_cached_iprops(&cached_iprops,
+                                                    wc_ctx->db,
+                                                    parent_abspath,
+                                                    scratch_pool,
+                                                    iterpool));
+            }
+        }
+
+      /* If PARENT_ABSPATH is a true parent of LOCAL_ABSPATH, then
+         LOCAL_ABSPATH can inherit properties from it. */
+      if (strcmp(local_abspath, parent_abspath) != 0)
+        {
+          SVN_ERR(svn_wc__db_read_props(&actual_props, wc_ctx->db,
+                                        parent_abspath, result_pool,
+                                        iterpool));
+          if (actual_props)
+            {
+              /* If we only want PROPNAME filter out any other properties. */
+              if (propname)
+                filter_unwanted_props(actual_props, propname, iterpool);
+
+              if (apr_hash_count(actual_props))
+                {
+                  svn_prop_inherited_item_t *iprop_elt =
+                    apr_pcalloc(result_pool,
+                                sizeof(svn_prop_inherited_item_t));
+                  iprop_elt->path_or_url = apr_pstrdup(result_pool,
+                                                       parent_abspath);
+                  iprop_elt->prop_hash = actual_props;
+                  /* Build the output array in depth-first order. */
+                  svn_sort__array_insert(&iprop_elt, *inherited_props, 0);
+                }
+            }
+        }
+
+      /* Inheritance only goes as far as the nearest WC root. */
+      if (is_wc_root)
+        break;
+
+      /* Keep looking for the WC root. */
+      parent_abspath = svn_dirent_dirname(parent_abspath, scratch_pool);
+    }
+
+  if (cached_iprops)
+    {
+      for (i = cached_iprops->nelts - 1; i >= 0; i--)
+        {
+          svn_prop_inherited_item_t *cached_iprop =
+            APR_ARRAY_IDX(cached_iprops, i, svn_prop_inherited_item_t *);
+
+          /* An empty property hash in the iprops cache means there are no
+             inherited properties. */
+          if (apr_hash_count(cached_iprop->prop_hash) == 0)
+            continue;
+
+          if (propname)
+            filter_unwanted_props(cached_iprop->prop_hash, propname,
+                                  scratch_pool);
+
+          /* If we didn't filter everything then keep this iprop. */
+          if (apr_hash_count(cached_iprop->prop_hash))
+            svn_sort__array_insert(&cached_iprop, *inherited_props, 0);
+        }
+    }
+
+  svn_pool_destroy(iterpool);
+  return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_wc__get_cached_iprop_children(apr_hash_t **iprop_paths,
+                                  svn_depth_t depth,
+                                  svn_wc_context_t *wc_ctx,
+                                  const char *local_abspath,
+                                  apr_pool_t *result_pool,
+                                  apr_pool_t *scratch_pool)
+{
+  SVN_ERR(svn_wc__db_get_children_with_cached_iprops(iprop_paths,
+                                                     depth,
+                                                     local_abspath,
+                                                     wc_ctx->db,
+                                                     result_pool,
+                                                     scratch_pool));
+  return SVN_NO_ERROR;
+}

Modified: subversion/trunk/subversion/libsvn_wc/update_editor.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_wc/update_editor.c?rev=1395109&r1=1395108&r2=1395109&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_wc/update_editor.c (original)
+++ subversion/trunk/subversion/libsvn_wc/update_editor.c Sat Oct  6 16:40:55 2012
@@ -170,6 +170,12 @@ struct edit_baton
      generated conflict files. */
   const apr_array_header_t *ext_patterns;
 
+  /* Hash mapping const char * absolute working copy paths to depth-first
+     ordered arrays of svn_prop_inherited_item_t * structures representing
+     the properties inherited by the base node at that working copy path.
+     May be NULL. */
+  apr_hash_t *wcroot_iprops;
+
   /* The revision we're targeting...or something like that.  This
      starts off as a pointer to the revision to which we are updating,
      or SVN_INVALID_REVNUM, but by the end of the edit, should be
@@ -2638,6 +2644,7 @@ close_directory(void *dir_baton,
   else
     {
       apr_hash_t *props;
+      apr_array_header_t *iprops = NULL;
 
       /* ### we know a base node already exists. it was created in
          ### open_directory or add_directory.  let's just preserve the
@@ -2697,6 +2704,22 @@ close_directory(void *dir_baton,
                                             scratch_pool);
         }
 
+      /* Any inherited props to be set set for this base node? */
+      if (eb->wcroot_iprops)
+        {
+          iprops = apr_hash_get(eb->wcroot_iprops, db->local_abspath,
+                                APR_HASH_KEY_STRING);
+
+          /* close_edit may also update iprops for switched nodes, catching
+             those for which close_directory is never called (e.g. a switch
+             with no changes).  So as a minor optimization we remove any
+             iprops from the hash so as not to set them again in
+             close_edit. */
+          if (iprops)
+            apr_hash_set(eb->wcroot_iprops, db->local_abspath,
+                         APR_HASH_KEY_STRING, NULL);
+        }
+
       /* Update the BASE data for the directory and mark the directory
          complete */
       SVN_ERR(svn_wc__db_base_add_directory(
@@ -2715,7 +2738,7 @@ close_directory(void *dir_baton,
                 conflict_skel,
                 (! db->shadowed) && new_base_props != NULL,
                 new_actual_props,
-                all_work_items,
+                iprops, all_work_items,
                 scratch_pool));
     }
 
@@ -4391,6 +4414,7 @@ close_edit(void *edit_baton,
                                                        eb->repos_uuid,
                                                        *(eb->target_revision),
                                                        eb->skipped_trees,
+                                                       eb->wcroot_iprops,
                                                        eb->pool));
 
       if (*eb->target_basename != '\0')
@@ -4464,6 +4488,7 @@ make_editor(svn_revnum_t *target_revisio
             svn_wc__db_t *db,
             const char *anchor_abspath,
             const char *target_basename,
+            apr_hash_t *wcroot_iprops,
             svn_boolean_t use_commit_times,
             const char *switch_url,
             svn_depth_t depth,
@@ -4529,6 +4554,7 @@ make_editor(svn_revnum_t *target_revisio
   eb->db                       = db;
   eb->target_basename          = target_basename;
   eb->anchor_abspath           = anchor_abspath;
+  eb->wcroot_iprops            = wcroot_iprops;
 
   SVN_ERR(svn_wc__db_get_wcroot(&eb->wcroot_abspath, db, anchor_abspath,
                                 edit_pool, scratch_pool));
@@ -4750,6 +4776,7 @@ svn_wc__get_update_editor(const svn_delt
                           svn_wc_context_t *wc_ctx,
                           const char *anchor_abspath,
                           const char *target_basename,
+                          apr_hash_t *wcroot_iprops,
                           svn_boolean_t use_commit_times,
                           svn_depth_t depth,
                           svn_boolean_t depth_is_sticky,
@@ -4773,7 +4800,7 @@ svn_wc__get_update_editor(const svn_delt
                           apr_pool_t *scratch_pool)
 {
   return make_editor(target_revision, wc_ctx->db, anchor_abspath,
-                     target_basename, use_commit_times,
+                     target_basename, wcroot_iprops, use_commit_times,
                      NULL, depth, depth_is_sticky, allow_unver_obstructions,
                      adds_as_modification, server_performs_filtering,
                      clean_checkout,
@@ -4794,6 +4821,7 @@ svn_wc__get_switch_editor(const svn_delt
                           const char *anchor_abspath,
                           const char *target_basename,
                           const char *switch_url,
+                          apr_hash_t *wcroot_iprops,
                           svn_boolean_t use_commit_times,
                           svn_depth_t depth,
                           svn_boolean_t depth_is_sticky,
@@ -4817,7 +4845,7 @@ svn_wc__get_switch_editor(const svn_delt
   SVN_ERR_ASSERT(switch_url && svn_uri_is_canonical(switch_url, scratch_pool));
 
   return make_editor(target_revision, wc_ctx->db, anchor_abspath,
-                     target_basename, use_commit_times,
+                     target_basename, wcroot_iprops, use_commit_times,
                      switch_url,
                      depth, depth_is_sticky, allow_unver_obstructions,
                      FALSE /* adds_as_modification */,
@@ -5072,7 +5100,7 @@ svn_wc_is_wc_root2(svn_boolean_t *wc_roo
       return svn_error_create(SVN_ERR_ENTRY_NOT_FOUND, err, err->message);
     }
 
-  *wc_root = is_root || (kind == svn_kind_dir && is_switched);
+  *wc_root = is_root || is_switched;
 
   return SVN_NO_ERROR;
 }

Modified: subversion/trunk/subversion/libsvn_wc/upgrade.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_wc/upgrade.c?rev=1395109&r1=1395108&r2=1395109&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_wc/upgrade.c (original)
+++ subversion/trunk/subversion/libsvn_wc/upgrade.c Sat Oct  6 16:40:55 2012
@@ -1559,6 +1559,47 @@ bump_to_30(void *baton, svn_sqlite__db_t
   return SVN_NO_ERROR;
 }
 
+static svn_error_t *
+bump_to_31(void *baton,
+           svn_sqlite__db_t *sdb,
+           apr_pool_t *scratch_pool)
+{
+  svn_sqlite__stmt_t *stmt, *stmt_mark_switch_roots;
+  svn_boolean_t have_row;
+  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
+  apr_array_header_t *empty_iprops = apr_array_make(
+    scratch_pool, 0, sizeof(svn_prop_inherited_item_t *));
+
+  /* Add the inherited_props column to NODES. */
+  SVN_ERR(svn_sqlite__exec_statements(sdb, STMT_UPGRADE_TO_31));
+
+  /* Set inherited_props to an empty array for the roots of all
+     switched subtrees in the WC.  This allows subsequent updates
+     to recognize these roots as needing an iprops cache. */
+  SVN_ERR(svn_sqlite__get_statement(&stmt, sdb,
+                                    STMT_SELECT_WCROOT_NODES));
+  SVN_ERR(svn_sqlite__step(&have_row, stmt));
+
+  SVN_ERR(svn_sqlite__get_statement(&stmt_mark_switch_roots, sdb,
+                                    STMT_UPDATE_IPROP));
+  while (have_row)
+    {
+      const char *switched_relpath = svn_sqlite__column_text(stmt, 1, NULL);
+      apr_int64_t wc_id = svn_sqlite__column_int64(stmt, 0);
+
+      SVN_ERR(svn_sqlite__bindf(stmt_mark_switch_roots, "is", wc_id,
+                                switched_relpath));
+      SVN_ERR(svn_sqlite__bind_iprops(stmt_mark_switch_roots, 3,
+                                      empty_iprops, iterpool));
+      SVN_ERR(svn_sqlite__step_done(stmt_mark_switch_roots));
+      SVN_ERR(svn_sqlite__step(&have_row, stmt));
+    }
+
+  SVN_ERR(svn_sqlite__reset(stmt));
+  svn_pool_destroy(iterpool);
+  return SVN_NO_ERROR;
+}
+
 
 struct upgrade_data_t {
   svn_sqlite__db_t *sdb;
@@ -1832,13 +1873,16 @@ svn_wc__upgrade_sdb(int *result_format,
         *result_format = 29;
         /* FALLTHROUGH  */
 
-#if SVN_WC__VERSION >= 30
       case 29:
         SVN_ERR(svn_sqlite__with_transaction(sdb, bump_to_30, &bb,
                                              scratch_pool));
         *result_format = 30;
+
+      case 30:
+        SVN_ERR(svn_sqlite__with_transaction(sdb, bump_to_31, &bb,
+                                             scratch_pool));
+        *result_format = 31;
         /* FALLTHROUGH  */
-#endif
       /* ### future bumps go here.  */
 #if 0
       case XXX-1:

Modified: subversion/trunk/subversion/libsvn_wc/wc-metadata.sql
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_wc/wc-metadata.sql?rev=1395109&r1=1395108&r2=1395109&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_wc/wc-metadata.sql (original)
+++ subversion/trunk/subversion/libsvn_wc/wc-metadata.sql Sat Oct  6 16:40:55 2012
@@ -475,6 +475,10 @@ CREATE TABLE NODES (
      ### anyway. */
   file_external  INTEGER,
 
+  /* serialized skel of this node's inherited properties. NULL if this
+     is not the BASE of a WC root node. */
+  inherited_props  BLOB,
+
   PRIMARY KEY (wc_id, local_relpath, op_depth)
 
   );
@@ -575,6 +579,8 @@ CREATE UNIQUE INDEX I_EXTERNALS_DEFINED 
                                                       def_local_relpath,
                                                       local_relpath);
 
+/* ------------------------------------------------------------------------- */
+
 /* Format 20 introduces NODES and removes BASE_NODE and WORKING_NODE */
 
 -- STMT_UPGRADE_TO_20
@@ -789,7 +795,11 @@ UPDATE nodes SET presence = "server-excl
    working copies that were never updated by 1.7.0+ style clients */
 UPDATE nodes SET file_external=1 WHERE file_external IS NOT NULL;
 
-PRAGMA user_version = 30;
+/* Format 31 adds the inherited_props column to the NODES table. */
+-- STMT_UPGRADE_TO_31
+ALTER TABLE NODES ADD COLUMN inherited_props BLOB;
+
+PRAGMA user_version = 31;
 
 -- STMT_UPGRADE_30_SELECT_CONFLICT_SEPARATE
 SELECT wc_id, local_relpath,

Modified: subversion/trunk/subversion/libsvn_wc/wc-queries.sql
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_wc/wc-queries.sql?rev=1395109&r1=1395108&r2=1395109&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_wc/wc-queries.sql (original)
+++ subversion/trunk/subversion/libsvn_wc/wc-queries.sql Sat Oct  6 16:40:55 2012
@@ -149,9 +149,10 @@ 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, moved_to, moved_here)
+  dav_cache, symlink_target, file_external, moved_to, moved_here,
+  inherited_props)
 VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13, ?14,
-        ?15, ?16, ?17, ?18, ?19, ?20, ?21, ?22)
+        ?15, ?16, ?17, ?18, ?19, ?20, ?21, ?22, ?23)
 
 -- STMT_SELECT_BASE_PRESENT
 SELECT local_relpath, kind FROM nodes n
@@ -1128,6 +1129,28 @@ WHERE wc_id = ?1 AND local_relpath = ?2 
 SELECT 1 FROM nodes WHERE op_depth > 0
 LIMIT 1
 
+-- STMT_SELECT_WCROOT_NODES
+/* Select all base nodes which are the root of a WC, including
+   switched subtrees, but excluding those which map to the root
+   of the repos.
+
+   ### IPROPS: Is this query horribly inefficient?  Quite likely,
+   ### but it only runs during an upgrade, so do we care? */
+SELECT l.wc_id, l.local_relpath FROM nodes as l
+LEFT OUTER JOIN nodes as r
+ON l.wc_id = r.wc_id
+   AND l.repos_id = r.repos_id
+   AND r.local_relpath = l.parent_relpath
+WHERE (l.local_relpath == '' AND l.repos_path != '')
+   OR (l.op_depth = 0
+       AND l.local_relpath != ''
+       AND l.repos_path != ltrim(r.repos_path
+                                 || '/'
+                                 || ltrim(substr(l.local_relpath,
+                                                 length(l.parent_relpath) + 1),
+                                          '/'),
+                                 '/'))
+
 /* --------------------------------------------------------------------------
  * Complex queries for callback walks, caching results in a temporary table.
  *
@@ -1491,6 +1514,40 @@ WHERE wc_id == ?1
 
 /* ------------------------------------------------------------------------- */
 
+/* Queries for cached inherited properties. */
+
+/* Select the inherited properties of a single base node. */
+-- STMT_SELECT_IPROPS
+SELECT inherited_props FROM nodes
+WHERE wc_id = ?1
+  AND local_relpath = ?2
+  AND op_depth = 0
+
+/* Update the inherited properties of a single base node. */
+-- STMT_UPDATE_IPROP
+UPDATE nodes
+SET inherited_props = ?3
+WHERE (wc_id = ?1 AND local_relpath = ?2 AND op_depth = 0)
+
+/* Select a single path if its base node has cached inherited properties. */
+-- STMT_SELECT_INODES
+SELECT local_relpath FROM nodes
+WHERE wc_id = ?1
+  AND local_relpath = ?2
+  AND op_depth = 0
+  AND (inherited_props not null)
+
+/* Select all paths whose base nodes at or below a given path, which
+   have cached inherited properties. */
+-- STMT_SELECT_INODES_RECURSIVE
+SELECT local_relpath FROM nodes
+WHERE wc_id = ?1
+  AND IS_STRICT_DESCENDANT_OF(local_relpath, ?2)
+  AND op_depth = 0
+  AND (inherited_props not null)
+
+/* ------------------------------------------------------------------------- */
+
 /* Grab all the statements related to the schema.  */
 
 -- include: wc-metadata

Modified: subversion/trunk/subversion/libsvn_wc/wc.h
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_wc/wc.h?rev=1395109&r1=1395108&r2=1395109&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_wc/wc.h (original)
+++ subversion/trunk/subversion/libsvn_wc/wc.h Sat Oct  6 16:40:55 2012
@@ -154,10 +154,13 @@ extern "C" {
  * The bump to 30 switched the conflict storage to a skel inside conflict_data.
  * Also clears some known invalid state.
  *
+ * The bump to 31 added the inherited_props column in the NODES table.
+ * Bumped in r????????.
+ *
  * Please document any further format changes here.
  */
 
-#define SVN_WC__VERSION 30
+#define SVN_WC__VERSION 31
 
 
 /* Formats <= this have no concept of "revert text-base/props".  */

Modified: subversion/trunk/subversion/libsvn_wc/wc_db.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_wc/wc_db.c?rev=1395109&r1=1395108&r2=1395109&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_wc/wc_db.c (original)
+++ subversion/trunk/subversion/libsvn_wc/wc_db.c Sat Oct  6 16:40:55 2012
@@ -176,6 +176,11 @@ typedef struct insert_base_baton_t {
   svn_boolean_t update_actual_props;
   const apr_hash_t *new_actual_props;
 
+  /* A depth-first ordered array of svn_prop_inherited_item_t *
+     structures representing the properties inherited by the base
+     node. */
+  apr_array_header_t *iprops;
+
   /* maybe we should copy information from a previous record? */
   svn_boolean_t keep_recorded_info;
 
@@ -249,6 +254,7 @@ typedef struct insert_external_baton_t {
 
   /* for file and symlink externals */
   const apr_hash_t *props;
+  apr_array_header_t *iprops;
   svn_revnum_t changed_rev;
   apr_time_t changed_date;
   const char *changed_author;
@@ -829,6 +835,10 @@ insert_base_node(void *baton,
 
   SVN_ERR(svn_sqlite__bind_properties(stmt, 15, pibb->props,
                                       scratch_pool));
+
+  SVN_ERR(svn_sqlite__bind_iprops(stmt, 23, pibb->iprops,
+                                      scratch_pool));
+
   if (pibb->dav_cache)
     SVN_ERR(svn_sqlite__bind_properties(stmt, 18, pibb->dav_cache,
                                         scratch_pool));
@@ -1676,6 +1686,7 @@ svn_wc__db_base_add_directory(svn_wc__db
                               const svn_skel_t *conflict,
                               svn_boolean_t update_actual_props,
                               apr_hash_t *new_actual_props,
+                              apr_array_header_t *new_iprops,
                               const svn_skel_t *work_items,
                               apr_pool_t *scratch_pool)
 {
@@ -1710,6 +1721,7 @@ svn_wc__db_base_add_directory(svn_wc__db
   ibb.repos_relpath = repos_relpath;
   ibb.revision = revision;
 
+  ibb.iprops = new_iprops;
   ibb.props = props;
   ibb.changed_rev = changed_rev;
   ibb.changed_date = changed_date;
@@ -2973,6 +2985,7 @@ insert_external_node(void *baton,
       ibb.revision        = ieb->revision;
 
       ibb.props           = ieb->props;
+      ibb.iprops          = ieb->iprops;
       ibb.changed_rev     = ieb->changed_rev;
       ibb.changed_date    = ieb->changed_date;
       ibb.changed_author  = ieb->changed_author;
@@ -3037,6 +3050,7 @@ svn_wc__db_external_add_file(svn_wc__db_
                              svn_revnum_t revision,
 
                              const apr_hash_t *props,
+                             apr_array_header_t *iprops,
 
                              svn_revnum_t changed_rev,
                              apr_time_t changed_date,
@@ -3091,6 +3105,7 @@ svn_wc__db_external_add_file(svn_wc__db_
   ieb.revision = revision;
 
   ieb.props = props;
+  ieb.iprops = iprops;
 
   ieb.changed_rev = changed_rev;
   ieb.changed_date = changed_date;
@@ -9309,6 +9324,175 @@ svn_wc__db_prop_retrieve_recursive(apr_h
   return svn_error_trace(svn_sqlite__reset(stmt));
 }
 
+svn_error_t *
+svn_wc__db_read_cached_iprops(apr_array_header_t **iprops,
+                              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;
+  svn_sqlite__stmt_t *stmt;
+  svn_boolean_t have_row;
+  const char *repos_root_url;
+  svn_revnum_t revision;
+  int op_depth;
+  const char *repos_relpath;
+
+  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
+
+  SVN_ERR(svn_wc__db_read_info(NULL, NULL,
+                               &revision, &repos_relpath, &repos_root_url,
+                               NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+                               NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+                               NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+                               NULL, db, local_abspath, result_pool,
+                               scratch_pool));
+
+  if (repos_relpath && repos_relpath[0] == '\0')
+    {
+      /* LOCAL_ABSPATH reflects the root of the repository, so there is
+         no parents to inherit from. */
+      *iprops = apr_array_make(result_pool, 0,
+                               sizeof(svn_prop_inherited_item_t *));
+    }
+  else
+    {
+      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_IPROPS));
+      SVN_ERR(op_depth_of(&op_depth, wcroot, local_relpath));
+      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
+      SVN_ERR(svn_sqlite__step(&have_row, stmt));
+
+      if (!have_row)
+        {
+          /* No cached iprops. */
+          *iprops = NULL;
+        }
+      else
+        {
+          SVN_ERR(svn_sqlite__column_iprops(iprops, stmt, 0, result_pool,
+                                            scratch_pool));
+         }
+
+      SVN_ERR(svn_sqlite__reset(stmt));
+    }
+
+  return SVN_NO_ERROR;
+}
+
+/* Recursive body of svn_wc__db_get_children_with_cached_iprops. */
+static svn_error_t *
+get_children_with_cached_iprops(apr_hash_t *iprop_paths,
+                                svn_depth_t depth,
+                                const char *local_abspath,
+                                svn_wc__db_t *db,
+                                apr_pool_t *result_pool,
+                                apr_pool_t *scratch_pool)
+{
+  svn_wc__db_wcroot_t *wcroot;
+  const char *local_relpath;
+  svn_sqlite__stmt_t *stmt;
+  svn_boolean_t have_row;
+
+  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);
+  if (depth == svn_depth_empty
+      || depth == svn_depth_files
+      || depth == svn_depth_immediates)
+    {
+      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
+                                        STMT_SELECT_INODES));  
+    }
+  else /* Default to svn_depth_infinity. */
+    {
+      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
+                                        STMT_SELECT_INODES_RECURSIVE));
+    }
+
+  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
+  SVN_ERR(svn_sqlite__step(&have_row, stmt));
+
+  while (have_row)
+    {
+      const char *relpath_with_cache = svn_sqlite__column_text(stmt, 0,
+                                                               NULL);
+      const char *abspath_with_cache = svn_dirent_join(wcroot->abspath,
+                                                       relpath_with_cache,
+                                                       result_pool);
+      apr_hash_set(iprop_paths, abspath_with_cache, APR_HASH_KEY_STRING,
+                   abspath_with_cache);
+      SVN_ERR(svn_sqlite__step(&have_row, stmt));
+    }
+
+  SVN_ERR(svn_sqlite__reset(stmt));
+
+  if (depth == svn_depth_files || depth == svn_depth_immediates)
+    {
+      const apr_array_header_t *rel_children;
+      int i;
+      apr_pool_t *iterpool = svn_pool_create(scratch_pool);
+
+      SVN_ERR(svn_wc__db_read_children_of_working_node(&rel_children,
+                                                       db, local_abspath,
+                                                       scratch_pool,
+                                                       scratch_pool));
+      for (i = 0; i < rel_children->nelts; i++)
+        {
+          const char *child_abspath;
+
+          svn_pool_clear(iterpool);
+          child_abspath = svn_dirent_join(
+            local_abspath, APR_ARRAY_IDX(rel_children, i, const char *),
+            iterpool);
+
+          if (depth == svn_depth_files)
+            {
+              svn_kind_t child_kind;
+
+              SVN_ERR(svn_wc__db_read_kind(&child_kind, db, child_abspath,
+                                           FALSE, FALSE, iterpool));
+              if (child_kind != svn_kind_file)
+                continue;
+            }
+
+          SVN_ERR(get_children_with_cached_iprops(iprop_paths,
+                                                  svn_depth_empty,
+                                                  child_abspath, db,
+                                                  result_pool,
+                                                  iterpool));
+        }
+
+      svn_pool_destroy(iterpool);
+    }
+
+  return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_wc__db_get_children_with_cached_iprops(apr_hash_t **iprop_paths,
+                                           svn_depth_t depth,
+                                           const char *local_abspath,
+                                           svn_wc__db_t *db,
+                                           apr_pool_t *result_pool,
+                                           apr_pool_t *scratch_pool)
+{
+  *iprop_paths = apr_hash_make(result_pool);
+  SVN_ERR(get_children_with_cached_iprops(*iprop_paths, depth,
+                                          local_abspath, db, result_pool,
+                                          scratch_pool));
+  return SVN_NO_ERROR;
+}
 
 svn_error_t *
 svn_wc__db_read_children_of_working_node(const apr_array_header_t **children,
@@ -10153,19 +10337,22 @@ svn_wc__db_global_update(svn_wc__db_t *d
 #endif
 }
 
-/* Sets a base nodes revision and/or repository relative path. If
-   LOCAL_ABSPATH's rev (REV) is valid, set is revision and if SET_REPOS_RELPATH
-   is TRUE set its repository relative path to REPOS_RELPATH (and make sure its
-   REPOS_ID is still valid).
+/* Sets a base nodes revision, repository relative path, and/or inherited
+   propertis. If LOCAL_ABSPATH's rev (REV) is valid, set its revision.  If
+   SET_REPOS_RELPATH is TRUE set its repository relative path to REPOS_RELPATH
+   (and make sure its REPOS_ID is still valid).  If IPROPS is not NULL set its
+   inherited properties to IPROPS, if IPROPS is NULL then clear any the iprops
+   cache for the base node.
  */
 static svn_error_t *
-db_op_set_rev_and_repos_relpath(svn_wc__db_wcroot_t *wcroot,
-                                const char *local_relpath,
-                                svn_revnum_t rev,
-                                svn_boolean_t set_repos_relpath,
-                                const char *repos_relpath,
-                                apr_int64_t repos_id,
-                                apr_pool_t *scratch_pool)
+db_op_set_rev_repos_relpath_iprops(svn_wc__db_wcroot_t *wcroot,
+                                   const char *local_relpath,
+                                   apr_array_header_t *iprops,
+                                   svn_revnum_t rev,
+                                   svn_boolean_t set_repos_relpath,
+                                   const char *repos_relpath,
+                                   apr_int64_t repos_id,
+                                   apr_pool_t *scratch_pool)
 {
   svn_sqlite__stmt_t *stmt;
 
@@ -10197,6 +10384,15 @@ db_op_set_rev_and_repos_relpath(svn_wc__
       SVN_ERR(svn_sqlite__step_done(stmt));
     }
 
+  /* Set or clear iprops. */
+  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
+                                    STMT_UPDATE_IPROP));
+  SVN_ERR(svn_sqlite__bindf(stmt, "is",
+                            wcroot->wc_id,
+                            local_relpath));
+  SVN_ERR(svn_sqlite__bind_iprops(stmt, 3, iprops, scratch_pool));
+  SVN_ERR(svn_sqlite__step_done(stmt));
+
   return SVN_NO_ERROR;
 }
 
@@ -10204,7 +10400,13 @@ db_op_set_rev_and_repos_relpath(svn_wc__
  *
  * Tweak the information for LOCAL_RELPATH in WCROOT.  If NEW_REPOS_RELPATH is
  * non-NULL update the entry to the new url specified by NEW_REPOS_RELPATH,
- * NEW_REPOS_ID..  If NEW_REV is valid, make this the node's working revision.
+ * NEW_REPOS_ID.  If NEW_REV is valid, make this the node's working revision.
+ *
+ * If WCROOT_IPROPS is not NULL it is a hash mapping const char * absolute
+ * working copy paths to depth-first ordered arrays of
+ * svn_prop_inherited_item_t * structures.  If the absolute path equivalent
+ * of LOCAL_RELPATH exists in WCROOT_IPROPS, then set the hashed value as the
+ * node's inherited properties.
  *
  * Unless S_ROOT is TRUE the tweaks might cause the node for LOCAL_ABSPATH to
  * be removed from the WC; if IS_ROOT is TRUE this will not happen.
@@ -10217,6 +10419,7 @@ bump_node_revision(svn_wc__db_wcroot_t *
                    svn_revnum_t new_rev,
                    svn_depth_t depth,
                    apr_hash_t *exclude_relpaths,
+                   apr_hash_t *wcroot_iprops,
                    svn_boolean_t is_root,
                    svn_boolean_t skip_when_dir,
                    svn_wc__db_t *db,
@@ -10233,6 +10436,7 @@ bump_node_revision(svn_wc__db_wcroot_t *
   svn_boolean_t set_repos_relpath = FALSE;
   svn_boolean_t update_root;
   svn_depth_t depth_below_here = depth;
+  apr_array_header_t *iprops = NULL;
 
   /* Skip an excluded path and its descendants. */
   if (apr_hash_get(exclude_relpaths, local_relpath, APR_HASH_KEY_STRING))
@@ -10278,14 +10482,23 @@ bump_node_revision(svn_wc__db_wcroot_t *
   if (new_repos_relpath != NULL && strcmp(repos_relpath, new_repos_relpath))
     set_repos_relpath = TRUE;
 
-  if (set_repos_relpath
+  if (wcroot_iprops)
+    iprops = apr_hash_get(wcroot_iprops,
+                          svn_dirent_join(wcroot->abspath, local_relpath,
+                                          scratch_pool),
+                          APR_HASH_KEY_STRING);
+
+  if (iprops
+      || set_repos_relpath
       || (SVN_IS_VALID_REVNUM(new_rev) && new_rev != revision))
-    SVN_ERR(db_op_set_rev_and_repos_relpath(wcroot, local_relpath,
-                                            new_rev,
-                                            set_repos_relpath,
-                                            new_repos_relpath,
-                                            new_repos_id,
-                                            scratch_pool));
+    {
+      SVN_ERR(db_op_set_rev_repos_relpath_iprops(wcroot, local_relpath,
+                                                 iprops, new_rev,
+                                                 set_repos_relpath,
+                                                 new_repos_relpath,
+                                                 new_repos_id,
+                                                 scratch_pool));
+    }
 
   /* Early out */
   if (depth <= svn_depth_empty
@@ -10325,7 +10538,8 @@ bump_node_revision(svn_wc__db_wcroot_t *
       SVN_ERR(bump_node_revision(wcroot, child_local_relpath, new_repos_id,
                                  child_repos_relpath, new_rev,
                                  depth_below_here,
-                                 exclude_relpaths, FALSE /* is_root */,
+                                 exclude_relpaths, wcroot_iprops,
+                                 FALSE /* is_root */,
                                  (depth < svn_depth_immediates), db,
                                  iterpool));
     }
@@ -10345,6 +10559,7 @@ struct bump_revisions_baton_t
   const char *new_repos_uuid;
   svn_revnum_t new_revision;
   apr_hash_t *exclude_relpaths;
+  apr_hash_t *wcroot_iprops;
   svn_wc__db_t *db;
 };
 
@@ -10391,6 +10606,7 @@ bump_revisions_post_update(void *baton,
   SVN_ERR(bump_node_revision(wcroot, local_relpath, new_repos_id,
                              brb->new_repos_relpath, brb->new_revision,
                              brb->depth, brb->exclude_relpaths,
+                             brb->wcroot_iprops,
                              TRUE /* is_root */, FALSE, brb->db,
                              scratch_pool));
 
@@ -10406,6 +10622,7 @@ svn_wc__db_op_bump_revisions_post_update
                                          const char *new_repos_uuid,
                                          svn_revnum_t new_revision,
                                          apr_hash_t *exclude_relpaths,
+                                         apr_hash_t *wcroot_iprops,
                                          apr_pool_t *scratch_pool)
 {
   const char *local_relpath;
@@ -10429,6 +10646,7 @@ svn_wc__db_op_bump_revisions_post_update
   brb.new_repos_uuid = new_repos_uuid;
   brb.new_revision = new_revision;
   brb.exclude_relpaths = exclude_relpaths;
+  brb.wcroot_iprops = wcroot_iprops;
   brb.db = db;
 
   SVN_ERR(svn_wc__db_with_txn(wcroot, local_relpath,