diff options
author | Lorry Tar Creator <lorry-tar-importer@baserock.org> | 2015-03-18 13:33:26 +0000 |
---|---|---|
committer | <> | 2015-07-08 14:41:01 +0000 |
commit | bb0ef45f7c46b0ae221b26265ef98a768c33f820 (patch) | |
tree | 98bae10dde41c746c51ae97ec4f879e330415aa7 /subversion/libsvn_client/switch.c | |
parent | 239dfafe71711b2f4c43d7b90a1228d7bdc5195e (diff) | |
download | subversion-tarball-subversion-1.8.13.tar.gz |
Imported from /home/lorry/working-area/delta_subversion-tarball/subversion-1.8.13.tar.gz.subversion-1.8.13
Diffstat (limited to 'subversion/libsvn_client/switch.c')
-rw-r--r-- | subversion/libsvn_client/switch.c | 255 |
1 files changed, 172 insertions, 83 deletions
diff --git a/subversion/libsvn_client/switch.c b/subversion/libsvn_client/switch.c index a67d9e9..cd39cad 100644 --- a/subversion/libsvn_client/switch.c +++ b/subversion/libsvn_client/switch.c @@ -29,6 +29,7 @@ #include "svn_client.h" #include "svn_error.h" +#include "svn_hash.h" #include "svn_time.h" #include "svn_dirent_uri.h" #include "svn_path.h" @@ -55,8 +56,35 @@ */ +/* A conflict callback that simply records the conflicted path in BATON. + + Implements svn_wc_conflict_resolver_func2_t. +*/ +static svn_error_t * +record_conflict(svn_wc_conflict_result_t **result, + const svn_wc_conflict_description2_t *description, + void *baton, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + apr_hash_t *conflicted_paths = baton; + + svn_hash_sets(conflicted_paths, + apr_pstrdup(apr_hash_pool_get(conflicted_paths), + description->local_abspath), ""); + *result = svn_wc_create_conflict_result(svn_wc_conflict_choose_postpone, + NULL, result_pool); + return SVN_NO_ERROR; +} + +/* ... + + Add the paths of any conflict victims to CONFLICTED_PATHS, if that + is not null. +*/ static svn_error_t * switch_internal(svn_revnum_t *result_rev, + apr_hash_t *conflicted_paths, const char *local_abspath, const char *anchor_abspath, const char *switch_url, @@ -73,24 +101,23 @@ switch_internal(svn_revnum_t *result_rev, { const svn_ra_reporter3_t *reporter; void *report_baton; - const char *url, *target, *source_root, *switch_rev_url; + const char *anchor_url, *target; + svn_client__pathrev_t *switch_loc; svn_ra_session_t *ra_session; svn_revnum_t revnum; - svn_error_t *err = SVN_NO_ERROR; const char *diff3_cmd; + apr_hash_t *wcroot_iprops; + apr_array_header_t *inherited_props; svn_boolean_t use_commit_times; - svn_boolean_t sleep_here = FALSE; - svn_boolean_t *use_sleep = timestamp_sleep ? timestamp_sleep : &sleep_here; const svn_delta_editor_t *switch_editor; void *switch_edit_baton; const char *preserved_exts_str; apr_array_header_t *preserved_exts; svn_boolean_t server_supports_depth; struct svn_client__dirent_fetcher_baton_t dfb; - svn_config_t *cfg = ctx->config ? apr_hash_get(ctx->config, - SVN_CONFIG_CATEGORY_CONFIG, - APR_HASH_KEY_STRING) - : NULL; + svn_config_t *cfg = ctx->config + ? svn_hash_gets(ctx->config, SVN_CONFIG_CATEGORY_CONFIG) + : NULL; /* An unknown depth can't be sticky. */ if (depth == svn_depth_unknown) @@ -141,8 +168,9 @@ switch_internal(svn_revnum_t *result_rev, else target = ""; - SVN_ERR(svn_wc__node_get_url(&url, ctx->wc_ctx, anchor_abspath, pool, pool)); - if (! url) + SVN_ERR(svn_wc__node_get_url(&anchor_url, ctx->wc_ctx, anchor_abspath, + pool, pool)); + if (! anchor_url) return svn_error_createf(SVN_ERR_ENTRY_MISSING_URL, NULL, _("Directory '%s' has no URL"), svn_dirent_local_style(anchor_abspath, pool)); @@ -164,8 +192,8 @@ switch_internal(svn_revnum_t *result_rev, return SVN_NO_ERROR; } - SVN_ERR(svn_wc_read_kind(&target_kind, ctx->wc_ctx, local_abspath, TRUE, - pool)); + SVN_ERR(svn_wc_read_kind2(&target_kind, ctx->wc_ctx, local_abspath, + TRUE, TRUE, pool)); if (target_kind == svn_node_dir) SVN_ERR(svn_wc_crop_tree2(ctx->wc_ctx, local_abspath, depth, @@ -175,20 +203,17 @@ switch_internal(svn_revnum_t *result_rev, } /* Open an RA session to 'source' URL */ - SVN_ERR(svn_client__ra_session_from_path(&ra_session, &revnum, - &switch_rev_url, - switch_url, anchor_abspath, - peg_revision, revision, - ctx, pool)); - - SVN_ERR(svn_ra_get_repos_root2(ra_session, &source_root, pool)); + SVN_ERR(svn_client__ra_session_from_path2(&ra_session, &switch_loc, + switch_url, anchor_abspath, + peg_revision, revision, + ctx, pool)); /* Disallow a switch operation to change the repository root of the target. */ - if (! svn_uri__is_ancestor(source_root, url)) + if (! svn_uri__is_ancestor(switch_loc->repos_root_url, anchor_url)) return svn_error_createf(SVN_ERR_WC_INVALID_SWITCH, NULL, _("'%s'\nis not the same repository as\n'%s'"), - url, source_root); + anchor_url, switch_loc->repos_root_url); /* If we're not ignoring ancestry, then error out if the switch source and target don't have a common ancestory. @@ -197,27 +222,82 @@ switch_internal(svn_revnum_t *result_rev, ### okay? */ if (! ignore_ancestry) { - const char *target_url, *yc_path; - svn_revnum_t target_rev, yc_rev; - - SVN_ERR(svn_wc__node_get_url(&target_url, ctx->wc_ctx, local_abspath, - pool, pool)); - SVN_ERR(svn_wc__node_get_base_rev(&target_rev, ctx->wc_ctx, - local_abspath, pool)); - /* ### It would be nice if this function could reuse the existing + svn_client__pathrev_t *target_base_loc, *yca; + + SVN_ERR(svn_client__wc_node_get_base(&target_base_loc, local_abspath, + ctx->wc_ctx, pool, pool)); + + if (!target_base_loc) + yca = NULL; /* Not versioned */ + else + { + /* ### It would be nice if this function could reuse the existing ra session instead of opening two for its own use. */ - SVN_ERR(svn_client__get_youngest_common_ancestor(&yc_path, &yc_rev, - switch_rev_url, revnum, - target_url, target_rev, - ctx, pool)); - if (! (yc_path && SVN_IS_VALID_REVNUM(yc_rev))) + SVN_ERR(svn_client__get_youngest_common_ancestor( + &yca, switch_loc, target_base_loc, ra_session, ctx, + pool, pool)); + } + if (! yca) return svn_error_createf(SVN_ERR_CLIENT_UNRELATED_RESOURCES, NULL, _("'%s' shares no common ancestry with '%s'"), - switch_url, local_abspath); + switch_url, + svn_dirent_local_style(local_abspath, pool)); } + wcroot_iprops = apr_hash_make(pool); + + /* Will the base of LOCAL_ABSPATH require an iprop cache post-switch? + If we are switching LOCAL_ABSPATH to the root of the repository then + we don't need to cache inherited properties. In all other cases we + *might* need to cache iprops. */ + if (strcmp(switch_loc->repos_root_url, switch_loc->url) != 0) + { + svn_boolean_t wc_root; + svn_boolean_t needs_iprop_cache = TRUE; + + SVN_ERR(svn_wc__is_wcroot(&wc_root, ctx->wc_ctx, local_abspath, + pool)); - SVN_ERR(svn_ra_reparent(ra_session, url, pool)); + /* Switching the WC root to anything but the repos root means + we need an iprop cache. */ + if (!wc_root) + { + /* We know we are switching a subtree to something other than the + repos root, but if we are unswitching that subtree we don't + need an iprops cache. */ + const char *target_parent_url; + const char *unswitched_url; + + /* Calculate the URL LOCAL_ABSPATH would have if it was unswitched + relative to its parent. */ + SVN_ERR(svn_wc__node_get_url(&target_parent_url, ctx->wc_ctx, + svn_dirent_dirname(local_abspath, + pool), + pool, pool)); + unswitched_url = svn_path_url_add_component2( + target_parent_url, + svn_dirent_basename(local_abspath, pool), + pool); + + /* If LOCAL_ABSPATH will be unswitched relative to its parent, then + it doesn't need an iprop cache. Note: It doesn't matter if + LOCAL_ABSPATH is withing a switched subtree, only if it's the + *root* of a switched subtree.*/ + if (strcmp(unswitched_url, switch_loc->url) == 0) + needs_iprop_cache = FALSE; + } + + + if (needs_iprop_cache) + { + SVN_ERR(svn_ra_get_inherited_props(ra_session, &inherited_props, + "", switch_loc->rev, pool, + pool)); + svn_hash_sets(wcroot_iprops, local_abspath, inherited_props); + } + } + + SVN_ERR(svn_ra_reparent(ra_session, anchor_url, pool)); /* Fetch the switch (update) editor. If REVISION is invalid, that's okay; the RA driver will call editor->set_target_revision() later on. */ @@ -225,18 +305,19 @@ switch_internal(svn_revnum_t *result_rev, SVN_RA_CAPABILITY_DEPTH, pool)); dfb.ra_session = ra_session; - SVN_ERR(svn_ra_get_session_url(ra_session, &dfb.anchor_url, pool)); - dfb.target_revision = revnum; + dfb.anchor_url = anchor_url; + dfb.target_revision = switch_loc->rev; - SVN_ERR(svn_wc_get_switch_editor4(&switch_editor, &switch_edit_baton, + SVN_ERR(svn_wc__get_switch_editor(&switch_editor, &switch_edit_baton, &revnum, ctx->wc_ctx, anchor_abspath, - target, switch_rev_url, use_commit_times, - depth, + target, switch_loc->url, wcroot_iprops, + use_commit_times, depth, depth_is_sticky, allow_unver_obstructions, server_supports_depth, diff3_cmd, preserved_exts, svn_client__dirent_fetcher, &dfb, - ctx->conflict_func2, ctx->conflict_baton2, + conflicted_paths ? record_conflict : NULL, + conflicted_paths, NULL, NULL, ctx->cancel_func, ctx->cancel_baton, ctx->notify_func2, ctx->notify_baton2, @@ -244,35 +325,31 @@ switch_internal(svn_revnum_t *result_rev, /* Tell RA to do an update of URL+TARGET to REVISION; if we pass an invalid revnum, that means RA will use the latest revision. */ - SVN_ERR(svn_ra_do_switch2(ra_session, &reporter, &report_baton, revnum, + SVN_ERR(svn_ra_do_switch3(ra_session, &reporter, &report_baton, + switch_loc->rev, target, depth_is_sticky ? depth : svn_depth_unknown, - switch_rev_url, - switch_editor, switch_edit_baton, pool)); + switch_loc->url, + FALSE /* send_copyfrom_args */, + ignore_ancestry, + switch_editor, switch_edit_baton, + pool, pool)); + + /* Past this point, we assume the WC is going to be modified so we will + * need to sleep for timestamps. */ + *timestamp_sleep = TRUE; /* 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. - - We pass in an external_func for recording all externals. It - shouldn't be needed for a switch if it wasn't for the relative - externals of type '../path'. All of those must be resolved to - the new location. */ - err = svn_wc_crawl_revisions5(ctx->wc_ctx, local_abspath, reporter, - report_baton, TRUE, depth, (! depth_is_sticky), - (! server_supports_depth), - use_commit_times, - ctx->cancel_func, ctx->cancel_baton, - ctx->notify_func2, ctx->notify_baton2, pool); - - if (err) - { - /* Don't rely on the error handling to handle the sleep later, do - it now */ - svn_io_sleep_for_timestamps(local_abspath, pool); - return svn_error_trace(err); - } - *use_sleep = TRUE; + will be driven by svn_repos_dir_delta2. */ + SVN_ERR(svn_wc_crawl_revisions5(ctx->wc_ctx, local_abspath, reporter, + report_baton, TRUE, + depth, (! depth_is_sticky), + (! server_supports_depth), + use_commit_times, + ctx->cancel_func, ctx->cancel_baton, + ctx->notify_func2, ctx->notify_baton2, + pool)); /* We handle externals after the switch is complete, so that handling external items (and any errors therefrom) doesn't delay @@ -288,20 +365,12 @@ switch_internal(svn_revnum_t *result_rev, SVN_ERR(svn_client__handle_externals(new_externals, new_depths, - source_root, local_abspath, - depth, use_sleep, + switch_loc->repos_root_url, + local_abspath, + depth, timestamp_sleep, ctx, pool)); } - /* Sleep to ensure timestamp integrity (we do this regardless of - errors in the actual switch operation(s)). */ - if (sleep_here) - svn_io_sleep_for_timestamps(local_abspath, pool); - - /* Return errors we might have sustained. */ - if (err) - return svn_error_trace(err); - /* Let everyone know we're finished here. */ if (ctx->notify_func2) { @@ -341,6 +410,8 @@ svn_client__switch_internal(svn_revnum_t *result_rev, const char *local_abspath, *anchor_abspath; svn_boolean_t acquired_lock; svn_error_t *err, *err1, *err2; + apr_hash_t *conflicted_paths + = ctx->conflict_func2 ? apr_hash_make(pool) : NULL; SVN_ERR_ASSERT(path); @@ -357,13 +428,21 @@ svn_client__switch_internal(svn_revnum_t *result_rev, acquired_lock = (err == SVN_NO_ERROR); svn_error_clear(err); - err1 = switch_internal(result_rev, local_abspath, anchor_abspath, + err1 = switch_internal(result_rev, conflicted_paths, + local_abspath, anchor_abspath, switch_url, peg_revision, revision, depth, depth_is_sticky, ignore_externals, allow_unver_obstructions, ignore_ancestry, timestamp_sleep, ctx, pool); + /* Give the conflict resolver callback the opportunity to + * resolve any conflicts that were raised. */ + if (! err1 && ctx->conflict_func2) + { + err1 = svn_client__resolve_conflicts(NULL, conflicted_paths, ctx, pool); + } + if (acquired_lock) err2 = svn_wc__release_write_lock(ctx->wc_ctx, anchor_abspath, pool); else @@ -386,13 +465,23 @@ svn_client_switch3(svn_revnum_t *result_rev, svn_client_ctx_t *ctx, apr_pool_t *pool) { + svn_error_t *err; + svn_boolean_t sleep_here = FALSE; + if (svn_path_is_url(path)) return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL, _("'%s' is not a local path"), path); - return svn_client__switch_internal(result_rev, path, switch_url, - peg_revision, revision, depth, - depth_is_sticky, ignore_externals, - allow_unver_obstructions, - ignore_ancestry, NULL, ctx, pool); + err = svn_client__switch_internal(result_rev, path, switch_url, + peg_revision, revision, depth, + depth_is_sticky, ignore_externals, + allow_unver_obstructions, + ignore_ancestry, &sleep_here, ctx, pool); + + /* Sleep to ensure timestamp integrity (we do this regardless of + errors in the actual switch operation(s)). */ + if (sleep_here) + svn_io_sleep_for_timestamps(path, pool); + + return svn_error_trace(err); } |