All pastes #734522 Raw Edit

Untitled

public text v1 · immutable
·published 2007-10-12 20:01 UTC
rendered paste body
Index: subversion/libsvn_ra/ra_loader.c
===================================================================
--- subversion/libsvn_ra/ra_loader.c	(revision 27149)
+++ subversion/libsvn_ra/ra_loader.c	(working copy)
@@ -1047,6 +1047,19 @@
                                  text_deltas, editor, edit_baton, pool);
 }
 
+svn_error_t *svn_ra_has_capability(svn_ra_session_t *session,
+                                   svn_boolean_t *has,
+                                   const char *capability,
+                                   apr_pool_t *pool)
+{
+  /* fooo: all RA layers should implement this */
+  if (session->vtable->has_capability)
+    return session->vtable->has_capability(session, has, capability, pool);
+
+  *has = FALSE;
+  return SVN_NO_ERROR;
+}
+
 
 
 svn_error_t *
Index: subversion/libsvn_ra/ra_loader.h
===================================================================
--- subversion/libsvn_ra/ra_loader.h	(revision 27149)
+++ subversion/libsvn_ra/ra_loader.h	(working copy)
@@ -231,6 +231,10 @@
                          const svn_delta_editor_t *editor,
                          void *edit_baton,
                          apr_pool_t *pool);
+  svn_error_t *(*has_capability)(svn_ra_session_t *session,
+                                 svn_boolean_t *has,
+                                 const char *capability,
+                                 apr_pool_t *pool);
 } svn_ra__vtable_t;
 
 /* The RA session object. */
Index: subversion/include/svn_error_codes.h
===================================================================
--- subversion/include/svn_error_codes.h	(revision 27149)
+++ subversion/include/svn_error_codes.h	(working copy)
@@ -715,6 +715,10 @@
              SVN_ERR_RA_CATEGORY_START + 7,
              "Path is not locked")
 
+  /** @since New in 1.5. */
+  SVN_ERRDEF(SVN_ERR_RA_UNKNOWN_CAPABILITY,
+             SVN_ERR_RA_CATEGORY_START + 8,
+             "Inquiry about unknown capability")
 
   /* ra_dav errors */
 
Index: subversion/include/svn_dav.h
===================================================================
--- subversion/include/svn_dav.h	(revision 27149)
+++ subversion/include/svn_dav.h	(working copy)
@@ -149,6 +149,14 @@
  */
 #define SVN_DAV_PROP_NS_DAV "http://subversion.tigris.org/xmlns/dav/"
 
+/* Presence of this in the DAV header response to an OPTIONS request
+   indicates that the server supports @c svn_depth_t.
+
+   ### TODO: this doesn't really have anything to do with properties,
+   ### but we should re-use the SVN_DAV_PROP_NS_DAV, right?  We could
+   ### change the name of SVN_DAV_PROP_NS_DAV_SVN_DEPTH, though. */
+#define SVN_DAV_PROP_NS_DAV_SVN_DEPTH SVN_DAV_PROP_NS_DAV "svn/depth"
+
 /** @} */
 
 #ifdef __cplusplus
Index: subversion/include/svn_ra_svn.h
===================================================================
--- subversion/include/svn_ra_svn.h	(revision 27149)
+++ subversion/include/svn_ra_svn.h	(working copy)
@@ -45,6 +45,7 @@
 #define SVN_RA_SVN_CAP_ABSENT_ENTRIES "absent-entries"
 #define SVN_RA_SVN_CAP_COMMIT_REVPROPS "commit-revprops"
 #define SVN_RA_SVN_CAP_MERGEINFO "mergeinfo"
