Modified: subversion/branches/move-tracking-2/subversion/libsvn_wc/update_editor.c URL: http://svn.apache.org/viewvc/subversion/branches/move-tracking-2/subversion/libsvn_wc/update_editor.c?rev=1665166&r1=1665165&r2=1665166&view=diff ============================================================================== --- subversion/branches/move-tracking-2/subversion/libsvn_wc/update_editor.c (original) +++ subversion/branches/move-tracking-2/subversion/libsvn_wc/update_editor.c Mon Mar 9 09:53:06 2015 @@ -1937,7 +1937,12 @@ add_directory(const char *path, } else if (status == svn_wc__db_status_normal) { - 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 !! @@ -1949,9 +1954,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. @@ -3108,18 +3120,32 @@ add_file(const char *path, } else if (status == svn_wc__db_status_normal) { - 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 {
Modified: subversion/branches/move-tracking-2/subversion/libsvn_wc/wc-queries.sql URL: http://svn.apache.org/viewvc/subversion/branches/move-tracking-2/subversion/libsvn_wc/wc-queries.sql?rev=1665166&r1=1665165&r2=1665166&view=diff ============================================================================== --- subversion/branches/move-tracking-2/subversion/libsvn_wc/wc-queries.sql (original) +++ subversion/branches/move-tracking-2/subversion/libsvn_wc/wc-queries.sql Mon Mar 9 09:53:06 2015 @@ -46,7 +46,7 @@ SELECT op_depth, nodes.repos_id, nodes.r lock_token, lock_owner, lock_comment, lock_date FROM nodes LEFT OUTER JOIN lock ON nodes.repos_id = lock.repos_id - AND nodes.repos_path = lock.repos_relpath + AND nodes.repos_path = lock.repos_relpath AND nodes.op_depth=0 WHERE wc_id = ?1 AND local_relpath = ?2 ORDER BY op_depth DESC @@ -134,7 +134,7 @@ SELECT op_depth, nodes.repos_id, nodes.r lock_comment, lock_date, local_relpath, moved_here, moved_to, file_external FROM nodes LEFT OUTER JOIN lock ON nodes.repos_id = lock.repos_id - AND nodes.repos_path = lock.repos_relpath AND op_depth = 0 + AND nodes.repos_path = lock.repos_relpath AND nodes.op_depth = 0 WHERE wc_id = ?1 AND parent_relpath = ?2 ORDER BY local_relpath DESC, op_depth DESC @@ -148,7 +148,7 @@ SELECT op_depth, nodes.repos_id, nodes.r lock_comment, lock_date, local_relpath, moved_here, moved_to, file_external FROM nodes LEFT OUTER JOIN lock ON nodes.repos_id = lock.repos_id - AND nodes.repos_path = lock.repos_relpath AND op_depth = 0 + AND nodes.repos_path = lock.repos_relpath WHERE wc_id = ?1 AND parent_relpath = ?2 AND op_depth = 0 ORDER BY local_relpath DESC @@ -249,7 +249,8 @@ WHERE wc_id = ?1 AND IS_STRICT_DESCENDAN -- STMT_DELETE_BASE_RECURSIVE DELETE FROM nodes -WHERE wc_id = ?1 AND IS_STRICT_DESCENDANT_OF(local_relpath, ?2) +WHERE wc_id = ?1 AND (local_relpath = ?2 + OR IS_STRICT_DESCENDANT_OF(local_relpath, ?2)) AND op_depth = 0 -- STMT_DELETE_WORKING_OP_DEPTH @@ -1780,6 +1781,10 @@ WHERE wc_id = ?1 AND op_depth = 0 AND (inherited_props not null) +-- STMT_HAVE_STAT1_TABLE +SELECT 1 FROM sqlite_master WHERE name='sqlite_stat1' AND type='table' +LIMIT 1 + /* ------------------------------------------------------------------------- */ /* Grab all the statements related to the schema. */ Modified: subversion/branches/move-tracking-2/subversion/libsvn_wc/wc.h URL: http://svn.apache.org/viewvc/subversion/branches/move-tracking-2/subversion/libsvn_wc/wc.h?rev=1665166&r1=1665165&r2=1665166&view=diff ============================================================================== --- subversion/branches/move-tracking-2/subversion/libsvn_wc/wc.h (original) +++ subversion/branches/move-tracking-2/subversion/libsvn_wc/wc.h Mon Mar 9 09:53:06 2015 @@ -190,6 +190,10 @@ extern "C" { /* A version < this has no work queue (see workqueue.h). */ #define SVN_WC__HAS_WORK_QUEUE 13 +/* While we still have this DB version we should verify if there is + sqlite_stat1 table on opening */ +#define SVN_WC__ENSURE_STAT1_TABLE 31 + /* Return a string indicating the released version (or versions) of * Subversion that used WC format number WC_FORMAT, or some other * suitable string if no released version used WC_FORMAT. Modified: subversion/branches/move-tracking-2/subversion/libsvn_wc/wc_db.c URL: http://svn.apache.org/viewvc/subversion/branches/move-tracking-2/subversion/libsvn_wc/wc_db.c?rev=1665166&r1=1665165&r2=1665166&view=diff ============================================================================== --- subversion/branches/move-tracking-2/subversion/libsvn_wc/wc_db.c (original) +++ subversion/branches/move-tracking-2/subversion/libsvn_wc/wc_db.c Mon Mar 9 09:53:06 2015 @@ -2125,11 +2125,13 @@ db_base_remove(svn_wc__db_wcroot_t *wcro int op_depth; svn_node_kind_t wrk_kind; svn_boolean_t no_delete_wc = FALSE; + svn_boolean_t file_external; SVN_ERR(svn_wc__db_base_get_info_internal(&status, &kind, &revision, &repos_relpath, &repos_id, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + &file_external, wcroot, local_relpath, scratch_pool, scratch_pool)); @@ -2360,35 +2362,69 @@ db_base_remove(svn_wc__db_wcroot_t *wcro SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath)); SVN_ERR(svn_sqlite__step_done(stmt)); - /* Step 5: handle the BASE node itself */ - SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, - STMT_DELETE_BASE_NODE)); - SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath)); - SVN_ERR(svn_sqlite__step_done(stmt)); - SVN_ERR(db_retract_parent_delete(wcroot, local_relpath, 0, scratch_pool)); if (mark_not_present || mark_excluded) { struct insert_base_baton_t ibb; - blank_ibb(&ibb); + svn_boolean_t no_marker = FALSE; - ibb.repos_id = repos_id; - ibb.status = mark_excluded ? svn_wc__db_status_excluded - : svn_wc__db_status_not_present; - ibb.kind = kind; - ibb.repos_relpath = repos_relpath; - ibb.revision = SVN_IS_VALID_REVNUM(marker_revision) - ? marker_revision - : revision; - - /* Depending upon KIND, any of these might get used. */ - ibb.children = NULL; - ibb.depth = svn_depth_unknown; - ibb.checksum = NULL; - ibb.target = NULL; + if (file_external) + { + const char *parent_local_relpath; + const char *name; + svn_error_t *err; - SVN_ERR(insert_base_node(&ibb, wcroot, local_relpath, scratch_pool)); + /* For file externals we only want to place a not present marker + if there is a BASE parent */ + + svn_relpath_split(&parent_local_relpath, &name, local_relpath, + scratch_pool); + + err = svn_wc__db_base_get_info_internal(NULL, NULL, NULL, + &repos_relpath, &repos_id, + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + wcroot, parent_local_relpath, + scratch_pool, scratch_pool); + + if (err && err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND) + return svn_error_trace(err); + else if (err) + { + svn_error_clear(err); + no_marker = TRUE; + } + else + { + /* Replace the repos_relpath with something more expected than + the unrelated old file external repository relpath, which + one day may come from a different repository */ + repos_relpath = svn_relpath_join(repos_relpath, name, scratch_pool); + } + } + + if (!no_marker) + { + blank_ibb(&ibb); + + ibb.repos_id = repos_id; + ibb.status = mark_excluded ? svn_wc__db_status_excluded + : svn_wc__db_status_not_present; + ibb.kind = kind; + ibb.repos_relpath = repos_relpath; + ibb.revision = SVN_IS_VALID_REVNUM(marker_revision) + ? marker_revision + : revision; + + /* Depending upon KIND, any of these might get used. */ + ibb.children = NULL; + ibb.depth = svn_depth_unknown; + ibb.checksum = NULL; + ibb.target = NULL; + + SVN_ERR(insert_base_node(&ibb, wcroot, local_relpath, scratch_pool)); + } } SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool)); @@ -3480,11 +3516,18 @@ db_external_remove(const svn_skel_t *wor apr_pool_t *scratch_pool) { svn_sqlite__stmt_t *stmt; + int affected_rows; SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_DELETE_EXTERNAL)); SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath)); - SVN_ERR(svn_sqlite__step_done(stmt)); + SVN_ERR(svn_sqlite__update(&affected_rows, stmt)); + + if (!affected_rows) + return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL, + _("The node '%s' is not an external."), + path_for_error_message(wcroot, local_relpath, + scratch_pool)); SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool)); @@ -11357,59 +11400,86 @@ svn_wc__db_global_relocate(svn_wc__db_t } -/* Set *REPOS_ID and *REPOS_RELPATH to the BASE repository location of +/* Helper for commit_node() + Set *REPOS_ID and *REPOS_RELPATH to the BASE repository location of (WCROOT, LOCAL_RELPATH), directly if its BASE row exists or implied from its parent's BASE row if not. In the latter case, error if the parent BASE row does not exist. */ static svn_error_t * -determine_repos_info(apr_int64_t *repos_id, - const char **repos_relpath, - svn_wc__db_wcroot_t *wcroot, - const char *local_relpath, - apr_pool_t *result_pool, - apr_pool_t *scratch_pool) +determine_commit_repos_info(apr_int64_t *repos_id, + const char **repos_relpath, + svn_wc__db_wcroot_t *wcroot, + const char *local_relpath, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) { svn_sqlite__stmt_t *stmt; svn_boolean_t have_row; - const char *repos_parent_relpath; - const char *local_parent_relpath, *name; - - /* ### is it faster to fetch fewer columns? */ + int op_depth; /* Prefer the current node's repository information. */ SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, - STMT_SELECT_BASE_NODE)); + 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) + 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)); + + op_depth = svn_sqlite__column_int(stmt, 0); + + if (op_depth > 0) { - SVN_ERR_ASSERT(!svn_sqlite__column_is_null(stmt, 0)); - SVN_ERR_ASSERT(!svn_sqlite__column_is_null(stmt, 1)); + svn_wc__db_status_t presence = svn_sqlite__column_token(stmt, 3, + presence_map); - *repos_id = svn_sqlite__column_int64(stmt, 0); - *repos_relpath = svn_sqlite__column_text(stmt, 1, result_pool); + if (presence == svn_wc__db_status_base_deleted) + { + SVN_ERR(svn_sqlite__step_row(stmt)); /* There must be a row */ + op_depth = svn_sqlite__column_int(stmt, 0); + } + else + { + const char *parent_repos_relpath; + const char *parent_relpath; + const char *name; - return svn_error_trace(svn_sqlite__reset(stmt)); + SVN_ERR(svn_sqlite__reset(stmt)); + + /* The repository relative path of an add/copy is based on its + ancestor, not on the shadowed base layer. + + As this function is only used from the commit processing we know + the parent directory has only a BASE row, so we can just obtain + the information directly by recursing (once!) */ + + svn_relpath_split(&parent_relpath, &name, local_relpath, + scratch_pool); + + SVN_ERR(determine_commit_repos_info(repos_id, &parent_repos_relpath, + wcroot, parent_relpath, + scratch_pool, scratch_pool)); + + *repos_relpath = svn_relpath_join(parent_repos_relpath, name, + result_pool); + return SVN_NO_ERROR; + } } - SVN_ERR(svn_sqlite__reset(stmt)); - /* This was a child node within this wcroot. We want to look at the - BASE node of the directory. */ - svn_relpath_split(&local_parent_relpath, &name, local_relpath, scratch_pool); - - /* The REPOS_ID will be the same (### until we support mixed-repos) */ - SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL, - &repos_parent_relpath, repos_id, - NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, - wcroot, local_parent_relpath, - scratch_pool, scratch_pool)); + SVN_ERR_ASSERT(op_depth == 0); /* And that row must be BASE */ - *repos_relpath = svn_relpath_join(repos_parent_relpath, name, result_pool); + SVN_ERR_ASSERT(!svn_sqlite__column_is_null(stmt, 1)); + SVN_ERR_ASSERT(!svn_sqlite__column_is_null(stmt, 2)); - return SVN_NO_ERROR; + *repos_id = svn_sqlite__column_int64(stmt, 1); + *repos_relpath = svn_sqlite__column_text(stmt, 2, result_pool); + + return svn_error_trace(svn_sqlite__reset(stmt)); } static svn_error_t * @@ -11616,9 +11686,9 @@ commit_node(svn_wc__db_wcroot_t *wcroot, For existing nodes, we should retain the (potentially-switched) repository information. */ - SVN_ERR(determine_repos_info(&repos_id, &repos_relpath, - wcroot, local_relpath, - scratch_pool, scratch_pool)); + SVN_ERR(determine_commit_repos_info(&repos_id, &repos_relpath, + wcroot, local_relpath, + scratch_pool, scratch_pool)); /* ### is it better to select only the data needed? */ SVN_ERR(svn_sqlite__get_statement(&stmt_info, wcroot->sdb, Modified: subversion/branches/move-tracking-2/subversion/libsvn_wc/wc_db.h URL: http://svn.apache.org/viewvc/subversion/branches/move-tracking-2/subversion/libsvn_wc/wc_db.h?rev=1665166&r1=1665165&r2=1665166&view=diff ============================================================================== --- subversion/branches/move-tracking-2/subversion/libsvn_wc/wc_db.h (original) +++ subversion/branches/move-tracking-2/subversion/libsvn_wc/wc_db.h Mon Mar 9 09:53:06 2015 @@ -20,7 +20,7 @@ * ==================================================================== * @endcopyright * - * @file svn_wc_db.h + * @file wc_db.h * @brief The Subversion Working Copy Library - Metadata/Base-Text Support * * Requires: Modified: subversion/branches/move-tracking-2/subversion/libsvn_wc/wc_db_update_move.c URL: http://svn.apache.org/viewvc/subversion/branches/move-tracking-2/subversion/libsvn_wc/wc_db_update_move.c?rev=1665166&r1=1665165&r2=1665166&view=diff ============================================================================== --- subversion/branches/move-tracking-2/subversion/libsvn_wc/wc_db_update_move.c (original) +++ subversion/branches/move-tracking-2/subversion/libsvn_wc/wc_db_update_move.c Mon Mar 9 09:53:06 2015 @@ -400,7 +400,7 @@ create_tree_conflict(svn_skel_t **confli if (conflict_operation != svn_wc_operation_update && conflict_operation != svn_wc_operation_switch) - return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL, + return svn_error_createf(SVN_ERR_WC_FOUND_CONFLICT, NULL, _("'%s' already in conflict"), path_for_error_message(wcroot, local_relpath, scratch_pool)); @@ -424,7 +424,7 @@ create_tree_conflict(svn_skel_t **confli && strcmp(move_src_op_root_relpath, svn_dirent_skip_ancestor(wcroot->abspath, existing_abspath)))) - return svn_error_createf(SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL, + return svn_error_createf(SVN_ERR_WC_FOUND_CONFLICT, NULL, _("'%s' already in conflict"), path_for_error_message(wcroot, local_relpath, Modified: subversion/branches/move-tracking-2/subversion/libsvn_wc/wc_db_wcroot.c URL: http://svn.apache.org/viewvc/subversion/branches/move-tracking-2/subversion/libsvn_wc/wc_db_wcroot.c?rev=1665166&r1=1665165&r2=1665166&view=diff ============================================================================== --- subversion/branches/move-tracking-2/subversion/libsvn_wc/wc_db_wcroot.c (original) +++ subversion/branches/move-tracking-2/subversion/libsvn_wc/wc_db_wcroot.c Mon Mar 9 09:53:06 2015 @@ -304,7 +304,7 @@ svn_wc__db_pdh_create_wcroot(svn_wc__db_ apr_pool_t *result_pool, apr_pool_t *scratch_pool) { - if (sdb != NULL) + if (sdb && format == FORMAT_FROM_SDB) SVN_ERR(svn_sqlite__read_schema_version(&format, sdb, scratch_pool)); /* If we construct a wcroot, then we better have a format. */ @@ -456,6 +456,56 @@ read_link_target(const char **link_targe return SVN_NO_ERROR; } +/* Verify if the sqlite_stat1 table exists and if not tries to add + this table (but ignores errors on adding the schema) */ +static svn_error_t * +verify_stats_table(svn_sqlite__db_t *sdb, + int format, + apr_pool_t *scratch_pool) +{ + svn_sqlite__stmt_t *stmt; + svn_boolean_t have_row; + + if (format != SVN_WC__ENSURE_STAT1_TABLE) + return SVN_NO_ERROR; + + SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, + STMT_HAVE_STAT1_TABLE)); + SVN_ERR(svn_sqlite__step(&have_row, stmt)); + SVN_ERR(svn_sqlite__reset(stmt)); + + if (!have_row) + { + svn_error_clear( + svn_wc__db_install_schema_statistics(sdb, scratch_pool)); + } + + return SVN_NO_ERROR; +} + +/* Sqlite transaction helper for opening the db in + svn_wc__db_wcroot_parse_local_abspath() to avoid multiple + db operations that each obtain and release a lock */ +static svn_error_t * +fetch_sdb_info(apr_int64_t *wc_id, + int *format, + svn_sqlite__db_t *sdb, + apr_pool_t *scratch_pool) +{ + *wc_id = -1; + *format = -1; + + SVN_SQLITE__WITH_LOCK4( + svn_wc__db_util_fetch_wc_id(wc_id, sdb, scratch_pool), + svn_sqlite__read_schema_version(format, sdb, scratch_pool), + verify_stats_table(sdb, *format, scratch_pool), + SVN_NO_ERROR, + sdb); + + return SVN_NO_ERROR; +} + + svn_error_t * svn_wc__db_wcroot_parse_local_abspath(svn_wc__db_wcroot_t **wcroot, const char **local_relpath, @@ -696,9 +746,10 @@ try_symlink_as_dir: /* We finally found the database. Construct a wcroot_t for it. */ apr_int64_t wc_id; + int format; svn_error_t *err; - err = svn_wc__db_util_fetch_wc_id(&wc_id, sdb, scratch_pool); + err = fetch_sdb_info(&wc_id, &format, sdb, scratch_pool); if (err) { if (err->apr_err == SVN_ERR_WC_CORRUPT) @@ -717,7 +768,7 @@ try_symlink_as_dir: symlink_wcroot_abspath ? symlink_wcroot_abspath : local_abspath), - sdb, wc_id, FORMAT_FROM_SDB, + sdb, wc_id, format, db->verify_format, db->state_pool, scratch_pool); if (err && (err->apr_err == SVN_ERR_WC_UNSUPPORTED_FORMAT || Modified: subversion/branches/move-tracking-2/subversion/mod_dav_svn/repos.c URL: http://svn.apache.org/viewvc/subversion/branches/move-tracking-2/subversion/mod_dav_svn/repos.c?rev=1665166&r1=1665165&r2=1665166&view=diff ============================================================================== --- subversion/branches/move-tracking-2/subversion/mod_dav_svn/repos.c (original) +++ subversion/branches/move-tracking-2/subversion/mod_dav_svn/repos.c Mon Mar 9 09:53:06 2015 @@ -1858,6 +1858,22 @@ do_out_of_date_check(dav_resource_combin "Attempting to modify out-of-date resource.", r->pool); } + else if (comb->priv.version_name > created_rev) + { + svn_revnum_t txn_base_rev; + + txn_base_rev = svn_fs_txn_base_revision(comb->res.info->root.txn); + if (comb->priv.version_name > txn_base_rev) + { + serr = svn_error_createf(SVN_ERR_FS_NO_SUCH_REVISION, NULL, + "No such revision %ld", + comb->priv.version_name); + + return dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR, + "Unknown base revision", + r->pool); + } + } } else if (comb->res.collection) { @@ -4099,6 +4115,22 @@ remove_resource(dav_resource *resource, "Can't DELETE out-of-date resource", resource->pool); } + else if (resource->info->version_name > created_rev) + { + svn_revnum_t txn_base_rev; + + txn_base_rev = svn_fs_txn_base_revision(resource->info->root.txn); + if (resource->info->version_name > txn_base_rev) + { + serr = svn_error_createf(SVN_ERR_FS_NO_SUCH_REVISION, NULL, + "No such revision %ld", + resource->info->version_name); + + return dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR, + "Unknown base revision", + resource->pool); + } + } } /* Before attempting the filesystem delete, we need to push any
