diff options
Diffstat (limited to 'subversion/libsvn_client')
| -rw-r--r-- | subversion/libsvn_client/copy.c | 5 | ||||
| -rw-r--r-- | subversion/libsvn_client/externals.c | 63 | ||||
| -rw-r--r-- | subversion/libsvn_client/log.c | 17 | ||||
| -rw-r--r-- | subversion/libsvn_client/merge.c | 32 | ||||
| -rw-r--r-- | subversion/libsvn_client/patch.c | 54 | ||||
| -rw-r--r-- | subversion/libsvn_client/upgrade.c | 88 |
6 files changed, 240 insertions, 19 deletions
diff --git a/subversion/libsvn_client/copy.c b/subversion/libsvn_client/copy.c index f204bbcf2c4e..1a48b67155b3 100644 --- a/subversion/libsvn_client/copy.c +++ b/subversion/libsvn_client/copy.c @@ -1006,7 +1006,10 @@ repos_to_repos_copy(const apr_array_header_t *copy_pairs, && (relpath != NULL && *relpath != '\0')) { info->resurrection = TRUE; - top_url = svn_uri_dirname(top_url, pool); + top_url = svn_uri_get_longest_ancestor( + top_url, + svn_uri_dirname(pair->dst_abspath_or_url, pool), + pool); SVN_ERR(svn_ra_reparent(ra_session, top_url, pool)); } } diff --git a/subversion/libsvn_client/externals.c b/subversion/libsvn_client/externals.c index 8c08f405279e..52c236c60467 100644 --- a/subversion/libsvn_client/externals.c +++ b/subversion/libsvn_client/externals.c @@ -146,6 +146,7 @@ relegate_dir_external(svn_wc_context_t *wc_ctx, static svn_error_t * switch_dir_external(const char *local_abspath, const char *url, + const char *url_from_externals_definition, const svn_opt_revision_t *peg_revision, const svn_opt_revision_t *revision, const char *defining_abspath, @@ -169,6 +170,46 @@ switch_dir_external(const char *local_abspath, if (revision->kind == svn_opt_revision_number) external_rev = revision->value.number; + /* + * The code below assumes existing versioned paths are *not* part of + * the external's defining working copy. + * The working copy library does not support registering externals + * on top of existing BASE nodes and will error out if we try. + * So if the external target is part of the defining working copy's + * BASE tree, don't attempt to create the external. Doing so would + * leave behind a switched path instead of an external (since the + * switch succeeds but registration of the external in the DB fails). + * The working copy then cannot be updated until the path is switched back. + * See issue #4085. + */ + SVN_ERR(svn_wc__node_get_base(&kind, NULL, NULL, + &repos_root_url, &repos_uuid, + NULL, ctx->wc_ctx, local_abspath, + TRUE, /* ignore_enoent */ + TRUE, /* show hidden */ + pool, pool)); + if (kind != svn_node_unknown) + { + const char *wcroot_abspath; + const char *defining_wcroot_abspath; + + SVN_ERR(svn_wc__get_wcroot(&wcroot_abspath, ctx->wc_ctx, + local_abspath, pool, pool)); + SVN_ERR(svn_wc__get_wcroot(&defining_wcroot_abspath, ctx->wc_ctx, + defining_abspath, pool, pool)); + if (strcmp(wcroot_abspath, defining_wcroot_abspath) == 0) + return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL, + _("The external '%s' defined in %s at '%s' " + "cannot be checked out because '%s' is " + "already a versioned path."), + url_from_externals_definition, + SVN_PROP_EXTERNALS, + svn_dirent_local_style(defining_abspath, + pool), + svn_dirent_local_style(local_abspath, + pool)); + } + /* If path is a directory, try to update/switch to the correct URL and revision. */ SVN_ERR(svn_io_check_path(local_abspath, &kind, pool)); @@ -201,6 +242,20 @@ switch_dir_external(const char *local_abspath, FALSE, TRUE, timestamp_sleep, ctx, subpool)); + + /* We just decided that this existing directory is an external, + so update the external registry with this information, like + when checking out an external */ + SVN_ERR(svn_wc__external_register(ctx->wc_ctx, + defining_abspath, + local_abspath, svn_node_dir, + repos_root_url, repos_uuid, + svn_uri_skip_ancestor(repos_root_url, + url, pool), + external_peg_rev, + external_rev, + pool)); + svn_pool_destroy(subpool); goto cleanup; } @@ -460,7 +515,10 @@ switch_file_external(const char *local_abspath, svn_dirent_split(&dir_abspath, &target, local_abspath, scratch_pool); - /* Open an RA session to 'source' URL */ + /* ### Why do we open a new session? RA_SESSION is a valid + ### session -- the caller used it to call svn_ra_check_path on + ### this very URL, the caller also did the resolving and + ### reparenting that is repeated here. */ SVN_ERR(svn_client__ra_session_from_path2(&ra_session, &switch_loc, url, dir_abspath, peg_revision, revision, @@ -497,7 +555,7 @@ switch_file_external(const char *local_abspath, invalid revnum, that means RA will use the latest revision. */ SVN_ERR(svn_ra_do_switch3(ra_session, &reporter, &report_baton, switch_loc->rev, - target, svn_depth_unknown, url, + target, svn_depth_unknown, switch_loc->url, FALSE /* send_copyfrom */, TRUE /* ignore_ancestry */, switch_editor, switch_baton, @@ -738,6 +796,7 @@ handle_external_item_change(svn_client_ctx_t *ctx, { case svn_node_dir: SVN_ERR(switch_dir_external(local_abspath, new_loc->url, + new_item->url, &(new_item->peg_revision), &(new_item->revision), parent_dir_abspath, diff --git a/subversion/libsvn_client/log.c b/subversion/libsvn_client/log.c index 5bf7e415b725..91961559d255 100644 --- a/subversion/libsvn_client/log.c +++ b/subversion/libsvn_client/log.c @@ -814,10 +814,12 @@ svn_client_log5(const apr_array_header_t *targets, svn_ra_session_t *ra_session; const char *old_session_url; const char *ra_target; + const char *path_or_url; svn_opt_revision_t youngest_opt_rev; svn_revnum_t youngest_rev; svn_revnum_t oldest_rev; svn_opt_revision_t peg_rev; + svn_client__pathrev_t *ra_session_loc; svn_client__pathrev_t *actual_loc; apr_array_header_t *log_segments; apr_array_header_t *revision_ranges; @@ -837,7 +839,7 @@ svn_client_log5(const apr_array_header_t *targets, SVN_ERR(resolve_log_targets(&relative_targets, &ra_target, &peg_rev, targets, ctx, pool, pool)); - SVN_ERR(svn_client__ra_session_from_path2(&ra_session, &actual_loc, + SVN_ERR(svn_client__ra_session_from_path2(&ra_session, &ra_session_loc, ra_target, NULL, &peg_rev, &peg_rev, ctx, pool)); @@ -851,11 +853,22 @@ svn_client_log5(const apr_array_header_t *targets, opt_rev_ranges, &peg_rev, ctx, pool, pool)); + /* For some peg revisions we must resolve revision and url via a local path + so use the original RA_TARGET. For others, use the potentially corrected + (redirected) ra session URL. */ + if (peg_rev.kind == svn_opt_revision_previous || + peg_rev.kind == svn_opt_revision_base || + peg_rev.kind == svn_opt_revision_committed || + peg_rev.kind == svn_opt_revision_working) + path_or_url = ra_target; + else + path_or_url = ra_session_loc->url; + /* Make ACTUAL_LOC and RA_SESSION point to the youngest operative rev. */ youngest_opt_rev.kind = svn_opt_revision_number; youngest_opt_rev.value.number = youngest_rev; SVN_ERR(svn_client__resolve_rev_and_url(&actual_loc, ra_session, - ra_target, &peg_rev, + path_or_url, &peg_rev, &youngest_opt_rev, ctx, pool)); SVN_ERR(svn_client__ensure_ra_session_url(&old_session_url, ra_session, actual_loc->url, pool)); diff --git a/subversion/libsvn_client/merge.c b/subversion/libsvn_client/merge.c index b314e8409b2a..f0ff9a233c84 100644 --- a/subversion/libsvn_client/merge.c +++ b/subversion/libsvn_client/merge.c @@ -1258,13 +1258,14 @@ record_skip(merge_cmd_baton_t *merge_b, svn_node_kind_t kind, svn_wc_notify_action_t action, svn_wc_notify_state_t state, + struct merge_dir_baton_t *pdb, apr_pool_t *scratch_pool) { if (merge_b->record_only) return SVN_NO_ERROR; /* ### Why? - Legacy compatibility */ - if (merge_b->merge_source.ancestral - || merge_b->reintegrate_merge) + if ((merge_b->merge_source.ancestral || merge_b->reintegrate_merge) + && !(pdb && pdb->shadowed)) { store_path(merge_b->skipped_abspaths, local_abspath); } @@ -1979,7 +1980,8 @@ merge_file_changed(const char *relpath, /* We haven't notified for this node yet: report a skip */ SVN_ERR(record_skip(merge_b, local_abspath, svn_node_file, svn_wc_notify_update_shadowed_update, - fb->skip_reason, scratch_pool)); + fb->skip_reason, fb->parent_baton, + scratch_pool)); } return SVN_NO_ERROR; @@ -2148,7 +2150,8 @@ merge_file_added(const char *relpath, /* We haven't notified for this node yet: report a skip */ SVN_ERR(record_skip(merge_b, local_abspath, svn_node_file, svn_wc_notify_update_shadowed_add, - fb->skip_reason, scratch_pool)); + fb->skip_reason, fb->parent_baton, + scratch_pool)); } return SVN_NO_ERROR; @@ -2359,7 +2362,8 @@ merge_file_deleted(const char *relpath, /* We haven't notified for this node yet: report a skip */ SVN_ERR(record_skip(merge_b, local_abspath, svn_node_file, svn_wc_notify_update_shadowed_delete, - fb->skip_reason, scratch_pool)); + fb->skip_reason, fb->parent_baton, + scratch_pool)); } return SVN_NO_ERROR; @@ -2723,6 +2727,12 @@ merge_dir_opened(void **new_dir_baton, /* Set a tree conflict */ db->shadowed = TRUE; db->tree_conflict_reason = svn_wc_conflict_reason_obstructed; + + if ((merge_b->merge_source.ancestral || merge_b->reintegrate_merge) + && !(pdb && pdb->shadowed)) + { + store_path(merge_b->skipped_abspaths, local_abspath); + } } } @@ -2847,7 +2857,8 @@ merge_dir_changed(const char *relpath, /* We haven't notified for this node yet: report a skip */ SVN_ERR(record_skip(merge_b, local_abspath, svn_node_dir, svn_wc_notify_update_shadowed_update, - db->skip_reason, scratch_pool)); + db->skip_reason, db->parent_baton, + scratch_pool)); } return SVN_NO_ERROR; @@ -2931,7 +2942,8 @@ merge_dir_added(const char *relpath, /* We haven't notified for this node yet: report a skip */ SVN_ERR(record_skip(merge_b, local_abspath, svn_node_dir, svn_wc_notify_update_shadowed_add, - db->skip_reason, scratch_pool)); + db->skip_reason, db->parent_baton, + scratch_pool)); } return SVN_NO_ERROR; @@ -3098,7 +3110,8 @@ merge_dir_deleted(const char *relpath, /* We haven't notified for this node yet: report a skip */ SVN_ERR(record_skip(merge_b, local_abspath, svn_node_dir, svn_wc_notify_update_shadowed_delete, - db->skip_reason, scratch_pool)); + db->skip_reason, db->parent_baton, + scratch_pool)); } return SVN_NO_ERROR; @@ -3278,13 +3291,14 @@ merge_node_absent(const char *relpath, apr_pool_t *scratch_pool) { merge_cmd_baton_t *merge_b = processor->baton; + struct merge_dir_baton_t *db = dir_baton; const char *local_abspath = svn_dirent_join(merge_b->target->abspath, relpath, scratch_pool); SVN_ERR(record_skip(merge_b, local_abspath, svn_node_unknown, svn_wc_notify_skip, svn_wc_notify_state_missing, - scratch_pool)); + db, scratch_pool)); return SVN_NO_ERROR; } diff --git a/subversion/libsvn_client/patch.c b/subversion/libsvn_client/patch.c index b965646f985a..b7fbf06145aa 100644 --- a/subversion/libsvn_client/patch.c +++ b/subversion/libsvn_client/patch.c @@ -2057,6 +2057,56 @@ send_patch_notification(const patch_target_t *target, return SVN_NO_ERROR; } +static void +svn_sort__array(apr_array_header_t *array, + int (*comparison_func)(const void *, + const void *)) +{ + qsort(array->elts, array->nelts, array->elt_size, comparison_func); +} + +/* Implements the callback for svn_sort__array. Puts hunks that match + before hunks that do not match, puts hunks that match in order + based on postion matched, puts hunks that do not match in order + based on original position. */ +static int +sort_matched_hunks(const void *a, const void *b) +{ + const hunk_info_t *item1 = *((const hunk_info_t * const *)a); + const hunk_info_t *item2 = *((const hunk_info_t * const *)b); + svn_boolean_t matched1 = !item1->rejected && !item1->already_applied; + svn_boolean_t matched2 = !item2->rejected && !item2->already_applied; + svn_linenum_t original1, original2; + + if (matched1 && matched2) + { + /* Both match so use order matched in file. */ + if (item1->matched_line > item2->matched_line) + return 1; + else if (item1->matched_line == item2->matched_line) + return 0; + else + return -1; + } + else if (matched2) + /* Only second matches, put it before first. */ + return 1; + else if (matched1) + /* Only first matches, put it before second. */ + return -1; + + /* Neither matches, sort by original_start. */ + original1 = svn_diff_hunk_get_original_start(item1->hunk); + original2 = svn_diff_hunk_get_original_start(item2->hunk); + if (original1 > original2) + return 1; + else if (original1 == original2) + return 0; + else + return -1; +} + + /* Apply a PATCH to a working copy at ABS_WC_PATH and put the result * into temporary files, to be installed in the working copy later. * Return information about the patch target in *PATCH_TARGET, allocated @@ -2138,6 +2188,10 @@ apply_one_patch(patch_target_t **patch_target, svn_patch_t *patch, APR_ARRAY_PUSH(target->content->hunks, hunk_info_t *) = hi; } + /* Hunks are applied in the order determined by the matched line and + this may be different from the order of the original lines. */ + svn_sort__array(target->content->hunks, sort_matched_hunks); + /* Apply or reject hunks. */ for (i = 0; i < target->content->hunks->nelts; i++) { diff --git a/subversion/libsvn_client/upgrade.c b/subversion/libsvn_client/upgrade.c index b9f323533ba4..7a696196d37e 100644 --- a/subversion/libsvn_client/upgrade.c +++ b/subversion/libsvn_client/upgrade.c @@ -82,6 +82,14 @@ fetch_repos_info(const char **repos_root, return SVN_NO_ERROR; } +/* Forward definition. Upgrades svn:externals properties in the working copy + LOCAL_ABSPATH to the WC-NG storage. + */ +static svn_error_t * +upgrade_externals_from_properties(svn_client_ctx_t *ctx, + const char *local_abspath, + apr_pool_t *scratch_pool); + svn_error_t * svn_client_upgrade(const char *path, svn_client_ctx_t *ctx, @@ -89,10 +97,6 @@ svn_client_upgrade(const char *path, { const char *local_abspath; apr_hash_t *externals; - apr_hash_index_t *hi; - apr_pool_t *iterpool; - apr_pool_t *iterpool2; - svn_opt_revision_t rev = {svn_opt_revision_unspecified, {0}}; struct repos_info_baton info_baton; info_baton.state_pool = scratch_pool; @@ -111,6 +115,80 @@ svn_client_upgrade(const char *path, ctx->notify_func2, ctx->notify_baton2, scratch_pool)); + SVN_ERR(svn_wc__externals_defined_below(&externals, + ctx->wc_ctx, local_abspath, + scratch_pool, scratch_pool)); + + if (apr_hash_count(externals) > 0) + { + apr_pool_t *iterpool = svn_pool_create(scratch_pool); + apr_hash_index_t *hi; + + /* We are upgrading from >= 1.7. No need to upgrade from + svn:externals properties. And by that avoiding the removal + of recorded externals information (issue #4519) + + Only directory externals need an explicit upgrade */ + for (hi = apr_hash_first(scratch_pool, externals); + hi; + hi = apr_hash_next(hi)) + { + const char *ext_abspath; + svn_node_kind_t kind; + + svn_pool_clear(iterpool); + + ext_abspath = svn__apr_hash_index_key(hi); + + SVN_ERR(svn_wc__read_external_info(&kind, NULL, NULL, NULL, NULL, + ctx->wc_ctx, local_abspath, + ext_abspath, FALSE, + iterpool, iterpool)); + + if (kind == svn_node_dir) + { + svn_error_t *err = svn_client_upgrade(ext_abspath, ctx, iterpool); + + if (err) + { + svn_wc_notify_t *notify = + svn_wc_create_notify(ext_abspath, + svn_wc_notify_failed_external, + iterpool); + notify->err = err; + ctx->notify_func2(ctx->notify_baton2, + notify, iterpool); + svn_error_clear(err); + /* Next external node, please... */ + } + } + } + + svn_pool_destroy(iterpool); + } + else + { + /* Upgrading from <= 1.6, or no svn:properties defined. + (There is no way to detect the difference from libsvn_client :( ) */ + + SVN_ERR(upgrade_externals_from_properties(ctx, local_abspath, + scratch_pool)); + } + return SVN_NO_ERROR; +} + +static svn_error_t * +upgrade_externals_from_properties(svn_client_ctx_t *ctx, + const char *local_abspath, + apr_pool_t *scratch_pool) +{ + apr_hash_index_t *hi; + apr_pool_t *iterpool; + apr_pool_t *iterpool2; + apr_hash_t *externals; + svn_opt_revision_t rev = {svn_opt_revision_unspecified, {0}}; + struct repos_info_baton info_baton; + /* Now it's time to upgrade the externals too. We do it after the wc upgrade to avoid that errors in the externals causes the wc upgrade to fail. Thanks to caching the performance penalty of walking the wc a @@ -163,7 +241,7 @@ svn_client_upgrade(const char *path, iterpool); if (!err) err = svn_wc_parse_externals_description3( - &externals_p, svn_dirent_dirname(path, iterpool), + &externals_p, svn_dirent_dirname(local_abspath, iterpool), external_desc->data, FALSE, iterpool); if (err) { |