+#define SVN_RA_SVN_CAP_DEPTH "depth"  /* maps to SVN_RA_CAPABILITY_DEPTH */
 
 /** ra_svn passes @c svn_dirent_t fields over the wire as a list of
  * words, these are the values used to represent each field.
Index: subversion/include/svn_wc.h
===================================================================
--- subversion/include/svn_wc.h	(revision 27149)
+++ subversion/include/svn_wc.h	(working copy)
@@ -2962,6 +2962,12 @@
  * course.  If @a depth is @c svn_depth_unknown, then just use
  * @c svn_depth_infinity, which in practice means depth of @a path.
  *
+ * Iff @a depth_compatibility_trick is TRUE, then set the @c start_empty
+ * flag on @a reporter->set_path() and @a reporter->link_path() calls
+ * as necessary to trick a pre-1.5 (i.e., depth-unaware) server into
+ * sending back all the items the client might need to upgrade a
+ * working copy from a shallower depth to a deeper one.
+ *
  * If @a restore_files is true, then unexpectedly missing working files
  * will be restored from the administrative directory's cache. For each
  * file restored, the @a notify_func function will be called with the
@@ -2983,6 +2989,7 @@
                         void *report_baton,
                         svn_boolean_t restore_files,
                         svn_depth_t depth,
+                        svn_boolean_t depth_compatibility_trick,
                         svn_boolean_t use_commit_times,
                         svn_wc_notify_func2_t notify_func,
                         void *notify_baton,
Index: subversion/include/svn_ra.h
===================================================================
--- subversion/include/svn_ra.h	(revision 27149)
+++ subversion/include/svn_ra.h	(working copy)
@@ -1511,6 +1511,36 @@
                            apr_pool_t *pool);
 
 /**
+ * Set @a *has to true if the server represented by @a session has
+ * capability @a capability (one of the capabilities beginning with
+ * @c "SVN_RA_CAPABILITY_"), else set @a *has to false.
+ *
+ * Use @a pool for all allocation.
+ *
+ * @since New in 1.5.
+ */
+svn_error_t *svn_ra_has_capability(svn_ra_session_t *session,
+                                   svn_boolean_t *has,
+                                   const char *capability,
+                                   apr_pool_t *pool);
+
+/**
+ * The capability of understanding @c svn_depth_t (e.g., the server
+ * understands what the client means when the client describes the
+ * depth of a working copy to the server.)
+ *
+ * @since New in 1.5.
+ */
+#define SVN_RA_CAPABILITY_DEPTH "depth"
+
+/*       *** PLEASE READ THIS IF YOU ADD A NEW CAPABILITY ***
+ * RA layers generally fetch all capabilities when asked about any
+ * capability, to save future round trips.  So if you add a new
+ * capability here, make sure to update the RA layers to remember
+ * it after any capabilities query.
+ */
+
+/**
  * Append a textual list of all available RA modules to the stringbuf
  * @a output.
  *
Index: subversion/libsvn_wc/adm_crawler.c
===================================================================
--- subversion/libsvn_wc/adm_crawler.c	(revision 27149)
+++ subversion/libsvn_wc/adm_crawler.c	(working copy)
@@ -157,6 +157,9 @@
    comes back from the server will be different for svn_depth_unknown
    than for svn_depth_infinity.)
 
+   DEPTH_COMPATIBILITY_TRICK means the same thing here as it does
+   in svn_wc_crawl_revisions3().
+
    If TRAVERSAL_INFO is non-null, record this directory's
    value of svn:externals in both TRAVERSAL_INFO->externals_old and
    TRAVERSAL_INFO->externals_new, using wc_path + dir_path as the key,
@@ -182,6 +185,7 @@
                             void *notify_baton,
                             svn_boolean_t restore_files,
                             svn_depth_t depth,
+                            svn_boolean_t depth_compatibility_trick,
                             svn_boolean_t report_everything,
                             svn_boolean_t use_commit_times,
                             svn_wc_traversal_info_t *traversal_info,
@@ -368,6 +372,7 @@
         {
           svn_wc_adm_access_t *subdir_access;
           const svn_wc_entry_t *subdir_entry;
+          svn_boolean_t start_empty;
 
           /* If a directory is missing from disk, we have no way to
              recreate it locally, so report as missing and move
@@ -390,6 +395,14 @@
           SVN_ERR(svn_wc_entry(&subdir_entry, this_full_path, subdir_access,
                                TRUE, iterpool));
 
+          start_empty = subdir_entry->incomplete;
+          if (depth_compatibility_trick
+              && subdir_entry->depth <= svn_depth_files
+              && depth > subdir_entry->depth)
+            {
+              start_empty = TRUE;
+            }
+
           if (report_everything)
             {
               /* Report the dir unconditionally, one way or another. */
@@ -398,14 +411,14 @@
                                             subdir_entry->url,
                                             subdir_entry->revision,
                                             subdir_entry->depth,
