summaryrefslogtreecommitdiff
path: root/subversion/libsvn_client/switch.c
diff options
context:
space:
mode:
authorLorry Tar Creator <lorry-tar-importer@baserock.org>2015-03-18 13:33:26 +0000
committer <>2015-07-08 14:41:01 +0000
commitbb0ef45f7c46b0ae221b26265ef98a768c33f820 (patch)
tree98bae10dde41c746c51ae97ec4f879e330415aa7 /subversion/libsvn_client/switch.c
parent239dfafe71711b2f4c43d7b90a1228d7bdc5195e (diff)
downloadsubversion-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.c255
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);
}