aboutsummaryrefslogtreecommitdiff
path: root/contrib/subversion/subversion/libsvn_wc/wc_db.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/subversion/subversion/libsvn_wc/wc_db.c')
-rw-r--r--contrib/subversion/subversion/libsvn_wc/wc_db.c661
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(&copyfrom_id, &copyfrom_relpath, &copyfrom_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(&copyfrom_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));