-                                            subdir_entry->incomplete,
+                                            start_empty,
                                             subdir_entry->lock_token,
                                             iterpool));
               else
                 SVN_ERR(reporter->set_path(report_baton, this_path,
                                            subdir_entry->revision,
                                            subdir_entry->depth,
-                                           subdir_entry->incomplete,
+                                           start_empty,
                                            subdir_entry->lock_token,
                                            iterpool));
             }
@@ -417,7 +430,7 @@
                                         subdir_entry->url,
                                         subdir_entry->revision,
                                         subdir_entry->depth,
-                                        subdir_entry->incomplete,
+                                        start_empty,
                                         subdir_entry->lock_token,
                                         iterpool));
           /* ... or perhaps just a differing revision, lock token, incomplete
@@ -435,7 +448,7 @@
                                        this_path,
                                        subdir_entry->revision,
                                        subdir_entry->depth,
-                                       subdir_entry->incomplete,
+                                       start_empty,
                                        subdir_entry->lock_token,
                                        iterpool));
 
@@ -445,7 +458,8 @@
                                                 reporter, report_baton,
                                                 notify_func, notify_baton,
                                                 restore_files, depth,
-                                                subdir_entry->incomplete,
+                                                depth_compatibility_trick,
+                                                start_empty,
                                                 use_commit_times,
                                                 traversal_info,
                                                 iterpool));
@@ -470,6 +484,7 @@
                         void *report_baton,
                         svn_boolean_t restore_files,
                         svn_depth_t depth,
+                        svn_boolean_t depth_compatibility_trick,
                         svn_boolean_t use_commit_times,
                         svn_wc_notify_func2_t notify_func,
                         void *notify_baton,
@@ -482,6 +497,7 @@
   svn_boolean_t missing = FALSE;
   const svn_wc_entry_t *parent_entry = NULL;
   svn_wc_notify_t *notify;
+  svn_boolean_t start_empty;
 
   /* The first thing we do is get the base_rev from the working copy's
      ROOT_DIRECTORY.  This is the first revnum that entries will be
@@ -518,6 +534,15 @@
     }
 
   base_rev = entry->revision;
+
+  start_empty = entry->incomplete;
+  if (depth_compatibility_trick
+      && entry->depth <= svn_depth_immediates
+      && depth > entry->depth)
+    {
+      start_empty = TRUE;
+    }
+
   if (base_rev == SVN_INVALID_REVNUM)
     {
       const char *dirname = svn_path_dirname(path, pool);
@@ -530,8 +555,7 @@
      top-level directory being updated is at BASE_REV.  Its PATH
      argument is ignored. */
   SVN_ERR(reporter->set_path(report_baton, "", base_rev, entry->depth,
-                             entry->incomplete , /* start_empty ? */
-                             NULL, pool));
+                             start_empty, NULL, pool));
 
   if (entry->schedule != svn_wc_schedule_delete)
     {
@@ -566,7 +590,8 @@
                                             reporter, report_baton,
                                             notify_func, notify_baton,
                                             restore_files, depth,
-                                            entry->incomplete,
+                                            depth_compatibility_trick,
+                                            start_empty,
                                             use_commit_times,
                                             traversal_info,
                                             pool);
@@ -753,6 +778,7 @@
                                  &wrap_3to2_reporter, &wrb,
                                  restore_files,
                                  SVN_DEPTH_INFINITY_OR_FILES(recurse),
+                                 FALSE,
                                  use_commit_times,
                                  notify_func,
                                  notify_baton,
Index: subversion/libsvn_ra_local/ra_plugin.c
===================================================================
--- subversion/libsvn_ra_local/ra_plugin.c	(revision 27149)
+++ subversion/libsvn_ra_local/ra_plugin.c	(working copy)
@@ -1415,6 +1415,26 @@
 }
 
 
+static svn_error_t *
+svn_ra_local__has_capability(svn_ra_session_t *session,
+                             svn_boolean_t *has,
+                             const char *capability,
+                             apr_pool_t *pool)
+{
+  if (strcmp(capability, SVN_RA_CAPABILITY_DEPTH) == 0)
+    {
+      *has = TRUE;
+    }
+  else  /* Don't know any other capabilities yet, so error. */
+    {
+        return svn_error_createf
+          (SVN_ERR_RA_UNKNOWN_CAPABILITY, NULL,
+           _("Don't know anything about capability '%s'"), capability);
+    }
+
+  return SVN_NO_ERROR;
+}
+
 /*----------------------------------------------------------------*/
 
 static const svn_version_t *
