Finish issue #2991: client should announce capabilities to server, and
server should pass them along to start-commit hook. This would allow
a server to reject commits from clients that don't don't know how to
keep mergeinfo up to date, for example.
* subversion/libsvn_repos/repos.h
(struct svn_repos_t): Add new capabilities field.
(svn_repos__hooks_start_commit): Take new capabilities parameter.
* subversion/svnserve/serve.c: Include svn_repos_private.h.
(find_repos): Take new capabilities parameter, use it to set a
capabilities list in the new repos object.
(serve): Pass the capabilities to find_repos.
* subversion/libsvn_repos/fs-wrap.c
(svn_repos_fs_begin_txn_for_commit2): Pass capabilities along to
svn_repos__hooks_start_commit.
* subversion/libsvn_repos/hooks.c
(svn_repos__hooks_start_commit): Take new capabilities parameter,
convert the list to a string, and pass along to the hook program.
* subversion/include/private/svn_repos_private.h: New file.
(svn_repos__capabilities_as_list): New declaration.
(svn_repos__set_capabilities): New declaration.
* subversion/libsvn_repos/repos.c: Include svn_ra.h, svn_repos_private.h.
(svn_repos__capabilities_as_list): Define new function.
(svn_repos__set_capabilities): Define new function.
(create_hooks): Document the new capabilities list passed to the
start-commit hook.
* subversion/mod_dav_svn/repos.c: Include svn_repos_private.h.
(get_resource): Handle new SVN_RA_CAPABILITY_LOG_REVPROPS; store
all capabilities in repos.
* subversion/libsvn_ra_local/split_url.c: Include svn_repos_private.h.
(svn_ra_local__split_URL): Set capabilities in new repos object.
* subversion/libsvn_ra_serf/util.c
(svn_ra_serf__setup_serf_req): Send new SVN_RA_CAPABILITY_LOG_REVPROPS.
* subversion/include/svn_dav.h
(SVN_DAV_PROP_NS_DAV_SVN_LOG_REVPROPS): Move definition, fix formatting.
* subversion/libsvn_ra_serf/propfind_buckets.c
(become_request): Send SVN_DAV_PROP_NS_DAV_SVN_LOG_REVPROPS.
* subversion/libsvn_ra_neon/util.c
(svn_ra_neon__request_dispatch): Send SVN_DAV_PROP_NS_DAV_SVN_LOG_REVPROPS.
* subversion/libsvn_ra_svn/client.c
(open_session): Count args correctly; send SVN_RA_SVN_CAP_LOG_REVPROPS.
(ra_svn_has_capability): Rewrite for compactness.
* subversion/include/svn_ra.h
(svn_ra_has_capability): Document error behavior.
* subversion/libsvn_ra_serf/serf.c
(capabilities_headers_iterator_callback): Formatting fix for
consistency with the rest of the capabilities code.
* subversion/include/svn_ra_svn.h
(SVN_RA_SVN_CAP_LOG_REVPROPS): Fix comment formatting, for consistency.
* subversion/tests/cmdline/commit_tests.py
(start_commit_detect_capabilities): New test.
(test_list): Run it.
git-svn-id: https://svn.apache.org/repos/asf/subversion/branches/issue-2991-dev@867687 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/subversion/include/private/svn_repos_private.h b/subversion/include/private/svn_repos_private.h
new file mode 100644
index 0000000..342428a
--- /dev/null
+++ b/subversion/include/private/svn_repos_private.h
@@ -0,0 +1,46 @@
+/*
+ * svn_repos_private.h: Private declarations for repos functionality
+ * used internally by non-libsvn_repos* modules.
+ *
+ * ====================================================================
+ * Copyright (c) 2007 CollabNet. All rights reserved.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://subversion.tigris.org/license-1.html.
+ * If newer versions of this license are posted there, you may use a
+ * newer version instead, at your option.
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals. For exact contribution history, see the revision
+ * history and logs, available at http://subversion.tigris.org/.
+ * ====================================================================
+ */
+
+#ifndef SVN_REPOS_PRIVATE_H
+#define SVN_REPOS_PRIVATE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/* Convert @a capabilities, a hash table mapping 'const char *' keys to
+ * "yes" or "no" values, to a list of all keys whose value is "yes".
+ * Return the list, allocated in @a pool, and use @a pool for all
+ * temporary allocation.
+ */
+apr_array_header_t *
+svn_repos__capabilities_as_list(apr_hash_t *capabilities, apr_pool_t *pool);
+
+/* Set the client-reported capabilities of @a repos to @a capabilities,
+ * which must be allocated in memory at least as long-lived as @a repos.
+ */
+void
+svn_repos__set_capabilities(svn_repos_t *repos,
+ apr_array_header_t *capabilities);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* SVN_REPOS_PRIVATE_H */
diff --git a/subversion/include/svn_dav.h b/subversion/include/svn_dav.h
index 259b5ed..a1c60c4 100644
--- a/subversion/include/svn_dav.h
+++ b/subversion/include/svn_dav.h
@@ -156,13 +156,15 @@
### 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"
-#define SVN_DAV_PROP_NS_DAV_SVN_LOG_REVPROPS SVN_DAV_PROP_NS_DAV "svn/log-revprops"
/* Presence of this in the DAV header response to an OPTIONS request
indicates that the server knows how to handle merge-tracking information.
### And see above about appropriateness of properties namespace. ### */
#define SVN_DAV_PROP_NS_DAV_SVN_MERGEINFO SVN_DAV_PROP_NS_DAV "svn/merginfo"
+#define SVN_DAV_PROP_NS_DAV_SVN_LOG_REVPROPS SVN_DAV_PROP_NS_DAV \
+ "svn/log-revprops"
+
/** @} */
#ifdef __cplusplus
diff --git a/subversion/include/svn_ra.h b/subversion/include/svn_ra.h
index add3ab0..62346b7 100644
--- a/subversion/include/svn_ra.h
+++ b/subversion/include/svn_ra.h
@@ -1515,9 +1515,12 @@
/**
* Set @a *has to true if the server represented by @a session has
- * capability @a capability (one of the capabilities beginning with
+ * @a capability (one of the capabilities beginning with
* @c "SVN_RA_CAPABILITY_"), else set @a *has to false.
*
+ * If @a capability isn't recognized, throw @c SVN_ERR_RA_UNKNOWN_CAPABILITY,
+ * with the effect on @a *has undefined.
+ *
* Use @a pool for all allocation.
*
* @since New in 1.5.
diff --git a/subversion/include/svn_ra_svn.h b/subversion/include/svn_ra_svn.h
index f5dc448..99bc7d0 100644
--- a/subversion/include/svn_ra_svn.h
+++ b/subversion/include/svn_ra_svn.h
@@ -48,7 +48,8 @@
#define SVN_RA_SVN_CAP_MERGEINFO "mergeinfo"
/* maps to SVN_RA_CAPABILITY_DEPTH: */
#define SVN_RA_SVN_CAP_DEPTH "depth"
-#define SVN_RA_SVN_CAP_LOG_REVPROPS "log-revprops" /* SVN_RA_CAPABILITY_LOG_REVPROPS */
+/* maps to SVN_RA_CAPABILITY_LOG_REVPROPS */
+#define SVN_RA_SVN_CAP_LOG_REVPROPS "log-revprops"
/** 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.
diff --git a/subversion/libsvn_ra_local/split_url.c b/subversion/libsvn_ra_local/split_url.c
index 066110b..f2df341 100644
--- a/subversion/libsvn_ra_local/split_url.c
+++ b/subversion/libsvn_ra_local/split_url.c
@@ -21,6 +21,7 @@
#include <string.h>
#include "svn_path.h"
#include "svn_private_config.h"
+#include "private/svn_repos_private.h"
svn_error_t *
@@ -133,6 +134,15 @@
(SVN_ERR_RA_LOCAL_REPOS_OPEN_FAILED, err,
_("Unable to open repository '%s'"), URL);
+ /* Assert capabilities directly, since client == server. */
+ {
+ apr_array_header_t *caps = apr_array_make(pool, 3, sizeof(const char *));
+ APR_ARRAY_PUSH(caps, const char *) = SVN_RA_CAPABILITY_DEPTH;
+ APR_ARRAY_PUSH(caps, const char *) = SVN_RA_CAPABILITY_MERGEINFO;
+ APR_ARRAY_PUSH(caps, const char *) = SVN_RA_CAPABILITY_LOG_REVPROPS;
+ svn_repos__set_capabilities(*repos, caps);
+ }
+
/* What remains of URL after being hacked at in the previous step is
REPOS_URL. FS_PATH is what we've hacked off in the process.
Note that path is not encoded and what we gave to svn_root_find_root_path
diff --git a/subversion/libsvn_ra_neon/util.c b/subversion/libsvn_ra_neon/util.c
index 6f93cb3..7ed2ead 100644
--- a/subversion/libsvn_ra_neon/util.c
+++ b/subversion/libsvn_ra_neon/util.c
@@ -1425,6 +1425,8 @@
SVN_DAV_PROP_NS_DAV_SVN_DEPTH);
ne_add_request_header(req->ne_req, "X-SVN-Capabilities",
SVN_DAV_PROP_NS_DAV_SVN_MERGEINFO);
+ ne_add_request_header(req->ne_req, "X-SVN-Capabilities",
+ SVN_DAV_PROP_NS_DAV_SVN_LOG_REVPROPS);
/* add any extra headers passed in by caller. */
if (extra_headers != NULL)
diff --git a/subversion/libsvn_ra_serf/propfind_buckets.c b/subversion/libsvn_ra_serf/propfind_buckets.c
index 3eb4607..0d7c0a6 100644
--- a/subversion/libsvn_ra_serf/propfind_buckets.c
+++ b/subversion/libsvn_ra_serf/propfind_buckets.c
@@ -159,6 +159,8 @@
SVN_DAV_PROP_NS_DAV_SVN_DEPTH);
serf_bucket_headers_setn(hdrs_bkt, "X-SVN-Capabilities",
SVN_DAV_PROP_NS_DAV_SVN_MERGEINFO);
+ serf_bucket_headers_setn(hdrs_bkt, "X-SVN-Capabilities",
+ SVN_DAV_PROP_NS_DAV_SVN_LOG_REVPROPS);
if (ctx->label)
{
diff --git a/subversion/libsvn_ra_serf/serf.c b/subversion/libsvn_ra_serf/serf.c
index f92185b..f4b2c61 100644
--- a/subversion/libsvn_ra_serf/serf.c
+++ b/subversion/libsvn_ra_serf/serf.c
@@ -791,8 +791,10 @@
}
if (svn_cstring_match_glob_list(SVN_DAV_PROP_NS_DAV_SVN_LOG_REVPROPS,
vals))
- apr_hash_set(crb->capabilities, SVN_RA_CAPABILITY_LOG_REVPROPS,
- APR_HASH_KEY_STRING, capability_yes);
+ {
+ apr_hash_set(crb->capabilities, SVN_RA_CAPABILITY_LOG_REVPROPS,
+ APR_HASH_KEY_STRING, capability_yes);
+ }
}
return 0;
diff --git a/subversion/libsvn_ra_serf/util.c b/subversion/libsvn_ra_serf/util.c
index 2a9c36a..5c82385 100644
--- a/subversion/libsvn_ra_serf/util.c
+++ b/subversion/libsvn_ra_serf/util.c
@@ -281,6 +281,8 @@
SVN_DAV_PROP_NS_DAV_SVN_DEPTH);
serf_bucket_headers_setn(hdrs_bkt, "X-SVN-Capabilities",
SVN_DAV_PROP_NS_DAV_SVN_MERGEINFO);
+ serf_bucket_headers_setn(hdrs_bkt, "X-SVN-Capabilities",
+ SVN_DAV_PROP_NS_DAV_SVN_LOG_REVPROPS);
if (conn->session->auth_protocol)
conn->session->auth_protocol->setup_request_func(conn, hdrs_bkt);
diff --git a/subversion/libsvn_ra_svn/client.c b/subversion/libsvn_ra_svn/client.c
index 74370c4..f6475df 100644
--- a/subversion/libsvn_ra_svn/client.c
+++ b/subversion/libsvn_ra_svn/client.c
@@ -568,12 +568,13 @@
/* In protocol version 2, we send back our protocol version, our
* capability list, and the URL, and subsequently there is an auth
* request. */
- SVN_ERR(svn_ra_svn_write_tuple(conn, pool, "n(www)c", (apr_uint64_t) 2,
+ SVN_ERR(svn_ra_svn_write_tuple(conn, pool, "n(wwwwww)c", (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_DEPTH,
SVN_RA_SVN_CAP_MERGEINFO,
+ SVN_RA_SVN_CAP_LOG_REVPROPS,
url));
SVN_ERR(handle_auth_request(sess, pool));
@@ -2158,26 +2159,25 @@
{
svn_ra_svn__session_baton_t *sess = session->priv;
- if (strcmp(capability, SVN_RA_CAPABILITY_DEPTH) == 0)
+ *has = FALSE;
+
+ if (strcmp(capability, SVN_RA_CAPABILITY_DEPTH) == 0
+ && svn_ra_svn_has_capability(sess->conn,
+ SVN_RA_SVN_CAP_DEPTH))
{
- if (svn_ra_svn_has_capability(sess->conn, SVN_RA_SVN_CAP_DEPTH))
- *has = TRUE;
- else
- *has = FALSE;
+ *has = TRUE;
}
- else if (strcmp(capability, SVN_RA_CAPABILITY_MERGEINFO) == 0)
+ else if (strcmp(capability, SVN_RA_CAPABILITY_MERGEINFO) == 0
+ && svn_ra_svn_has_capability(sess->conn,
+ SVN_RA_SVN_CAP_MERGEINFO))
{
- if (svn_ra_svn_has_capability(sess->conn, SVN_RA_SVN_CAP_MERGEINFO))
- *has = TRUE;
- else
- *has = FALSE;
+ *has = TRUE;
}
- else if (strcmp(capability, SVN_RA_CAPABILITY_LOG_REVPROPS) == 0)
+ else if (strcmp(capability, SVN_RA_CAPABILITY_LOG_REVPROPS) == 0
+ && svn_ra_svn_has_capability(sess->conn,
+ SVN_RA_SVN_CAP_LOG_REVPROPS))
{
- if (svn_ra_svn_has_capability(sess->conn, SVN_RA_SVN_CAP_LOG_REVPROPS))
- *has = TRUE;
- else
- *has = FALSE;
+ *has = TRUE;
}
else /* Don't know any other capabilities, so error. */
{
diff --git a/subversion/libsvn_repos/fs-wrap.c b/subversion/libsvn_repos/fs-wrap.c
index 769e6e8..25dadd1 100644
--- a/subversion/libsvn_repos/fs-wrap.c
+++ b/subversion/libsvn_repos/fs-wrap.c
@@ -77,7 +77,7 @@
/* Run start-commit hooks. */
SVN_ERR(svn_repos__hooks_start_commit(repos, author ? author->data : NULL,
- pool));
+ repos->capabilities, pool));
/* Begin the transaction, ask for the fs to do on-the-fly lock checks. */
SVN_ERR(svn_fs_begin_txn2(txn_p, repos->fs, rev,
diff --git a/subversion/libsvn_repos/hooks.c b/subversion/libsvn_repos/hooks.c
index f0aafd3..2e5bea3 100644
--- a/subversion/libsvn_repos/hooks.c
+++ b/subversion/libsvn_repos/hooks.c
@@ -536,6 +536,7 @@
svn_error_t *
svn_repos__hooks_start_commit(svn_repos_t *repos,
const char *user,
+ apr_array_header_t *capabilities,
apr_pool_t *pool)
{
const char *hook = svn_repos_start_commit_hook(repos, pool);
@@ -548,11 +549,15 @@
else if (hook)
{
const char *args[4];
+ char *capabilities_string = svn_cstring_join(capabilities, ",", pool);
+ /* Get rid of that annoying final comma. */
+ capabilities_string[strlen(capabilities_string) - 1] = '\0';
args[0] = hook;
args[1] = svn_path_local_style(svn_repos_path(repos, pool), pool);
args[2] = user ? user : "";
- args[3] = NULL;
+ args[3] = capabilities_string;
+ args[4] = NULL;
SVN_ERR(run_hook_cmd(SVN_REPOS__HOOK_START_COMMIT, hook, args, NULL,
pool));
diff --git a/subversion/libsvn_repos/repos.c b/subversion/libsvn_repos/repos.c
index a6abbad..0014263 100644
--- a/subversion/libsvn_repos/repos.c
+++ b/subversion/libsvn_repos/repos.c
@@ -26,8 +26,10 @@
#include "svn_utf.h"
#include "svn_time.h"
#include "svn_fs.h"
+#include "svn_ra.h" /* for SVN_RA_CAPABILITY_* */
#include "svn_repos.h"
#include "svn_private_config.h" /* for SVN_TEMPLATE_ROOT_DIR */
+#include "private/svn_repos_private.h"
#include "repos.h"
@@ -305,9 +307,27 @@
"#" NL
"# [1] REPOS-PATH (the path to this repository)" NL
"# [2] USER (the authenticated user attempting to commit)" NL
+"# [3] CAPABILITIES (a comma-separated list of capabilities reported" NL
+"# by the client; see note below)" NL
"#" NL
-"# The default working directory for the invocation is undefined, so" NL
-"# the program should set one explicitly if it cares." NL
+"# Note: The CAPABILITIES parameter is new in Subversion 1.5, and 1.5" NL
+"# clients will typically report at least the \"" \
+ SVN_RA_CAPABILITY_MERGEINFO "\" capability." NL
+"# If there are other capabilities, then the list is comma-separated," NL
+"# e.g.: \"" \
+ SVN_RA_CAPABILITY_LOG_REVPROPS "," \
+ SVN_RA_CAPABILITY_MERGEINFO "," \
+ SVN_RA_CAPABILITY_DEPTH \
+"\" (the order is undefined)." NL
+"#" NL
+"# The list is self-reported by the client. Therefore, you should not" NL
+"# make security assumptions based on the capabilities list, nor should" NL
+"# you assume that clients reliably report every capability they have." NL
+"# However, stock Subversion 1.5 (and higher) clients will report" NL
+"# at least the \"" SVN_RA_CAPABILITY_MERGEINFO "\" capability by default." NL
+"#" NL
+"# The working directory for this hook program's invocation is undefined," NL
+"# so the program should set one explicitly if it cares." NL
"#" NL
"# If the hook program exits with success, the commit continues; but" NL
"# if it exits with failure (non-zero), the commit is stopped before" NL
@@ -1680,3 +1700,30 @@
*dirent = ent;
return SVN_NO_ERROR;
}
+
+apr_array_header_t *
+svn_repos__capabilities_as_list(apr_hash_t *capabilities, apr_pool_t *pool)
+{
+ apr_hash_index_t *hi;
+ apr_array_header_t *list = apr_array_make(pool, apr_hash_count(capabilities),
+ sizeof(char *));
+
+ for (hi = apr_hash_first(pool, capabilities); hi; hi = apr_hash_next(hi))
+ {
+ const void *key;
+ void *val;
+ apr_hash_this(hi, &key, NULL, &val);
+ if (strcmp((const char *) val, "yes") == 0)
+ APR_ARRAY_PUSH(list, const char *) = key;
+ }
+
+ return list;
+}
+
+void
+svn_repos__set_capabilities(svn_repos_t *repos,
+ apr_array_header_t *capabilities)
+{
+ repos->capabilities = capabilities;
+}
+
diff --git a/subversion/libsvn_repos/repos.h b/subversion/libsvn_repos/repos.h
index a65fa0e..be62b00 100644
--- a/subversion/libsvn_repos/repos.h
+++ b/subversion/libsvn_repos/repos.h
@@ -117,6 +117,11 @@
/* The FS backend in use within this repository. */
const char *fs_type;
+
+ /* If non-null, a list of all the capabilities the client (on the
+ current connection) has self-reported. Each element is a
+ 'const char *', one of SVN_RA_CAPABILITY_*. */
+ apr_array_header_t *capabilities;
};
@@ -125,10 +130,15 @@
/* Run the start-commit hook for REPOS. Use POOL for any temporary
allocations. If the hook fails, return SVN_ERR_REPOS_HOOK_FAILURE.
- USER is the authenticated name of the user starting the commit. */
+ USER is the authenticated name of the user starting the commit.
+ CAPABILITIES is a list of 'const char *' capability names (using
+ SVN_RA_CAPABILITY_*) that the client has self-reported. Note that
+ there is no guarantee the client is telling the truth: the hook
+ should not make security assumptions based on the capabilities. */
svn_error_t *
svn_repos__hooks_start_commit(svn_repos_t *repos,
const char *user,
+ apr_array_header_t *capabilities,
apr_pool_t *pool);
/* Run the pre-commit hook for REPOS. Use POOL for any temporary
diff --git a/subversion/mod_dav_svn/repos.c b/subversion/mod_dav_svn/repos.c
index e2043ff..ad910c1 100644
--- a/subversion/mod_dav_svn/repos.c
+++ b/subversion/mod_dav_svn/repos.c
@@ -41,6 +41,7 @@
#include "svn_props.h"
#include "mod_dav_svn.h"
#include "svn_ra.h" /* for SVN_RA_CAPABILITY_* */
+#include "private/svn_repos_private.h"
#include "dav_svn.h"
@@ -1629,6 +1630,8 @@
APR_HASH_KEY_STRING, capability_no);
apr_hash_set(repos->capabilities, SVN_RA_CAPABILITY_MERGEINFO,
APR_HASH_KEY_STRING, capability_no);
+ apr_hash_set(repos->capabilities, SVN_RA_CAPABILITY_LOG_REVPROPS,
+ APR_HASH_KEY_STRING, capability_no);
/* Then see what we can find. */
val = apr_table_get(r->headers_in, "X-SVN-Capabilities");
@@ -1649,6 +1652,13 @@
apr_hash_set(repos->capabilities, SVN_RA_CAPABILITY_MERGEINFO,
APR_HASH_KEY_STRING, capability_yes);
}
+ if (svn_cstring_match_glob_list
+ (SVN_DAV_PROP_NS_DAV_SVN_LOG_REVPROPS, vals))
+ {
+ apr_hash_set(repos->capabilities,
+ SVN_RA_CAPABILITY_LOG_REVPROPS,
+ APR_HASH_KEY_STRING, capability_yes);
+ }
}
}
}
@@ -1675,6 +1685,12 @@
/* Cache the open repos for the next request on this connection */
apr_pool_userdata_set(repos->repos, repos_key,
NULL, r->connection->pool);
+
+ /* Store the capabilities of the current connection, making sure
+ to use the same pool repos->repos itself was created in. */
+ svn_repos__set_capabilities
+ (repos->repos, svn_repos__capabilities_as_list(repos->capabilities,
+ r->connection->pool));
}
/* cache the filesystem object */
diff --git a/subversion/svnserve/serve.c b/subversion/svnserve/serve.c
index df2143e..36fcbcb 100644
--- a/subversion/svnserve/serve.c
+++ b/subversion/svnserve/serve.c
@@ -42,6 +42,7 @@
#include "svn_props.h"
#include "svn_mergeinfo.h"
#include "svn_user.h"
+#include "private/svn_repos_private.h"
#include "server.h"
@@ -2404,9 +2405,14 @@
/* Look for the repository given by URL, using ROOT as the virtual
* repository root. If we find one, fill in the repos, fs, cfg,
- * repos_url, and fs_path fields of B. */
+ * repos_url, and fs_path fields of B, and set the capabilities of
+ * B->repos to CAPABILITIES (which must be at least as long-lived
+ * as POOL).
+ */
static svn_error_t *find_repos(const char *url, const char *root,
- server_baton_t *b, apr_pool_t *pool)
+ server_baton_t *b,
+ apr_array_header_t *capabilities,
+ apr_pool_t *pool)
{
const char *path, *full_path, *repos_root;
svn_stringbuf_t *url_buf;
@@ -2442,6 +2448,7 @@
/* Open the repository and fill in b with the resulting information. */
SVN_ERR(svn_repos_open(&b->repos, repos_root, pool));
+ svn_repos__set_capabilities(b->repos, capabilities);
b->fs = svn_repos_fs(b->repos);
b->fs_path = svn_stringbuf_create(full_path + strlen(repos_root),
pool);
@@ -2506,7 +2513,7 @@
svn_error_t *err, *io_err;
apr_uint64_t ver;
const char *uuid, *client_url;
- apr_array_header_t *caplist;
+ apr_array_header_t *caplist, *cap_words;
server_baton_t b;
b.tunnel = params->tunnel;
@@ -2547,7 +2554,26 @@
if (! svn_ra_svn_has_capability(conn, SVN_RA_SVN_CAP_EDIT_PIPELINE))
return SVN_NO_ERROR;
- err = find_repos(client_url, params->root, &b, pool);
+ /* find_repos needs the capabilities as a list of words (eventually
+ they get handed to the start-commit hook). While we could add a
+ new interface to re-retrieve them from conn and convert the
+ result to a list, it's simpler to just convert caplist by hand
+ here, since we already have it and turning 'svn_ra_svn_item_t's
+ into 'const char *'s is pretty easy. */
+ {
+ int i;
+ svn_ra_svn_item_t *item;
+
+ cap_words = apr_array_make(pool, caplist->nelts, sizeof(const char *));
+ for (i = 0; i < caplist->nelts; i++)
+ {
+ item = &APR_ARRAY_IDX(caplist, i, svn_ra_svn_item_t);
+ /* ra_svn_set_capabilities() already type-checked for us */
+ APR_ARRAY_PUSH(cap_words, const char *) = item->u.word;
+ }
+ }
+
+ err = find_repos(client_url, params->root, &b, cap_words, pool);
if (!err)
{
SVN_ERR(auth_request(conn, pool, &b, READ_ACCESS, FALSE));
diff --git a/subversion/tests/cmdline/commit_tests.py b/subversion/tests/cmdline/commit_tests.py
index 3a3973c..4717d08 100755
--- a/subversion/tests/cmdline/commit_tests.py
+++ b/subversion/tests/cmdline/commit_tests.py
@@ -2502,6 +2502,42 @@
'commit', '-m', 'log message',
wc_backup)
+def start_commit_detect_capabilities(sbox):
+ "start-commit hook sees client capabilities" # Issue #2991
+ sbox.build()
+ wc_dir = sbox.wc_dir
+ repos_dir = sbox.repo_dir
+
+ # Create a start-commit hook that detects the "mergeinfo" capability.
+ hook_text = "import sys\n" + \
+ "fp = open(sys.argv[1] + '/hooks.log', 'w')\n" + \
+ "caps = sys.argv[3].split(',')\n" + \
+ "if 'mergeinfo' in caps:\n" + \
+ " fp.write('yes')\n" + \
+ "else:\n" + \
+ " fp.write('no')\n" + \
+ "fp.close()\n"
+
+ start_commit_hook = svntest.main.get_start_commit_hook_path(repos_dir)
+ svntest.main.create_python_hook_script(start_commit_hook, hook_text)
+
+ # Commit something.
+ iota_path = os.path.join(wc_dir, "iota")
+ svntest.main.file_append(iota_path, "More stuff in iota")
+ svntest.actions.run_and_verify_svn(None, [], [], 'ci', '--quiet',
+ '-m', 'log msg', wc_dir)
+
+ # Check that "mergeinfo" was detected.
+ log_path = os.path.join(repos_dir, "hooks.log")
+ if os.path.exists(log_path):
+ data = open(log_path).read()
+ os.unlink(log_path)
+ else:
+ raise svntest.actions.SVNUnexpectedOutput("'%s' not found") % log_path
+ if data != 'yes':
+ raise svntest.Failure
+
+
########################################################################
# Run the tests
@@ -2563,6 +2599,7 @@
changelist_near_conflict,
no_such_changelist,
commit_out_of_date_file,
+ start_commit_detect_capabilities,
]
if __name__ == '__main__':