diff options
Diffstat (limited to 'contrib/subversion/subversion/libsvn_wc/wc_db.c')
-rw-r--r-- | contrib/subversion/subversion/libsvn_wc/wc_db.c | 661 |
1 files changed, 545 insertions, 116 deletions
diff --git a/contrib/subversion/subversion/libsvn_wc/wc_db.c b/contrib/subversion/subversion/libsvn_wc/wc_db.c index 7d038cf57fb3..81056c9a4a6a 100644 --- a/contrib/subversion/subversion/libsvn_wc/wc_db.c +++ b/contrib/subversion/subversion/libsvn_wc/wc_db.c @@ -627,6 +627,10 @@ svn_wc__db_extend_parent_delete(svn_wc__db_wcroot_t *wcroot, When removing a node if the parent has a higher working node then the parent node and this node are both deleted or replaced and any delete over this node must be removed. + + This function (like most wcroot functions) assumes that its caller + only uses this function within an sqlite transaction if atomic + behavior is needed. */ svn_error_t * svn_wc__db_retract_parent_delete(svn_wc__db_wcroot_t *wcroot, @@ -635,14 +639,60 @@ svn_wc__db_retract_parent_delete(svn_wc__db_wcroot_t *wcroot, apr_pool_t *scratch_pool) { svn_sqlite__stmt_t *stmt; + svn_boolean_t have_row; + int working_depth; + svn_wc__db_status_t presence; + const char *moved_to; SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, - STMT_DELETE_LOWEST_WORKING_NODE)); + STMT_SELECT_LOWEST_WORKING_NODE)); SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath, op_depth)); - SVN_ERR(svn_sqlite__step_done(stmt)); + SVN_ERR(svn_sqlite__step(&have_row, stmt)); - return SVN_NO_ERROR; + if (!have_row) + return svn_error_trace(svn_sqlite__reset(stmt)); + + working_depth = svn_sqlite__column_int(stmt, 0); + presence = svn_sqlite__column_token(stmt, 1, presence_map); + moved_to = svn_sqlite__column_text(stmt, 3, scratch_pool); + + SVN_ERR(svn_sqlite__reset(stmt)); + + if (moved_to) + { + /* Turn the move into a copy to keep the NODES table valid */ + SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, + STMT_CLEAR_MOVED_HERE_RECURSIVE)); + SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, + moved_to, relpath_depth(moved_to))); + SVN_ERR(svn_sqlite__step_done(stmt)); + + /* This leaves just the moved_to information on the origin, + which we will remove in the next step */ + } + + if (presence == svn_wc__db_status_base_deleted) + { + /* Nothing left to shadow; remove the base-deleted node */ + SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_DELETE_NODE)); + } + else if (moved_to) + { + /* Clear moved to information, as this node is no longer base-deleted */ + SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, + STMT_CLEAR_MOVED_TO_RELPATH)); + } + else + { + /* Nothing to update */ + return SVN_NO_ERROR; + } + + SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath, + working_depth)); + + return svn_error_trace(svn_sqlite__update(NULL, stmt)); } @@ -4076,8 +4126,9 @@ get_info_for_copy(apr_int64_t *copyfrom_id, svn_wc__db_status_t *status, svn_node_kind_t *kind, svn_boolean_t *op_root, - svn_wc__db_wcroot_t *wcroot, + svn_wc__db_wcroot_t *src_wcroot, const char *local_relpath, + svn_wc__db_wcroot_t *dst_wcroot, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { @@ -4094,7 +4145,7 @@ get_info_for_copy(apr_int64_t *copyfrom_id, NULL /* have_base */, NULL /* have_more_work */, NULL /* have_work */, - wcroot, local_relpath, result_pool, scratch_pool)); + src_wcroot, local_relpath, result_pool, scratch_pool)); if (op_root) *op_root = is_op_root; @@ -4109,7 +4160,7 @@ get_info_for_copy(apr_int64_t *copyfrom_id, scratch_pool); SVN_ERR(get_info_for_copy(copyfrom_id, copyfrom_relpath, copyfrom_rev, NULL, NULL, NULL, - wcroot, parent_relpath, + src_wcroot, parent_relpath, dst_wcroot, scratch_pool, scratch_pool)); if (*copyfrom_relpath) *copyfrom_relpath = svn_relpath_join(*copyfrom_relpath, base_name, @@ -4118,7 +4169,7 @@ get_info_for_copy(apr_int64_t *copyfrom_id, else if (node_status == svn_wc__db_status_added) { SVN_ERR(scan_addition(&node_status, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, wcroot, local_relpath, + NULL, NULL, NULL, src_wcroot, local_relpath, scratch_pool, scratch_pool)); } else if (node_status == svn_wc__db_status_deleted && is_op_root) @@ -4127,7 +4178,7 @@ get_info_for_copy(apr_int64_t *copyfrom_id, SVN_ERR(scan_deletion_txn(&base_del_relpath, NULL, &work_del_relpath, - NULL, wcroot, local_relpath, + NULL, src_wcroot, local_relpath, scratch_pool, scratch_pool)); if (work_del_relpath) { @@ -4140,7 +4191,8 @@ get_info_for_copy(apr_int64_t *copyfrom_id, SVN_ERR(scan_addition(NULL, &op_root_relpath, NULL, NULL, /* repos_* */ copyfrom_relpath, copyfrom_id, copyfrom_rev, - NULL, NULL, NULL, wcroot, parent_del_relpath, + NULL, NULL, NULL, + src_wcroot, parent_del_relpath, scratch_pool, scratch_pool)); *copyfrom_relpath = svn_relpath_join(*copyfrom_relpath, @@ -4155,7 +4207,7 @@ get_info_for_copy(apr_int64_t *copyfrom_id, copyfrom_id, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - wcroot, local_relpath, + src_wcroot, local_relpath, result_pool, scratch_pool)); } @@ -4177,6 +4229,24 @@ get_info_for_copy(apr_int64_t *copyfrom_id, if (status) *status = node_status; + if (src_wcroot != dst_wcroot && *copyfrom_relpath) + { + const char *repos_root_url; + const char *repos_uuid; + + /* Pass the right repos-id for the destination db. We can't just use + the id of the source database, as this value can change after + relocation (and perhaps also when we start storing multiple + working copies in a single db)! */ + + SVN_ERR(svn_wc__db_fetch_repos_info(&repos_root_url, &repos_uuid, + src_wcroot->sdb, *copyfrom_id, + scratch_pool)); + + SVN_ERR(create_repos_id(copyfrom_id, repos_root_url, repos_uuid, + dst_wcroot->sdb, scratch_pool)); + } + return SVN_NO_ERROR; } @@ -4336,8 +4406,9 @@ db_op_copy(svn_wc__db_wcroot_t *src_wcroot, const apr_array_header_t *children; SVN_ERR(get_info_for_copy(©from_id, ©from_relpath, ©from_rev, - &status, &kind, &op_root, src_wcroot, - src_relpath, scratch_pool, scratch_pool)); + &status, &kind, &op_root, + src_wcroot, src_relpath, dst_wcroot, + scratch_pool, scratch_pool)); SVN_ERR(op_depth_for_copy(&dst_op_depth, &dst_np_op_depth, &dst_parent_op_depth, @@ -4562,21 +4633,6 @@ db_op_copy(svn_wc__db_wcroot_t *src_wcroot, } else { - if (copyfrom_relpath) - { - const char *repos_root_url; - const char *repos_uuid; - - /* Pass the right repos-id for the destination db! */ - - SVN_ERR(svn_wc__db_fetch_repos_info(&repos_root_url, &repos_uuid, - src_wcroot->sdb, copyfrom_id, - scratch_pool)); - - SVN_ERR(create_repos_id(©from_id, repos_root_url, repos_uuid, - dst_wcroot->sdb, scratch_pool)); - } - SVN_ERR(cross_db_copy(src_wcroot, src_relpath, dst_wcroot, dst_relpath, dst_presence, dst_op_depth, dst_np_op_depth, kind, @@ -7176,7 +7232,7 @@ remove_node_txn(svn_boolean_t *left_changes, if (local_relpath[0] != '\0') { SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, - STMT_DELETE_NODE)); + STMT_DELETE_NODE_ALL_LAYERS)); SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath)); SVN_ERR(svn_sqlite__step_done(stmt)); } @@ -7487,8 +7543,86 @@ struct moved_node_t { /* The op-depth of the deleted node at the source of the move. */ int op_depth; + + /* When >= 1 the op_depth at which local_relpath was moved to its + location. Used to find its original location outside the delete */ + int moved_from_depth; }; +/* Helper function to resolve the original location of local_relpath at OP_DEPTH + before it was moved into the tree rooted at ROOT_RELPATH. */ +static svn_error_t * +resolve_moved_from(const char **moved_from_relpath, + int *moved_from_op_depth, + svn_wc__db_wcroot_t *wcroot, + const char *root_relpath, + const char *local_relpath, + int op_depth, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + const char *suffix = ""; + svn_sqlite__stmt_t *stmt; + const char *m_from_relpath; + int m_from_op_depth; + int m_move_from_depth; + svn_boolean_t have_row; + + while (relpath_depth(local_relpath) > op_depth) + { + const char *name; + svn_relpath_split(&local_relpath, &name, local_relpath, scratch_pool); + suffix = svn_relpath_join(suffix, name, scratch_pool); + } + + SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, + STMT_SELECT_MOVED_FROM_FOR_DELETE)); + SVN_ERR(svn_sqlite__bindf(stmt, "is", + wcroot->wc_id, local_relpath)); + SVN_ERR(svn_sqlite__step(&have_row, stmt)); + + if (!have_row) + { + /* assert(have_row); */ + *moved_from_relpath = NULL; + *moved_from_op_depth = -1; + + SVN_ERR(svn_sqlite__reset(stmt)); + + return SVN_NO_ERROR; + } + + m_from_relpath = svn_sqlite__column_text(stmt, 0, scratch_pool); + m_from_op_depth = svn_sqlite__column_int(stmt, 1); + m_move_from_depth = svn_sqlite__column_int(stmt, 2); + + SVN_ERR(svn_sqlite__reset(stmt)); + + if (! svn_relpath_skip_ancestor(root_relpath, m_from_relpath)) + { + *moved_from_relpath = svn_relpath_join(m_from_relpath, suffix, + result_pool); + *moved_from_op_depth = m_from_op_depth; /* ### Ok? */ + return SVN_NO_ERROR; + } + else if (!m_move_from_depth) + { + *moved_from_relpath = NULL; + *moved_from_op_depth = -1; + return SVN_NO_ERROR; + } + + return svn_error_trace( + resolve_moved_from(moved_from_relpath, + moved_from_op_depth, + wcroot, + root_relpath, + svn_relpath_join(m_from_relpath, suffix, + scratch_pool), + m_move_from_depth, + result_pool, scratch_pool)); +} + static svn_error_t * delete_node(void *baton, svn_wc__db_wcroot_t *wcroot, @@ -7500,19 +7634,70 @@ delete_node(void *baton, svn_boolean_t have_row, op_root; svn_boolean_t add_work = FALSE; svn_sqlite__stmt_t *stmt; - int select_depth; /* Depth of what is to be deleted */ - svn_boolean_t refetch_depth = FALSE; + int working_op_depth; /* Depth of what is to be deleted */ + int keep_op_depth = 0; /* Depth of what is below what is deleted */ svn_node_kind_t kind; apr_array_header_t *moved_nodes = NULL; - int delete_depth = relpath_depth(local_relpath); + int delete_op_depth = relpath_depth(local_relpath); - SVN_ERR(read_info(&status, - &kind, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - &op_root, NULL, NULL, - NULL, NULL, NULL, - wcroot, local_relpath, - scratch_pool, scratch_pool)); + assert(*local_relpath); /* Can't delete wcroot */ + + SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, + STMT_SELECT_NODE_INFO)); + SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath)); + SVN_ERR(svn_sqlite__step(&have_row, stmt)); + + if (!have_row) + { + return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, + svn_sqlite__reset(stmt), + _("The node '%s' was not found."), + path_for_error_message(wcroot, + local_relpath, + scratch_pool)); + } + + working_op_depth = svn_sqlite__column_int(stmt, 0); + status = svn_sqlite__column_token(stmt, 3, presence_map); + kind = svn_sqlite__column_token(stmt, 4, kind_map); + + if (working_op_depth < delete_op_depth) + { + op_root = FALSE; + add_work = TRUE; + keep_op_depth = working_op_depth; + } + else + { + op_root = TRUE; + + SVN_ERR(svn_sqlite__step(&have_row, stmt)); + + if (have_row) + { + svn_wc__db_status_t below_status; + int below_op_depth; + + below_op_depth = svn_sqlite__column_int(stmt, 0); + below_status = svn_sqlite__column_token(stmt, 3, presence_map); + + if (below_status != svn_wc__db_status_not_present + && below_status != svn_wc__db_status_base_deleted) + { + add_work = TRUE; + keep_op_depth = below_op_depth; + } + else + keep_op_depth = 0; + } + else + keep_op_depth = -1; + } + + SVN_ERR(svn_sqlite__reset(stmt)); + + if (working_op_depth != 0) /* WORKING */ + SVN_ERR(convert_to_working_status(&status, status)); if (status == svn_wc__db_status_deleted || status == svn_wc__db_status_not_present) @@ -7591,6 +7776,7 @@ delete_node(void *baton, part, scratch_pool); moved_node->op_depth = move_op_depth; moved_node->moved_to_relpath = b->moved_to_relpath; + moved_node->moved_from_depth = -1; APR_ARRAY_PUSH(moved_nodes, const struct moved_node_t *) = moved_node; } @@ -7602,8 +7788,9 @@ delete_node(void *baton, * possibly because of a nested move operation. */ moved_node = apr_palloc(scratch_pool, sizeof(struct moved_node_t)); moved_node->local_relpath = local_relpath; - moved_node->op_depth = delete_depth; + moved_node->op_depth = delete_op_depth; moved_node->moved_to_relpath = b->moved_to_relpath; + moved_node->moved_from_depth = -1; APR_ARRAY_PUSH(moved_nodes, const struct moved_node_t *) = moved_node; } @@ -7618,24 +7805,18 @@ delete_node(void *baton, b->moved_to_relpath)); SVN_ERR(svn_sqlite__update(NULL, stmt)); } - else - { - SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, - STMT_CLEAR_MOVED_TO_DESCENDANTS)); - SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, - local_relpath)); - SVN_ERR(svn_sqlite__update(NULL, stmt)); - } /* Find children that were moved out of the subtree rooted at this node. * We'll need to update their op-depth columns because their deletion * is now implied by the deletion of their parent (i.e. this node). */ { apr_pool_t *iterpool; + int i; SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_SELECT_MOVED_FOR_DELETE)); - SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath)); + SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath, + delete_op_depth)); SVN_ERR(svn_sqlite__step(&have_row, stmt)); iterpool = svn_pool_create(scratch_pool); @@ -7645,52 +7826,85 @@ delete_node(void *baton, const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL); const char *mv_to_relpath = svn_sqlite__column_text(stmt, 1, NULL); int child_op_depth = svn_sqlite__column_int(stmt, 2); + int moved_from_depth = -1; svn_boolean_t fixup = FALSE; - if (!b->moved_to_relpath + if (! b->moved_to_relpath && ! svn_relpath_skip_ancestor(local_relpath, mv_to_relpath)) { - /* Update the op-depth of an moved node below this tree */ - fixup = TRUE; - child_op_depth = delete_depth; - } - else if (b->moved_to_relpath - && delete_depth == child_op_depth) - { - /* Update the op-depth of a tree shadowed by this tree */ - fixup = TRUE; - child_op_depth = delete_depth; - } - else if (b->moved_to_relpath - && child_op_depth >= delete_depth - && !svn_relpath_skip_ancestor(local_relpath, mv_to_relpath)) - { - /* Update the move destination of something that is now moved - away further */ + /* a NULL moved_here_depth will be reported as 0 */ + int moved_here_depth = svn_sqlite__column_int(stmt, 3); - child_relpath = svn_relpath_skip_ancestor(local_relpath, child_relpath); + /* Plain delete. Fixup move information of descendants that were + moved here, or that were moved out */ - if (child_relpath) + if (moved_here_depth >= delete_op_depth) { - child_relpath = svn_relpath_join(b->moved_to_relpath, child_relpath, scratch_pool); + /* The move we recorded here must be moved to the location + this node had before it was moved here. - if (child_op_depth > delete_depth - && svn_relpath_skip_ancestor(local_relpath, child_relpath)) - child_op_depth = delete_depth; - else - child_op_depth = relpath_depth(child_relpath); + This might contain multiple steps when the node was moved + in several places within the to be deleted tree */ + + /* ### TODO: Add logic */ + fixup = TRUE; + moved_from_depth = moved_here_depth; + } + else + { + /* Update the op-depth of an moved away node that was + registered as moved by the records that we are about + to delete */ + fixup = TRUE; + child_op_depth = delete_op_depth; + } + } + else if (b->moved_to_relpath) + { + /* The node is moved to a new location */ + if (delete_op_depth == child_op_depth) + { + /* Update the op-depth of a tree shadowed by this tree */ fixup = TRUE; + /*child_op_depth = delete_depth;*/ + } + else if (child_op_depth >= delete_op_depth + && !svn_relpath_skip_ancestor(local_relpath, + mv_to_relpath)) + { + /* Update the move destination of something that is now moved + away further */ + + child_relpath = svn_relpath_skip_ancestor(local_relpath, + child_relpath); + + if (child_relpath) + { + child_relpath = svn_relpath_join(b->moved_to_relpath, + child_relpath, + scratch_pool); + + if (child_op_depth > delete_op_depth + && svn_relpath_skip_ancestor(local_relpath, + child_relpath)) + child_op_depth = delete_op_depth; + else + child_op_depth = relpath_depth(child_relpath); + + fixup = TRUE; + } } } if (fixup) { - mn = apr_pcalloc(scratch_pool, sizeof(struct moved_node_t)); + mn = apr_palloc(scratch_pool, sizeof(struct moved_node_t)); mn->local_relpath = apr_pstrdup(scratch_pool, child_relpath); mn->moved_to_relpath = apr_pstrdup(scratch_pool, mv_to_relpath); mn->op_depth = child_op_depth; + mn->moved_from_depth = moved_from_depth; if (!moved_nodes) moved_nodes = apr_array_make(scratch_pool, 1, @@ -7700,58 +7914,67 @@ delete_node(void *baton, SVN_ERR(svn_sqlite__step(&have_row, stmt)); } - svn_pool_destroy(iterpool); SVN_ERR(svn_sqlite__reset(stmt)); - } - - if (op_root) - { - svn_boolean_t below_base; - svn_boolean_t below_work; - svn_wc__db_status_t below_status; - /* Use STMT_SELECT_NODE_INFO directly instead of read_info plus - info_below_working */ - SVN_ERR(info_below_working(&below_base, &below_work, &below_status, - wcroot, local_relpath, -1, scratch_pool)); - if ((below_base || below_work) - && below_status != svn_wc__db_status_not_present - && below_status != svn_wc__db_status_deleted) + for (i = 0; moved_nodes && (i < moved_nodes->nelts); i++) { - add_work = TRUE; - refetch_depth = TRUE; + struct moved_node_t *mn = APR_ARRAY_IDX(moved_nodes, i, + struct moved_node_t *); + + if (mn->moved_from_depth > 0) + { + svn_pool_clear(iterpool); + + SVN_ERR(resolve_moved_from(&mn->local_relpath, &mn->op_depth, + wcroot, local_relpath, + mn->local_relpath, + mn->moved_from_depth, + scratch_pool, iterpool)); + + if (!mn->local_relpath) + svn_sort__array_delete(moved_nodes, i--, 1); + } } - select_depth = relpath_depth(local_relpath); + svn_pool_destroy(iterpool); } - else + + if (!b->moved_to_relpath) { - add_work = TRUE; - if (status != svn_wc__db_status_normal) - SVN_ERR(op_depth_of(&select_depth, wcroot, local_relpath)); - else - select_depth = 0; /* Deleting BASE node */ + SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, + STMT_CLEAR_MOVED_TO_DESCENDANTS)); + SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, + local_relpath)); + SVN_ERR(svn_sqlite__update(NULL, stmt)); + + if (op_root) + { + SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, + STMT_CLEAR_MOVED_TO_FROM_DEST)); + SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, + local_relpath)); + + SVN_ERR(svn_sqlite__update(NULL, stmt)); + } } + /* ### Put actual-only nodes into the list? */ if (b->notify) { SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_INSERT_DELETE_LIST)); SVN_ERR(svn_sqlite__bindf(stmt, "isd", - wcroot->wc_id, local_relpath, select_depth)); + wcroot->wc_id, local_relpath, working_op_depth)); SVN_ERR(svn_sqlite__step_done(stmt)); } SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_DELETE_NODES_ABOVE_DEPTH_RECURSIVE)); SVN_ERR(svn_sqlite__bindf(stmt, "isd", - wcroot->wc_id, local_relpath, delete_depth)); + wcroot->wc_id, local_relpath, delete_op_depth)); SVN_ERR(svn_sqlite__step_done(stmt)); - if (refetch_depth) - SVN_ERR(op_depth_of(&select_depth, wcroot, local_relpath)); - /* Delete ACTUAL_NODE rows, but leave those that have changelist and a NODES row. */ SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, @@ -7781,7 +8004,7 @@ delete_node(void *baton, STMT_INSERT_DELETE_FROM_NODE_RECURSIVE)); SVN_ERR(svn_sqlite__bindf(stmt, "isdd", wcroot->wc_id, local_relpath, - select_depth, delete_depth)); + keep_op_depth, delete_op_depth)); SVN_ERR(svn_sqlite__step_done(stmt)); } @@ -8680,7 +8903,11 @@ read_children_info(svn_wc__db_wcroot_t *wcroot, else child->op_root = (op_depth == relpath_depth(child_relpath)); - svn_hash_sets(nodes, apr_pstrdup(result_pool, name), child); + if (op_depth && child->op_root) + child_item->info.moved_here = svn_sqlite__column_boolean(stmt, 20); + + if (new_child) + svn_hash_sets(nodes, apr_pstrdup(result_pool, name), child); } if (op_depth == 0) @@ -8702,18 +8929,48 @@ read_children_info(svn_wc__db_wcroot_t *wcroot, child_item->nr_layers++; child_item->info.have_more_work = (child_item->nr_layers > 1); - /* Moved-to can only exist at op_depth > 0. */ - /* ### Should we really do this for every layer where op_depth > 0 - in undefined order? */ + + /* A local_relpath can be moved multiple times at different op + depths and it really depends on the caller what is interesting. + We provide a simple linked list with the moved_from information */ + moved_to_relpath = svn_sqlite__column_text(stmt, 21, NULL); if (moved_to_relpath) - child_item->info.moved_to_abspath = - svn_dirent_join(wcroot->abspath, moved_to_relpath, result_pool); + { + struct svn_wc__db_moved_to_info_t *moved_to; + struct svn_wc__db_moved_to_info_t **next; + const char *shadow_op_relpath; + int cur_op_depth; + + moved_to = apr_pcalloc(result_pool, sizeof(*moved_to)); + moved_to->moved_to_abspath = svn_dirent_join(wcroot->abspath, + moved_to_relpath, + result_pool); - /* Moved-here can only exist at op_depth > 0. */ - /* ### Should we really do this for every layer where op_depth > 0 - in undefined order? */ - child_item->info.moved_here = svn_sqlite__column_boolean(stmt, 20); + cur_op_depth = relpath_depth(child_relpath); + shadow_op_relpath = child_relpath; + + while (cur_op_depth > op_depth) + { + shadow_op_relpath = svn_relpath_dirname(shadow_op_relpath, + scratch_pool); + cur_op_depth--; + } + + moved_to->shadow_op_root_abspath = + svn_dirent_join(wcroot->abspath, shadow_op_relpath, + result_pool); + + next = &child_item->info.moved_to; + + while (*next && + 0 < strcmp((*next)->shadow_op_root_abspath, + moved_to->shadow_op_root_abspath)) + next = &((*next)->next); + + moved_to->next = *next; + *next = moved_to; + } } SVN_ERR(svn_sqlite__step(&have_row, stmt)); @@ -8801,6 +9058,178 @@ svn_wc__db_read_children_info(apr_hash_t **nodes, return SVN_NO_ERROR; } +static svn_error_t * +db_read_props(apr_hash_t **props, + svn_wc__db_wcroot_t *wcroot, + const char *local_relpath, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + +static svn_error_t * +read_single_info(const struct svn_wc__db_info_t **info, + svn_wc__db_wcroot_t *wcroot, + const char *local_relpath, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + struct svn_wc__db_info_t *mtb; + apr_int64_t repos_id; + const svn_checksum_t *checksum; + const char *original_repos_relpath; + svn_boolean_t have_work; + + mtb = apr_pcalloc(result_pool, sizeof(*mtb)); + + SVN_ERR(read_info(&mtb->status, &mtb->kind, &mtb->revnum, + &mtb->repos_relpath, &repos_id, &mtb->changed_rev, + &mtb->changed_date, &mtb->changed_author, &mtb->depth, + &checksum, NULL, &original_repos_relpath, NULL, NULL, + &mtb->lock, &mtb->recorded_size, &mtb->recorded_time, + &mtb->changelist, &mtb->conflicted, &mtb->op_root, + &mtb->had_props, &mtb->props_mod, &mtb->have_base, + &mtb->have_more_work, &have_work, + wcroot, local_relpath, + result_pool, scratch_pool)); + + /* Query the same rows in the database again for move information */ + if (have_work && (mtb->have_base || mtb->have_more_work)) + { + svn_sqlite__stmt_t *stmt; + svn_boolean_t have_row; + const char *cur_relpath = NULL; + int cur_op_depth; + + SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, + STMT_SELECT_MOVED_TO_NODE)); + SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath)); + + SVN_ERR(svn_sqlite__step(&have_row, stmt)); + + while (have_row) + { + struct svn_wc__db_moved_to_info_t *move; + int op_depth = svn_sqlite__column_int(stmt, 0); + const char *moved_to_relpath = svn_sqlite__column_text(stmt, 1, NULL); + + move = apr_pcalloc(result_pool, sizeof(*move)); + move->moved_to_abspath = svn_dirent_join(wcroot->abspath, + moved_to_relpath, + result_pool); + + if (!cur_relpath) + { + cur_relpath = local_relpath; + cur_op_depth = relpath_depth(cur_relpath); + } + while (cur_op_depth > op_depth) + { + cur_relpath = svn_relpath_dirname(cur_relpath, scratch_pool); + cur_op_depth--; + } + move->shadow_op_root_abspath = svn_dirent_join(wcroot->abspath, + cur_relpath, + result_pool); + + move->next = mtb->moved_to; + mtb->moved_to = move; + + SVN_ERR(svn_sqlite__step(&have_row, stmt)); + } + + SVN_ERR(svn_sqlite__reset(stmt)); + } + + /* Maybe we have to get some shadowed lock from BASE to make our test suite + happy... (It might be completely unrelated, but...) + This queries the same BASE row again, joined to the lock table */ + if (mtb->have_base && (have_work || mtb->kind == svn_node_file)) + { + svn_boolean_t update_root; + svn_wc__db_lock_t **lock_arg = NULL; + + if (have_work) + lock_arg = &mtb->lock; + + SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL, lock_arg, NULL, NULL, + &update_root, + wcroot, local_relpath, + result_pool, scratch_pool)); + + mtb->file_external = (update_root && mtb->kind == svn_node_file); + } + + if (mtb->status == svn_wc__db_status_added) + { + svn_wc__db_status_t status; + + SVN_ERR(scan_addition(&status, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, + wcroot, local_relpath, + result_pool, scratch_pool)); + + mtb->moved_here = (status == svn_wc__db_status_moved_here); + mtb->incomplete = (status == svn_wc__db_status_incomplete); + } + +#ifdef HAVE_SYMLINK + if (mtb->kind == svn_node_file + && (mtb->had_props || mtb->props_mod)) + { + apr_hash_t *properties; + + if (mtb->props_mod) + SVN_ERR(db_read_props(&properties, + wcroot, local_relpath, + scratch_pool, scratch_pool)); + else + SVN_ERR(db_read_pristine_props(&properties, wcroot, local_relpath, + TRUE /* deleted_ok */, + scratch_pool, scratch_pool)); + + mtb->special = (NULL != svn_hash_gets(properties, SVN_PROP_SPECIAL)); + } +#endif + + mtb->has_checksum = (checksum != NULL); + mtb->copied = (original_repos_relpath != NULL); + + SVN_ERR(svn_wc__db_fetch_repos_info(&mtb->repos_root_url, &mtb->repos_uuid, + wcroot->sdb, repos_id, result_pool)); + + if (mtb->kind == svn_node_dir) + SVN_ERR(is_wclocked(&mtb->locked, wcroot, local_relpath, scratch_pool)); + + *info = mtb; + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_wc__db_read_single_info(const struct svn_wc__db_info_t **info, + svn_wc__db_t *db, + const char *local_abspath, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + svn_wc__db_wcroot_t *wcroot; + const char *local_relpath; + + SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath)); + + 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(read_single_info(info, wcroot, local_relpath, + result_pool, scratch_pool), + wcroot); + + return SVN_NO_ERROR; +} + svn_error_t * svn_wc__db_read_pristine_info(svn_wc__db_status_t *status, svn_node_kind_t *kind, @@ -10730,7 +11159,7 @@ commit_node(svn_wc__db_wcroot_t *wcroot, if we need to remove shadowed layers below our descendants. */ SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, - STMT_DELETE_ALL_LAYERS)); + STMT_DELETE_NODE_ALL_LAYERS)); SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath)); SVN_ERR(svn_sqlite__update(&affected_rows, stmt)); |