@@ -1459,6 +1479,7 @@
   svn_ra_local__get_lock,
   svn_ra_local__get_locks,
   svn_ra_local__replay,
+  svn_ra_local__has_capability
 };
 
 
Index: subversion/libsvn_client/switch.c
===================================================================
--- subversion/libsvn_client/switch.c	(revision 27149)
+++ subversion/libsvn_client/switch.c	(working copy)
@@ -83,6 +83,7 @@
   svn_wc_traversal_info_t *traversal_info = svn_wc_init_traversal_info(pool);
   const char *preserved_exts_str;
   apr_array_header_t *preserved_exts;
+  svn_boolean_t server_supports_depth;
   svn_config_t *cfg = ctx->config ? apr_hash_get(ctx->config,
                                                  SVN_CONFIG_CATEGORY_CONFIG,
                                                  APR_HASH_KEY_STRING)
@@ -181,6 +182,9 @@
                             target, depth, switch_url,
                             switch_editor, switch_edit_baton, pool));
 
+  SVN_ERR(svn_ra_has_capability(ra_session, &server_supports_depth,
+                                SVN_RA_CAPABILITY_DEPTH, pool));
+
   /* Drive the reporter structure, describing the revisions within
      PATH.  When we call reporter->finish_report, the update_editor
      will be driven by svn_repos_dir_delta2.
@@ -189,7 +193,8 @@
      update, and therefore we don't want to handle any externals
      except the ones directly affected by the switch. */
   err = svn_wc_crawl_revisions3(path, dir_access, reporter, report_baton,
-                                TRUE, depth, use_commit_times,
+                                TRUE, depth, (! server_supports_depth),
+                                use_commit_times,
                                 ctx->notify_func2, ctx->notify_baton2,
                                 NULL, /* no traversal info */
                                 pool);
Index: subversion/libsvn_client/status.c
===================================================================
--- subversion/libsvn_client/status.c	(revision 27149)
+++ subversion/libsvn_client/status.c	(working copy)
@@ -277,6 +277,7 @@
       svn_ra_session_t *ra_session;
       const char *URL;
       svn_node_kind_t kind;
+      svn_boolean_t server_supports_depth;
 
       /* Get full URL from the ANCHOR. */
       if (! entry)
@@ -343,14 +344,17 @@
           rb.ctx = ctx;
           rb.pool = pool;
 
+          SVN_ERR(svn_ra_has_capability(ra_session, &server_supports_depth,
+                                        SVN_RA_CAPABILITY_DEPTH, pool));
+
           /* Drive the reporter structure, describing the revisions
              within PATH.  When we call reporter->finish_report,
              EDITOR will be driven to describe differences between our
              working copy and HEAD. */
           SVN_ERR(svn_wc_crawl_revisions3(path, target_access,
                                           &lock_fetch_reporter, &rb, FALSE,
-                                          depth, FALSE, NULL, NULL, NULL,
-                                          pool));
+                                          depth, (! server_supports_depth),
+                                          FALSE, NULL, NULL, NULL, pool));
         }
     }
   else
Index: subversion/libsvn_client/diff.c
===================================================================
--- subversion/libsvn_client/diff.c	(revision 27149)
+++ subversion/libsvn_client/diff.c	(working copy)
@@ -1206,6 +1206,7 @@
   void *diff_edit_baton;
   svn_boolean_t rev2_is_base = (revision2->kind == svn_opt_revision_base);
   int levels_to_lock = levels_to_lock_from_depth(depth);
+  svn_boolean_t server_supports_depth;
 
   /* Assert that we have valid input. */
   assert(! svn_path_is_url(path2));
@@ -1291,12 +1292,15 @@
                           url1,
                           diff_editor, diff_edit_baton, pool));
 
+  SVN_ERR(svn_ra_has_capability(ra_session, &server_supports_depth,
+                                SVN_RA_CAPABILITY_DEPTH, pool));
+
   /* Create a txn mirror of path2;  the diff editor will print
      diffs in reverse.  :-)  */
   SVN_ERR(svn_wc_crawl_revisions3(path2, dir_access,
                                   reporter, report_baton,
-                                  FALSE, depth, FALSE,
-                                  NULL, NULL, /* notification is N/A */
+                                  FALSE, depth, (! server_supports_depth),
+                                  FALSE, NULL, NULL, /* notification is N/A */
                                   NULL, pool));
 
   SVN_ERR(svn_wc_adm_close(adm_access));
