Modified: subversion/branches/fsx-1.10/subversion/libsvn_wc/wc_db.c URL: http://svn.apache.org/viewvc/subversion/branches/fsx-1.10/subversion/libsvn_wc/wc_db.c?rev=1685464&r1=1685463&r2=1685464&view=diff ============================================================================== --- subversion/branches/fsx-1.10/subversion/libsvn_wc/wc_db.c (original) +++ subversion/branches/fsx-1.10/subversion/libsvn_wc/wc_db.c Sun Jun 14 20:58:10 2015 @@ -278,10 +278,9 @@ add_work_items(svn_sqlite__db_t *sdb, apr_pool_t *scratch_pool); static svn_error_t * -set_actual_props(apr_int64_t wc_id, +set_actual_props(svn_wc__db_wcroot_t *wcroot, const char *local_relpath, apr_hash_t *props, - svn_sqlite__db_t *db, apr_pool_t *scratch_pool); static svn_error_t * @@ -487,35 +486,6 @@ repos_location_from_columns(apr_int64_t } } - -/* Get the statement given by STMT_IDX, and bind the appropriate wc_id and - local_relpath based upon LOCAL_ABSPATH. Store it in *STMT, and use - SCRATCH_POOL for temporary allocations. - - Note: WC_ID and LOCAL_RELPATH must be arguments 1 and 2 in the statement. */ -static svn_error_t * -get_statement_for_path(svn_sqlite__stmt_t **stmt, - svn_wc__db_t *db, - const char *local_abspath, - int stmt_idx, - 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_ERR(svn_sqlite__get_statement(stmt, wcroot->sdb, stmt_idx)); - SVN_ERR(svn_sqlite__bindf(*stmt, "is", wcroot->wc_id, local_relpath)); - - return SVN_NO_ERROR; -} - - /* For a given REPOS_ROOT_URL/REPOS_UUID pair, return the existing REPOS_ID value. If one does not exist, then create a new one. */ static svn_error_t * @@ -611,8 +581,7 @@ blank_ibb(insert_base_baton_t *pibb) was recorded, otherwise to FALSE. */ static svn_error_t * -db_extend_parent_delete(svn_boolean_t *added_delete, - svn_wc__db_wcroot_t *wcroot, +db_extend_parent_delete(svn_wc__db_wcroot_t *wcroot, const char *local_relpath, svn_node_kind_t kind, int op_depth, @@ -625,9 +594,6 @@ db_extend_parent_delete(svn_boolean_t *a SVN_ERR_ASSERT(local_relpath[0]); - if (added_delete) - *added_delete = FALSE; - SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_SELECT_LOWEST_WORKING_NODE)); SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, parent_relpath, @@ -654,9 +620,6 @@ db_extend_parent_delete(svn_boolean_t *a local_relpath, parent_op_depth, parent_relpath, kind_map, kind)); SVN_ERR(svn_sqlite__update(NULL, stmt)); - - if (added_delete) - *added_delete = TRUE; } } @@ -750,6 +713,7 @@ insert_base_node(const insert_base_baton svn_sqlite__stmt_t *stmt; svn_filesize_t recorded_size = SVN_INVALID_FILESIZE; apr_int64_t recorded_time; + svn_boolean_t present; /* The directory at the WCROOT has a NULL parent_relpath. Otherwise, bind the appropriate parent_relpath. */ @@ -780,6 +744,9 @@ insert_base_node(const insert_base_baton SVN_ERR(svn_sqlite__reset(stmt)); } + present = (pibb->status == svn_wc__db_status_normal + || pibb->status == svn_wc__db_status_incomplete); + SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_INSERT_NODE)); SVN_ERR(svn_sqlite__bindf(stmt, "isdsisr" "tstr" /* 8 - 11 */ @@ -792,15 +759,16 @@ insert_base_node(const insert_base_baton pibb->repos_relpath, pibb->revision, presence_map, pibb->status, /* 8 */ - (pibb->kind == svn_node_dir) ? /* 9 */ - svn_token__to_word(depth_map, pibb->depth) : NULL, + (pibb->kind == svn_node_dir && present) /* 9 */ + ? svn_token__to_word(depth_map, pibb->depth) + : NULL, kind_map, pibb->kind, /* 10 */ pibb->changed_rev, /* 11 */ pibb->changed_date, /* 12 */ pibb->changed_author, /* 13 */ - (pibb->kind == svn_node_symlink) ? + (pibb->kind == svn_node_symlink && present) ? pibb->target : NULL)); /* 19 */ - if (pibb->kind == svn_node_file) + if (pibb->kind == svn_node_file && present) { if (!pibb->checksum && pibb->status != svn_wc__db_status_not_present @@ -825,11 +793,14 @@ insert_base_node(const insert_base_baton assert(pibb->status == svn_wc__db_status_normal || pibb->status == svn_wc__db_status_incomplete || pibb->props == NULL); - SVN_ERR(svn_sqlite__bind_properties(stmt, 15, pibb->props, - scratch_pool)); + if (present) + { + SVN_ERR(svn_sqlite__bind_properties(stmt, 15, pibb->props, + scratch_pool)); - SVN_ERR(svn_sqlite__bind_iprops(stmt, 23, pibb->iprops, + SVN_ERR(svn_sqlite__bind_iprops(stmt, 23, pibb->iprops, scratch_pool)); + } if (pibb->dav_cache) SVN_ERR(svn_sqlite__bind_properties(stmt, 18, pibb->dav_cache, @@ -859,8 +830,8 @@ insert_base_node(const insert_base_baton new_actual_props = NULL; } - SVN_ERR(set_actual_props(wcroot->wc_id, local_relpath, new_actual_props, - wcroot->sdb, scratch_pool)); + SVN_ERR(set_actual_props(wcroot, local_relpath, new_actual_props, + scratch_pool)); } if (pibb->kind == svn_node_dir && pibb->children) @@ -881,8 +852,7 @@ insert_base_node(const insert_base_baton || (pibb->status == svn_wc__db_status_incomplete)) && ! pibb->file_external) { - SVN_ERR(db_extend_parent_delete(NULL, - wcroot, local_relpath, + SVN_ERR(db_extend_parent_delete(wcroot, local_relpath, pibb->kind, 0, scratch_pool)); } @@ -1035,6 +1005,7 @@ insert_working_node(const insert_working const char *moved_to_relpath = NULL; svn_sqlite__stmt_t *stmt; svn_boolean_t have_row; + svn_boolean_t present; SVN_ERR_ASSERT(piwb->op_depth > 0); @@ -1053,6 +1024,9 @@ insert_working_node(const insert_working moved_to_relpath = svn_sqlite__column_text(stmt, 0, scratch_pool); SVN_ERR(svn_sqlite__reset(stmt)); + present = (piwb->presence == svn_wc__db_status_normal + || piwb->presence == svn_wc__db_status_incomplete); + SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_INSERT_NODE)); SVN_ERR(svn_sqlite__bindf(stmt, "isdsnnntstrisn" "nnnn" /* properties translated_size last_mod_time dav_cache */ @@ -1061,14 +1035,14 @@ insert_working_node(const insert_working piwb->op_depth, parent_relpath, presence_map, piwb->presence, - (piwb->kind == svn_node_dir) + (piwb->kind == svn_node_dir && present) ? svn_token__to_word(depth_map, piwb->depth) : NULL, kind_map, piwb->kind, piwb->changed_rev, piwb->changed_date, piwb->changed_author, /* Note: incomplete nodes may have a NULL target. */ - (piwb->kind == svn_node_symlink) + (piwb->kind == svn_node_symlink && present) ? piwb->target : NULL, moved_to_relpath)); @@ -1077,7 +1051,7 @@ insert_working_node(const insert_working SVN_ERR(svn_sqlite__bind_int(stmt, 8, TRUE)); } - if (piwb->kind == svn_node_file) + if (piwb->kind == svn_node_file && present) { SVN_ERR(svn_sqlite__bind_checksum(stmt, 14, piwb->checksum, scratch_pool)); @@ -1094,7 +1068,8 @@ insert_working_node(const insert_working assert(piwb->presence == svn_wc__db_status_normal || piwb->presence == svn_wc__db_status_incomplete || piwb->props == NULL); - SVN_ERR(svn_sqlite__bind_properties(stmt, 15, piwb->props, scratch_pool)); + if (present && piwb->original_repos_relpath) + SVN_ERR(svn_sqlite__bind_properties(stmt, 15, piwb->props, scratch_pool)); SVN_ERR(svn_sqlite__insert(NULL, stmt)); @@ -1131,8 +1106,8 @@ insert_working_node(const insert_working new_actual_props = NULL; } - SVN_ERR(set_actual_props(wcroot->wc_id, local_relpath, new_actual_props, - wcroot->sdb, scratch_pool)); + SVN_ERR(set_actual_props(wcroot, local_relpath, new_actual_props, + scratch_pool)); } if (piwb->kind == svn_node_dir) @@ -1178,119 +1153,40 @@ insert_working_node(const insert_working } -/* Each name is allocated in RESULT_POOL and stored into CHILDREN as a key - pointed to the same name. */ -static svn_error_t * -add_children_to_hash(apr_hash_t *children, - int stmt_idx, - svn_sqlite__db_t *sdb, - apr_int64_t wc_id, - const char *parent_relpath, - apr_pool_t *result_pool) -{ - svn_sqlite__stmt_t *stmt; - svn_boolean_t have_row; - - SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, stmt_idx)); - SVN_ERR(svn_sqlite__bindf(stmt, "is", wc_id, parent_relpath)); - SVN_ERR(svn_sqlite__step(&have_row, stmt)); - while (have_row) - { - const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL); - const char *name = svn_relpath_basename(child_relpath, result_pool); - - svn_hash_sets(children, name, name); - - SVN_ERR(svn_sqlite__step(&have_row, stmt)); - } - - return svn_sqlite__reset(stmt); -} - - -/* Set *CHILDREN to a new array of the (const char *) basenames of the - immediate children, whatever their status, of the working node at - LOCAL_RELPATH. */ -static svn_error_t * -gather_children2(const apr_array_header_t **children, - svn_wc__db_wcroot_t *wcroot, - const char *local_relpath, - apr_pool_t *result_pool, - apr_pool_t *scratch_pool) -{ - apr_hash_t *names_hash = apr_hash_make(scratch_pool); - apr_array_header_t *names_array; - - /* All of the names get allocated in RESULT_POOL. It - appears to be faster to use the hash to remove duplicates than to - use DISTINCT in the SQL query. */ - SVN_ERR(add_children_to_hash(names_hash, STMT_SELECT_WORKING_CHILDREN, - wcroot->sdb, wcroot->wc_id, - local_relpath, result_pool)); - - SVN_ERR(svn_hash_keys(&names_array, names_hash, result_pool)); - *children = names_array; - return SVN_NO_ERROR; -} - /* Return in *CHILDREN all of the children of the directory LOCAL_RELPATH, of any status, in all op-depths in the NODES table. */ static svn_error_t * gather_children(const apr_array_header_t **children, svn_wc__db_wcroot_t *wcroot, - const char *local_relpath, + const char *parent_relpath, + int stmt_idx, + int op_depth, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { - apr_hash_t *names_hash = apr_hash_make(scratch_pool); - apr_array_header_t *names_array; - - /* All of the names get allocated in RESULT_POOL. It - appears to be faster to use the hash to remove duplicates than to - use DISTINCT in the SQL query. */ - SVN_ERR(add_children_to_hash(names_hash, STMT_SELECT_NODE_CHILDREN, - wcroot->sdb, wcroot->wc_id, - local_relpath, result_pool)); - - SVN_ERR(svn_hash_keys(&names_array, names_hash, result_pool)); - *children = names_array; - return SVN_NO_ERROR; -} - - -/* Set *CHILDREN to a new array of (const char *) names of the children of - the repository directory corresponding to WCROOT:LOCAL_RELPATH:OP_DEPTH - - that is, only the children that are at the same op-depth as their parent. */ -static svn_error_t * -gather_repo_children(const apr_array_header_t **children, - svn_wc__db_wcroot_t *wcroot, - const char *local_relpath, - int op_depth, - apr_pool_t *result_pool, - apr_pool_t *scratch_pool) -{ - apr_array_header_t *result - = apr_array_make(result_pool, 0, sizeof(const char *)); + apr_array_header_t *result; svn_sqlite__stmt_t *stmt; svn_boolean_t have_row; - SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, - STMT_SELECT_OP_DEPTH_CHILDREN)); - SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath, - op_depth)); + result = apr_array_make(result_pool, 16, sizeof(const char*)); + + SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, stmt_idx)); + SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, parent_relpath)); + if (op_depth >= 0) + SVN_ERR(svn_sqlite__bind_int(stmt, 3, op_depth)); + SVN_ERR(svn_sqlite__step(&have_row, stmt)); while (have_row) { const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL); + const char *name = svn_relpath_basename(child_relpath, result_pool); - /* Allocate the name in RESULT_POOL so we won't have to copy it. */ - APR_ARRAY_PUSH(result, const char *) - = svn_relpath_basename(child_relpath, result_pool); + APR_ARRAY_PUSH(result, const char *) = name; SVN_ERR(svn_sqlite__step(&have_row, stmt)); } - SVN_ERR(svn_sqlite__reset(stmt)); + SVN_ERR(svn_sqlite__reset(stmt)); *children = result; return SVN_NO_ERROR; } @@ -1585,7 +1481,7 @@ svn_wc__db_init(svn_wc__db_t *db, /* ### REPOS_ROOT_URL and REPOS_UUID may be NULL. ... more doc: tbd */ - SVN_ERR(svn_config_get_bool((svn_config_t *)db->config, &sqlite_exclusive, + SVN_ERR(svn_config_get_bool(db->config, &sqlite_exclusive, SVN_CONFIG_SECTION_WORKING_COPY, SVN_CONFIG_OPTION_SQLITE_EXCLUSIVE, FALSE)); @@ -2177,7 +2073,7 @@ clear_moved_here(svn_wc__db_wcroot_t *wc svn_error_t * svn_wc__db_op_break_move_internal(svn_wc__db_wcroot_t *wcroot, const char *src_relpath, - int src_op_depth, + int delete_op_depth, const char *dst_relpath, const svn_skel_t *work_items, apr_pool_t *scratch_pool) @@ -2188,7 +2084,7 @@ svn_wc__db_op_break_move_internal(svn_wc SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_CLEAR_MOVED_TO_RELPATH)); SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, src_relpath, - src_op_depth)); + delete_op_depth)); SVN_ERR(svn_sqlite__update(&affected, stmt)); if (affected != 1) @@ -2211,9 +2107,9 @@ db_base_remove(svn_wc__db_wcroot_t *wcro const char *local_relpath, svn_wc__db_t *db, /* For checking conflicts */ svn_boolean_t keep_as_working, - svn_boolean_t queue_deletes, - svn_boolean_t remove_locks, - svn_revnum_t not_present_revision, + svn_boolean_t mark_not_present, + svn_boolean_t mark_excluded, + svn_revnum_t marker_revision, svn_skel_t *conflict, svn_skel_t *work_items, apr_pool_t *scratch_pool) @@ -2221,66 +2117,82 @@ db_base_remove(svn_wc__db_wcroot_t *wcro svn_sqlite__stmt_t *stmt; svn_boolean_t have_row; svn_wc__db_status_t status; + svn_revnum_t revision; apr_int64_t repos_id; const char *repos_relpath; svn_node_kind_t kind; svn_boolean_t keep_working; + 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, NULL, + 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)); - if (remove_locks) - { - svn_sqlite__stmt_t *lock_stmt; + /* Check if there is already a working node */ + 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)); - SVN_ERR(svn_sqlite__get_statement(&lock_stmt, wcroot->sdb, - STMT_DELETE_LOCK_RECURSIVELY)); - SVN_ERR(svn_sqlite__bindf(lock_stmt, "is", repos_id, repos_relpath)); - SVN_ERR(svn_sqlite__step_done(lock_stmt)); - } + if (!have_row) + return svn_error_trace(svn_sqlite__reset(stmt)); /* No BASE */ - if (status == svn_wc__db_status_normal - && keep_as_working) + op_depth = svn_sqlite__column_int(stmt, 0); + wrk_kind = svn_sqlite__column_token(stmt, 4, kind_map); + + if (op_depth > 0 + && op_depth == relpath_depth(local_relpath)) { - SVN_ERR(svn_wc__db_op_make_copy(db, - svn_dirent_join(wcroot->abspath, - local_relpath, - scratch_pool), - NULL, NULL, - scratch_pool)); - keep_working = TRUE; + svn_wc__db_status_t presence; + presence = svn_sqlite__column_token(stmt, 3, presence_map); + + if (presence == svn_wc__db_status_base_deleted) + { + keep_working = FALSE; + no_delete_wc = TRUE; + } + else + { + keep_working = TRUE; + } } else + keep_working = FALSE; + SVN_ERR(svn_sqlite__reset(stmt)); + + if (keep_as_working && op_depth == 0) { - /* Check if there is already a working node */ - SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, - STMT_SELECT_WORKING_NODE)); - SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath)); - SVN_ERR(svn_sqlite__step(&keep_working, stmt)); - SVN_ERR(svn_sqlite__reset(stmt)); + if (status == svn_wc__db_status_normal + || status == svn_wc__db_status_incomplete) + { + SVN_ERR(svn_wc__db_op_make_copy_internal(wcroot, local_relpath, TRUE, + NULL, NULL, + scratch_pool)); + } + keep_working = TRUE; } /* Step 1: Create workqueue operations to remove files and dirs in the local-wc */ - if (!keep_working - && queue_deletes - && (status == svn_wc__db_status_normal - || status == svn_wc__db_status_incomplete)) + if (!keep_working && !no_delete_wc) { svn_skel_t *work_item; const char *local_abspath; local_abspath = svn_dirent_join(wcroot->abspath, local_relpath, scratch_pool); - if (kind == svn_node_dir) + if (wrk_kind == svn_node_dir) { apr_pool_t *iterpool; SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, - STMT_SELECT_BASE_PRESENT)); + STMT_SELECT_WORKING_PRESENT)); SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath)); iterpool = svn_pool_create(scratch_pool); @@ -2359,27 +2271,12 @@ db_base_remove(svn_wc__db_wcroot_t *wcro ACTUAL_NODE records */ /* Step 3: Delete WORKING nodes */ - if (conflict) + if (!keep_working) { apr_pool_t *iterpool; - /* - * When deleting a conflicted node, moves of any moved-outside children - * of the node must be broken. Else, the destination will still be marked - * moved-here after the move source disappears from the working copy. - * - * ### FIXME: It would be nicer to have the conflict resolver - * break the move instead. It might also be a good idea to - * flag a tree conflict on each moved-away child. But doing so - * might introduce actual-only nodes without direct parents, - * and we're not yet sure if other existing code is prepared - * to handle such nodes. To be revisited post-1.8. - * - * ### In case of a conflict we are most likely creating WORKING nodes - * describing a copy of what was in BASE. The move information - * should be updated to describe a move from the WORKING layer. - * When stored that way the resolver of the tree conflict still has - * the knowledge of what was moved. + /* When deleting everything in working we should break moves from + here and to here. */ SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_SELECT_MOVED_OUTSIDE)); @@ -2403,10 +2300,51 @@ db_base_remove(svn_wc__db_wcroot_t *wcro svn_pool_destroy(iterpool); SVN_ERR(svn_sqlite__reset(stmt)); } - if (keep_working) + else { + /* We are keeping things that are in WORKING, but we should still + break moves of things in BASE. (Mixed revisions make it + impossible to guarantee that we can keep everything moved) */ + + apr_pool_t *iterpool; + SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, - STMT_DELETE_WORKING_BASE_DELETE)); + STMT_SELECT_MOVED_DESCENDANTS_SRC)); + SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, + local_relpath, 0)); + SVN_ERR(svn_sqlite__step(&have_row, stmt)); + iterpool = svn_pool_create(scratch_pool); + while (have_row) + { + int delete_op_depth = svn_sqlite__column_int(stmt, 0); + const char *src_relpath; + const char *dst_relpath; + svn_error_t *err; + + svn_pool_clear(iterpool); + + src_relpath = svn_sqlite__column_text(stmt, 1, iterpool); + dst_relpath = svn_sqlite__column_text(stmt, 4, iterpool); + + err = svn_wc__db_op_break_move_internal(wcroot, src_relpath, + delete_op_depth, + dst_relpath, + NULL, + iterpool); + + if (err) + return svn_error_compose_create(err, svn_sqlite__reset(stmt)); + + SVN_ERR(svn_sqlite__step(&have_row, stmt)); + } + svn_pool_destroy(iterpool); + SVN_ERR(svn_sqlite__reset(stmt)); + } + if (keep_working) + { + SVN_ERR(svn_sqlite__get_statement( + &stmt, wcroot->sdb, + STMT_DELETE_WORKING_BASE_DELETE_RECURSIVE)); SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath, 0)); SVN_ERR(svn_sqlite__step_done(stmt)); } @@ -2424,41 +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)); - /* Step 6: Delete actual node if we don't keep working */ - if (! keep_working) - { - SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, - STMT_DELETE_ACTUAL_NODE)); - SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath)); - SVN_ERR(svn_sqlite__step_done(stmt)); - } - - if (SVN_IS_VALID_REVNUM(not_present_revision)) + 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 = svn_wc__db_status_not_present; - ibb.kind = kind; - ibb.repos_relpath = repos_relpath; - ibb.revision = not_present_revision; + if (file_external) + { + const char *parent_local_relpath; + const char *name; + svn_error_t *err; - /* Depending upon KIND, any of these might get used. */ - ibb.children = NULL; - ibb.depth = svn_depth_unknown; - ibb.checksum = NULL; - ibb.target = NULL; + /* 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); - SVN_ERR(insert_base_node(&ibb, wcroot, 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)); @@ -2474,9 +2440,9 @@ svn_error_t * svn_wc__db_base_remove(svn_wc__db_t *db, const char *local_abspath, svn_boolean_t keep_as_working, - svn_boolean_t queue_deletes, - svn_boolean_t remove_locks, - svn_revnum_t not_present_revision, + svn_boolean_t mark_not_present, + svn_boolean_t mark_excluded, + svn_revnum_t marker_revision, svn_skel_t *conflict, svn_skel_t *work_items, apr_pool_t *scratch_pool) @@ -2491,8 +2457,9 @@ svn_wc__db_base_remove(svn_wc__db_t *db, VERIFY_USABLE_WCROOT(wcroot); SVN_WC__DB_WITH_TXN(db_base_remove(wcroot, local_relpath, - db, keep_as_working, queue_deletes, - remove_locks, not_present_revision, + db, keep_as_working, + mark_not_present, mark_excluded, + marker_revision, conflict, work_items, scratch_pool), wcroot); @@ -2843,8 +2810,10 @@ svn_wc__db_base_get_children(const apr_a scratch_pool, scratch_pool)); VERIFY_USABLE_WCROOT(wcroot); - return gather_repo_children(children, wcroot, local_relpath, 0, - result_pool, scratch_pool); + return svn_error_trace( + gather_children(children, wcroot, local_relpath, + STMT_SELECT_OP_DEPTH_CHILDREN, 0, + result_pool, scratch_pool)); } @@ -2854,12 +2823,20 @@ svn_wc__db_base_set_dav_cache(svn_wc__db const apr_hash_t *props, apr_pool_t *scratch_pool) { + svn_wc__db_wcroot_t *wcroot; + const char *local_relpath; svn_sqlite__stmt_t *stmt; int affected_rows; - SVN_ERR(get_statement_for_path(&stmt, db, local_abspath, - STMT_UPDATE_BASE_NODE_DAV_CACHE, - scratch_pool)); + 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_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, + STMT_UPDATE_BASE_NODE_DAV_CACHE)); + SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath)); SVN_ERR(svn_sqlite__bind_properties(stmt, 3, props, scratch_pool)); SVN_ERR(svn_sqlite__update(&affected_rows, stmt)); @@ -2881,11 +2858,20 @@ svn_wc__db_base_get_dav_cache(apr_hash_t apr_pool_t *result_pool, apr_pool_t *scratch_pool) { + svn_wc__db_wcroot_t *wcroot; + const char *local_relpath; svn_sqlite__stmt_t *stmt; svn_boolean_t have_row; - SVN_ERR(get_statement_for_path(&stmt, db, local_abspath, - STMT_SELECT_BASE_DAV_CACHE, scratch_pool)); + 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_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, + STMT_SELECT_BASE_DAV_CACHE)); + SVN_ERR(svn_sqlite__step(&have_row, stmt)); if (!have_row) return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, @@ -3057,6 +3043,11 @@ svn_wc__db_depth_get_info(svn_wc__db_sta return svn_error_compose_create(err, svn_sqlite__reset(stmt)); } +/* A callback which supplies WCROOTs and LOCAL_RELPATHs. */ +typedef svn_error_t *(*svn_wc__db_txn_callback_t)(void *baton, + svn_wc__db_wcroot_t *wcroot, + const char *local_relpath, + apr_pool_t *scratch_pool); /* Baton for passing args to with_triggers(). */ struct with_triggers_baton_t { @@ -3133,8 +3124,13 @@ with_finalization(svn_wc__db_wcroot_t *w svn_error_t *err1; svn_error_t *err2; - err1 = svn_wc__db_with_txn(wcroot, local_relpath, txn_cb, txn_baton, - scratch_pool); + err1 = svn_sqlite__begin_savepoint(wcroot->sdb); + if (!err1) + { + err1 = txn_cb(txn_baton, wcroot, local_relpath, scratch_pool); + + err1 = svn_sqlite__finish_savepoint(wcroot->sdb, err1); + } if (err1 == NULL && notify_func != NULL) { @@ -3520,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)); @@ -4007,14 +4010,14 @@ get_moved_to(const char **moved_to_relpa /* The body of svn_wc__db_scan_deletion(). */ static svn_error_t * -scan_deletion_txn(const char **base_del_relpath, - const char **moved_to_relpath, - const char **work_del_relpath, - const char **moved_to_op_root_relpath, - svn_wc__db_wcroot_t *wcroot, - const char *local_relpath, - apr_pool_t *result_pool, - apr_pool_t *scratch_pool) +scan_deletion(const char **base_del_relpath, + const char **moved_to_relpath, + const char **work_del_relpath, + const char **moved_to_op_root_relpath, + svn_wc__db_wcroot_t *wcroot, + const char *local_relpath, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) { const char *current_relpath = local_relpath; svn_sqlite__stmt_t *stmt; @@ -4038,9 +4041,7 @@ scan_deletion_txn(const char **base_del_ scan = (moved_to_op_root_relpath || moved_to_relpath); SVN_ERR(svn_sqlite__get_statement( - &stmt, wcroot->sdb, - scan ? STMT_SELECT_DELETION_INFO_SCAN - : STMT_SELECT_DELETION_INFO)); + &stmt, wcroot->sdb, STMT_SELECT_DELETION_INFO)); SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, current_relpath)); SVN_ERR(svn_sqlite__step(&have_row, stmt)); @@ -4161,6 +4162,25 @@ scan_deletion_txn(const char **base_del_ } svn_error_t * +svn_wc__db_scan_deletion_internal( + const char **base_del_relpath, + const char **moved_to_relpath, + const char **work_del_relpath, + const char **moved_to_op_root_relpath, + svn_wc__db_wcroot_t *wcroot, + const char *local_relpath, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + return svn_error_trace( + scan_deletion(base_del_relpath, moved_to_relpath, work_del_relpath, + moved_to_op_root_relpath, + wcroot, local_relpath, + result_pool, scratch_pool)); +} + + +svn_error_t * svn_wc__db_scan_deletion(const char **base_del_abspath, const char **moved_to_abspath, const char **work_del_abspath, @@ -4182,9 +4202,9 @@ svn_wc__db_scan_deletion(const char **ba VERIFY_USABLE_WCROOT(wcroot); SVN_WC__DB_WITH_TXN( - scan_deletion_txn(&base_del_relpath, &moved_to_relpath, - &work_del_relpath, &moved_to_op_root_relpath, - wcroot, local_relpath, result_pool, scratch_pool), + scan_deletion(&base_del_relpath, &moved_to_relpath, + &work_del_relpath, &moved_to_op_root_relpath, + wcroot, local_relpath, result_pool, scratch_pool), wcroot); if (base_del_abspath) @@ -4282,10 +4302,10 @@ get_info_for_copy(apr_int64_t *copyfrom_ { const char *base_del_relpath, *work_del_relpath; - SVN_ERR(scan_deletion_txn(&base_del_relpath, NULL, - &work_del_relpath, - NULL, src_wcroot, local_relpath, - scratch_pool, scratch_pool)); + SVN_ERR(scan_deletion(&base_del_relpath, NULL, + &work_del_relpath, + NULL, src_wcroot, local_relpath, + scratch_pool, scratch_pool)); if (work_del_relpath) { const char *op_root_relpath; @@ -4614,8 +4634,9 @@ db_op_copy(svn_wc__db_wcroot_t *src_wcro int src_op_depth; SVN_ERR(op_depth_of(&src_op_depth, src_wcroot, src_relpath)); - SVN_ERR(gather_repo_children(&children, src_wcroot, src_relpath, - src_op_depth, scratch_pool, scratch_pool)); + SVN_ERR(gather_children(&children, src_wcroot, src_relpath, + STMT_SELECT_OP_DEPTH_CHILDREN, src_op_depth, + scratch_pool, scratch_pool)); } else children = NULL; @@ -4839,16 +4860,82 @@ svn_wc__db_op_copy(svn_wc__db_t *db, return SVN_NO_ERROR; } -svn_error_t * -svn_wc__db_op_copy_layer_internal(svn_wc__db_wcroot_t *wcroot, - const char *src_op_relpath, - int src_op_depth, - const char *dst_op_relpath, - svn_skel_t *conflict, - svn_skel_t *work_items, - apr_pool_t *scratch_pool) -{ - svn_sqlite__stmt_t *stmt, *stmt2; +/* Remove unneeded actual nodes for svn_wc__db_op_copy_layer_internal */ +static svn_error_t * +clear_or_remove_actual(svn_wc__db_wcroot_t *wcroot, + const char *local_relpath, + int op_depth, + apr_pool_t *scratch_pool) +{ + svn_sqlite__stmt_t *stmt; + svn_boolean_t have_row, shadowed; + svn_boolean_t keep_conflict = FALSE; + + 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) + { + svn_wc__db_status_t presence; + + shadowed = (svn_sqlite__column_int(stmt, 0) > op_depth); + presence = svn_sqlite__column_token(stmt, 3, presence_map); + + if (shadowed && presence == svn_wc__db_status_base_deleted) + { + keep_conflict = TRUE; + SVN_ERR(svn_sqlite__step(&have_row, stmt)); + + if (have_row) + shadowed = (svn_sqlite__column_int(stmt, 0) > op_depth); + else + shadowed = FALSE; + } + } + else + shadowed = FALSE; + + SVN_ERR(svn_sqlite__reset(stmt)); + if (shadowed) + return SVN_NO_ERROR; + + if (keep_conflict) + { + /* We don't want to accidentally remove delete-delete conflicts */ + SVN_ERR(svn_sqlite__get_statement( + &stmt, wcroot->sdb, + STMT_CLEAR_ACTUAL_NODE_LEAVING_CONFLICT)); + SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath)); + SVN_ERR(svn_sqlite__step_done(stmt)); + SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, + STMT_DELETE_ACTUAL_EMPTY)); + SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath)); + SVN_ERR(svn_sqlite__step_done(stmt)); + } + else + { + SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, + STMT_DELETE_ACTUAL_NODE)); + SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath)); + SVN_ERR(svn_sqlite__step_done(stmt)); + } + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_wc__db_op_copy_layer_internal(svn_wc__db_wcroot_t *wcroot, + const char *src_op_relpath, + int src_op_depth, + const char *dst_op_relpath, + svn_skel_t *conflict, + svn_skel_t *work_items, + apr_pool_t *scratch_pool) +{ + svn_sqlite__stmt_t *stmt, *stmt2; svn_boolean_t have_row; apr_pool_t *iterpool = svn_pool_create(scratch_pool); int dst_op_depth = relpath_depth(dst_op_relpath); @@ -4878,15 +4965,12 @@ svn_wc__db_op_copy_layer_internal(svn_wc { const char *src_relpath; const char *dst_relpath; - svn_boolean_t exists; svn_pool_clear(iterpool); src_relpath = svn_sqlite__column_text(stmt, 0, iterpool); dst_relpath = svn_sqlite__column_text(stmt, 2, iterpool); - exists = !svn_sqlite__column_is_null(stmt, 3); - err = svn_sqlite__bindf(stmt2, "isdsds", wcroot->wc_id, src_relpath, src_op_depth, dst_relpath, dst_op_depth, @@ -4899,21 +4983,11 @@ svn_wc__db_op_copy_layer_internal(svn_wc if (err) break; - if (strlen(dst_relpath) > strlen(dst_op_relpath)) + /* The node can't be deleted where it is added, so extension of + an existing shadowing is only interesting 2 levels deep. */ + if (relpath_depth(dst_relpath) > (dst_op_depth+1)) { - svn_boolean_t added_delete = FALSE; - svn_node_kind_t kind = svn_sqlite__column_token(stmt, 1, kind_map); - - /* The op root can't be shadowed, so extension of a parent delete - is only needed when the parent can be deleted */ - if (relpath_depth(dst_relpath) > (dst_op_depth+1)) - { - err = db_extend_parent_delete(&added_delete, wcroot, dst_relpath, - kind, dst_op_depth, iterpool); - - if (err) - break; - } + svn_boolean_t exists = !svn_sqlite__column_is_null(stmt, 3); if (exists) { @@ -4921,28 +4995,19 @@ svn_wc__db_op_copy_layer_internal(svn_wc presence = svn_sqlite__column_token(stmt, 3, presence_map); - if (presence == svn_wc__db_status_not_present) + if (presence != svn_wc__db_status_normal) exists = FALSE; } - /* ### Fails in a few tests... Needs further research */ - /*SVN_ERR_ASSERT(!(exists && added_delete));*/ - if (!exists) { - svn_boolean_t shadowed; + svn_node_kind_t kind = svn_sqlite__column_token(stmt, 1, kind_map); - shadowed = svn_sqlite__column_int(stmt, 4); + err = db_extend_parent_delete(wcroot, dst_relpath, + kind, dst_op_depth, iterpool); - /*if (!shadowed && !added_delete) - { - err = svn_error_createf( - SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL, - _("Node '%s' was unexpectedly added unshadowed"), - path_for_error_message(wcroot, dst_relpath, - iterpool)); - break; - }*/ + if (err) + break; } } @@ -4992,6 +5057,12 @@ svn_wc__db_op_copy_layer_internal(svn_wc err = svn_sqlite__step_done(stmt2); /* stmt2 is reset (never modified or by step_done) */ + if (err) + break; + + /* Delete ACTUAL information about this node that we just deleted */ + err = clear_or_remove_actual(wcroot, dst_relpath, dst_op_depth, + scratch_pool); if (err) break; @@ -5009,8 +5080,6 @@ svn_wc__db_op_copy_layer_internal(svn_wc SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt))); - /* ### TODO: Did we handle ACTUAL as intended? */ - SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool)); if (conflict) @@ -5427,8 +5496,9 @@ db_op_copy_shadowed_layer(svn_wc__db_wcr return SVN_NO_ERROR; } - SVN_ERR(gather_repo_children(&children, src_wcroot, src_relpath, - src_op_depth, scratch_pool, iterpool)); + SVN_ERR(gather_children(&children, src_wcroot, src_relpath, + STMT_SELECT_OP_DEPTH_CHILDREN, src_op_depth, + scratch_pool, iterpool)); for (i = 0; i < children->nelts; i++) { @@ -5615,8 +5685,8 @@ svn_wc__db_op_copy_dir(svn_wc__db_t *db, const char *original_uuid, svn_revnum_t original_revision, const apr_array_header_t *children, - svn_boolean_t is_move, svn_depth_t depth, + svn_boolean_t is_move, const svn_skel_t *conflict, const svn_skel_t *work_items, apr_pool_t *scratch_pool) @@ -5643,11 +5713,6 @@ svn_wc__db_op_copy_dir(svn_wc__db_t *db, iwb.presence = svn_wc__db_status_normal; iwb.kind = svn_node_dir; - iwb.props = props; - iwb.changed_rev = changed_rev; - iwb.changed_date = changed_date; - iwb.changed_author = changed_author; - if (original_root_url != NULL) { SVN_ERR(create_repos_id(&iwb.original_repos_id, @@ -5655,6 +5720,11 @@ svn_wc__db_op_copy_dir(svn_wc__db_t *db, wcroot->sdb, scratch_pool)); iwb.original_repos_relpath = original_repos_relpath; iwb.original_revnum = original_revision; + + iwb.props = props; + iwb.changed_rev = changed_rev; + iwb.changed_date = changed_date; + iwb.changed_author = changed_author; } /* ### Should we do this inside the transaction? */ @@ -5723,11 +5793,6 @@ svn_wc__db_op_copy_file(svn_wc__db_t *db iwb.presence = svn_wc__db_status_normal; iwb.kind = svn_node_file; - iwb.props = props; - iwb.changed_rev = changed_rev; - iwb.changed_date = changed_date; - iwb.changed_author = changed_author; - if (original_root_url != NULL) { SVN_ERR(create_repos_id(&iwb.original_repos_id, @@ -5735,6 +5800,11 @@ svn_wc__db_op_copy_file(svn_wc__db_t *db wcroot->sdb, scratch_pool)); iwb.original_repos_relpath = original_repos_relpath; iwb.original_revnum = original_revision; + + iwb.props = props; + iwb.changed_rev = changed_rev; + iwb.changed_date = changed_date; + iwb.changed_author = changed_author; } /* ### Should we do this inside the transaction? */ @@ -5777,6 +5847,7 @@ svn_wc__db_op_copy_symlink(svn_wc__db_t const char *original_uuid, svn_revnum_t original_revision, const char *target, + svn_boolean_t is_move, const svn_skel_t *conflict, const svn_skel_t *work_items, apr_pool_t *scratch_pool) @@ -5801,11 +5872,6 @@ svn_wc__db_op_copy_symlink(svn_wc__db_t iwb.presence = svn_wc__db_status_normal; iwb.kind = svn_node_symlink; - iwb.props = props; - iwb.changed_rev = changed_rev; - iwb.changed_date = changed_date; - iwb.changed_author = changed_author; - iwb.moved_here = FALSE; if (original_root_url != NULL) { @@ -5814,6 +5880,11 @@ svn_wc__db_op_copy_symlink(svn_wc__db_t wcroot->sdb, scratch_pool)); iwb.original_repos_relpath = original_repos_relpath; iwb.original_revnum = original_revision; + + iwb.props = props; + iwb.changed_rev = changed_rev; + iwb.changed_date = changed_date; + iwb.changed_author = changed_author; } /* ### Should we do this inside the transaction? */ @@ -5823,6 +5894,8 @@ svn_wc__db_op_copy_symlink(svn_wc__db_t wcroot, local_relpath, scratch_pool)); iwb.target = target; + iwb.moved_here = is_move && (parent_op_depth == 0 || + iwb.op_depth == parent_op_depth); iwb.work_items = work_items; iwb.conflict = conflict; @@ -6032,27 +6105,39 @@ svn_wc__db_global_record_fileinfo(svn_wc * props; to indicate no properties when the pristine has some props, * PROPS must be an empty hash. */ static svn_error_t * -set_actual_props(apr_int64_t wc_id, +set_actual_props(svn_wc__db_wcroot_t *wcroot, const char *local_relpath, apr_hash_t *props, - svn_sqlite__db_t *db, apr_pool_t *scratch_pool) { svn_sqlite__stmt_t *stmt; int affected_rows; - SVN_ERR(svn_sqlite__get_statement(&stmt, db, STMT_UPDATE_ACTUAL_PROPS)); - SVN_ERR(svn_sqlite__bindf(stmt, "is", wc_id, local_relpath)); + SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, + STMT_UPDATE_ACTUAL_PROPS)); + SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath)); SVN_ERR(svn_sqlite__bind_properties(stmt, 3, props, scratch_pool)); SVN_ERR(svn_sqlite__update(&affected_rows, stmt)); if (affected_rows == 1 || !props) - return SVN_NO_ERROR; /* We are done */ + { + /* Perhaps the entire ACTUAL record is unneeded now? */ + if (!props && affected_rows) + { + SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, + STMT_DELETE_ACTUAL_EMPTY)); + SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath)); + SVN_ERR(svn_sqlite__step_done(stmt)); + } + + return SVN_NO_ERROR; /* We are done */ + } /* We have to insert a row in ACTUAL */ - SVN_ERR(svn_sqlite__get_statement(&stmt, db, STMT_INSERT_ACTUAL_PROPS)); - SVN_ERR(svn_sqlite__bindf(stmt, "is", wc_id, local_relpath)); + SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, + STMT_INSERT_ACTUAL_PROPS)); + SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath)); if (*local_relpath != '\0') SVN_ERR(svn_sqlite__bind_text(stmt, 3, svn_relpath_dirname(local_relpath, @@ -6068,8 +6153,7 @@ svn_wc__db_op_set_props_internal(svn_wc_ svn_boolean_t clear_recorded_info, apr_pool_t *scratch_pool) { - SVN_ERR(set_actual_props(wcroot->wc_id, local_relpath, - props, wcroot->sdb, scratch_pool)); + SVN_ERR(set_actual_props(wcroot, local_relpath, props, scratch_pool)); if (clear_recorded_info) { @@ -6348,7 +6432,7 @@ set_changelist_txn(void *baton, if (scb->new_changelist) { SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, - STMT_INSERT_ACTUAL_EMPTIES)); + STMT_INSERT_ACTUAL_EMPTIES_FILES)); SVN_ERR(svn_sqlite__step_done(stmt)); } @@ -6565,15 +6649,15 @@ svn_wc__db_op_mark_conflict(svn_wc__db_t /* The body of svn_wc__db_op_mark_resolved(). */ -static svn_error_t * -db_op_mark_resolved(svn_wc__db_wcroot_t *wcroot, - const char *local_relpath, - svn_wc__db_t *db, - svn_boolean_t resolved_text, - svn_boolean_t resolved_props, - svn_boolean_t resolved_tree, - const svn_skel_t *work_items, - apr_pool_t *scratch_pool) +svn_error_t * +svn_wc__db_op_mark_resolved_internal(svn_wc__db_wcroot_t *wcroot, + const char *local_relpath, + svn_wc__db_t *db, + svn_boolean_t resolved_text, + svn_boolean_t resolved_props, + svn_boolean_t resolved_tree, + const svn_skel_t *work_items, + apr_pool_t *scratch_pool) { svn_sqlite__stmt_t *stmt; svn_boolean_t have_row; @@ -6671,7 +6755,8 @@ svn_wc__db_op_mark_resolved(svn_wc__db_t VERIFY_USABLE_WCROOT(wcroot); SVN_WC__DB_WITH_TXN( - db_op_mark_resolved(wcroot, local_relpath, db, + svn_wc__db_op_mark_resolved_internal( + wcroot, local_relpath, db, resolved_text, resolved_props, resolved_tree, work_items, scratch_pool), wcroot); @@ -6739,6 +6824,7 @@ op_revert_txn(void *baton, int affected_rows; const char *moved_to; int op_depth_increased = 0; + int op_depth_below; svn_skel_t *conflict; /* ### Similar structure to op_revert_recursive_txn, should they be @@ -6786,6 +6872,13 @@ op_revert_txn(void *baton, op_depth = svn_sqlite__column_int(stmt, 0); moved_here = svn_sqlite__column_boolean(stmt, 15); moved_to = svn_sqlite__column_text(stmt, 17, scratch_pool); + + SVN_ERR(svn_sqlite__step(&have_row, stmt)); + if (have_row) + op_depth_below = svn_sqlite__column_int(stmt, 0); + else + op_depth_below = -1; + SVN_ERR(svn_sqlite__reset(stmt)); if (moved_to) @@ -6796,7 +6889,7 @@ op_revert_txn(void *baton, } else { - SVN_ERR(svn_wc__db_read_conflict_internal(&conflict, wcroot, + SVN_ERR(svn_wc__db_read_conflict_internal(&conflict, NULL, NULL, wcroot, local_relpath, scratch_pool, scratch_pool)); } @@ -6873,7 +6966,7 @@ op_revert_txn(void *baton, || reason == svn_wc_conflict_reason_replaced) { SVN_ERR(svn_wc__db_op_raise_moved_away_internal( - wcroot, local_relpath, op_depth+1, db, + wcroot, local_relpath, op_depth_below, db, operation, action, (locations && locations->nelts > 0) ? APR_ARRAY_IDX(locations, 0, @@ -7024,7 +7117,7 @@ op_revert_recursive_txn(void *baton, STMT_DELETE_ACTUAL_NODE_LEAVING_CHANGELIST_RECURSIVE)); SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath)); SVN_ERR(svn_sqlite__step_done(stmt)); - + SVN_ERR(svn_sqlite__get_statement( &stmt, wcroot->sdb, STMT_CLEAR_ACTUAL_NODE_LEAVING_CHANGELIST_RECURSIVE)); @@ -7397,9 +7490,6 @@ remove_node_txn(svn_boolean_t *left_chan svn_wc__db_t *db, svn_boolean_t destroy_wc, svn_boolean_t destroy_changes, - svn_revnum_t not_present_rev, - svn_wc__db_status_t not_present_status, - svn_node_kind_t not_present_kind, const svn_skel_t *conflict, const svn_skel_t *work_items, svn_cancel_func_t cancel_func, @@ -7408,9 +7498,6 @@ remove_node_txn(svn_boolean_t *left_chan { svn_sqlite__stmt_t *stmt; - apr_int64_t repos_id; - const char *repos_relpath; - /* Note that unlike many similar functions it is a valid scenario for this function to be called on a wcroot! */ @@ -7420,15 +7507,6 @@ remove_node_txn(svn_boolean_t *left_chan if (left_changes) *left_changes = FALSE; - /* Need info for not_present node? */ - if (SVN_IS_VALID_REVNUM(not_present_rev)) - SVN_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, local_relpath, - scratch_pool, scratch_pool)); - if (destroy_wc && (!destroy_changes || *local_relpath == '\0')) { @@ -7625,26 +7703,6 @@ remove_node_txn(svn_boolean_t *left_chan local_relpath)); SVN_ERR(svn_sqlite__step_done(stmt)); - /* Should we leave a not-present node? */ - if (SVN_IS_VALID_REVNUM(not_present_rev)) - { - insert_base_baton_t ibb; - blank_ibb(&ibb); - - ibb.repos_id = repos_id; - - SVN_ERR_ASSERT(not_present_status == svn_wc__db_status_not_present - || not_present_status == svn_wc__db_status_excluded); - - ibb.status = not_present_status; - ibb.kind = not_present_kind; - - ibb.repos_relpath = repos_relpath; - ibb.revision = not_present_rev; - - SVN_ERR(insert_base_node(&ibb, wcroot, local_relpath, scratch_pool)); - } - SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool)); if (conflict) SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath, @@ -7659,9 +7717,6 @@ svn_wc__db_op_remove_node(svn_boolean_t const char *local_abspath, svn_boolean_t destroy_wc, svn_boolean_t destroy_changes, - svn_revnum_t not_present_revision, - svn_wc__db_status_t not_present_status, - svn_node_kind_t not_present_kind, const svn_skel_t *conflict, const svn_skel_t *work_items, svn_cancel_func_t cancel_func, @@ -7680,8 +7735,7 @@ svn_wc__db_op_remove_node(svn_boolean_t SVN_WC__DB_WITH_TXN(remove_node_txn(left_changes, wcroot, local_relpath, db, destroy_wc, destroy_changes, - not_present_revision, not_present_status, - not_present_kind, conflict, work_items, + conflict, work_items, cancel_func, cancel_baton, scratch_pool), wcroot); @@ -9008,10 +9062,8 @@ read_info(svn_wc__db_status_t *status, err = svn_error_compose_create(err, svn_sqlite__reset(stmt_act)); if (err && err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND) - err = svn_error_quick_wrap(err, - apr_psprintf(scratch_pool, - _("Error reading node '%s'"), - local_relpath)); + err = svn_error_quick_wrapf(err, _("Error reading node '%s'"), + local_relpath); SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt_info))); @@ -9131,6 +9183,27 @@ is_wclocked(svn_boolean_t *locked, const char *dir_relpath, apr_pool_t *scratch_pool); +/* Helper for read_children_info and single variant */ +static svn_error_t * +find_conflict_descendants(svn_boolean_t *conflict_exists, + svn_wc__db_wcroot_t *wcroot, + const char *local_relpath, + apr_pool_t *scratch_pool) +{ + svn_sqlite__stmt_t *stmt; + + /* Only used on files, so certainly not wcroot*/ + assert(local_relpath[0] != '\0'); + + SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, + STMT_FIND_CONFLICT_DESCENDANT)); + + SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath)); + SVN_ERR(svn_sqlite__step(conflict_exists, stmt)); + + return svn_error_trace(svn_sqlite__reset(stmt)); +} + /* What we really want to store about a node. This relies on the offset of svn_wc__db_info_t being zero. */ struct read_children_info_item_t @@ -9138,6 +9211,7 @@ struct read_children_info_item_t struct svn_wc__db_info_t info; int op_depth; int nr_layers; + svn_boolean_t was_dir; }; /* Implementation of svn_wc__db_read_children_info */ @@ -9187,7 +9261,7 @@ read_children_info(svn_wc__db_wcroot_t * op_depth = svn_sqlite__column_int(stmt, 0); /* Do we have new or better information? */ - if (new_child || op_depth > child_item->op_depth) + if (new_child) { struct svn_wc__db_info_t *child = &child_item->info; child_item->op_depth = op_depth; @@ -9268,6 +9342,8 @@ read_children_info(svn_wc__db_wcroot_t * child->depth = svn_depth_unknown; else { + child->has_descendants = TRUE; + child_item->was_dir = TRUE; child->depth = svn_sqlite__column_token_null(stmt, 11, depth_map, svn_depth_unknown); if (new_child) @@ -9310,6 +9386,17 @@ read_children_info(svn_wc__db_wcroot_t * if (new_child) svn_hash_sets(nodes, apr_pstrdup(result_pool, name), child); } + else if (!child_item->was_dir + && svn_sqlite__column_token(stmt, 4, kind_map) == svn_node_dir) + { + child_item->was_dir = TRUE; + + err = find_conflict_descendants(&child_item->info.has_descendants, + wcroot, child_relpath, + scratch_pool); + if (err) + SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt))); + } if (op_depth == 0) { @@ -9347,8 +9434,8 @@ read_children_info(svn_wc__db_wcroot_t * moved_to_relpath, result_pool); - shadow_op_relpath = svn_relpath_limit(child_relpath, op_depth, - scratch_pool); + shadow_op_relpath = svn_relpath_prefix(child_relpath, op_depth, + scratch_pool); moved_to->shadow_op_root_abspath = svn_dirent_join(wcroot->abspath, shadow_op_relpath, @@ -9537,8 +9624,8 @@ read_single_info(const struct svn_wc__db moved_to_relpath, result_pool); - cur_relpath = svn_relpath_limit(local_relpath, op_depth, - scratch_pool); + cur_relpath = svn_relpath_prefix(local_relpath, op_depth, + scratch_pool); move->shadow_op_root_abspath = svn_dirent_join(wcroot->abspath, cur_relpath, @@ -9618,6 +9705,12 @@ read_single_info(const struct svn_wc__db if (!base_tree_only && mtb->kind == svn_node_dir) SVN_ERR(is_wclocked(&mtb->locked, wcroot, local_relpath, scratch_pool)); + if (mtb->kind == svn_node_dir) + mtb->has_descendants = TRUE; + else + SVN_ERR(find_conflict_descendants(&mtb->has_descendants, + wcroot, local_relpath, scratch_pool)); + *info = mtb; return SVN_NO_ERROR; @@ -9812,7 +9905,7 @@ svn_wc__db_read_pristine_info(svn_wc__db } svn_error_t * -svn_wc__db_read_children_walker_info(apr_hash_t **nodes, +svn_wc__db_read_children_walker_info(const apr_array_header_t **items, svn_wc__db_t *db, const char *dir_abspath, apr_pool_t *result_pool, @@ -9822,6 +9915,7 @@ svn_wc__db_read_children_walker_info(apr const char *dir_relpath; svn_sqlite__stmt_t *stmt; svn_boolean_t have_row; + apr_array_header_t *nodes; SVN_ERR_ASSERT(svn_dirent_is_absolute(dir_abspath)); @@ -9835,16 +9929,18 @@ svn_wc__db_read_children_walker_info(apr SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, dir_relpath)); SVN_ERR(svn_sqlite__step(&have_row, stmt)); - *nodes = apr_hash_make(result_pool); + nodes = apr_array_make(result_pool, 16, + sizeof(struct svn_wc__db_walker_info_t *)); while (have_row) { struct svn_wc__db_walker_info_t *child; const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL); - const char *name = svn_relpath_basename(child_relpath, NULL); + const char *name = svn_relpath_basename(child_relpath, result_pool); int op_depth = svn_sqlite__column_int(stmt, 1); svn_error_t *err; child = apr_palloc(result_pool, sizeof(*child)); + child->name = name; child->status = svn_sqlite__column_token(stmt, 2, presence_map); if (op_depth > 0) { @@ -9853,13 +9949,16 @@ svn_wc__db_read_children_walker_info(apr SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt))); } child->kind = svn_sqlite__column_token(stmt, 3, kind_map); - svn_hash_sets(*nodes, apr_pstrdup(result_pool, name), child); + + APR_ARRAY_PUSH(nodes, struct svn_wc__db_walker_info_t *) = child; SVN_ERR(svn_sqlite__step(&have_row, stmt)); } SVN_ERR(svn_sqlite__reset(stmt)); + *items = nodes; + return SVN_NO_ERROR; } @@ -9943,68 +10042,48 @@ svn_wc__db_read_node_install_info(const -/* The body of svn_wc__db_read_url(). +/* The body of svn_wc__db_read_repos_info(). */ static svn_error_t * -read_url_txn(const char **url, - svn_wc__db_wcroot_t *wcroot, - const char *local_relpath, - apr_pool_t *result_pool, - apr_pool_t *scratch_pool) +db_read_repos_info(svn_revnum_t *revision, + const char **repos_relpath, + apr_int64_t *repos_id, + svn_wc__db_wcroot_t *wcroot, + const char *local_relpath, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) { svn_wc__db_status_t status; - const char *repos_relpath; - const char *repos_root_url; - apr_int64_t repos_id; - svn_boolean_t have_base; - SVN_ERR(read_info(&status, NULL, NULL, &repos_relpath, &repos_id, NULL, + SVN_ERR(read_info(&status, NULL, revision, repos_relpath, repos_id, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - &have_base, NULL, NULL, - wcroot, local_relpath, scratch_pool, scratch_pool)); + NULL, NULL, NULL, + wcroot, local_relpath, result_pool, scratch_pool)); - if (repos_relpath == NULL) + if ((repos_relpath && !*repos_relpath) + || (repos_id && *repos_id == INVALID_REPOS_ID)) { if (status == svn_wc__db_status_added) { - SVN_ERR(scan_addition(NULL, NULL, &repos_relpath, &repos_id, NULL, + SVN_ERR(scan_addition(NULL, NULL, repos_relpath, repos_id, NULL, NULL, NULL, NULL, NULL, NULL, wcroot, local_relpath, - scratch_pool, scratch_pool)); + result_pool, scratch_pool)); } else if (status == svn_wc__db_status_deleted) { const char *base_del_relpath; const char *work_del_relpath; - SVN_ERR(scan_deletion_txn(&base_del_relpath, NULL, - &work_del_relpath, - NULL, wcroot, - local_relpath, - scratch_pool, - scratch_pool)); - - if (base_del_relpath) - { - SVN_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, - base_del_relpath, - scratch_pool, - scratch_pool)); + SVN_ERR(scan_deletion(&base_del_relpath, NULL, + &work_del_relpath, + NULL, wcroot, + local_relpath, + scratch_pool, + scratch_pool)); - repos_relpath = svn_relpath_join( - repos_relpath, - svn_dirent_skip_ancestor(base_del_relpath, - local_relpath), - scratch_pool); - } - else + if (work_del_relpath) { /* The parent of the WORKING delete, must be an addition */ const char *work_relpath = NULL; @@ -10017,34 +10096,57 @@ read_url_txn(const char **url, work_relpath = svn_relpath_dirname(work_del_relpath, scratch_pool); - SVN_ERR(scan_addition(NULL, NULL, &repos_relpath, &repos_id, + SVN_ERR(scan_addition(NULL, NULL, repos_relpath, repos_id, NULL, NULL, NULL, NULL, NULL, NULL, wcroot, work_relpath, scratch_pool, scratch_pool)); - repos_relpath = svn_relpath_join( - repos_relpath, + if (repos_relpath) + *repos_relpath = svn_relpath_join( + *repos_relpath, svn_dirent_skip_ancestor(work_relpath, local_relpath), - scratch_pool); + result_pool); + } + else + { + SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, revision, + repos_relpath, + repos_id, + NULL, NULL, NULL, + NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + wcroot, + base_del_relpath, + scratch_pool, + scratch_pool)); + + if (repos_relpath) + *repos_relpath = svn_relpath_join( + *repos_relpath, + svn_dirent_skip_ancestor(base_del_relpath, + local_relpath), + result_pool); } } else if (status == svn_wc__db_status_excluded) { const char *parent_relpath; const char *name; - const char *url2; - /* Set 'url' to the *full URL* of the parent WC dir, - * and 'name' to the *single path component* that is the - * basename of this WC directory, so that joining them will result - * in the correct full URL. */ + /* A BASE excluded would have had repository information, so + we have a working exclude, which must be below an addition */ + svn_relpath_split(&parent_relpath, &name, local_relpath, scratch_pool); - SVN_ERR(read_url_txn(&url2, wcroot, parent_relpath, - scratch_pool, scratch_pool)); + SVN_ERR(scan_addition(NULL, NULL, repos_relpath, repos_id, NULL, + NULL, NULL, NULL, NULL, NULL, + wcroot, parent_relpath, + scratch_pool, scratch_pool)); - *url = svn_path_url_add_component2(url2, name, result_pool); + if (repos_relpath) + *repos_relpath = svn_relpath_join(*repos_relpath, name, + result_pool); return SVN_NO_ERROR; } @@ -10056,26 +10158,23 @@ read_url_txn(const char **url, } } - SVN_ERR(svn_wc__db_fetch_repos_info(&repos_root_url, NULL, wcroot, - repos_id, scratch_pool)); - - SVN_ERR_ASSERT(repos_root_url != NULL && repos_relpath != NULL); - *url = svn_path_url_add_component2(repos_root_url, repos_relpath, - result_pool); - return SVN_NO_ERROR; } svn_error_t * -svn_wc__db_read_url(const char **url, - svn_wc__db_t *db, - const char *local_abspath, - apr_pool_t *result_pool, - apr_pool_t *scratch_pool) +svn_wc__db_read_repos_info(svn_revnum_t *revision, + const char **repos_relpath, + const char **repos_root_url, + const char **repos_uuid, + 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; + apr_int64_t repos_id = INVALID_REPOS_ID; SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath)); @@ -10084,9 +10183,17 @@ svn_wc__db_read_url(const char **url, scratch_pool, scratch_pool)); VERIFY_USABLE_WCROOT(wcroot); - SVN_WC__DB_WITH_TXN(read_url_txn(url, wcroot, local_relpath, - result_pool, scratch_pool), - wcroot); + SVN_WC__DB_WITH_TXN4(db_read_repos_info(revision, repos_relpath, + (repos_root_url || repos_uuid) + ? &repos_id : NULL, + wcroot, local_relpath, + result_pool, scratch_pool), + svn_wc__db_fetch_repos_info(repos_root_url, + repos_uuid, + wcroot, repos_id, + result_pool), + SVN_NO_ERROR, SVN_NO_ERROR, + wcroot); return SVN_NO_ERROR; } @@ -10928,19 +11035,45 @@ svn_wc__db_read_children_of_working_node scratch_pool, scratch_pool)); VERIFY_USABLE_WCROOT(wcroot); - return gather_children2(children, wcroot, local_relpath, - result_pool, scratch_pool); + return svn_error_trace( + gather_children(children, wcroot, local_relpath, + STMT_SELECT_WORKING_CHILDREN, -1, + result_pool, scratch_pool)); } -/* Helper for svn_wc__db_node_check_replace(). - */ -static svn_error_t * -check_replace_txn(svn_boolean_t *is_replace_root_p, - svn_boolean_t *base_replace_p, - svn_boolean_t *is_replace_p, - svn_wc__db_wcroot_t *wcroot, - const char *local_relpath, - apr_pool_t *scratch_pool) +svn_error_t * +svn_wc__db_base_read_not_present_children( + const apr_array_header_t **children, + 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); + + return svn_error_trace( + gather_children(children, wcroot, local_relpath, + STMT_SELECT_BASE_NOT_PRESENT_CHILDREN, -1, + result_pool, scratch_pool)); +} + +/* Helper for svn_wc__db_node_check_replace(). + */ +static svn_error_t * +check_replace_txn(svn_boolean_t *is_replace_root_p, + svn_boolean_t *base_replace_p, + svn_boolean_t *is_replace_p, + svn_wc__db_wcroot_t *wcroot, + const char *local_relpath, + apr_pool_t *scratch_pool) { svn_sqlite__stmt_t *stmt; svn_boolean_t have_row; @@ -11116,6 +11249,7 @@ svn_wc__db_read_children(const apr_array VERIFY_USABLE_WCROOT(wcroot); return gather_children(children, wcroot, local_relpath, + STMT_SELECT_NODE_CHILDREN, -1, result_pool, scratch_pool); } @@ -11174,11 +11308,11 @@ relocate_txn(svn_wc__db_wcroot_t *wcroot { const char *work_del_relpath; - SVN_ERR(scan_deletion_txn(NULL, NULL, - &work_del_relpath, NULL, - wcroot, local_dir_relpath, - scratch_pool, - scratch_pool)); + SVN_ERR(scan_deletion(NULL, NULL, + &work_del_relpath, NULL, + wcroot, local_dir_relpath, + scratch_pool, + scratch_pool)); if (work_del_relpath) { /* Deleted within a copy/move */ @@ -11266,90 +11400,100 @@ 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)); - 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. - /* 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); + 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!) */ - /* 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_relpath_split(&parent_relpath, &name, local_relpath, + scratch_pool); - *repos_relpath = svn_relpath_join(repos_parent_relpath, name, result_pool); + SVN_ERR(determine_commit_repos_info(repos_id, &parent_repos_relpath, + wcroot, parent_relpath, + scratch_pool, scratch_pool)); - return SVN_NO_ERROR; -} + *repos_relpath = svn_relpath_join(parent_repos_relpath, name, + result_pool); + return SVN_NO_ERROR; + } + } -/* Helper for svn_wc__db_global_commit() - Makes local_relpath and all its descendants at the same op-depth represent - the copy origin repos_id:repos_relpath@revision. + SVN_ERR_ASSERT(op_depth == 0); /* And that row must be BASE */ - This code is only valid to fix-up a move from an old location, to a new - location during a commit. + SVN_ERR_ASSERT(!svn_sqlite__column_is_null(stmt, 1)); + SVN_ERR_ASSERT(!svn_sqlite__column_is_null(stmt, 2)); + + *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)); +} - Assumptions: - * local_relpath is not the working copy root (can't be moved) - * repos_relpath is not the repository root (can't be moved) - */ static svn_error_t * -moved_descendant_commit(svn_wc__db_wcroot_t *wcroot, +moved_descendant_collect(apr_hash_t **map, + svn_wc__db_wcroot_t *wcroot, const char *local_relpath, int op_depth, - apr_int64_t repos_id, - const char *repos_relpath, - svn_revnum_t revision, + apr_pool_t *result_pool, apr_pool_t *scratch_pool) { - apr_hash_t *children; - apr_pool_t *iterpool; svn_sqlite__stmt_t *stmt; svn_boolean_t have_row; - apr_hash_index_t *hi; - SVN_ERR_ASSERT(*local_relpath != '\0' - && *repos_relpath != '\0'); + *map = NULL; SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_SELECT_MOVED_DESCENDANTS_SRC)); @@ -11361,21 +11505,56 @@ moved_descendant_commit(svn_wc__db_wcroo if (! have_row) return svn_error_trace(svn_sqlite__reset(stmt)); - children = apr_hash_make(scratch_pool); - - /* First, obtain all moved descendants */ - /* To keep error handling simple, first cache them in a hashtable */ + /* Find all moved descendants. Key them on target, because that is + always unique */ while (have_row) { - const char *src_relpath = svn_sqlite__column_text(stmt, 1, scratch_pool); - const char *to_relpath = svn_sqlite__column_text(stmt, 4, scratch_pool); + const char *src_relpath = svn_sqlite__column_text(stmt, 1, result_pool); + const char *to_relpath = svn_sqlite__column_text(stmt, 4, result_pool); + + if (!*map) + *map = apr_hash_make(result_pool); - svn_hash_sets(children, src_relpath, to_relpath); + svn_hash_sets(*map, to_relpath, src_relpath); SVN_ERR(svn_sqlite__step(&have_row, stmt)); } SVN_ERR(svn_sqlite__reset(stmt)); + return SVN_NO_ERROR; +} + +/* Helper for svn_wc__db_global_commit() + + Makes local_relpath and all its descendants at the same op-depth represent + the copy origin repos_id:repos_relpath@revision. + + This code is only valid to fix-up a move from an old location, to a new + location during a commit. + + Assumptions: + * local_relpath is not the working copy root (can't be moved) + * repos_relpath is not the repository root (can't be moved) + */ +static svn_error_t * +moved_descendant_commit(svn_wc__db_wcroot_t *wcroot, + const char *local_relpath, + apr_int64_t repos_id, + const char *repos_relpath, + svn_revnum_t revision, + apr_hash_t *children, + apr_pool_t *scratch_pool) +{ + apr_pool_t *iterpool; + svn_sqlite__stmt_t *stmt; + apr_hash_index_t *hi; + + SVN_ERR_ASSERT(*local_relpath != '\0' + && *repos_relpath != '\0'); + + if (!children) + return SVN_NO_ERROR; + /* Then update them */ SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_COMMIT_UPDATE_ORIGIN)); @@ -11383,11 +11562,12 @@ moved_descendant_commit(svn_wc__db_wcroo iterpool = svn_pool_create(scratch_pool); for (hi = apr_hash_first(scratch_pool, children); hi; hi = apr_hash_next(hi)) { - const char *src_relpath = apr_hash_this_key(hi); - const char *to_relpath = apr_hash_this_val(hi); + const char *src_relpath = apr_hash_this_val(hi); + const char *to_relpath = apr_hash_this_key(hi); const char *new_repos_relpath; int to_op_depth = relpath_depth(to_relpath); int affected; + apr_hash_t *map; svn_pool_clear(iterpool); @@ -11414,9 +11594,11 @@ moved_descendant_commit(svn_wc__db_wcroo SVN_ERR_ASSERT(affected >= 1); /* If this fails there is no move dest */ #endif - SVN_ERR(moved_descendant_commit(wcroot, to_relpath, to_op_depth, + SVN_ERR(moved_descendant_collect(&map, wcroot, to_relpath, to_op_depth, + iterpool, iterpool)); + SVN_ERR(moved_descendant_commit(wcroot, to_relpath, repos_id, new_repos_relpath, revision, - iterpool)); + map, iterpool)); } svn_pool_destroy(iterpool); @@ -11475,7 +11657,6 @@ commit_node(svn_wc__db_wcroot_t *wcroot, apr_time_t changed_date, const char *changed_author, const svn_checksum_t *new_checksum, - const apr_array_header_t *new_children, apr_hash_t *new_dav_cache, svn_boolean_t keep_changelist, svn_boolean_t no_unlock,
[... 1601 lines stripped ...]
