summaryrefslogtreecommitdiff
path: root/subversion/libsvn_client/update.c
diff options
context:
space:
mode:
Diffstat (limited to 'subversion/libsvn_client/update.c')
-rw-r--r--subversion/libsvn_client/update.c272
1 files changed, 179 insertions, 93 deletions
diff --git a/subversion/libsvn_client/update.c b/subversion/libsvn_client/update.c
index 1d694ea..0b006cc 100644
--- a/subversion/libsvn_client/update.c
+++ b/subversion/libsvn_client/update.c
@@ -27,6 +27,7 @@
/*** Includes. ***/
+#include "svn_hash.h"
#include "svn_wc.h"
#include "svn_client.h"
#include "svn_error.h"
@@ -83,8 +84,7 @@ svn_client__dirent_fetcher(void *baton,
*dirents = NULL;
if (old_url)
- SVN_ERR(svn_client__ensure_ra_session_url(&old_url, dfb->ra_session,
- old_url, scratch_pool));
+ SVN_ERR(svn_ra_reparent(dfb->ra_session, old_url, scratch_pool));
return SVN_NO_ERROR;
}
@@ -161,6 +161,27 @@ is_empty_wc(svn_boolean_t *clean_checkout,
return svn_io_dir_close(dir);
}
+/* 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;
+}
+
/* This is a helper for svn_client__update_internal(), which see for
an explanation of most of these parameters. Some stuff that's
unique is as follows:
@@ -169,12 +190,16 @@ is_empty_wc(svn_boolean_t *clean_checkout,
This is typically either the same as LOCAL_ABSPATH, or the
immediate parent of LOCAL_ABSPATH.
- If NOTIFY_SUMMARY is set (and there's a notification hander in
+ If NOTIFY_SUMMARY is set (and there's a notification handler in
CTX), transmit the final update summary upon successful
completion of the update.
+
+ Add the paths of any conflict victims to CONFLICTED_PATHS, if that
+ is not null.
*/
static svn_error_t *
update_internal(svn_revnum_t *result_rev,
+ apr_hash_t *conflicted_paths,
const char *local_abspath,
const char *anchor_abspath,
const svn_opt_revision_t *revision,
@@ -192,26 +217,31 @@ update_internal(svn_revnum_t *result_rev,
void *update_edit_baton;
const svn_ra_reporter3_t *reporter;
void *report_baton;
- const char *anchor_url;
const char *corrected_url;
const char *target;
- const char *repos_root;
- svn_error_t *err;
+ const char *repos_root_url;
+ const char *repos_relpath;
+ const char *repos_uuid;
+ const char *anchor_url;
svn_revnum_t revnum;
svn_boolean_t use_commit_times;
- svn_boolean_t sleep_here = FALSE;
- svn_boolean_t *use_sleep = timestamp_sleep ? timestamp_sleep : &sleep_here;
svn_boolean_t clean_checkout = FALSE;
const char *diff3_cmd;
+ apr_hash_t *wcroot_iprops;
+ svn_opt_revision_t opt_rev;
svn_ra_session_t *ra_session;
const char *preserved_exts_str;
apr_array_header_t *preserved_exts;
struct svn_client__dirent_fetcher_baton_t dfb;
svn_boolean_t server_supports_depth;
- svn_boolean_t tree_conflicted;
- svn_config_t *cfg = ctx->config ? apr_hash_get(ctx->config,
- SVN_CONFIG_CATEGORY_CONFIG,
- APR_HASH_KEY_STRING) : NULL;
+ svn_boolean_t cropping_target;
+ svn_boolean_t target_conflicted = FALSE;
+ svn_config_t *cfg = ctx->config
+ ? svn_hash_gets(ctx->config, SVN_CONFIG_CATEGORY_CONFIG)
+ : NULL;
+
+ if (result_rev)
+ *result_rev = SVN_INVALID_REVNUM;
/* An unknown depth can't be sticky. */
if (depth == svn_depth_unknown)
@@ -222,39 +252,45 @@ update_internal(svn_revnum_t *result_rev,
else
target = "";
- /* Get full URL from the ANCHOR. */
- 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,
- _("'%s' has no URL"),
- svn_dirent_local_style(anchor_abspath, pool));
-
- /* Check if our anchor exists in BASE. If it doesn't we can't update.
- ### For performance reasons this should be handled with the same query
- ### as retrieving the anchor url. */
- SVN_ERR(svn_wc__node_get_base_rev(&revnum, ctx->wc_ctx, anchor_abspath,
- pool));
+ /* Check if our anchor exists in BASE. If it doesn't we can't update. */
+ SVN_ERR(svn_wc__node_get_base(NULL, NULL, &repos_relpath, &repos_root_url,
+ &repos_uuid, NULL,
+ ctx->wc_ctx, anchor_abspath,
+ TRUE, FALSE,
+ pool, pool));
- /* It does not make sense to update tree-conflict victims. */
- err = svn_wc_conflicted_p3(NULL, NULL, &tree_conflicted,
- ctx->wc_ctx, local_abspath, pool);
- if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
+ /* It does not make sense to update conflict victims. */
+ if (repos_relpath)
{
+ svn_error_t *err;
+ svn_boolean_t text_conflicted, prop_conflicted;
+
+ anchor_url = svn_path_url_add_component2(repos_root_url, repos_relpath,
+ pool);
+
+ err = svn_wc_conflicted_p3(&text_conflicted, &prop_conflicted,
+ NULL,
+ ctx->wc_ctx, local_abspath, pool);
+
+ if (err && err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
+ return svn_error_trace(err);
svn_error_clear(err);
- tree_conflicted = FALSE;
+
+ /* tree-conflicts are handled by the update editor */
+ if (!err && (text_conflicted || prop_conflicted))
+ target_conflicted = TRUE;
}
else
- SVN_ERR(err);
+ anchor_url = NULL;
- if (!SVN_IS_VALID_REVNUM(revnum) || tree_conflicted)
+ if (! anchor_url || target_conflicted)
{
if (ctx->notify_func2)
{
svn_wc_notify_t *nt;
nt = svn_wc_create_notify(local_abspath,
- tree_conflicted
+ target_conflicted
? svn_wc_notify_skip_conflicted
: svn_wc_notify_update_skip_working_only,
pool);
@@ -265,7 +301,8 @@ update_internal(svn_revnum_t *result_rev,
}
/* We may need to crop the tree if the depth is sticky */
- if (depth_is_sticky && depth < svn_depth_infinity)
+ cropping_target = (depth_is_sticky && depth < svn_depth_infinity);
+ if (cropping_target)
{
svn_node_kind_t target_kind;
@@ -281,8 +318,8 @@ update_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,
@@ -333,33 +370,42 @@ update_internal(svn_revnum_t *result_rev,
SVN_ERR(svn_client__open_ra_session_internal(&ra_session, &corrected_url,
anchor_url,
anchor_abspath, NULL, TRUE,
- TRUE, ctx, pool));
-
- SVN_ERR(svn_ra_get_repos_root2(ra_session, &repos_root, pool));
+ TRUE, ctx, pool, pool));
/* If we got a corrected URL from the RA subsystem, we'll need to
relocate our working copy first. */
if (corrected_url)
{
- const char *current_repos_root;
- const char *current_uuid;
+ const char *new_repos_root_url;
/* To relocate everything inside our repository we need the old and new
- repos root. ### And we should only perform relocates on the wcroot */
- SVN_ERR(svn_wc__node_get_repos_info(&current_repos_root, &current_uuid,
- ctx->wc_ctx, anchor_abspath,
- pool, pool));
-
- /* ### Check uuid here before calling relocate? */
- SVN_ERR(svn_client_relocate2(anchor_abspath, current_repos_root,
- repos_root, ignore_externals, ctx, pool));
+ repos root. */
+ SVN_ERR(svn_ra_get_repos_root2(ra_session, &new_repos_root_url, pool));
+
+ /* svn_client_relocate2() will check the uuid */
+ SVN_ERR(svn_client_relocate2(anchor_abspath, repos_root_url,
+ new_repos_root_url, ignore_externals,
+ ctx, pool));
+
+ /* Store updated repository root for externals */
+ repos_root_url = new_repos_root_url;
+ /* ### We should update anchor_loc->repos_uuid too, although currently
+ * we don't use it. */
anchor_url = corrected_url;
}
+ /* Resolve unspecified REVISION now, because we need to retrieve the
+ correct inherited props prior to the editor drive and we need to
+ use the same value of HEAD for both. */
+ opt_rev.kind = revision->kind;
+ opt_rev.value = revision->value;
+ if (opt_rev.kind == svn_opt_revision_unspecified)
+ opt_rev.kind = svn_opt_revision_head;
+
/* ### todo: shouldn't svn_client__get_revision_number be able
to take a URL as easily as a local path? */
SVN_ERR(svn_client__get_revision_number(&revnum, NULL, ctx->wc_ctx,
- local_abspath, ra_session, revision,
+ local_abspath, ra_session, &opt_rev,
pool));
SVN_ERR(svn_ra_has_capability(ra_session, &server_supports_depth,
@@ -369,18 +415,24 @@ update_internal(svn_revnum_t *result_rev,
dfb.target_revision = revnum;
dfb.anchor_url = anchor_url;
+ SVN_ERR(svn_client__get_inheritable_props(&wcroot_iprops, local_abspath,
+ revnum, depth, ra_session,
+ ctx, pool, pool));
+
/* Fetch the update editor. If REVISION is invalid, that's okay;
the RA driver will call editor->set_target_revision later on. */
- SVN_ERR(svn_wc_get_update_editor4(&update_editor, &update_edit_baton,
+ SVN_ERR(svn_wc__get_update_editor(&update_editor, &update_edit_baton,
&revnum, ctx->wc_ctx, anchor_abspath,
- target, use_commit_times, depth,
- depth_is_sticky, allow_unver_obstructions,
+ target, wcroot_iprops, use_commit_times,
+ depth, depth_is_sticky,
+ allow_unver_obstructions,
adds_as_modification,
server_supports_depth,
clean_checkout,
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,
@@ -388,37 +440,36 @@ update_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_update2(ra_session, &reporter, &report_baton,
+ SVN_ERR(svn_ra_do_update3(ra_session, &reporter, &report_baton,
revnum, target,
(!server_supports_depth || depth_is_sticky
? depth
: svn_depth_unknown),
- FALSE, update_editor, update_edit_baton, pool));
+ FALSE /* send_copyfrom_args */,
+ FALSE /* ignore_ancestry */,
+ update_editor, update_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. */
- 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;
+ 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 update is complete, so that
handling external items (and any errors therefrom) doesn't delay
the primary operation. */
- if (SVN_DEPTH_IS_RECURSIVE(depth) && (! ignore_externals))
+ if ((SVN_DEPTH_IS_RECURSIVE(depth) || cropping_target)
+ && (! ignore_externals))
{
apr_hash_t *new_externals;
apr_hash_t *new_depths;
@@ -429,14 +480,11 @@ update_internal(svn_revnum_t *result_rev,
SVN_ERR(svn_client__handle_externals(new_externals,
new_depths,
- repos_root, local_abspath,
- depth, use_sleep,
+ repos_root_url, local_abspath,
+ depth, timestamp_sleep,
ctx, pool));
}
- if (sleep_here)
- svn_io_sleep_for_timestamps(local_abspath, pool);
-
/* Let everyone know we're finished here (unless we're asked not to). */
if (ctx->notify_func2 && notify_summary)
{
@@ -476,6 +524,8 @@ svn_client__update_internal(svn_revnum_t *result_rev,
const char *anchor_abspath, *lockroot_abspath;
svn_error_t *err;
svn_opt_revision_t peg_revision = *revision;
+ apr_hash_t *conflicted_paths
+ = ctx->conflict_func2 ? apr_hash_make(pool) : NULL;
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
SVN_ERR_ASSERT(! (innerupdate && make_parents));
@@ -516,7 +566,9 @@ svn_client__update_internal(svn_revnum_t *result_rev,
{
const char *missing_parent =
APR_ARRAY_IDX(missing_parents, i, const char *);
- err = update_internal(result_rev, missing_parent, anchor_abspath,
+
+ err = update_internal(result_rev, conflicted_paths,
+ missing_parent, anchor_abspath,
&peg_revision, svn_depth_empty, FALSE,
ignore_externals, allow_unver_obstructions,
adds_as_modification, timestamp_sleep,
@@ -540,11 +592,20 @@ svn_client__update_internal(svn_revnum_t *result_rev,
anchor_abspath = lockroot_abspath;
}
- err = update_internal(result_rev, local_abspath, anchor_abspath,
+ err = update_internal(result_rev, conflicted_paths,
+ local_abspath, anchor_abspath,
&peg_revision, depth, depth_is_sticky,
ignore_externals, allow_unver_obstructions,
adds_as_modification, timestamp_sleep,
TRUE, ctx, pool);
+
+ /* Give the conflict resolver callback the opportunity to
+ * resolve any conflicts that were raised. */
+ if (! err && ctx->conflict_func2)
+ {
+ err = svn_client__resolve_conflicts(NULL, conflicted_paths, ctx, pool);
+ }
+
cleanup:
err = svn_error_compose_create(
err,
@@ -568,9 +629,10 @@ svn_client_update4(apr_array_header_t **result_revs,
apr_pool_t *pool)
{
int i;
- apr_pool_t *subpool = svn_pool_create(pool);
+ apr_pool_t *iterpool = svn_pool_create(pool);
const char *path = NULL;
svn_boolean_t sleep = FALSE;
+ svn_error_t *err = SVN_NO_ERROR;
if (result_revs)
*result_revs = apr_array_make(pool, paths->nelts, sizeof(svn_revnum_t));
@@ -586,17 +648,22 @@ svn_client_update4(apr_array_header_t **result_revs,
for (i = 0; i < paths->nelts; ++i)
{
- svn_error_t *err = SVN_NO_ERROR;
svn_revnum_t result_rev;
const char *local_abspath;
path = APR_ARRAY_IDX(paths, i, const char *);
- svn_pool_clear(subpool);
+ svn_pool_clear(iterpool);
if (ctx->cancel_func)
- SVN_ERR(ctx->cancel_func(ctx->cancel_baton));
+ {
+ err = ctx->cancel_func(ctx->cancel_baton);
+ if (err)
+ goto cleanup;
+ }
- SVN_ERR(svn_dirent_get_absolute(&local_abspath, path, subpool));
+ err = svn_dirent_get_absolute(&local_abspath, path, iterpool);
+ if (err)
+ goto cleanup;
err = svn_client__update_internal(&result_rev, local_abspath,
revision, depth, depth_is_sticky,
ignore_externals,
@@ -604,14 +671,16 @@ svn_client_update4(apr_array_header_t **result_revs,
adds_as_modification,
make_parents,
FALSE, &sleep,
- ctx, subpool);
+ ctx,
+ iterpool);
if (err)
{
- if(err->apr_err != SVN_ERR_WC_NOT_WORKING_COPY)
- return svn_error_trace(err);
+ if (err->apr_err != SVN_ERR_WC_NOT_WORKING_COPY)
+ goto cleanup;
svn_error_clear(err);
+ err = SVN_NO_ERROR;
/* SVN_ERR_WC_NOT_WORKING_COPY: it's not versioned */
@@ -621,17 +690,34 @@ svn_client_update4(apr_array_header_t **result_revs,
svn_wc_notify_t *notify;
notify = svn_wc_create_notify(path,
svn_wc_notify_skip,
- subpool);
- (*ctx->notify_func2)(ctx->notify_baton2, notify, subpool);
+ iterpool);
+ (*ctx->notify_func2)(ctx->notify_baton2, notify, iterpool);
}
}
if (result_revs)
APR_ARRAY_PUSH(*result_revs, svn_revnum_t) = result_rev;
}
+ svn_pool_destroy(iterpool);
- svn_pool_destroy(subpool);
+ cleanup:
if (sleep)
- svn_io_sleep_for_timestamps((paths->nelts == 1) ? path : NULL, pool);
+ {
+ const char *wcroot_abspath;
- return SVN_NO_ERROR;
+ if (paths->nelts == 1)
+ {
+ const char *abspath;
+
+ /* PATH iteslf may have been removed by the update. */
+ SVN_ERR(svn_dirent_get_absolute(&abspath, path, pool));
+ SVN_ERR(svn_wc__get_wcroot(&wcroot_abspath, ctx->wc_ctx, abspath,
+ pool, pool));
+ }
+ else
+ wcroot_abspath = NULL;
+
+ svn_io_sleep_for_timestamps(wcroot_abspath, pool);
+ }
+
+ return svn_error_trace(err);
}