Index: subversion/libsvn_client/update.c
===================================================================
--- subversion/libsvn_client/update.c	(revision 27149)
+++ subversion/libsvn_client/update.c	(working copy)
@@ -113,6 +113,7 @@
   const char *preserved_exts_str;
   apr_array_header_t *preserved_exts;
   struct ff_baton *ffb;
+  svn_boolean_t server_supports_depth;
   svn_config_t *cfg = ctx->config ? apr_hash_get(ctx->config,
                                                  SVN_CONFIG_CATEGORY_CONFIG,
                                                  APR_HASH_KEY_STRING) : NULL;
@@ -230,11 +231,15 @@
                             TRUE, /* send copyfrom args, please */
                             update_editor, update_edit_baton, pool));
 
+  SVN_ERR(svn_ra_has_capability(ra_session, &server_supports_depth,
+                                SVN_RA_CAPABILITY_DEPTH, pool));
+
   /* Drive the reporter structure, describing the revisions within
      PATH.  When we call reporter->finish_report, the
      update_editor will be driven by svn_repos_dir_delta2. */
   err = svn_wc_crawl_revisions3(path, dir_access, reporter, report_baton,
-                                TRUE, depth, use_commit_times,
+                                TRUE, depth, (! server_supports_depth),
+                                use_commit_times,
                                 ctx->notify_func2, ctx->notify_baton2,
                                 traversal_info, pool);
 
Index: subversion/libsvn_ra_serf/serf.c
===================================================================
--- subversion/libsvn_ra_serf/serf.c	(revision 27149)
+++ subversion/libsvn_ra_serf/serf.c	(working copy)
@@ -732,6 +732,104 @@
   return SVN_NO_ERROR;
 }
 
+
+/* Baton type for parsing capabilities out of "OPTIONS" response headers. */
+struct capabilities_response_baton
+{
+  /* Signaler for svn_ra_serf__context_run_wait(). */
+  svn_boolean_t done;
+};
+
+
+/* This implements serf_bucket_headers_do_callback_fn_t.
+ * BATON is a 'struct capabilities_response_baton *'.
+ */
+static int
+capabilities_headers_iterator_callback(void *baton,
+                                       const char *key,
+                                       const char *value)
+{
+  printf("KFF key: '%s'\n", key);
+  printf("KFF val: '%s'\n", value);
+  fflush(stdout);
+  return 0;
+}
+
+
+/* This implements serf_response_handler_t.
+ * HANDLER_BATON is a 'struct capabilities_response_baton *'.
+ */
+static apr_status_t
+capabilities_response_handler(serf_request_t *request,
+                              serf_bucket_t *response,
+                              void *handler_baton,
+                              apr_pool_t *pool)
+{
+  struct capabilities_response_baton *crb = handler_baton;
+
+  printf("KFF: in capabilities_response_handler()\n");
+  fflush(stdout);
+  printf("KFF: about to get response headers\n");
+  fflush(stdout);
+  serf_bucket_t *hdrs = serf_bucket_response_get_headers(response);
+  printf("KFF: got response headers\n");
+  fflush(stdout);
+
+  printf("KFF: about to enter iteration\n");
+  fflush(stdout);
+  serf_bucket_headers_do(hdrs, capabilities_headers_iterator_callback, NULL);
+  printf("KFF: done with iteration\n");
+  fflush(stdout);
+  crb->done = TRUE;
+
+  return APR_SUCCESS;
+}
+
+static svn_error_t *
+svn_ra_serf__has_capability(svn_ra_session_t *ra_session,
+                            svn_boolean_t *has,
+                            const char *capability,
+                            apr_pool_t *pool)
+{
+  svn_ra_serf__session_t *serf_sess = ra_session->priv;
+  svn_ra_serf__handler_t *handler;
+  struct capabilities_response_baton crb;
+
+  crb.done = FALSE;
+
+  printf("KFF: in serf, asking about '%s'\n", capability);
+  fflush(stdout);
+
+  handler = apr_pcalloc(pool, sizeof(*handler));
+  handler->method = "OPTIONS";
+  handler->path = "";
+  handler->body_buckets = NULL;
+  handler->response_handler = capabilities_response_handler;
+  handler->response_baton = &crb;
+  handler->session = serf_sess;
+  handler->conn = serf_sess->conns[0];
+
+  printf("KFF: about to create request\n");
+  fflush(stdout);
+  svn_ra_serf__request_create(handler);
+  printf("KFF: created request\n");
+  fflush(stdout);
+
+  printf("KFF: about to run_wait\n");
+  fflush(stdout);
+  SVN_ERR(svn_ra_serf__context_run_wait(&(crb.done), serf_sess, pool));
+  printf("KFF: done running_waiting\n");
+  fflush(stdout);
+
+  printf("KFF: in serf, done asking about '%s'\n", capability);
+  fflush(stdout);
+
+  *has = FALSE;
+
+  return SVN_NO_ERROR;
+}
+
+
 static const svn_ra__vtable_t serf_vtable = {
   ra_serf_version,
   ra_serf_get_description,
@@ -765,6 +863,7 @@
   svn_ra_serf__get_lock,
   svn_ra_serf__get_locks,
   svn_ra_serf__replay,
+  svn_ra_serf__has_capability
 };
 
 svn_error_t *
