diff options
Diffstat (limited to 'subversion/libsvn_wc/update_editor.c')
-rw-r--r-- | subversion/libsvn_wc/update_editor.c | 1205 |
1 files changed, 637 insertions, 568 deletions
diff --git a/subversion/libsvn_wc/update_editor.c b/subversion/libsvn_wc/update_editor.c index fd3e9ca..4dca3af 100644 --- a/subversion/libsvn_wc/update_editor.c +++ b/subversion/libsvn_wc/update_editor.c @@ -211,7 +211,7 @@ struct edit_baton /* If this is a 'switch' operation, the new relpath of target_abspath, else NULL. */ - const char *switch_relpath; + const char *switch_repos_relpath; /* The URL to the root of the repository. */ const char *repos_root; @@ -258,6 +258,9 @@ struct edit_baton /* Absolute path of the working copy root or NULL if not initialized yet */ const char *wcroot_abspath; + /* After closing the root directory a copy of its edited value */ + svn_boolean_t edited; + apr_pool_t *pool; }; @@ -295,7 +298,7 @@ struct dir_baton const char *local_abspath; /* The repository relative path this directory will correspond to. */ - const char *new_relpath; + const char *new_repos_relpath; /* The revision of the directory before updating */ svn_revnum_t old_revision; @@ -342,9 +345,10 @@ struct dir_baton and reinstall it. */ apr_hash_t *deletion_conflicts; - /* A hash of file names (only the hash key matters) seen by add_file - and not yet added to the database by close_file. */ - apr_hash_t *not_present_files; + /* A hash of file names (only the hash key matters) seen by add_file and + add_directory and not yet added to the database, mapping to a const + char * node kind (via svn_node_kind_to_word(). */ + apr_hash_t *not_present_nodes; /* Set if an unversioned dir of the same name already existed in this directory. */ @@ -398,7 +402,7 @@ struct handler_baton struct file_baton *fb; /* Where we are assembling the new file. */ - const char *new_text_base_tmp_abspath; + svn_wc__db_install_data_t *install_data; /* The expected source checksum of the text source or NULL if no base checksum is available (MD5 if the server provides a checksum, SHA1 if @@ -412,7 +416,7 @@ struct handler_baton provide a sha1, so we may not have to calculate both, but for the time being, that's the way it is. */ - /* The calculated checksum of the text source or NULL if the acual + /* The calculated checksum of the text source or NULL if the actual checksum is not being calculated. The checksum kind is identical to the kind of expected_source_checksum. */ svn_checksum_t *actual_source_checksum; @@ -486,47 +490,20 @@ cleanup_edit_baton(void *edit_baton) return APR_SUCCESS; } -/* Make a new dir baton in a subpool of PB->pool. PB is the parent baton. - If PATH and PB are NULL, this is the root directory of the edit; in this - case, make the new dir baton in a subpool of EB->pool. - ADDING should be TRUE if we are adding this directory. */ +/* Calculate the new repos_relpath for a directory or file */ static svn_error_t * -make_dir_baton(struct dir_baton **d_p, - const char *path, - struct edit_baton *eb, - struct dir_baton *pb, - svn_boolean_t adding, - apr_pool_t *scratch_pool) +calculate_repos_relpath(const char **new_repos_relpath, + const char *local_abspath, + const char *old_repos_relpath, + struct edit_baton *eb, + struct dir_baton *pb, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) { - apr_pool_t *dir_pool; - struct dir_baton *d; - - if (pb != NULL) - dir_pool = svn_pool_create(pb->pool); - else - dir_pool = svn_pool_create(eb->pool); - - SVN_ERR_ASSERT(path || (! pb)); - - /* Okay, no easy out, so allocate and initialize a dir baton. */ - d = apr_pcalloc(dir_pool, sizeof(*d)); + const char *name = svn_dirent_basename(local_abspath, NULL); - /* Construct the PATH and baseNAME of this directory. */ - if (path) - { - d->name = svn_dirent_basename(path, dir_pool); - SVN_ERR(path_join_under_root(&d->local_abspath, - pb->local_abspath, d->name, dir_pool)); - } - else - { - /* This is the root baton. */ - d->name = NULL; - d->local_abspath = eb->anchor_abspath; - } - - /* Figure out the new_relpath for this directory. */ - if (eb->switch_relpath) + /* Figure out the new_repos_relpath for this directory. */ + if (eb->switch_repos_relpath) { /* Handle switches... */ @@ -535,18 +512,16 @@ make_dir_baton(struct dir_baton **d_p, if (*eb->target_basename == '\0') { /* No parent baton and target_basename=="" means that we are - the target of the switch. Thus, our NEW_RELPATH will be - the SWITCH_RELPATH. */ - d->new_relpath = eb->switch_relpath; + the target of the switch. Thus, our new_repos_relpath will be + the switch_repos_relpath. */ + *new_repos_relpath = eb->switch_repos_relpath; } else { /* This node is NOT the target of the switch (one of our children is the target); therefore, it must already exist. Get its old REPOS_RELPATH, as it won't be changing. */ - SVN_ERR(svn_wc__db_scan_base_repos(&d->new_relpath, NULL, NULL, - eb->db, d->local_abspath, - dir_pool, scratch_pool)); + *new_repos_relpath = apr_pstrdup(result_pool, old_repos_relpath); } } else @@ -554,36 +529,76 @@ make_dir_baton(struct dir_baton **d_p, /* This directory is *not* the root (has a parent). If there is no grandparent, then we may have anchored at the parent, and self is the target. If we match the target, then set - NEW_RELPATH to the SWITCH_RELPATH. + new_repos_relpath to the switch_repos_relpath. + + Otherwise, we simply extend new_repos_relpath from the parent. */ - Otherwise, we simply extend NEW_RELPATH from the parent. */ if (pb->parent_baton == NULL - && strcmp(eb->target_basename, d->name) == 0) - d->new_relpath = eb->switch_relpath; + && strcmp(eb->target_basename, name) == 0) + *new_repos_relpath = eb->switch_repos_relpath; else - d->new_relpath = svn_relpath_join(pb->new_relpath, d->name, - dir_pool); + *new_repos_relpath = svn_relpath_join(pb->new_repos_relpath, name, + result_pool); } } else /* must be an update */ { /* If we are adding the node, then simply extend the parent's relpath for our own. */ - if (adding) + if (old_repos_relpath == NULL) { SVN_ERR_ASSERT(pb != NULL); - d->new_relpath = svn_relpath_join(pb->new_relpath, d->name, - dir_pool); + *new_repos_relpath = svn_relpath_join(pb->new_repos_relpath, name, + result_pool); } else { - SVN_ERR(svn_wc__db_scan_base_repos(&d->new_relpath, NULL, NULL, - eb->db, d->local_abspath, - dir_pool, scratch_pool)); - SVN_ERR_ASSERT(d->new_relpath); + *new_repos_relpath = apr_pstrdup(result_pool, old_repos_relpath); } } + return SVN_NO_ERROR; +} + +/* Make a new dir baton in a subpool of PB->pool. PB is the parent baton. + If PATH and PB are NULL, this is the root directory of the edit; in this + case, make the new dir baton in a subpool of EB->pool. + ADDING should be TRUE if we are adding this directory. */ +static svn_error_t * +make_dir_baton(struct dir_baton **d_p, + const char *path, + struct edit_baton *eb, + struct dir_baton *pb, + svn_boolean_t adding, + apr_pool_t *scratch_pool) +{ + apr_pool_t *dir_pool; + struct dir_baton *d; + + if (pb != NULL) + dir_pool = svn_pool_create(pb->pool); + else + dir_pool = svn_pool_create(eb->pool); + + SVN_ERR_ASSERT(path || (! pb)); + + /* Okay, no easy out, so allocate and initialize a dir baton. */ + d = apr_pcalloc(dir_pool, sizeof(*d)); + + /* Construct the PATH and baseNAME of this directory. */ + if (path) + { + d->name = svn_dirent_basename(path, dir_pool); + SVN_ERR(path_join_under_root(&d->local_abspath, + pb->local_abspath, d->name, dir_pool)); + } + else + { + /* This is the root baton. */ + d->name = NULL; + d->local_abspath = eb->anchor_abspath; + } + d->edit_baton = eb; d->parent_baton = pb; d->pool = dir_pool; @@ -594,7 +609,7 @@ make_dir_baton(struct dir_baton **d_p, d->old_revision = SVN_INVALID_REVNUM; d->adding_dir = adding; d->changed_rev = SVN_INVALID_REVNUM; - d->not_present_files = apr_hash_make(dir_pool); + d->not_present_nodes = apr_hash_make(dir_pool); /* Copy some flags from the parent baton */ if (pb) @@ -614,7 +629,6 @@ make_dir_baton(struct dir_baton **d_p, return SVN_NO_ERROR; } - /* Forward declarations. */ static svn_error_t * already_in_a_tree_conflict(svn_boolean_t *conflicted, @@ -680,7 +694,7 @@ struct file_baton const char *local_abspath; /* The repository relative path this file will correspond to. */ - const char *new_relpath; + const char *new_repos_relpath; /* The revision of the file before updating */ svn_revnum_t old_revision; @@ -764,7 +778,6 @@ make_file_baton(struct file_baton **f_p, svn_boolean_t adding, apr_pool_t *scratch_pool) { - struct edit_baton *eb = pb->edit_baton; apr_pool_t *file_pool = svn_pool_create(pb->pool); struct file_baton *f = apr_pcalloc(file_pool, sizeof(*f)); @@ -776,37 +789,6 @@ make_file_baton(struct file_baton **f_p, SVN_ERR(path_join_under_root(&f->local_abspath, pb->local_abspath, f->name, file_pool)); - /* Figure out the new URL for this file. */ - if (eb->switch_relpath) - { - /* Handle switches... */ - - /* This file has a parent directory. If there is - no grandparent, then we may have anchored at the parent, - and self is the target. If we match the target, then set - NEW_RELPATH to the SWITCH_RELPATH. - - Otherwise, we simply extend NEW_RELPATH from the parent. */ - if (pb->parent_baton == NULL - && strcmp(eb->target_basename, f->name) == 0) - f->new_relpath = eb->switch_relpath; - else - f->new_relpath = svn_relpath_join(pb->new_relpath, f->name, - file_pool); - } - else /* must be an update */ - { - if (adding) - f->new_relpath = svn_relpath_join(pb->new_relpath, f->name, file_pool); - else - { - SVN_ERR(svn_wc__db_scan_base_repos(&f->new_relpath, NULL, NULL, - eb->db, f->local_abspath, - file_pool, scratch_pool)); - SVN_ERR_ASSERT(f->new_relpath); - } - } - f->pool = file_pool; f->edit_baton = pb->edit_baton; f->propchanges = apr_array_make(file_pool, 1, sizeof(svn_prop_t)); @@ -843,13 +825,16 @@ complete_conflict(svn_skel_t *conflict, const char *new_repos_relpath, svn_node_kind_t local_kind, svn_node_kind_t target_kind, + const svn_skel_t *delete_conflict, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { - svn_wc_conflict_version_t *original_version; + const svn_wc_conflict_version_t *original_version = NULL; svn_wc_conflict_version_t *target_version; svn_boolean_t is_complete; + SVN_ERR_ASSERT(new_repos_relpath); + if (!conflict) return SVN_NO_ERROR; /* Not conflicted */ @@ -865,20 +850,30 @@ complete_conflict(svn_skel_t *conflict, old_revision, local_kind, result_pool); - else - original_version = NULL; + else if (delete_conflict) + { + const apr_array_header_t *locations; - if (new_repos_relpath) - target_version = svn_wc_conflict_version_create2(eb->repos_root, - eb->repos_uuid, - new_repos_relpath, - *eb->target_revision, - target_kind, - result_pool); - else - target_version = NULL; + SVN_ERR(svn_wc__conflict_read_info(NULL, &locations, NULL, NULL, NULL, + eb->db, local_abspath, + delete_conflict, + scratch_pool, scratch_pool)); + + if (locations) + { + original_version = APR_ARRAY_IDX(locations, 0, + const svn_wc_conflict_version_t *); + } + } - if (eb->switch_relpath) + target_version = svn_wc_conflict_version_create2(eb->repos_root, + eb->repos_uuid, + new_repos_relpath, + *eb->target_revision, + target_kind, + result_pool); + + if (eb->switch_repos_relpath) SVN_ERR(svn_wc__conflict_skel_set_op_switch(conflict, original_version, target_version, @@ -913,8 +908,9 @@ mark_directory_edited(struct dir_baton *db, apr_pool_t *scratch_pool) SVN_ERR(complete_conflict(db->edit_conflict, db->edit_baton, db->local_abspath, db->old_repos_relpath, db->old_revision, - db->new_relpath, + db->new_repos_relpath, svn_node_dir, svn_node_dir, + NULL, db->pool, scratch_pool)); SVN_ERR(svn_wc__db_op_mark_conflict(db->edit_baton->db, db->local_abspath, @@ -924,7 +920,6 @@ mark_directory_edited(struct dir_baton *db, apr_pool_t *scratch_pool) do_notification(db->edit_baton, db->local_abspath, svn_node_dir, svn_wc_notify_tree_conflict, scratch_pool); db->already_notified = TRUE; - } return SVN_NO_ERROR; @@ -948,8 +943,9 @@ mark_file_edited(struct file_baton *fb, apr_pool_t *scratch_pool) SVN_ERR(complete_conflict(fb->edit_conflict, fb->edit_baton, fb->local_abspath, fb->old_repos_relpath, - fb->old_revision, fb->new_relpath, + fb->old_revision, fb->new_repos_relpath, svn_node_file, svn_node_file, + NULL, fb->pool, scratch_pool)); SVN_ERR(svn_wc__db_op_mark_conflict(fb->edit_baton->db, @@ -974,7 +970,6 @@ window_handler(svn_txdelta_window_t *window, void *baton) { struct handler_baton *hb = baton; struct file_baton *fb = hb->fb; - svn_wc__db_t *db = fb->edit_baton->db; svn_error_t *err; /* Apply this window. We may be done at that point. */ @@ -1014,10 +1009,10 @@ window_handler(svn_txdelta_window_t *window, void *baton) { /* We failed to apply the delta; clean up the temporary file if it already created by lazy_open_target(). */ - if (hb->new_text_base_tmp_abspath) + if (hb->install_data) { - svn_error_clear(svn_io_remove_file2(hb->new_text_base_tmp_abspath, - TRUE, hb->pool)); + svn_error_clear(svn_wc__db_pristine_install_abort(hb->install_data, + hb->pool)); } } else @@ -1031,7 +1026,7 @@ window_handler(svn_txdelta_window_t *window, void *baton) /* Store the new pristine text in the pristine store now. Later, in a single transaction we will update the BASE_NODE to include a reference to this pristine text's checksum. */ - SVN_ERR(svn_wc__db_pristine_install(db, hb->new_text_base_tmp_abspath, + SVN_ERR(svn_wc__db_pristine_install(hb->install_data, fb->new_text_base_sha1_checksum, fb->new_text_base_md5_checksum, hb->pool)); @@ -1154,17 +1149,6 @@ set_target_revision(void *edit_baton, return SVN_NO_ERROR; } -static svn_error_t * -check_tree_conflict(svn_skel_t **pconflict, - struct edit_baton *eb, - const char *local_abspath, - svn_wc__db_status_t working_status, - svn_boolean_t exists_in_repos, - svn_node_kind_t expected_kind, - svn_wc_conflict_action_t action, - apr_pool_t *result_pool, - apr_pool_t *scratch_pool); - /* An svn_delta_editor_t function. */ static svn_error_t * open_root(void *edit_baton, @@ -1228,10 +1212,27 @@ open_root(void *edit_baton, eb->db, db->local_abspath, db->pool, pool)); - if (conflict_ignored) + if (have_work) { - db->shadowed = TRUE; + SVN_ERR(svn_wc__db_base_get_info(&base_status, NULL, + &db->old_revision, + &db->old_repos_relpath, NULL, NULL, + &db->changed_rev, &db->changed_date, + &db->changed_author, + &db->ambient_depth, + NULL, NULL, NULL, NULL, NULL, NULL, + eb->db, db->local_abspath, + db->pool, pool)); } + else + base_status = status; + + SVN_ERR(calculate_repos_relpath(&db->new_repos_relpath, db->local_abspath, + db->old_repos_relpath, eb, NULL, + db->pool, pool)); + + if (conflict_ignored) + db->shadowed = TRUE; else if (have_work) { const char *move_src_root_abspath; @@ -1239,16 +1240,6 @@ open_root(void *edit_baton, SVN_ERR(svn_wc__db_base_moved_to(NULL, NULL, &move_src_root_abspath, NULL, eb->db, db->local_abspath, pool, pool)); - if (move_src_root_abspath || *eb->target_basename == '\0') - SVN_ERR(svn_wc__db_base_get_info(&base_status, NULL, - &db->old_revision, - &db->old_repos_relpath, NULL, NULL, - &db->changed_rev, &db->changed_date, - &db->changed_author, - &db->ambient_depth, - NULL, NULL, NULL, NULL, NULL, NULL, - eb->db, db->local_abspath, - db->pool, pool)); if (move_src_root_abspath) { @@ -1271,9 +1262,10 @@ open_root(void *edit_baton, SVN_ERR(complete_conflict(tree_conflict, eb, move_src_root_abspath, db->old_repos_relpath, - db->old_revision, db->new_relpath, + db->old_revision, + db->new_repos_relpath, svn_node_dir, svn_node_dir, - pool, pool)); + NULL, pool, pool)); SVN_ERR(svn_wc__db_op_mark_conflict(eb->db, move_src_root_abspath, tree_conflict, @@ -1288,8 +1280,6 @@ open_root(void *edit_baton, db->shadowed = TRUE; /* Needed for the close_directory() on the root, to make sure it doesn't use the ACTUAL tree */ } - else - base_status = status; if (*eb->target_basename == '\0') { @@ -1308,7 +1298,7 @@ open_root(void *edit_baton, SVN_ERR(svn_wc__db_temp_op_start_directory_update(eb->db, db->local_abspath, - db->new_relpath, + db->new_repos_relpath, *eb->target_revision, pool)); } @@ -1320,99 +1310,6 @@ open_root(void *edit_baton, /* ===================================================================== */ /* Checking for local modifications. */ -/* A baton for use with modcheck_found_entry(). */ -typedef struct modcheck_baton_t { - svn_wc__db_t *db; /* wc_db to access nodes */ - svn_boolean_t found_mod; /* whether a modification has been found */ - svn_boolean_t found_not_delete; /* Found a not-delete modification */ -} modcheck_baton_t; - -/* An implementation of svn_wc_status_func4_t. */ -static svn_error_t * -modcheck_callback(void *baton, - const char *local_abspath, - const svn_wc_status3_t *status, - apr_pool_t *scratch_pool) -{ - modcheck_baton_t *mb = baton; - - switch (status->node_status) - { - case svn_wc_status_normal: - case svn_wc_status_incomplete: - case svn_wc_status_ignored: - case svn_wc_status_none: - case svn_wc_status_unversioned: - case svn_wc_status_external: - break; - - case svn_wc_status_deleted: - mb->found_mod = TRUE; - break; - - case svn_wc_status_missing: - case svn_wc_status_obstructed: - mb->found_mod = TRUE; - mb->found_not_delete = TRUE; - /* Exit from the status walker: We know what we want to know */ - return svn_error_create(SVN_ERR_CEASE_INVOCATION, NULL, NULL); - - default: - case svn_wc_status_added: - case svn_wc_status_replaced: - case svn_wc_status_modified: - mb->found_mod = TRUE; - mb->found_not_delete = TRUE; - /* Exit from the status walker: We know what we want to know */ - return svn_error_create(SVN_ERR_CEASE_INVOCATION, NULL, NULL); - } - - return SVN_NO_ERROR; -} - - -/* Set *MODIFIED to true iff there are any local modifications within the - * tree rooted at LOCAL_ABSPATH, using DB. If *MODIFIED - * is set to true and all the local modifications were deletes then set - * *ALL_EDITS_ARE_DELETES to true, set it to false otherwise. LOCAL_ABSPATH - * may be a file or a directory. */ -svn_error_t * -svn_wc__node_has_local_mods(svn_boolean_t *modified, - svn_boolean_t *all_edits_are_deletes, - svn_wc__db_t *db, - const char *local_abspath, - svn_cancel_func_t cancel_func, - void *cancel_baton, - apr_pool_t *scratch_pool) -{ - modcheck_baton_t modcheck_baton = { NULL, FALSE, FALSE }; - svn_error_t *err; - - modcheck_baton.db = db; - - /* Walk the WC tree for status with depth infinity, looking for any local - * modifications. If it's a "sparse" directory, that's OK: there can be - * no local mods in the pieces that aren't present in the WC. */ - - err = svn_wc__internal_walk_status(db, local_abspath, - svn_depth_infinity, - FALSE, FALSE, FALSE, NULL, - modcheck_callback, &modcheck_baton, - cancel_func, cancel_baton, - scratch_pool); - - if (err && err->apr_err == SVN_ERR_CEASE_INVOCATION) - svn_error_clear(err); - else - SVN_ERR(err); - - *modified = modcheck_baton.found_mod; - *all_edits_are_deletes = (modcheck_baton.found_mod - && !modcheck_baton.found_not_delete); - - return SVN_NO_ERROR; -} - /* Indicates an unset svn_wc_conflict_reason_t. */ #define SVN_WC_CONFLICT_REASON_NONE (svn_wc_conflict_reason_t)(-1) @@ -1447,7 +1344,6 @@ check_tree_conflict(svn_skel_t **pconflict, { svn_wc_conflict_reason_t reason = SVN_WC_CONFLICT_REASON_NONE; svn_boolean_t modified = FALSE; - svn_boolean_t all_mods_are_deletes = FALSE; const char *move_src_op_root_abspath = NULL; *pconflict = NULL; @@ -1486,15 +1382,15 @@ check_tree_conflict(svn_skel_t **pconflict, } else { - /* The node is locally replaced but could also be moved-away. */ - SVN_ERR(svn_wc__db_base_moved_to(NULL, NULL, NULL, - &move_src_op_root_abspath, - eb->db, local_abspath, - scratch_pool, scratch_pool)); - if (move_src_op_root_abspath) - reason = svn_wc_conflict_reason_moved_away; - else - reason = svn_wc_conflict_reason_replaced; + /* The node is locally replaced but could also be moved-away, + but we can't report that it is moved away and replaced. + + And we wouldn't be able to store that each of a dozen + descendants was moved to other locations... + + Replaced is what actually happened... */ + + reason = svn_wc_conflict_reason_replaced; } break; @@ -1557,14 +1453,14 @@ check_tree_conflict(svn_skel_t **pconflict, * not visit the subdirectories of a directory that it wants to delete. * Therefore, we need to start a separate crawl here. */ - SVN_ERR(svn_wc__node_has_local_mods(&modified, &all_mods_are_deletes, - eb->db, local_abspath, + SVN_ERR(svn_wc__node_has_local_mods(&modified, NULL, + eb->db, local_abspath, FALSE, eb->cancel_func, eb->cancel_baton, scratch_pool)); if (modified) { - if (all_mods_are_deletes) + if (working_status == svn_wc__db_status_deleted) reason = svn_wc_conflict_reason_deleted; else reason = svn_wc_conflict_reason_edited; @@ -1711,7 +1607,8 @@ delete_entry(const char *path, const char *base = svn_relpath_basename(path, NULL); const char *local_abspath; const char *repos_relpath; - svn_node_kind_t kind, base_kind; + const char *deleted_repos_relpath; + svn_node_kind_t kind; svn_revnum_t old_revision; svn_boolean_t conflicted; svn_boolean_t have_work; @@ -1721,8 +1618,6 @@ delete_entry(const char *path, apr_pool_t *scratch_pool; svn_boolean_t deleting_target; svn_boolean_t deleting_switched; - svn_boolean_t keep_as_working = FALSE; - svn_boolean_t queue_deletes = TRUE; if (pb->skip_this) return SVN_NO_ERROR; @@ -1740,6 +1635,7 @@ delete_entry(const char *path, { svn_boolean_t is_root; + SVN_ERR(svn_wc__db_is_wcroot(&is_root, eb->db, local_abspath, scratch_pool)); @@ -1767,10 +1663,9 @@ delete_entry(const char *path, if (!have_work) { base_status = status; - base_kind = kind; } else - SVN_ERR(svn_wc__db_base_get_info(&base_status, &base_kind, &old_revision, + SVN_ERR(svn_wc__db_base_get_info(&base_status, NULL, &old_revision, &repos_relpath, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, @@ -1815,11 +1710,9 @@ delete_entry(const char *path, || base_status == svn_wc__db_status_excluded || base_status == svn_wc__db_status_server_excluded) { - SVN_ERR(svn_wc__db_base_remove(eb->db, local_abspath, - FALSE /* keep_as_working */, - FALSE /* queue_deletes */, - FALSE /* remove_locks */, - SVN_INVALID_REVNUM /* not_present_rev */, + SVN_ERR(svn_wc__db_base_remove(eb->db, local_abspath, TRUE, + deleting_target, FALSE, + *eb->target_revision, NULL, NULL, scratch_pool)); @@ -1842,18 +1735,13 @@ delete_entry(const char *path, { SVN_ERR(check_tree_conflict(&tree_conflict, eb, local_abspath, status, TRUE, - (kind == svn_node_dir) - ? svn_node_dir - : svn_node_file, + kind, svn_wc_conflict_action_delete, pb->pool, scratch_pool)); } - else - queue_deletes = FALSE; /* There is no in-wc representation */ if (tree_conflict != NULL) { - svn_wc_conflict_reason_t reason; /* When we raise a tree conflict on a node, we don't want to mark the * node as skipped, to allow a replacement to continue doing at least * a bit of its work (possibly adding a not present node, for the @@ -1864,45 +1752,19 @@ delete_entry(const char *path, svn_hash_sets(pb->deletion_conflicts, apr_pstrdup(pb->pool, base), tree_conflict); - SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, NULL, NULL, - eb->db, local_abspath, - tree_conflict, - scratch_pool, scratch_pool)); - - if (reason == svn_wc_conflict_reason_edited - || reason == svn_wc_conflict_reason_obstructed) - { - /* The item exists locally and has some sort of local mod. - * It no longer exists in the repository at its target URL@REV. - * - * To prepare the "accept mine" resolution for the tree conflict, - * we must schedule the existing content for re-addition as a copy - * of what it was, but with its local modifications preserved. */ - keep_as_working = TRUE; - - /* Fall through to remove the BASE_NODEs properly, with potentially - keeping a not-present marker */ - } - else if (reason == svn_wc_conflict_reason_deleted - || reason == svn_wc_conflict_reason_moved_away - || reason == svn_wc_conflict_reason_replaced) - { - /* The item does not exist locally because it was already shadowed. - * We must complete the deletion, leaving the tree conflict info - * as the only difference from a normal deletion. */ - - /* Fall through to the normal "delete" code path. */ - } - else - SVN_ERR_MALFUNCTION(); /* other reasons are not expected here */ + /* Whatever the kind of conflict, we can just clear BASE + by turning whatever is there into a copy */ } + /* Calculate the repository-relative path of the entry which was + * deleted. For updates it's the same as REPOS_RELPATH but for + * switches it is within the switch target. */ + SVN_ERR(calculate_repos_relpath(&deleted_repos_relpath, local_abspath, + repos_relpath, eb, pb, scratch_pool, + scratch_pool)); SVN_ERR(complete_conflict(tree_conflict, eb, local_abspath, repos_relpath, - old_revision, NULL, - (kind == svn_node_dir) - ? svn_node_dir - : svn_node_file, - svn_node_none, + old_revision, deleted_repos_relpath, + kind, svn_node_none, NULL, pb->pool, scratch_pool)); /* Issue a wq operation to delete the BASE_NODE data and to delete actual @@ -1917,7 +1779,8 @@ delete_entry(const char *path, { /* Delete, and do not leave a not-present node. */ SVN_ERR(svn_wc__db_base_remove(eb->db, local_abspath, - keep_as_working, queue_deletes, FALSE, + (tree_conflict != NULL), + FALSE, FALSE, SVN_INVALID_REVNUM /* not_present_rev */, tree_conflict, NULL, scratch_pool)); @@ -1926,7 +1789,8 @@ delete_entry(const char *path, { /* Delete, leaving a not-present node. */ SVN_ERR(svn_wc__db_base_remove(eb->db, local_abspath, - keep_as_working, queue_deletes, FALSE, + (tree_conflict != NULL), + TRUE, FALSE, *eb->target_revision, tree_conflict, NULL, scratch_pool)); @@ -1948,6 +1812,7 @@ delete_entry(const char *path, { if (eb->conflict_func) SVN_ERR(svn_wc__conflict_invoke_resolver(eb->db, local_abspath, + kind, tree_conflict, NULL /* merge_options */, eb->conflict_func, @@ -1955,23 +1820,17 @@ delete_entry(const char *path, eb->cancel_func, eb->cancel_baton, scratch_pool)); - do_notification(eb, local_abspath, svn_node_unknown, + do_notification(eb, local_abspath, kind, svn_wc_notify_tree_conflict, scratch_pool); } else { svn_wc_notify_action_t action = svn_wc_notify_update_delete; - svn_node_kind_t node_kind; if (pb->shadowed || pb->edit_obstructed) action = svn_wc_notify_update_shadowed_delete; - if (kind == svn_node_dir) - node_kind = svn_node_dir; - else - node_kind = svn_node_file; - - do_notification(eb, local_abspath, node_kind, action, scratch_pool); + do_notification(eb, local_abspath, kind, action, scratch_pool); } svn_pool_destroy(scratch_pool); @@ -1991,6 +1850,7 @@ add_directory(const char *path, struct dir_baton *pb = parent_baton; struct edit_baton *eb = pb->edit_baton; struct dir_baton *db; + apr_pool_t *scratch_pool = svn_pool_create(pool); svn_node_kind_t kind; svn_wc__db_status_t status; svn_node_kind_t wc_kind; @@ -2008,6 +1868,9 @@ add_directory(const char *path, if (db->skip_this) return SVN_NO_ERROR; + SVN_ERR(calculate_repos_relpath(&db->new_repos_relpath, db->local_abspath, + NULL, eb, pb, db->pool, scratch_pool)); + SVN_ERR(mark_directory_edited(db, pool)); if (strcmp(eb->target_abspath, db->local_abspath) == 0) @@ -2037,13 +1900,26 @@ add_directory(const char *path, "administrative directory"), svn_dirent_local_style(db->local_abspath, pool)); - SVN_ERR(svn_io_check_path(db->local_abspath, &kind, db->pool)); + if (!eb->clean_checkout) + { + SVN_ERR(svn_io_check_path(db->local_abspath, &kind, db->pool)); + + err = svn_wc__db_read_info(&status, &wc_kind, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + &conflicted, NULL, NULL, NULL, NULL, NULL, NULL, + eb->db, db->local_abspath, + scratch_pool, scratch_pool); + } + else + { + kind = svn_node_none; + status = svn_wc__db_status_not_present; + wc_kind = svn_node_unknown; + conflicted = FALSE; + err = NULL; + } - err = svn_wc__db_read_info(&status, &wc_kind, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, - &conflicted, NULL, NULL, NULL, NULL, NULL, NULL, - eb->db, db->local_abspath, db->pool, db->pool); if (err) { if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND) @@ -2056,63 +1932,68 @@ add_directory(const char *path, versioned_locally_and_present = FALSE; } - else if (wc_kind == svn_node_dir - && status == svn_wc__db_status_normal) + else if (status == svn_wc__db_status_normal && wc_kind == svn_node_unknown) { - /* !! We found the root of a separate working copy obstructing the wc !! - - If the directory would be part of our own working copy then - we wouldn't have been called as an add_directory(). - - The only thing we can do is add a not-present node, to allow - a future update to bring in the new files when the problem is - resolved. Note that svn_wc__db_base_add_not_present_node() - explicitly adds the node into the parent's node database. */ - - SVN_ERR(svn_wc__db_base_add_not_present_node(eb->db, db->local_abspath, - db->new_relpath, - eb->repos_root, - eb->repos_uuid, - *eb->target_revision, - svn_node_file, - NULL, NULL, - pool)); - - SVN_ERR(remember_skipped_tree(eb, db->local_abspath, pool)); - db->skip_this = TRUE; - db->already_notified = TRUE; - - do_notification(eb, db->local_abspath, svn_node_dir, - svn_wc_notify_update_skip_obstruction, pool); - - return SVN_NO_ERROR; + SVN_ERR_ASSERT(conflicted); + versioned_locally_and_present = FALSE; /* Tree conflict ACTUAL-only node */ } else if (status == svn_wc__db_status_normal - && (wc_kind == svn_node_file - || wc_kind == svn_node_symlink)) + || status == svn_wc__db_status_incomplete) { - /* We found a file external occupating the place we need in BASE. + svn_boolean_t root; - We can't add a not-present node in this case as that would overwrite - the file external. Luckily the file external itself stops us from - forgetting a child of this parent directory like an obstructing - working copy would. + SVN_ERR(svn_wc__db_is_wcroot(&root, eb->db, db->local_abspath, + scratch_pool)); - The reason we get here is that the adm crawler doesn't report - file externals. - */ + if (root) + { + /* !! We found the root of a working copy obstructing the wc !! - SVN_ERR(remember_skipped_tree(eb, db->local_abspath, pool)); + If the directory would be part of our own working copy then + we wouldn't have been called as an add_directory(). + + The only thing we can do is add a not-present node, to allow + a future update to bring in the new files when the problem is + resolved. Note that svn_wc__db_base_add_not_present_node() + explicitly adds the node into the parent's node database. */ + + svn_hash_sets(pb->not_present_nodes, + apr_pstrdup(pb->pool, db->name), + svn_node_kind_to_word(svn_node_dir)); + } + else if (wc_kind == svn_node_dir) + { + /* We have an editor violation. Github sometimes does this + in its subversion compatibility code, when changing the + depth of a working copy, or on updates from incomplete */ + } + else + { + /* We found a file external occupating the place we need in BASE. + + We can't add a not-present node in this case as that would overwrite + the file external. Luckily the file external itself stops us from + forgetting a child of this parent directory like an obstructing + working copy would. + + The reason we get here is that the adm crawler doesn't report + file externals. + */ + SVN_ERR_ASSERT(wc_kind == svn_node_file + || wc_kind == svn_node_symlink); + } + + SVN_ERR(remember_skipped_tree(eb, db->local_abspath, scratch_pool)); db->skip_this = TRUE; db->already_notified = TRUE; - do_notification(eb, db->local_abspath, svn_node_file, - svn_wc_notify_update_skip_obstruction, pool); + do_notification(eb, db->local_abspath, wc_kind, + svn_wc_notify_update_skip_obstruction, scratch_pool); + + svn_pool_destroy(scratch_pool); return SVN_NO_ERROR; } - else if (wc_kind == svn_node_unknown) - versioned_locally_and_present = FALSE; /* Tree conflict ACTUAL-only node */ else versioned_locally_and_present = IS_NODE_PRESENT(status); @@ -2134,7 +2015,7 @@ add_directory(const char *path, eb->db, db->local_abspath, tree_conflict, - db->pool, db->pool)); + db->pool, scratch_pool)); tree_conflict = svn_wc__conflict_skel_create(db->pool); @@ -2143,7 +2024,7 @@ add_directory(const char *path, eb->db, db->local_abspath, reason, svn_wc_conflict_action_replace, move_src_op_root_abspath, - db->pool, db->pool)); + db->pool, scratch_pool)); /* And now stop checking for conflicts here and just perform a shadowed update */ @@ -2154,7 +2035,8 @@ add_directory(const char *path, } else SVN_ERR(node_already_conflicted(&conflicted, &conflict_ignored, - eb->db, db->local_abspath, pool)); + eb->db, db->local_abspath, + scratch_pool)); } /* Now the "usual" behaviour if already conflicted. Skip it. */ @@ -2177,18 +2059,13 @@ add_directory(const char *path, Note that we can safely assume that no present base node exists, because then we would not have received an add_directory. */ - SVN_ERR(svn_wc__db_base_add_not_present_node(eb->db, db->local_abspath, - db->new_relpath, - eb->repos_root, - eb->repos_uuid, - *eb->target_revision, - svn_node_dir, - NULL, NULL, - pool)); + svn_hash_sets(pb->not_present_nodes, apr_pstrdup(pb->pool, db->name), + svn_node_kind_to_word(svn_node_dir)); - /* ### TODO: Also print victim_path in the skip msg. */ do_notification(eb, db->local_abspath, svn_node_dir, - svn_wc_notify_skip_conflicted, pool); + svn_wc_notify_skip_conflicted, scratch_pool); + + svn_pool_destroy(scratch_pool); return SVN_NO_ERROR; } else if (conflict_ignored) @@ -2221,7 +2098,7 @@ add_directory(const char *path, SVN_ERR(svn_wc__db_scan_addition(&add_status, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, eb->db, db->local_abspath, - pool, pool)); + scratch_pool, scratch_pool)); /* Is there *something* that is not a dir? */ @@ -2244,7 +2121,7 @@ add_directory(const char *path, db->local_abspath, status, FALSE, svn_node_none, svn_wc_conflict_action_add, - pool, pool)); + db->pool, scratch_pool)); } if (tree_conflict == NULL) @@ -2272,7 +2149,7 @@ add_directory(const char *path, eb->db, db->local_abspath, svn_wc_conflict_reason_unversioned, svn_wc_conflict_action_add, NULL, - db->pool, pool)); + db->pool, scratch_pool)); db->edit_conflict = tree_conflict; } } @@ -2280,14 +2157,17 @@ add_directory(const char *path, if (tree_conflict) SVN_ERR(complete_conflict(tree_conflict, eb, db->local_abspath, db->old_repos_relpath, db->old_revision, - db->new_relpath, - wc_kind, - svn_node_dir, - db->pool, pool)); + db->new_repos_relpath, + wc_kind, svn_node_dir, + pb->deletion_conflicts + ? svn_hash_gets(pb->deletion_conflicts, + db->name) + : NULL, + db->pool, scratch_pool)); SVN_ERR(svn_wc__db_base_add_incomplete_directory( eb->db, db->local_abspath, - db->new_relpath, + db->new_repos_relpath, eb->repos_root, eb->repos_uuid, *eb->target_revision, @@ -2296,28 +2176,20 @@ add_directory(const char *path, (! db->shadowed && status == svn_wc__db_status_added), tree_conflict, NULL, - pool)); + scratch_pool)); /* Make sure there is a real directory at LOCAL_ABSPATH, unless we are just updating the DB */ if (!db->shadowed) - SVN_ERR(svn_wc__ensure_directory(db->local_abspath, pool)); + SVN_ERR(svn_wc__ensure_directory(db->local_abspath, scratch_pool)); if (tree_conflict != NULL) { - if (eb->conflict_func) - SVN_ERR(svn_wc__conflict_invoke_resolver(eb->db, db->local_abspath, - tree_conflict, - NULL /* merge_options */, - eb->conflict_func, - eb->conflict_baton, - eb->cancel_func, - eb->cancel_baton, - pool)); + db->edit_conflict = tree_conflict; db->already_notified = TRUE; do_notification(eb, db->local_abspath, svn_node_dir, - svn_wc_notify_tree_conflict, pool); + svn_wc_notify_tree_conflict, scratch_pool); } @@ -2339,9 +2211,12 @@ add_directory(const char *path, db->already_notified = TRUE; - do_notification(eb, db->local_abspath, svn_node_dir, action, pool); + do_notification(eb, db->local_abspath, svn_node_dir, action, + scratch_pool); } + svn_pool_destroy(scratch_pool); + return SVN_NO_ERROR; } @@ -2416,6 +2291,10 @@ open_directory(const char *path, db->was_incomplete = (base_status == svn_wc__db_status_incomplete); + SVN_ERR(calculate_repos_relpath(&db->new_repos_relpath, db->local_abspath, + db->old_repos_relpath, eb, pb, + db->pool, pool)); + /* Is this path a conflict victim? */ if (db->shadowed) conflicted = FALSE; /* Conflict applies to WORKING */ @@ -2475,7 +2354,7 @@ open_directory(const char *path, /* Mark directory as being at target_revision and URL, but incomplete. */ SVN_ERR(svn_wc__db_temp_op_start_directory_update(eb->db, db->local_abspath, - db->new_relpath, + db->new_repos_relpath, *eb->target_revision, pool)); @@ -2498,7 +2377,7 @@ change_dir_prop(void *dir_baton, propchange = apr_array_push(db->propchanges); propchange->name = apr_pstrdup(db->pool, name); - propchange->value = value ? svn_string_dup(value, db->pool) : NULL; + propchange->value = svn_string_dup(value, db->pool); if (!db->edited && svn_property_kind2(name) == svn_prop_regular_kind) SVN_ERR(mark_directory_edited(db, pool)); @@ -2619,7 +2498,7 @@ close_directory(void *dir_baton, hi != NULL; hi = apr_hash_next(hi)) { - const char *propname = svn__apr_hash_index_key(hi); + const char *propname = apr_hash_this_key(hi); svn_prop_t *prop = apr_array_push(regular_prop_changes); /* Record a deletion for PROPNAME. */ @@ -2702,7 +2581,8 @@ close_directory(void *dir_baton, /* Check if we should add some not-present markers before marking the directory complete (Issue #3569) */ { - apr_hash_t *new_children = svn_hash_gets(eb->dir_dirents, db->new_relpath); + apr_hash_t *new_children = svn_hash_gets(eb->dir_dirents, + db->new_repos_relpath); if (new_children != NULL) { @@ -2723,11 +2603,11 @@ close_directory(void *dir_baton, svn_pool_clear(iterpool); - child_name = svn__apr_hash_index_key(hi); + child_name = apr_hash_this_key(hi); child_abspath = svn_dirent_join(db->local_abspath, child_name, iterpool); - dirent = svn__apr_hash_index_val(hi); + dirent = apr_hash_this_val(hi); child_kind = (dirent->kind == svn_node_dir) ? svn_node_dir : svn_node_file; @@ -2758,7 +2638,7 @@ close_directory(void *dir_baton, svn_error_clear(err); - child_relpath = svn_relpath_join(db->new_relpath, child_name, + child_relpath = svn_relpath_join(db->new_repos_relpath, child_name, iterpool); SVN_ERR(svn_wc__db_base_add_not_present_node(eb->db, @@ -2776,7 +2656,7 @@ close_directory(void *dir_baton, } } - if (apr_hash_count(db->not_present_files)) + if (apr_hash_count(db->not_present_nodes)) { apr_hash_index_t *hi; apr_pool_t *iterpool = svn_pool_create(scratch_pool); @@ -2786,17 +2666,18 @@ close_directory(void *dir_baton, transaction, but I can't even trigger it. I've tried ra_local, ra_svn, ra_neon, ra_serf and they all call close_file before close_dir. */ - for (hi = apr_hash_first(scratch_pool, db->not_present_files); + for (hi = apr_hash_first(scratch_pool, db->not_present_nodes); hi; hi = apr_hash_next(hi)) { - const char *child = svn__apr_hash_index_key(hi); + const char *child = apr_hash_this_key(hi); const char *child_abspath, *child_relpath; + svn_node_kind_t kind = svn_node_kind_from_word(apr_hash_this_val(hi)); svn_pool_clear(iterpool); child_abspath = svn_dirent_join(db->local_abspath, child, iterpool); - child_relpath = svn_dirent_join(db->new_relpath, child, iterpool); + child_relpath = svn_dirent_join(db->new_repos_relpath, child, iterpool); SVN_ERR(svn_wc__db_base_add_not_present_node(eb->db, child_abspath, @@ -2804,7 +2685,7 @@ close_directory(void *dir_baton, eb->repos_root, eb->repos_uuid, *eb->target_revision, - svn_node_file, + kind, NULL, NULL, iterpool)); } @@ -2872,8 +2753,14 @@ close_directory(void *dir_baton, db->local_abspath, db->old_repos_relpath, db->old_revision, - db->new_relpath, + db->new_repos_relpath, svn_node_dir, svn_node_dir, + (db->parent_baton + && db->parent_baton->deletion_conflicts) + ? svn_hash_gets( + db->parent_baton->deletion_conflicts, + db->name) + : NULL, db->pool, scratch_pool)); SVN_ERR(svn_wc__conflict_create_markers(&work_item, @@ -2904,7 +2791,7 @@ close_directory(void *dir_baton, SVN_ERR(svn_wc__db_base_add_directory( eb->db, db->local_abspath, eb->wcroot_abspath, - db->new_relpath, + db->new_repos_relpath, eb->repos_root, eb->repos_uuid, *eb->target_revision, props, @@ -2914,10 +2801,9 @@ close_directory(void *dir_baton, (dav_prop_changes->nelts > 0) ? svn_prop_array_to_hash(dav_prop_changes, pool) : NULL, - conflict_skel, (! db->shadowed) && new_base_props != NULL, - new_actual_props, - iprops, all_work_items, + new_actual_props, iprops, + conflict_skel, all_work_items, scratch_pool)); } @@ -2926,8 +2812,12 @@ close_directory(void *dir_baton, eb->cancel_func, eb->cancel_baton, scratch_pool)); + if (db->parent_baton) + svn_hash_sets(db->parent_baton->not_present_nodes, db->name, NULL); + if (conflict_skel && eb->conflict_func) SVN_ERR(svn_wc__conflict_invoke_resolver(eb->db, db->local_abspath, + svn_node_dir, conflict_skel, NULL /* merge_options */, eb->conflict_func, @@ -2962,6 +2852,9 @@ close_directory(void *dir_baton, eb->notify_func(eb->notify_baton, notify, scratch_pool); } + if (db->edited) + eb->edited = db->edited; + /* We're done with this directory, so remove one reference from the bump information. */ SVN_ERR(maybe_release_dir_info(db)); @@ -2985,14 +2878,12 @@ absent_node(const char *path, svn_error_t *err; svn_wc__db_status_t status; svn_node_kind_t kind; + svn_skel_t *tree_conflict = NULL; if (pb->skip_this) return SVN_NO_ERROR; - SVN_ERR(mark_directory_edited(pb, scratch_pool)); - local_abspath = svn_dirent_join(pb->local_abspath, name, scratch_pool); - /* If an item by this name is scheduled for addition that's a genuine tree-conflict. */ err = svn_wc__db_read_info(&status, &kind, NULL, NULL, NULL, NULL, NULL, @@ -3012,6 +2903,10 @@ absent_node(const char *path, kind = svn_node_unknown; } + if (status != svn_wc__db_status_server_excluded) + SVN_ERR(mark_directory_edited(pb, scratch_pool)); + /* Else fall through as we should update the revision anyway */ + if (status == svn_wc__db_status_normal) { svn_boolean_t wcroot; @@ -3035,31 +2930,53 @@ absent_node(const char *path, } else { - /* The server asks us to replace a file external - (Existing BASE node; not reported by the working copy crawler or - there would have been a delete_entry() call. - - There is no way we can store this state in the working copy as - the BASE layer is already filled. + svn_boolean_t file_external; + svn_revnum_t revnum; - We could error out, but that is not helping anybody; the user is not - even seeing with what the file external would be replaced, so let's - report a skip and continue the update. - */ + SVN_ERR(svn_wc__db_base_get_info(NULL, NULL, &revnum, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + &file_external, + eb->db, local_abspath, + scratch_pool, scratch_pool)); - if (eb->notify_func) + if (file_external) { - svn_wc_notify_t *notify; - notify = svn_wc_create_notify( + /* The server asks us to replace a file external + (Existing BASE node; not reported by the working copy crawler + or there would have been a delete_entry() call. + + There is no way we can store this state in the working copy as + the BASE layer is already filled. + We could error out, but that is not helping anybody; the user is not + even seeing with what the file external would be replaced, so let's + report a skip and continue the update. + */ + + if (eb->notify_func) + { + svn_wc_notify_t *notify; + notify = svn_wc_create_notify( local_abspath, svn_wc_notify_update_skip_obstruction, scratch_pool); - eb->notify_func(eb->notify_baton, notify, scratch_pool); + eb->notify_func(eb->notify_baton, notify, scratch_pool); + } + + svn_pool_destroy(scratch_pool); + return SVN_NO_ERROR; } + else + { + /* We have a normal local node that will now be hidden for the + user. Let's try to delete what is there. This may introduce + tree conflicts if there are local changes */ + SVN_ERR(delete_entry(path, revnum, pb, scratch_pool)); - svn_pool_destroy(scratch_pool); - return SVN_NO_ERROR; + /* delete_entry() promises that BASE is empty after the operation, + so we can just fall through now */ + } } } else if (status == svn_wc__db_status_not_present @@ -3073,24 +2990,26 @@ absent_node(const char *path, { /* We have a local addition. If this would be a BASE node it would have been deleted before we get here. (Which might have turned it into - a copy). - - ### This should be recorded as a tree conflict and the update - ### can just continue, as we can just record the absent status - ### in BASE. - */ + a copy). */ SVN_ERR_ASSERT(status != svn_wc__db_status_normal); - return svn_error_createf( - SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL, - _("Failed to mark '%s' absent: item of the same name is already " - "scheduled for addition"), - svn_dirent_local_style(local_abspath, pool)); + if (!pb->shadowed && !pb->edit_obstructed) + SVN_ERR(check_tree_conflict(&tree_conflict, eb, local_abspath, + status, FALSE, svn_node_unknown, + svn_wc_conflict_action_add, + scratch_pool, scratch_pool)); + } { const char *repos_relpath; - repos_relpath = svn_relpath_join(pb->new_relpath, name, scratch_pool); + repos_relpath = svn_relpath_join(pb->new_repos_relpath, name, scratch_pool); + + if (tree_conflict) + SVN_ERR(complete_conflict(tree_conflict, eb, local_abspath, + NULL, SVN_INVALID_REVNUM, repos_relpath, + kind, svn_node_unknown, NULL, + scratch_pool, scratch_pool)); /* Insert an excluded node below the parent node to note that this child is absent. (This puts it in the parent db if the child is obstructed) */ @@ -3100,8 +3019,24 @@ absent_node(const char *path, *(eb->target_revision), absent_kind, svn_wc__db_status_server_excluded, - NULL, NULL, + tree_conflict, NULL, scratch_pool)); + + if (tree_conflict) + { + if (eb->conflict_func) + SVN_ERR(svn_wc__conflict_invoke_resolver(eb->db, local_abspath, + kind, + tree_conflict, + NULL /* merge_options */, + eb->conflict_func, + eb->conflict_baton, + eb->cancel_func, + eb->cancel_baton, + scratch_pool)); + do_notification(eb, local_abspath, kind, svn_wc_notify_tree_conflict, + scratch_pool); + } } svn_pool_destroy(scratch_pool); @@ -3142,13 +3077,13 @@ add_file(const char *path, struct dir_baton *pb = parent_baton; struct edit_baton *eb = pb->edit_baton; struct file_baton *fb; - svn_node_kind_t kind = svn_node_none; - svn_node_kind_t wc_kind = svn_node_unknown; - svn_wc__db_status_t status = svn_wc__db_status_normal; + svn_node_kind_t kind; + svn_node_kind_t wc_kind; + svn_wc__db_status_t status; apr_pool_t *scratch_pool; - svn_boolean_t conflicted = FALSE; + svn_boolean_t conflicted; svn_boolean_t conflict_ignored = FALSE; - svn_boolean_t versioned_locally_and_present = FALSE; + svn_boolean_t versioned_locally_and_present; svn_skel_t *tree_conflict = NULL; svn_error_t *err = SVN_NO_ERROR; @@ -3160,6 +3095,8 @@ add_file(const char *path, if (fb->skip_this) return SVN_NO_ERROR; + SVN_ERR(calculate_repos_relpath(&fb->new_repos_relpath, fb->local_abspath, + NULL, eb, pb, fb->pool, pool)); SVN_ERR(mark_file_edited(fb, pool)); /* The file_pool can stick around for a *long* time, so we want to @@ -3186,6 +3123,13 @@ add_file(const char *path, eb->db, fb->local_abspath, scratch_pool, scratch_pool); } + else + { + kind = svn_node_none; + status = svn_wc__db_status_not_present; + wc_kind = svn_node_unknown; + conflicted = FALSE; + } if (err) { @@ -3198,58 +3142,68 @@ add_file(const char *path, versioned_locally_and_present = FALSE; } - else if (wc_kind == svn_node_dir - && status == svn_wc__db_status_normal) + else if (status == svn_wc__db_status_normal && wc_kind == svn_node_unknown) { - /* !! We found the root of a separate working copy obstructing the wc !! + SVN_ERR_ASSERT(conflicted); + versioned_locally_and_present = FALSE; /* Tree conflict ACTUAL-only node */ + } + else if (status == svn_wc__db_status_normal + || status == svn_wc__db_status_incomplete) + { + svn_boolean_t root; - If the directory would be part of our own working copy then - we wouldn't have been called as an add_file(). + SVN_ERR(svn_wc__db_is_wcroot(&root, eb->db, fb->local_abspath, + scratch_pool)); - The only thing we can do is add a not-present node, to allow - a future update to bring in the new files when the problem is - resolved. */ - svn_hash_sets(pb->not_present_files, apr_pstrdup(pb->pool, fb->name), - (void *)1); + if (root) + { + /* !! We found the root of a working copy obstructing the wc !! - SVN_ERR(remember_skipped_tree(eb, fb->local_abspath, pool)); - fb->skip_this = TRUE; - fb->already_notified = TRUE; + If the directory would be part of our own working copy then + we wouldn't have been called as an add_directory(). - do_notification(eb, fb->local_abspath, svn_node_file, - svn_wc_notify_update_skip_obstruction, scratch_pool); + The only thing we can do is add a not-present node, to allow + a future update to bring in the new files when the problem is + resolved. Note that svn_wc__db_base_add_not_present_node() + explicitly adds the node into the parent's node database. */ - svn_pool_destroy(scratch_pool); + svn_hash_sets(pb->not_present_nodes, + apr_pstrdup(pb->pool, fb->name), + svn_node_kind_to_word(svn_node_dir)); + } + else if (wc_kind == svn_node_dir) + { + /* We have an editor violation. Github sometimes does this + in its subversion compatibility code, when changing the + depth of a working copy, or on updates from incomplete */ + } + else + { + /* We found a file external occupating the place we need in BASE. - return SVN_NO_ERROR; - } - else if (status == svn_wc__db_status_normal - && (wc_kind == svn_node_file - || wc_kind == svn_node_symlink)) - { - /* We found a file external occupating the place we need in BASE. + We can't add a not-present node in this case as that would overwrite + the file external. Luckily the file external itself stops us from + forgetting a child of this parent directory like an obstructing + working copy would. - We can't add a not-present node in this case as that would overwrite - the file external. Luckily the file external itself stops us from - forgetting a child of this parent directory like an obstructing - working copy would. + The reason we get here is that the adm crawler doesn't report + file externals. + */ + SVN_ERR_ASSERT(wc_kind == svn_node_file + || wc_kind == svn_node_symlink); + } - The reason we get here is that the adm crawler doesn't report - file externals. - */ SVN_ERR(remember_skipped_tree(eb, fb->local_abspath, pool)); fb->skip_this = TRUE; fb->already_notified = TRUE; - do_notification(eb, fb->local_abspath, svn_node_file, + do_notification(eb, fb->local_abspath, wc_kind, svn_wc_notify_update_skip_obstruction, scratch_pool); svn_pool_destroy(scratch_pool); return SVN_NO_ERROR; } - else if (wc_kind == svn_node_unknown) - versioned_locally_and_present = FALSE; /* Tree conflict ACTUAL-only node */ else versioned_locally_and_present = IS_NODE_PRESENT(status); @@ -3274,7 +3228,7 @@ add_file(const char *path, eb->db, fb->local_abspath, tree_conflict, - fb->pool, fb->pool)); + fb->pool, scratch_pool)); tree_conflict = svn_wc__conflict_skel_create(fb->pool); @@ -3283,7 +3237,7 @@ add_file(const char *path, eb->db, fb->local_abspath, reason, svn_wc_conflict_action_replace, move_src_op_root_abspath, - fb->pool, fb->pool)); + fb->pool, scratch_pool)); /* And now stop checking for conflicts here and just perform a shadowed update */ @@ -3316,10 +3270,10 @@ add_file(const char *path, Note that we can safely assume that no present base node exists, because then we would not have received an add_file. */ - svn_hash_sets(pb->not_present_files, apr_pstrdup(pb->pool, fb->name), - (void *)1); + svn_hash_sets(pb->not_present_nodes, apr_pstrdup(pb->pool, fb->name), + svn_node_kind_to_word(svn_node_file)); - do_notification(eb, fb->local_abspath, svn_node_unknown, + do_notification(eb, fb->local_abspath, svn_node_file, svn_wc_notify_skip_conflicted, scratch_pool); svn_pool_destroy(scratch_pool); @@ -3380,7 +3334,7 @@ add_file(const char *path, fb->local_abspath, status, FALSE, svn_node_none, svn_wc_conflict_action_add, - scratch_pool, scratch_pool)); + fb->pool, scratch_pool)); } if (tree_conflict == NULL) @@ -3421,8 +3375,8 @@ add_file(const char *path, || *eb->target_basename == '\0' || (strcmp(fb->local_abspath, eb->target_abspath) != 0)) { - svn_hash_sets(pb->not_present_files, apr_pstrdup(pb->pool, fb->name), - (void *)1); + svn_hash_sets(pb->not_present_nodes, apr_pstrdup(pb->pool, fb->name), + svn_node_kind_to_word(svn_node_file)); } if (tree_conflict != NULL) @@ -3432,9 +3386,12 @@ add_file(const char *path, fb->local_abspath, fb->old_repos_relpath, fb->old_revision, - fb->new_relpath, - wc_kind, - svn_node_file, + fb->new_repos_relpath, + wc_kind, svn_node_file, + pb->deletion_conflicts + ? svn_hash_gets(pb->deletion_conflicts, + fb->name) + : NULL, fb->pool, scratch_pool)); SVN_ERR(svn_wc__db_op_mark_conflict(eb->db, @@ -3442,15 +3399,7 @@ add_file(const char *path, tree_conflict, NULL, scratch_pool)); - if (eb->conflict_func) - SVN_ERR(svn_wc__conflict_invoke_resolver(eb->db, fb->local_abspath, - tree_conflict, - NULL /* merge_options */, - eb->conflict_func, - eb->conflict_baton, - eb->cancel_func, - eb->cancel_baton, - scratch_pool)); + fb->edit_conflict = tree_conflict; fb->already_notified = TRUE; do_notification(eb, fb->local_abspath, svn_node_file, @@ -3514,7 +3463,6 @@ open_file(const char *path, /* Sanity check. */ - /* If replacing, make sure the .svn entry already exists. */ SVN_ERR(svn_wc__db_read_info(&status, &wc_kind, &fb->old_revision, &fb->old_repos_relpath, NULL, NULL, &fb->changed_rev, &fb->changed_date, @@ -3536,6 +3484,10 @@ open_file(const char *path, eb->db, fb->local_abspath, fb->pool, scratch_pool)); + SVN_ERR(calculate_repos_relpath(&fb->new_repos_relpath, fb->local_abspath, + fb->old_repos_relpath, eb, pb, + fb->pool, scratch_pool)); + /* Is this path a conflict victim? */ if (fb->shadowed) conflicted = FALSE; /* Conflict applies to WORKING */ @@ -3615,12 +3567,6 @@ lazy_open_source(svn_stream_t **stream, return SVN_NO_ERROR; } -struct lazy_target_baton { - struct file_baton *fb; - struct handler_baton *hb; - struct edit_baton *eb; -}; - /* Implements svn_stream_lazyopen_func_t. */ static svn_error_t * lazy_open_target(svn_stream_t **stream, @@ -3628,13 +3574,23 @@ lazy_open_target(svn_stream_t **stream, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { - struct lazy_target_baton *tb = baton; - - SVN_ERR(svn_wc__open_writable_base(stream, &tb->hb->new_text_base_tmp_abspath, - NULL, &tb->hb->new_text_base_sha1_checksum, - tb->fb->edit_baton->db, - tb->eb->wcroot_abspath, - result_pool, scratch_pool)); + struct handler_baton *hb = baton; + svn_wc__db_install_data_t *install_data; + + /* By convention return value is undefined on error, but we rely + on HB->INSTALL_DATA value in window_handler() and abort + INSTALL_STREAM if is not NULL on error. + So we store INSTALL_DATA to local variable first, to leave + HB->INSTALL_DATA unchanged on error. */ + SVN_ERR(svn_wc__db_pristine_prepare_install(stream, + &install_data, + &hb->new_text_base_sha1_checksum, + NULL, + hb->fb->edit_baton->db, + hb->fb->dir_baton->local_abspath, + result_pool, scratch_pool)); + + hb->install_data = install_data; return SVN_NO_ERROR; } @@ -3654,7 +3610,6 @@ apply_textdelta(void *file_baton, const svn_checksum_t *recorded_base_checksum; svn_checksum_t *expected_base_checksum; svn_stream_t *source; - struct lazy_target_baton *tb; svn_stream_t *target; if (fb->skip_this) @@ -3748,16 +3703,12 @@ apply_textdelta(void *file_baton, hb->source_checksum_stream = source; } - tb = apr_palloc(handler_pool, sizeof(struct lazy_target_baton)); - tb->hb = hb; - tb->fb = fb; - tb->eb = eb; - target = svn_stream_lazyopen_create(lazy_open_target, tb, TRUE, handler_pool); + target = svn_stream_lazyopen_create(lazy_open_target, hb, TRUE, handler_pool); /* Prepare to apply the delta. */ svn_txdelta_apply(source, target, hb->new_text_base_md5_digest, - hb->new_text_base_tmp_abspath /* error_info */, + fb->local_abspath /* error_info */, handler_pool, &hb->apply_handler, &hb->apply_baton); @@ -3788,7 +3739,7 @@ change_file_prop(void *file_baton, /* Push a new propchange to the file baton's array of propchanges */ propchange = apr_array_push(fb->propchanges); propchange->name = apr_pstrdup(fb->pool, name); - propchange->value = value ? svn_string_dup(value, fb->pool) : NULL; + propchange->value = svn_string_dup(value, fb->pool); if (!fb->edited && svn_property_kind2(name) == svn_prop_regular_kind) SVN_ERR(mark_file_edited(fb, scratch_pool)); @@ -3851,9 +3802,9 @@ change_file_prop(void *file_baton, SVN_ERR(complete_conflict(fb->edit_conflict, fb->edit_baton, fb->local_abspath, fb->old_repos_relpath, - fb->old_revision, fb->new_relpath, + fb->old_revision, fb->new_repos_relpath, svn_node_file, svn_node_file, - fb->pool, scratch_pool)); + NULL, fb->pool, scratch_pool)); /* Create a copy of the existing (pre update) BASE node in WORKING, mark a tree conflict and handle the rest of the update as @@ -3913,13 +3864,13 @@ svn_wc__perform_file_merge(svn_skel_t **work_items, const char *merge_left; svn_boolean_t delete_left = FALSE; const char *path_ext = ""; - const char *new_text_base_tmp_abspath; + const char *new_pristine_abspath; enum svn_wc_merge_outcome_t merge_outcome = svn_wc_merge_unchanged; svn_skel_t *work_item; *work_items = NULL; - SVN_ERR(svn_wc__db_pristine_get_path(&new_text_base_tmp_abspath, + SVN_ERR(svn_wc__db_pristine_get_path(&new_pristine_abspath, db, wri_abspath, new_checksum, scratch_pool, scratch_pool)); @@ -3972,7 +3923,7 @@ svn_wc__perform_file_merge(svn_skel_t **work_items, &merge_outcome, db, merge_left, - new_text_base_tmp_abspath, + new_pristine_abspath, local_abspath, wri_abspath, oldrev_str, newrev_str, mine_str, @@ -4321,11 +4272,11 @@ close_file(void *file_baton, { /* If we lose the lock, but not because we are switching to another url, remove the state lock from the wc */ - if (! eb->switch_relpath - || strcmp(fb->new_relpath, fb->old_repos_relpath) == 0) + if (! eb->switch_repos_relpath + || strcmp(fb->new_repos_relpath, fb->old_repos_relpath) == 0) { SVN_ERR_ASSERT(prop->value == NULL); - SVN_ERR(svn_wc__db_lock_remove(eb->db, fb->local_abspath, + SVN_ERR(svn_wc__db_lock_remove(eb->db, fb->local_abspath, NULL, scratch_pool)); lock_state = svn_wc_notify_lock_state_unlocked; @@ -4564,8 +4515,13 @@ close_file(void *file_baton, fb->local_abspath, fb->old_repos_relpath, fb->old_revision, - fb->new_relpath, + fb->new_repos_relpath, svn_node_file, svn_node_file, + fb->dir_baton->deletion_conflicts + ? svn_hash_gets( + fb->dir_baton->deletion_conflicts, + fb->name) + : NULL, fb->pool, scratch_pool)); SVN_ERR(svn_wc__conflict_create_markers(&work_item, @@ -4593,7 +4549,7 @@ close_file(void *file_baton, SVN_ERR(svn_wc__db_base_add_file(eb->db, fb->local_abspath, eb->wcroot_abspath, - fb->new_relpath, + fb->new_repos_relpath, eb->repos_root, eb->repos_uuid, *eb->target_revision, new_base_props, @@ -4618,6 +4574,7 @@ close_file(void *file_baton, if (conflict_skel && eb->conflict_func) SVN_ERR(svn_wc__conflict_invoke_resolver(eb->db, fb->local_abspath, + svn_node_file, conflict_skel, NULL /* merge_options */, eb->conflict_func, @@ -4628,7 +4585,7 @@ close_file(void *file_baton, /* Deal with the WORKING tree, based on updates to the BASE tree. */ - svn_hash_sets(fb->dir_baton->not_present_files, fb->name, NULL); + svn_hash_sets(fb->dir_baton->not_present_nodes, fb->name, NULL); /* Send a notification to the callback function. (Skip notifications about files which were already notified for another reason.) */ @@ -4687,6 +4644,78 @@ close_file(void *file_baton, } +/* Implements svn_wc__proplist_receiver_t. + * Check for the presence of an svn:keywords property and queues an install_file + * work queue item if present. Thus, when the work queue is run to complete the + * switch operation, all files with keywords will go through the translation + * process so URLs etc are updated. */ +static svn_error_t * +update_keywords_after_switch_cb(void *baton, + const char *local_abspath, + apr_hash_t *props, + apr_pool_t *scratch_pool) +{ + struct edit_baton *eb = baton; + svn_string_t *propval; + svn_boolean_t modified; + svn_boolean_t record_fileinfo; + svn_skel_t *work_items; + const char *install_from; + + propval = svn_hash_gets(props, SVN_PROP_KEYWORDS); + if (!propval) + return SVN_NO_ERROR; + + SVN_ERR(svn_wc__internal_file_modified_p(&modified, eb->db, + local_abspath, FALSE, + scratch_pool)); + if (modified) + { + const char *temp_dir_abspath; + svn_stream_t *working_stream; + svn_stream_t *install_from_stream; + + SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&temp_dir_abspath, eb->db, + local_abspath, scratch_pool, + scratch_pool)); + SVN_ERR(svn_stream_open_readonly(&working_stream, local_abspath, + scratch_pool, scratch_pool)); + SVN_ERR(svn_stream_open_unique(&install_from_stream, &install_from, + temp_dir_abspath, svn_io_file_del_none, + scratch_pool, scratch_pool)); + SVN_ERR(svn_stream_copy3(working_stream, install_from_stream, + eb->cancel_func, eb->cancel_baton, + scratch_pool)); + record_fileinfo = FALSE; + } + else + { + install_from = NULL; + record_fileinfo = TRUE; + } + + SVN_ERR(svn_wc__wq_build_file_install(&work_items, eb->db, local_abspath, + install_from, + eb->use_commit_times, + record_fileinfo, + scratch_pool, scratch_pool)); + if (install_from) + { + svn_skel_t *work_item; + + SVN_ERR(svn_wc__wq_build_file_remove(&work_item, eb->db, + local_abspath, install_from, + scratch_pool, scratch_pool)); + work_items = svn_wc__wq_merge(work_items, work_item, scratch_pool); + } + + SVN_ERR(svn_wc__db_wq_add(eb->db, local_abspath, work_items, + scratch_pool)); + + return SVN_NO_ERROR; +} + + /* An svn_delta_editor_t function. */ static svn_error_t * close_edit(void *edit_baton, @@ -4728,12 +4757,13 @@ close_edit(void *edit_baton, SVN_ERR(svn_wc__db_op_bump_revisions_post_update(eb->db, eb->target_abspath, eb->requested_depth, - eb->switch_relpath, + eb->switch_repos_relpath, eb->repos_root, eb->repos_uuid, *(eb->target_revision), eb->skipped_trees, eb->wcroot_iprops, + ! eb->edited, eb->notify_func, eb->notify_baton, eb->pool)); @@ -4773,15 +4803,36 @@ close_edit(void *edit_baton, If so, we should get rid of this excluded node now. */ SVN_ERR(svn_wc__db_base_remove(eb->db, eb->target_abspath, - FALSE /* keep_as_working */, - FALSE /* queue_deletes */, - FALSE /* remove_locks */, + TRUE, FALSE, FALSE, SVN_INVALID_REVNUM, NULL, NULL, scratch_pool)); } } } + /* Update keywords in switched files. + GOTO #1975 (the year of the Altair 8800). */ + if (eb->switch_repos_relpath) + { + svn_depth_t depth; + + if (eb->requested_depth > svn_depth_empty) + depth = eb->requested_depth; + else + depth = svn_depth_infinity; + + SVN_ERR(svn_wc__db_read_props_streamily(eb->db, + eb->target_abspath, + depth, + FALSE, /* pristine */ + NULL, /* changelists */ + update_keywords_after_switch_cb, + eb, + eb->cancel_func, + eb->cancel_baton, + scratch_pool)); + } + /* The edit is over: run the wq with proper cancel support, but first kill the handler that would run it on the pool cleanup at the end of this function. */ @@ -4854,9 +4905,11 @@ make_editor(svn_revnum_t *target_revision, /* Get the anchor's repository root and uuid. The anchor must already exist in BASE. */ - SVN_ERR(svn_wc__db_scan_base_repos(NULL, &repos_root, &repos_uuid, - db, anchor_abspath, - result_pool, scratch_pool)); + SVN_ERR(svn_wc__db_base_get_info(NULL, NULL, NULL, NULL, &repos_root, + &repos_uuid, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, + db, anchor_abspath, + result_pool, scratch_pool)); /* With WC-NG we need a valid repository root */ SVN_ERR_ASSERT(repos_root != NULL && repos_uuid != NULL); @@ -4884,10 +4937,10 @@ make_editor(svn_revnum_t *target_revision, edit_pool, scratch_pool)); if (switch_url) - eb->switch_relpath = + eb->switch_repos_relpath = svn_uri_skip_ancestor(repos_root, switch_url, scratch_pool); else - eb->switch_relpath = NULL; + eb->switch_repos_relpath = NULL; if (svn_path_is_empty(target_basename)) eb->target_abspath = eb->anchor_abspath; @@ -4968,8 +5021,8 @@ make_editor(svn_revnum_t *target_revision, apr_hash_t *dirents; /* If we switch, we should look at the new relpath */ - if (eb->switch_relpath) - dir_repos_relpath = eb->switch_relpath; + if (eb->switch_repos_relpath) + dir_repos_relpath = eb->switch_repos_relpath; SVN_ERR(fetch_dirents_func(fetch_dirents_baton, &dirents, repos_root, dir_repos_relpath, @@ -5022,9 +5075,9 @@ make_editor(svn_revnum_t *target_revision, apr_hash_t *dirents; /* If we switch, we should look at the new relpath */ - if (eb->switch_relpath) + if (eb->switch_repos_relpath) dir_repos_relpath = svn_relpath_join( - eb->switch_relpath, + eb->switch_repos_relpath, child_name, iterpool); SVN_ERR(fetch_dirents_func(fetch_dirents_baton, &dirents, @@ -5223,6 +5276,8 @@ svn_wc_add_repos_file4(svn_wc_context_t *wc_ctx, svn_revnum_t changed_rev; apr_time_t changed_date; const char *changed_author; + svn_stream_t *tmp_base_contents; + svn_wc__db_install_data_t *install_data; svn_error_t *err; apr_pool_t *pool = scratch_pool; @@ -5344,18 +5399,35 @@ svn_wc_add_repos_file4(svn_wc_context_t *wc_ctx, /* Copy NEW_BASE_CONTENTS into a temporary file so our log can refer to it, and set TMP_TEXT_BASE_ABSPATH to its path. Compute its NEW_TEXT_BASE_MD5_CHECKSUM and NEW_TEXT_BASE_SHA1_CHECKSUM as we copy. */ - { - svn_stream_t *tmp_base_contents; + if (copyfrom_url) + { + SVN_ERR(svn_wc__db_pristine_prepare_install(&tmp_base_contents, + &install_data, + &new_text_base_sha1_checksum, + &new_text_base_md5_checksum, + wc_ctx->db, local_abspath, + scratch_pool, scratch_pool)); + } + else + { + const char *tmp_dir_abspath; - SVN_ERR(svn_wc__open_writable_base(&tmp_base_contents, - &tmp_text_base_abspath, - &new_text_base_md5_checksum, - &new_text_base_sha1_checksum, - wc_ctx->db, local_abspath, - pool, pool)); - SVN_ERR(svn_stream_copy3(new_base_contents, tmp_base_contents, - cancel_func, cancel_baton, pool)); - } + /* We are not installing a PRISTINE file, but we use the same code to + create whatever we want to install */ + + SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&tmp_dir_abspath, + db, dir_abspath, + scratch_pool, scratch_pool)); + + SVN_ERR(svn_stream_open_unique(&tmp_base_contents, &tmp_text_base_abspath, + tmp_dir_abspath, svn_io_file_del_none, + scratch_pool, scratch_pool)); + + new_text_base_sha1_checksum = NULL; + new_text_base_md5_checksum = NULL; + } + SVN_ERR(svn_stream_copy3(new_base_contents, tmp_base_contents, + cancel_func, cancel_baton, pool)); /* If the caller gave us a new working file, copy it to a safe (temporary) location and set SOURCE_ABSPATH to that path. We'll then translate/copy @@ -5378,7 +5450,7 @@ svn_wc_add_repos_file4(svn_wc_context_t *wc_ctx, text base. */ if (copyfrom_url != NULL) { - SVN_ERR(svn_wc__db_pristine_install(db, tmp_text_base_abspath, + SVN_ERR(svn_wc__db_pristine_install(install_data, new_text_base_sha1_checksum, new_text_base_md5_checksum, pool)); } @@ -5445,9 +5517,6 @@ svn_wc_add_repos_file4(svn_wc_context_t *wc_ctx, } } - /* ### ideally, we would have a single DB operation, and queue the work - ### items on that. for now, we'll queue them with the second call. */ - SVN_ERR(svn_wc__db_op_copy_file(db, local_abspath, new_base_props, changed_rev, |