You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by st...@apache.org on 2015/06/14 22:58:16 UTC
svn commit: r1685464 [20/43] - in /subversion/branches/fsx-1.10: ./ build/
build/ac-macros/ build/generator/ build/generator/templates/
contrib/client-side/svncopy/ doc/ notes/ subversion/bindings/javahl/
subversion/bindings/javahl/native/ subversion/b...
Modified: subversion/branches/fsx-1.10/subversion/libsvn_wc/questions.c
URL: http://svn.apache.org/viewvc/subversion/branches/fsx-1.10/subversion/libsvn_wc/questions.c?rev=1685464&r1=1685463&r2=1685464&view=diff
==============================================================================
--- subversion/branches/fsx-1.10/subversion/libsvn_wc/questions.c (original)
+++ subversion/branches/fsx-1.10/subversion/libsvn_wc/questions.c Sun Jun 14 20:58:10 2015
@@ -369,7 +369,8 @@ internal_conflicted_p(svn_boolean_t *tex
svn_boolean_t resolved_text = FALSE;
svn_boolean_t resolved_props = FALSE;
- SVN_ERR(svn_wc__db_read_conflict(&conflicts, db, local_abspath,
+ SVN_ERR(svn_wc__db_read_conflict(&conflicts, NULL, NULL,
+ db, local_abspath,
scratch_pool, scratch_pool));
if (!conflicts)
@@ -596,18 +597,150 @@ svn_wc__has_switched_subtrees(svn_boolea
}
+/* A baton for use with modcheck_found_entry(). */
+typedef struct modcheck_baton_t {
+ svn_boolean_t ignore_unversioned;
+ svn_boolean_t found_mod; /* whether a modification has been found */
+ svn_boolean_t found_not_delete; /* Found a not-delete modification */
+} modcheck_baton_t;
+
+/* An implementation of svn_wc_status_func4_t. */
+static svn_error_t *
+modcheck_callback(void *baton,
+ const char *local_abspath,
+ const svn_wc_status3_t *status,
+ apr_pool_t *scratch_pool)
+{
+ modcheck_baton_t *mb = baton;
+
+ switch (status->node_status)
+ {
+ case svn_wc_status_normal:
+ case svn_wc_status_ignored:
+ case svn_wc_status_none:
+ case svn_wc_status_external:
+ break;
+
+ case svn_wc_status_incomplete:
+ if ((status->text_status != svn_wc_status_normal
+ && status->text_status != svn_wc_status_none)
+ || (status->prop_status != svn_wc_status_normal
+ && status->prop_status != svn_wc_status_none))
+ {
+ mb->found_mod = TRUE;
+ mb->found_not_delete = TRUE;
+ /* Incomplete, but local modifications */
+ return svn_error_create(SVN_ERR_CEASE_INVOCATION, NULL, NULL);
+ }
+ break;
+
+ case svn_wc_status_deleted:
+ mb->found_mod = TRUE;
+ if (!mb->ignore_unversioned
+ && status->actual_kind != svn_node_none
+ && status->actual_kind != svn_node_unknown)
+ {
+ /* The delete is obstructed by something unversioned */
+ mb->found_not_delete = TRUE;
+ return svn_error_create(SVN_ERR_CEASE_INVOCATION, NULL, NULL);
+ }
+ break;
+
+ case svn_wc_status_unversioned:
+ if (mb->ignore_unversioned)
+ break;
+ /* else fall through */
+ case svn_wc_status_missing:
+ case svn_wc_status_obstructed:
+ mb->found_mod = TRUE;
+ mb->found_not_delete = TRUE;
+ /* Exit from the status walker: We know what we want to know */
+ return svn_error_create(SVN_ERR_CEASE_INVOCATION, NULL, NULL);
+
+ default:
+ case svn_wc_status_added:
+ case svn_wc_status_replaced:
+ case svn_wc_status_modified:
+ mb->found_mod = TRUE;
+ mb->found_not_delete = TRUE;
+ /* Exit from the status walker: We know what we want to know */
+ return svn_error_create(SVN_ERR_CEASE_INVOCATION, NULL, NULL);
+ }
+
+ return SVN_NO_ERROR;
+}
+
+
+/* Set *MODIFIED to true iff there are any local modifications within the
+ * tree rooted at LOCAL_ABSPATH, using DB. If *MODIFIED
+ * is set to true and all the local modifications were deletes then set
+ * *ALL_EDITS_ARE_DELETES to true, set it to false otherwise. LOCAL_ABSPATH
+ * may be a file or a directory. */
+svn_error_t *
+svn_wc__node_has_local_mods(svn_boolean_t *modified,
+ svn_boolean_t *all_edits_are_deletes,
+ svn_wc__db_t *db,
+ const char *local_abspath,
+ svn_boolean_t ignore_unversioned,
+ svn_cancel_func_t cancel_func,
+ void *cancel_baton,
+ apr_pool_t *scratch_pool)
+{
+ modcheck_baton_t modcheck_baton = { FALSE, FALSE, FALSE };
+ svn_error_t *err;
+
+ if (!all_edits_are_deletes)
+ {
+ SVN_ERR(svn_wc__db_has_db_mods(modified, db, local_abspath,
+ scratch_pool));
+
+ if (*modified)
+ return SVN_NO_ERROR;
+ }
+
+ modcheck_baton.ignore_unversioned = ignore_unversioned;
+
+ /* Walk the WC tree for status with depth infinity, looking for any local
+ * modifications. If it's a "sparse" directory, that's OK: there can be
+ * no local mods in the pieces that aren't present in the WC. */
+
+ err = svn_wc__internal_walk_status(db, local_abspath,
+ svn_depth_infinity,
+ FALSE, FALSE, FALSE, NULL,
+ modcheck_callback, &modcheck_baton,
+ cancel_func, cancel_baton,
+ scratch_pool);
+
+ if (err && err->apr_err == SVN_ERR_CEASE_INVOCATION)
+ svn_error_clear(err);
+ else
+ SVN_ERR(err);
+
+ *modified = modcheck_baton.found_mod;
+ if (all_edits_are_deletes)
+ *all_edits_are_deletes = (modcheck_baton.found_mod
+ && !modcheck_baton.found_not_delete);
+
+ return SVN_NO_ERROR;
+}
+
svn_error_t *
svn_wc__has_local_mods(svn_boolean_t *is_modified,
svn_wc_context_t *wc_ctx,
const char *local_abspath,
+ svn_boolean_t ignore_unversioned,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *scratch_pool)
{
- return svn_error_trace(svn_wc__db_has_local_mods(is_modified,
- wc_ctx->db,
- local_abspath,
- cancel_func,
- cancel_baton,
- scratch_pool));
+ svn_boolean_t modified;
+
+ SVN_ERR(svn_wc__node_has_local_mods(&modified, NULL,
+ wc_ctx->db, local_abspath,
+ ignore_unversioned,
+ cancel_func, cancel_baton,
+ scratch_pool));
+
+ *is_modified = modified;
+ return SVN_NO_ERROR;
}
Modified: subversion/branches/fsx-1.10/subversion/libsvn_wc/revision_status.c
URL: http://svn.apache.org/viewvc/subversion/branches/fsx-1.10/subversion/libsvn_wc/revision_status.c?rev=1685464&r1=1685463&r2=1685464&view=diff
==============================================================================
--- subversion/branches/fsx-1.10/subversion/libsvn_wc/revision_status.c (original)
+++ subversion/branches/fsx-1.10/subversion/libsvn_wc/revision_status.c Sun Jun 14 20:58:10 2015
@@ -60,8 +60,14 @@ svn_wc_revision_status2(svn_wc_revision_
&result->modified,
&result->switched,
wc_ctx->db, local_abspath, trail_url,
- committed, cancel_func, cancel_baton,
+ committed,
scratch_pool));
+ if (!result->modified)
+ SVN_ERR(svn_wc__node_has_local_mods(&result->modified, NULL,
+ wc_ctx->db, local_abspath, TRUE,
+ cancel_func, cancel_baton,
+ scratch_pool));
+
return SVN_NO_ERROR;
}
Modified: subversion/branches/fsx-1.10/subversion/libsvn_wc/status.c
URL: http://svn.apache.org/viewvc/subversion/branches/fsx-1.10/subversion/libsvn_wc/status.c?rev=1685464&r1=1685463&r2=1685464&view=diff
==============================================================================
--- subversion/branches/fsx-1.10/subversion/libsvn_wc/status.c (original)
+++ subversion/branches/fsx-1.10/subversion/libsvn_wc/status.c Sun Jun 14 20:58:10 2015
@@ -53,6 +53,27 @@
#include "private/svn_editor.h"
+/* The file internal variant of svn_wc_status3_t, with slightly more
+ data.
+
+ Instead of directly creating svn_wc_status3_t instances, we really
+ create instances of this struct with slightly more data for processing
+ by the status walker and status editor.
+
+ svn_wc_status3_dup() allocates space for this struct, but doesn't
+ copy the actual data. The remaining fields are copied by hash_stash(),
+ which is where the status editor stashes information for producing
+ later. */
+typedef struct svn_wc__internal_status_t
+{
+ svn_wc_status3_t s; /* First member; same pointer*/
+
+ svn_boolean_t has_descendants;
+ svn_boolean_t op_root;
+
+ /* Make sure to update hash_stash() when adding values here */
+} svn_wc__internal_status_t;
+
/*** Baton used for walking the local status */
struct walk_status_baton
@@ -126,7 +147,7 @@ struct edit_baton
const apr_array_header_t *ignores;
/* Status item for the path represented by the anchor of the edit. */
- svn_wc_status3_t *anchor_status;
+ svn_wc__internal_status_t *anchor_status;
/* Was open_root() called for this edit drive? */
svn_boolean_t root_opened;
@@ -275,63 +296,20 @@ get_repos_root_url_relpath(const char **
*repos_root_url = apr_pstrdup(result_pool, parent_repos_root_url);
*repos_uuid = apr_pstrdup(result_pool, parent_repos_uuid);
}
- else if (info->status == svn_wc__db_status_added)
- {
- SVN_ERR(svn_wc__db_scan_addition(NULL, NULL,
- repos_relpath, repos_root_url,
- repos_uuid, NULL, NULL, NULL, NULL,
- db, local_abspath,
- result_pool, scratch_pool));
- }
- else if (info->status == svn_wc__db_status_deleted
- && !info->have_more_work
- && info->have_base)
- {
- SVN_ERR(svn_wc__db_base_get_info(NULL, NULL, NULL, repos_relpath,
- repos_root_url, repos_uuid, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL, NULL,
- NULL, NULL,
- db, local_abspath,
- result_pool, scratch_pool));
- }
- else if (info->status == svn_wc__db_status_deleted)
- {
- const char *work_del_abspath;
- const char *add_abspath;
-
- /* Handles working DELETE and the special case where there is just
- svn_wc__db_status_not_present in WORKING */
-
- SVN_ERR(svn_wc__db_scan_deletion(NULL, NULL, &work_del_abspath, NULL,
- db, local_abspath,
- scratch_pool, scratch_pool));
-
- /* The parent of what has been deleted must be added */
- add_abspath = svn_dirent_dirname(work_del_abspath, scratch_pool);
-
- SVN_ERR(svn_wc__db_scan_addition(NULL, NULL, repos_relpath,
- repos_root_url, repos_uuid, NULL,
- NULL, NULL, NULL,
- db, add_abspath,
- result_pool, scratch_pool));
-
- *repos_relpath = svn_relpath_join(*repos_relpath,
- svn_dirent_skip_ancestor(
- add_abspath,
- local_abspath),
- result_pool);
- }
else
{
- *repos_relpath = NULL;
- *repos_root_url = NULL;
- *repos_uuid = NULL;
+ SVN_ERR(svn_wc__db_read_repos_info(NULL,
+ repos_relpath, repos_root_url,
+ repos_uuid,
+ db, local_abspath,
+ result_pool, scratch_pool));
}
+
return SVN_NO_ERROR;
}
static svn_error_t *
-internal_status(svn_wc_status3_t **status,
+internal_status(svn_wc__internal_status_t **status,
svn_wc__db_t *db,
const char *local_abspath,
svn_boolean_t check_working_copy,
@@ -358,7 +336,7 @@ internal_status(svn_wc_status3_t **statu
The status struct's repos_lock field will be set to REPOS_LOCK.
*/
static svn_error_t *
-assemble_status(svn_wc_status3_t **status,
+assemble_status(svn_wc__internal_status_t **status,
svn_wc__db_t *db,
const char *local_abspath,
const char *parent_repos_root_url,
@@ -373,14 +351,12 @@ assemble_status(svn_wc_status3_t **statu
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
+ svn_wc__internal_status_t *inner_stat;
svn_wc_status3_t *stat;
svn_boolean_t switched_p = FALSE;
svn_boolean_t copied = FALSE;
svn_boolean_t conflicted;
const char *moved_from_abspath = NULL;
- svn_filesize_t filesize = (dirent && (dirent->kind == svn_node_file))
- ? dirent->filesize
- : SVN_INVALID_FILESIZE;
/* Defaults for two main variables. */
enum svn_wc_status_kind node_status = svn_wc_status_normal;
@@ -612,7 +588,8 @@ assemble_status(svn_wc_status3_t **statu
This filter should match the filter in is_sendable_status() */
if (! get_all)
if (((node_status == svn_wc_status_none)
- || (node_status == svn_wc_status_normal))
+ || (node_status == svn_wc_status_normal)
+ || (node_status == svn_wc_status_deleted && !info->op_root))
&& (! switched_p)
&& (! info->locked)
@@ -628,7 +605,10 @@ assemble_status(svn_wc_status3_t **statu
/* 6. Build and return a status structure. */
- stat = apr_pcalloc(result_pool, sizeof(**status));
+ inner_stat = apr_pcalloc(result_pool, sizeof(*inner_stat));
+ stat = &inner_stat->s;
+ inner_stat->has_descendants = info->has_descendants;
+ inner_stat->op_root = info->op_root;
switch (info->kind)
{
@@ -644,7 +624,22 @@ assemble_status(svn_wc_status3_t **statu
stat->kind = svn_node_unknown;
}
stat->depth = info->depth;
- stat->filesize = filesize;
+
+ if (dirent)
+ {
+ stat->filesize = (dirent->kind == svn_node_file)
+ ? dirent->filesize
+ : SVN_INVALID_FILESIZE;
+ stat->actual_kind = dirent->special ? svn_node_symlink
+ : dirent->kind;
+ }
+ else
+ {
+ stat->filesize = SVN_INVALID_FILESIZE;
+ stat->actual_kind = ignore_text_mods ? svn_node_unknown
+ : svn_node_none;
+ }
+
stat->node_status = node_status;
stat->text_status = text_status;
stat->prop_status = prop_status;
@@ -702,7 +697,7 @@ assemble_status(svn_wc_status3_t **statu
stat->file_external = info->file_external;
- *status = stat;
+ *status = inner_stat;
return SVN_NO_ERROR;
}
@@ -716,7 +711,7 @@ assemble_status(svn_wc_status3_t **statu
node_status to svn_wc_status_unversioned.
*/
static svn_error_t *
-assemble_unversioned(svn_wc_status3_t **status,
+assemble_unversioned(svn_wc__internal_status_t **status,
svn_wc__db_t *db,
const char *local_abspath,
const svn_io_dirent2_t *dirent,
@@ -725,17 +720,30 @@ assemble_unversioned(svn_wc_status3_t **
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
+ svn_wc__internal_status_t *inner_status;
svn_wc_status3_t *stat;
/* return a fairly blank structure. */
- stat = apr_pcalloc(result_pool, sizeof(*stat));
+ inner_status = apr_pcalloc(result_pool, sizeof(*inner_status));
+ stat = &inner_status->s;
/*stat->versioned = FALSE;*/
stat->kind = svn_node_unknown; /* not versioned */
stat->depth = svn_depth_unknown;
- stat->filesize = (dirent && dirent->kind == svn_node_file)
- ? dirent->filesize
- : SVN_INVALID_FILESIZE;
+ if (dirent)
+ {
+ stat->actual_kind = dirent->special ? svn_node_symlink
+ : dirent->kind;
+ stat->filesize = (dirent->kind == svn_node_file)
+ ? dirent->filesize
+ : SVN_INVALID_FILESIZE;
+ }
+ else
+ {
+ stat->actual_kind = svn_node_none;
+ stat->filesize = SVN_INVALID_FILESIZE;
+ }
+
stat->node_status = svn_wc_status_none;
stat->text_status = svn_wc_status_none;
stat->prop_status = svn_wc_status_none;
@@ -772,7 +780,7 @@ assemble_unversioned(svn_wc_status3_t **
stat->conflicted = tree_conflicted;
stat->changelist = NULL;
- *status = stat;
+ *status = inner_status;
return SVN_NO_ERROR;
}
@@ -793,7 +801,7 @@ send_status_structure(const struct walk_
void *status_baton,
apr_pool_t *scratch_pool)
{
- svn_wc_status3_t *statstruct;
+ svn_wc__internal_status_t *statstruct;
const svn_lock_t *repos_lock = NULL;
/* Check for a repository lock. */
@@ -827,7 +835,8 @@ send_status_structure(const struct walk_
if (statstruct && status_func)
return svn_error_trace((*status_func)(status_baton, local_abspath,
- statstruct, scratch_pool));
+ &statstruct->s,
+ scratch_pool));
return SVN_NO_ERROR;
}
@@ -983,7 +992,7 @@ send_unversioned_item(const struct walk_
{
svn_boolean_t is_ignored;
svn_boolean_t is_external;
- svn_wc_status3_t *status;
+ svn_wc__internal_status_t *status;
const char *base_name = svn_dirent_basename(local_abspath, NULL);
is_ignored = svn_wc_match_ignore_list(base_name, patterns, scratch_pool);
@@ -995,12 +1004,12 @@ send_unversioned_item(const struct walk_
is_external = is_external_path(wb->externals, local_abspath, scratch_pool);
if (is_external)
- status->node_status = svn_wc_status_external;
+ status->s.node_status = svn_wc_status_external;
/* We can have a tree conflict on an unversioned path, i.e. an incoming
* delete on a locally deleted path during an update. Don't ever ignore
* those! */
- if (status->conflicted)
+ if (status->s.conflicted)
is_ignored = FALSE;
/* If we aren't ignoring it, or if it's an externals path, pass this
@@ -1009,7 +1018,7 @@ send_unversioned_item(const struct walk_
|| !is_ignored
|| is_external)
return svn_error_trace((*status_func)(status_baton, local_abspath,
- status, scratch_pool));
+ &status->s, scratch_pool));
return SVN_NO_ERROR;
}
@@ -1112,7 +1121,7 @@ one_child_status(const struct walk_statu
/* Descend in subdirectories. */
if (depth == svn_depth_infinity
- && info->kind == svn_node_dir)
+ && info->has_descendants /* is dir, or was dir and tc descendants */)
{
SVN_ERR(get_dir_status(wb, local_abspath, TRUE,
dir_repos_root_url, dir_repos_relpath,
@@ -1472,9 +1481,16 @@ hash_stash(void *baton,
{
apr_hash_t *stat_hash = baton;
apr_pool_t *hash_pool = apr_hash_pool_get(stat_hash);
+ void *new_status = svn_wc_dup_status3(status, hash_pool);
+ const svn_wc__internal_status_t *old_status = (const void*)status;
+
+ /* Copy the internal/private data. */
+ svn_wc__internal_status_t *is = new_status;
+ is->has_descendants = old_status->has_descendants;
+ is->op_root = old_status->op_root;
+
assert(! svn_hash_gets(stat_hash, path));
- svn_hash_sets(stat_hash, apr_pstrdup(hash_pool, path),
- svn_wc_dup_status3(status, hash_pool));
+ svn_hash_sets(stat_hash, apr_pstrdup(hash_pool, path), new_status);
return SVN_NO_ERROR;
}
@@ -1539,6 +1555,7 @@ tweak_statushash(void *baton,
/* If not, make it so. */
if (! statstruct)
{
+ svn_wc__internal_status_t *i_stat;
/* If this item isn't being added, then we're most likely
dealing with a non-recursive (or at least partially
non-recursive) working copy. Due to bugs in how the client
@@ -1554,8 +1571,9 @@ tweak_statushash(void *baton,
return SVN_NO_ERROR;
/* Use the public API to get a statstruct, and put it into the hash. */
- SVN_ERR(internal_status(&statstruct, db, local_abspath,
+ SVN_ERR(internal_status(&i_stat, db, local_abspath,
check_working_copy, pool, scratch_pool));
+ statstruct = &i_stat->s;
statstruct->repos_lock = repos_lock;
svn_hash_sets(statushash, apr_pstrdup(pool, local_abspath), statstruct);
}
@@ -1594,9 +1612,9 @@ tweak_statushash(void *baton,
statstruct->repos_relpath = apr_pstrdup(pool, b->repos_relpath);
statstruct->repos_root_url =
- b->edit_baton->anchor_status->repos_root_url;
+ b->edit_baton->anchor_status->s.repos_root_url;
statstruct->repos_uuid =
- b->edit_baton->anchor_status->repos_uuid;
+ b->edit_baton->anchor_status->s.repos_uuid;
}
/* The last committed date, and author for deleted items
@@ -1636,9 +1654,9 @@ tweak_statushash(void *baton,
{
statstruct->repos_relpath = apr_pstrdup(pool, b->repos_relpath);
statstruct->repos_root_url =
- b->edit_baton->anchor_status->repos_root_url;
+ b->edit_baton->anchor_status->s.repos_root_url;
statstruct->repos_uuid =
- b->edit_baton->anchor_status->repos_uuid;
+ b->edit_baton->anchor_status->s.repos_uuid;
}
statstruct->ood_kind = b->ood_kind;
if (b->ood_changed_author)
@@ -1654,7 +1672,7 @@ find_dir_repos_relpath(const struct dir_
{
/* If we have no name, we're the root, return the anchor URL. */
if (! db->name)
- return db->edit_baton->anchor_status->repos_relpath;
+ return db->edit_baton->anchor_status->s.repos_relpath;
else
{
const char *repos_relpath;
@@ -1686,7 +1704,7 @@ make_dir_baton(void **dir_baton,
struct edit_baton *eb = edit_baton;
struct dir_baton *d;
const char *local_abspath;
- const svn_wc_status3_t *status_in_parent;
+ const svn_wc__internal_status_t *status_in_parent;
apr_pool_t *dir_pool;
if (parent_baton)
@@ -1745,8 +1763,7 @@ make_dir_baton(void **dir_baton,
status_in_parent = eb->anchor_status;
if (status_in_parent
- && status_in_parent->versioned
- && (status_in_parent->kind == svn_node_dir)
+ && (status_in_parent->has_descendants)
&& (! d->excluded)
&& (d->depth == svn_depth_unknown
|| d->depth == svn_depth_infinity
@@ -1758,9 +1775,9 @@ make_dir_baton(void **dir_baton,
const apr_array_header_t *ignores = eb->ignores;
SVN_ERR(get_dir_status(&eb->wb, local_abspath, TRUE,
- status_in_parent->repos_root_url,
+ status_in_parent->s.repos_root_url,
NULL /*parent_repos_relpath*/,
- status_in_parent->repos_uuid,
+ status_in_parent->s.repos_uuid,
NULL,
NULL /* dirent */, ignores,
d->depth == svn_depth_files
@@ -1775,7 +1792,7 @@ make_dir_baton(void **dir_baton,
this_dir_status = svn_hash_gets(d->statii, d->local_abspath);
if (this_dir_status && this_dir_status->versioned
&& (d->depth == svn_depth_unknown
- || d->depth > status_in_parent->depth))
+ || d->depth > status_in_parent->s.depth))
{
d->depth = this_dir_status->depth;
}
@@ -1821,10 +1838,11 @@ make_file_baton(struct dir_baton *parent
* This implementation should match the filter in assemble_status()
*/
static svn_boolean_t
-is_sendable_status(const svn_wc_status3_t *status,
+is_sendable_status(const svn_wc__internal_status_t *i_status,
svn_boolean_t no_ignore,
svn_boolean_t get_all)
{
+ const svn_wc_status3_t *status = &i_status->s;
/* If the repository status was touched at all, it's interesting. */
if (status->repos_node_status != svn_wc_status_none)
return TRUE;
@@ -1850,8 +1868,10 @@ is_sendable_status(const svn_wc_status3_
return TRUE;
/* If the text, property or tree state is interesting, send it. */
- if ((status->node_status != svn_wc_status_none
- && (status->node_status != svn_wc_status_normal)))
+ if ((status->node_status != svn_wc_status_none)
+ && (status->node_status != svn_wc_status_normal)
+ && !(status->node_status == svn_wc_status_deleted
+ && !i_status->op_root))
return TRUE;
/* If it's switched, send it. */
@@ -1934,14 +1954,14 @@ handle_statii(struct edit_baton *eb,
for (hi = apr_hash_first(pool, statii); hi; hi = apr_hash_next(hi))
{
const char *local_abspath = apr_hash_this_key(hi);
- svn_wc_status3_t *status = apr_hash_this_val(hi);
+ svn_wc__internal_status_t *status = apr_hash_this_val(hi);
/* Clear the subpool. */
svn_pool_clear(iterpool);
/* Now, handle the status. We don't recurse for svn_depth_immediates
because we already have the subdirectories' statii. */
- if (status->versioned && status->kind == svn_node_dir
+ if (status->has_descendants
&& (depth == svn_depth_unknown
|| depth == svn_depth_infinity))
{
@@ -1957,9 +1977,9 @@ handle_statii(struct edit_baton *eb,
iterpool));
}
if (dir_was_deleted)
- status->repos_node_status = svn_wc_status_deleted;
+ status->s.repos_node_status = svn_wc_status_deleted;
if (is_sendable_status(status, eb->no_ignore, eb->get_all))
- SVN_ERR((eb->status_func)(eb->status_baton, local_abspath, status,
+ SVN_ERR((eb->status_func)(eb->status_baton, local_abspath, &status->s,
iterpool));
}
@@ -2159,17 +2179,17 @@ close_directory(void *dir_baton,
/* We're editing the root dir of the WC. As its repos
status info isn't otherwise set, set it directly to
trigger invocation of the status callback below. */
- eb->anchor_status->repos_node_status = repos_node_status;
- eb->anchor_status->repos_prop_status = repos_prop_status;
- eb->anchor_status->repos_text_status = repos_text_status;
+ eb->anchor_status->s.repos_node_status = repos_node_status;
+ eb->anchor_status->s.repos_prop_status = repos_prop_status;
+ eb->anchor_status->s.repos_text_status = repos_text_status;
/* If the root dir is out of date set the ood info directly too. */
- if (db->ood_changed_rev != eb->anchor_status->revision)
+ if (db->ood_changed_rev != eb->anchor_status->s.revision)
{
- eb->anchor_status->ood_changed_rev = db->ood_changed_rev;
- eb->anchor_status->ood_changed_date = db->ood_changed_date;
- eb->anchor_status->ood_kind = db->ood_kind;
- eb->anchor_status->ood_changed_author =
+ eb->anchor_status->s.ood_changed_rev = db->ood_changed_rev;
+ eb->anchor_status->s.ood_changed_date = db->ood_changed_date;
+ eb->anchor_status->s.ood_kind = db->ood_kind;
+ eb->anchor_status->s.ood_changed_author =
apr_pstrdup(pool, db->ood_changed_author);
}
}
@@ -2180,25 +2200,25 @@ close_directory(void *dir_baton,
if (pb && ! db->excluded)
{
svn_boolean_t was_deleted = FALSE;
- const svn_wc_status3_t *dir_status;
+ svn_wc__internal_status_t *dir_status;
/* See if the directory was deleted or replaced. */
dir_status = svn_hash_gets(pb->statii, db->local_abspath);
if (dir_status &&
- ((dir_status->repos_node_status == svn_wc_status_deleted)
- || (dir_status->repos_node_status == svn_wc_status_replaced)))
+ ((dir_status->s.repos_node_status == svn_wc_status_deleted)
+ || (dir_status->s.repos_node_status == svn_wc_status_replaced)))
was_deleted = TRUE;
/* Now do the status reporting. */
SVN_ERR(handle_statii(eb,
- dir_status ? dir_status->repos_root_url : NULL,
- dir_status ? dir_status->repos_relpath : NULL,
- dir_status ? dir_status->repos_uuid : NULL,
+ dir_status ? dir_status->s.repos_root_url : NULL,
+ dir_status ? dir_status->s.repos_relpath : NULL,
+ dir_status ? dir_status->s.repos_uuid : NULL,
db->statii, was_deleted, db->depth, scratch_pool));
if (dir_status && is_sendable_status(dir_status, eb->no_ignore,
eb->get_all))
SVN_ERR((eb->status_func)(eb->status_baton, db->local_abspath,
- dir_status, scratch_pool));
+ &dir_status->s, scratch_pool));
svn_hash_sets(pb->statii, db->local_abspath, NULL);
}
else if (! pb)
@@ -2207,13 +2227,12 @@ close_directory(void *dir_baton,
target, we should only report the target. */
if (*eb->target_basename)
{
- const svn_wc_status3_t *tgt_status;
+ const svn_wc__internal_status_t *tgt_status;
tgt_status = svn_hash_gets(db->statii, eb->target_abspath);
if (tgt_status)
{
- if (tgt_status->versioned
- && tgt_status->kind == svn_node_dir)
+ if (tgt_status->has_descendants)
{
SVN_ERR(get_dir_status(&eb->wb,
eb->target_abspath, TRUE,
@@ -2228,7 +2247,7 @@ close_directory(void *dir_baton,
}
if (is_sendable_status(tgt_status, eb->no_ignore, eb->get_all))
SVN_ERR((eb->status_func)(eb->status_baton, eb->target_abspath,
- tgt_status, scratch_pool));
+ &tgt_status->s, scratch_pool));
}
}
else
@@ -2237,15 +2256,15 @@ close_directory(void *dir_baton,
Note that our directory couldn't have been deleted,
because it is the root of the edit drive. */
SVN_ERR(handle_statii(eb,
- eb->anchor_status->repos_root_url,
- eb->anchor_status->repos_relpath,
- eb->anchor_status->repos_uuid,
+ eb->anchor_status->s.repos_root_url,
+ eb->anchor_status->s.repos_relpath,
+ eb->anchor_status->s.repos_uuid,
db->statii, FALSE, eb->default_depth,
scratch_pool));
if (is_sendable_status(eb->anchor_status, eb->no_ignore,
eb->get_all))
SVN_ERR((eb->status_func)(eb->status_baton, db->local_abspath,
- eb->anchor_status, scratch_pool));
+ &eb->anchor_status->s, scratch_pool));
eb->anchor_status = NULL;
}
}
@@ -2666,7 +2685,7 @@ svn_wc__internal_walk_status(svn_wc__db_
}
if (info
- && info->kind == svn_node_dir
+ && info->has_descendants /* is dir, or was dir and has tc descendants */
&& info->status != svn_wc__db_status_not_present
&& info->status != svn_wc__db_status_excluded
&& info->status != svn_wc__db_status_server_excluded)
@@ -2786,7 +2805,7 @@ svn_wc_get_default_ignores(apr_array_hea
/* */
static svn_error_t *
-internal_status(svn_wc_status3_t **status,
+internal_status(svn_wc__internal_status_t **status,
svn_wc__db_t *db,
const char *local_abspath,
svn_boolean_t check_working_copy,
@@ -2893,17 +2912,21 @@ svn_wc_status3(svn_wc_status3_t **status
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
- return svn_error_trace(
- internal_status(status, wc_ctx->db, local_abspath,
- TRUE /* check_working_copy */,
- result_pool, scratch_pool));
+ svn_wc__internal_status_t *stat;
+ SVN_ERR(internal_status(&stat, wc_ctx->db, local_abspath,
+ TRUE /* check_working_copy */,
+ result_pool, scratch_pool));
+ *status = &stat->s;
+ return SVN_NO_ERROR;
}
svn_wc_status3_t *
svn_wc_dup_status3(const svn_wc_status3_t *orig_stat,
apr_pool_t *pool)
{
- svn_wc_status3_t *new_stat = apr_palloc(pool, sizeof(*new_stat));
+ /* Allocate slightly more room */
+ svn_wc__internal_status_t *new_istat = apr_palloc(pool, sizeof(*new_istat));
+ svn_wc_status3_t *new_stat = &new_istat->s;
/* Shallow copy all members. */
*new_stat = *orig_stat;
Modified: subversion/branches/fsx-1.10/subversion/libsvn_wc/translate.c
URL: http://svn.apache.org/viewvc/subversion/branches/fsx-1.10/subversion/libsvn_wc/translate.c?rev=1685464&r1=1685463&r2=1685464&view=diff
==============================================================================
--- subversion/branches/fsx-1.10/subversion/libsvn_wc/translate.c (original)
+++ subversion/branches/fsx-1.10/subversion/libsvn_wc/translate.c Sun Jun 14 20:58:10 2015
@@ -316,12 +316,15 @@ svn_wc__expand_keywords(apr_hash_t **key
db, local_abspath,
scratch_pool, scratch_pool));
- if (repos_relpath)
- url = svn_path_url_add_component2(repos_root_url, repos_relpath,
- scratch_pool);
- else
- SVN_ERR(svn_wc__db_read_url(&url, db, local_abspath, scratch_pool,
- scratch_pool));
+ /* Handle special statuses (e.g. added) */
+ if (!repos_relpath)
+ SVN_ERR(svn_wc__db_read_repos_info(NULL, &repos_relpath,
+ &repos_root_url, NULL,
+ db, local_abspath,
+ scratch_pool, scratch_pool));
+
+ url = svn_path_url_add_component2(repos_root_url, repos_relpath,
+ scratch_pool);
}
else
{
Modified: subversion/branches/fsx-1.10/subversion/libsvn_wc/tree_conflicts.c
URL: http://svn.apache.org/viewvc/subversion/branches/fsx-1.10/subversion/libsvn_wc/tree_conflicts.c?rev=1685464&r1=1685463&r2=1685464&view=diff
==============================================================================
--- subversion/branches/fsx-1.10/subversion/libsvn_wc/tree_conflicts.c (original)
+++ subversion/branches/fsx-1.10/subversion/libsvn_wc/tree_conflicts.c Sun Jun 14 20:58:10 2015
@@ -485,8 +485,10 @@ svn_wc__get_tree_conflict(const svn_wc_c
int i;
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
- SVN_ERR(svn_wc__read_conflicts(&conflicts,
- wc_ctx->db, local_abspath, FALSE,
+ SVN_ERR(svn_wc__read_conflicts(&conflicts, NULL,
+ wc_ctx->db, local_abspath,
+ FALSE /* temp files */,
+ TRUE /* only tree conflicts */,
scratch_pool, scratch_pool));
if (!conflicts || conflicts->nelts == 0)
Modified: subversion/branches/fsx-1.10/subversion/libsvn_wc/update_editor.c
URL: http://svn.apache.org/viewvc/subversion/branches/fsx-1.10/subversion/libsvn_wc/update_editor.c?rev=1685464&r1=1685463&r2=1685464&view=diff
==============================================================================
--- subversion/branches/fsx-1.10/subversion/libsvn_wc/update_editor.c (original)
+++ subversion/branches/fsx-1.10/subversion/libsvn_wc/update_editor.c Sun Jun 14 20:58:10 2015
@@ -825,10 +825,11 @@ complete_conflict(svn_skel_t *conflict,
const char *new_repos_relpath,
svn_node_kind_t local_kind,
svn_node_kind_t target_kind,
+ const svn_skel_t *delete_conflict,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
- svn_wc_conflict_version_t *original_version;
+ const svn_wc_conflict_version_t *original_version = NULL;
svn_wc_conflict_version_t *target_version;
svn_boolean_t is_complete;
@@ -849,8 +850,21 @@ complete_conflict(svn_skel_t *conflict,
old_revision,
local_kind,
result_pool);
- else
- original_version = NULL;
+ else if (delete_conflict)
+ {
+ const apr_array_header_t *locations;
+
+ SVN_ERR(svn_wc__conflict_read_info(NULL, &locations, NULL, NULL, NULL,
+ eb->db, local_abspath,
+ delete_conflict,
+ scratch_pool, scratch_pool));
+
+ if (locations)
+ {
+ original_version = APR_ARRAY_IDX(locations, 0,
+ const svn_wc_conflict_version_t *);
+ }
+ }
target_version = svn_wc_conflict_version_create2(eb->repos_root,
eb->repos_uuid,
@@ -896,6 +910,7 @@ mark_directory_edited(struct dir_baton *
db->old_repos_relpath, db->old_revision,
db->new_repos_relpath,
svn_node_dir, svn_node_dir,
+ NULL,
db->pool, scratch_pool));
SVN_ERR(svn_wc__db_op_mark_conflict(db->edit_baton->db,
db->local_abspath,
@@ -930,6 +945,7 @@ mark_file_edited(struct file_baton *fb,
fb->local_abspath, fb->old_repos_relpath,
fb->old_revision, fb->new_repos_relpath,
svn_node_file, svn_node_file,
+ NULL,
fb->pool, scratch_pool));
SVN_ERR(svn_wc__db_op_mark_conflict(fb->edit_baton->db,
@@ -1249,7 +1265,7 @@ open_root(void *edit_baton,
db->old_revision,
db->new_repos_relpath,
svn_node_dir, svn_node_dir,
- pool, pool));
+ NULL, pool, pool));
SVN_ERR(svn_wc__db_op_mark_conflict(eb->db,
move_src_root_abspath,
tree_conflict,
@@ -1294,99 +1310,6 @@ open_root(void *edit_baton,
/* ===================================================================== */
/* Checking for local modifications. */
-/* A baton for use with modcheck_found_entry(). */
-typedef struct modcheck_baton_t {
- svn_wc__db_t *db; /* wc_db to access nodes */
- svn_boolean_t found_mod; /* whether a modification has been found */
- svn_boolean_t found_not_delete; /* Found a not-delete modification */
-} modcheck_baton_t;
-
-/* An implementation of svn_wc_status_func4_t. */
-static svn_error_t *
-modcheck_callback(void *baton,
- const char *local_abspath,
- const svn_wc_status3_t *status,
- apr_pool_t *scratch_pool)
-{
- modcheck_baton_t *mb = baton;
-
- switch (status->node_status)
- {
- case svn_wc_status_normal:
- case svn_wc_status_incomplete:
- case svn_wc_status_ignored:
- case svn_wc_status_none:
- case svn_wc_status_unversioned:
- case svn_wc_status_external:
- break;
-
- case svn_wc_status_deleted:
- mb->found_mod = TRUE;
- break;
-
- case svn_wc_status_missing:
- case svn_wc_status_obstructed:
- mb->found_mod = TRUE;
- mb->found_not_delete = TRUE;
- /* Exit from the status walker: We know what we want to know */
- return svn_error_create(SVN_ERR_CEASE_INVOCATION, NULL, NULL);
-
- default:
- case svn_wc_status_added:
- case svn_wc_status_replaced:
- case svn_wc_status_modified:
- mb->found_mod = TRUE;
- mb->found_not_delete = TRUE;
- /* Exit from the status walker: We know what we want to know */
- return svn_error_create(SVN_ERR_CEASE_INVOCATION, NULL, NULL);
- }
-
- return SVN_NO_ERROR;
-}
-
-
-/* Set *MODIFIED to true iff there are any local modifications within the
- * tree rooted at LOCAL_ABSPATH, using DB. If *MODIFIED
- * is set to true and all the local modifications were deletes then set
- * *ALL_EDITS_ARE_DELETES to true, set it to false otherwise. LOCAL_ABSPATH
- * may be a file or a directory. */
-svn_error_t *
-svn_wc__node_has_local_mods(svn_boolean_t *modified,
- svn_boolean_t *all_edits_are_deletes,
- svn_wc__db_t *db,
- const char *local_abspath,
- svn_cancel_func_t cancel_func,
- void *cancel_baton,
- apr_pool_t *scratch_pool)
-{
- modcheck_baton_t modcheck_baton = { NULL, FALSE, FALSE };
- svn_error_t *err;
-
- modcheck_baton.db = db;
-
- /* Walk the WC tree for status with depth infinity, looking for any local
- * modifications. If it's a "sparse" directory, that's OK: there can be
- * no local mods in the pieces that aren't present in the WC. */
-
- err = svn_wc__internal_walk_status(db, local_abspath,
- svn_depth_infinity,
- FALSE, FALSE, FALSE, NULL,
- modcheck_callback, &modcheck_baton,
- cancel_func, cancel_baton,
- scratch_pool);
-
- if (err && err->apr_err == SVN_ERR_CEASE_INVOCATION)
- svn_error_clear(err);
- else
- SVN_ERR(err);
-
- *modified = modcheck_baton.found_mod;
- *all_edits_are_deletes = (modcheck_baton.found_mod
- && !modcheck_baton.found_not_delete);
-
- return SVN_NO_ERROR;
-}
-
/* Indicates an unset svn_wc_conflict_reason_t. */
#define SVN_WC_CONFLICT_REASON_NONE (svn_wc_conflict_reason_t)(-1)
@@ -1421,7 +1344,6 @@ check_tree_conflict(svn_skel_t **pconfli
{
svn_wc_conflict_reason_t reason = SVN_WC_CONFLICT_REASON_NONE;
svn_boolean_t modified = FALSE;
- svn_boolean_t all_mods_are_deletes = FALSE;
const char *move_src_op_root_abspath = NULL;
*pconflict = NULL;
@@ -1460,15 +1382,15 @@ check_tree_conflict(svn_skel_t **pconfli
}
else
{
- /* The node is locally replaced but could also be moved-away. */
- SVN_ERR(svn_wc__db_base_moved_to(NULL, NULL, NULL,
- &move_src_op_root_abspath,
- eb->db, local_abspath,
- scratch_pool, scratch_pool));
- if (move_src_op_root_abspath)
- reason = svn_wc_conflict_reason_moved_away;
- else
- reason = svn_wc_conflict_reason_replaced;
+ /* The node is locally replaced but could also be moved-away,
+ but we can't report that it is moved away and replaced.
+
+ And we wouldn't be able to store that each of a dozen
+ descendants was moved to other locations...
+
+ Replaced is what actually happened... */
+
+ reason = svn_wc_conflict_reason_replaced;
}
break;
@@ -1531,14 +1453,14 @@ check_tree_conflict(svn_skel_t **pconfli
* not visit the subdirectories of a directory that it wants to delete.
* Therefore, we need to start a separate crawl here. */
- SVN_ERR(svn_wc__node_has_local_mods(&modified, &all_mods_are_deletes,
- eb->db, local_abspath,
+ SVN_ERR(svn_wc__node_has_local_mods(&modified, NULL,
+ eb->db, local_abspath, FALSE,
eb->cancel_func, eb->cancel_baton,
scratch_pool));
if (modified)
{
- if (all_mods_are_deletes)
+ if (working_status == svn_wc__db_status_deleted)
reason = svn_wc_conflict_reason_deleted;
else
reason = svn_wc_conflict_reason_edited;
@@ -1696,8 +1618,6 @@ delete_entry(const char *path,
apr_pool_t *scratch_pool;
svn_boolean_t deleting_target;
svn_boolean_t deleting_switched;
- svn_boolean_t keep_as_working = FALSE;
- svn_boolean_t queue_deletes = TRUE;
if (pb->skip_this)
return SVN_NO_ERROR;
@@ -1790,11 +1710,9 @@ delete_entry(const char *path,
|| base_status == svn_wc__db_status_excluded
|| base_status == svn_wc__db_status_server_excluded)
{
- SVN_ERR(svn_wc__db_base_remove(eb->db, local_abspath,
- FALSE /* keep_as_working */,
- FALSE /* queue_deletes */,
- FALSE /* remove_locks */,
- SVN_INVALID_REVNUM /* not_present_rev */,
+ SVN_ERR(svn_wc__db_base_remove(eb->db, local_abspath, TRUE,
+ deleting_target, FALSE,
+ *eb->target_revision,
NULL, NULL,
scratch_pool));
@@ -1821,12 +1739,9 @@ delete_entry(const char *path,
svn_wc_conflict_action_delete,
pb->pool, scratch_pool));
}
- else
- queue_deletes = FALSE; /* There is no in-wc representation */
if (tree_conflict != NULL)
{
- svn_wc_conflict_reason_t reason;
/* When we raise a tree conflict on a node, we don't want to mark the
* node as skipped, to allow a replacement to continue doing at least
* a bit of its work (possibly adding a not present node, for the
@@ -1837,37 +1752,8 @@ delete_entry(const char *path,
svn_hash_sets(pb->deletion_conflicts, apr_pstrdup(pb->pool, base),
tree_conflict);
- SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, NULL, NULL,
- eb->db, local_abspath,
- tree_conflict,
- scratch_pool, scratch_pool));
-
- if (reason == svn_wc_conflict_reason_edited
- || reason == svn_wc_conflict_reason_obstructed)
- {
- /* The item exists locally and has some sort of local mod.
- * It no longer exists in the repository at its target URL@REV.
- *
- * To prepare the "accept mine" resolution for the tree conflict,
- * we must schedule the existing content for re-addition as a copy
- * of what it was, but with its local modifications preserved. */
- keep_as_working = TRUE;
-
- /* Fall through to remove the BASE_NODEs properly, with potentially
- keeping a not-present marker */
- }
- else if (reason == svn_wc_conflict_reason_deleted
- || reason == svn_wc_conflict_reason_moved_away
- || reason == svn_wc_conflict_reason_replaced)
- {
- /* The item does not exist locally because it was already shadowed.
- * We must complete the deletion, leaving the tree conflict info
- * as the only difference from a normal deletion. */
-
- /* Fall through to the normal "delete" code path. */
- }
- else
- SVN_ERR_MALFUNCTION(); /* other reasons are not expected here */
+ /* Whatever the kind of conflict, we can just clear BASE
+ by turning whatever is there into a copy */
}
/* Calculate the repository-relative path of the entry which was
@@ -1878,10 +1764,7 @@ delete_entry(const char *path,
scratch_pool));
SVN_ERR(complete_conflict(tree_conflict, eb, local_abspath, repos_relpath,
old_revision, deleted_repos_relpath,
- (kind == svn_node_dir)
- ? svn_node_dir
- : svn_node_file,
- svn_node_none,
+ kind, svn_node_none, NULL,
pb->pool, scratch_pool));
/* Issue a wq operation to delete the BASE_NODE data and to delete actual
@@ -1896,7 +1779,8 @@ delete_entry(const char *path,
{
/* Delete, and do not leave a not-present node. */
SVN_ERR(svn_wc__db_base_remove(eb->db, local_abspath,
- keep_as_working, queue_deletes, FALSE,
+ (tree_conflict != NULL),
+ FALSE, FALSE,
SVN_INVALID_REVNUM /* not_present_rev */,
tree_conflict, NULL,
scratch_pool));
@@ -1905,7 +1789,8 @@ delete_entry(const char *path,
{
/* Delete, leaving a not-present node. */
SVN_ERR(svn_wc__db_base_remove(eb->db, local_abspath,
- keep_as_working, queue_deletes, FALSE,
+ (tree_conflict != NULL),
+ TRUE, FALSE,
*eb->target_revision,
tree_conflict, NULL,
scratch_pool));
@@ -1927,6 +1812,7 @@ delete_entry(const char *path,
{
if (eb->conflict_func)
SVN_ERR(svn_wc__conflict_invoke_resolver(eb->db, local_abspath,
+ kind,
tree_conflict,
NULL /* merge_options */,
eb->conflict_func,
@@ -1934,23 +1820,17 @@ delete_entry(const char *path,
eb->cancel_func,
eb->cancel_baton,
scratch_pool));
- do_notification(eb, local_abspath, svn_node_unknown,
+ do_notification(eb, local_abspath, kind,
svn_wc_notify_tree_conflict, scratch_pool);
}
else
{
svn_wc_notify_action_t action = svn_wc_notify_update_delete;
- svn_node_kind_t node_kind;
if (pb->shadowed || pb->edit_obstructed)
action = svn_wc_notify_update_shadowed_delete;
- if (kind == svn_node_dir)
- node_kind = svn_node_dir;
- else
- node_kind = svn_node_file;
-
- do_notification(eb, local_abspath, node_kind, action, scratch_pool);
+ do_notification(eb, local_abspath, kind, action, scratch_pool);
}
svn_pool_destroy(scratch_pool);
@@ -2056,9 +1936,15 @@ add_directory(const char *path,
SVN_ERR_ASSERT(conflicted);
versioned_locally_and_present = FALSE; /* Tree conflict ACTUAL-only node */
}
- else if (status == svn_wc__db_status_normal)
+ else if (status == svn_wc__db_status_normal
+ || status == svn_wc__db_status_incomplete)
{
- if (wc_kind == svn_node_dir)
+ svn_boolean_t root;
+
+ SVN_ERR(svn_wc__db_is_wcroot(&root, eb->db, db->local_abspath,
+ scratch_pool));
+
+ if (root)
{
/* !! We found the root of a working copy obstructing the wc !!
@@ -2070,9 +1956,16 @@ add_directory(const char *path,
resolved. Note that svn_wc__db_base_add_not_present_node()
explicitly adds the node into the parent's node database. */
- svn_hash_sets(pb->not_present_nodes, apr_pstrdup(pb->pool, db->name),
+ svn_hash_sets(pb->not_present_nodes,
+ apr_pstrdup(pb->pool, db->name),
svn_node_kind_to_word(svn_node_dir));
}
+ else if (wc_kind == svn_node_dir)
+ {
+ /* We have an editor violation. Github sometimes does this
+ in its subversion compatibility code, when changing the
+ depth of a working copy, or on updates from incomplete */
+ }
else
{
/* We found a file external occupating the place we need in BASE.
@@ -2117,7 +2010,7 @@ add_directory(const char *path,
replacement. Let's install a better tree conflict. */
SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, NULL,
- &move_src_op_root_abspath,
+ &move_src_op_root_abspath,
eb->db,
db->local_abspath,
tree_conflict,
@@ -2264,8 +2157,11 @@ add_directory(const char *path,
SVN_ERR(complete_conflict(tree_conflict, eb, db->local_abspath,
db->old_repos_relpath, db->old_revision,
db->new_repos_relpath,
- wc_kind,
- svn_node_dir,
+ wc_kind, svn_node_dir,
+ pb->deletion_conflicts
+ ? svn_hash_gets(pb->deletion_conflicts,
+ db->name)
+ : NULL,
db->pool, scratch_pool));
SVN_ERR(svn_wc__db_base_add_incomplete_directory(
@@ -2858,6 +2754,12 @@ close_directory(void *dir_baton,
db->old_revision,
db->new_repos_relpath,
svn_node_dir, svn_node_dir,
+ (db->parent_baton
+ && db->parent_baton->deletion_conflicts)
+ ? svn_hash_gets(
+ db->parent_baton->deletion_conflicts,
+ db->name)
+ : NULL,
db->pool, scratch_pool));
SVN_ERR(svn_wc__conflict_create_markers(&work_item,
@@ -2914,6 +2816,7 @@ close_directory(void *dir_baton,
if (conflict_skel && eb->conflict_func)
SVN_ERR(svn_wc__conflict_invoke_resolver(eb->db, db->local_abspath,
+ svn_node_dir,
conflict_skel,
NULL /* merge_options */,
eb->conflict_func,
@@ -3081,9 +2984,8 @@ absent_node(const char *path,
if (tree_conflict)
SVN_ERR(complete_conflict(tree_conflict, eb, local_abspath,
NULL, SVN_INVALID_REVNUM, repos_relpath,
- kind, svn_node_unknown,
+ kind, svn_node_unknown, NULL,
scratch_pool, scratch_pool));
-
/* Insert an excluded node below the parent node to note that this child
is absent. (This puts it in the parent db if the child is obstructed) */
@@ -3100,6 +3002,7 @@ absent_node(const char *path,
{
if (eb->conflict_func)
SVN_ERR(svn_wc__conflict_invoke_resolver(eb->db, local_abspath,
+ kind,
tree_conflict,
NULL /* merge_options */,
eb->conflict_func,
@@ -3220,20 +3123,35 @@ add_file(const char *path,
SVN_ERR_ASSERT(conflicted);
versioned_locally_and_present = FALSE; /* Tree conflict ACTUAL-only node */
}
- else if (status == svn_wc__db_status_normal)
+ else if (status == svn_wc__db_status_normal
+ || status == svn_wc__db_status_incomplete)
{
- if (wc_kind == svn_node_dir)
+ svn_boolean_t root;
+
+ SVN_ERR(svn_wc__db_is_wcroot(&root, eb->db, fb->local_abspath,
+ scratch_pool));
+
+ if (root)
{
/* !! We found the root of a working copy obstructing the wc !!
If the directory would be part of our own working copy then
- we wouldn't have been called as an add_file().
+ we wouldn't have been called as an add_directory().
The only thing we can do is add a not-present node, to allow
a future update to bring in the new files when the problem is
- resolved. */
- svn_hash_sets(pb->not_present_nodes, apr_pstrdup(pb->pool, fb->name),
- svn_node_kind_to_word(svn_node_file));
+ resolved. Note that svn_wc__db_base_add_not_present_node()
+ explicitly adds the node into the parent's node database. */
+
+ svn_hash_sets(pb->not_present_nodes,
+ apr_pstrdup(pb->pool, fb->name),
+ svn_node_kind_to_word(svn_node_dir));
+ }
+ else if (wc_kind == svn_node_dir)
+ {
+ /* We have an editor violation. Github sometimes does this
+ in its subversion compatibility code, when changing the
+ depth of a working copy, or on updates from incomplete */
}
else
{
@@ -3282,7 +3200,7 @@ add_file(const char *path,
replacement. Let's install a better tree conflict. */
SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, NULL,
- &move_src_op_root_abspath,
+ &move_src_op_root_abspath,
eb->db,
fb->local_abspath,
tree_conflict,
@@ -3445,8 +3363,11 @@ add_file(const char *path,
fb->old_repos_relpath,
fb->old_revision,
fb->new_repos_relpath,
- wc_kind,
- svn_node_file,
+ wc_kind, svn_node_file,
+ pb->deletion_conflicts
+ ? svn_hash_gets(pb->deletion_conflicts,
+ fb->name)
+ : NULL,
fb->pool, scratch_pool));
SVN_ERR(svn_wc__db_op_mark_conflict(eb->db,
@@ -3851,7 +3772,7 @@ change_file_prop(void *file_baton,
fb->local_abspath, fb->old_repos_relpath,
fb->old_revision, fb->new_repos_relpath,
svn_node_file, svn_node_file,
- fb->pool, scratch_pool));
+ NULL, fb->pool, scratch_pool));
/* Create a copy of the existing (pre update) BASE node in WORKING,
mark a tree conflict and handle the rest of the update as
@@ -4564,6 +4485,11 @@ close_file(void *file_baton,
fb->old_revision,
fb->new_repos_relpath,
svn_node_file, svn_node_file,
+ fb->dir_baton->deletion_conflicts
+ ? svn_hash_gets(
+ fb->dir_baton->deletion_conflicts,
+ fb->name)
+ : NULL,
fb->pool, scratch_pool));
SVN_ERR(svn_wc__conflict_create_markers(&work_item,
@@ -4616,6 +4542,7 @@ close_file(void *file_baton,
if (conflict_skel && eb->conflict_func)
SVN_ERR(svn_wc__conflict_invoke_resolver(eb->db, fb->local_abspath,
+ svn_node_file,
conflict_skel,
NULL /* merge_options */,
eb->conflict_func,
@@ -4734,7 +4661,7 @@ update_keywords_after_switch_cb(void *ba
install_from = NULL;
record_fileinfo = TRUE;
}
-
+
SVN_ERR(svn_wc__wq_build_file_install(&work_items, eb->db, local_abspath,
install_from,
eb->use_commit_times,
@@ -4844,9 +4771,7 @@ close_edit(void *edit_baton,
If so, we should get rid of this excluded node now. */
SVN_ERR(svn_wc__db_base_remove(eb->db, eb->target_abspath,
- FALSE /* keep_as_working */,
- FALSE /* queue_deletes */,
- FALSE /* remove_locks */,
+ TRUE, FALSE, FALSE,
SVN_INVALID_REVNUM,
NULL, NULL, scratch_pool));
}
@@ -4858,7 +4783,7 @@ close_edit(void *edit_baton,
if (eb->switch_repos_relpath)
{
svn_depth_t depth;
-
+
if (eb->requested_depth > svn_depth_empty)
depth = eb->requested_depth;
else
@@ -5663,8 +5588,8 @@ svn_wc__complete_directory_add(svn_wc_co
original_repos_relpath, original_root_url,
original_uuid, original_revision,
NULL /* children */,
- FALSE /* is_move */,
svn_depth_infinity,
+ FALSE /* is_move */,
NULL /* conflict */,
NULL /* work_items */,
scratch_pool));
Modified: subversion/branches/fsx-1.10/subversion/libsvn_wc/upgrade.c
URL: http://svn.apache.org/viewvc/subversion/branches/fsx-1.10/subversion/libsvn_wc/upgrade.c?rev=1685464&r1=1685463&r2=1685464&view=diff
==============================================================================
--- subversion/branches/fsx-1.10/subversion/libsvn_wc/upgrade.c (original)
+++ subversion/branches/fsx-1.10/subversion/libsvn_wc/upgrade.c Sun Jun 14 20:58:10 2015
@@ -37,6 +37,7 @@
#include "tree_conflicts.h"
#include "wc-queries.h" /* for STMT_* */
#include "workqueue.h"
+#include "token-map.h"
#include "svn_private_config.h"
#include "private/svn_wc_private.h"
@@ -534,7 +535,7 @@ svn_wc__wipe_postupgrade(const char *dir
int i;
if (cancel_func)
- SVN_ERR((*cancel_func)(cancel_baton));
+ SVN_ERR(cancel_func(cancel_baton));
err = get_versioned_subdirs(&subdirs, &delete_dir, dir_abspath, TRUE,
scratch_pool, iterpool);
@@ -824,6 +825,190 @@ migrate_tree_conflict_data(svn_sqlite__d
return SVN_NO_ERROR;
}
+/* ### need much more docco
+
+ ### this function should be called within a sqlite transaction. it makes
+ ### assumptions around this fact.
+
+ Apply the various sets of properties to the database nodes based on
+ their existence/presence, the current state of the node, and the original
+ format of the working copy which provided these property sets.
+*/
+static svn_error_t *
+upgrade_apply_props(svn_sqlite__db_t *sdb,
+ const char *dir_abspath,
+ const char *local_relpath,
+ apr_hash_t *base_props,
+ apr_hash_t *revert_props,
+ apr_hash_t *working_props,
+ int original_format,
+ apr_int64_t wc_id,
+ apr_pool_t *scratch_pool)
+{
+ svn_sqlite__stmt_t *stmt;
+ svn_boolean_t have_row;
+ int top_op_depth = -1;
+ int below_op_depth = -1;
+ svn_wc__db_status_t top_presence;
+ svn_wc__db_status_t below_presence;
+ int affected_rows;
+
+ /* ### working_props: use set_props_txn.
+ ### if working_props == NULL, then skip. what if they equal the
+ ### pristine props? we should probably do the compare here.
+ ###
+ ### base props go into WORKING_NODE if avail, otherwise BASE.
+ ###
+ ### revert only goes into BASE. (and WORKING better be there!)
+
+ Prior to 1.4.0 (ORIGINAL_FORMAT < 8), REVERT_PROPS did not exist. If a
+ file was deleted, then a copy (potentially with props) was disallowed
+ and could not replace the deletion. An addition *could* be performed,
+ but that would never bring its own props.
+
+ 1.4.0 through 1.4.5 created the concept of REVERT_PROPS, but had a
+ bug in svn_wc_add_repos_file2() whereby a copy-with-props did NOT
+ construct a REVERT_PROPS if the target had no props. Thus, reverting
+ the delete/copy would see no REVERT_PROPS to restore, leaving the
+ props from the copy source intact, and appearing as if they are (now)
+ the base props for the previously-deleted file. (wc corruption)
+
+ 1.4.6 ensured that an empty REVERT_PROPS would be established at all
+ times. See issue 2530, and r861670 as starting points.
+
+ We will use ORIGINAL_FORMAT and SVN_WC__NO_REVERT_FILES to determine
+ the handling of our inputs, relative to the state of this node.
+ */
+
+ SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_SELECT_NODE_INFO));
+ SVN_ERR(svn_sqlite__bindf(stmt, "is", wc_id, local_relpath));
+ SVN_ERR(svn_sqlite__step(&have_row, stmt));
+ if (have_row)
+ {
+ top_op_depth = svn_sqlite__column_int(stmt, 0);
+ top_presence = svn_sqlite__column_token(stmt, 3, presence_map);
+ SVN_ERR(svn_sqlite__step(&have_row, stmt));
+ if (have_row)
+ {
+ below_presence = svn_sqlite__column_token(stmt, 3, presence_map);
+
+ /* There might be an intermediate layer on mixed-revision copies,
+ or when BASE is shadowed */
+ if (below_presence == svn_wc__db_status_not_present
+ || below_presence == svn_wc__db_status_deleted)
+ SVN_ERR(svn_sqlite__step(&have_row, stmt));
+
+ if (have_row)
+ {
+ below_presence = svn_sqlite__column_token(stmt, 3, presence_map);
+ below_op_depth = svn_sqlite__column_int(stmt, 0);
+ }
+ }
+ }
+ SVN_ERR(svn_sqlite__reset(stmt));
+
+ /* Detect the buggy scenario described above. We cannot upgrade this
+ working copy if we have no idea where BASE_PROPS should go. */
+ if (original_format > SVN_WC__NO_REVERT_FILES
+ && revert_props == NULL
+ && top_op_depth != -1
+ && top_presence == svn_wc__db_status_normal
+ && below_op_depth != -1
+ && below_presence != svn_wc__db_status_not_present)
+ {
+ /* There should be REVERT_PROPS, so it appears that we just ran into
+ the described bug. Sigh. */
+ return svn_error_createf(SVN_ERR_WC_CORRUPT, NULL,
+ _("The properties of '%s' are in an "
+ "indeterminate state and cannot be "
+ "upgraded. See issue #2530."),
+ svn_dirent_local_style(
+ svn_dirent_join(dir_abspath, local_relpath,
+ scratch_pool), scratch_pool));
+ }
+
+ /* Need at least one row, or two rows if there are revert props */
+ if (top_op_depth == -1
+ || (below_op_depth == -1 && revert_props))
+ return svn_error_createf(SVN_ERR_WC_CORRUPT, NULL,
+ _("Insufficient NODES rows for '%s'"),
+ svn_dirent_local_style(
+ svn_dirent_join(dir_abspath, local_relpath,
+ scratch_pool), scratch_pool));
+
+ /* one row, base props only: upper row gets base props
+ two rows, base props only: lower row gets base props
+ two rows, revert props only: lower row gets revert props
+ two rows, base and revert props: upper row gets base, lower gets revert */
+
+
+ if (revert_props || below_op_depth == -1)
+ {
+ SVN_ERR(svn_sqlite__get_statement(&stmt, sdb,
+ STMT_UPDATE_NODE_PROPS));
+ SVN_ERR(svn_sqlite__bindf(stmt, "isd",
+ wc_id, local_relpath, top_op_depth));
+ SVN_ERR(svn_sqlite__bind_properties(stmt, 4, base_props, scratch_pool));
+ SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
+
+ SVN_ERR_ASSERT(affected_rows == 1);
+ }
+
+ if (below_op_depth != -1)
+ {
+ apr_hash_t *props = revert_props ? revert_props : base_props;
+
+ SVN_ERR(svn_sqlite__get_statement(&stmt, sdb,
+ STMT_UPDATE_NODE_PROPS));
+ SVN_ERR(svn_sqlite__bindf(stmt, "isd",
+ wc_id, local_relpath, below_op_depth));
+ SVN_ERR(svn_sqlite__bind_properties(stmt, 4, props, scratch_pool));
+ SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
+
+ SVN_ERR_ASSERT(affected_rows == 1);
+ }
+
+ /* If there are WORKING_PROPS, then they always go into ACTUAL_NODE. */
+ if (working_props != NULL
+ && base_props != NULL)
+ {
+ apr_array_header_t *diffs;
+
+ SVN_ERR(svn_prop_diffs(&diffs, working_props, base_props, scratch_pool));
+
+ if (diffs->nelts == 0)
+ working_props = NULL; /* No differences */
+ }
+
+ if (working_props != NULL)
+ {
+ SVN_ERR(svn_sqlite__get_statement(&stmt, sdb,
+ STMT_UPDATE_ACTUAL_PROPS));
+ SVN_ERR(svn_sqlite__bindf(stmt, "is", wc_id, local_relpath));
+ SVN_ERR(svn_sqlite__bind_properties(stmt, 3, working_props,
+ scratch_pool));
+ SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
+
+ if (affected_rows == 0)
+ {
+ /* We have to insert a row in ACTUAL */
+
+ SVN_ERR(svn_sqlite__get_statement(&stmt, sdb,
+ STMT_INSERT_ACTUAL_PROPS));
+ SVN_ERR(svn_sqlite__bindf(stmt, "is", wc_id, local_relpath));
+ if (*local_relpath != '\0')
+ SVN_ERR(svn_sqlite__bind_text(stmt, 3,
+ svn_relpath_dirname(local_relpath,
+ scratch_pool)));
+ SVN_ERR(svn_sqlite__bind_properties(stmt, 4, working_props,
+ scratch_pool));
+ return svn_error_trace(svn_sqlite__step_done(stmt));
+ }
+ }
+
+ return SVN_NO_ERROR;
+}
+
struct bump_baton {
const char *wcroot_abspath;
@@ -899,7 +1084,7 @@ migrate_node_props(const char *dir_abspa
SVN_ERR(read_propfile(&working_props, working_abspath,
scratch_pool, scratch_pool));
- return svn_error_trace(svn_wc__db_upgrade_apply_props(
+ return svn_error_trace(upgrade_apply_props(
sdb, new_wcroot_abspath,
svn_relpath_join(dir_relpath, name, scratch_pool),
base_props, revert_props, working_props,
@@ -1671,6 +1856,43 @@ bump_to_31(void *baton,
return SVN_NO_ERROR;
}
+static svn_error_t *
+upgrade_apply_dav_cache(svn_sqlite__db_t *sdb,
+ const char *dir_relpath,
+ apr_int64_t wc_id,
+ apr_hash_t *cache_values,
+ apr_pool_t *scratch_pool)
+{
+ apr_pool_t *iterpool = svn_pool_create(scratch_pool);
+ apr_hash_index_t *hi;
+ svn_sqlite__stmt_t *stmt;
+
+ SVN_ERR(svn_sqlite__get_statement(&stmt, sdb,
+ STMT_UPDATE_BASE_NODE_DAV_CACHE));
+
+ /* Iterate over all the wcprops, writing each one to the wc_db. */
+ for (hi = apr_hash_first(scratch_pool, cache_values);
+ hi;
+ hi = apr_hash_next(hi))
+ {
+ const char *name = apr_hash_this_key(hi);
+ apr_hash_t *props = apr_hash_this_val(hi);
+ const char *local_relpath;
+
+ svn_pool_clear(iterpool);
+
+ local_relpath = svn_relpath_join(dir_relpath, name, iterpool);
+
+ SVN_ERR(svn_sqlite__bindf(stmt, "is", wc_id, local_relpath));
+ SVN_ERR(svn_sqlite__bind_properties(stmt, 3, props, iterpool));
+ SVN_ERR(svn_sqlite__step_done(stmt));
+ }
+
+ svn_pool_destroy(iterpool);
+
+ return SVN_NO_ERROR;
+}
+
struct upgrade_data_t {
svn_sqlite__db_t *sdb;
@@ -1808,8 +2030,8 @@ upgrade_to_wcng(void **dir_baton,
SVN_ERR(read_wcprops(&all_wcprops, dir_abspath,
scratch_pool, scratch_pool));
- SVN_ERR(svn_wc__db_upgrade_apply_dav_cache(data->sdb, dir_relpath,
- all_wcprops, scratch_pool));
+ SVN_ERR(upgrade_apply_dav_cache(data->sdb, dir_relpath, wc_id,
+ all_wcprops, scratch_pool));
}
/* Upgrade all the properties (including "this dir").
@@ -2144,40 +2366,6 @@ is_old_wcroot(const char *local_abspath,
svn_dirent_local_style(parent_abspath, scratch_pool));
}
-/* Data for upgrade_working_copy_txn(). */
-typedef struct upgrade_working_copy_baton_t
-{
- svn_wc__db_t *db;
- const char *dir_abspath;
- svn_wc_upgrade_get_repos_info_t repos_info_func;
- void *repos_info_baton;
- apr_hash_t *repos_cache;
- const struct upgrade_data_t *data;
- svn_cancel_func_t cancel_func;
- void *cancel_baton;
- svn_wc_notify_func2_t notify_func;
- void *notify_baton;
- apr_pool_t *result_pool;
-} upgrade_working_copy_baton_t;
-
-
-/* Helper for svn_wc_upgrade. Implements svn_sqlite__transaction_callback_t */
-static svn_error_t *
-upgrade_working_copy_txn(void *baton,
- svn_sqlite__db_t *sdb,
- apr_pool_t *scratch_pool)
-{
- upgrade_working_copy_baton_t *b = baton;
-
- /* Upgrade the pre-wcng into a wcng in a temporary location. */
- return(upgrade_working_copy(NULL, b->db, b->dir_abspath,
- b->repos_info_func, b->repos_info_baton,
- b->repos_cache, b->data,
- b->cancel_func, b->cancel_baton,
- b->notify_func, b->notify_baton,
- b->result_pool, scratch_pool));
-}
-
svn_error_t *
svn_wc_upgrade(svn_wc_context_t *wc_ctx,
const char *local_abspath,
@@ -2197,7 +2385,6 @@ svn_wc_upgrade(svn_wc_context_t *wc_ctx,
svn_wc_entry_t *this_dir;
apr_hash_t *entries;
const char *root_adm_abspath;
- upgrade_working_copy_baton_t cb_baton;
svn_error_t *err;
int result_format;
svn_boolean_t bumped_format;
@@ -2295,22 +2482,14 @@ svn_wc_upgrade(svn_wc_context_t *wc_ctx,
SVN_ERR(svn_wc__db_wclock_obtain(db, data.root_abspath, 0, FALSE,
scratch_pool));
- cb_baton.db = db;
- cb_baton.dir_abspath = local_abspath;
- cb_baton.repos_info_func = repos_info_func;
- cb_baton.repos_info_baton = repos_info_baton;
- cb_baton.repos_cache = repos_cache;
- cb_baton.data = &data;
- cb_baton.cancel_func = cancel_func;
- cb_baton.cancel_baton = cancel_baton;
- cb_baton.notify_func = notify_func;
- cb_baton.notify_baton = notify_baton;
- cb_baton.result_pool = scratch_pool;
-
- SVN_ERR(svn_sqlite__with_lock(data.sdb,
- upgrade_working_copy_txn,
- &cb_baton,
- scratch_pool));
+ SVN_SQLITE__WITH_LOCK(
+ upgrade_working_copy(NULL, db, local_abspath,
+ repos_info_func, repos_info_baton,
+ repos_cache, &data,
+ cancel_func, cancel_baton,
+ notify_func, notify_baton,
+ scratch_pool, scratch_pool),
+ data.sdb);
/* A workqueue item to move the pristine dir into place */
pristine_from = svn_wc__adm_child(data.root_abspath, PRISTINE_STORAGE_RELPATH,
Modified: subversion/branches/fsx-1.10/subversion/libsvn_wc/wc-checks.sql
URL: http://svn.apache.org/viewvc/subversion/branches/fsx-1.10/subversion/libsvn_wc/wc-checks.sql?rev=1685464&r1=1685463&r2=1685464&view=diff
==============================================================================
--- subversion/branches/fsx-1.10/subversion/libsvn_wc/wc-checks.sql (original)
+++ subversion/branches/fsx-1.10/subversion/libsvn_wc/wc-checks.sql Sun Jun 14 20:58:10 2015
@@ -75,3 +75,217 @@ BEGIN
SELECT RAISE(FAIL, 'WC DB validity check 04 failed');
END;
+-- STMT_STATIC_VERIFY
+SELECT local_relpath, op_depth, 1, 'Invalid parent relpath set in NODES'
+FROM nodes n WHERE local_relpath != ''
+ AND (parent_relpath IS NULL
+ OR NOT IS_STRICT_DESCENDANT_OF(local_relpath, parent_relpath)
+ OR relpath_depth(local_relpath) != relpath_depth(parent_relpath)+1)
+
+UNION ALL
+
+SELECT local_relpath, -1, 2, 'Invalid parent relpath set in ACTUAL'
+FROM actual_node a WHERE local_relpath != ''
+ AND (parent_relpath IS NULL
+ OR NOT IS_STRICT_DESCENDANT_OF(local_relpath, parent_relpath)
+ OR relpath_depth(local_relpath) != relpath_depth(parent_relpath)+1)
+
+UNION ALL
+
+/* All ACTUAL nodes must have an equivalent NODE in NODES
+ or be only one level deep (delete-delete tc) */
+SELECT local_relpath, -1, 10, 'No ancestor in ACTUAL'
+FROM actual_node a WHERE local_relpath != ''
+ AND NOT EXISTS(SELECT 1 from nodes i
+ WHERE i.wc_id=a.wc_id
+ AND i.local_relpath=a.parent_relpath)
+ AND NOT EXISTS(SELECT 1 from nodes i
+ WHERE i.wc_id=a.wc_id
+ AND i.local_relpath=a.local_relpath)
+
+UNION ALL
+/* Verify if the ACTUAL data makes sense for the related node.
+ Only conflict data is valid if there is none */
+SELECT a.local_relpath, -1, 11, 'Bad or Unneeded actual data'
+FROM actual_node a
+LEFT JOIN nodes n on n.wc_id = a.wc_id AND n.local_relpath = a.local_relpath
+ AND n.op_depth = (SELECT MAX(op_depth) from nodes i
+ WHERE i.wc_id=a.wc_id AND i.local_relpath=a.local_relpath)
+WHERE (a.properties IS NOT NULL
+ AND (n.presence IS NULL
+ OR n.presence NOT IN (MAP_NORMAL, MAP_INCOMPLETE)))
+ OR (a.changelist IS NOT NULL AND (n.kind IS NOT NULL AND n.kind != MAP_FILE))
+ OR (a.conflict_data IS NULL AND a.properties IS NULL AND a.changelist IS NULL)
+ AND NOT EXISTS(SELECT 1 from nodes i
+ WHERE i.wc_id=a.wc_id
+ AND i.local_relpath=a.parent_relpath)
+
+UNION ALL
+
+/* A parent node must exist for every normal node except the root.
+ That node must exist at a lower or equal op-depth */
+SELECT local_relpath, op_depth, 20, 'No ancestor in NODES'
+FROM nodes n WHERE local_relpath != ''
+ AND file_external IS NULL
+ AND NOT EXISTS(SELECT 1 from nodes i
+ WHERE i.wc_id=n.wc_id
+ AND i.local_relpath=n.parent_relpath
+ AND i.op_depth <= n.op_depth)
+
+UNION ALL
+/* If a node is not present in the working copy (normal, add, copy) it doesn't
+ have revision details stored on this record */
+SELECT local_relpath, op_depth, 21, 'Unneeded node data'
+FROM nodes
+WHERE presence NOT IN (MAP_NORMAL, MAP_INCOMPLETE)
+AND (properties IS NOT NULL
+ OR checksum IS NOT NULL
+ OR depth IS NOT NULL
+ OR symlink_target IS NOT NULL
+ OR changed_revision IS NOT NULL
+ OR (changed_date IS NOT NULL AND changed_date != 0)
+ OR changed_author IS NOT NULL
+ OR translated_size IS NOT NULL
+ OR last_mod_time IS NOT NULL
+ OR dav_cache IS NOT NULL
+ OR file_external IS NOT NULL
+ OR inherited_props IS NOT NULL)
+
+UNION ALL
+/* base-deleted nodes don't have a repository location. They are just
+ shadowing without a replacement */
+SELECT local_relpath, op_depth, 22, 'Unneeded base-deleted node data'
+FROM nodes
+WHERE presence IN (MAP_BASE_DELETED)
+AND (repos_id IS NOT NULL
+ OR repos_path IS NOT NULL
+ OR revision IS NOT NULL)
+
+UNION ALL
+/* Verify if type specific data is set (or not set for wrong type) */
+SELECT local_relpath, op_depth, 23, 'Kind specific data invalid on normal'
+FROM nodes
+WHERE presence IN (MAP_NORMAL, MAP_INCOMPLETE)
+AND (kind IS NULL
+ OR (repos_path IS NULL
+ AND (properties IS NOT NULL
+ OR changed_revision IS NOT NULL
+ OR changed_author IS NOT NULL
+ OR (changed_date IS NOT NULL AND changed_date != 0)))
+ OR (CASE WHEN kind = MAP_FILE AND repos_path IS NOT NULL
+ THEN checksum IS NULL
+ ELSE checksum IS NOT NULL END)
+ OR (CASE WHEN kind = MAP_DIR THEN depth IS NULL
+ ELSE depth IS NOT NULL END)
+ OR (CASE WHEN kind = MAP_SYMLINK THEN symlink_target IS NULL
+ ELSE symlink_target IS NOT NULL END))
+
+UNION ALL
+/* Local-adds are always their own operation (read: they don't have
+ op-depth descendants, nor are op-depth descendants */
+SELECT local_relpath, op_depth, 24, 'Invalid op-depth for local add'
+FROM nodes
+WHERE presence IN (MAP_NORMAL, MAP_INCOMPLETE)
+ AND repos_path IS NULL
+ AND op_depth != relpath_depth(local_relpath)
+
+UNION ALL
+/* op-depth descendants are only valid if they have a direct parent
+ node at the same op-depth. Only certain types allow further
+ descendants */
+SELECT local_relpath, op_depth, 25, 'Node missing op-depth ancestor'
+FROM nodes n
+WHERE op_depth < relpath_depth(local_relpath)
+ AND file_external IS NULL
+ AND NOT EXISTS(SELECT 1 FROM nodes p
+ WHERE p.wc_id=n.wc_id AND p.local_relpath=n.parent_relpath
+ AND p.op_depth=n.op_depth
+ AND (p.presence IN (MAP_NORMAL, MAP_INCOMPLETE)
+ OR (p.presence IN (MAP_BASE_DELETED, MAP_NOT_PRESENT)
+ AND n.presence = MAP_BASE_DELETED)))
+
+UNION ALL
+/* Present op-depth descendants have the repository location implied by their
+ ancestor */
+SELECT n.local_relpath, n.op_depth, 26, 'Copied descendant mismatch'
+FROM nodes n
+JOIN nodes p
+ ON p.wc_id=n.wc_id AND p.local_relpath=n.parent_relpath
+ AND n.op_depth=p.op_depth
+WHERE n.op_depth > 0 AND n.presence IN (MAP_NORMAL, MAP_INCOMPLETE)
+ AND (n.repos_id != p.repos_id
+ OR n.repos_path !=
+ RELPATH_SKIP_JOIN(n.parent_relpath, p.repos_path, n.local_relpath)
+ OR n.revision != p.revision
+ OR p.kind != MAP_DIR
+ OR n.moved_here IS NOT p.moved_here)
+
+UNION ALL
+/* Only certain presence values are valid as op-root.
+ Note that the wc-root always has presence normal or incomplete */
+SELECT n.local_relpath, n.op_depth, 27, 'Invalid op-root presence'
+FROM nodes n
+WHERE n.op_depth = relpath_depth(local_relpath)
+ AND presence NOT IN (MAP_NORMAL, MAP_INCOMPLETE, MAP_BASE_DELETED)
+
+UNION ALL
+/* If a node is shadowed, all its present op-depth descendants
+ must be shadowed at the same op-depth as well */
+SELECT n.local_relpath, s.op_depth, 28, 'Incomplete shadowing'
+FROM nodes n
+JOIN nodes s ON s.wc_id=n.wc_id AND s.local_relpath=n.local_relpath
+ AND s.op_depth = relpath_depth(s.local_relpath)
+ AND s.op_depth = (SELECT MIN(op_depth) FROM nodes d
+ WHERE d.wc_id=s.wc_id AND d.local_relpath=s.local_relpath
+ AND d.op_depth > n.op_depth)
+WHERE n.presence IN (MAP_NORMAL, MAP_INCOMPLETE)
+ AND EXISTS(SELECT 1
+ FROM nodes dn
+ WHERE dn.wc_id=n.wc_id AND dn.op_depth=n.op_depth
+ AND dn.presence IN (MAP_NORMAL, MAP_INCOMPLETE)
+ AND IS_STRICT_DESCENDANT_OF(dn.local_relpath, n.local_relpath)
+ AND dn.file_external IS NULL
+ AND NOT EXISTS(SELECT 1
+ FROM nodes ds
+ WHERE ds.wc_id=n.wc_id AND ds.op_depth=s.op_depth
+ AND ds.local_relpath=dn.local_relpath))
+
+UNION ALL
+/* A base-delete is only valid if it directly deletes a present node */
+SELECT s.local_relpath, s.op_depth, 29, 'Invalid base-delete'
+FROM nodes s
+LEFT JOIN nodes n ON n.wc_id=s.wc_id AND n.local_relpath=s.local_relpath
+ AND n.op_depth = (SELECT MAX(op_depth) FROM nodes d
+ WHERE d.wc_id=s.wc_id AND d.local_relpath=s.local_relpath
+ AND d.op_depth < s.op_depth)
+WHERE s.presence = MAP_BASE_DELETED
+ AND (n.presence IS NULL
+ OR n.presence NOT IN (MAP_NORMAL, MAP_INCOMPLETE)
+ /*OR n.kind != s.kind*/)
+
+UNION ALL
+/* Moves are stored in the working layers, not in BASE */
+SELECT n.local_relpath, n.op_depth, 30, 'Invalid data for BASE'
+FROM nodes n
+WHERE n.op_depth = 0
+ AND (n.moved_to IS NOT NULL
+ OR n.moved_here IS NOT NULL)
+
+UNION ALL
+/* If moved_here is set on an op-root, there must be a proper moved_to */
+SELECT d.local_relpath, d.op_depth, 60, 'Moved here without origin'
+FROM nodes d
+WHERE d.op_depth = relpath_depth(d.local_relpath)
+ AND d.moved_here IS NOT NULL
+ AND NOT EXISTS(SELECT 1 FROM nodes s
+ WHERE s.wc_id = d.wc_id AND s.moved_to = d.local_relpath)
+
+UNION ALL
+/* If moved_to is set there should be an moved op root at the target */
+SELECT s.local_relpath, s.op_depth, 61, 'Moved to without target'
+FROM nodes s
+WHERE s.moved_to IS NOT NULL
+ AND NOT EXISTS(SELECT 1 FROM nodes d
+ WHERE d.wc_id = s.wc_id AND d.local_relpath = s.moved_to
+ AND d.op_depth = relpath_depth(d.local_relpath)
+ AND d.moved_here =1 AND d.repos_path IS NOT NULL)