Index: subversion/libsvn_ra_neon/session.c
===================================================================
--- subversion/libsvn_ra_neon/session.c	(revision 27149)
+++ subversion/libsvn_ra_neon/session.c	(working copy)
@@ -788,6 +788,7 @@
   ras->compression = compression;
   ras->progress_baton = callbacks->progress_baton;
   ras->progress_func = callbacks->progress_func;
+  ras->capabilities = apr_hash_make(session->pool);
   /* save config and server group in the auth parameter hash */
   svn_auth_set_parameter(ras->callbacks->auth_baton,
                          SVN_AUTH_PARAM_CONFIG, cfg);
@@ -962,6 +963,145 @@
 }
 
 
+/* The only two possible values for a capability. */
+static const char *capability_yes = "yes";
+static const char *capability_no = "no";
+
+
+/* Store in RAS the capabilities discovered from REQ's headers.
+   Use POOL for temporary allocation only. */ 
+static void
+parse_capabilities(ne_request *req,
+                   svn_ra_neon__session_t *ras,
+                   apr_pool_t *pool)
+{
+  void *ne_header_cursor = NULL;
+
+  /* Start out assuming all capabilities are unsupported. */
+  apr_hash_set(ras->capabilities, SVN_RA_CAPABILITY_DEPTH,
+               APR_HASH_KEY_STRING, capability_no);
+
+  /* Then find out which ones are supported. */
+  do {
+    const char *header_name;
+    const char *header_value;
+    ne_header_cursor = ne_response_header_iterate(req,
+                                                  ne_header_cursor,
+                                                  &header_name,
+                                                  &header_value);
+    if (ne_header_cursor && strcasecmp(header_name, "dav") == 0)
+      {
+        /* By the time we get the headers, Neon has downcased them and
+           merged them together -- merged in the sense that if a
+           header "foo" appears multiple times, all the values will be
+           concatenated together, with spaces at the splice points.
+           For example, if the server sent:
+
+              DAV: version-control,checkout,working-resource
+              DAV: merge,baseline,activity,version-controlled-collection
+              DAV: http://subversion.tigris.org/xmlns/dav/svn/depth
+
+           Here we might see:
+
+              header_name  == "dav"
+              header_value == "1,2, version-control,checkout,working-resource, merge,baseline,activity,version-controlled-collection, http://subversion.tigris.org/xmlns/dav/svn/depth, <http://apache.org/dav/propset/fs/1>"
+
+           (Deliberately not line-wrapping that, so you can see what
+           we're about to parse.)
+        */
+
+        apr_array_header_t *vals =
+          svn_cstring_split(header_value, ",", TRUE, pool);
+
+        /* Right now the only capability we detect is depth, so just
+           seek for it directly.  This could be rewritten as an
+           iteration with a switch-case inside, or whatever, if we
+           ever need to detect other capabilities, though. */
+
+        if (svn_cstring_match_glob_list(SVN_DAV_PROP_NS_DAV_SVN_DEPTH, vals))
+          apr_hash_set(ras->capabilities, SVN_RA_CAPABILITY_DEPTH,
+                       APR_HASH_KEY_STRING, capability_yes);
+      }
+  } while (ne_header_cursor);
+}
+
+
+/* Fill RAS->capabilities with the server's capabilities.  Use
+   POOL only for temporary allocation. */
+static svn_error_t *
+discover_capabilities (svn_ra_neon__session_t *ras, apr_pool_t *pool)
+{
+  int http_ret_code;
+  svn_ra_neon__request_t *rar;
+  
+  rar = svn_ra_neon__request_create(ras, "OPTIONS", ras->repos_root, pool);
+  SVN_ERR(svn_ra_neon__request_dispatch(&http_ret_code, rar,
+                                        NULL, NULL, 200, 0, pool));
+  
+  if (http_ret_code == 200)
+    {
+      parse_capabilities(rar->ne_req, ras, pool);
+    }
+  else
+    {
+      /* "can't happen", because svn_ra_neon__request_dispatch()
+         itself should have returned error if response code != 200. */
+      return svn_error_createf
+        (SVN_ERR_RA_DAV_OPTIONS_REQ_FAILED, NULL,
+         _("OPTIONS request (for capabilities) got HTTP response code %d"),
+         http_ret_code);
+    }
+
+  return SVN_NO_ERROR;
+}
+
+
+svn_error_t *
+svn_ra_neon__has_capability(svn_ra_session_t *session,
+                            svn_boolean_t *has,
+                            const char *capability,
+                            apr_pool_t *pool)
+{
+  svn_ra_neon__session_t *ras = session->priv;
+  const char *cap_result = apr_hash_get(ras->capabilities,
+                                        SVN_RA_CAPABILITY_DEPTH,
+                                        APR_HASH_KEY_STRING);
+
+  /* If any capability is unknown, they're all unknown, so ask. */
+  if (cap_result == NULL)
+    discover_capabilities(ras, pool);
+
+  /* Try again, now that we've fetched the capabilities. */
+  cap_result = apr_hash_get(ras->capabilities,
+                            SVN_RA_CAPABILITY_DEPTH, APR_HASH_KEY_STRING);
+
+  if (cap_result == capability_yes)
+    {
+      *has = TRUE;
+    }
+  else if (cap_result == capability_no)
+    {
+      *has = FALSE;
+    }
+  else if (cap_result == NULL)
+    {
+      return svn_error_createf
+        (SVN_ERR_RA_UNKNOWN_CAPABILITY, NULL,
+         _("Don't know anything about capability '%s'"), capability);
+    }
+  else  /* "can't happen" */
+    {
+      /* Well, let's hope it's a string. */
+      return svn_error_createf
+        (SVN_ERR_RA_DAV_OPTIONS_REQ_FAILED, NULL,
+         _("attempt to fetch capability '%s' resulted in '%s'"),
+         capability, cap_result);
+    }
+  
+  return SVN_NO_ERROR;
+}
+
+
 static const svn_version_t *
 ra_neon_version(void)
 {
@@ -1001,6 +1141,7 @@
   svn_ra_neon__get_lock,
   svn_ra_neon__get_locks,
   svn_ra_neon__replay,
+  svn_ra_neon__has_capability
 };
 
 svn_error_t *
