Modified: subversion/branches/multi-wc-format/subversion/libsvn_client/copy.c URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/libsvn_client/copy.c?rev=1897034&r1=1897033&r2=1897034&view=diff ============================================================================== --- subversion/branches/multi-wc-format/subversion/libsvn_client/copy.c (original) +++ subversion/branches/multi-wc-format/subversion/libsvn_client/copy.c Fri Jan 14 14:01:45 2022 @@ -571,14 +571,14 @@ pin_externals_prop(svn_string_t **pinned * mentioned in EXTERNALS_TO_PIN. * The pinning operation takes place as part of the copy operation for * the source/destination pair PAIR. Use RA_SESSION and REPOS_ROOT_URL - * to contact the repository containing the externals definition, if neccesary. + * to contact the repository containing the externals definition, if necessary. * Use CX to fopen additional RA sessions to external repositories, if - * neccessary. Allocate *NEW_EXTERNALS in RESULT_POOL. + * necessary. Allocate *NEW_EXTERNALS in RESULT_POOL. * Use SCRATCH_POOL for temporary allocations. */ static svn_error_t * resolve_pinned_externals(apr_hash_t **pinned_externals, const apr_hash_t *externals_to_pin, - svn_client__copy_pair_t *pair, + const svn_client__copy_pair_t *pair, svn_ra_session_t *ra_session, const char *repos_root_url, svn_client_ctx_t *ctx, @@ -1099,14 +1099,13 @@ verify_wc_dsts(const apr_array_header_t return SVN_NO_ERROR; } +/* Verify that the WC sources in COPY_PAIRS exist, and set pair->src_kind + for each. + */ static svn_error_t * -verify_wc_srcs_and_dsts(const apr_array_header_t *copy_pairs, - svn_boolean_t make_parents, - svn_boolean_t is_move, - svn_boolean_t metadata_only, - svn_client_ctx_t *ctx, - apr_pool_t *result_pool, - apr_pool_t *scratch_pool) +verify_wc_srcs(const apr_array_header_t *copy_pairs, + svn_client_ctx_t *ctx, + apr_pool_t *scratch_pool) { int i; apr_pool_t *iterpool = svn_pool_create(scratch_pool); @@ -1133,10 +1132,6 @@ verify_wc_srcs_and_dsts(const apr_array_ pair->src_abspath_or_url, scratch_pool)); } - - SVN_ERR(verify_wc_dsts(copy_pairs, make_parents, is_move, metadata_only, ctx, - result_pool, iterpool)); - svn_pool_destroy(iterpool); return SVN_NO_ERROR; @@ -1163,10 +1158,6 @@ typedef struct path_driver_info_t or move operation. */ struct path_driver_cb_baton { - /* The editor (and its state) used to perform the operation. */ - const svn_delta_editor_t *editor; - void *edit_baton; - /* A hash of path -> path_driver_info_t *'s. */ apr_hash_t *action_hash; @@ -1176,6 +1167,8 @@ struct path_driver_cb_baton static svn_error_t * path_driver_cb_func(void **dir_baton, + const svn_delta_editor_t *editor, + void *edit_baton, void *parent_baton, void *callback_baton, const char *path, @@ -1196,9 +1189,9 @@ path_driver_cb_func(void **dir_baton, /* Check to see if we need to add the path as a parent directory. */ if (path_info->dir_add) { - return cb_baton->editor->add_directory(path, parent_baton, NULL, - SVN_INVALID_REVNUM, pool, - dir_baton); + return editor->add_directory(path, parent_baton, NULL, + SVN_INVALID_REVNUM, pool, + dir_baton); } /* If this is a resurrection, we know the source and dest paths are @@ -1230,8 +1223,8 @@ path_driver_cb_func(void **dir_baton, if (do_delete) { - SVN_ERR(cb_baton->editor->delete_entry(path, SVN_INVALID_REVNUM, - parent_baton, pool)); + SVN_ERR(editor->delete_entry(path, SVN_INVALID_REVNUM, + parent_baton, pool)); } if (do_add) { @@ -1240,40 +1233,40 @@ path_driver_cb_func(void **dir_baton, if (path_info->src_kind == svn_node_file) { void *file_baton; - SVN_ERR(cb_baton->editor->add_file(path, parent_baton, - path_info->src_url, - path_info->src_revnum, - pool, &file_baton)); + SVN_ERR(editor->add_file(path, parent_baton, + path_info->src_url, + path_info->src_revnum, + pool, &file_baton)); if (path_info->mergeinfo) - SVN_ERR(cb_baton->editor->change_file_prop(file_baton, - SVN_PROP_MERGEINFO, - path_info->mergeinfo, - pool)); - SVN_ERR(cb_baton->editor->close_file(file_baton, NULL, pool)); + SVN_ERR(editor->change_file_prop(file_baton, + SVN_PROP_MERGEINFO, + path_info->mergeinfo, + pool)); + SVN_ERR(editor->close_file(file_baton, NULL, pool)); } else { - SVN_ERR(cb_baton->editor->add_directory(path, parent_baton, - path_info->src_url, - path_info->src_revnum, - pool, dir_baton)); + SVN_ERR(editor->add_directory(path, parent_baton, + path_info->src_url, + path_info->src_revnum, + pool, dir_baton)); if (path_info->mergeinfo) - SVN_ERR(cb_baton->editor->change_dir_prop(*dir_baton, - SVN_PROP_MERGEINFO, - path_info->mergeinfo, - pool)); + SVN_ERR(editor->change_dir_prop(*dir_baton, + SVN_PROP_MERGEINFO, + path_info->mergeinfo, + pool)); } } if (path_info->externals) { if (*dir_baton == NULL) - SVN_ERR(cb_baton->editor->open_directory(path, parent_baton, - SVN_INVALID_REVNUM, - pool, dir_baton)); + SVN_ERR(editor->open_directory(path, parent_baton, + SVN_INVALID_REVNUM, + pool, dir_baton)); - SVN_ERR(cb_baton->editor->change_dir_prop(*dir_baton, SVN_PROP_EXTERNALS, - path_info->externals, pool)); + SVN_ERR(editor->change_dir_prop(*dir_baton, SVN_PROP_EXTERNALS, + path_info->externals, pool)); } return SVN_NO_ERROR; @@ -1857,13 +1850,11 @@ repos_to_repos_copy(const apr_array_head pool)); /* Setup the callback baton. */ - cb_baton.editor = editor; - cb_baton.edit_baton = edit_baton; cb_baton.action_hash = action_hash; cb_baton.is_move = is_move; /* Call the path-based editor driver. */ - err = svn_delta_path_driver2(editor, edit_baton, paths, TRUE, + err = svn_delta_path_driver3(editor, edit_baton, paths, TRUE, path_driver_cb_func, &cb_baton, pool); if (err) { @@ -2318,9 +2309,15 @@ struct notification_adjust_baton }; /* A svn_wc_notify_func2_t function that wraps BATON->inner_func (whose - * baton is BATON->inner_baton) and adjusts the notification paths that - * start with BATON->checkout_abspath to start instead with - * BATON->final_abspath. */ + * baton is BATON->inner_baton) to turn the result of a 'checkout' into + * what we want to see for a 'copy to WC' operation. + * + * - Adjust the notification paths that start with BATON->checkout_abspath + * to start instead with BATON->final_abspath. + * - Change start-of-update notification into a plain WC 'add' for the root. + * - Change checkout 'add' notifications into a plain WC 'add'. + * - Discard 'update_completed' notifications. + */ static void notification_adjust_func(void *baton, const svn_wc_notify_t *notify, @@ -2333,18 +2330,373 @@ notification_adjust_func(void *baton, relpath = svn_dirent_skip_ancestor(nb->checkout_abspath, notify->path); inner_notify->path = svn_dirent_join(nb->final_abspath, relpath, pool); + /* Convert 'update' notifications to plain 'add' notifications; discard + notifications about checkout/update starting/finishing. */ + if (notify->action == svn_wc_notify_update_started /* root */ + || notify->action == svn_wc_notify_update_add) /* non-root */ + { + inner_notify->action = svn_wc_notify_add; + } + else if (notify->action == svn_wc_notify_update_update + || notify->action == svn_wc_notify_update_completed) + { + /* update_update happens only for a prop mod on root; the root was + already notified so discard this */ + return; + } + if (nb->inner_func) nb->inner_func(nb->inner_baton, inner_notify, pool); } -/* Peform each individual copy operation for a repos -> wc copy. A +/** Copy a directory tree from a remote repository. + * + * Copy from RA_SESSION:LOCATION to WC_CTX:DST_ABSPATH. + * + * Create the directory DST_ABSPATH, if not present. Its parent should be + * already under version control in the WC and in a suitable state for + * scheduling the addition of a child. + * + * Ignore any incoming non-regular properties (entry-props, DAV/WC-props). + * Remove any incoming 'svn:mergeinfo' properties. + */ +static svn_error_t * +copy_foreign_dir(svn_ra_session_t *ra_session, + const svn_client__pathrev_t *location, + const char *dst_abspath, + svn_wc_notify_func2_t notify_func, + void *notify_baton, + svn_cancel_func_t cancel_func, + void *cancel_baton, + svn_client_ctx_t *ctx, + apr_pool_t *scratch_pool) +{ + const svn_delta_editor_t *editor; + void *eb; + const svn_delta_editor_t *wrapped_editor; + void *wrapped_baton; + const svn_ra_reporter3_t *reporter; + void *reporter_baton; + + /* Get a WC editor. It does not need an RA session because we will not + be sending it any 'copy from' requests, only 'add' requests. */ + SVN_ERR(svn_client__wc_editor_internal(&editor, &eb, + dst_abspath, + TRUE /*root_dir_add*/, + TRUE /*ignore_mergeinfo_changes*/, + FALSE /*manage_wc_write_lock*/, + notify_func, notify_baton, + NULL /*ra_session*/, + ctx, scratch_pool)); + + SVN_ERR(svn_delta_get_cancellation_editor(cancel_func, cancel_baton, + editor, eb, + &wrapped_editor, &wrapped_baton, + scratch_pool)); + + SVN_ERR(svn_ra_do_update3(ra_session, &reporter, &reporter_baton, + location->rev, "", svn_depth_infinity, + FALSE, FALSE, wrapped_editor, wrapped_baton, + scratch_pool, scratch_pool)); + + SVN_ERR(reporter->set_path(reporter_baton, "", location->rev, + svn_depth_infinity /* irrelevant */, + TRUE /*start_empty*/, + NULL, scratch_pool)); + + SVN_ERR(reporter->finish_report(reporter_baton, scratch_pool)); + + return SVN_NO_ERROR; +} + +/* Implementation of svn_client__repos_to_wc_copy() for a dir. + */ +static svn_error_t * +svn_client__repos_to_wc_copy_dir(svn_boolean_t *timestamp_sleep, + const char *src_url, + svn_revnum_t src_revnum, + const char *dst_abspath, + svn_boolean_t same_repositories, + svn_ra_session_t *ra_session, + svn_client_ctx_t *ctx, + apr_pool_t *scratch_pool) +{ + const char *tmpdir_abspath, *tmp_abspath; + + SVN_ERR_ASSERT(svn_dirent_is_absolute(dst_abspath)); + + if (!same_repositories) + { + svn_client__pathrev_t *location; + + *timestamp_sleep = TRUE; + + /* ### Reparenting "ra_session" can't be right, can it? As this is + a foreign repo, surely we need a new RA session? */ + SVN_ERR(svn_client__pathrev_create_with_session(&location, ra_session, + src_revnum, src_url, + scratch_pool)); + SVN_ERR(svn_ra_reparent(ra_session, src_url, scratch_pool)); + SVN_ERR(copy_foreign_dir(ra_session, location, + dst_abspath, + ctx->notify_func2, ctx->notify_baton2, + ctx->cancel_func, ctx->cancel_baton, + ctx, scratch_pool)); + + return SVN_NO_ERROR; + } + + /* Find a temporary location in which to check out the copy source. */ + SVN_ERR(svn_wc__get_tmpdir(&tmpdir_abspath, ctx->wc_ctx, dst_abspath, + scratch_pool, scratch_pool)); + + /* Get a temporary path. The crude way we do this is to create a + temporary file, remember its name, and let it be deleted immediately. */ + SVN_ERR(svn_io_open_unique_file3(NULL, &tmp_abspath, tmpdir_abspath, + svn_io_file_del_on_close, + scratch_pool, scratch_pool)); + + /* Make a new checkout of the requested source. While doing so, + * resolve copy_src_revnum to an actual revision number in case it + * was until now 'invalid' meaning 'head'. Ask this function not to + * sleep for timestamps, by passing a sleep_needed output param. + * Send notifications for all nodes except the root node, and adjust + * them to refer to the destination rather than this temporary path. */ + { + svn_wc_notify_func2_t old_notify_func2 = ctx->notify_func2; + void *old_notify_baton2 = ctx->notify_baton2; + struct notification_adjust_baton nb; + svn_error_t *err; + svn_opt_revision_t copy_src_revision; + + copy_src_revision.kind = svn_opt_revision_number; + copy_src_revision.value.number = src_revnum; + + nb.inner_func = ctx->notify_func2; + nb.inner_baton = ctx->notify_baton2; + nb.checkout_abspath = tmp_abspath; + nb.final_abspath = dst_abspath; + ctx->notify_func2 = notification_adjust_func; + ctx->notify_baton2 = &nb; + + err = svn_client__checkout_internal(NULL /*result_rev*/, timestamp_sleep, + src_url, + tmp_abspath, + ©_src_revision, + ©_src_revision, + svn_depth_infinity, + TRUE /*ignore_externals*/, + FALSE, /* we don't allow obstructions */ + NULL, /* default WC format */ + ra_session, ctx, scratch_pool); + + ctx->notify_func2 = old_notify_func2; + ctx->notify_baton2 = old_notify_baton2; + + SVN_ERR(err); + } + + /* Schedule dst_path for addition in parent, with copy history. + Don't send any notification here. + Then remove the temporary checkout's .svn dir in preparation for + moving the rest of it into the final destination. */ + SVN_ERR(svn_wc_copy3(ctx->wc_ctx, tmp_abspath, dst_abspath, + TRUE /* metadata_only */, + NULL, NULL, /* don't allow user to cancel here */ + NULL, NULL, scratch_pool)); + SVN_ERR(svn_wc__acquire_write_lock(NULL, ctx->wc_ctx, tmp_abspath, + FALSE, scratch_pool, scratch_pool)); + SVN_ERR(svn_wc_remove_from_revision_control2(ctx->wc_ctx, + tmp_abspath, + FALSE, FALSE, + NULL, NULL, /* don't cancel */ + scratch_pool)); + + /* Move the temporary disk tree into place. */ + SVN_ERR(svn_io_file_rename2(tmp_abspath, dst_abspath, FALSE, scratch_pool)); + + return SVN_NO_ERROR; +} + +/* Implementation of svn_client__repos_to_wc_copy() for a file. + * + * This has no 'ignore_externals' parameter because we don't support the + * 'svn:externals' property being set on a file. + */ +static svn_error_t * +svn_client__repos_to_wc_copy_file(svn_boolean_t *timestamp_sleep, + const char *src_url, + svn_revnum_t src_rev, + const char *dst_abspath, + svn_boolean_t same_repositories, + svn_ra_session_t *ra_session, + svn_client_ctx_t *ctx, + apr_pool_t *scratch_pool) +{ + const char *src_rel; + apr_hash_t *new_props; + svn_stream_t *new_base_contents = svn_stream_buffered(scratch_pool); + + SVN_ERR(svn_ra_get_path_relative_to_session(ra_session, &src_rel, src_url, + scratch_pool)); + /* Fetch the file content. */ + SVN_ERR(svn_ra_get_file(ra_session, src_rel, src_rev, + new_base_contents, NULL, &new_props, + scratch_pool)); + if (!same_repositories) + svn_hash_sets(new_props, SVN_PROP_MERGEINFO, NULL); + + *timestamp_sleep = TRUE; + SVN_ERR(svn_wc_add_repos_file4( + ctx->wc_ctx, dst_abspath, + new_base_contents, NULL, new_props, NULL, + same_repositories ? src_url : NULL, + same_repositories ? src_rev : SVN_INVALID_REVNUM, + ctx->cancel_func, ctx->cancel_baton, + scratch_pool)); + /* Do our own notification for the root node, even if we could possibly + have delegated it. See also issue #2198. */ + if (ctx->notify_func2) + { + svn_wc_notify_t *notify + = svn_wc_create_notify(dst_abspath, svn_wc_notify_add, scratch_pool); + + notify->kind = svn_node_file; + ctx->notify_func2(ctx->notify_baton2, notify, scratch_pool); + } + return SVN_NO_ERROR; +} + +/* Are RA_SESSION and the versioned *parent* dir of WC_TARGET_ABSPATH in + * the same repository? + */ +static svn_error_t * +is_same_repository(svn_boolean_t *same_repository, + svn_ra_session_t *ra_session, + const char *wc_target_abspath, + svn_client_ctx_t *ctx, + apr_pool_t *scratch_pool) +{ + const char *src_uuid, *dst_uuid; + + /* Get the repository UUIDs of copy source URL and WC parent path */ + SVN_ERR(svn_ra_get_uuid2(ra_session, &src_uuid, scratch_pool)); + SVN_ERR(svn_client_get_repos_root(NULL /*root_url*/, &dst_uuid, + svn_dirent_dirname(wc_target_abspath, + scratch_pool), + ctx, scratch_pool, scratch_pool)); + *same_repository = (strcmp(src_uuid, dst_uuid) == 0); + return SVN_NO_ERROR; +} + +svn_error_t * +svn_client__repos_to_wc_copy_internal(svn_boolean_t *timestamp_sleep, + svn_node_kind_t kind, + const char *src_url, + svn_revnum_t src_rev, + const char *dst_abspath, + svn_ra_session_t *ra_session, + svn_client_ctx_t *ctx, + apr_pool_t *scratch_pool) +{ + const char *old_session_url; + svn_boolean_t timestamp_sleep_ignored; + svn_boolean_t same_repositories; + + SVN_ERR(svn_client__ensure_ra_session_url(&old_session_url, ra_session, + src_url, scratch_pool)); + + SVN_ERR(is_same_repository(&same_repositories, + ra_session, dst_abspath, ctx, scratch_pool)); + + if (!timestamp_sleep) + timestamp_sleep = ×tamp_sleep_ignored; + + if (kind == svn_node_dir) + { + SVN_ERR(svn_client__repos_to_wc_copy_dir(timestamp_sleep, + src_url, src_rev, + dst_abspath, + same_repositories, + ra_session, + ctx, scratch_pool)); + } + else if (kind == svn_node_file) + { + SVN_ERR(svn_client__repos_to_wc_copy_file(timestamp_sleep, + src_url, src_rev, + dst_abspath, + same_repositories, + ra_session, + ctx, scratch_pool)); + } + + /* Reparent the session back to the original URL. */ + SVN_ERR(svn_ra_reparent(ra_session, old_session_url, scratch_pool)); + return SVN_NO_ERROR; +} + +svn_error_t * +svn_client__repos_to_wc_copy_by_editor(svn_boolean_t *timestamp_sleep, + svn_node_kind_t kind, + const char *src_url, + svn_revnum_t src_rev, + const char *dst_abspath, + svn_ra_session_t *ra_session, + svn_client_ctx_t *ctx, + apr_pool_t *scratch_pool) +{ + const svn_delta_editor_t *editor; + void *eb; + const char *src_anchor = svn_uri_dirname(src_url, scratch_pool); + const char *dst_target = svn_dirent_basename(dst_abspath, scratch_pool); + void *rb, *db; + + SVN_ERR(svn_ra_reparent(ra_session, src_anchor, scratch_pool)); + + SVN_ERR(svn_client__wc_editor_internal( + &editor, &eb, + svn_dirent_dirname(dst_abspath, scratch_pool), + FALSE /*root_dir_add*/, + FALSE /*ignore_mergeinfo_changes*/, + FALSE /*manage_wc_write_lock*/, + ctx->notify_func2, ctx->notify_baton2, + ra_session, + ctx, scratch_pool)); + + SVN_ERR(editor->open_root(eb, SVN_INVALID_REVNUM, scratch_pool, &rb)); + if (kind == svn_node_dir) + { + SVN_ERR(editor->add_directory(dst_target, rb, + src_url, src_rev, + scratch_pool, + &db)); + SVN_ERR(editor->close_directory(db, scratch_pool)); + } + else + { + SVN_ERR(editor->add_file(dst_target, rb, + src_url, src_rev, + scratch_pool, + &db)); + SVN_ERR(editor->close_file(db, NULL, scratch_pool)); + } + SVN_ERR(editor->close_edit(eb, scratch_pool)); + + if (timestamp_sleep) + *timestamp_sleep = TRUE; + return SVN_NO_ERROR; +} + +/* Perform each individual copy operation for a repos -> wc copy. A helper for repos_to_wc_copy(). - Resolve PAIR->src_revnum to a real revision number if it isn't already. */ + PAIR->src_revnum PAIR->src_abspath_or_url should already have been + resolved to the operative revision number and operative URL. + */ static svn_error_t * repos_to_wc_copy_single(svn_boolean_t *timestamp_sleep, - svn_client__copy_pair_t *pair, - svn_boolean_t same_repositories, + const svn_client__copy_pair_t *pair, svn_boolean_t ignore_externals, svn_boolean_t pin_externals, const apr_hash_t *externals_to_pin, @@ -2354,9 +2706,14 @@ repos_to_wc_copy_single(svn_boolean_t *t { apr_hash_t *src_mergeinfo; const char *dst_abspath = pair->dst_abspath_or_url; + svn_boolean_t same_repositories; + SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(pair->src_revnum)); + SVN_ERR_ASSERT(svn_path_is_url(pair->src_abspath_or_url)); SVN_ERR_ASSERT(svn_dirent_is_absolute(dst_abspath)); + SVN_ERR(is_same_repository(&same_repositories, + ra_session, dst_abspath, ctx, pool)); if (!same_repositories && ctx->notify_func2) { svn_wc_notify_t *notify; @@ -2372,136 +2729,59 @@ repos_to_wc_copy_single(svn_boolean_t *t SVN_ERR(ctx->cancel_func(ctx->cancel_baton)); } - if (pair->src_kind == svn_node_dir) + SVN_ERR(svn_client__repos_to_wc_copy_by_editor( + timestamp_sleep, + pair->src_kind, + pair->src_abspath_or_url, + pair->src_revnum, + dst_abspath, + ra_session, ctx, pool)); + + /* Fetch externals, pinning them if requested */ + if (!ignore_externals && pair->src_kind == svn_node_dir) { if (same_repositories) { - const char *tmpdir_abspath, *tmp_abspath; - - /* Find a temporary location in which to check out the copy source. */ - SVN_ERR(svn_wc__get_tmpdir(&tmpdir_abspath, ctx->wc_ctx, dst_abspath, - pool, pool)); - - SVN_ERR(svn_io_open_unique_file3(NULL, &tmp_abspath, tmpdir_abspath, - svn_io_file_del_on_close, pool, pool)); - - /* Make a new checkout of the requested source. While doing so, - * resolve pair->src_revnum to an actual revision number in case it - * was until now 'invalid' meaning 'head'. Ask this function not to - * sleep for timestamps, by passing a sleep_needed output param. - * Send notifications for all nodes except the root node, and adjust - * them to refer to the destination rather than this temporary path. */ - { - svn_wc_notify_func2_t old_notify_func2 = ctx->notify_func2; - void *old_notify_baton2 = ctx->notify_baton2; - struct notification_adjust_baton nb; - svn_error_t *err; - - nb.inner_func = ctx->notify_func2; - nb.inner_baton = ctx->notify_baton2; - nb.checkout_abspath = tmp_abspath; - nb.final_abspath = dst_abspath; - ctx->notify_func2 = notification_adjust_func; - ctx->notify_baton2 = &nb; - - /* Avoid a chicken-and-egg problem: - * If pinning externals we'll need to adjust externals - * properties before checking out any externals. - * But copy needs to happen before pinning because else there - * are no svn:externals properties to pin. */ - if (pin_externals) - ignore_externals = TRUE; - - err = svn_client__checkout_internal(&pair->src_revnum, timestamp_sleep, - pair->src_original, - tmp_abspath, - &pair->src_peg_revision, - &pair->src_op_revision, - svn_depth_infinity, - ignore_externals, FALSE, - NULL, /* default WC format */ - ra_session, ctx, pool); - - ctx->notify_func2 = old_notify_func2; - ctx->notify_baton2 = old_notify_baton2; - - SVN_ERR(err); - } - - *timestamp_sleep = TRUE; - - /* Schedule dst_path for addition in parent, with copy history. - Don't send any notification here. - Then remove the temporary checkout's .svn dir in preparation for - moving the rest of it into the final destination. */ - SVN_ERR(svn_wc_copy3(ctx->wc_ctx, tmp_abspath, dst_abspath, - TRUE /* metadata_only */, - ctx->cancel_func, ctx->cancel_baton, - NULL, NULL, pool)); - SVN_ERR(svn_wc__acquire_write_lock(NULL, ctx->wc_ctx, tmp_abspath, - FALSE, pool, pool)); - SVN_ERR(svn_wc_remove_from_revision_control2(ctx->wc_ctx, - tmp_abspath, - FALSE, FALSE, - ctx->cancel_func, - ctx->cancel_baton, - pool)); - - /* Move the temporary disk tree into place. */ - SVN_ERR(svn_io_file_rename2(tmp_abspath, dst_abspath, FALSE, pool)); - } - else - { - *timestamp_sleep = TRUE; - - SVN_ERR(svn_client__copy_foreign(pair->src_abspath_or_url, - dst_abspath, - &pair->src_peg_revision, - &pair->src_op_revision, - svn_depth_infinity, - FALSE /* make_parents */, - TRUE /* already_locked */, - ctx, pool)); - - return SVN_NO_ERROR; - } - - if (pin_externals) - { - apr_hash_t *pinned_externals; - apr_hash_index_t *hi; - apr_pool_t *iterpool; const char *repos_root_url; apr_hash_t *new_externals; apr_hash_t *new_depths; SVN_ERR(svn_ra_get_repos_root2(ra_session, &repos_root_url, pool)); - SVN_ERR(resolve_pinned_externals(&pinned_externals, - externals_to_pin, pair, - ra_session, repos_root_url, - ctx, pool, pool)); - iterpool = svn_pool_create(pool); - for (hi = apr_hash_first(pool, pinned_externals); - hi; - hi = apr_hash_next(hi)) + if (pin_externals) { - const char *dst_relpath = apr_hash_this_key(hi); - svn_string_t *externals_propval = apr_hash_this_val(hi); - const char *local_abspath; + apr_hash_t *pinned_externals; + apr_hash_index_t *hi; + apr_pool_t *iterpool; + + SVN_ERR(resolve_pinned_externals(&pinned_externals, + externals_to_pin, pair, + ra_session, repos_root_url, + ctx, pool, pool)); + + iterpool = svn_pool_create(pool); + for (hi = apr_hash_first(pool, pinned_externals); + hi; + hi = apr_hash_next(hi)) + { + const char *dst_relpath = apr_hash_this_key(hi); + svn_string_t *externals_propval = apr_hash_this_val(hi); + const char *local_abspath; - svn_pool_clear(iterpool); + svn_pool_clear(iterpool); - local_abspath = svn_dirent_join(pair->dst_abspath_or_url, - dst_relpath, iterpool); - /* ### use a work queue? */ - SVN_ERR(svn_wc_prop_set4(ctx->wc_ctx, local_abspath, - SVN_PROP_EXTERNALS, externals_propval, - svn_depth_empty, TRUE /* skip_checks */, - NULL /* changelist_filter */, - ctx->cancel_func, ctx->cancel_baton, - NULL, NULL, /* no extra notification */ - iterpool)); + local_abspath = svn_dirent_join(pair->dst_abspath_or_url, + dst_relpath, iterpool); + /* ### use a work queue? */ + SVN_ERR(svn_wc_prop_set4(ctx->wc_ctx, local_abspath, + SVN_PROP_EXTERNALS, externals_propval, + svn_depth_empty, TRUE /* skip_checks */, + NULL /* changelist_filter */, + ctx->cancel_func, ctx->cancel_baton, + NULL, NULL, /* no extra notification */ + iterpool)); + } + svn_pool_destroy(iterpool); } /* Now update all externals in the newly created copy. */ @@ -2510,65 +2790,30 @@ repos_to_wc_copy_single(svn_boolean_t *t ctx->wc_ctx, dst_abspath, svn_depth_infinity, - iterpool, iterpool)); + pool, pool)); SVN_ERR(svn_client__handle_externals(new_externals, new_depths, repos_root_url, dst_abspath, svn_depth_infinity, timestamp_sleep, ra_session, - ctx, iterpool)); - svn_pool_destroy(iterpool); + ctx, pool)); } - } /* end directory case */ + } - else if (pair->src_kind == svn_node_file) + if (same_repositories) { - apr_hash_t *new_props; - const char *src_rel; - svn_stream_t *new_base_contents = svn_stream_buffered(pool); - - SVN_ERR(svn_ra_get_path_relative_to_session(ra_session, &src_rel, - pair->src_abspath_or_url, - pool)); - /* Fetch the file content. While doing so, resolve pair->src_revnum - * to an actual revision number if it's 'invalid' meaning 'head'. */ - SVN_ERR(svn_ra_get_file(ra_session, src_rel, pair->src_revnum, - new_base_contents, - &pair->src_revnum, &new_props, pool)); - - if (new_props && ! same_repositories) - svn_hash_sets(new_props, SVN_PROP_MERGEINFO, NULL); + /* Record the implied mergeinfo. */ + SVN_ERR(svn_client__get_repos_mergeinfo(&src_mergeinfo, ra_session, + pair->src_abspath_or_url, + pair->src_revnum, + svn_mergeinfo_inherited, + TRUE /*squelch_incapable*/, + pool)); + SVN_ERR(extend_wc_mergeinfo(dst_abspath, src_mergeinfo, ctx, pool)); - *timestamp_sleep = TRUE; - - SVN_ERR(svn_wc_add_repos_file4( - ctx->wc_ctx, dst_abspath, - new_base_contents, NULL, new_props, NULL, - same_repositories ? pair->src_abspath_or_url : NULL, - same_repositories ? pair->src_revnum : SVN_INVALID_REVNUM, - ctx->cancel_func, ctx->cancel_baton, - pool)); - } - - /* Record the implied mergeinfo (before the notification callback - is invoked for the root node). */ - SVN_ERR(svn_client__get_repos_mergeinfo( - &src_mergeinfo, ra_session, - pair->src_abspath_or_url, pair->src_revnum, - svn_mergeinfo_inherited, TRUE /*squelch_incapable*/, pool)); - SVN_ERR(extend_wc_mergeinfo(dst_abspath, src_mergeinfo, ctx, pool)); - - /* Do our own notification for the root node, even if we could possibly - have delegated it. See also issue #1552. - - ### Maybe this notification should mention the mergeinfo change. */ - if (ctx->notify_func2) - { - svn_wc_notify_t *notify = svn_wc_create_notify( - dst_abspath, svn_wc_notify_add, pool); - notify->kind = pair->src_kind; - ctx->notify_func2(ctx->notify_baton2, notify, pool); + /* ### Maybe the notification should mention this mergeinfo change. */ + /* ### Maybe we should do this during rather than after the copy. */ } return SVN_NO_ERROR; @@ -2586,38 +2831,8 @@ repos_to_wc_copy_locked(svn_boolean_t *t apr_pool_t *scratch_pool) { int i; - svn_boolean_t same_repositories; apr_pool_t *iterpool = svn_pool_create(scratch_pool); - /* We've already checked for physical obstruction by a working file. - But there could also be logical obstruction by an entry whose - working file happens to be missing.*/ - SVN_ERR(verify_wc_dsts(copy_pairs, FALSE, FALSE, FALSE /* metadata_only */, - ctx, scratch_pool, iterpool)); - - /* Decide whether the two repositories are the same or not. */ - { - const char *parent_abspath; - const char *src_uuid, *dst_uuid; - - /* Get the repository uuid of SRC_URL */ - SVN_ERR(svn_ra_get_uuid2(ra_session, &src_uuid, iterpool)); - - /* Get repository uuid of dst's parent directory, since dst may - not exist. ### TODO: we should probably walk up the wc here, - in case the parent dir has an imaginary URL. */ - if (copy_pairs->nelts == 1) - parent_abspath = svn_dirent_dirname(top_dst_abspath, scratch_pool); - else - parent_abspath = top_dst_abspath; - - SVN_ERR(svn_client_get_repos_root(NULL /* root_url */, &dst_uuid, - parent_abspath, ctx, - iterpool, iterpool)); - /* ### Also check repos_root_url? */ - same_repositories = (strcmp(src_uuid, dst_uuid) == 0); - } - /* Perform the move for each of the copy_pairs. */ for (i = 0; i < copy_pairs->nelts; i++) { @@ -2630,7 +2845,6 @@ repos_to_wc_copy_locked(svn_boolean_t *t SVN_ERR(repos_to_wc_copy_single(timestamp_sleep, APR_ARRAY_IDX(copy_pairs, i, svn_client__copy_pair_t *), - same_repositories, ignore_externals, pin_externals, externals_to_pin, ra_session, ctx, iterpool)); @@ -2643,7 +2857,6 @@ repos_to_wc_copy_locked(svn_boolean_t *t static svn_error_t * repos_to_wc_copy(svn_boolean_t *timestamp_sleep, const apr_array_header_t *copy_pairs, - svn_boolean_t make_parents, svn_boolean_t ignore_externals, svn_boolean_t pin_externals, const apr_hash_t *externals_to_pin, @@ -2697,8 +2910,6 @@ repos_to_wc_copy(svn_boolean_t *timestam { svn_client__copy_pair_t *pair = APR_ARRAY_IDX(copy_pairs, i, svn_client__copy_pair_t *); - svn_node_kind_t dst_parent_kind, dst_kind; - const char *dst_parent; const char *src_rel; svn_pool_clear(iterpool); @@ -2722,43 +2933,6 @@ repos_to_wc_copy(svn_boolean_t *timestam _("Path '%s' not found in head revision"), pair->src_abspath_or_url); } - - /* Figure out about dst. */ - SVN_ERR(svn_io_check_path(pair->dst_abspath_or_url, &dst_kind, - iterpool)); - if (dst_kind != svn_node_none) - { - return svn_error_createf( - SVN_ERR_ENTRY_EXISTS, NULL, - _("Path '%s' already exists"), - svn_dirent_local_style(pair->dst_abspath_or_url, pool)); - } - - /* Make sure the destination parent is a directory and produce a clear - error message if it is not. */ - dst_parent = svn_dirent_dirname(pair->dst_abspath_or_url, iterpool); - SVN_ERR(svn_io_check_path(dst_parent, &dst_parent_kind, iterpool)); - if (make_parents && dst_parent_kind == svn_node_none) - { - SVN_ERR(svn_client__make_local_parents(dst_parent, TRUE, ctx, - iterpool)); - } - else if (make_parents && dst_parent_kind == svn_node_dir) - { - SVN_ERR(svn_wc_read_kind2(&dst_parent_kind, ctx->wc_ctx, dst_parent, - FALSE, TRUE, iterpool)); - if (dst_parent_kind == svn_node_none) - { - SVN_ERR(svn_client__make_local_parents(dst_parent, TRUE, ctx, - iterpool)); - } - } - else if (dst_parent_kind != svn_node_dir) - { - return svn_error_createf(SVN_ERR_WC_NOT_WORKING_COPY, NULL, - _("Path '%s' is not a directory"), - svn_dirent_local_style(dst_parent, pool)); - } } svn_pool_destroy(iterpool); @@ -3081,8 +3255,9 @@ try_copy(svn_boolean_t *timestamp_sleep, /* Now, call the right handler for the operation. */ if ((! srcs_are_urls) && (! dst_is_url)) { - SVN_ERR(verify_wc_srcs_and_dsts(copy_pairs, make_parents, is_move, - metadata_only, ctx, pool, pool)); + SVN_ERR(verify_wc_srcs(copy_pairs, ctx, pool)); + SVN_ERR(verify_wc_dsts(copy_pairs, make_parents, is_move, metadata_only, + ctx, pool, pool)); /* Copy or move all targets. */ if (is_move) @@ -3112,9 +3287,13 @@ try_copy(svn_boolean_t *timestamp_sleep, } else if ((srcs_are_urls) && (! dst_is_url)) { + SVN_ERR(verify_wc_dsts(copy_pairs, make_parents, + FALSE, FALSE /* metadata_only */, + ctx, pool, pool)); + return svn_error_trace( repos_to_wc_copy(timestamp_sleep, - copy_pairs, make_parents, ignore_externals, + copy_pairs, ignore_externals, pin_externals, externals_to_pin, ctx, pool)); } else
Modified: subversion/branches/multi-wc-format/subversion/libsvn_client/delete.c URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/libsvn_client/delete.c?rev=1897034&r1=1897033&r2=1897034&view=diff ============================================================================== --- subversion/branches/multi-wc-format/subversion/libsvn_client/delete.c (original) +++ subversion/branches/multi-wc-format/subversion/libsvn_client/delete.c Fri Jan 14 14:01:45 2022 @@ -181,12 +181,13 @@ can_delete_node(svn_boolean_t *target_mi static svn_error_t * path_driver_cb_func(void **dir_baton, + const svn_delta_editor_t *editor, + void *edit_baton, void *parent_baton, void *callback_baton, const char *path, apr_pool_t *pool) { - const svn_delta_editor_t *editor = callback_baton; *dir_baton = NULL; return editor->delete_entry(path, SVN_INVALID_REVNUM, parent_baton, pool); } @@ -248,8 +249,8 @@ single_repos_delete(svn_ra_session_t *ra pool)); /* Call the path-based editor driver. */ - err = svn_delta_path_driver2(editor, edit_baton, relpaths, TRUE, - path_driver_cb_func, (void *)editor, pool); + err = svn_delta_path_driver3(editor, edit_baton, relpaths, TRUE, + path_driver_cb_func, NULL, pool); if (err) { Modified: subversion/branches/multi-wc-format/subversion/libsvn_client/deprecated.c URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/libsvn_client/deprecated.c?rev=1897034&r1=1897033&r2=1897034&view=diff ============================================================================== --- subversion/branches/multi-wc-format/subversion/libsvn_client/deprecated.c (original) +++ subversion/branches/multi-wc-format/subversion/libsvn_client/deprecated.c Fri Jan 14 14:01:45 2022 @@ -166,6 +166,61 @@ svn_client_mkdir(svn_client_commit_info_ } /*** From blame.c ***/ +struct blame_receiver_wrapper_baton3 { + void *baton; + svn_client_blame_receiver3_t receiver; + svn_revnum_t start_revnum; + svn_revnum_t end_revnum; +}; + +static svn_error_t * +blame_wrapper_receiver3(void *baton, + apr_int64_t line_no, + svn_revnum_t revision, + apr_hash_t *rev_props, + svn_revnum_t merged_revision, + apr_hash_t *merged_rev_props, + const char *merged_path, + const svn_string_t *line, + svn_boolean_t local_change, + apr_pool_t *pool) +{ + struct blame_receiver_wrapper_baton3 *brwb = baton; + + if (brwb->receiver) + return brwb->receiver(brwb->baton, brwb->start_revnum, brwb->end_revnum, + line_no, + revision, rev_props, merged_revision, + merged_rev_props, merged_path, line->data, + local_change, pool); + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_client_blame5(const char *target, + const svn_opt_revision_t *peg_revision, + const svn_opt_revision_t *start, + const svn_opt_revision_t *end, + const svn_diff_file_options_t *diff_options, + svn_boolean_t ignore_mime_type, + svn_boolean_t include_merged_revisions, + svn_client_blame_receiver3_t receiver, + void *receiver_baton, + svn_client_ctx_t *ctx, + apr_pool_t *pool) +{ + struct blame_receiver_wrapper_baton3 baton; + + baton.receiver = receiver; + baton.baton = receiver_baton; + + return svn_client_blame6(&baton.start_revnum, &baton.end_revnum, + target, peg_revision, start, end, + diff_options, + ignore_mime_type, include_merged_revisions, + blame_wrapper_receiver3, &baton, ctx, pool); +} struct blame_receiver_wrapper_baton2 { void *baton; Modified: subversion/branches/multi-wc-format/subversion/libsvn_client/diff.c URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/libsvn_client/diff.c?rev=1897034&r1=1897033&r2=1897034&view=diff ============================================================================== --- subversion/branches/multi-wc-format/subversion/libsvn_client/diff.c (original) +++ subversion/branches/multi-wc-format/subversion/libsvn_client/diff.c Fri Jan 14 14:01:45 2022 @@ -51,6 +51,7 @@ #include "svn_subst.h" #include "client.h" +#include "private/svn_client_shelf.h" #include "private/svn_wc_private.h" #include "private/svn_diff_private.h" #include "private/svn_subr_private.h" @@ -587,7 +588,7 @@ print_diff_index_header(svn_stream_t *ou ### FIXME needs proper docstring - If USE_GIT_DIFF_FORMAT is TRUE, pring git diff headers, which always + If USE_GIT_DIFF_FORMAT is TRUE, print git diff headers, which always show paths relative to the repository root. DDI->session_relpath and DDI->wc_ctx are needed to normalize paths relative the repository root, and are ignored if USE_GIT_DIFF_FORMAT is FALSE. @@ -1439,7 +1440,7 @@ diff_dir_deleted(const char *relpath, With only one distinct revision the working copy provides the other. When path is a URL there is no working copy. Thus - 1: compare repository versions for URL coresponding to working copy + 1: compare repository versions for URL corresponding to working copy 2: compare working copy against repository version 3: compare repository versions for URL 4: nothing to do. @@ -1661,7 +1662,7 @@ diff_prepare_repos_repos(const char **ur { /* It would be nice if we could just return an error when resolving a location fails... But in many such cases we prefer diffing against - an not existing location to show adds od removes (see issue #4153) */ + a non-existent location to show adds or removes (see issue #4153) */ if (resolved2 && (peg_kind != svn_opt_revision_unspecified @@ -1687,7 +1688,7 @@ diff_prepare_repos_repos(const char **ur { /* It would be nice if we could just return an error when resolving a location fails... But in many such cases we prefer diffing against - an not existing location to show adds od removes (see issue #4153) */ + a non-existent location to show adds or removes (see issue #4153) */ if (resolved1 && (peg_kind != svn_opt_revision_unspecified @@ -1893,7 +1894,7 @@ diff_wc_wc(const char *path1, for the underlying diff implementation if the target on either side is a file, else at the actual requested targets. - (The choice of WC anchor implementated here for DDI->anchor appears to + (The choice of WC anchor implemented here for DDI->anchor appears to be: choose PATH_OR_URL2 (if it's a WC path) or else PATH_OR_URL1 (if it's a WC path); then take its parent dir unless both resolved URLs refer to directories.) Modified: subversion/branches/multi-wc-format/subversion/libsvn_client/export.c URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/libsvn_client/export.c?rev=1897034&r1=1897033&r2=1897034&view=diff ============================================================================== --- subversion/branches/multi-wc-format/subversion/libsvn_client/export.c (original) +++ subversion/branches/multi-wc-format/subversion/libsvn_client/export.c Fri Jan 14 14:01:45 2022 @@ -182,16 +182,18 @@ export_node(void *baton, { struct export_info_baton *eib = baton; svn_wc_context_t *wc_ctx = eib->wc_ctx; - apr_hash_t *kw = NULL; + apr_hash_t *kw; svn_subst_eol_style_t style; apr_hash_t *props; svn_string_t *eol_style, *keywords, *executable, *special; - const char *eol = NULL; + const char *eol_style_val; + const char *eol; svn_boolean_t local_mod = FALSE; apr_time_t tm; svn_stream_t *source; svn_stream_t *dst_stream; - const char *dst_tmp; + const char *tmp_abspath; + svn_wc__working_file_writer_t *file_writer; svn_error_t *err; const char *to_abspath = svn_dirent_join( @@ -268,7 +270,7 @@ export_node(void *baton, } /* Skip file externals if they are a descendant of the export, - BUT NOT if we are explictly exporting the file external. */ + BUT NOT if we are explicitly exporting the file external. */ if (status->file_external && strcmp(eib->origin_abspath, local_abspath) != 0) return SVN_NO_ERROR; @@ -340,26 +342,17 @@ export_node(void *baton, local_mod = TRUE; } - /* We can early-exit if we're creating a special file. */ special = svn_hash_gets(props, SVN_PROP_SPECIAL); - if (special != NULL) - { - /* Create the destination as a special file, and copy the source - details into the destination stream. */ - /* ### And forget the notification */ - SVN_ERR(svn_subst_create_specialfile(&dst_stream, to_abspath, - scratch_pool, scratch_pool)); - return svn_error_trace( - svn_stream_copy3(source, dst_stream, NULL, NULL, scratch_pool)); - } - - eol_style = svn_hash_gets(props, SVN_PROP_EOL_STYLE); keywords = svn_hash_gets(props, SVN_PROP_KEYWORDS); executable = svn_hash_gets(props, SVN_PROP_EXECUTABLE); if (eol_style) - SVN_ERR(get_eol_style(&style, &eol, eol_style->data, eib->native_eol)); + eol_style_val = eol_style->data; + else + eol_style_val = NULL; + + SVN_ERR(get_eol_style(&style, &eol, eol_style_val, eib->native_eol)); if (local_mod) { @@ -372,7 +365,7 @@ export_node(void *baton, tm = status->changed_date; } - if (keywords) + if (keywords && !eib->ignore_keywords) { svn_revnum_t changed_rev = status->changed_rev; const char *suffix; @@ -400,39 +393,33 @@ export_node(void *baton, url, status->repos_root_url, tm, author, scratch_pool)); } + else + { + kw = NULL; + } - /* For atomicity, we translate to a tmp file and then rename the tmp file - over the real destination. */ - SVN_ERR(svn_stream_open_unique(&dst_stream, &dst_tmp, - svn_dirent_dirname(to_abspath, scratch_pool), - svn_io_file_del_none, scratch_pool, - scratch_pool)); - - /* If some translation is needed, then wrap the output stream (this is - more efficient than wrapping the input). */ - if (eol || (kw && (apr_hash_count(kw) > 0))) - dst_stream = svn_subst_stream_translated(dst_stream, - eol, - FALSE /* repair */, - kw, - ! eib->ignore_keywords /* expand */, - scratch_pool); + tmp_abspath = svn_dirent_dirname(to_abspath, scratch_pool); + SVN_ERR(svn_wc__working_file_writer_open(&file_writer, + tmp_abspath, + tm, + style, + eol, + FALSE /* repair_eol */, + kw, + special != NULL, + executable != NULL, + FALSE /* is_readonly */, + scratch_pool, + scratch_pool)); /* ###: use cancel func/baton in place of NULL/NULL below. */ - err = svn_stream_copy3(source, dst_stream, NULL, NULL, scratch_pool); - - if (!err && executable) - err = svn_io_set_file_executable(dst_tmp, TRUE, FALSE, scratch_pool); + dst_stream = svn_wc__working_file_writer_get_stream(file_writer); + SVN_ERR(svn_stream_copy3(source, dst_stream, NULL, NULL, scratch_pool)); - if (!err) - err = svn_io_set_file_affected_time(tm, dst_tmp, scratch_pool); - - if (err) - return svn_error_compose_create(err, svn_io_remove_file2(dst_tmp, FALSE, - scratch_pool)); - - /* Now that dst_tmp contains the translated data, do the atomic rename. */ - SVN_ERR(svn_io_file_rename2(dst_tmp, to_abspath, FALSE, scratch_pool)); + SVN_ERR(svn_wc__working_file_writer_finalize(NULL, NULL, file_writer, + scratch_pool)); + SVN_ERR(svn_wc__working_file_writer_install(file_writer, to_abspath, + scratch_pool)); if (eib->notify_func) { @@ -526,11 +513,8 @@ struct file_baton struct edit_baton *edit_baton; const char *path; - const char *tmppath; - - /* We need to keep this around so we can explicitly close it in close_file, - thus flushing its output to disk so we can copy and translate it. */ - svn_stream_t *tmp_stream; + /* The writer for the file being exported. */ + svn_wc__working_file_writer_t *file_writer; /* The MD5 digest of the file's fulltext. This is all zeros until the last textdelta window handler call returns. */ @@ -558,8 +542,6 @@ struct handler_baton { svn_txdelta_window_handler_t apply_handler; void *apply_baton; - apr_pool_t *pool; - const char *tmppath; }; @@ -679,23 +661,74 @@ static svn_error_t * window_handler(svn_txdelta_window_t *window, void *baton) { struct handler_baton *hb = baton; - svn_error_t *err; - err = hb->apply_handler(window, hb->apply_baton); - if (err) + SVN_ERR(hb->apply_handler(window, hb->apply_baton)); + + return SVN_NO_ERROR; +} + + +/* Create the writer for the file being exported based on the + state in the file baton FB. */ +static svn_error_t * +open_working_file_writer(svn_wc__working_file_writer_t **writer_p, + struct file_baton *fb, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + const char *eol_style_val; + svn_subst_eol_style_t eol_style; + const char *eol; + apr_hash_t *keywords; + apr_time_t final_mtime; + const char *tmp_path; + const char *tmp_abspath; + + if (fb->eol_style_val) + eol_style_val = fb->eol_style_val->data; + else + eol_style_val = NULL; + + SVN_ERR(get_eol_style(&eol_style, &eol, eol_style_val, + fb->edit_baton->native_eol)); + + if (fb->keywords_val) { - /* We failed to apply the patch; clean up the temporary file. */ - err = svn_error_compose_create( - err, - svn_io_remove_file2(hb->tmppath, TRUE, hb->pool)); + SVN_ERR(svn_subst_build_keywords3(&keywords, fb->keywords_val->data, + fb->revision, fb->url, + fb->repos_root_url, fb->date, + fb->author, scratch_pool)); + } + else + { + keywords = NULL; } - return svn_error_trace(err); -} + if (fb->date) + final_mtime = fb->date; + else + final_mtime = -1; + /* Create a temporary file in the same directory as the file. */ + tmp_path = svn_dirent_dirname(fb->path, scratch_pool); + SVN_ERR(svn_dirent_get_absolute(&tmp_abspath, tmp_path, scratch_pool)); + SVN_ERR(svn_wc__working_file_writer_open(writer_p, + tmp_abspath, + final_mtime, + eol_style, + eol, + TRUE /* repair_eol */, + keywords, + fb->special, + fb->executable_val != NULL, + FALSE /* is_readonly */, + result_pool, + scratch_pool)); + return SVN_NO_ERROR; +} -/* Write incoming data into the tmpfile stream */ +/* Write incoming data into the file writer */ static svn_error_t * apply_textdelta(void *file_baton, const char *base_checksum, @@ -706,21 +739,10 @@ apply_textdelta(void *file_baton, struct file_baton *fb = file_baton; struct handler_baton *hb = apr_palloc(pool, sizeof(*hb)); - /* Create a temporary file in the same directory as the file. We're going - to rename the thing into place when we're done. */ - SVN_ERR(svn_stream_open_unique(&fb->tmp_stream, &fb->tmppath, - svn_dirent_dirname(fb->path, pool), - svn_io_file_del_none, fb->pool, fb->pool)); - - hb->pool = pool; - hb->tmppath = fb->tmppath; - - /* svn_txdelta_apply() closes the stream, but we want to close it in the - close_file() function, so disown it here. */ - /* ### contrast to when we call svn_ra_get_file() which does NOT close the - ### tmp_stream. we *should* be much more consistent! */ + SVN_ERR(open_working_file_writer(&fb->file_writer, fb, fb->pool, pool)); + svn_txdelta_apply(svn_stream_empty(pool), - svn_stream_disown(fb->tmp_stream, pool), + svn_wc__working_file_writer_get_stream(fb->file_writer), fb->text_digest, NULL, pool, &hb->apply_handler, &hb->apply_baton); @@ -785,23 +807,21 @@ change_dir_prop(void *dir_baton, } -/* Move the tmpfile to file, and send feedback. */ +/* Install the file, and send feedback. */ static svn_error_t * close_file(void *file_baton, const char *text_digest, apr_pool_t *pool) { struct file_baton *fb = file_baton; - struct edit_baton *eb = fb->edit_baton; svn_checksum_t *text_checksum; svn_checksum_t *actual_checksum; + const char *target_abspath; /* Was a txdelta even sent? */ - if (! fb->tmppath) + if (! fb->file_writer) return SVN_NO_ERROR; - SVN_ERR(svn_stream_close(fb->tmp_stream)); - SVN_ERR(svn_checksum_parse_hex(&text_checksum, svn_checksum_md5, text_digest, pool)); actual_checksum = svn_checksum__from_digest_md5(fb->text_digest, pool); @@ -814,45 +834,12 @@ close_file(void *file_baton, _("Checksum mismatch for '%s'"), svn_dirent_local_style(fb->path, pool)); - if ((! fb->eol_style_val) && (! fb->keywords_val) && (! fb->special)) - { - SVN_ERR(svn_io_file_rename2(fb->tmppath, fb->path, FALSE, pool)); - } - else - { - svn_subst_eol_style_t style; - const char *eol = NULL; - svn_boolean_t repair = FALSE; - apr_hash_t *final_kw = NULL; + SVN_ERR(svn_dirent_get_absolute(&target_abspath, fb->path, pool)); - if (fb->eol_style_val) - { - SVN_ERR(get_eol_style(&style, &eol, fb->eol_style_val->data, - eb->native_eol)); - repair = TRUE; - } - - if (fb->keywords_val) - SVN_ERR(svn_subst_build_keywords3(&final_kw, fb->keywords_val->data, - fb->revision, fb->url, - fb->repos_root_url, fb->date, - fb->author, pool)); - - SVN_ERR(svn_subst_copy_and_translate4(fb->tmppath, fb->path, - eol, repair, final_kw, - TRUE, /* expand */ - fb->special, - eb->cancel_func, eb->cancel_baton, - pool)); - - SVN_ERR(svn_io_remove_file2(fb->tmppath, FALSE, pool)); - } - - if (fb->executable_val) - SVN_ERR(svn_io_set_file_executable(fb->path, TRUE, FALSE, pool)); - - if (fb->date && (! fb->special)) - SVN_ERR(svn_io_set_file_affected_time(fb->date, fb->path, pool)); + SVN_ERR(svn_wc__working_file_writer_finalize(NULL, NULL, fb->file_writer, + pool)); + SVN_ERR(svn_wc__working_file_writer_install(fb->file_writer, target_abspath, + pool)); if (fb->edit_baton->notify_func) { @@ -1212,6 +1199,8 @@ export_file(const char *from_url, apr_hash_index_t *hi; struct file_baton *fb = apr_pcalloc(scratch_pool, sizeof(*fb)); svn_node_kind_t to_kind; + svn_revnum_t target_rev; + svn_stream_t *stream; SVN_ERR_ASSERT(svn_path_is_url(from_url)); @@ -1251,18 +1240,20 @@ export_file(const char *from_url, fb->pool = scratch_pool; fb->repos_root_url = eb->repos_root_url; - /* Copied from apply_textdelta(). */ - SVN_ERR(svn_stream_open_unique(&fb->tmp_stream, &fb->tmppath, - svn_dirent_dirname(fb->path, scratch_pool), - svn_io_file_del_none, - fb->pool, fb->pool)); - - /* Step outside the editor-likeness for a moment, to actually talk - * to the repository. */ - /* ### note: the stream will not be closed */ - SVN_ERR(svn_ra_get_file(ra_session, "", loc->rev, - fb->tmp_stream, - NULL, &props, scratch_pool)); + /* Grab some properties we need to know in order to figure out if anything + special needs to be done with this file. */ + target_rev = loc->rev; + if (SVN_IS_VALID_REVNUM(target_rev)) + { + SVN_ERR(svn_ra_get_file(ra_session, "", target_rev, NULL, NULL, + &props, scratch_pool)); + } + else + { + /* For HEAD, fetch the actual revision and use in subsequent calls. */ + SVN_ERR(svn_ra_get_file(ra_session, "", SVN_INVALID_REVNUM, NULL, + &target_rev, &props, scratch_pool)); + } /* Push the props into change_file_prop(), to update the file_baton * with information. */ @@ -1274,8 +1265,16 @@ export_file(const char *from_url, SVN_ERR(change_file_prop(fb, propname, propval, scratch_pool)); } - /* And now just use close_file() to do all the keyword and EOL - * work, and put the file into place. */ + /* Step outside the editor-likeness for a moment, to open the file writer + * and to actually talk to the repository. */ + SVN_ERR(open_working_file_writer(&fb->file_writer, fb, fb->pool, + scratch_pool)); + stream = svn_wc__working_file_writer_get_stream(fb->file_writer); + SVN_ERR(svn_ra_get_file(ra_session, "", target_rev, stream, NULL, NULL, + scratch_pool)); + SVN_ERR(svn_stream_close(stream)); + + /* And now just use close_file() to put the file into place. */ SVN_ERR(close_file(fb, NULL, scratch_pool)); return SVN_NO_ERROR; Modified: subversion/branches/multi-wc-format/subversion/libsvn_client/import.c URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/libsvn_client/import.c?rev=1897034&r1=1897033&r2=1897034&view=diff ============================================================================== --- subversion/branches/multi-wc-format/subversion/libsvn_client/import.c (original) +++ subversion/branches/multi-wc-format/subversion/libsvn_client/import.c Fri Jan 14 14:01:45 2022 @@ -609,7 +609,7 @@ import_dir(const svn_delta_editor_t *edi * EDIT_BATON. LOCAL_ABSPATH can be a file or directory. * * Sets *UPDATED_REPOSITORY to TRUE when the repository was modified by - * a successfull commit, otherwise to FALSE. + * a successful commit, otherwise to FALSE. * * DEPTH is the depth at which to import LOCAL_ABSPATH; it behaves as for * svn_client_import5(). Modified: subversion/branches/multi-wc-format/subversion/libsvn_client/info.c URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/libsvn_client/info.c?rev=1897034&r1=1897033&r2=1897034&view=diff ============================================================================== --- subversion/branches/multi-wc-format/subversion/libsvn_client/info.c (original) +++ subversion/branches/multi-wc-format/subversion/libsvn_client/info.c Fri Jan 14 14:01:45 2022 @@ -167,7 +167,8 @@ build_info_from_dirent(svn_client_info2_ #define DIRENT_FIELDS (SVN_DIRENT_KIND | \ SVN_DIRENT_CREATED_REV | \ SVN_DIRENT_TIME | \ - SVN_DIRENT_LAST_AUTHOR) + SVN_DIRENT_LAST_AUTHOR | \ + SVN_DIRENT_SIZE) /* Helper func for recursively fetching svn_dirent_t's from a remote @@ -267,6 +268,7 @@ same_resource_in_head(svn_boolean_t *sam ctx, pool); if (err && ((err->apr_err == SVN_ERR_CLIENT_UNRELATED_RESOURCES) || + (err->apr_err == SVN_ERR_FS_NOT_DIRECTORY) || (err->apr_err == SVN_ERR_FS_NOT_FOUND))) { svn_error_clear(err);
