diff options
author | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-08-05 16:22:51 +0000 |
---|---|---|
committer | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-08-05 16:22:51 +0000 |
commit | cf46733632c7279a9fd0fe6ce26f9185a4ae82a9 (patch) | |
tree | da27775a2161723ef342e91af41a8b51fedef405 /subversion/libsvn_wc/wc_db_update_move.c | |
parent | bb0ef45f7c46b0ae221b26265ef98a768c33f820 (diff) | |
download | subversion-tarball-master.tar.gz |
subversion-1.9.7HEADsubversion-1.9.7master
Diffstat (limited to 'subversion/libsvn_wc/wc_db_update_move.c')
-rw-r--r-- | subversion/libsvn_wc/wc_db_update_move.c | 2828 |
1 files changed, 1423 insertions, 1405 deletions
diff --git a/subversion/libsvn_wc/wc_db_update_move.c b/subversion/libsvn_wc/wc_db_update_move.c index 7f4f853..46cbeae 100644 --- a/subversion/libsvn_wc/wc_db_update_move.c +++ b/subversion/libsvn_wc/wc_db_update_move.c @@ -46,7 +46,7 @@ * layer. The destination may have additional higher op-depths * representing adds, deletes, moves within the move destination. [2] * - * After the intial move an update has modified the NODES in the move + * After the initial move an update has modified the NODES in the move * source and may have introduced a tree-conflict since the source and * destination trees are no longer equivalent. The source is a * different revision and may have text, property and tree changes @@ -88,9 +88,9 @@ #include "svn_sorts.h" #include "private/svn_skel.h" +#include "private/svn_sorts_private.h" #include "private/svn_sqlite.h" #include "private/svn_wc_private.h" -#include "private/svn_editor.h" #include "wc.h" #include "props.h" @@ -100,6 +100,80 @@ #include "workqueue.h" #include "token-map.h" +/* Helper functions */ +/* Return the absolute path, in local path style, of LOCAL_RELPATH + in WCROOT. */ +static const char * +path_for_error_message(const svn_wc__db_wcroot_t *wcroot, + const char *local_relpath, + apr_pool_t *result_pool) +{ + const char *local_abspath + = svn_dirent_join(wcroot->abspath, local_relpath, result_pool); + + return svn_dirent_local_style(local_abspath, result_pool); +} + +/* Ensure that there is a working copy lock for LOCAL_RELPATH in WCROOT */ +static svn_error_t * +verify_write_lock(svn_wc__db_wcroot_t *wcroot, + const char *local_relpath, + apr_pool_t *scratch_pool) +{ + svn_boolean_t locked; + + SVN_ERR(svn_wc__db_wclock_owns_lock_internal(&locked, wcroot, local_relpath, + FALSE, scratch_pool)); + if (!locked) + { + return svn_error_createf(SVN_ERR_WC_NOT_LOCKED, NULL, + _("No write-lock in '%s'"), + path_for_error_message(wcroot, local_relpath, + scratch_pool)); + } + + return SVN_NO_ERROR; +} + +/* In our merge conflicts we record the move_op_src path, which is essentially + the depth at which what was moved is marked deleted. The problem is that + this depth is not guaranteed to be stable, because somebody might just + remove another ancestor, or revert one. + + To work around this problem we locate the layer below this path, and use + that to pinpoint whatever is moved. + + For a path SRC_RELPATH that was deleted by an operation rooted at + DELETE_OP_DEPTH find the op-depth at which the node was originally added. + */ +static svn_error_t * +find_src_op_depth(int *src_op_depth, + svn_wc__db_wcroot_t *wcroot, + const char *src_relpath, + int delete_op_depth, + apr_pool_t *scratch_pool) +{ + svn_sqlite__stmt_t *stmt; + svn_boolean_t have_row; + + SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, + STMT_SELECT_HIGHEST_WORKING_NODE)); + SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, + src_relpath, delete_op_depth)); + + SVN_ERR(svn_sqlite__step(&have_row, stmt)); + if (have_row) + *src_op_depth = svn_sqlite__column_int(stmt, 0); + SVN_ERR(svn_sqlite__reset(stmt)); + if (!have_row) + return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL, + _("'%s' is not deleted"), + path_for_error_message(wcroot, src_relpath, + scratch_pool)); + + return SVN_NO_ERROR; +} + /* * Receiver code. * @@ -113,20 +187,34 @@ * be made at the move destination. */ -struct tc_editor_baton { +typedef struct update_move_baton_t { svn_wc__db_t *db; svn_wc__db_wcroot_t *wcroot; - const char *move_root_dst_relpath; - /* The most recent conflict raised during this drive. We rely on the - non-Ev2, depth-first, drive for this to make sense. */ - const char *conflict_root_relpath; + int src_op_depth; + int dst_op_depth; svn_wc_operation_t operation; svn_wc_conflict_version_t *old_version; svn_wc_conflict_version_t *new_version; - apr_pool_t *result_pool; /* For things that live as long as the baton. */ -}; + + svn_cancel_func_t cancel_func; + void *cancel_baton; +} update_move_baton_t; + +/* Per node flags for tree conflict collection */ +typedef struct node_move_baton_t +{ + svn_boolean_t skip; + svn_boolean_t shadowed; + svn_boolean_t edited; + + const char *src_relpath; + const char *dst_relpath; + + update_move_baton_t *umb; + struct node_move_baton_t *pb; +} node_move_baton_t; /* * Notifications are delayed until the entire update-move transaction @@ -135,24 +223,52 @@ struct tc_editor_baton { * and spooling notifications out of that table after the transaction. */ -/* Add an entry to the notification list. */ +/* Add an entry to the notification list, and at the same time install + a conflict and/or work items. */ static svn_error_t * update_move_list_add(svn_wc__db_wcroot_t *wcroot, const char *local_relpath, + svn_wc__db_t *db, svn_wc_notify_action_t action, svn_node_kind_t kind, svn_wc_notify_state_t content_state, - svn_wc_notify_state_t prop_state) - + svn_wc_notify_state_t prop_state, + svn_skel_t *conflict, + svn_skel_t *work_item, + apr_pool_t *scratch_pool) { svn_sqlite__stmt_t *stmt; + if (conflict) + { + svn_boolean_t tree_conflict; + + SVN_ERR(svn_wc__conflict_read_info(NULL, NULL, NULL, NULL, + &tree_conflict, + db, wcroot->abspath, conflict, + scratch_pool, scratch_pool)); + if (tree_conflict) + { + action = svn_wc_notify_tree_conflict; + content_state = svn_wc_notify_state_inapplicable; + prop_state = svn_wc_notify_state_inapplicable; + } + } + SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_INSERT_UPDATE_MOVE_LIST)); - SVN_ERR(svn_sqlite__bindf(stmt, "sdddd", local_relpath, - action, kind, content_state, prop_state)); + SVN_ERR(svn_sqlite__bindf(stmt, "sdtdd", local_relpath, + action, kind_map_none, kind, + content_state, prop_state)); SVN_ERR(svn_sqlite__step_done(stmt)); + if (conflict) + SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath, conflict, + scratch_pool)); + + if (work_item) + SVN_ERR(svn_wc__db_wq_add_internal(wcroot, work_item, scratch_pool)); + return SVN_NO_ERROR; } @@ -192,7 +308,7 @@ svn_wc__db_update_move_list_notify(svn_wc__db_wcroot_t *wcroot, local_relpath, iterpool), action, iterpool); - notify->kind = svn_sqlite__column_int(stmt, 2); + notify->kind = svn_sqlite__column_token(stmt, 2, kind_map_none); notify->content_state = svn_sqlite__column_int(stmt, 3); notify->prop_state = svn_sqlite__column_int(stmt, 4); notify->old_revision = old_revision; @@ -212,23 +328,25 @@ svn_wc__db_update_move_list_notify(svn_wc__db_wcroot_t *wcroot, return SVN_NO_ERROR; } -/* Mark a tree-conflict on LOCAL_RELPATH if such a tree-conflict does - not already exist. */ +/* Create a tree-conflict for recording on LOCAL_RELPATH if such + a tree-conflict does not already exist. */ static svn_error_t * -mark_tree_conflict(const char *local_relpath, - svn_wc__db_wcroot_t *wcroot, - svn_wc__db_t *db, - const svn_wc_conflict_version_t *old_version, - const svn_wc_conflict_version_t *new_version, - const char *move_root_dst_relpath, - svn_wc_operation_t operation, - svn_node_kind_t old_kind, - svn_node_kind_t new_kind, - const char *old_repos_relpath, - svn_wc_conflict_reason_t reason, - svn_wc_conflict_action_t action, - const char *move_src_op_root_relpath, - apr_pool_t *scratch_pool) +create_tree_conflict(svn_skel_t **conflict_p, + svn_wc__db_wcroot_t *wcroot, + const char *local_relpath, + const char *dst_op_root_relpath, + svn_wc__db_t *db, + const svn_wc_conflict_version_t *old_version, + const svn_wc_conflict_version_t *new_version, + svn_wc_operation_t operation, + svn_node_kind_t old_kind, + svn_node_kind_t new_kind, + const char *old_repos_relpath, + svn_wc_conflict_reason_t reason, + svn_wc_conflict_action_t action, + const char *move_src_op_root_relpath, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) { svn_error_t *err; svn_skel_t *conflict; @@ -250,14 +368,18 @@ mark_tree_conflict(const char *local_relpath, : NULL; if (!new_repos_relpath) - new_repos_relpath - = svn_relpath_join(new_version->path_in_repos, - svn_relpath_skip_ancestor(move_root_dst_relpath, - local_relpath), - scratch_pool); - - err = svn_wc__db_read_conflict_internal(&conflict, wcroot, local_relpath, - scratch_pool, scratch_pool); + { + const char *child_relpath = svn_relpath_skip_ancestor( + dst_op_root_relpath, + local_relpath); + SVN_ERR_ASSERT(child_relpath != NULL); + new_repos_relpath = svn_relpath_join(new_version->path_in_repos, + child_relpath, scratch_pool); + } + + err = svn_wc__db_read_conflict_internal(&conflict, NULL, NULL, + wcroot, local_relpath, + result_pool, scratch_pool); if (err && err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND) return svn_error_trace(err); else if (err) @@ -280,7 +402,7 @@ mark_tree_conflict(const char *local_relpath, && conflict_operation != svn_wc_operation_switch) return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL, _("'%s' already in conflict"), - svn_dirent_local_style(local_relpath, + path_for_error_message(wcroot, local_relpath, scratch_pool)); if (tree_conflicted) @@ -302,17 +424,19 @@ mark_tree_conflict(const char *local_relpath, && strcmp(move_src_op_root_relpath, svn_dirent_skip_ancestor(wcroot->abspath, existing_abspath)))) - return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL, + return svn_error_createf(SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL, _("'%s' already in conflict"), - svn_dirent_local_style(local_relpath, + path_for_error_message(wcroot, + local_relpath, scratch_pool)); /* Already a suitable tree-conflict. */ + *conflict_p = conflict; return SVN_NO_ERROR; } } else - conflict = svn_wc__conflict_skel_create(scratch_pool); + conflict = svn_wc__conflict_skel_create(result_pool); SVN_ERR(svn_wc__conflict_skel_add_tree_conflict( conflict, db, @@ -321,19 +445,13 @@ mark_tree_conflict(const char *local_relpath, reason, action, move_src_op_root_abspath, - scratch_pool, + result_pool, scratch_pool)); - if (reason != svn_wc_conflict_reason_unversioned - && old_repos_relpath != NULL /* no local additions */) - { - conflict_old_version = svn_wc_conflict_version_create2( + conflict_old_version = svn_wc_conflict_version_create2( old_version->repos_url, old_version->repos_uuid, old_repos_relpath, old_version->peg_rev, old_kind, scratch_pool); - } - else - conflict_old_version = NULL; conflict_new_version = svn_wc_conflict_version_create2( new_version->repos_url, new_version->repos_uuid, @@ -344,312 +462,368 @@ mark_tree_conflict(const char *local_relpath, { SVN_ERR(svn_wc__conflict_skel_set_op_update( conflict, conflict_old_version, conflict_new_version, - scratch_pool, scratch_pool)); + result_pool, scratch_pool)); } else { assert(operation == svn_wc_operation_switch); SVN_ERR(svn_wc__conflict_skel_set_op_switch( conflict, conflict_old_version, conflict_new_version, - scratch_pool, scratch_pool)); + result_pool, scratch_pool)); } - SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath, - conflict, scratch_pool)); - - SVN_ERR(update_move_list_add(wcroot, local_relpath, - svn_wc_notify_tree_conflict, - new_kind, - svn_wc_notify_state_inapplicable, - svn_wc_notify_state_inapplicable)); + *conflict_p = conflict; return SVN_NO_ERROR; } -/* If LOCAL_RELPATH is a child of the most recently raised - tree-conflict or is shadowed then set *IS_CONFLICTED to TRUE and - raise a tree-conflict on the root of the obstruction if such a - tree-conflict does not already exist. KIND is the kind of the - incoming LOCAL_RELPATH. This relies on the non-Ev2, depth-first, - drive. */ static svn_error_t * -check_tree_conflict(svn_boolean_t *is_conflicted, - struct tc_editor_baton *b, +create_node_tree_conflict(svn_skel_t **conflict_p, + node_move_baton_t *nmb, + const char *dst_local_relpath, + svn_node_kind_t old_kind, + svn_node_kind_t new_kind, + svn_wc_conflict_reason_t reason, + svn_wc_conflict_action_t action, + const char *move_src_op_root_relpath, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + update_move_baton_t *umb = nmb->umb; + const char *dst_repos_relpath; + const char *dst_root_relpath = svn_relpath_prefix(nmb->dst_relpath, + nmb->umb->dst_op_depth, + scratch_pool); + + dst_repos_relpath = + svn_relpath_join(nmb->umb->old_version->path_in_repos, + svn_relpath_skip_ancestor(dst_root_relpath, + nmb->dst_relpath), + scratch_pool); + + + + return svn_error_trace( + create_tree_conflict(conflict_p, umb->wcroot, dst_local_relpath, + svn_relpath_prefix(dst_local_relpath, + umb->dst_op_depth, + scratch_pool), + umb->db, + umb->old_version, umb->new_version, + umb->operation, old_kind, new_kind, + dst_repos_relpath, + reason, action, move_src_op_root_relpath, + result_pool, scratch_pool)); +} + +/* Checks if a specific local path is shadowed as seen from the move root. + Helper for update_moved_away_node() */ +static svn_error_t * +check_node_shadowed(svn_boolean_t *shadowed, + svn_wc__db_wcroot_t *wcroot, const char *local_relpath, - svn_node_kind_t old_kind, - svn_node_kind_t new_kind, - const char *old_repos_relpath, - svn_wc_conflict_action_t action, + int move_root_dst_op_depth, apr_pool_t *scratch_pool) { svn_sqlite__stmt_t *stmt; svn_boolean_t have_row; - int dst_op_depth = relpath_depth(b->move_root_dst_relpath); - int op_depth; - const char *conflict_root_relpath = local_relpath; - const char *move_dst_relpath, *dummy1; - const char *dummy2, *move_src_op_root_relpath; - if (b->conflict_root_relpath) - { - if (svn_relpath_skip_ancestor(b->conflict_root_relpath, local_relpath)) - { - *is_conflicted = TRUE; - return SVN_NO_ERROR; - } - b->conflict_root_relpath = NULL; - } + SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, + STMT_SELECT_WORKING_NODE)); + SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath)); - SVN_ERR(svn_sqlite__get_statement(&stmt, b->wcroot->sdb, - STMT_SELECT_LOWEST_WORKING_NODE)); - SVN_ERR(svn_sqlite__bindf(stmt, "isd", b->wcroot->wc_id, local_relpath, - dst_op_depth)); SVN_ERR(svn_sqlite__step(&have_row, stmt)); + if (have_row) - op_depth = svn_sqlite__column_int(stmt, 0); + { + int op_depth = svn_sqlite__column_int(stmt, 0); + + *shadowed = (op_depth > move_root_dst_op_depth); + } + else + *shadowed = FALSE; SVN_ERR(svn_sqlite__reset(stmt)); - if (!have_row) + return SVN_NO_ERROR; +} + +/* Set a tree conflict for the shadowed node LOCAL_RELPATH, which is + the ROOT OF THE OBSTRUCTION if such a tree-conflict does not + already exist. KIND is the kind of the incoming LOCAL_RELPATH. */ +static svn_error_t * +mark_tc_on_op_root(node_move_baton_t *nmb, + svn_node_kind_t old_kind, + svn_node_kind_t new_kind, + svn_wc_conflict_action_t action, + apr_pool_t *scratch_pool) +{ + update_move_baton_t *b = nmb->umb; + const char *move_dst_relpath; + svn_skel_t *conflict; + + SVN_ERR_ASSERT(nmb->shadowed && !nmb->pb->shadowed); + + nmb->skip = TRUE; + + if (old_kind == svn_node_none) + move_dst_relpath = NULL; + else + SVN_ERR(svn_wc__db_scan_moved_to_internal(NULL, &move_dst_relpath, NULL, + b->wcroot, nmb->dst_relpath, + b->dst_op_depth, + scratch_pool, scratch_pool)); + + SVN_ERR(create_node_tree_conflict(&conflict, nmb, nmb->dst_relpath, + old_kind, new_kind, + (move_dst_relpath + ? svn_wc_conflict_reason_moved_away + : svn_wc_conflict_reason_deleted), + action, move_dst_relpath + ? nmb->dst_relpath + : NULL, + scratch_pool, scratch_pool)); + + SVN_ERR(update_move_list_add(b->wcroot, nmb->dst_relpath, b->db, + svn_wc_notify_tree_conflict, + new_kind, + svn_wc_notify_state_inapplicable, + svn_wc_notify_state_inapplicable, + conflict, NULL, scratch_pool)); + + return SVN_NO_ERROR; +} + +static svn_error_t * +mark_node_edited(node_move_baton_t *nmb, + apr_pool_t *scratch_pool) +{ + if (nmb->edited) + return SVN_NO_ERROR; + + if (nmb->pb) { - *is_conflicted = FALSE; - return SVN_NO_ERROR; + SVN_ERR(mark_node_edited(nmb->pb, scratch_pool)); + + if (nmb->pb->skip) + nmb->skip = TRUE; } - *is_conflicted = TRUE; + nmb->edited = TRUE; - while (relpath_depth(conflict_root_relpath) > op_depth) + if (nmb->skip) + return SVN_NO_ERROR; + + if (nmb->shadowed && !(nmb->pb && nmb->pb->shadowed)) { - conflict_root_relpath = svn_relpath_dirname(conflict_root_relpath, - scratch_pool); - old_kind = new_kind = svn_node_dir; - if (old_repos_relpath) - old_repos_relpath = svn_relpath_dirname(old_repos_relpath, - scratch_pool); - action = svn_wc_conflict_action_edit; + svn_node_kind_t dst_kind, src_kind; + + SVN_ERR(svn_wc__db_depth_get_info(NULL, &dst_kind, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, + nmb->umb->wcroot, nmb->dst_relpath, + nmb->umb->dst_op_depth, + scratch_pool, scratch_pool)); + + SVN_ERR(svn_wc__db_depth_get_info(NULL, &src_kind, NULL, NULL, + NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, + nmb->umb->wcroot, nmb->src_relpath, + nmb->umb->src_op_depth, + scratch_pool, scratch_pool)); + + SVN_ERR(mark_tc_on_op_root(nmb, + dst_kind, src_kind, + svn_wc_conflict_action_edit, + scratch_pool)); } - SVN_ERR(svn_wc__db_op_depth_moved_to(&move_dst_relpath, - &dummy1, - &dummy2, - &move_src_op_root_relpath, - dst_op_depth, - b->wcroot, conflict_root_relpath, - scratch_pool, scratch_pool)); - - SVN_ERR(mark_tree_conflict(conflict_root_relpath, - b->wcroot, b->db, b->old_version, b->new_version, - b->move_root_dst_relpath, b->operation, - old_kind, new_kind, - old_repos_relpath, - (move_dst_relpath - ? svn_wc_conflict_reason_moved_away - : svn_wc_conflict_reason_deleted), - action, move_src_op_root_relpath, - scratch_pool)); - b->conflict_root_relpath = apr_pstrdup(b->result_pool, conflict_root_relpath); + return SVN_NO_ERROR; +} + +static svn_error_t * +mark_parent_edited(node_move_baton_t *nmb, + apr_pool_t *scratch_pool) +{ + SVN_ERR_ASSERT(nmb && nmb->pb); + + SVN_ERR(mark_node_edited(nmb->pb, scratch_pool)); + + if (nmb->pb->skip) + nmb->skip = TRUE; return SVN_NO_ERROR; } static svn_error_t * -tc_editor_add_directory(void *baton, +tc_editor_add_directory(node_move_baton_t *nmb, const char *relpath, - const apr_array_header_t *children, + svn_node_kind_t old_kind, apr_hash_t *props, - svn_revnum_t replaces_rev, apr_pool_t *scratch_pool) { - struct tc_editor_baton *b = baton; - int op_depth = relpath_depth(b->move_root_dst_relpath); - const char *move_dst_repos_relpath; - svn_node_kind_t move_dst_kind; - svn_boolean_t is_conflicted; - const char *abspath; - svn_node_kind_t old_kind; - svn_skel_t *work_item; - svn_wc_notify_action_t action = svn_wc_notify_update_add; - svn_error_t *err; + update_move_baton_t *b = nmb->umb; + const char *local_abspath; + svn_node_kind_t wc_kind; + svn_skel_t *work_item = NULL; + svn_skel_t *conflict = NULL; + svn_wc_conflict_reason_t reason = svn_wc_conflict_reason_unversioned; + + SVN_ERR(mark_parent_edited(nmb, scratch_pool)); + if (nmb->skip) + return SVN_NO_ERROR; - /* Update NODES, only the bits not covered by the later call to - replace_moved_layer. */ - SVN_ERR(svn_wc__db_extend_parent_delete(b->wcroot, relpath, svn_node_dir, - op_depth, scratch_pool)); - - err = svn_wc__db_depth_get_info(NULL, &move_dst_kind, NULL, - &move_dst_repos_relpath, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, - b->wcroot, relpath, - relpath_depth(b->move_root_dst_relpath), - scratch_pool, scratch_pool); - if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND) + if (nmb->shadowed) { - svn_error_clear(err); - old_kind = svn_node_none; - move_dst_repos_relpath = NULL; + svn_wc__db_status_t status; + + SVN_ERR(svn_wc__db_read_info_internal(&status, &wc_kind, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, + b->wcroot, relpath, + scratch_pool, scratch_pool)); + + if (status == svn_wc__db_status_deleted) + reason = svn_wc_conflict_reason_deleted; + else if (status != svn_wc__db_status_added) + wc_kind = svn_node_none; + else if (old_kind == svn_node_none) + reason = svn_wc_conflict_reason_added; + else + reason = svn_wc_conflict_reason_replaced; } else + wc_kind = svn_node_none; + + local_abspath = svn_dirent_join(b->wcroot->abspath, relpath, scratch_pool); + + if (wc_kind == svn_node_none) { - SVN_ERR(err); - old_kind = move_dst_kind; + /* Check for unversioned tree-conflict */ + SVN_ERR(svn_io_check_path(local_abspath, &wc_kind, scratch_pool)); } - /* Check for NODES tree-conflict. */ - SVN_ERR(check_tree_conflict(&is_conflicted, b, relpath, - old_kind, svn_node_dir, - move_dst_repos_relpath, - svn_wc_conflict_action_add, - scratch_pool)); - if (is_conflicted) - return SVN_NO_ERROR; + if (!nmb->shadowed && wc_kind == old_kind) + wc_kind = svn_node_none; /* Node will be gone once we install */ - /* Check for unversioned tree-conflict */ - abspath = svn_dirent_join(b->wcroot->abspath, relpath, scratch_pool); - SVN_ERR(svn_io_check_path(abspath, &old_kind, scratch_pool)); - - switch (old_kind) + if (wc_kind != svn_node_none + && (nmb->shadowed || wc_kind != old_kind)) /* replace */ { - case svn_node_file: - default: - SVN_ERR(mark_tree_conflict(relpath, b->wcroot, b->db, b->old_version, - b->new_version, b->move_root_dst_relpath, - b->operation, old_kind, svn_node_dir, - move_dst_repos_relpath, - svn_wc_conflict_reason_unversioned, - svn_wc_conflict_action_add, NULL, - scratch_pool)); - b->conflict_root_relpath = apr_pstrdup(b->result_pool, relpath); - action = svn_wc_notify_tree_conflict; - is_conflicted = TRUE; - break; - - case svn_node_none: - SVN_ERR(svn_wc__wq_build_dir_install(&work_item, b->db, abspath, + SVN_ERR(create_node_tree_conflict(&conflict, nmb, relpath, + old_kind, svn_node_dir, + reason, + (old_kind == svn_node_none) + ? svn_wc_conflict_action_add + : svn_wc_conflict_action_replace, + NULL, + scratch_pool, scratch_pool)); + nmb->skip = TRUE; + } + else + { + SVN_ERR(svn_wc__wq_build_dir_install(&work_item, b->db, local_abspath, scratch_pool, scratch_pool)); - - SVN_ERR(svn_wc__db_wq_add(b->db, b->wcroot->abspath, work_item, - scratch_pool)); - /* Fall through */ - case svn_node_dir: - break; } - if (!is_conflicted) - SVN_ERR(update_move_list_add(b->wcroot, relpath, - action, - svn_node_dir, - svn_wc_notify_state_inapplicable, - svn_wc_notify_state_inapplicable)); + SVN_ERR(update_move_list_add(b->wcroot, relpath, b->db, + (old_kind == svn_node_none) + ? svn_wc_notify_update_add + : svn_wc_notify_update_replace, + svn_node_dir, + svn_wc_notify_state_inapplicable, + svn_wc_notify_state_inapplicable, + conflict, work_item, scratch_pool)); return SVN_NO_ERROR; } static svn_error_t * -tc_editor_add_file(void *baton, +tc_editor_add_file(node_move_baton_t *nmb, const char *relpath, + svn_node_kind_t old_kind, const svn_checksum_t *checksum, - svn_stream_t *contents, apr_hash_t *props, - svn_revnum_t replaces_rev, apr_pool_t *scratch_pool) { - struct tc_editor_baton *b = baton; - int op_depth = relpath_depth(b->move_root_dst_relpath); - const char *move_dst_repos_relpath; - svn_node_kind_t move_dst_kind; - svn_node_kind_t old_kind; - svn_boolean_t is_conflicted; - const char *abspath; - svn_skel_t *work_item; - svn_error_t *err; + update_move_baton_t *b = nmb->umb; + svn_wc_conflict_reason_t reason = svn_wc_conflict_reason_unversioned; + svn_node_kind_t wc_kind; + const char *local_abspath; + svn_skel_t *work_item = NULL; + svn_skel_t *conflict = NULL; + + SVN_ERR(mark_parent_edited(nmb, scratch_pool)); + if (nmb->skip) + return SVN_NO_ERROR; - /* Update NODES, only the bits not covered by the later call to - replace_moved_layer. */ - SVN_ERR(svn_wc__db_extend_parent_delete(b->wcroot, relpath, svn_node_file, - op_depth, scratch_pool)); - - err = svn_wc__db_depth_get_info(NULL, &move_dst_kind, NULL, - &move_dst_repos_relpath, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, - b->wcroot, relpath, - relpath_depth(b->move_root_dst_relpath), - scratch_pool, scratch_pool); - if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND) + if (nmb->shadowed) { - svn_error_clear(err); - old_kind = svn_node_none; - move_dst_repos_relpath = NULL; + svn_wc__db_status_t status; + + SVN_ERR(svn_wc__db_read_info_internal(&status, &wc_kind, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, + b->wcroot, relpath, + scratch_pool, scratch_pool)); + + if (status == svn_wc__db_status_deleted) + reason = svn_wc_conflict_reason_deleted; + else if (status != svn_wc__db_status_added) + wc_kind = svn_node_none; + else if (old_kind == svn_node_none) + reason = svn_wc_conflict_reason_added; + else + reason = svn_wc_conflict_reason_replaced; } else - { - SVN_ERR(err); - old_kind = move_dst_kind; - } - - /* Check for NODES tree-conflict. */ - SVN_ERR(check_tree_conflict(&is_conflicted, b, relpath, - old_kind, svn_node_file, move_dst_repos_relpath, - svn_wc_conflict_action_add, - scratch_pool)); - if (is_conflicted) - return SVN_NO_ERROR; + wc_kind = svn_node_none; - /* Check for unversioned tree-conflict */ - abspath = svn_dirent_join(b->wcroot->abspath, relpath, scratch_pool); - SVN_ERR(svn_io_check_path(abspath, &old_kind, scratch_pool)); + local_abspath = svn_dirent_join(b->wcroot->abspath, relpath, scratch_pool); - if (old_kind != svn_node_none) + if (wc_kind == svn_node_none) { - SVN_ERR(mark_tree_conflict(relpath, b->wcroot, b->db, b->old_version, - b->new_version, b->move_root_dst_relpath, - b->operation, old_kind, svn_node_file, - move_dst_repos_relpath, - svn_wc_conflict_reason_unversioned, - svn_wc_conflict_action_add, NULL, - scratch_pool)); - b->conflict_root_relpath = apr_pstrdup(b->result_pool, relpath); - return SVN_NO_ERROR; + /* Check for unversioned tree-conflict */ + SVN_ERR(svn_io_check_path(local_abspath, &wc_kind, scratch_pool)); } - /* Update working file. */ - SVN_ERR(svn_wc__wq_build_file_install(&work_item, b->db, - svn_dirent_join(b->wcroot->abspath, - relpath, - scratch_pool), + if (wc_kind != svn_node_none + && (nmb->shadowed || wc_kind != old_kind)) /* replace */ + { + SVN_ERR(create_node_tree_conflict(&conflict, nmb, relpath, + old_kind, svn_node_file, + reason, + (old_kind == svn_node_none) + ? svn_wc_conflict_action_add + : svn_wc_conflict_action_replace, NULL, - FALSE /* FIXME: use_commit_times? */, - TRUE /* record_file_info */, scratch_pool, scratch_pool)); + nmb->skip = TRUE; + } + else + { + /* Update working file. */ + SVN_ERR(svn_wc__wq_build_file_install(&work_item, b->db, + svn_dirent_join(b->wcroot->abspath, + relpath, + scratch_pool), + NULL, + FALSE /*FIXME: use_commit_times?*/, + TRUE /* record_file_info */, + scratch_pool, scratch_pool)); + } - SVN_ERR(svn_wc__db_wq_add(b->db, b->wcroot->abspath, work_item, - scratch_pool)); - - SVN_ERR(update_move_list_add(b->wcroot, relpath, - svn_wc_notify_update_add, + SVN_ERR(update_move_list_add(b->wcroot, relpath, b->db, + (old_kind == svn_node_none) + ? svn_wc_notify_update_add + : svn_wc_notify_update_replace, svn_node_file, svn_wc_notify_state_inapplicable, - svn_wc_notify_state_inapplicable)); + svn_wc_notify_state_inapplicable, + conflict, work_item, scratch_pool)); return SVN_NO_ERROR; } -static svn_error_t * -tc_editor_add_symlink(void *baton, - const char *relpath, - const char *target, - apr_hash_t *props, - svn_revnum_t replaces_rev, - apr_pool_t *scratch_pool) -{ - return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL, NULL); -} - -static svn_error_t * -tc_editor_add_absent(void *baton, - const char *relpath, - svn_node_kind_t kind, - svn_revnum_t replaces_rev, - apr_pool_t *scratch_pool) -{ - return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL, NULL); -} - /* All the info we need about one version of a working node. */ typedef struct working_node_version_t { @@ -669,6 +843,7 @@ create_conflict_markers(svn_skel_t **work_items, const working_node_version_t *old_version, const working_node_version_t *new_version, svn_node_kind_t kind, + svn_boolean_t set_operation, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { @@ -689,19 +864,22 @@ create_conflict_markers(svn_skel_t **work_items, = svn_relpath_join(conflicted_version->path_in_repos, part, scratch_pool); original_version->path_in_repos = repos_relpath; - if (operation == svn_wc_operation_update) + if (set_operation) { - SVN_ERR(svn_wc__conflict_skel_set_op_update( - conflict_skel, original_version, - conflicted_version, - scratch_pool, scratch_pool)); - } - else - { - SVN_ERR(svn_wc__conflict_skel_set_op_switch( - conflict_skel, original_version, - conflicted_version, - scratch_pool, scratch_pool)); + if (operation == svn_wc_operation_update) + { + SVN_ERR(svn_wc__conflict_skel_set_op_update( + conflict_skel, original_version, + conflicted_version, + scratch_pool, scratch_pool)); + } + else + { + SVN_ERR(svn_wc__conflict_skel_set_op_switch( + conflict_skel, original_version, + conflicted_version, + scratch_pool, scratch_pool)); + } } /* According to this func's doc string, it is "Currently only used for @@ -720,8 +898,8 @@ update_working_props(svn_wc_notify_state_t *prop_state, svn_skel_t **conflict_skel, apr_array_header_t **propchanges, apr_hash_t **actual_props, - svn_wc__db_t *db, - const char *local_abspath, + update_move_baton_t *b, + const char *local_relpath, const struct working_node_version_t *old_version, const struct working_node_version_t *new_version, apr_pool_t *result_pool, @@ -736,139 +914,123 @@ update_working_props(svn_wc_notify_state_t *prop_state, * merge-left version, and the current props of the * moved-here working file as the merge-right version. */ - SVN_ERR(svn_wc__db_read_props(actual_props, - db, local_abspath, - result_pool, scratch_pool)); + SVN_ERR(svn_wc__db_read_props_internal(actual_props, + b->wcroot, local_relpath, + result_pool, scratch_pool)); SVN_ERR(svn_prop_diffs(propchanges, new_version->props, old_version->props, result_pool)); SVN_ERR(svn_wc__merge_props(conflict_skel, prop_state, &new_actual_props, - db, local_abspath, + b->db, svn_dirent_join(b->wcroot->abspath, + local_relpath, + scratch_pool), old_version->props, old_version->props, *actual_props, *propchanges, result_pool, scratch_pool)); - /* Setting properties in ACTUAL_NODE with svn_wc__db_op_set_props - relies on NODES row having been updated first which we don't do - at present. So this extra property diff has the same effect. + /* Setting properties in ACTUAL_NODE with svn_wc__db_op_set_props_internal + relies on NODES row being updated via a different route . - ### Perhaps we should update NODES first (but after - ### svn_wc__db_read_props above)? */ + This extra property diff makes sure we clear the actual row when + the final result is unchanged properties. */ SVN_ERR(svn_prop_diffs(&new_propchanges, new_actual_props, new_version->props, scratch_pool)); if (!new_propchanges->nelts) new_actual_props = NULL; - /* Install the new actual props. Don't set the conflict_skel yet, because - we might need to add a text conflict to it as well. */ - SVN_ERR(svn_wc__db_op_set_props(db, local_abspath, - new_actual_props, - svn_wc__has_magic_property(*propchanges), - NULL/*conflict_skel*/, NULL/*work_items*/, - scratch_pool)); + /* Install the new actual props. */ + SVN_ERR(svn_wc__db_op_set_props_internal(b->wcroot, local_relpath, + new_actual_props, + svn_wc__has_magic_property( + *propchanges), + scratch_pool)); return SVN_NO_ERROR; } static svn_error_t * -tc_editor_alter_directory(void *baton, +tc_editor_alter_directory(node_move_baton_t *nmb, const char *dst_relpath, - svn_revnum_t expected_move_dst_revision, - const apr_array_header_t *children, + apr_hash_t *old_props, apr_hash_t *new_props, apr_pool_t *scratch_pool) { - struct tc_editor_baton *b = baton; - const char *move_dst_repos_relpath; - svn_revnum_t move_dst_revision; - svn_node_kind_t move_dst_kind; + update_move_baton_t *b = nmb->umb; working_node_version_t old_version, new_version; - svn_wc__db_status_t status; - svn_boolean_t is_conflicted; - - SVN_ERR_ASSERT(expected_move_dst_revision == b->old_version->peg_rev); - - SVN_ERR(svn_wc__db_depth_get_info(&status, &move_dst_kind, &move_dst_revision, - &move_dst_repos_relpath, NULL, NULL, NULL, - NULL, NULL, &old_version.checksum, NULL, - NULL, &old_version.props, - b->wcroot, dst_relpath, - relpath_depth(b->move_root_dst_relpath), - scratch_pool, scratch_pool)); + svn_skel_t *work_items = NULL; + svn_skel_t *conflict_skel = NULL; + const char *local_abspath = svn_dirent_join(b->wcroot->abspath, dst_relpath, + scratch_pool); + svn_wc_notify_state_t prop_state; + apr_hash_t *actual_props; + apr_array_header_t *propchanges; + svn_node_kind_t wc_kind; + svn_boolean_t obstructed = FALSE; - /* If the node would be recorded as svn_wc__db_status_base_deleted it - wouldn't have a repos_relpath */ - /* ### Can svn_wc__db_depth_get_info() do this for us without this hint? */ - if (status == svn_wc__db_status_deleted && move_dst_repos_relpath) - status = svn_wc__db_status_not_present; - - /* There might be not-present nodes of a different revision as the same - depth as a copy. This is commonly caused by copying/moving mixed revision - directories */ - SVN_ERR_ASSERT(move_dst_revision == expected_move_dst_revision - || status == svn_wc__db_status_not_present); - SVN_ERR_ASSERT(move_dst_kind == svn_node_dir); - - SVN_ERR(check_tree_conflict(&is_conflicted, b, dst_relpath, - move_dst_kind, - svn_node_dir, - move_dst_repos_relpath, - svn_wc_conflict_action_edit, - scratch_pool)); - if (is_conflicted) + SVN_ERR(mark_node_edited(nmb, scratch_pool)); + if (nmb->skip) return SVN_NO_ERROR; + SVN_ERR(svn_io_check_path(local_abspath, &wc_kind, scratch_pool)); + if (wc_kind != svn_node_none && wc_kind != svn_node_dir) + { + SVN_ERR(create_node_tree_conflict(&conflict_skel, nmb, dst_relpath, + svn_node_dir, svn_node_dir, + svn_wc_conflict_reason_obstructed, + svn_wc_conflict_action_edit, + NULL, + scratch_pool, scratch_pool)); + obstructed = TRUE; + } + old_version.location_and_kind = b->old_version; new_version.location_and_kind = b->new_version; + old_version.checksum = NULL; /* not a file */ + old_version.props = old_props; new_version.checksum = NULL; /* not a file */ - new_version.props = new_props ? new_props : old_version.props; + new_version.props = new_props; - if (new_props) + SVN_ERR(update_working_props(&prop_state, &conflict_skel, + &propchanges, &actual_props, + b, dst_relpath, + &old_version, &new_version, + scratch_pool, scratch_pool)); + + if (prop_state == svn_wc_notify_state_conflicted) { - const char *dst_abspath = svn_dirent_join(b->wcroot->abspath, - dst_relpath, - scratch_pool); - svn_wc_notify_state_t prop_state; - svn_skel_t *conflict_skel = NULL; - apr_hash_t *actual_props; - apr_array_header_t *propchanges; - - SVN_ERR(update_working_props(&prop_state, &conflict_skel, - &propchanges, &actual_props, - b->db, dst_abspath, - &old_version, &new_version, - scratch_pool, scratch_pool)); - - if (conflict_skel) - { - svn_skel_t *work_items; - - SVN_ERR(create_conflict_markers(&work_items, dst_abspath, - b->db, move_dst_repos_relpath, - conflict_skel, b->operation, - &old_version, &new_version, - svn_node_dir, - scratch_pool, scratch_pool)); - SVN_ERR(svn_wc__db_mark_conflict_internal(b->wcroot, dst_relpath, - conflict_skel, - scratch_pool)); - SVN_ERR(svn_wc__db_wq_add(b->db, b->wcroot->abspath, work_items, - scratch_pool)); - } + const char *move_dst_repos_relpath; - SVN_ERR(update_move_list_add(b->wcroot, dst_relpath, - svn_wc_notify_update_update, - svn_node_dir, - svn_wc_notify_state_inapplicable, - prop_state)); + SVN_ERR(svn_wc__db_depth_get_info(NULL, NULL, NULL, + &move_dst_repos_relpath, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, + NULL, + b->wcroot, dst_relpath, + b->dst_op_depth, + scratch_pool, scratch_pool)); + + SVN_ERR(create_conflict_markers(&work_items, local_abspath, + b->db, move_dst_repos_relpath, + conflict_skel, b->operation, + &old_version, &new_version, + svn_node_dir, !obstructed, + scratch_pool, scratch_pool)); } + SVN_ERR(update_move_list_add(b->wcroot, dst_relpath, b->db, + svn_wc_notify_update_update, + svn_node_dir, + svn_wc_notify_state_inapplicable, + prop_state, + conflict_skel, work_items, scratch_pool)); + return SVN_NO_ERROR; } - -/* Merge the difference between OLD_VERSION and NEW_VERSION into +/* Edit the file found at the move destination, which is initially at + * the old state. Merge the changes into the "working"/"actual" file. + * + * Merge the difference between OLD_VERSION and NEW_VERSION into * the working file at LOCAL_RELPATH. * * The term 'old' refers to the pre-update state, which is the state of @@ -883,17 +1045,18 @@ tc_editor_alter_directory(void *baton, * Set *WORK_ITEMS to any required work items, allocated in RESULT_POOL. * Use SCRATCH_POOL for temporary allocations. */ static svn_error_t * -update_working_file(const char *local_relpath, - const char *repos_relpath, - svn_wc_operation_t operation, - const working_node_version_t *old_version, - const working_node_version_t *new_version, - svn_wc__db_wcroot_t *wcroot, - svn_wc__db_t *db, - apr_pool_t *scratch_pool) +tc_editor_alter_file(node_move_baton_t *nmb, + const char *dst_relpath, + const svn_checksum_t *old_checksum, + const svn_checksum_t *new_checksum, + apr_hash_t *old_props, + apr_hash_t *new_props, + apr_pool_t *scratch_pool) { - const char *local_abspath = svn_dirent_join(wcroot->abspath, - local_relpath, + update_move_baton_t *b = nmb->umb; + working_node_version_t old_version, new_version; + const char *local_abspath = svn_dirent_join(b->wcroot->abspath, + dst_relpath, scratch_pool); const char *old_pristine_abspath; const char *new_pristine_abspath; @@ -903,23 +1066,51 @@ update_working_file(const char *local_relpath, enum svn_wc_merge_outcome_t merge_outcome; svn_wc_notify_state_t prop_state, content_state; svn_skel_t *work_item, *work_items = NULL; + svn_node_kind_t wc_kind; + svn_boolean_t obstructed = FALSE; + SVN_ERR(mark_node_edited(nmb, scratch_pool)); + if (nmb->skip) + return SVN_NO_ERROR; + + SVN_ERR(svn_io_check_path(local_abspath, &wc_kind, scratch_pool)); + if (wc_kind != svn_node_none && wc_kind != svn_node_file) + { + SVN_ERR(create_node_tree_conflict(&conflict_skel, nmb, dst_relpath, + svn_node_file, svn_node_file, + svn_wc_conflict_reason_obstructed, + svn_wc_conflict_action_edit, + NULL, + scratch_pool, scratch_pool)); + obstructed = TRUE; + } + + old_version.location_and_kind = b->old_version; + new_version.location_and_kind = b->new_version; + + old_version.checksum = old_checksum; + old_version.props = old_props; + new_version.checksum = new_checksum; + new_version.props = new_props; + + /* ### TODO: Only do this when there is no higher WORKING layer */ SVN_ERR(update_working_props(&prop_state, &conflict_skel, &propchanges, - &actual_props, db, local_abspath, - old_version, new_version, + &actual_props, b, dst_relpath, + &old_version, &new_version, scratch_pool, scratch_pool)); - if (!svn_checksum_match(new_version->checksum, old_version->checksum)) + if (!obstructed + && !svn_checksum_match(new_version.checksum, old_version.checksum)) { svn_boolean_t is_locally_modified; SVN_ERR(svn_wc__internal_file_modified_p(&is_locally_modified, - db, local_abspath, + b->db, local_abspath, FALSE /* exact_comparison */, scratch_pool)); if (!is_locally_modified) { - SVN_ERR(svn_wc__wq_build_file_install(&work_item, db, + SVN_ERR(svn_wc__wq_build_file_install(&work_item, b->db, local_abspath, NULL, FALSE /* FIXME: use_commit_times? */, @@ -939,15 +1130,15 @@ update_working_file(const char *local_relpath, * moved-here working file as the merge-right version. */ SVN_ERR(svn_wc__db_pristine_get_path(&old_pristine_abspath, - db, wcroot->abspath, - old_version->checksum, + b->db, b->wcroot->abspath, + old_version.checksum, scratch_pool, scratch_pool)); SVN_ERR(svn_wc__db_pristine_get_path(&new_pristine_abspath, - db, wcroot->abspath, - new_version->checksum, + b->db, b->wcroot->abspath, + new_version.checksum, scratch_pool, scratch_pool)); SVN_ERR(svn_wc__internal_merge(&work_item, &conflict_skel, - &merge_outcome, db, + &merge_outcome, b->db, old_pristine_abspath, new_pristine_abspath, local_abspath, @@ -958,7 +1149,7 @@ update_working_file(const char *local_relpath, NULL, /* diff3-cmd */ NULL, /* merge options */ propchanges, - NULL, NULL, /* cancel_func + baton */ + b->cancel_func, b->cancel_baton, scratch_pool, scratch_pool)); work_items = svn_wc__wq_merge(work_items, work_item, scratch_pool); @@ -976,207 +1167,113 @@ update_working_file(const char *local_relpath, * too. */ if (conflict_skel) { - SVN_ERR(create_conflict_markers(&work_item, local_abspath, db, - repos_relpath, conflict_skel, - operation, old_version, new_version, - svn_node_file, - scratch_pool, scratch_pool)); + const char *move_dst_repos_relpath; - SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath, - conflict_skel, - scratch_pool)); + SVN_ERR(svn_wc__db_depth_get_info(NULL, NULL, NULL, + &move_dst_repos_relpath, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, + NULL, + b->wcroot, dst_relpath, + b->dst_op_depth, + scratch_pool, scratch_pool)); + + SVN_ERR(create_conflict_markers(&work_item, local_abspath, b->db, + move_dst_repos_relpath, conflict_skel, + b->operation, &old_version, &new_version, + svn_node_file, !obstructed, + scratch_pool, scratch_pool)); work_items = svn_wc__wq_merge(work_items, work_item, scratch_pool); } - SVN_ERR(svn_wc__db_wq_add(db, wcroot->abspath, work_items, scratch_pool)); - - SVN_ERR(update_move_list_add(wcroot, local_relpath, + SVN_ERR(update_move_list_add(b->wcroot, dst_relpath, b->db, svn_wc_notify_update_update, svn_node_file, content_state, - prop_state)); + prop_state, + conflict_skel, work_items, scratch_pool)); return SVN_NO_ERROR; } - -/* Edit the file found at the move destination, which is initially at - * the old state. Merge the changes into the "working"/"actual" file. - */ static svn_error_t * -tc_editor_alter_file(void *baton, - const char *dst_relpath, - svn_revnum_t expected_move_dst_revision, - apr_hash_t *new_props, - const svn_checksum_t *new_checksum, - svn_stream_t *new_contents, - apr_pool_t *scratch_pool) -{ - struct tc_editor_baton *b = baton; - const char *move_dst_repos_relpath; - svn_revnum_t move_dst_revision; - svn_node_kind_t move_dst_kind; - working_node_version_t old_version, new_version; - svn_boolean_t is_conflicted; - svn_wc__db_status_t status; - - SVN_ERR(svn_wc__db_depth_get_info(&status, &move_dst_kind, &move_dst_revision, - &move_dst_repos_relpath, NULL, NULL, NULL, - NULL, NULL, &old_version.checksum, NULL, - NULL, &old_version.props, - b->wcroot, dst_relpath, - relpath_depth(b->move_root_dst_relpath), - scratch_pool, scratch_pool)); - - /* If the node would be recorded as svn_wc__db_status_base_deleted it - wouldn't have a repos_relpath */ - /* ### Can svn_wc__db_depth_get_info() do this for us without this hint? */ - if (status == svn_wc__db_status_deleted && move_dst_repos_relpath) - status = svn_wc__db_status_not_present; - - SVN_ERR_ASSERT(move_dst_revision == expected_move_dst_revision - || status == svn_wc__db_status_not_present); - SVN_ERR_ASSERT(move_dst_kind == svn_node_file); - - SVN_ERR(check_tree_conflict(&is_conflicted, b, dst_relpath, - move_dst_kind, - svn_node_file, - move_dst_repos_relpath, - svn_wc_conflict_action_edit, - scratch_pool)); - if (is_conflicted) - return SVN_NO_ERROR; - - old_version.location_and_kind = b->old_version; - new_version.location_and_kind = b->new_version; - - /* If new checksum is null that means no change; similarly props. */ - new_version.checksum = new_checksum ? new_checksum : old_version.checksum; - new_version.props = new_props ? new_props : old_version.props; - - /* Update file and prop contents if the update has changed them. */ - if (!svn_checksum_match(new_checksum, old_version.checksum) || new_props) - { - SVN_ERR(update_working_file(dst_relpath, move_dst_repos_relpath, - b->operation, &old_version, &new_version, - b->wcroot, b->db, - scratch_pool)); - } - - return SVN_NO_ERROR; -} - -static svn_error_t * -tc_editor_alter_symlink(void *baton, - const char *relpath, - svn_revnum_t revision, - apr_hash_t *props, - const char *target, - apr_pool_t *scratch_pool) -{ - return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL, NULL); -} - -static svn_error_t * -tc_editor_delete(void *baton, +tc_editor_delete(node_move_baton_t *nmb, const char *relpath, - svn_revnum_t revision, + svn_node_kind_t old_kind, + svn_node_kind_t new_kind, apr_pool_t *scratch_pool) { - struct tc_editor_baton *b = baton; + update_move_baton_t *b = nmb->umb; svn_sqlite__stmt_t *stmt; - int op_depth = relpath_depth(b->move_root_dst_relpath); - const char *move_dst_repos_relpath; - svn_node_kind_t move_dst_kind; - svn_boolean_t is_conflicted; - svn_boolean_t must_delete_working_nodes = FALSE; - const char *local_abspath = svn_dirent_join(b->wcroot->abspath, relpath, - scratch_pool); - const char *parent_relpath = svn_relpath_dirname(relpath, scratch_pool); - int op_depth_below; - svn_boolean_t have_row; + const char *local_abspath; + svn_boolean_t is_modified, is_all_deletes; + svn_skel_t *work_items = NULL; + svn_skel_t *conflict = NULL; - SVN_ERR(svn_wc__db_depth_get_info(NULL, &move_dst_kind, NULL, - &move_dst_repos_relpath, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, - b->wcroot, relpath, - relpath_depth(b->move_root_dst_relpath), - scratch_pool, scratch_pool)); + SVN_ERR(mark_parent_edited(nmb, scratch_pool)); + if (nmb->skip) + return SVN_NO_ERROR; /* Check before retracting delete to catch delete-delete conflicts. This catches conflicts on the node itself; deleted children are caught as local modifications below.*/ - SVN_ERR(check_tree_conflict(&is_conflicted, b, relpath, - move_dst_kind, - svn_node_unknown, - move_dst_repos_relpath, - svn_wc_conflict_action_delete, - scratch_pool)); + if (nmb->shadowed) + { + SVN_ERR(mark_tc_on_op_root(nmb, + old_kind, new_kind, + svn_wc_conflict_action_delete, + scratch_pool)); + return SVN_NO_ERROR; + } - if (!is_conflicted) + local_abspath = svn_dirent_join(b->wcroot->abspath, relpath, scratch_pool); + SVN_ERR(svn_wc__node_has_local_mods(&is_modified, &is_all_deletes, + nmb->umb->db, local_abspath, FALSE, + NULL, NULL, scratch_pool)); + if (is_modified) { - svn_boolean_t is_modified, is_all_deletes; + svn_wc_conflict_reason_t reason; - SVN_ERR(svn_wc__node_has_local_mods(&is_modified, &is_all_deletes, b->db, - local_abspath, - NULL, NULL, scratch_pool)); - if (is_modified) - { - svn_wc_conflict_reason_t reason; + /* No conflict means no NODES rows at the relpath op-depth + so it's easy to convert the modified tree into a copy. - if (!is_all_deletes) - { - /* No conflict means no NODES rows at the relpath op-depth - so it's easy to convert the modified tree into a copy. */ - SVN_ERR(svn_sqlite__get_statement(&stmt, b->wcroot->sdb, - STMT_UPDATE_OP_DEPTH_RECURSIVE)); - SVN_ERR(svn_sqlite__bindf(stmt, "isdd", b->wcroot->wc_id, relpath, - op_depth, relpath_depth(relpath))); - SVN_ERR(svn_sqlite__step_done(stmt)); - - reason = svn_wc_conflict_reason_edited; - } - else - { + Note the following assumptions for relpath: + * it is not shadowed + * it is not the/an op-root. (or we can't make us a copy) + */ - SVN_ERR(svn_sqlite__get_statement(&stmt, b->wcroot->sdb, - STMT_DELETE_WORKING_OP_DEPTH_ABOVE)); - SVN_ERR(svn_sqlite__bindf(stmt, "isd", b->wcroot->wc_id, relpath, - op_depth)); - SVN_ERR(svn_sqlite__step_done(stmt)); + SVN_ERR(svn_wc__db_op_make_copy_internal(b->wcroot, relpath, FALSE, + NULL, NULL, scratch_pool)); - reason = svn_wc_conflict_reason_deleted; - must_delete_working_nodes = TRUE; - } - is_conflicted = TRUE; - SVN_ERR(mark_tree_conflict(relpath, b->wcroot, b->db, b->old_version, - b->new_version, b->move_root_dst_relpath, - b->operation, - move_dst_kind, - svn_node_none, - move_dst_repos_relpath, reason, - svn_wc_conflict_action_delete, NULL, - scratch_pool)); - b->conflict_root_relpath = apr_pstrdup(b->result_pool, relpath); - } - } + reason = svn_wc_conflict_reason_edited; - if (!is_conflicted || must_delete_working_nodes) + SVN_ERR(create_node_tree_conflict(&conflict, nmb, relpath, + old_kind, new_kind, reason, + (new_kind == svn_node_none) + ? svn_wc_conflict_action_delete + : svn_wc_conflict_action_replace, + NULL, + scratch_pool, scratch_pool)); + nmb->skip = TRUE; + } + else { apr_pool_t *iterpool = svn_pool_create(scratch_pool); - svn_skel_t *work_item; - svn_node_kind_t del_kind; const char *del_abspath; + svn_boolean_t have_row; + /* Get all descendants of the node in reverse order (so children are + handled before their parents, but not strictly depth first) */ SVN_ERR(svn_sqlite__get_statement(&stmt, b->wcroot->sdb, - STMT_SELECT_CHILDREN_OP_DEPTH)); + STMT_SELECT_DESCENDANTS_OP_DEPTH_RV)); SVN_ERR(svn_sqlite__bindf(stmt, "isd", b->wcroot->wc_id, relpath, - op_depth)); + b->dst_op_depth)); SVN_ERR(svn_sqlite__step(&have_row, stmt)); while (have_row) { svn_error_t *err; + svn_skel_t *work_item; + svn_node_kind_t del_kind; svn_pool_clear(iterpool); @@ -1194,8 +1291,7 @@ tc_editor_delete(void *baton, b->wcroot->abspath, del_abspath, iterpool, iterpool); if (!err) - err = svn_wc__db_wq_add(b->db, b->wcroot->abspath, work_item, - iterpool); + err = svn_wc__db_wq_add_internal(b->wcroot, work_item, iterpool); if (err) return svn_error_compose_create(err, svn_sqlite__reset(stmt)); @@ -1203,139 +1299,34 @@ tc_editor_delete(void *baton, } SVN_ERR(svn_sqlite__reset(stmt)); - SVN_ERR(svn_wc__db_depth_get_info(NULL, &del_kind, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, - b->wcroot, relpath, op_depth, - iterpool, iterpool)); - if (del_kind == svn_node_dir) - SVN_ERR(svn_wc__wq_build_dir_remove(&work_item, b->db, + if (old_kind == svn_node_dir) + SVN_ERR(svn_wc__wq_build_dir_remove(&work_items, b->db, b->wcroot->abspath, local_abspath, FALSE /* recursive */, - iterpool, iterpool)); + scratch_pool, iterpool)); else - SVN_ERR(svn_wc__wq_build_file_remove(&work_item, b->db, + SVN_ERR(svn_wc__wq_build_file_remove(&work_items, b->db, b->wcroot->abspath, local_abspath, - iterpool, iterpool)); - SVN_ERR(svn_wc__db_wq_add(b->db, b->wcroot->abspath, work_item, - iterpool)); - - if (!is_conflicted) - SVN_ERR(update_move_list_add(b->wcroot, relpath, - svn_wc_notify_update_delete, - del_kind, - svn_wc_notify_state_inapplicable, - svn_wc_notify_state_inapplicable)); - svn_pool_destroy(iterpool); - } + scratch_pool, iterpool)); - /* Deleting the ROWS is valid so long as we update the parent before - committing the transaction. The removed rows could have been - replacing a lower layer in which case we need to add base-deleted - rows. */ - SVN_ERR(svn_sqlite__get_statement(&stmt, b->wcroot->sdb, - STMT_SELECT_HIGHEST_WORKING_NODE)); - SVN_ERR(svn_sqlite__bindf(stmt, "isd", b->wcroot->wc_id, parent_relpath, - op_depth)); - SVN_ERR(svn_sqlite__step(&have_row, stmt)); - if (have_row) - op_depth_below = svn_sqlite__column_int(stmt, 0); - SVN_ERR(svn_sqlite__reset(stmt)); - if (have_row) - { - /* Remove non-shadowing nodes. */ - SVN_ERR(svn_sqlite__get_statement(&stmt, b->wcroot->sdb, - STMT_DELETE_NO_LOWER_LAYER)); - SVN_ERR(svn_sqlite__bindf(stmt, "isdd", b->wcroot->wc_id, relpath, - op_depth, op_depth_below)); - SVN_ERR(svn_sqlite__step_done(stmt)); - - /* Convert remaining shadowing nodes to presence='base-deleted'. */ - SVN_ERR(svn_sqlite__get_statement(&stmt, b->wcroot->sdb, - STMT_REPLACE_WITH_BASE_DELETED)); - SVN_ERR(svn_sqlite__bindf(stmt, "isd", b->wcroot->wc_id, relpath, - op_depth)); - SVN_ERR(svn_sqlite__step_done(stmt)); - } - else - { - SVN_ERR(svn_sqlite__get_statement(&stmt, b->wcroot->sdb, - STMT_DELETE_WORKING_OP_DEPTH)); - SVN_ERR(svn_sqlite__bindf(stmt, "isd", b->wcroot->wc_id, relpath, - op_depth)); - SVN_ERR(svn_sqlite__step_done(stmt)); + svn_pool_destroy(iterpool); } - /* Retract any base-delete. */ - SVN_ERR(svn_wc__db_retract_parent_delete(b->wcroot, relpath, op_depth, - scratch_pool)); - - return SVN_NO_ERROR; -} - -static svn_error_t * -tc_editor_copy(void *baton, - const char *src_relpath, - svn_revnum_t src_revision, - const char *dst_relpath, - svn_revnum_t replaces_rev, - apr_pool_t *scratch_pool) -{ - return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL, NULL); -} - -static svn_error_t * -tc_editor_move(void *baton, - const char *src_relpath, - svn_revnum_t src_revision, - const char *dst_relpath, - svn_revnum_t replaces_rev, - apr_pool_t *scratch_pool) -{ - return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL, NULL); -} - -static svn_error_t * -tc_editor_rotate(void *baton, - const apr_array_header_t *relpaths, - const apr_array_header_t *revisions, - apr_pool_t *scratch_pool) -{ - return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL, NULL); -} - -static svn_error_t * -tc_editor_complete(void *baton, - apr_pool_t *scratch_pool) -{ - return SVN_NO_ERROR; -} + /* Only notify if add_file/add_dir is not going to notify */ + if (conflict || (new_kind == svn_node_none)) + SVN_ERR(update_move_list_add(b->wcroot, relpath, b->db, + svn_wc_notify_update_delete, + new_kind, + svn_wc_notify_state_inapplicable, + svn_wc_notify_state_inapplicable, + conflict, work_items, scratch_pool)); + else if (work_items) + SVN_ERR(svn_wc__db_wq_add_internal(b->wcroot, work_items, + scratch_pool)); -static svn_error_t * -tc_editor_abort(void *baton, - apr_pool_t *scratch_pool) -{ return SVN_NO_ERROR; } -/* The editor callback table implementing the receiver. */ -static const svn_editor_cb_many_t editor_ops = { - tc_editor_add_directory, - tc_editor_add_file, - tc_editor_add_symlink, - tc_editor_add_absent, - tc_editor_alter_directory, - tc_editor_alter_file, - tc_editor_alter_symlink, - tc_editor_delete, - tc_editor_copy, - tc_editor_move, - tc_editor_rotate, - tc_editor_complete, - tc_editor_abort -}; - - /* * Driver code. * @@ -1352,73 +1343,11 @@ static const svn_editor_cb_many_t editor_ops = { * single-revision. */ -/* Set *OPERATION, *LOCAL_CHANGE, *INCOMING_CHANGE, *OLD_VERSION, *NEW_VERSION - * to reflect the tree conflict on the victim SRC_ABSPATH in DB. - * - * If SRC_ABSPATH is not a tree-conflict victim, return an error. - */ -static svn_error_t * -get_tc_info(svn_wc_operation_t *operation, - svn_wc_conflict_reason_t *local_change, - svn_wc_conflict_action_t *incoming_change, - const char **move_src_op_root_abspath, - svn_wc_conflict_version_t **old_version, - svn_wc_conflict_version_t **new_version, - svn_wc__db_t *db, - const char *src_abspath, - apr_pool_t *result_pool, - apr_pool_t *scratch_pool) -{ - const apr_array_header_t *locations; - svn_boolean_t tree_conflicted; - svn_skel_t *conflict_skel; - - /* Check for tree conflict on src. */ - SVN_ERR(svn_wc__db_read_conflict(&conflict_skel, db, - src_abspath, - scratch_pool, scratch_pool)); - if (!conflict_skel) - return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL, - _("'%s' is not in conflict"), - svn_dirent_local_style(src_abspath, - scratch_pool)); - - SVN_ERR(svn_wc__conflict_read_info(operation, &locations, - NULL, NULL, &tree_conflicted, - db, src_abspath, - conflict_skel, result_pool, - scratch_pool)); - if ((*operation != svn_wc_operation_update - && *operation != svn_wc_operation_switch) - || !tree_conflicted) - return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL, - _("'%s' is not a tree-conflict victim"), - svn_dirent_local_style(src_abspath, - scratch_pool)); - if (locations) - { - SVN_ERR_ASSERT(locations->nelts >= 2); - *old_version = APR_ARRAY_IDX(locations, 0, - svn_wc_conflict_version_t *); - *new_version = APR_ARRAY_IDX(locations, 1, - svn_wc_conflict_version_t *); - } - - SVN_ERR(svn_wc__conflict_read_tree_conflict(local_change, - incoming_change, - move_src_op_root_abspath, - db, src_abspath, - conflict_skel, scratch_pool, - scratch_pool)); - - return SVN_NO_ERROR; -} - /* Return *PROPS, *CHECKSUM, *CHILDREN and *KIND for LOCAL_RELPATH at OP_DEPTH provided the row exists. Return *KIND of svn_node_none if - the row does not exist. *CHILDREN is a sorted array of basenames of - type 'const char *', rather than a hash, to allow the driver to - process children in a defined order. */ + the row does not exist, or only describes a delete of a lower op-depth. + *CHILDREN is a sorted array of basenames of type 'const char *', rather + than a hash, to allow the driver to process children in a defined order. */ static svn_error_t * get_info(apr_hash_t **props, const svn_checksum_t **checksum, @@ -1430,64 +1359,68 @@ get_info(apr_hash_t **props, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { - apr_hash_t *hash_children; - apr_array_header_t *sorted_children; + svn_wc__db_status_t status; + const char *repos_relpath; + svn_node_kind_t db_kind; svn_error_t *err; - int i; - err = svn_wc__db_depth_get_info(NULL, kind, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, checksum, NULL, NULL, props, + err = svn_wc__db_depth_get_info(&status, &db_kind, NULL, &repos_relpath, NULL, + NULL, NULL, NULL, NULL, checksum, NULL, + NULL, props, wcroot, local_relpath, op_depth, result_pool, scratch_pool); - if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND) + + /* If there is no node at this depth, or only a node that describes a delete + of a lower layer we report this node as not existing. */ + if ((err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND) + || (!err && status != svn_wc__db_status_added + && status != svn_wc__db_status_normal)) { svn_error_clear(err); - *kind = svn_node_none; + + if (kind) + *kind = svn_node_none; + if (checksum) + *checksum = NULL; + if (props) + *props = NULL; + if (children) + *children = apr_array_make(result_pool, 0, sizeof(const char *)); + + return SVN_NO_ERROR; } else SVN_ERR(err); + if (kind) + *kind = db_kind; - SVN_ERR(svn_wc__db_get_children_op_depth(&hash_children, wcroot, - local_relpath, op_depth, - scratch_pool, scratch_pool)); - - sorted_children = svn_sort__hash(hash_children, - svn_sort_compare_items_lexically, - scratch_pool); - - *children = apr_array_make(result_pool, sorted_children->nelts, - sizeof(const char *)); - for (i = 0; i < sorted_children->nelts; ++i) - APR_ARRAY_PUSH(*children, const char *) - = apr_pstrdup(result_pool, APR_ARRAY_IDX(sorted_children, i, - svn_sort__item_t).key); - - return SVN_NO_ERROR; -} - -/* Return TRUE if SRC_CHILDREN and DST_CHILDREN represent the same - children, FALSE otherwise. SRC_CHILDREN and DST_CHILDREN are - sorted arrays of basenames of type 'const char *'. */ -static svn_boolean_t -children_match(apr_array_header_t *src_children, - apr_array_header_t *dst_children) { int i; + if (children && db_kind == svn_node_dir) + { + svn_sqlite__stmt_t *stmt; + svn_boolean_t have_row; - if (src_children->nelts != dst_children->nelts) - return FALSE; + *children = apr_array_make(result_pool, 16, sizeof(const char *)); + SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, + STMT_SELECT_OP_DEPTH_CHILDREN_EXISTS)); + SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath, + op_depth)); + SVN_ERR(svn_sqlite__step(&have_row, stmt)); + while (have_row) + { + const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL); - for(i = 0; i < src_children->nelts; ++i) - { - const char *src_child = - APR_ARRAY_IDX(src_children, i, const char *); - const char *dst_child = - APR_ARRAY_IDX(dst_children, i, const char *); + APR_ARRAY_PUSH(*children, const char *) + = svn_relpath_basename(child_relpath, result_pool); - if (strcmp(src_child, dst_child)) - return FALSE; + SVN_ERR(svn_sqlite__step(&have_row, stmt)); + } + SVN_ERR(svn_sqlite__reset(stmt)); } + else if (children) + *children = apr_array_make(result_pool, 0, sizeof(const char *)); - return TRUE; + return SVN_NO_ERROR; } /* Return TRUE if SRC_PROPS and DST_PROPS contain the same properties, @@ -1516,97 +1449,77 @@ props_match(svn_boolean_t *match, /* ### Drive TC_EDITOR so as to ... */ static svn_error_t * -update_moved_away_node(svn_editor_t *tc_editor, +update_moved_away_node(node_move_baton_t *nmb, + svn_wc__db_wcroot_t *wcroot, const char *src_relpath, const char *dst_relpath, - int src_op_depth, - const char *move_root_dst_relpath, - svn_revnum_t move_root_dst_revision, - svn_wc__db_t *db, - svn_wc__db_wcroot_t *wcroot, apr_pool_t *scratch_pool) { + update_move_baton_t *b = nmb->umb; svn_node_kind_t src_kind, dst_kind; const svn_checksum_t *src_checksum, *dst_checksum; apr_hash_t *src_props, *dst_props; apr_array_header_t *src_children, *dst_children; - int dst_op_depth = relpath_depth(move_root_dst_relpath); + + if (b->cancel_func) + SVN_ERR(b->cancel_func(b->cancel_baton)); SVN_ERR(get_info(&src_props, &src_checksum, &src_children, &src_kind, - src_relpath, src_op_depth, + src_relpath, b->src_op_depth, wcroot, scratch_pool, scratch_pool)); SVN_ERR(get_info(&dst_props, &dst_checksum, &dst_children, &dst_kind, - dst_relpath, dst_op_depth, + dst_relpath, b->dst_op_depth, wcroot, scratch_pool, scratch_pool)); if (src_kind == svn_node_none || (dst_kind != svn_node_none && src_kind != dst_kind)) { - SVN_ERR(svn_editor_delete(tc_editor, dst_relpath, - move_root_dst_revision)); + SVN_ERR(tc_editor_delete(nmb, dst_relpath, dst_kind, src_kind, + scratch_pool)); } + if (nmb->skip) + return SVN_NO_ERROR; + if (src_kind != svn_node_none && src_kind != dst_kind) { if (src_kind == svn_node_file || src_kind == svn_node_symlink) { - svn_stream_t *contents; - - SVN_ERR(svn_wc__db_pristine_read(&contents, NULL, db, - wcroot->abspath, src_checksum, - scratch_pool, scratch_pool)); - SVN_ERR(svn_editor_add_file(tc_editor, dst_relpath, - src_checksum, contents, src_props, - move_root_dst_revision)); + SVN_ERR(tc_editor_add_file(nmb, dst_relpath, dst_kind, + src_checksum, src_props, scratch_pool)); } else if (src_kind == svn_node_dir) { - SVN_ERR(svn_editor_add_directory(tc_editor, dst_relpath, - src_children, src_props, - move_root_dst_revision)); + SVN_ERR(tc_editor_add_directory(nmb, dst_relpath, dst_kind, + src_props, scratch_pool)); } } else if (src_kind != svn_node_none) { - svn_boolean_t match; - apr_hash_t *props; - - SVN_ERR(props_match(&match, src_props, dst_props, scratch_pool)); - props = match ? NULL: src_props; + svn_boolean_t props_equal; + SVN_ERR(props_match(&props_equal, src_props, dst_props, scratch_pool)); if (src_kind == svn_node_file || src_kind == svn_node_symlink) { - svn_stream_t *contents; - - if (svn_checksum_match(src_checksum, dst_checksum)) - src_checksum = NULL; - - if (src_checksum) - SVN_ERR(svn_wc__db_pristine_read(&contents, NULL, db, - wcroot->abspath, src_checksum, - scratch_pool, scratch_pool)); - else - contents = NULL; - - if (props || src_checksum) - SVN_ERR(svn_editor_alter_file(tc_editor, dst_relpath, - move_root_dst_revision, - props, src_checksum, contents)); + if (!props_equal || !svn_checksum_match(src_checksum, dst_checksum)) + SVN_ERR(tc_editor_alter_file(nmb, dst_relpath, + dst_checksum, src_checksum, + dst_props, src_props, scratch_pool)); } else if (src_kind == svn_node_dir) { - apr_array_header_t *children - = children_match(src_children, dst_children) ? NULL : src_children; - - if (props || children) - SVN_ERR(svn_editor_alter_directory(tc_editor, dst_relpath, - move_root_dst_revision, - children, props)); + if (!props_equal) + SVN_ERR(tc_editor_alter_directory(nmb, dst_relpath, + dst_props, src_props, + scratch_pool)); } } + if (nmb->skip) + return SVN_NO_ERROR; + if (src_kind == svn_node_dir) { apr_pool_t *iterpool = svn_pool_create(scratch_pool); @@ -1615,8 +1528,12 @@ update_moved_away_node(svn_editor_t *tc_editor, while (i < src_children->nelts || j < dst_children->nelts) { const char *child_name; - const char *src_child_relpath, *dst_child_relpath; svn_boolean_t src_only = FALSE, dst_only = FALSE; + node_move_baton_t cnmb = { 0 }; + + cnmb.pb = nmb; + cnmb.umb = nmb->umb; + cnmb.shadowed = nmb->shadowed; svn_pool_clear(iterpool); if (i >= src_children->nelts) @@ -1645,130 +1562,28 @@ update_moved_away_node(svn_editor_t *tc_editor, child_name = dst_only ? dst_name : src_name; } - src_child_relpath = svn_relpath_join(src_relpath, child_name, - iterpool); - dst_child_relpath = svn_relpath_join(dst_relpath, child_name, - iterpool); + cnmb.src_relpath = svn_relpath_join(src_relpath, child_name, + iterpool); + cnmb.dst_relpath = svn_relpath_join(dst_relpath, child_name, + iterpool); - SVN_ERR(update_moved_away_node(tc_editor, src_child_relpath, - dst_child_relpath, src_op_depth, - move_root_dst_relpath, - move_root_dst_revision, - db, wcroot, scratch_pool)); + if (!cnmb.shadowed) + SVN_ERR(check_node_shadowed(&cnmb.shadowed, wcroot, + cnmb.dst_relpath, b->dst_op_depth, + iterpool)); + + SVN_ERR(update_moved_away_node(&cnmb, wcroot, cnmb.src_relpath, + cnmb.dst_relpath, iterpool)); if (!dst_only) ++i; if (!src_only) ++j; - } - } - - return SVN_NO_ERROR; -} - -/* Update the single op-depth layer in the move destination subtree - rooted at DST_RELPATH to make it match the move source subtree - rooted at SRC_RELPATH. */ -static svn_error_t * -replace_moved_layer(const char *src_relpath, - const char *dst_relpath, - int src_op_depth, - svn_wc__db_wcroot_t *wcroot, - apr_pool_t *scratch_pool) -{ - svn_sqlite__stmt_t *stmt; - svn_boolean_t have_row; - int dst_op_depth = relpath_depth(dst_relpath); - /* Replace entire subtree at one op-depth. */ - SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, - STMT_SELECT_LOCAL_RELPATH_OP_DEPTH)); - SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, - src_relpath, src_op_depth)); - SVN_ERR(svn_sqlite__step(&have_row, stmt)); - while (have_row) - { - svn_error_t *err; - svn_sqlite__stmt_t *stmt2; - const char *src_cp_relpath = svn_sqlite__column_text(stmt, 0, NULL); - const char *dst_cp_relpath - = svn_relpath_join(dst_relpath, - svn_relpath_skip_ancestor(src_relpath, - src_cp_relpath), - scratch_pool); - - err = svn_sqlite__get_statement(&stmt2, wcroot->sdb, - STMT_COPY_NODE_MOVE); - if (!err) - err = svn_sqlite__bindf(stmt2, "isdsds", wcroot->wc_id, - src_cp_relpath, src_op_depth, - dst_cp_relpath, dst_op_depth, - svn_relpath_dirname(dst_cp_relpath, - scratch_pool)); - if (!err) - err = svn_sqlite__step_done(stmt2); - if (err) - return svn_error_compose_create(err, svn_sqlite__reset(stmt)); - - SVN_ERR(svn_sqlite__step(&have_row, stmt)); + if (nmb->skip) /* Does parent now want a skip? */ + break; + } } - SVN_ERR(svn_sqlite__reset(stmt)); - - return SVN_NO_ERROR; -} - -/* Transfer changes from the move source to the move destination. - * - * Drive the editor TC_EDITOR with the difference between DST_RELPATH - * (at its own op-depth) and SRC_RELPATH (at op-depth zero). - * - * Then update the single op-depth layer in the move destination subtree - * rooted at DST_RELPATH to make it match the move source subtree - * rooted at SRC_RELPATH. - * - * ### And the other params? - */ -static svn_error_t * -drive_tree_conflict_editor(svn_editor_t *tc_editor, - const char *src_relpath, - const char *dst_relpath, - int src_op_depth, - svn_wc_operation_t operation, - svn_wc_conflict_reason_t local_change, - svn_wc_conflict_action_t incoming_change, - svn_wc_conflict_version_t *old_version, - svn_wc_conflict_version_t *new_version, - svn_wc__db_t *db, - svn_wc__db_wcroot_t *wcroot, - svn_cancel_func_t cancel_func, - void *cancel_baton, - apr_pool_t *scratch_pool) -{ - /* - * Refuse to auto-resolve unsupported tree conflicts. - */ - /* ### Only handle conflicts created by update/switch operations for now. */ - if (operation != svn_wc_operation_update && - operation != svn_wc_operation_switch) - return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL, - _("Cannot auto-resolve tree-conflict on '%s'"), - svn_dirent_local_style( - svn_dirent_join(wcroot->abspath, - src_relpath, scratch_pool), - scratch_pool)); - - /* We walk the move source (i.e. the post-update tree), comparing each node - * with the equivalent node at the move destination and applying the update - * to nodes at the move destination. */ - SVN_ERR(update_moved_away_node(tc_editor, src_relpath, dst_relpath, - src_op_depth, - dst_relpath, old_version->peg_rev, - db, wcroot, scratch_pool)); - - SVN_ERR(replace_moved_layer(src_relpath, dst_relpath, src_op_depth, - wcroot, scratch_pool)); - - SVN_ERR(svn_editor_complete(tc_editor)); return SVN_NO_ERROR; } @@ -1788,14 +1603,13 @@ suitable_for_move(svn_wc__db_wcroot_t *wcroot, STMT_SELECT_BASE_NODE)); SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath)); SVN_ERR(svn_sqlite__step(&have_row, stmt)); - if (have_row) - { - revision = svn_sqlite__column_revnum(stmt, 4); - repos_relpath = svn_sqlite__column_text(stmt, 1, scratch_pool); - } - SVN_ERR(svn_sqlite__reset(stmt)); if (!have_row) - return SVN_NO_ERROR; /* Return an error? */ + return svn_error_trace(svn_sqlite__reset(stmt)); + + revision = svn_sqlite__column_revnum(stmt, 4); + repos_relpath = svn_sqlite__column_text(stmt, 1, scratch_pool); + + SVN_ERR(svn_sqlite__reset(stmt)); SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_SELECT_REPOS_PATH_REVISION)); @@ -1816,22 +1630,17 @@ suitable_for_move(svn_wc__db_wcroot_t *wcroot, svn_sqlite__reset(stmt), _("Cannot apply update because move source " "%s' is a mixed-revision working copy"), - svn_dirent_local_style(svn_dirent_join( - wcroot->abspath, - local_relpath, - scratch_pool), - scratch_pool)); + path_for_error_message(wcroot, local_relpath, + scratch_pool)); if (strcmp(relpath, svn_sqlite__column_text(stmt, 1, NULL))) return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, svn_sqlite__reset(stmt), _("Cannot apply update because move source " "'%s' is a switched subtree"), - svn_dirent_local_style(svn_dirent_join( - wcroot->abspath, - local_relpath, - scratch_pool), - scratch_pool)); + path_for_error_message(wcroot, + local_relpath, + scratch_pool)); SVN_ERR(svn_sqlite__step(&have_row, stmt)); } @@ -1845,96 +1654,113 @@ suitable_for_move(svn_wc__db_wcroot_t *wcroot, /* The body of svn_wc__db_update_moved_away_conflict_victim(), which see. */ static svn_error_t * -update_moved_away_conflict_victim(svn_wc__db_t *db, +update_moved_away_conflict_victim(svn_revnum_t *old_rev, + svn_revnum_t *new_rev, + svn_wc__db_t *db, svn_wc__db_wcroot_t *wcroot, - const char *victim_relpath, + const char *local_relpath, + const char *delete_relpath, svn_wc_operation_t operation, - svn_wc_conflict_reason_t local_change, - svn_wc_conflict_action_t incoming_change, - const char *move_src_op_root_relpath, - svn_wc_conflict_version_t *old_version, - svn_wc_conflict_version_t *new_version, + svn_wc_conflict_action_t action, + svn_wc_conflict_reason_t reason, svn_cancel_func_t cancel_func, void *cancel_baton, apr_pool_t *scratch_pool) { - svn_editor_t *tc_editor; - struct tc_editor_baton *tc_editor_baton; - svn_sqlite__stmt_t *stmt; - svn_boolean_t have_row; - const char *dummy1, *dummy2, *dummy3; - int src_op_depth; - const char *move_root_dst_abspath; + update_move_baton_t umb = { NULL }; + const char *src_relpath, *dst_relpath; + svn_wc_conflict_version_t old_version; + svn_wc_conflict_version_t new_version; + apr_int64_t repos_id; + node_move_baton_t nmb = { 0 }; - /* ### assumes wc write lock already held */ + SVN_ERR_ASSERT(svn_relpath_skip_ancestor(delete_relpath, local_relpath)); /* Construct editor baton. */ - tc_editor_baton = apr_pcalloc(scratch_pool, sizeof(*tc_editor_baton)); - SVN_ERR(svn_wc__db_op_depth_moved_to( - &dummy1, &tc_editor_baton->move_root_dst_relpath, &dummy2, &dummy3, - relpath_depth(move_src_op_root_relpath) - 1, - wcroot, victim_relpath, scratch_pool, scratch_pool)); - if (tc_editor_baton->move_root_dst_relpath == NULL) + + SVN_ERR(find_src_op_depth(&umb.src_op_depth, wcroot, + local_relpath, relpath_depth(delete_relpath), + scratch_pool)); + + SVN_ERR(svn_wc__db_scan_moved_to_internal(&src_relpath, &dst_relpath, NULL, + wcroot, local_relpath, + umb.src_op_depth, + scratch_pool, scratch_pool)); + + if (dst_relpath == NULL) return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL, _("The node '%s' has not been moved away"), - svn_dirent_local_style( - svn_dirent_join(wcroot->abspath, victim_relpath, - scratch_pool), - scratch_pool)); + path_for_error_message(wcroot, local_relpath, + scratch_pool)); - move_root_dst_abspath - = svn_dirent_join(wcroot->abspath, tc_editor_baton->move_root_dst_relpath, - scratch_pool); - SVN_ERR(svn_wc__write_check(db, move_root_dst_abspath, scratch_pool)); + umb.dst_op_depth = relpath_depth(dst_relpath); - tc_editor_baton->operation = operation; - tc_editor_baton->old_version= old_version; - tc_editor_baton->new_version= new_version; - tc_editor_baton->db = db; - tc_editor_baton->wcroot = wcroot; - tc_editor_baton->result_pool = scratch_pool; + SVN_ERR(verify_write_lock(wcroot, src_relpath, scratch_pool)); + SVN_ERR(verify_write_lock(wcroot, dst_relpath, scratch_pool)); - SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, - STMT_SELECT_HIGHEST_WORKING_NODE)); - SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, - move_src_op_root_relpath, - relpath_depth(move_src_op_root_relpath))); - SVN_ERR(svn_sqlite__step(&have_row, stmt)); - if (have_row) - src_op_depth = svn_sqlite__column_int(stmt, 0); - SVN_ERR(svn_sqlite__reset(stmt)); - if (!have_row) - return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL, - _("'%s' is not deleted"), - svn_dirent_local_style( - svn_dirent_join(wcroot->abspath, victim_relpath, - scratch_pool), - scratch_pool)); - if (src_op_depth == 0) - SVN_ERR(suitable_for_move(wcroot, victim_relpath, scratch_pool)); + SVN_ERR(svn_wc__db_depth_get_info(NULL, &new_version.node_kind, + &new_version.peg_rev, + &new_version.path_in_repos, &repos_id, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, + wcroot, src_relpath, umb.src_op_depth, + scratch_pool, scratch_pool)); + + SVN_ERR(svn_wc__db_fetch_repos_info(&new_version.repos_url, + &new_version.repos_uuid, + wcroot, repos_id, + scratch_pool)); + + SVN_ERR(svn_wc__db_depth_get_info(NULL, &old_version.node_kind, + &old_version.peg_rev, + &old_version.path_in_repos, &repos_id, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, + wcroot, dst_relpath, umb.dst_op_depth, + scratch_pool, scratch_pool)); + + SVN_ERR(svn_wc__db_fetch_repos_info(&old_version.repos_url, + &old_version.repos_uuid, + wcroot, repos_id, + scratch_pool)); + *old_rev = old_version.peg_rev; + *new_rev = new_version.peg_rev; + + umb.operation = operation; + umb.old_version= &old_version; + umb.new_version= &new_version; + umb.db = db; + umb.wcroot = wcroot; + umb.cancel_func = cancel_func; + umb.cancel_baton = cancel_baton; + + if (umb.src_op_depth == 0) + SVN_ERR(suitable_for_move(wcroot, src_relpath, scratch_pool)); /* Create a new, and empty, list for notification information. */ SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb, STMT_CREATE_UPDATE_MOVE_LIST)); - /* Create the editor... */ - SVN_ERR(svn_editor_create(&tc_editor, tc_editor_baton, - cancel_func, cancel_baton, - scratch_pool, scratch_pool)); - SVN_ERR(svn_editor_setcb_many(tc_editor, &editor_ops, scratch_pool)); - - /* ... and drive it. */ - SVN_ERR(drive_tree_conflict_editor(tc_editor, - victim_relpath, - tc_editor_baton->move_root_dst_relpath, - src_op_depth, - operation, - local_change, incoming_change, - tc_editor_baton->old_version, - tc_editor_baton->new_version, - db, wcroot, - cancel_func, cancel_baton, - scratch_pool)); + + /* Drive the editor... */ + + nmb.umb = &umb; + nmb.src_relpath = src_relpath; + nmb.dst_relpath = dst_relpath; + /* nmb.shadowed = FALSE; */ + /* nmb.edited = FALSE; */ + /* nmb.skip_children = FALSE; */ + + /* We walk the move source (i.e. the post-update tree), comparing each node + * with the equivalent node at the move destination and applying the update + * to nodes at the move destination. */ + SVN_ERR(update_moved_away_node(&nmb, wcroot, src_relpath, dst_relpath, + scratch_pool)); + + SVN_ERR(svn_wc__db_op_copy_layer_internal(wcroot, src_relpath, + umb.src_op_depth, + dst_relpath, NULL, NULL, + scratch_pool)); return SVN_NO_ERROR; } @@ -1942,58 +1768,43 @@ update_moved_away_conflict_victim(svn_wc__db_t *db, svn_error_t * svn_wc__db_update_moved_away_conflict_victim(svn_wc__db_t *db, - const char *victim_abspath, - svn_wc_notify_func2_t notify_func, - void *notify_baton, + const char *local_abspath, + const char *delete_op_abspath, + svn_wc_operation_t operation, + svn_wc_conflict_action_t action, + svn_wc_conflict_reason_t reason, svn_cancel_func_t cancel_func, void *cancel_baton, + svn_wc_notify_func2_t notify_func, + void *notify_baton, apr_pool_t *scratch_pool) { svn_wc__db_wcroot_t *wcroot; + svn_revnum_t old_rev, new_rev; const char *local_relpath; - svn_wc_operation_t operation; - svn_wc_conflict_reason_t local_change; - svn_wc_conflict_action_t incoming_change; - svn_wc_conflict_version_t *old_version; - svn_wc_conflict_version_t *new_version; - const char *move_src_op_root_abspath, *move_src_op_root_relpath; + const char *delete_relpath; /* ### Check for mixed-rev src or dst? */ - SVN_ERR(get_tc_info(&operation, &local_change, &incoming_change, - &move_src_op_root_abspath, - &old_version, &new_version, - db, victim_abspath, - scratch_pool, scratch_pool)); - - SVN_ERR(svn_wc__write_check(db, move_src_op_root_abspath, scratch_pool)); - SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, - db, victim_abspath, + db, local_abspath, scratch_pool, scratch_pool)); VERIFY_USABLE_WCROOT(wcroot); - move_src_op_root_relpath - = svn_dirent_skip_ancestor(wcroot->abspath, move_src_op_root_abspath); + delete_relpath + = svn_dirent_skip_ancestor(wcroot->abspath, delete_op_abspath); SVN_WC__DB_WITH_TXN( update_moved_away_conflict_victim( - db, wcroot, local_relpath, - operation, local_change, incoming_change, - move_src_op_root_relpath, - old_version, new_version, + &old_rev, &new_rev, + db, wcroot, local_relpath, delete_relpath, + operation, action, reason, cancel_func, cancel_baton, scratch_pool), wcroot); /* Send all queued up notifications. */ - SVN_ERR(svn_wc__db_update_move_list_notify(wcroot, - (old_version - ? old_version->peg_rev - : SVN_INVALID_REVNUM), - (new_version - ? new_version->peg_rev - : SVN_INVALID_REVNUM), + SVN_ERR(svn_wc__db_update_move_list_notify(wcroot, old_rev, new_rev, notify_func, notify_baton, scratch_pool)); if (notify_func) @@ -2008,7 +1819,7 @@ svn_wc__db_update_moved_away_conflict_victim(svn_wc__db_t *db, notify->kind = svn_node_none; notify->content_state = svn_wc_notify_state_inapplicable; notify->prop_state = svn_wc_notify_state_inapplicable; - notify->revision = new_version->peg_rev; + notify->revision = new_rev; notify_func(notify_baton, notify, scratch_pool); } @@ -2017,11 +1828,12 @@ svn_wc__db_update_moved_away_conflict_victim(svn_wc__db_t *db, } /* Set *CAN_BUMP to TRUE if DEPTH is sufficient to cover the entire - BASE tree at LOCAL_RELPATH, to FALSE otherwise. */ + tree LOCAL_RELPATH at OP_DEPTH, to FALSE otherwise. */ static svn_error_t * depth_sufficient_to_bump(svn_boolean_t *can_bump, - const char *local_relpath, svn_wc__db_wcroot_t *wcroot, + const char *local_relpath, + int op_depth, svn_depth_t depth, apr_pool_t *scratch_pool) { @@ -2038,21 +1850,21 @@ depth_sufficient_to_bump(svn_boolean_t *can_bump, SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_SELECT_OP_DEPTH_CHILDREN)); SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, - local_relpath, 0)); + local_relpath, op_depth)); break; case svn_depth_files: SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_SELECT_HAS_NON_FILE_CHILDREN)); - SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, - local_relpath)); + SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, + local_relpath, op_depth)); break; case svn_depth_immediates: SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_SELECT_HAS_GRANDCHILDREN)); - SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, - local_relpath)); + SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, + local_relpath, op_depth)); break; default: SVN_ERR_MALFUNCTION(); @@ -2068,6 +1880,7 @@ depth_sufficient_to_bump(svn_boolean_t *can_bump, static svn_error_t * bump_mark_tree_conflict(svn_wc__db_wcroot_t *wcroot, const char *move_src_root_relpath, + int src_op_depth, const char *move_src_op_root_relpath, const char *move_dst_op_root_relpath, svn_wc__db_t *db, @@ -2084,18 +1897,29 @@ bump_mark_tree_conflict(svn_wc__db_wcroot_t *wcroot, svn_node_kind_t new_kind; svn_wc_conflict_version_t *old_version; svn_wc_conflict_version_t *new_version; + svn_skel_t *conflict; + + /* Verify precondition: We are allowed to set a tree conflict here. */ + SVN_ERR(verify_write_lock(wcroot, move_src_root_relpath, scratch_pool)); /* Read new (post-update) information from the new move source BASE node. */ - SVN_ERR(svn_wc__db_base_get_info_internal(NULL, &new_kind, &new_rev, - &new_repos_relpath, &repos_id, - NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, - wcroot, move_src_op_root_relpath, - scratch_pool, scratch_pool)); + SVN_ERR(svn_wc__db_depth_get_info(NULL, &new_kind, &new_rev, + &new_repos_relpath, &repos_id, + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, + wcroot, move_src_op_root_relpath, + src_op_depth, scratch_pool, scratch_pool)); SVN_ERR(svn_wc__db_fetch_repos_info(&repos_root_url, &repos_uuid, - wcroot->sdb, repos_id, scratch_pool)); + wcroot, repos_id, scratch_pool)); + + /* Read old (pre-update) information from the move destination node. - /* Read old (pre-update) information from the move destination node. */ + This potentially touches nodes that aren't locked by us, but that is not + a problem because we have a SQLite write lock here, and all sqlite + operations that affect move stability use a sqlite lock as well. + (And affecting the move itself requires a write lock on the node that + we do own the lock for: the move source) + */ SVN_ERR(svn_wc__db_depth_get_info(NULL, &old_kind, &old_rev, &old_repos_relpath, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, @@ -2103,6 +1927,21 @@ bump_mark_tree_conflict(svn_wc__db_wcroot_t *wcroot, relpath_depth(move_dst_op_root_relpath), scratch_pool, scratch_pool)); + if (strcmp(move_src_root_relpath, move_src_op_root_relpath)) + { + /* We have information for the op-root, but need it for the node that + we are putting the tree conflict on. Luckily we know that we have + a clean BASE */ + + const char *rpath = svn_relpath_skip_ancestor(move_src_op_root_relpath, + move_src_root_relpath); + + old_repos_relpath = svn_relpath_join(old_repos_relpath, rpath, + scratch_pool); + new_repos_relpath = svn_relpath_join(new_repos_relpath, rpath, + scratch_pool); + } + old_version = svn_wc_conflict_version_create2( repos_root_url, repos_uuid, old_repos_relpath, old_rev, old_kind, scratch_pool); @@ -2110,38 +1949,202 @@ bump_mark_tree_conflict(svn_wc__db_wcroot_t *wcroot, repos_root_url, repos_uuid, new_repos_relpath, new_rev, new_kind, scratch_pool); - SVN_ERR(mark_tree_conflict(move_src_root_relpath, - wcroot, db, old_version, new_version, - move_dst_op_root_relpath, - svn_wc_operation_update, - old_kind, new_kind, - old_repos_relpath, - svn_wc_conflict_reason_moved_away, - svn_wc_conflict_action_edit, - move_src_op_root_relpath, - scratch_pool)); + SVN_ERR(create_tree_conflict(&conflict, wcroot, move_src_root_relpath, + move_dst_op_root_relpath, + db, old_version, new_version, + svn_wc_operation_update, + old_kind, new_kind, + old_repos_relpath, + svn_wc_conflict_reason_moved_away, + svn_wc_conflict_action_edit, + move_src_op_root_relpath, + scratch_pool, scratch_pool)); + + SVN_ERR(update_move_list_add(wcroot, move_src_root_relpath, db, + svn_wc_notify_tree_conflict, + new_kind, + svn_wc_notify_state_inapplicable, + svn_wc_notify_state_inapplicable, + conflict, NULL, scratch_pool)); return SVN_NO_ERROR; } -/* Bump LOCAL_RELPATH, and all the children of LOCAL_RELPATH, that are - moved-to at op-depth greater than OP_DEPTH. SRC_DONE is a hash - with keys that are 'const char *' relpaths that have already been - bumped. Any bumped paths are added to SRC_DONE. */ +/* Checks if SRC_RELPATH is within BUMP_DEPTH from BUMP_ROOT. Sets + * *SKIP to TRUE if the node should be skipped, otherwise to FALSE. + * Sets *SRC_DEPTH to the remaining depth at SRC_RELPATH. + */ +static svn_error_t * +check_bump_layer(svn_boolean_t *skip, + svn_depth_t *src_depth, + const char *bump_root, + svn_depth_t bump_depth, + const char *src_relpath, + svn_node_kind_t src_kind, + apr_pool_t *scratch_pool) +{ + const char *relpath; + + *skip = FALSE; + *src_depth = bump_depth; + + relpath = svn_relpath_skip_ancestor(bump_root, src_relpath); + + if (!relpath) + *skip = TRUE; + + if (bump_depth == svn_depth_infinity) + return SVN_NO_ERROR; + + if (relpath && *relpath == '\0') + return SVN_NO_ERROR; + + switch (bump_depth) + { + case svn_depth_empty: + *skip = TRUE; + break; + + case svn_depth_files: + if (src_kind != svn_node_file) + { + *skip = TRUE; + break; + } + /* Fallthrough */ + case svn_depth_immediates: + if (!relpath || relpath_depth(relpath) > 1) + *skip = TRUE; + + *src_depth = svn_depth_empty; + break; + default: + SVN_ERR_MALFUNCTION(); + } + + return SVN_NO_ERROR; +} + +/* The guts of bump_moved_away: Determines if a move can be bumped to match + * the move origin and if so performs this bump. + */ +static svn_error_t * +bump_moved_layer(svn_boolean_t *recurse, + svn_wc__db_wcroot_t *wcroot, + const char *local_relpath, + int op_depth, + const char *src_relpath, + int src_del_depth, + svn_depth_t src_depth, + const char *dst_relpath, + svn_wc__db_t *db, + apr_pool_t *scratch_pool) +{ + svn_sqlite__stmt_t *stmt; + svn_boolean_t have_row; + svn_skel_t *conflict; + svn_boolean_t can_bump; + const char *src_root_relpath; + + SVN_ERR(verify_write_lock(wcroot, local_relpath, scratch_pool)); + + *recurse = FALSE; + + SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, + STMT_HAS_LAYER_BETWEEN)); + + SVN_ERR(svn_sqlite__bindf(stmt, "isdd", wcroot->wc_id, local_relpath, + op_depth, src_del_depth)); + + SVN_ERR(svn_sqlite__step(&have_row, stmt)); + SVN_ERR(svn_sqlite__reset(stmt)); + + if (have_row) + return SVN_NO_ERROR; + + if (op_depth == 0) + SVN_ERR(depth_sufficient_to_bump(&can_bump, wcroot, src_relpath, + op_depth, src_depth, scratch_pool)); + else + /* Having chosen to bump an entire BASE tree move we + always have sufficient depth to bump subtree moves. */ + can_bump = TRUE; + + /* Are we allowed to bump */ + if (can_bump) + { + svn_boolean_t locked; + + SVN_ERR(svn_wc__db_wclock_owns_lock_internal(&locked, wcroot, + dst_relpath, + FALSE, scratch_pool)); + + if (!locked) + can_bump = FALSE; + } + + src_root_relpath = svn_relpath_prefix(src_relpath, src_del_depth, + scratch_pool); + + if (!can_bump) + { + SVN_ERR(bump_mark_tree_conflict(wcroot, src_relpath, op_depth, + src_root_relpath, dst_relpath, + db, scratch_pool)); + + return SVN_NO_ERROR; + } + + SVN_ERR(svn_wc__db_read_conflict_internal(&conflict, NULL, NULL, + wcroot, src_root_relpath, + scratch_pool, scratch_pool)); + + /* ### TODO: check this is the right sort of tree-conflict? */ + if (!conflict) + { + /* ### TODO: verify moved_here? */ + + SVN_ERR(verify_write_lock(wcroot, src_relpath, scratch_pool)); + + SVN_ERR(svn_wc__db_op_copy_layer_internal(wcroot, + src_relpath, op_depth, + dst_relpath, NULL, NULL, + scratch_pool)); + + *recurse = TRUE; + } + + return SVN_NO_ERROR; +} + +/* Internal storage for bump_moved_away() */ +struct bump_pair_t +{ + const char *src_relpath; + const char *dst_relpath; + int src_del_op_depth; + svn_node_kind_t src_kind; +}; + +/* Bump moves of LOCAL_RELPATH and all its descendants that were + originally below LOCAL_RELPATH at op-depth OP_DEPTH. + */ static svn_error_t * bump_moved_away(svn_wc__db_wcroot_t *wcroot, const char *local_relpath, int op_depth, - apr_hash_t *src_done, svn_depth_t depth, svn_wc__db_t *db, - apr_pool_t *result_pool, apr_pool_t *scratch_pool) { svn_sqlite__stmt_t *stmt; svn_boolean_t have_row; apr_pool_t *iterpool; + int i; + apr_array_header_t *pairs = apr_array_make(scratch_pool, 32, + sizeof(struct bump_pair_t*)); + /* Build an array, as we can't execute the same Sqlite query recursively */ iterpool = svn_pool_create(scratch_pool); SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, @@ -2151,126 +2154,48 @@ bump_moved_away(svn_wc__db_wcroot_t *wcroot, SVN_ERR(svn_sqlite__step(&have_row, stmt)); while(have_row) { - svn_sqlite__stmt_t *stmt2; - const char *src_relpath, *dst_relpath; - int src_op_depth = svn_sqlite__column_int(stmt, 2); - svn_error_t *err; - svn_skel_t *conflict; - svn_depth_t src_depth = depth; - - svn_pool_clear(iterpool); + struct bump_pair_t *bp = apr_pcalloc(scratch_pool, sizeof(*bp)); - src_relpath = svn_sqlite__column_text(stmt, 0, iterpool); - dst_relpath = svn_sqlite__column_text(stmt, 1, iterpool); + bp->src_relpath = svn_sqlite__column_text(stmt, 0, scratch_pool); + bp->dst_relpath = svn_sqlite__column_text(stmt, 1, scratch_pool); + bp->src_del_op_depth = svn_sqlite__column_int(stmt, 2); + bp->src_kind = svn_sqlite__column_token(stmt, 3, kind_map); - if (depth != svn_depth_infinity) - { - svn_boolean_t skip_this_src = FALSE; - svn_node_kind_t src_kind; + APR_ARRAY_PUSH(pairs, struct bump_pair_t *) = bp; - if (strcmp(src_relpath, local_relpath)) - { - switch (depth) - { - case svn_depth_empty: - skip_this_src = TRUE; - break; - case svn_depth_files: - src_kind = svn_sqlite__column_token(stmt, 3, kind_map); - if (src_kind != svn_node_file) - { - skip_this_src = TRUE; - break; - } - /* Fallthrough */ - case svn_depth_immediates: - if (strcmp(svn_relpath_dirname(src_relpath, scratch_pool), - local_relpath)) - skip_this_src = TRUE; - src_depth = svn_depth_empty; - break; - default: - SVN_ERR_MALFUNCTION(); - } - } + SVN_ERR(svn_sqlite__step(&have_row, stmt)); + } - if (skip_this_src) - { - SVN_ERR(svn_sqlite__step(&have_row, stmt)); - continue; - } - } + SVN_ERR(svn_sqlite__reset(stmt)); - err = svn_sqlite__get_statement(&stmt2, wcroot->sdb, - STMT_HAS_LAYER_BETWEEN); - if (!err) - err = svn_sqlite__bindf(stmt2, "isdd", wcroot->wc_id, local_relpath, - op_depth, src_op_depth); - if (!err) - err = svn_sqlite__step(&have_row, stmt2); - if (!err) - err = svn_sqlite__reset(stmt2); - if (!err && !have_row) - { - svn_boolean_t can_bump; - const char *src_root_relpath = src_relpath; + for (i = 0; i < pairs->nelts; i++) + { + struct bump_pair_t *bp = APR_ARRAY_IDX(pairs, i, struct bump_pair_t *); + svn_boolean_t skip; + svn_depth_t src_wc_depth; - if (op_depth == 0) - err = depth_sufficient_to_bump(&can_bump, src_relpath, wcroot, - src_depth, scratch_pool); - else - /* Having chosen to bump an entire BASE tree move we - always have sufficient depth to bump subtree moves. */ - can_bump = TRUE; + svn_pool_clear(iterpool); - if (!err) - { - if (!can_bump) - { - err = bump_mark_tree_conflict(wcroot, src_relpath, - src_root_relpath, dst_relpath, - db, scratch_pool); - if (err) - return svn_error_compose_create(err, - svn_sqlite__reset(stmt)); - SVN_ERR(svn_sqlite__step(&have_row, stmt)); - continue; - } - while (relpath_depth(src_root_relpath) > src_op_depth) - src_root_relpath = svn_relpath_dirname(src_root_relpath, - iterpool); + SVN_ERR(check_bump_layer(&skip, &src_wc_depth, local_relpath, depth, + bp->src_relpath, bp->src_kind, iterpool)); - if (!svn_hash_gets(src_done, src_relpath)) - { - svn_hash_sets(src_done, - apr_pstrdup(result_pool, src_relpath), ""); - err = svn_wc__db_read_conflict_internal(&conflict, wcroot, - src_root_relpath, - iterpool, iterpool); - /* ### TODO: check this is the right sort of tree-conflict? */ - if (!err && !conflict) - { - /* ### TODO: verify moved_here? */ - err = replace_moved_layer(src_relpath, dst_relpath, - op_depth, wcroot, iterpool); - - if (!err) - err = bump_moved_away(wcroot, dst_relpath, - relpath_depth(dst_relpath), - src_done, depth, db, - result_pool, iterpool); - } - } - } + if (!skip) + { + svn_boolean_t recurse; + + SVN_ERR(bump_moved_layer(&recurse, wcroot, + local_relpath, op_depth, + bp->src_relpath, bp->src_del_op_depth, + src_wc_depth, bp->dst_relpath, + db, iterpool)); + + if (recurse) + SVN_ERR(bump_moved_away(wcroot, bp->dst_relpath, + relpath_depth(bp->dst_relpath), + depth, db, iterpool)); } - - if (err) - return svn_error_compose_create(err, svn_sqlite__reset(stmt)); - - SVN_ERR(svn_sqlite__step(&have_row, stmt)); } - SVN_ERR(svn_sqlite__reset(stmt)); svn_pool_destroy(iterpool); @@ -2284,84 +2209,216 @@ svn_wc__db_bump_moved_away(svn_wc__db_wcroot_t *wcroot, svn_wc__db_t *db, apr_pool_t *scratch_pool) { - apr_hash_t *src_done; - SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb, STMT_CREATE_UPDATE_MOVE_LIST)); if (local_relpath[0] != '\0') { - const char *dummy1, *move_dst_op_root_relpath; - const char *move_src_root_relpath, *move_src_op_root_relpath; + const char *move_dst_op_root_relpath; + const char *move_src_root_relpath, *delete_relpath; + svn_error_t *err; /* Is the root of the update moved away? (Impossible for the wcroot) */ - SVN_ERR(svn_wc__db_op_depth_moved_to(&dummy1, &move_dst_op_root_relpath, - &move_src_root_relpath, - &move_src_op_root_relpath, 0, - wcroot, local_relpath, - scratch_pool, scratch_pool)); - if (move_src_root_relpath) + err = svn_wc__db_scan_moved_to_internal(&move_src_root_relpath, + &move_dst_op_root_relpath, + &delete_relpath, + wcroot, local_relpath, + 0 /* BASE */, + scratch_pool, scratch_pool); + + if (err) + { + if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND) + return svn_error_trace(err); + + svn_error_clear(err); + } + else if (move_src_root_relpath) { if (strcmp(move_src_root_relpath, local_relpath)) { - SVN_ERR(bump_mark_tree_conflict(wcroot, move_src_root_relpath, - move_src_op_root_relpath, - move_dst_op_root_relpath, - db, scratch_pool)); + /* An ancestor of the path that was updated is moved away. + + If we have a lock on that ancestor, we can mark a tree + conflict on it, if we don't we ignore this case. A future + update of the ancestor will handle this. */ + svn_boolean_t locked; + + SVN_ERR(svn_wc__db_wclock_owns_lock_internal( + &locked, wcroot, + move_src_root_relpath, + FALSE, scratch_pool)); + + if (locked) + { + SVN_ERR(bump_mark_tree_conflict(wcroot, + move_src_root_relpath, 0, + delete_relpath, + move_dst_op_root_relpath, + db, scratch_pool)); + } return SVN_NO_ERROR; } } } - src_done = apr_hash_make(scratch_pool); - SVN_ERR(bump_moved_away(wcroot, local_relpath, 0, src_done, depth, db, - scratch_pool, scratch_pool)); + SVN_ERR(bump_moved_away(wcroot, local_relpath, 0, depth, db, scratch_pool)); return SVN_NO_ERROR; } +/* Set *OPERATION, *LOCAL_CHANGE, *INCOMING_CHANGE, *OLD_VERSION, *NEW_VERSION + * to reflect the tree conflict on the victim SRC_ABSPATH in DB. + * + * If SRC_ABSPATH is not a tree-conflict victim, return an error. + */ static svn_error_t * -resolve_delete_raise_moved_away(svn_wc__db_wcroot_t *wcroot, - const char *local_relpath, - svn_wc__db_t *db, - svn_wc_operation_t operation, - svn_wc_conflict_action_t action, - svn_wc_conflict_version_t *old_version, - svn_wc_conflict_version_t *new_version, - apr_pool_t *scratch_pool) +fetch_conflict_details(int *src_op_depth, + svn_wc_operation_t *operation, + svn_wc_conflict_action_t *action, + svn_wc_conflict_version_t **left_version, + svn_wc_conflict_version_t **right_version, + svn_wc__db_wcroot_t *wcroot, + svn_wc__db_t *db, + const char *local_relpath, + const svn_skel_t *conflict_skel, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + const apr_array_header_t *locations; + svn_boolean_t text_conflicted; + svn_boolean_t prop_conflicted; + svn_boolean_t tree_conflicted; + const char *move_src_op_root_abspath; + svn_wc_conflict_reason_t reason; + const char *local_abspath = svn_dirent_join(wcroot->abspath, local_relpath, + scratch_pool); + + if (!conflict_skel) + return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL, + _("'%s' is not in conflict"), + path_for_error_message(wcroot, local_relpath, + scratch_pool)); + + SVN_ERR(svn_wc__conflict_read_info(operation, &locations, + &text_conflicted, &prop_conflicted, + &tree_conflicted, + db, local_abspath, + conflict_skel, result_pool, + scratch_pool)); + + if (text_conflicted || prop_conflicted || !tree_conflicted) + return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL, + _("'%s' is not a valid tree-conflict victim"), + path_for_error_message(wcroot, local_relpath, + scratch_pool)); + + SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, + action, + &move_src_op_root_abspath, + db, local_abspath, + conflict_skel, result_pool, + scratch_pool)); + + if (reason == svn_wc_conflict_reason_moved_away) + return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL, + _("'%s' is already a moved away tree-conflict"), + path_for_error_message(wcroot, local_relpath, + scratch_pool)); + + if (left_version) + { + if (locations && locations->nelts > 0) + *left_version = APR_ARRAY_IDX(locations, 0, + svn_wc_conflict_version_t *); + else + *left_version = NULL; + } + + if (right_version) + { + if (locations && locations->nelts > 1) + *right_version = APR_ARRAY_IDX(locations, 1, + svn_wc_conflict_version_t *); + else + *right_version = NULL; + } + + { + int del_depth = relpath_depth(local_relpath); + + if (move_src_op_root_abspath) + del_depth = relpath_depth( + svn_dirent_skip_ancestor(wcroot->abspath, + move_src_op_root_abspath)); + + SVN_ERR(find_src_op_depth(src_op_depth, wcroot, local_relpath, del_depth, + scratch_pool)); + } + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_wc__db_op_raise_moved_away_internal( + svn_wc__db_wcroot_t *wcroot, + const char *local_relpath, + int src_op_depth, + svn_wc__db_t *db, + svn_wc_operation_t operation, + svn_wc_conflict_action_t action, + const svn_wc_conflict_version_t *old_version, + const svn_wc_conflict_version_t *new_version, + apr_pool_t *scratch_pool) { svn_sqlite__stmt_t *stmt; svn_boolean_t have_row; - int op_depth = relpath_depth(local_relpath); apr_pool_t *iterpool = svn_pool_create(scratch_pool); SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb, STMT_CREATE_UPDATE_MOVE_LIST)); SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, - STMT_SELECT_OP_DEPTH_MOVED_PAIR)); + STMT_SELECT_MOVED_DESCENDANTS_SRC)); SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath, - op_depth)); + src_op_depth)); SVN_ERR(svn_sqlite__step(&have_row, stmt)); while(have_row) { - const char *moved_relpath = svn_sqlite__column_text(stmt, 0, NULL); - const char *move_root_dst_relpath = svn_sqlite__column_text(stmt, 1, - NULL); - const char *moved_dst_repos_relpath = svn_sqlite__column_text(stmt, 2, - NULL); + svn_error_t *err; + int delete_op_depth = svn_sqlite__column_int(stmt, 0); + const char *src_relpath = svn_sqlite__column_text(stmt, 1, NULL); + svn_node_kind_t src_kind = svn_sqlite__column_token(stmt, 2, kind_map); + const char *src_repos_relpath = svn_sqlite__column_text(stmt, 3, NULL); + const char *dst_relpath = svn_sqlite__column_text(stmt, 4, NULL); + svn_skel_t *conflict; svn_pool_clear(iterpool); - SVN_ERR(mark_tree_conflict(moved_relpath, - wcroot, db, old_version, new_version, - move_root_dst_relpath, operation, - svn_node_dir /* ### ? */, - svn_node_dir /* ### ? */, - moved_dst_repos_relpath, + SVN_ERR_ASSERT(src_repos_relpath != NULL); + + err = create_tree_conflict(&conflict, wcroot, src_relpath, dst_relpath, + db, old_version, new_version, operation, + src_kind /* ### old kind */, + src_kind /* ### new kind */, + src_repos_relpath, svn_wc_conflict_reason_moved_away, - action, local_relpath, - iterpool)); + action, + svn_relpath_prefix(src_relpath, + delete_op_depth, + iterpool), + iterpool, iterpool); + + if (!err) + err = update_move_list_add(wcroot, src_relpath, db, + svn_wc_notify_tree_conflict, + src_kind, + svn_wc_notify_state_inapplicable, + svn_wc_notify_state_inapplicable, + conflict, NULL, scratch_pool); + + if (err) + return svn_error_compose_create(err, svn_sqlite__reset(stmt)); SVN_ERR(svn_sqlite__step(&have_row, stmt)); } @@ -2373,41 +2430,52 @@ resolve_delete_raise_moved_away(svn_wc__db_wcroot_t *wcroot, } svn_error_t * -svn_wc__db_resolve_delete_raise_moved_away(svn_wc__db_t *db, - const char *local_abspath, - svn_wc_notify_func2_t notify_func, - void *notify_baton, - apr_pool_t *scratch_pool) +svn_wc__db_op_raise_moved_away(svn_wc__db_t *db, + const char *local_abspath, + svn_wc_notify_func2_t notify_func, + void *notify_baton, + apr_pool_t *scratch_pool) { svn_wc__db_wcroot_t *wcroot; const char *local_relpath; svn_wc_operation_t operation; - svn_wc_conflict_reason_t reason; svn_wc_conflict_action_t action; - svn_wc_conflict_version_t *old_version, *new_version; + svn_wc_conflict_version_t *left_version, *right_version; + int move_src_op_depth; + svn_skel_t *conflict; SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db, local_abspath, scratch_pool, scratch_pool)); VERIFY_USABLE_WCROOT(wcroot); - SVN_ERR(get_tc_info(&operation, &reason, &action, NULL, - &old_version, &new_version, - db, local_abspath, scratch_pool, scratch_pool)); - - SVN_WC__DB_WITH_TXN( - resolve_delete_raise_moved_away(wcroot, local_relpath, - db, operation, action, - old_version, new_version, - scratch_pool), + SVN_WC__DB_WITH_TXN4( + svn_wc__db_read_conflict_internal(&conflict, NULL, NULL, + wcroot, local_relpath, + scratch_pool, scratch_pool), + fetch_conflict_details(&move_src_op_depth, + &operation, &action, + &left_version, &right_version, + wcroot, db, local_relpath, conflict, + scratch_pool, scratch_pool), + svn_wc__db_op_mark_resolved_internal(wcroot, local_relpath, db, + FALSE, FALSE, TRUE, + NULL, scratch_pool), + svn_wc__db_op_raise_moved_away_internal(wcroot, local_relpath, + move_src_op_depth, + db, operation, action, + left_version, right_version, + scratch_pool), wcroot); + /* These version numbers are valid for update/switch notifications + only! */ SVN_ERR(svn_wc__db_update_move_list_notify(wcroot, - (old_version - ? old_version->peg_rev + (left_version + ? left_version->peg_rev : SVN_INVALID_REVNUM), - (new_version - ? new_version->peg_rev + (right_version + ? right_version->peg_rev : SVN_INVALID_REVNUM), notify_func, notify_baton, scratch_pool)); @@ -2416,159 +2484,109 @@ svn_wc__db_resolve_delete_raise_moved_away(svn_wc__db_t *db, } static svn_error_t * -break_move(svn_wc__db_wcroot_t *wcroot, - const char *src_relpath, - int src_op_depth, - const char *dst_relpath, - int dst_op_depth, - apr_pool_t *scratch_pool) -{ - svn_sqlite__stmt_t *stmt; - - SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, - STMT_CLEAR_MOVED_TO_RELPATH)); - SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, src_relpath, - src_op_depth)); - SVN_ERR(svn_sqlite__step_done(stmt)); - - /* This statement clears moved_here. */ - SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, - STMT_UPDATE_OP_DEPTH_RECURSIVE)); - SVN_ERR(svn_sqlite__bindf(stmt, "isdd", wcroot->wc_id, - dst_relpath, dst_op_depth, dst_op_depth)); - SVN_ERR(svn_sqlite__step_done(stmt)); - - return SVN_NO_ERROR; -} - -svn_error_t * -svn_wc__db_resolve_break_moved_away_internal(svn_wc__db_wcroot_t *wcroot, - const char *local_relpath, - int op_depth, - apr_pool_t *scratch_pool) -{ - const char *dummy1, *move_dst_op_root_relpath; - const char *dummy2, *move_src_op_root_relpath; - - /* We want to include the passed op-depth, but the function does a > check */ - SVN_ERR(svn_wc__db_op_depth_moved_to(&dummy1, &move_dst_op_root_relpath, - &dummy2, - &move_src_op_root_relpath, - op_depth - 1, - wcroot, local_relpath, - scratch_pool, scratch_pool)); - - SVN_ERR_ASSERT(move_src_op_root_relpath != NULL - && move_dst_op_root_relpath != NULL); - - SVN_ERR(break_move(wcroot, local_relpath, - relpath_depth(move_src_op_root_relpath), - move_dst_op_root_relpath, - relpath_depth(move_dst_op_root_relpath), - scratch_pool)); - - return SVN_NO_ERROR; -} - -static svn_error_t * -break_moved_away_children_internal(svn_wc__db_wcroot_t *wcroot, - const char *local_relpath, - apr_pool_t *scratch_pool) +break_moved_away(svn_wc__db_wcroot_t *wcroot, + svn_wc__db_t *db, + const char *local_relpath, + int parent_src_op_depth, + apr_pool_t *scratch_pool) { svn_sqlite__stmt_t *stmt; svn_boolean_t have_row; apr_pool_t *iterpool; + svn_error_t *err = NULL; SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb, STMT_CREATE_UPDATE_MOVE_LIST)); SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, - STMT_SELECT_MOVED_PAIR2)); - SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath)); + STMT_SELECT_MOVED_DESCENDANTS_SRC)); + SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath, + parent_src_op_depth)); SVN_ERR(svn_sqlite__step(&have_row, stmt)); iterpool = svn_pool_create(scratch_pool); while (have_row) { - const char *src_relpath = svn_sqlite__column_text(stmt, 0, iterpool); - const char *dst_relpath = svn_sqlite__column_text(stmt, 1, iterpool); - int src_op_depth = svn_sqlite__column_int(stmt, 2); + int src_op_depth = svn_sqlite__column_int(stmt, 0); + const char *src_relpath = svn_sqlite__column_text(stmt, 1, NULL); + svn_node_kind_t src_kind = svn_sqlite__column_token(stmt, 2, kind_map); + const char *dst_relpath = svn_sqlite__column_text(stmt, 4, NULL); svn_pool_clear(iterpool); - SVN_ERR(break_move(wcroot, src_relpath, src_op_depth, dst_relpath, - relpath_depth(dst_relpath), iterpool)); - SVN_ERR(update_move_list_add(wcroot, src_relpath, - svn_wc_notify_move_broken, - svn_node_unknown, - svn_wc_notify_state_inapplicable, - svn_wc_notify_state_inapplicable)); - SVN_ERR(svn_sqlite__step(&have_row, stmt)); - } - svn_pool_destroy(iterpool); + err = verify_write_lock(wcroot, src_relpath, iterpool); - SVN_ERR(svn_sqlite__reset(stmt)); + if (!err) + err = verify_write_lock(wcroot, dst_relpath, iterpool); - return SVN_NO_ERROR; -} + if (err) + break; -svn_error_t * -svn_wc__db_resolve_break_moved_away(svn_wc__db_t *db, - const char *local_abspath, - svn_wc_notify_func2_t notify_func, - void *notify_baton, - apr_pool_t *scratch_pool) -{ - svn_wc__db_wcroot_t *wcroot; - const char *local_relpath; + err = svn_error_trace( + svn_wc__db_op_break_move_internal(wcroot, + src_relpath, src_op_depth, + dst_relpath, NULL, iterpool)); - SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, - db, local_abspath, - scratch_pool, scratch_pool)); - VERIFY_USABLE_WCROOT(wcroot); + if (err) + break; - SVN_WC__DB_WITH_TXN( - svn_wc__db_resolve_break_moved_away_internal(wcroot, local_relpath, - relpath_depth(local_relpath), - scratch_pool), - wcroot); + err = svn_error_trace( + update_move_list_add(wcroot, src_relpath, db, + svn_wc_notify_move_broken, + src_kind, + svn_wc_notify_state_inapplicable, + svn_wc_notify_state_inapplicable, + NULL, NULL, scratch_pool)); - if (notify_func) - { - svn_wc_notify_t *notify; + if (err) + break; - notify = svn_wc_create_notify(svn_dirent_join(wcroot->abspath, - local_relpath, - scratch_pool), - svn_wc_notify_move_broken, - scratch_pool); - notify->kind = svn_node_unknown; - notify->content_state = svn_wc_notify_state_inapplicable; - notify->prop_state = svn_wc_notify_state_inapplicable; - notify->revision = SVN_INVALID_REVNUM; - notify_func(notify_baton, notify, scratch_pool); + SVN_ERR(svn_sqlite__step(&have_row, stmt)); } + svn_pool_destroy(iterpool); - return SVN_NO_ERROR; + return svn_error_compose_create(err, svn_sqlite__reset(stmt)); } svn_error_t * -svn_wc__db_resolve_break_moved_away_children(svn_wc__db_t *db, - const char *local_abspath, - svn_wc_notify_func2_t notify_func, - void *notify_baton, - apr_pool_t *scratch_pool) +svn_wc__db_op_break_moved_away(svn_wc__db_t *db, + const char *local_abspath, + const char *del_op_root_abspath, + svn_boolean_t mark_tc_resolved, + svn_wc_notify_func2_t notify_func, + void *notify_baton, + apr_pool_t *scratch_pool) { svn_wc__db_wcroot_t *wcroot; const char *local_relpath; + const char *del_relpath; + int src_op_depth; SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db, local_abspath, scratch_pool, scratch_pool)); VERIFY_USABLE_WCROOT(wcroot); - SVN_WC__DB_WITH_TXN( - break_moved_away_children_internal(wcroot, local_relpath, scratch_pool), + if (del_op_root_abspath) + del_relpath = svn_dirent_skip_ancestor(wcroot->abspath, + del_op_root_abspath); + else + del_relpath = NULL; + + + SVN_WC__DB_WITH_TXN4( + find_src_op_depth(&src_op_depth, wcroot, local_relpath, + del_relpath ? relpath_depth(del_relpath) + : relpath_depth(local_relpath), + scratch_pool), + break_moved_away(wcroot, db, local_relpath, src_op_depth, + scratch_pool), + mark_tc_resolved + ? svn_wc__db_op_mark_resolved_internal(wcroot, local_relpath, db, + FALSE, FALSE, TRUE, + NULL, scratch_pool) + : SVN_NO_ERROR, + SVN_NO_ERROR, wcroot); SVN_ERR(svn_wc__db_update_move_list_notify(wcroot, |