Index: subversion/libsvn_ra_neon/ra_neon.h
===================================================================
--- subversion/libsvn_ra_neon/ra_neon.h	(revision 27149)
+++ subversion/libsvn_ra_neon/ra_neon.h	(working copy)
@@ -100,6 +100,14 @@
 
   svn_ra_progress_notify_func_t progress_func;
   void *progress_baton;
+
+  /* Maps SVN_RA_CAPABILITY_foo keys to "yes" or "no" values.
+     If a capability is not yet discovered, it is absent from the table.
+     The table itself is allocated in the svn_ra_session_t's pool;
+     keys and values must have at least that lifetime.  Most likely
+     the keys and values are constants anyway (and internal code may
+     just compare against those constants' addresses, therefore). */
+  apr_hash_t *capabilities;
 } svn_ra_neon__session_t;
 
 
@@ -999,6 +1007,15 @@
                     void *edit_baton,
                     apr_pool_t *pool);
 
+/*
+ * Implements the has_capability RA layer function. */
+svn_error_t *
+svn_ra_neon__has_capability(svn_ra_session_t *session,
+                            svn_boolean_t *has,
+                            const char *capability,
+                            apr_pool_t *pool);
+
+
 /* Helper function.  Loop over LOCK_TOKENS and assemble all keys and
    values into a stringbuf allocated in POOL.  The string will be of
    the form
Index: subversion/mod_dav_svn/version.c
===================================================================
--- subversion/mod_dav_svn/version.c	(revision 27149)
+++ subversion/mod_dav_svn/version.c	(working copy)
@@ -133,8 +133,8 @@
                   "version-control,checkout,working-resource");
   apr_text_append(p, phdr,
                   "merge,baseline,activity,version-controlled-collection");
-
   /* ### fork-control? */
