2 * prop_commands.c: Implementation of propset, propget, and proplist.
4 * ====================================================================
5 * Licensed to the Apache Software Foundation (ASF) under one
6 * or more contributor license agreements. See the NOTICE file
7 * distributed with this work for additional information
8 * regarding copyright ownership. The ASF licenses this file
9 * to you under the Apache License, Version 2.0 (the
10 * "License"); you may not use this file except in compliance
11 * with the License. You may obtain a copy of the License at
13 * http://www.apache.org/licenses/LICENSE-2.0
15 * Unless required by applicable law or agreed to in writing,
16 * software distributed under the License is distributed on an
17 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18 * KIND, either express or implied. See the License for the
19 * specific language governing permissions and limitations
21 * ====================================================================
24 /* ==================================================================== */
30 #define APR_WANT_STRFUNC
33 #include "svn_error.h"
34 #include "svn_client.h"
36 #include "svn_dirent_uri.h"
38 #include "svn_pools.h"
39 #include "svn_props.h"
41 #include "svn_sorts.h"
43 #include "svn_private_config.h"
44 #include "private/svn_wc_private.h"
45 #include "private/svn_ra_private.h"
46 #include "private/svn_client_private.h"
51 /* Return an SVN_ERR_CLIENT_PROPERTY_NAME error if NAME is a wcprop,
52 else return SVN_NO_ERROR. */
54 error_if_wcprop_name(const char *name)
56 if (svn_property_kind2(name) == svn_prop_wc_kind)
58 return svn_error_createf
59 (SVN_ERR_CLIENT_PROPERTY_NAME, NULL,
60 _("'%s' is a wcprop, thus not accessible to clients"),
70 svn_ra_session_t *ra_session;
71 svn_revnum_t base_revision_for_url;
76 get_file_for_validation(const svn_string_t **mime_type,
81 struct getter_baton *gb = baton;
82 svn_ra_session_t *ra_session = gb->ra_session;
85 SVN_ERR(svn_ra_get_file(ra_session, "", gb->base_revision_for_url,
87 (mime_type ? &props : NULL),
91 *mime_type = svn_hash_gets(props, SVN_PROP_MIME_TYPE);
99 do_url_propset(const char *url,
100 const char *propname,
101 const svn_string_t *propval,
102 const svn_node_kind_t kind,
103 const svn_revnum_t base_revision_for_url,
104 const svn_delta_editor_t *editor,
110 SVN_ERR(editor->open_root(edit_baton, base_revision_for_url, pool,
113 if (kind == svn_node_file)
116 const char *uri_basename = svn_uri_basename(url, pool);
118 SVN_ERR(editor->open_file(uri_basename, root_baton,
119 base_revision_for_url, pool, &file_baton));
120 SVN_ERR(editor->change_file_prop(file_baton, propname, propval, pool));
121 SVN_ERR(editor->close_file(file_baton, NULL, pool));
125 SVN_ERR(editor->change_dir_prop(root_baton, propname, propval, pool));
128 return editor->close_directory(root_baton, pool);
132 propset_on_url(const char *propname,
133 const svn_string_t *propval,
135 svn_boolean_t skip_checks,
136 svn_revnum_t base_revision_for_url,
137 const apr_hash_t *revprop_table,
138 svn_commit_callback2_t commit_callback,
140 svn_client_ctx_t *ctx,
143 enum svn_prop_kind prop_kind = svn_property_kind2(propname);
144 svn_ra_session_t *ra_session;
145 svn_node_kind_t node_kind;
147 const svn_delta_editor_t *editor;
149 apr_hash_t *commit_revprops;
152 if (prop_kind != svn_prop_regular_kind)
153 return svn_error_createf
154 (SVN_ERR_BAD_PROP_KIND, NULL,
155 _("Property '%s' is not a regular property"), propname);
157 /* Open an RA session for the URL. Note that we don't have a local
158 directory, nor a place to put temp files. */
159 SVN_ERR(svn_client_open_ra_session2(&ra_session, target, NULL,
162 SVN_ERR(svn_ra_check_path(ra_session, "", base_revision_for_url,
164 if (node_kind == svn_node_none)
165 return svn_error_createf
166 (SVN_ERR_FS_NOT_FOUND, NULL,
167 _("Path '%s' does not exist in revision %ld"),
168 target, base_revision_for_url);
170 if (node_kind == svn_node_file)
172 /* We need to reparent our session one directory up, since editor
173 semantics require the root is a directory.
175 ### How does this interact with authz? */
176 const char *parent_url;
177 parent_url = svn_uri_dirname(target, pool);
179 SVN_ERR(svn_ra_reparent(ra_session, parent_url, pool));
182 /* Setting an inappropriate property is not allowed (unless
183 overridden by 'skip_checks', in some circumstances). Deleting an
184 inappropriate property is allowed, however, since older clients
185 allowed (and other clients possibly still allow) setting it in
187 if (propval && svn_prop_is_svn_prop(propname))
189 const svn_string_t *new_value;
190 struct getter_baton gb;
192 gb.ra_session = ra_session;
193 gb.base_revision_for_url = base_revision_for_url;
194 SVN_ERR(svn_wc_canonicalize_svn_prop(&new_value, propname, propval,
195 target, node_kind, skip_checks,
196 get_file_for_validation, &gb, pool));
200 /* Create a new commit item and add it to the array. */
201 if (SVN_CLIENT__HAS_LOG_MSG_FUNC(ctx))
203 svn_client_commit_item3_t *item;
204 const char *tmp_file;
205 apr_array_header_t *commit_items = apr_array_make(pool, 1, sizeof(item));
207 item = svn_client_commit_item3_create(pool);
209 item->state_flags = SVN_CLIENT_COMMIT_ITEM_PROP_MODS;
210 APR_ARRAY_PUSH(commit_items, svn_client_commit_item3_t *) = item;
211 SVN_ERR(svn_client__get_log_msg(&message, &tmp_file, commit_items,
219 SVN_ERR(svn_client__ensure_revprop_table(&commit_revprops, revprop_table,
220 message, ctx, pool));
222 /* Fetch RA commit editor. */
223 SVN_ERR(svn_ra__register_editor_shim_callbacks(ra_session,
224 svn_client__get_shim_callbacks(ctx->wc_ctx,
226 SVN_ERR(svn_ra_get_commit_editor3(ra_session, &editor, &edit_baton,
230 NULL, TRUE, /* No lock tokens */
233 err = do_url_propset(target, propname, propval, node_kind,
234 base_revision_for_url, editor, edit_baton, pool);
238 /* At least try to abort the edit (and fs txn) before throwing err. */
239 svn_error_clear(editor->abort_edit(edit_baton, pool));
240 return svn_error_trace(err);
243 /* Close the edit. */
244 return editor->close_edit(edit_baton, pool);
247 /* Check that PROPNAME is a valid name for a versioned property. Return an
248 * error if it is not valid, specifically if it is:
249 * - the name of a standard Subversion rev-prop; or
250 * - in the namespace of WC-props; or
251 * - not a well-formed property name (except if PROPVAL is NULL: in other
252 * words we do allow deleting a prop with an ill-formed name).
254 * Since Subversion controls the "svn:" property namespace, we don't honor
255 * a 'skip_checks' flag here. Checks for unusual property combinations such
256 * as svn:eol-style with a non-text svn:mime-type might understandably be
257 * skipped, but things such as using a property name reserved for revprops
258 * on a local target are never allowed.
261 check_prop_name(const char *propname,
262 const svn_string_t *propval)
264 if (svn_prop_is_known_svn_rev_prop(propname))
265 return svn_error_createf(SVN_ERR_CLIENT_PROPERTY_NAME, NULL,
266 _("Revision property '%s' not allowed "
267 "in this context"), propname);
269 SVN_ERR(error_if_wcprop_name(propname));
271 if (propval && ! svn_prop_name_is_valid(propname))
272 return svn_error_createf(SVN_ERR_CLIENT_PROPERTY_NAME, NULL,
273 _("Bad property name: '%s'"), propname);
279 svn_client_propset_local(const char *propname,
280 const svn_string_t *propval,
281 const apr_array_header_t *targets,
283 svn_boolean_t skip_checks,
284 const apr_array_header_t *changelists,
285 svn_client_ctx_t *ctx,
286 apr_pool_t *scratch_pool)
288 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
289 svn_boolean_t targets_are_urls;
292 if (targets->nelts == 0)
295 /* Check for homogeneity among our targets. */
296 targets_are_urls = svn_path_is_url(APR_ARRAY_IDX(targets, 0, const char *));
297 SVN_ERR(svn_client__assert_homogeneous_target_type(targets));
299 if (targets_are_urls)
300 return svn_error_create(SVN_ERR_ILLEGAL_TARGET, NULL,
301 _("Targets must be working copy paths"));
303 SVN_ERR(check_prop_name(propname, propval));
305 for (i = 0; i < targets->nelts; i++)
307 svn_node_kind_t kind;
308 const char *target_abspath;
309 const char *target = APR_ARRAY_IDX(targets, i, const char *);
311 svn_pool_clear(iterpool);
313 /* Check for cancellation */
314 if (ctx->cancel_func)
315 SVN_ERR(ctx->cancel_func(ctx->cancel_baton));
317 SVN_ERR(svn_dirent_get_absolute(&target_abspath, target, iterpool));
319 /* Call prop_set for deleted nodes to have special errors */
320 SVN_ERR(svn_wc_read_kind2(&kind, ctx->wc_ctx, target_abspath,
321 FALSE, FALSE, iterpool));
323 if (kind == svn_node_unknown || kind == svn_node_none)
325 if (ctx->notify_func2)
327 svn_wc_notify_t *notify = svn_wc_create_notify(
329 svn_wc_notify_path_nonexistent,
332 ctx->notify_func2(ctx->notify_baton2, notify, iterpool);
336 SVN_WC__CALL_WITH_WRITE_LOCK(
337 svn_wc_prop_set4(ctx->wc_ctx, target_abspath, propname,
338 propval, depth, skip_checks, changelists,
339 ctx->cancel_func, ctx->cancel_baton,
340 ctx->notify_func2, ctx->notify_baton2, iterpool),
341 ctx->wc_ctx, target_abspath, FALSE /* lock_anchor */, iterpool);
343 svn_pool_destroy(iterpool);
349 svn_client_propset_remote(const char *propname,
350 const svn_string_t *propval,
352 svn_boolean_t skip_checks,
353 svn_revnum_t base_revision_for_url,
354 const apr_hash_t *revprop_table,
355 svn_commit_callback2_t commit_callback,
357 svn_client_ctx_t *ctx,
358 apr_pool_t *scratch_pool)
360 if (!svn_path_is_url(url))
361 return svn_error_create(SVN_ERR_ILLEGAL_TARGET, NULL,
362 _("Targets must be URLs"));
364 SVN_ERR(check_prop_name(propname, propval));
366 /* The rationale for requiring the base_revision_for_url
367 argument is that without it, it's too easy to possibly
368 overwrite someone else's change without noticing. (See also
369 tools/examples/svnput.c). */
370 if (! SVN_IS_VALID_REVNUM(base_revision_for_url))
371 return svn_error_create(SVN_ERR_CLIENT_BAD_REVISION, NULL,
372 _("Setting property on non-local targets "
373 "needs a base revision"));
375 /* ### When you set svn:eol-style or svn:keywords on a wc file,
376 ### Subversion sends a textdelta at commit time to properly
377 ### normalize the file in the repository. If we want to
378 ### support editing these properties on URLs, then we should
379 ### generate the same textdelta; for now, we won't support
380 ### editing these properties on URLs. (Admittedly, this
381 ### means that all the machinery with get_file_for_validation
384 if ((strcmp(propname, SVN_PROP_EOL_STYLE) == 0) ||
385 (strcmp(propname, SVN_PROP_KEYWORDS) == 0))
386 return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
387 _("Setting property '%s' on non-local "
388 "targets is not supported"), propname);
390 SVN_ERR(propset_on_url(propname, propval, url, skip_checks,
391 base_revision_for_url, revprop_table,
392 commit_callback, commit_baton, ctx, scratch_pool));
398 check_and_set_revprop(svn_revnum_t *set_rev,
399 svn_ra_session_t *ra_session,
400 const char *propname,
401 const svn_string_t *original_propval,
402 const svn_string_t *propval,
405 if (original_propval)
407 /* Ensure old value hasn't changed behind our back. */
408 svn_string_t *current;
409 SVN_ERR(svn_ra_rev_prop(ra_session, *set_rev, propname, ¤t, pool));
411 if (original_propval->data && (! current))
413 return svn_error_createf(
414 SVN_ERR_RA_OUT_OF_DATE, NULL,
415 _("revprop '%s' in r%ld is unexpectedly absent "
416 "in repository (maybe someone else deleted it?)"),
419 else if (original_propval->data
420 && (! svn_string_compare(original_propval, current)))
422 return svn_error_createf(
423 SVN_ERR_RA_OUT_OF_DATE, NULL,
424 _("revprop '%s' in r%ld has unexpected value "
425 "in repository (maybe someone else changed it?)"),
428 else if ((! original_propval->data) && current)
430 return svn_error_createf(
431 SVN_ERR_RA_OUT_OF_DATE, NULL,
432 _("revprop '%s' in r%ld is unexpectedly present "
433 "in repository (maybe someone else set it?)"),
438 SVN_ERR(svn_ra_change_rev_prop2(ra_session, *set_rev, propname,
439 NULL, propval, pool));
445 svn_client_revprop_set2(const char *propname,
446 const svn_string_t *propval,
447 const svn_string_t *original_propval,
449 const svn_opt_revision_t *revision,
450 svn_revnum_t *set_rev,
452 svn_client_ctx_t *ctx,
455 svn_ra_session_t *ra_session;
456 svn_boolean_t be_atomic;
458 if ((strcmp(propname, SVN_PROP_REVISION_AUTHOR) == 0)
460 && strchr(propval->data, '\n') != NULL
462 return svn_error_create(SVN_ERR_CLIENT_REVISION_AUTHOR_CONTAINS_NEWLINE,
463 NULL, _("Author name should not contain a newline;"
464 " value will not be set unless forced"));
466 if (propval && ! svn_prop_name_is_valid(propname))
467 return svn_error_createf(SVN_ERR_CLIENT_PROPERTY_NAME, NULL,
468 _("Bad property name: '%s'"), propname);
470 /* Open an RA session for the URL. */
471 SVN_ERR(svn_client_open_ra_session2(&ra_session, URL, NULL,
474 /* Resolve the revision into something real, and return that to the
476 SVN_ERR(svn_client__get_revision_number(set_rev, NULL, ctx->wc_ctx, NULL,
477 ra_session, revision, pool));
479 SVN_ERR(svn_ra_has_capability(ra_session, &be_atomic,
480 SVN_RA_CAPABILITY_ATOMIC_REVPROPS, pool));
483 /* Convert ORIGINAL_PROPVAL to an OLD_VALUE_P. */
484 const svn_string_t *const *old_value_p;
485 const svn_string_t *unset = NULL;
487 if (original_propval == NULL)
489 else if (original_propval->data == NULL)
490 old_value_p = &unset;
492 old_value_p = &original_propval;
494 /* The actual RA call. */
495 SVN_ERR(svn_ra_change_rev_prop2(ra_session, *set_rev, propname,
496 old_value_p, propval, pool));
500 /* The actual RA call. */
501 SVN_ERR(check_and_set_revprop(set_rev, ra_session, propname,
502 original_propval, propval, pool));
505 if (ctx->notify_func2)
507 svn_wc_notify_t *notify = svn_wc_create_notify_url(URL,
509 ? svn_wc_notify_revprop_deleted
510 : svn_wc_notify_revprop_set,
512 notify->prop_name = propname;
513 notify->revision = *set_rev;
515 (*ctx->notify_func2)(ctx->notify_baton2, notify, pool);
521 /* Helper for the remote case of svn_client_propget.
523 * If PROPS is not null, then get the value of property PROPNAME in REVNUM,
524 using RA_LIB and SESSION. Store the value ('svn_string_t *') in PROPS,
525 under the path key "TARGET_PREFIX/TARGET_RELATIVE" ('const char *').
527 * If INHERITED_PROPS is not null, then set *INHERITED_PROPS to a
528 * depth-first ordered array of svn_prop_inherited_item_t * structures
529 * representing the PROPNAME properties inherited by the target. If
530 * INHERITABLE_PROPS in not null and no inheritable properties are found,
531 * then set *INHERITED_PROPS to an empty array.
533 * Recurse according to DEPTH, similarly to svn_client_propget3().
535 * KIND is the kind of the node at "TARGET_PREFIX/TARGET_RELATIVE".
536 * Yes, caller passes this; it makes the recursion more efficient :-).
538 * Allocate PROPS and *INHERITED_PROPS in RESULT_POOL, but do all temporary
539 * work in SCRATCH_POOL. The two pools can be the same; recursive
540 * calls may use a different SCRATCH_POOL, however.
543 remote_propget(apr_hash_t *props,
544 apr_array_header_t **inherited_props,
545 const char *propname,
546 const char *target_prefix,
547 const char *target_relative,
548 svn_node_kind_t kind,
550 svn_ra_session_t *ra_session,
552 apr_pool_t *result_pool,
553 apr_pool_t *scratch_pool)
556 apr_hash_t *prop_hash = NULL;
557 const svn_string_t *val;
558 const char *target_full_url =
559 svn_path_url_add_component2(target_prefix, target_relative,
562 if (kind == svn_node_dir)
564 SVN_ERR(svn_ra_get_dir2(ra_session,
565 (depth >= svn_depth_files ? &dirents : NULL),
566 NULL, &prop_hash, target_relative, revnum,
567 SVN_DIRENT_KIND, scratch_pool));
569 else if (kind == svn_node_file)
571 SVN_ERR(svn_ra_get_file(ra_session, target_relative, revnum,
572 NULL, NULL, &prop_hash, scratch_pool));
574 else if (kind == svn_node_none)
576 return svn_error_createf(SVN_ERR_ENTRY_NOT_FOUND, NULL,
577 _("'%s' does not exist in revision %ld"),
578 target_full_url, revnum);
582 return svn_error_createf(SVN_ERR_NODE_UNKNOWN_KIND, NULL,
583 _("Unknown node kind for '%s'"),
589 const char *repos_root_url;
591 /* We will filter out all but PROPNAME later, making a final copy
592 in RESULT_POOL, so pass SCRATCH_POOL for all pools. */
593 SVN_ERR(svn_ra_get_inherited_props(ra_session, inherited_props,
594 target_relative, revnum,
595 scratch_pool, scratch_pool));
596 SVN_ERR(svn_ra_get_repos_root2(ra_session, &repos_root_url,
598 SVN_ERR(svn_client__iprop_relpaths_to_urls(*inherited_props,
604 /* Make a copy of any inherited PROPNAME properties in RESULT_POOL. */
608 apr_array_header_t *final_iprops =
609 apr_array_make(result_pool, 1, sizeof(svn_prop_inherited_item_t *));
611 for (i = 0; i < (*inherited_props)->nelts; i++)
613 svn_prop_inherited_item_t *iprop =
614 APR_ARRAY_IDX((*inherited_props), i, svn_prop_inherited_item_t *);
615 svn_string_t *iprop_val = svn_hash_gets(iprop->prop_hash, propname);
619 svn_prop_inherited_item_t *new_iprop =
620 apr_palloc(result_pool, sizeof(*new_iprop));
621 new_iprop->path_or_url =
622 apr_pstrdup(result_pool, iprop->path_or_url);
623 new_iprop->prop_hash = apr_hash_make(result_pool);
624 svn_hash_sets(new_iprop->prop_hash,
625 apr_pstrdup(result_pool, propname),
626 svn_string_dup(iprop_val, result_pool));
627 APR_ARRAY_PUSH(final_iprops, svn_prop_inherited_item_t *) =
631 *inherited_props = final_iprops;
635 && (val = svn_hash_gets(prop_hash, propname)))
638 apr_pstrdup(result_pool, target_full_url),
639 svn_string_dup(val, result_pool));
642 if (depth >= svn_depth_files
643 && kind == svn_node_dir
644 && apr_hash_count(dirents) > 0)
646 apr_hash_index_t *hi;
647 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
649 for (hi = apr_hash_first(scratch_pool, dirents);
651 hi = apr_hash_next(hi))
653 const char *this_name = svn__apr_hash_index_key(hi);
654 svn_dirent_t *this_ent = svn__apr_hash_index_val(hi);
655 const char *new_target_relative;
656 svn_depth_t depth_below_here = depth;
658 svn_pool_clear(iterpool);
660 if (depth == svn_depth_files && this_ent->kind == svn_node_dir)
663 if (depth == svn_depth_files || depth == svn_depth_immediates)
664 depth_below_here = svn_depth_empty;
666 new_target_relative = svn_relpath_join(target_relative, this_name,
669 SVN_ERR(remote_propget(props, NULL,
677 result_pool, iterpool));
680 svn_pool_destroy(iterpool);
686 /* Baton for recursive_propget_receiver(). */
687 struct recursive_propget_receiver_baton
689 apr_hash_t *props; /* Hash to collect props. */
690 apr_pool_t *pool; /* Pool to allocate additions to PROPS. */
691 svn_wc_context_t *wc_ctx; /* Working copy context. */
694 /* An implementation of svn_wc__proplist_receiver_t. */
696 recursive_propget_receiver(void *baton,
697 const char *local_abspath,
699 apr_pool_t *scratch_pool)
701 struct recursive_propget_receiver_baton *b = baton;
703 if (apr_hash_count(props))
705 apr_hash_index_t *hi = apr_hash_first(scratch_pool, props);
706 svn_hash_sets(b->props, apr_pstrdup(b->pool, local_abspath),
707 svn_string_dup(svn__apr_hash_index_val(hi), b->pool));
713 /* Return the property value for any PROPNAME set on TARGET in *PROPS,
714 with WC paths of char * for keys and property values of
715 svn_string_t * for values. Assumes that PROPS is non-NULL. Additions
716 to *PROPS are allocated in RESULT_POOL, temporary allocations happen in
719 CHANGELISTS is an array of const char * changelist names, used as a
720 restrictive filter on items whose properties are set; that is,
721 don't set properties on any item unless it's a member of one of
722 those changelists. If CHANGELISTS is empty (or altogether NULL),
723 no changelist filtering occurs.
725 Treat DEPTH as in svn_client_propget3().
728 get_prop_from_wc(apr_hash_t **props,
729 const char *propname,
730 const char *target_abspath,
731 svn_boolean_t pristine,
732 svn_node_kind_t kind,
734 const apr_array_header_t *changelists,
735 svn_client_ctx_t *ctx,
736 apr_pool_t *result_pool,
737 apr_pool_t *scratch_pool)
739 struct recursive_propget_receiver_baton rb;
741 /* Technically, svn_depth_unknown just means use whatever depth(s)
742 we find in the working copy. But this is a walk over extant
743 working copy paths: if they're there at all, then by definition
744 the local depth reaches them, so let's just use svn_depth_infinity
746 if (depth == svn_depth_unknown)
747 depth = svn_depth_infinity;
749 if (!pristine && depth == svn_depth_infinity
750 && (!changelists || changelists->nelts == 0))
752 /* Handle this common svn:mergeinfo case more efficient than the target
753 list handling in the recursive retrieval. */
754 SVN_ERR(svn_wc__prop_retrieve_recursive(
755 props, ctx->wc_ctx, target_abspath, propname,
756 result_pool, scratch_pool));
760 *props = apr_hash_make(result_pool);
762 rb.pool = result_pool;
763 rb.wc_ctx = ctx->wc_ctx;
765 SVN_ERR(svn_wc__prop_list_recursive(ctx->wc_ctx, target_abspath,
766 propname, depth, pristine,
768 recursive_propget_receiver, &rb,
769 ctx->cancel_func, ctx->cancel_baton,
775 /* Note: this implementation is very similar to svn_client_proplist. */
777 svn_client_propget5(apr_hash_t **props,
778 apr_array_header_t **inherited_props,
779 const char *propname,
781 const svn_opt_revision_t *peg_revision,
782 const svn_opt_revision_t *revision,
783 svn_revnum_t *actual_revnum,
785 const apr_array_header_t *changelists,
786 svn_client_ctx_t *ctx,
787 apr_pool_t *result_pool,
788 apr_pool_t *scratch_pool)
791 svn_boolean_t local_explicit_props;
792 svn_boolean_t local_iprops;
794 SVN_ERR(error_if_wcprop_name(propname));
795 if (!svn_path_is_url(target))
796 SVN_ERR_ASSERT(svn_dirent_is_absolute(target));
798 peg_revision = svn_cl__rev_default_to_head_or_working(peg_revision,
800 revision = svn_cl__rev_default_to_peg(revision, peg_revision);
802 local_explicit_props =
803 (! svn_path_is_url(target)
804 && SVN_CLIENT__REVKIND_IS_LOCAL_TO_WC(peg_revision->kind)
805 && SVN_CLIENT__REVKIND_IS_LOCAL_TO_WC(revision->kind));
808 (local_explicit_props
809 && (peg_revision->kind == svn_opt_revision_working
810 || peg_revision->kind == svn_opt_revision_unspecified )
811 && (revision->kind == svn_opt_revision_working
812 || revision->kind == svn_opt_revision_unspecified ));
814 if (local_explicit_props)
816 svn_node_kind_t kind;
817 svn_boolean_t pristine;
820 /* If FALSE, we want the working revision. */
821 pristine = (revision->kind == svn_opt_revision_committed
822 || revision->kind == svn_opt_revision_base);
824 SVN_ERR(svn_wc_read_kind2(&kind, ctx->wc_ctx, target,
828 if (kind == svn_node_unknown || kind == svn_node_none)
830 /* svn uses SVN_ERR_UNVERSIONED_RESOURCE as warning only
831 for this function. */
832 return svn_error_createf(SVN_ERR_UNVERSIONED_RESOURCE, NULL,
833 _("'%s' is not under version control"),
834 svn_dirent_local_style(target,
838 err = svn_client__get_revision_number(&revnum, NULL, ctx->wc_ctx,
839 target, NULL, revision,
841 if (err && err->apr_err == SVN_ERR_CLIENT_BAD_REVISION)
843 svn_error_clear(err);
844 revnum = SVN_INVALID_REVNUM;
847 return svn_error_trace(err);
849 if (inherited_props && local_iprops)
851 const char *repos_root_url;
853 SVN_ERR(svn_wc__get_iprops(inherited_props, ctx->wc_ctx,
855 result_pool, scratch_pool));
856 SVN_ERR(svn_client_get_repos_root(&repos_root_url, NULL,
857 target, ctx, scratch_pool,
859 SVN_ERR(svn_client__iprop_relpaths_to_urls(*inherited_props,
865 SVN_ERR(get_prop_from_wc(props, propname, target,
867 depth, changelists, ctx, result_pool,
871 if ((inherited_props && !local_iprops)
872 || !local_explicit_props)
874 svn_ra_session_t *ra_session;
875 svn_node_kind_t kind;
876 svn_opt_revision_t new_operative_rev;
877 svn_opt_revision_t new_peg_rev;
879 /* Peg or operative revisions may be WC specific for
880 TARGET's explicit props, but still require us to
881 contact the repository for the inherited properties. */
882 if (SVN_CLIENT__REVKIND_NEEDS_WC(peg_revision->kind)
883 || SVN_CLIENT__REVKIND_NEEDS_WC(revision->kind))
885 svn_revnum_t origin_rev;
886 const char *repos_relpath;
887 const char *repos_root_url;
888 const char *repos_uuid;
889 const char *local_abspath;
890 const char *copy_root_abspath;
891 svn_boolean_t is_copy;
893 /* Avoid assertion on the next line when somebody accidentally asks for
894 a working copy revision on a URL */
895 if (svn_path_is_url(target))
896 return svn_error_create(SVN_ERR_CLIENT_VERSIONED_PATH_REQUIRED,
899 SVN_ERR_ASSERT(svn_dirent_is_absolute(target));
900 local_abspath = target;
902 if (SVN_CLIENT__REVKIND_NEEDS_WC(peg_revision->kind))
904 SVN_ERR(svn_wc__node_get_origin(&is_copy,
912 FALSE, /* scan_deleted */
917 target = svn_path_url_add_component2(repos_root_url,
920 if (SVN_CLIENT__REVKIND_NEEDS_WC(peg_revision->kind))
922 svn_revnum_t resolved_peg_rev;
924 SVN_ERR(svn_client__get_revision_number(
925 &resolved_peg_rev, NULL, ctx->wc_ctx,
926 local_abspath, NULL, peg_revision, scratch_pool));
927 new_peg_rev.kind = svn_opt_revision_number;
928 new_peg_rev.value.number = resolved_peg_rev;
929 peg_revision = &new_peg_rev;
932 if (SVN_CLIENT__REVKIND_NEEDS_WC(revision->kind))
934 svn_revnum_t resolved_operative_rev;
936 SVN_ERR(svn_client__get_revision_number(
937 &resolved_operative_rev, NULL, ctx->wc_ctx,
938 local_abspath, NULL, revision, scratch_pool));
939 new_operative_rev.kind = svn_opt_revision_number;
940 new_operative_rev.value.number = resolved_operative_rev;
941 revision = &new_operative_rev;
946 /* TARGET doesn't exist in the repository, so there are
947 obviously not inherited props to be found there. */
949 *inherited_props = apr_array_make(
950 result_pool, 0, sizeof(svn_prop_inherited_item_t *));
955 /* Do we still have anything to ask the repository about? */
956 if (!local_explicit_props || !local_iprops)
958 svn_client__pathrev_t *loc;
960 /* Get an RA plugin for this filesystem object. */
961 SVN_ERR(svn_client__ra_session_from_path2(&ra_session, &loc,
967 SVN_ERR(svn_ra_check_path(ra_session, "", loc->rev, &kind,
970 if (!local_explicit_props)
971 *props = apr_hash_make(result_pool);
973 SVN_ERR(remote_propget(!local_explicit_props ? *props : NULL,
974 !local_iprops ? inherited_props : NULL,
975 propname, loc->url, "",
976 kind, loc->rev, ra_session,
977 depth, result_pool, scratch_pool));
983 *actual_revnum = revnum;
988 svn_client_revprop_get(const char *propname,
989 svn_string_t **propval,
991 const svn_opt_revision_t *revision,
992 svn_revnum_t *set_rev,
993 svn_client_ctx_t *ctx,
996 svn_ra_session_t *ra_session;
997 apr_pool_t *subpool = svn_pool_create(pool);
1000 /* Open an RA session for the URL. Note that we don't have a local
1001 directory, nor a place to put temp files. */
1002 SVN_ERR(svn_client_open_ra_session2(&ra_session, URL, NULL,
1003 ctx, subpool, subpool));
1005 /* Resolve the revision into something real, and return that to the
1007 SVN_ERR(svn_client__get_revision_number(set_rev, NULL, ctx->wc_ctx, NULL,
1008 ra_session, revision, subpool));
1010 /* The actual RA call. */
1011 err = svn_ra_rev_prop(ra_session, *set_rev, propname, propval, pool);
1013 /* Close RA session */
1014 svn_pool_destroy(subpool);
1015 return svn_error_trace(err);
1019 /* Call RECEIVER for the given PATH and its PROP_HASH and/or
1020 * INHERITED_PROPERTIES.
1022 * If PROP_HASH is null or has zero count or INHERITED_PROPERTIES is null,
1026 call_receiver(const char *path,
1027 apr_hash_t *prop_hash,
1028 apr_array_header_t *inherited_properties,
1029 svn_proplist_receiver2_t receiver,
1030 void *receiver_baton,
1031 apr_pool_t *scratch_pool)
1033 if ((prop_hash && apr_hash_count(prop_hash))
1034 || inherited_properties)
1035 SVN_ERR(receiver(receiver_baton, path, prop_hash, inherited_properties,
1038 return SVN_NO_ERROR;
1042 /* Helper for the remote case of svn_client_proplist.
1044 * If GET_EXPLICIT_PROPS is true, then call RECEIVER for paths at or under
1045 * "TARGET_PREFIX/TARGET_RELATIVE@REVNUM" (obtained using RA_SESSION) which
1046 * have regular properties. If GET_TARGET_INHERITED_PROPS is true, then send
1047 * the target's inherited properties to the callback.
1049 * The 'path' and keys for 'prop_hash' and 'inherited_prop' arguments to
1050 * RECEIVER are all URLs.
1052 * RESULT_POOL is used to allocated the 'path', 'prop_hash', and
1053 * 'inherited_prop' arguments to RECEIVER. SCRATCH_POOL is used for all
1054 * other (temporary) allocations.
1056 * KIND is the kind of the node at "TARGET_PREFIX/TARGET_RELATIVE".
1058 * If the target is a directory, only fetch properties for the files
1059 * and directories at depth DEPTH. DEPTH has not effect on inherited
1062 static svn_error_t *
1063 remote_proplist(const char *target_prefix,
1064 const char *target_relative,
1065 svn_node_kind_t kind,
1066 svn_revnum_t revnum,
1067 svn_ra_session_t *ra_session,
1068 svn_boolean_t get_explicit_props,
1069 svn_boolean_t get_target_inherited_props,
1071 svn_proplist_receiver2_t receiver,
1072 void *receiver_baton,
1073 svn_cancel_func_t cancel_func,
1075 apr_pool_t *scratch_pool)
1077 apr_hash_t *dirents;
1078 apr_hash_t *prop_hash = NULL;
1079 apr_hash_index_t *hi;
1080 const char *target_full_url =
1081 svn_path_url_add_component2(target_prefix, target_relative, scratch_pool);
1082 apr_array_header_t *inherited_props;
1084 /* Note that we pass only the SCRATCH_POOL to svn_ra_get[dir*|file*] because
1085 we'll be filtering out non-regular properties from PROP_HASH before we
1087 if (kind == svn_node_dir)
1089 SVN_ERR(svn_ra_get_dir2(ra_session,
1090 (depth > svn_depth_empty) ? &dirents : NULL,
1091 NULL, &prop_hash, target_relative, revnum,
1092 SVN_DIRENT_KIND, scratch_pool));
1094 else if (kind == svn_node_file)
1096 SVN_ERR(svn_ra_get_file(ra_session, target_relative, revnum,
1097 NULL, NULL, &prop_hash, scratch_pool));
1101 return svn_error_createf(SVN_ERR_NODE_UNKNOWN_KIND, NULL,
1102 _("Unknown node kind for '%s'"),
1106 if (get_target_inherited_props)
1108 const char *repos_root_url;
1110 SVN_ERR(svn_ra_get_inherited_props(ra_session, &inherited_props,
1111 target_relative, revnum,
1112 scratch_pool, scratch_pool));
1113 SVN_ERR(svn_ra_get_repos_root2(ra_session, &repos_root_url,
1115 SVN_ERR(svn_client__iprop_relpaths_to_urls(inherited_props,
1122 inherited_props = NULL;
1125 if (!get_explicit_props)
1129 /* Filter out non-regular properties, since the RA layer returns all
1130 kinds. Copy regular properties keys/vals from the prop_hash
1131 allocated in SCRATCH_POOL to the "final" hash allocated in
1133 for (hi = apr_hash_first(scratch_pool, prop_hash);
1135 hi = apr_hash_next(hi))
1137 const char *name = svn__apr_hash_index_key(hi);
1138 apr_ssize_t klen = svn__apr_hash_index_klen(hi);
1139 svn_prop_kind_t prop_kind;
1141 prop_kind = svn_property_kind2(name);
1143 if (prop_kind != svn_prop_regular_kind)
1145 apr_hash_set(prop_hash, name, klen, NULL);
1150 SVN_ERR(call_receiver(target_full_url, prop_hash, inherited_props,
1151 receiver, receiver_baton, scratch_pool));
1153 if (depth > svn_depth_empty
1154 && get_explicit_props
1155 && (kind == svn_node_dir) && (apr_hash_count(dirents) > 0))
1157 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
1159 for (hi = apr_hash_first(scratch_pool, dirents);
1161 hi = apr_hash_next(hi))
1163 const char *this_name = svn__apr_hash_index_key(hi);
1164 svn_dirent_t *this_ent = svn__apr_hash_index_val(hi);
1165 const char *new_target_relative;
1168 SVN_ERR(cancel_func(cancel_baton));
1170 svn_pool_clear(iterpool);
1172 new_target_relative = svn_relpath_join(target_relative,
1173 this_name, iterpool);
1175 if (this_ent->kind == svn_node_file
1176 || depth > svn_depth_files)
1178 svn_depth_t depth_below_here = depth;
1180 if (depth == svn_depth_immediates)
1181 depth_below_here = svn_depth_empty;
1183 SVN_ERR(remote_proplist(target_prefix,
1184 new_target_relative,
1188 TRUE /* get_explicit_props */,
1189 FALSE /* get_target_inherited_props */,
1191 receiver, receiver_baton,
1192 cancel_func, cancel_baton,
1197 svn_pool_destroy(iterpool);
1200 return SVN_NO_ERROR;
1204 /* Baton for recursive_proplist_receiver(). */
1205 struct recursive_proplist_receiver_baton
1207 svn_wc_context_t *wc_ctx; /* Working copy context. */
1208 svn_proplist_receiver2_t wrapped_receiver; /* Proplist receiver to call. */
1209 void *wrapped_receiver_baton; /* Baton for the proplist receiver. */
1210 apr_array_header_t *iprops;
1212 /* Anchor, anchor_abspath pair for converting to relative paths */
1214 const char *anchor_abspath;
1217 /* An implementation of svn_wc__proplist_receiver_t. */
1218 static svn_error_t *
1219 recursive_proplist_receiver(void *baton,
1220 const char *local_abspath,
1222 apr_pool_t *scratch_pool)
1224 struct recursive_proplist_receiver_baton *b = baton;
1226 apr_array_header_t *iprops = NULL;
1229 && ! strcmp(local_abspath, b->anchor_abspath))
1231 /* Report iprops with the properties for the anchor */
1237 /* No report for the root?
1238 Report iprops anyway */
1240 SVN_ERR(b->wrapped_receiver(b->wrapped_receiver_baton,
1241 b->anchor ? b->anchor : b->anchor_abspath,
1242 NULL /* prop_hash */,
1248 /* Attempt to convert absolute paths to relative paths for
1249 * presentation purposes, if needed. */
1250 if (b->anchor && b->anchor_abspath)
1252 path = svn_dirent_join(b->anchor,
1253 svn_dirent_skip_ancestor(b->anchor_abspath,
1258 path = local_abspath;
1260 return svn_error_trace(b->wrapped_receiver(b->wrapped_receiver_baton,
1261 path, props, iprops,
1265 /* Helper for svn_client_proplist4 when retrieving properties and/or
1266 inherited properties from the repository. Except as noted below,
1267 all arguments are as per svn_client_proplist4.
1269 GET_EXPLICIT_PROPS controls if explicit props are retrieved. */
1270 static svn_error_t *
1271 get_remote_props(const char *path_or_url,
1272 const svn_opt_revision_t *peg_revision,
1273 const svn_opt_revision_t *revision,
1275 svn_boolean_t get_explicit_props,
1276 svn_boolean_t get_target_inherited_props,
1277 svn_proplist_receiver2_t receiver,
1278 void *receiver_baton,
1279 svn_client_ctx_t *ctx,
1280 apr_pool_t *scratch_pool)
1282 svn_ra_session_t *ra_session;
1283 svn_node_kind_t kind;
1284 svn_opt_revision_t new_operative_rev;
1285 svn_opt_revision_t new_peg_rev;
1286 svn_client__pathrev_t *loc;
1288 /* Peg or operative revisions may be WC specific for
1289 PATH_OR_URL's explicit props, but still require us to
1290 contact the repository for the inherited properties. */
1291 if (SVN_CLIENT__REVKIND_NEEDS_WC(peg_revision->kind)
1292 || SVN_CLIENT__REVKIND_NEEDS_WC(revision->kind))
1294 svn_revnum_t origin_rev;
1295 const char *repos_relpath;
1296 const char *repos_root_url;
1297 const char *repos_uuid;
1298 const char *local_abspath;
1299 const char *copy_root_abspath;
1300 svn_boolean_t is_copy;
1302 /* Avoid assertion on the next line when somebody accidentally asks for
1303 a working copy revision on a URL */
1304 if (svn_path_is_url(path_or_url))
1305 return svn_error_create(SVN_ERR_CLIENT_VERSIONED_PATH_REQUIRED,
1308 SVN_ERR(svn_dirent_get_absolute(&local_abspath, path_or_url,
1311 if (SVN_CLIENT__REVKIND_NEEDS_WC(peg_revision->kind))
1313 SVN_ERR(svn_wc__node_get_origin(&is_copy,
1321 FALSE, /* scan_deleted */
1327 svn_path_url_add_component2(repos_root_url,
1330 if (SVN_CLIENT__REVKIND_NEEDS_WC(peg_revision->kind))
1332 svn_revnum_t resolved_peg_rev;
1334 SVN_ERR(svn_client__get_revision_number(&resolved_peg_rev,
1336 local_abspath, NULL,
1339 new_peg_rev.kind = svn_opt_revision_number;
1340 new_peg_rev.value.number = resolved_peg_rev;
1341 peg_revision = &new_peg_rev;
1344 if (SVN_CLIENT__REVKIND_NEEDS_WC(revision->kind))
1346 svn_revnum_t resolved_operative_rev;
1348 SVN_ERR(svn_client__get_revision_number(
1349 &resolved_operative_rev,
1351 local_abspath, NULL,
1354 new_operative_rev.kind = svn_opt_revision_number;
1355 new_operative_rev.value.number = resolved_operative_rev;
1356 revision = &new_operative_rev;
1361 /* PATH_OR_URL doesn't exist in the repository, so there are
1362 obviously not inherited props to be found there. If we
1363 aren't looking for explicit props then we're done. */
1364 if (!get_explicit_props)
1365 return SVN_NO_ERROR;
1370 /* Get an RA session for this URL. */
1371 SVN_ERR(svn_client__ra_session_from_path2(&ra_session, &loc,
1377 SVN_ERR(svn_ra_check_path(ra_session, "", loc->rev, &kind,
1380 SVN_ERR(remote_proplist(loc->url, "", kind, loc->rev, ra_session,
1382 get_target_inherited_props,
1383 depth, receiver, receiver_baton,
1384 ctx->cancel_func, ctx->cancel_baton,
1386 return SVN_NO_ERROR;
1389 /* Helper for svn_client_proplist4 when retrieving properties and
1390 possibly inherited properties from the WC. All arguments are as
1391 per svn_client_proplist4. */
1392 static svn_error_t *
1393 get_local_props(const char *path_or_url,
1394 const svn_opt_revision_t *revision,
1396 const apr_array_header_t *changelists,
1397 svn_boolean_t get_target_inherited_props,
1398 svn_proplist_receiver2_t receiver,
1399 void *receiver_baton,
1400 svn_client_ctx_t *ctx,
1401 apr_pool_t *scratch_pool)
1403 svn_boolean_t pristine;
1404 svn_node_kind_t kind;
1405 apr_hash_t *changelist_hash = NULL;
1406 const char *local_abspath;
1407 apr_array_header_t *iprops = NULL;
1409 SVN_ERR(svn_dirent_get_absolute(&local_abspath, path_or_url,
1412 pristine = ((revision->kind == svn_opt_revision_committed)
1413 || (revision->kind == svn_opt_revision_base));
1415 SVN_ERR(svn_wc_read_kind2(&kind, ctx->wc_ctx, local_abspath,
1416 pristine, FALSE, scratch_pool));
1418 if (kind == svn_node_unknown || kind == svn_node_none)
1420 /* svn uses SVN_ERR_UNVERSIONED_RESOURCE as warning only
1421 for this function. */
1422 return svn_error_createf(SVN_ERR_UNVERSIONED_RESOURCE, NULL,
1423 _("'%s' is not under version control"),
1424 svn_dirent_local_style(local_abspath,
1428 if (get_target_inherited_props)
1430 const char *repos_root_url;
1432 SVN_ERR(svn_wc__get_iprops(&iprops, ctx->wc_ctx, local_abspath,
1433 NULL, scratch_pool, scratch_pool));
1434 SVN_ERR(svn_client_get_repos_root(&repos_root_url, NULL, local_abspath,
1435 ctx, scratch_pool, scratch_pool));
1436 SVN_ERR(svn_client__iprop_relpaths_to_urls(iprops, repos_root_url,
1441 if (changelists && changelists->nelts)
1442 SVN_ERR(svn_hash_from_cstring_keys(&changelist_hash,
1443 changelists, scratch_pool));
1445 /* Fetch, recursively or not. */
1446 if (kind == svn_node_dir)
1448 struct recursive_proplist_receiver_baton rb;
1450 rb.wc_ctx = ctx->wc_ctx;
1451 rb.wrapped_receiver = receiver;
1452 rb.wrapped_receiver_baton = receiver_baton;
1454 rb.anchor_abspath = local_abspath;
1456 if (strcmp(path_or_url, local_abspath) != 0)
1458 rb.anchor = path_or_url;
1465 SVN_ERR(svn_wc__prop_list_recursive(ctx->wc_ctx, local_abspath, NULL,
1466 depth, pristine, changelists,
1467 recursive_proplist_receiver, &rb,
1468 ctx->cancel_func, ctx->cancel_baton,
1473 /* We didn't report for the root. Report iprops anyway */
1474 SVN_ERR(call_receiver(path_or_url, NULL /* props */, rb.iprops,
1475 receiver, receiver_baton, scratch_pool));
1478 else if (svn_wc__changelist_match(ctx->wc_ctx, local_abspath,
1479 changelist_hash, scratch_pool))
1484 SVN_ERR(svn_wc_get_pristine_props(&props,
1485 ctx->wc_ctx, local_abspath,
1486 scratch_pool, scratch_pool));
1491 err = svn_wc_prop_list2(&props, ctx->wc_ctx, local_abspath,
1492 scratch_pool, scratch_pool);
1497 if (err->apr_err != SVN_ERR_WC_PATH_UNEXPECTED_STATUS)
1498 return svn_error_trace(err);
1499 /* As svn_wc_prop_list2() doesn't return NULL for locally-deleted
1500 let's do that here. */
1501 svn_error_clear(err);
1502 props = apr_hash_make(scratch_pool);
1506 SVN_ERR(call_receiver(path_or_url, props, iprops,
1507 receiver, receiver_baton, scratch_pool));
1510 return SVN_NO_ERROR;
1514 svn_client_proplist4(const char *path_or_url,
1515 const svn_opt_revision_t *peg_revision,
1516 const svn_opt_revision_t *revision,
1518 const apr_array_header_t *changelists,
1519 svn_boolean_t get_target_inherited_props,
1520 svn_proplist_receiver2_t receiver,
1521 void *receiver_baton,
1522 svn_client_ctx_t *ctx,
1523 apr_pool_t *scratch_pool)
1525 svn_boolean_t local_explicit_props;
1526 svn_boolean_t local_iprops;
1528 peg_revision = svn_cl__rev_default_to_head_or_working(peg_revision,
1530 revision = svn_cl__rev_default_to_peg(revision, peg_revision);
1532 if (depth == svn_depth_unknown)
1533 depth = svn_depth_empty;
1535 /* Are explicit props available locally? */
1536 local_explicit_props =
1537 (! svn_path_is_url(path_or_url)
1538 && SVN_CLIENT__REVKIND_IS_LOCAL_TO_WC(peg_revision->kind)
1539 && SVN_CLIENT__REVKIND_IS_LOCAL_TO_WC(revision->kind));
1541 /* If we want iprops are they available locally? */
1543 (get_target_inherited_props /* We want iprops */
1544 && local_explicit_props /* No local explicit props means no local iprops. */
1545 && (peg_revision->kind == svn_opt_revision_working
1546 || peg_revision->kind == svn_opt_revision_unspecified )
1547 && (revision->kind == svn_opt_revision_working
1548 || revision->kind == svn_opt_revision_unspecified ));
1550 if ((get_target_inherited_props && !local_iprops)
1551 || !local_explicit_props)
1553 SVN_ERR(get_remote_props(path_or_url, peg_revision, revision, depth,
1554 !local_explicit_props,
1555 (get_target_inherited_props && !local_iprops),
1556 receiver, receiver_baton, ctx, scratch_pool));
1559 if (local_explicit_props)
1561 SVN_ERR(get_local_props(path_or_url, revision, depth, changelists,
1562 local_iprops, receiver, receiver_baton, ctx,
1566 return SVN_NO_ERROR;
1570 svn_client_revprop_list(apr_hash_t **props,
1572 const svn_opt_revision_t *revision,
1573 svn_revnum_t *set_rev,
1574 svn_client_ctx_t *ctx,
1577 svn_ra_session_t *ra_session;
1578 apr_hash_t *proplist;
1579 apr_pool_t *subpool = svn_pool_create(pool);
1582 /* Open an RA session for the URL. Note that we don't have a local
1583 directory, nor a place to put temp files. */
1584 SVN_ERR(svn_client_open_ra_session2(&ra_session, URL, NULL,
1585 ctx, subpool, subpool));
1587 /* Resolve the revision into something real, and return that to the
1589 SVN_ERR(svn_client__get_revision_number(set_rev, NULL, ctx->wc_ctx, NULL,
1590 ra_session, revision, subpool));
1592 /* The actual RA call. */
1593 err = svn_ra_rev_proplist(ra_session, *set_rev, &proplist, pool);
1596 svn_pool_destroy(subpool); /* Close RA session */
1597 return svn_error_trace(err);