summaryrefslogtreecommitdiff
path: root/subversion/libsvn_client/list.c
diff options
context:
space:
mode:
Diffstat (limited to 'subversion/libsvn_client/list.c')
-rw-r--r--subversion/libsvn_client/list.c439
1 files changed, 369 insertions, 70 deletions
diff --git a/subversion/libsvn_client/list.c b/subversion/libsvn_client/list.c
index 15250eb..4093893 100644
--- a/subversion/libsvn_client/list.c
+++ b/subversion/libsvn_client/list.c
@@ -23,6 +23,7 @@
#include "svn_client.h"
#include "svn_dirent_uri.h"
+#include "svn_hash.h"
#include "svn_path.h"
#include "svn_pools.h"
#include "svn_time.h"
@@ -32,8 +33,37 @@
#include "client.h"
#include "private/svn_fspath.h"
+#include "private/svn_ra_private.h"
+#include "private/svn_wc_private.h"
#include "svn_private_config.h"
+/* Prototypes for referencing before declaration */
+static svn_error_t *
+list_externals(apr_hash_t *externals,
+ svn_depth_t depth,
+ apr_uint32_t dirent_fields,
+ svn_boolean_t fetch_locks,
+ svn_client_list_func2_t list_func,
+ void *baton,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *scratch_pool);
+
+static svn_error_t *
+list_internal(const char *path_or_url,
+ const svn_opt_revision_t *peg_revision,
+ const svn_opt_revision_t *revision,
+ svn_depth_t depth,
+ apr_uint32_t dirent_fields,
+ svn_boolean_t fetch_locks,
+ svn_boolean_t include_externals,
+ const char *external_parent_url,
+ const char *external_target,
+ svn_client_list_func2_t list_func,
+ void *baton,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *pool);
+
+
/* Get the directory entries of DIR at REV (relative to the root of
RA_SESSION), getting at least the fields specified by DIRENT_FIELDS.
Use the cancellation function/baton of CTX to check for cancellation.
@@ -46,7 +76,15 @@
LOCKS, if non-NULL, is a hash mapping const char * paths to svn_lock_t
objects and FS_PATH is the absolute filesystem path of the RA session.
- Use POOL for temporary allocations.
+ Use SCRATCH_POOL for temporary allocations.
+
+ If the caller passes EXTERNALS as non-NULL, populate the EXTERNALS
+ hash table whose keys are URLs of the directory which has externals
+ definitions, and whose values are the externals description text.
+ Allocate the hash's keys and values in RESULT_POOL.
+
+ EXTERNAL_PARENT_URL and EXTERNAL_TARGET are set when external items
+ are listed, otherwise both are set to NULL by the caller.
*/
static svn_error_t *
get_dir_contents(apr_uint32_t dirent_fields,
@@ -57,23 +95,31 @@ get_dir_contents(apr_uint32_t dirent_fields,
const char *fs_path,
svn_depth_t depth,
svn_client_ctx_t *ctx,
- svn_client_list_func_t list_func,
+ apr_hash_t *externals,
+ const char *external_parent_url,
+ const char *external_target,
+ svn_client_list_func2_t list_func,
void *baton,
- apr_pool_t *pool)
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
{
apr_hash_t *tmpdirents;
- apr_pool_t *iterpool = svn_pool_create(pool);
+ apr_pool_t *iterpool = svn_pool_create(scratch_pool);
apr_array_header_t *array;
svn_error_t *err;
+ apr_hash_t *prop_hash = NULL;
+ const svn_string_t *prop_val = NULL;
int i;
if (depth == svn_depth_empty)
return SVN_NO_ERROR;
- /* Get the directory's entries, but not its props. Ignore any
- not-authorized errors. */
- err = svn_ra_get_dir2(ra_session, &tmpdirents, NULL, NULL,
- dir, rev, dirent_fields, pool);
+ /* Get the directory's entries. If externals hash is non-NULL, get its
+ properties also. Ignore any not-authorized errors. */
+ err = svn_ra_get_dir2(ra_session, &tmpdirents, NULL,
+ externals ? &prop_hash : NULL,
+ dir, rev, dirent_fields, scratch_pool);
+
if (err && ((err->apr_err == SVN_ERR_RA_NOT_AUTHORIZED) ||
(err->apr_err == SVN_ERR_RA_DAV_FORBIDDEN)))
{
@@ -82,16 +128,31 @@ get_dir_contents(apr_uint32_t dirent_fields,
}
SVN_ERR(err);
+ /* Filter out svn:externals from all properties hash. */
+ if (prop_hash)
+ prop_val = svn_hash_gets(prop_hash, SVN_PROP_EXTERNALS);
+ if (prop_val)
+ {
+ const char *url;
+
+ SVN_ERR(svn_ra_get_session_url(ra_session, &url, scratch_pool));
+
+ svn_hash_sets(externals,
+ svn_path_url_add_component2(url, dir, result_pool),
+ svn_string_dup(prop_val, result_pool));
+ }
+
if (ctx->cancel_func)
SVN_ERR(ctx->cancel_func(ctx->cancel_baton));
/* Sort the hash, so we can call the callback in a "deterministic" order. */
- array = svn_sort__hash(tmpdirents, svn_sort_compare_items_lexically, pool);
+ array = svn_sort__hash(tmpdirents, svn_sort_compare_items_lexically,
+ scratch_pool);
for (i = 0; i < array->nelts; ++i)
{
svn_sort__item_t *item = &APR_ARRAY_IDX(array, i, svn_sort__item_t);
const char *path;
- svn_dirent_t *the_ent = apr_hash_get(tmpdirents, item->key, item->klen);
+ svn_dirent_t *the_ent = item->value;
svn_lock_t *lock;
svn_pool_clear(iterpool);
@@ -101,7 +162,7 @@ get_dir_contents(apr_uint32_t dirent_fields,
if (locks)
{
const char *abs_path = svn_fspath__join(fs_path, path, iterpool);
- lock = apr_hash_get(locks, abs_path, APR_HASH_KEY_STRING);
+ lock = svn_hash_gets(locks, abs_path);
}
else
lock = NULL;
@@ -109,91 +170,83 @@ get_dir_contents(apr_uint32_t dirent_fields,
if (the_ent->kind == svn_node_file
|| depth == svn_depth_immediates
|| depth == svn_depth_infinity)
- SVN_ERR(list_func(baton, path, the_ent, lock, fs_path, iterpool));
+ SVN_ERR(list_func(baton, path, the_ent, lock, fs_path,
+ external_parent_url, external_target, iterpool));
+ /* If externals is non-NULL, populate the externals hash table
+ recursively for all directory entries. */
if (depth == svn_depth_infinity && the_ent->kind == svn_node_dir)
SVN_ERR(get_dir_contents(dirent_fields, path, rev,
ra_session, locks, fs_path, depth, ctx,
- list_func, baton, iterpool));
+ externals, external_parent_url,
+ external_target, list_func, baton,
+ result_pool, iterpool));
}
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
+/* Like svn_ra_stat() but with a compatibility hack for pre-1.2 svnserve. */
+/* ### Maybe we should move this behavior into the svn_ra_stat wrapper? */
svn_error_t *
-svn_client_list2(const char *path_or_url,
- const svn_opt_revision_t *peg_revision,
- const svn_opt_revision_t *revision,
- svn_depth_t depth,
- apr_uint32_t dirent_fields,
- svn_boolean_t fetch_locks,
- svn_client_list_func_t list_func,
- void *baton,
- svn_client_ctx_t *ctx,
- apr_pool_t *pool)
+svn_client__ra_stat_compatible(svn_ra_session_t *ra_session,
+ svn_revnum_t rev,
+ svn_dirent_t **dirent_p,
+ apr_uint32_t dirent_fields,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *pool)
{
- svn_ra_session_t *ra_session;
- svn_revnum_t rev;
- svn_dirent_t *dirent;
- const char *url;
- const char *repos_root;
- const char *fs_path;
svn_error_t *err;
- apr_hash_t *locks;
-
- /* We use the kind field to determine if we should recurse, so we
- always need it. */
- dirent_fields |= SVN_DIRENT_KIND;
-
- /* Get an RA plugin for this filesystem object. */
- SVN_ERR(svn_client__ra_session_from_path(&ra_session, &rev,
- &url, path_or_url, NULL,
- peg_revision,
- revision, ctx, pool));
-
- SVN_ERR(svn_ra_get_repos_root2(ra_session, &repos_root, pool));
- SVN_ERR(svn_client__path_relative_to_root(&fs_path, ctx->wc_ctx, url,
- repos_root, TRUE, ra_session,
- pool, pool));
-
- err = svn_ra_stat(ra_session, "", rev, &dirent, pool);
+ err = svn_ra_stat(ra_session, "", rev, dirent_p, pool);
/* svnserve before 1.2 doesn't support the above, so fall back on
a less efficient method. */
if (err && err->apr_err == SVN_ERR_RA_NOT_IMPLEMENTED)
{
+ const char *repos_root_url;
+ const char *session_url;
svn_node_kind_t kind;
+ svn_dirent_t *dirent;
svn_error_clear(err);
+ SVN_ERR(svn_ra_get_repos_root2(ra_session, &repos_root_url, pool));
+ SVN_ERR(svn_ra_get_session_url(ra_session, &session_url, pool));
+
SVN_ERR(svn_ra_check_path(ra_session, "", rev, &kind, pool));
if (kind != svn_node_none)
{
- if (strcmp(url, repos_root) != 0)
+ if (strcmp(session_url, repos_root_url) != 0)
{
svn_ra_session_t *parent_session;
apr_hash_t *parent_ents;
const char *parent_url, *base_name;
+ apr_pool_t *subpool = svn_pool_create(pool);
/* Open another session to the path's parent. This server
doesn't support svn_ra_reparent anyway, so don't try it. */
- svn_uri_split(&parent_url, &base_name, url, pool);
+ svn_uri_split(&parent_url, &base_name, session_url, subpool);
- SVN_ERR(svn_client__open_ra_session_internal(&parent_session,
- NULL, parent_url,
- NULL, NULL, FALSE,
- TRUE, ctx, pool));
+ SVN_ERR(svn_client_open_ra_session2(&parent_session, parent_url,
+ NULL, ctx,
+ subpool, subpool));
/* Get all parent's entries, no props. */
SVN_ERR(svn_ra_get_dir2(parent_session, &parent_ents, NULL,
- NULL, "", rev, dirent_fields, pool));
+ NULL, "", rev, dirent_fields, subpool));
/* Get the relevant entry. */
- dirent = apr_hash_get(parent_ents, base_name,
- APR_HASH_KEY_STRING);
+ dirent = svn_hash_gets(parent_ents, base_name);
+
+ if (dirent)
+ *dirent_p = svn_dirent_dup(dirent, pool);
+ else
+ *dirent_p = NULL;
+
+ svn_pool_destroy(subpool); /* Close RA session */
}
else
{
@@ -203,7 +256,7 @@ svn_client_list2(const char *path_or_url,
be rev. */
dirent = apr_palloc(pool, sizeof(*dirent));
dirent->kind = kind;
- dirent->size = 0;
+ dirent->size = SVN_INVALID_FILESIZE;
if (dirent_fields & SVN_DIRENT_HAS_PROPS)
{
apr_hash_t *props;
@@ -220,30 +273,108 @@ svn_client_list2(const char *path_or_url,
SVN_ERR(svn_ra_rev_proplist(ra_session, rev, &props,
pool));
- val = apr_hash_get(props, SVN_PROP_REVISION_DATE,
- APR_HASH_KEY_STRING);
+ val = svn_hash_gets(props, SVN_PROP_REVISION_DATE);
if (val)
SVN_ERR(svn_time_from_cstring(&dirent->time, val->data,
pool));
else
dirent->time = 0;
- val = apr_hash_get(props, SVN_PROP_REVISION_AUTHOR,
- APR_HASH_KEY_STRING);
+ val = svn_hash_gets(props, SVN_PROP_REVISION_AUTHOR);
dirent->last_author = val ? val->data : NULL;
}
+
+ *dirent_p = dirent;
}
}
else
- dirent = NULL;
+ *dirent_p = NULL;
}
- else if (err)
- return svn_error_trace(err);
+ else
+ SVN_ERR(err);
+
+ return SVN_NO_ERROR;
+}
+
+/* List the file/directory entries for PATH_OR_URL at REVISION.
+ The actual node revision selected is determined by the path as
+ it exists in PEG_REVISION.
+
+ If DEPTH is svn_depth_infinity, then list all file and directory entries
+ recursively. Else if DEPTH is svn_depth_files, list all files under
+ PATH_OR_URL (if any), but not subdirectories. Else if DEPTH is
+ svn_depth_immediates, list all files and include immediate
+ subdirectories (at svn_depth_empty). Else if DEPTH is
+ svn_depth_empty, just list PATH_OR_URL with none of its entries.
+
+ DIRENT_FIELDS controls which fields in the svn_dirent_t's are
+ filled in. To have them totally filled in use SVN_DIRENT_ALL,
+ otherwise simply bitwise OR together the combination of SVN_DIRENT_*
+ fields you care about.
+
+ If FETCH_LOCKS is TRUE, include locks when reporting directory entries.
+
+ If INCLUDE_EXTERNALS is TRUE, also list all external items
+ reached by recursion. DEPTH value passed to the original list target
+ applies for the externals also. EXTERNAL_PARENT_URL is url of the
+ directory which has the externals definitions. EXTERNAL_TARGET is the
+ target subdirectory of externals definitions.
+
+ Report directory entries by invoking LIST_FUNC/BATON.
+ Pass EXTERNAL_PARENT_URL and EXTERNAL_TARGET to LIST_FUNC when external
+ items are listed, otherwise both are set to NULL.
+
+ Use authentication baton cached in CTX to authenticate against the
+ repository.
+ Use POOL for all allocations.
+*/
+static svn_error_t *
+list_internal(const char *path_or_url,
+ const svn_opt_revision_t *peg_revision,
+ const svn_opt_revision_t *revision,
+ svn_depth_t depth,
+ apr_uint32_t dirent_fields,
+ svn_boolean_t fetch_locks,
+ svn_boolean_t include_externals,
+ const char *external_parent_url,
+ const char *external_target,
+ svn_client_list_func2_t list_func,
+ void *baton,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *pool)
+{
+ svn_ra_session_t *ra_session;
+ svn_client__pathrev_t *loc;
+ svn_dirent_t *dirent;
+ const char *fs_path;
+ svn_error_t *err;
+ apr_hash_t *locks;
+ apr_hash_t *externals;
+
+ if (include_externals)
+ externals = apr_hash_make(pool);
+ else
+ externals = NULL;
+
+ /* We use the kind field to determine if we should recurse, so we
+ always need it. */
+ dirent_fields |= SVN_DIRENT_KIND;
+
+ /* Get an RA plugin for this filesystem object. */
+ SVN_ERR(svn_client__ra_session_from_path2(&ra_session, &loc,
+ path_or_url, NULL,
+ peg_revision,
+ revision, ctx, pool));
+
+ fs_path = svn_client__pathrev_fspath(loc, pool);
+
+ SVN_ERR(svn_client__ra_stat_compatible(ra_session, loc->rev, &dirent,
+ dirent_fields, ctx, pool));
if (! dirent)
return svn_error_createf(SVN_ERR_FS_NOT_FOUND, NULL,
_("URL '%s' non-existent in revision %ld"),
- url, rev);
+ loc->url, loc->rev);
/* Maybe get all locks under url. */
if (fetch_locks)
@@ -265,16 +396,184 @@ svn_client_list2(const char *path_or_url,
/* Report the dirent for the target. */
SVN_ERR(list_func(baton, "", dirent, locks
- ? (apr_hash_get(locks, fs_path,
- APR_HASH_KEY_STRING))
- : NULL, fs_path, pool));
+ ? (svn_hash_gets(locks, fs_path))
+ : NULL, fs_path, external_parent_url,
+ external_target, pool));
if (dirent->kind == svn_node_dir
&& (depth == svn_depth_files
|| depth == svn_depth_immediates
|| depth == svn_depth_infinity))
- SVN_ERR(get_dir_contents(dirent_fields, "", rev, ra_session, locks,
- fs_path, depth, ctx, list_func, baton, pool));
+ SVN_ERR(get_dir_contents(dirent_fields, "", loc->rev, ra_session, locks,
+ fs_path, depth, ctx, externals,
+ external_parent_url, external_target, list_func,
+ baton, pool, pool));
+
+ /* We handle externals after listing entries under path_or_url, so that
+ handling external items (and any errors therefrom) doesn't delay
+ the primary operation. */
+ if (include_externals && apr_hash_count(externals))
+ {
+ /* The 'externals' hash populated by get_dir_contents() is processed
+ here. */
+ SVN_ERR(list_externals(externals, depth, dirent_fields,
+ fetch_locks, list_func, baton,
+ ctx, pool));
+ }
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+wrap_list_error(const svn_client_ctx_t *ctx,
+ const char *target_abspath,
+ svn_error_t *err,
+ apr_pool_t *scratch_pool)
+{
+ if (err && err->apr_err != SVN_ERR_CANCELLED)
+ {
+ if (ctx->notify_func2)
+ {
+ svn_wc_notify_t *notifier = svn_wc_create_notify(
+ target_abspath,
+ svn_wc_notify_failed_external,
+ scratch_pool);
+ notifier->err = err;
+ ctx->notify_func2(ctx->notify_baton2, notifier, scratch_pool);
+ }
+ svn_error_clear(err);
+ return SVN_NO_ERROR;
+ }
+
+ return err;
+}
+
+
+/* Walk through all the external items and list them. */
+static svn_error_t *
+list_external_items(apr_array_header_t *external_items,
+ const char *externals_parent_url,
+ svn_depth_t depth,
+ apr_uint32_t dirent_fields,
+ svn_boolean_t fetch_locks,
+ svn_client_list_func2_t list_func,
+ void *baton,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *scratch_pool)
+{
+ const char *externals_parent_repos_root_url;
+ apr_pool_t *iterpool;
+ int i;
+
+ SVN_ERR(svn_client_get_repos_root(&externals_parent_repos_root_url,
+ NULL /* uuid */,
+ externals_parent_url, ctx,
+ scratch_pool, scratch_pool));
+
+ iterpool = svn_pool_create(scratch_pool);
+
+ for (i = 0; i < external_items->nelts; i++)
+ {
+ const char *resolved_url;
+
+ svn_wc_external_item2_t *item =
+ APR_ARRAY_IDX(external_items, i, svn_wc_external_item2_t *);
+
+ svn_pool_clear(iterpool);
+
+ SVN_ERR(svn_wc__resolve_relative_external_url(
+ &resolved_url,
+ item,
+ externals_parent_repos_root_url,
+ externals_parent_url,
+ iterpool, iterpool));
+
+ /* List the external */
+ SVN_ERR(wrap_list_error(ctx, item->target_dir,
+ list_internal(resolved_url,
+ &item->peg_revision,
+ &item->revision,
+ depth, dirent_fields,
+ fetch_locks,
+ TRUE,
+ externals_parent_url,
+ item->target_dir,
+ list_func, baton, ctx,
+ iterpool),
+ iterpool));
+
+ }
+ svn_pool_destroy(iterpool);
+
+ return SVN_NO_ERROR;
+}
+
+/* List external items defined on each external in EXTERNALS, a const char *
+ externals_parent_url(url of the directory which has the externals
+ definitions) of all externals mapping to the svn_string_t * externals_desc
+ (externals description text). All other options are the same as those
+ passed to svn_client_list(). */
+static svn_error_t *
+list_externals(apr_hash_t *externals,
+ svn_depth_t depth,
+ apr_uint32_t dirent_fields,
+ svn_boolean_t fetch_locks,
+ svn_client_list_func2_t list_func,
+ void *baton,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *scratch_pool)
+{
+ apr_pool_t *iterpool = svn_pool_create(scratch_pool);
+ apr_hash_index_t *hi;
+
+ for (hi = apr_hash_first(scratch_pool, externals);
+ hi;
+ hi = apr_hash_next(hi))
+ {
+ const char *externals_parent_url = svn__apr_hash_index_key(hi);
+ svn_string_t *externals_desc = svn__apr_hash_index_val(hi);
+ apr_array_header_t *external_items;
+
+ svn_pool_clear(iterpool);
+
+ SVN_ERR(svn_wc_parse_externals_description3(&external_items,
+ externals_parent_url,
+ externals_desc->data,
+ FALSE, iterpool));
+
+ if (! external_items->nelts)
+ continue;
+
+ SVN_ERR(list_external_items(external_items, externals_parent_url, depth,
+ dirent_fields, fetch_locks, list_func,
+ baton, ctx, iterpool));
+
+ }
+ svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
+
+
+svn_error_t *
+svn_client_list3(const char *path_or_url,
+ const svn_opt_revision_t *peg_revision,
+ const svn_opt_revision_t *revision,
+ svn_depth_t depth,
+ apr_uint32_t dirent_fields,
+ svn_boolean_t fetch_locks,
+ svn_boolean_t include_externals,
+ svn_client_list_func2_t list_func,
+ void *baton,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *pool)
+{
+
+ return svn_error_trace(list_internal(path_or_url, peg_revision,
+ revision,
+ depth, dirent_fields,
+ fetch_locks,
+ include_externals,
+ NULL, NULL, list_func,
+ baton, ctx, pool));
+}