+  apr_text_append(p, phdr, SVN_DAV_PROP_NS_DAV_SVN_DEPTH);
 }
 
 
Index: subversion/libsvn_ra_svn/client.c
===================================================================
--- subversion/libsvn_ra_svn/client.c	(revision 27149)
+++ subversion/libsvn_ra_svn/client.c	(working copy)
@@ -2095,6 +2095,31 @@
 }
 
 
+static svn_error_t *ra_svn_has_capability(svn_ra_session_t *session,
+                                          svn_boolean_t *has,
+                                          const char *capability,
+                                          apr_pool_t *pool)
+{
+  svn_ra_svn__session_baton_t *sess = session->priv;
+
+  if (strcmp(capability, SVN_RA_CAPABILITY_DEPTH) == 0)
+    {
+      if (svn_ra_svn_has_capability(sess->conn, SVN_RA_SVN_CAP_DEPTH))
+        *has = TRUE;
+      else
+        *has = FALSE;
+    }
+  else  /* Don't know any other capabilities yet, so error. */
+    {
+        return svn_error_createf
+          (SVN_ERR_RA_UNKNOWN_CAPABILITY, NULL,
+           _("Don't know anything about capability '%s'"), capability);
+    }
+
+  return SVN_NO_ERROR;
+}
+
+
 static const svn_ra__vtable_t ra_svn_vtable = {
   svn_ra_svn_version,
   ra_svn_get_description,
@@ -2128,6 +2153,7 @@
   ra_svn_get_lock,
   ra_svn_get_locks,
   ra_svn_replay,
+  ra_svn_has_capability
 };
 
 svn_error_t *
Index: subversion/libsvn_ra_svn/protocol
===================================================================
--- subversion/libsvn_ra_svn/protocol	(revision 27149)
+++ subversion/libsvn_ra_svn/protocol	(working copy)
@@ -188,6 +188,9 @@
                        See section 3.1.1.
 [S]  mergeinfo         If the server presents this capability, it supports the 
                        get-mergeinfo command.  See section 3.1.1.
+[S]  depth             If the server presents this capability, it understands
+                       requested operational depth (see section 3.1.1) and
+                       per-path ambient depth (see section 3.1.3).
 
 3. Commands
 -----------
Index: subversion/svnserve/serve.c
===================================================================
--- subversion/svnserve/serve.c	(revision 27149)
+++ subversion/svnserve/serve.c	(working copy)
@@ -2450,13 +2450,14 @@
 
   /* Send greeting.  We don't support version 1 any more, so we can
    * send an empty mechlist. */
-  SVN_ERR(svn_ra_svn_write_cmd_response(conn, pool, "nn()(wwwww)",
+  SVN_ERR(svn_ra_svn_write_cmd_response(conn, pool, "nn()(wwwwww)",
                                         (apr_uint64_t) 2, (apr_uint64_t) 2,
                                         SVN_RA_SVN_CAP_EDIT_PIPELINE,
                                         SVN_RA_SVN_CAP_SVNDIFF1,
                                         SVN_RA_SVN_CAP_ABSENT_ENTRIES,
                                         SVN_RA_SVN_CAP_COMMIT_REVPROPS,
-                                        SVN_RA_SVN_CAP_MERGEINFO));
+                                        SVN_RA_SVN_CAP_MERGEINFO,
+                                        SVN_RA_SVN_CAP_DEPTH));
 
   /* Read client response, which we assume to be in version 2 format:
    * version, capability list, and client URL; then we do an auth