Author: rhuijben Date: Wed Feb 18 22:15:50 2015 New Revision: 1660742 URL: http://svn.apache.org/r1660742 Log: Fix svn_wc__db_op_make_copy_internal()'s handling of mixed revision BASE trees.
This fixes some issues around tree conflict handling, and improves database consistency. * build/transform_sql.py (process_file): Allow passing columns and binding variables as argument of IS_STRICT_DESCENDANT_OF, like the other macros. * subversion/libsvn_wc/wc-checks.sql (STMT_STATIC_VERIFY): Add documentation to the older statements. Extend. * subversion/libsvn_wc/wc-queries.sql (STMT_DELETE_WORKING_BASE_DELETE): Split into non-recursive... (STMT_DELETE_WORKING_BASE_DELETE_RECURSIVE): ... and recursive variants. (STMT_INSERT_WORKING_NODE_FROM_BASE_COPY): Allow replacing nodes, and handle moved_to when doing that. * subversion/libsvn_wc/wc_db.c (db_base_remove): Update caller. (make_copy_txn): Add recursion arguments to determine when a new op-depth is needed. Simplify code, by moving the initial shadowing to the calling function. (make_copy_move_moved_to): New function. (svn_wc__db_op_make_copy_internal): Update caller. Shadow all nodes with base-deleted before calling make_copy_txn. Move moved-to information to this layer. * subversion/tests/cmdline/tree_conflict_tests.py (update_delete_mixed_rev): Remove XFail. Tweak status value. * subversion/tests/libsvn_wc/op-depth-test.c (make_copy_mixed): New function. Tests svn_wc__db_op_make_copy_internal. make_copy_and_delete_mixed): New functions. Tests svn_wc__db_base_remove and shows an existing issue with move handling on base-delete. (test_list): Add new items. Modified: subversion/trunk/build/transform_sql.py subversion/trunk/subversion/libsvn_wc/wc-checks.sql subversion/trunk/subversion/libsvn_wc/wc-queries.sql subversion/trunk/subversion/libsvn_wc/wc_db.c subversion/trunk/subversion/tests/cmdline/tree_conflict_tests.py subversion/trunk/subversion/tests/libsvn_wc/op-depth-test.c Modified: subversion/trunk/build/transform_sql.py URL: http://svn.apache.org/viewvc/subversion/trunk/build/transform_sql.py?rev=1660742&r1=1660741&r2=1660742&view=diff ============================================================================== --- subversion/trunk/build/transform_sql.py (original) +++ subversion/trunk/build/transform_sql.py Wed Feb 18 22:15:50 2015 @@ -140,7 +140,7 @@ class Processor(object): # '/'+1 == '0' line = re.sub( - r'IS_STRICT_DESCENDANT_OF[(]([A-Za-z_.]+), ([?][0-9]+)[)]', + r'IS_STRICT_DESCENDANT_OF[(]([?]?[A-Za-z0-9_.]+), ([?]?[A-Za-z0-9_.]+)[)]', r"(((\1) > (CASE (\2) WHEN '' THEN '' ELSE (\2) || '/' END))" + r" AND ((\1) < CASE (\2) WHEN '' THEN X'FFFF' ELSE (\2) || '0' END))", line) Modified: subversion/trunk/subversion/libsvn_wc/wc-checks.sql URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_wc/wc-checks.sql?rev=1660742&r1=1660741&r2=1660742&view=diff ============================================================================== --- subversion/trunk/subversion/libsvn_wc/wc-checks.sql (original) +++ subversion/trunk/subversion/libsvn_wc/wc-checks.sql Wed Feb 18 22:15:50 2015 @@ -76,6 +76,8 @@ BEGIN END; -- STMT_STATIC_VERIFY +/* 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, 'SV001: No ancestor in NODES' FROM nodes n WHERE local_relpath != '' AND file_external IS NULL @@ -86,6 +88,8 @@ FROM nodes n WHERE local_relpath != '' 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, 'SV002: No ancestor in ACTUAL' FROM actual_node a WHERE local_relpath != '' AND NOT EXISTS(SELECT 1 from nodes i @@ -96,7 +100,8 @@ FROM actual_node a WHERE local_relpath ! 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, 'SV003: 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 @@ -111,7 +116,8 @@ WHERE (a.properties IS NOT NULL AND i.local_relpath=a.parent_relpath) 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, 'SV004: Unneeded node data' FROM nodes WHERE presence NOT IN (MAP_NORMAL, MAP_INCOMPLETE) @@ -129,7 +135,8 @@ AND (properties 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, 'SV005: Unneeded base-deleted node data' FROM nodes WHERE presence IN (MAP_BASE_DELETED) @@ -138,7 +145,7 @@ AND (repos_id 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, 'SV006: Kind specific data invalid on normal' FROM nodes WHERE presence IN (MAP_NORMAL, MAP_INCOMPLETE) @@ -157,7 +164,8 @@ AND (kind 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, 'SV007: Invalid op-depth for local add' FROM nodes WHERE presence IN (MAP_NORMAL, MAP_INCOMPLETE) @@ -165,7 +173,9 @@ WHERE presence IN (MAP_NORMAL, MAP_INCOM 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, 'SV008: Node missing ancestor' FROM nodes n WHERE op_depth < relpath_depth(local_relpath) @@ -174,11 +184,12 @@ WHERE op_depth < relpath_depth(local_rel 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 = MAP_BASE_DELETED + OR (p.presence IN (MAP_BASE_DELETED, MAP_NOT_PRESENT) AND n.presence = MAP_BASE_DELETED))) -UNION all - +UNION ALL +/* Present op-depth descendants have the repository location implied by their + ancestor */ SELECT n.local_relpath, n.op_depth, 'SV009: Copied descendant mismatch' FROM nodes n JOIN nodes p @@ -188,11 +199,65 @@ WHERE n.op_depth > 0 AND n.presence IN ( 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) - -UNION all + OR n.revision != p.revision + OR p.kind != MAP_DIR + OR n.moved_here != 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, 'SV010: 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, n.op_depth, 'SV011: 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, 'SV012: 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 NOT IN (MAP_NORMAL, MAP_INCOMPLETE) + +UNION ALL + +SELECT d.local_relpath, d.op_depth, 'SV013: Moved here without origin' +FROM nodes d +WHERE d.op_depth = relpath_depth(d.local_relpath) + AND d.moved_here = 1 + AND NOT EXISTS(SELECT 1 FROM nodes s + WHERE s.wc_id = d.wc_id AND s.moved_to = d.local_relpath) + +UNION ALL + +SELECT s.local_relpath, s.op_depth, 'SV014: 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.moved_here =1 AND d.repos_path IS NOT NULL) \ No newline at end of file Modified: subversion/trunk/subversion/libsvn_wc/wc-queries.sql URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_wc/wc-queries.sql?rev=1660742&r1=1660741&r2=1660742&view=diff ============================================================================== --- subversion/trunk/subversion/libsvn_wc/wc-queries.sql (original) +++ subversion/trunk/subversion/libsvn_wc/wc-queries.sql Wed Feb 18 22:15:50 2015 @@ -233,6 +233,16 @@ WHERE wc_id = ?1 AND IS_STRICT_DESCENDAN -- STMT_DELETE_WORKING_BASE_DELETE DELETE FROM nodes +WHERE wc_id = ?1 AND local_relpath = ?2 + AND presence = MAP_BASE_DELETED + AND op_depth > ?3 + AND op_depth = (SELECT MIN(op_depth) FROM nodes n + WHERE n.wc_id = ?1 + AND n.local_relpath = nodes.local_relpath + AND op_depth > ?3) + +-- STMT_DELETE_WORKING_BASE_DELETE_RECURSIVE +DELETE FROM nodes WHERE wc_id = ?1 AND IS_STRICT_DESCENDANT_OF(local_relpath, ?2) AND presence = MAP_BASE_DELETED AND op_depth > ?3 @@ -1023,15 +1033,17 @@ WHERE wc_id = ?1 ORDER BY local_relpath -- STMT_INSERT_WORKING_NODE_FROM_BASE_COPY -INSERT INTO nodes ( +INSERT OR REPLACE INTO nodes ( wc_id, local_relpath, op_depth, parent_relpath, repos_id, repos_path, revision, presence, depth, kind, changed_revision, changed_date, changed_author, checksum, properties, translated_size, last_mod_time, - symlink_target ) + symlink_target, moved_to ) SELECT wc_id, local_relpath, ?3 /*op_depth*/, parent_relpath, repos_id, repos_path, revision, presence, depth, kind, changed_revision, changed_date, changed_author, checksum, properties, translated_size, - last_mod_time, symlink_target + last_mod_time, symlink_target, + (SELECT moved_to FROM nodes + WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth = ?3) moved_to FROM nodes WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth = 0 Modified: subversion/trunk/subversion/libsvn_wc/wc_db.c URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_wc/wc_db.c?rev=1660742&r1=1660741&r2=1660742&view=diff ============================================================================== --- subversion/trunk/subversion/libsvn_wc/wc_db.c (original) +++ subversion/trunk/subversion/libsvn_wc/wc_db.c Wed Feb 18 22:15:50 2015 @@ -2410,8 +2410,9 @@ db_base_remove(svn_wc__db_wcroot_t *wcro } if (keep_working) { - SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, - STMT_DELETE_WORKING_BASE_DELETE)); + 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)); } @@ -15010,95 +15011,163 @@ svn_wc__db_temp_op_start_directory_updat static svn_error_t * make_copy_txn(svn_wc__db_wcroot_t *wcroot, const char *local_relpath, - int op_depth, + apr_int64_t last_repos_id, + const char *last_repos_relpath, + svn_revnum_t last_revision, + int last_op_depth, + svn_boolean_t shadowed, apr_pool_t *scratch_pool) { svn_sqlite__stmt_t *stmt; - svn_boolean_t have_row; - svn_boolean_t add_working_base_deleted = FALSE; - svn_boolean_t remove_working = FALSE; - const apr_array_header_t *children; - apr_pool_t *iterpool = svn_pool_create(scratch_pool); - int i; - - SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, - STMT_SELECT_LOWEST_WORKING_NODE)); - SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath, 0)); - SVN_ERR(svn_sqlite__step(&have_row, stmt)); + svn_boolean_t have_row = FALSE; + svn_revnum_t revision; + apr_int64_t repos_id; + const char *repos_relpath; + svn_node_kind_t kind; + int op_depth = relpath_depth(local_relpath); - if (have_row) + if (last_op_depth != op_depth) { - svn_wc__db_status_t working_status; - int working_op_depth; - - working_status = svn_sqlite__column_token(stmt, 1, presence_map); - working_op_depth = svn_sqlite__column_int(stmt, 0); + SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, + STMT_SELECT_DEPTH_NODE)); + SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath, + op_depth)); + SVN_ERR(svn_sqlite__step(&have_row, stmt)); SVN_ERR(svn_sqlite__reset(stmt)); + if (have_row) + shadowed = TRUE; + } - SVN_ERR_ASSERT(working_status == svn_wc__db_status_normal - || working_status == svn_wc__db_status_base_deleted - || working_status == svn_wc__db_status_not_present - || working_status == svn_wc__db_status_incomplete); - - /* Only change nodes in the layers where we are creating the copy. - Deletes in higher layers will just apply to the copy */ - if (working_op_depth <= op_depth) - { - add_working_base_deleted = TRUE; + SVN_ERR(svn_wc__db_base_get_info_internal(NULL, &kind, &revision, + &repos_relpath, &repos_id, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, + wcroot, local_relpath, + scratch_pool, scratch_pool)); - if (working_status == svn_wc__db_status_base_deleted) - remove_working = TRUE; - } + if (last_repos_relpath + && repos_id == last_repos_id + && revision == last_revision) + { + const char *name = svn_relpath_skip_ancestor(last_repos_relpath, + repos_relpath); + + if (strcmp(name, svn_relpath_basename(local_relpath, NULL)) == 0) + op_depth = last_op_depth; } - else - SVN_ERR(svn_sqlite__reset(stmt)); - if (remove_working) + /* Insert a not-present node to mark that we don't know what exists + here */ + if (last_op_depth > 0 && last_op_depth != op_depth) { - SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, - STMT_DELETE_LOWEST_WORKING_NODE)); - SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath)); - SVN_ERR(svn_sqlite__step_done(stmt)); + insert_working_baton_t iwb; + + blank_iwb(&iwb); + iwb.presence = svn_wc__db_status_not_present; + iwb.op_depth = last_op_depth; + + iwb.original_repos_id = repos_id; + iwb.original_repos_relpath = repos_relpath; + iwb.original_revnum = revision; + iwb.kind = kind; + + SVN_ERR(insert_working_node(&iwb, wcroot, local_relpath, scratch_pool)); } - if (add_working_base_deleted) + /* Can we add a new copy node at the wanted op-depth? */ + if (!have_row || op_depth == last_op_depth) { - SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, - STMT_INSERT_DELETE_FROM_BASE)); + int i; + + SVN_ERR(svn_sqlite__get_statement( + &stmt, wcroot->sdb, + STMT_INSERT_WORKING_NODE_FROM_BASE_COPY)); SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath, op_depth)); SVN_ERR(svn_sqlite__step_done(stmt)); + + if (shadowed) + SVN_ERR(db_extend_parent_delete(wcroot, local_relpath, kind, + op_depth, scratch_pool)); + + if (kind == svn_node_dir) + { + const apr_array_header_t *children; + apr_pool_t *iterpool = svn_pool_create(scratch_pool); + + SVN_ERR(gather_repo_children(&children, wcroot, local_relpath, + 0, scratch_pool, iterpool)); + + for (i = 0; i < children->nelts; i++) + { + const char *name = APR_ARRAY_IDX(children, i, const char *); + const char *copy_relpath; + + svn_pool_clear(iterpool); + + copy_relpath = svn_relpath_join(local_relpath, name, iterpool); + + SVN_ERR(make_copy_txn(wcroot, copy_relpath, + repos_id, repos_relpath, revision, + op_depth, shadowed, scratch_pool)); + } + svn_pool_destroy(iterpool); + } } else { + /* Auch... we can't make a copy of whatever comes deeper, as this + op-depth is already filled by something else. Let's hope + the user doesn't mind. + + Luckily we know that the moves are already moved to the shadowing + layer, so we can just remove dangling base-deletes if there are + any. + */ + /* BASE_DELETED may be at op_depth, so let's use last_op_depth! */ + SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, - STMT_INSERT_WORKING_NODE_FROM_BASE_COPY)); + STMT_DELETE_WORKING_BASE_DELETE)); SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath, - op_depth)); + last_op_depth)); + SVN_ERR(svn_sqlite__step_done(stmt)); + 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, + last_op_depth)); SVN_ERR(svn_sqlite__step_done(stmt)); } - /* Get the BASE children, as WORKING children don't need modifications */ - SVN_ERR(gather_repo_children(&children, wcroot, local_relpath, - 0, scratch_pool, iterpool)); - - for (i = 0; i < children->nelts; i++) - { - const char *name = APR_ARRAY_IDX(children, i, const char *); - const char *copy_relpath; - - svn_pool_clear(iterpool); + return SVN_NO_ERROR; +} - copy_relpath = svn_relpath_join(local_relpath, name, iterpool); +/* Helper for svn_wc__db_op_make_copy_internal */ +static svn_error_t * +make_copy_move_moved_to(svn_wc__db_wcroot_t *wcroot, + const char *local_relpath, + int cur_op_depth, + int new_op_depth, + const char *moved_to, + apr_pool_t *scratch_pool) +{ + svn_sqlite__stmt_t *stmt; - SVN_ERR(make_copy_txn(wcroot, copy_relpath, op_depth, iterpool)); - } + SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, + STMT_UPDATE_MOVED_TO_RELPATH)); + SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, + local_relpath, cur_op_depth)); + SVN_ERR(svn_sqlite__step_done(stmt)); - svn_pool_destroy(iterpool); + SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, + STMT_UPDATE_MOVED_TO_RELPATH)); + SVN_ERR(svn_sqlite__bindf(stmt, "isds", wcroot->wc_id, + local_relpath, new_op_depth, moved_to)); + SVN_ERR(svn_sqlite__step_done(stmt)); return SVN_NO_ERROR; } + svn_error_t * svn_wc__db_op_make_copy_internal(svn_wc__db_wcroot_t *wcroot, const char *local_relpath, @@ -15140,12 +15209,56 @@ svn_wc__db_op_make_copy_internal(svn_wc_ } else { + apr_pool_t *iterpool = svn_pool_create(scratch_pool); + svn_error_t *err = NULL; + int affected_rows; + + op_depth = relpath_depth(local_relpath); /* We don't allow copies to contain server-excluded nodes; the update editor is going to have to bail out. */ - SVN_ERR(catch_copy_of_server_excluded(wcroot, local_relpath, scratch_pool)); + SVN_ERR(catch_copy_of_server_excluded(wcroot, local_relpath, iterpool)); + + /* Insert a shadowing layer */ + SVN_ERR(svn_sqlite__get_statement( + &stmt, wcroot->sdb, + STMT_INSERT_DELETE_FROM_NODE_RECURSIVE)); + + /* As we are keeping whatever is below, move the*/ + + SVN_ERR(svn_sqlite__bindf(stmt, "isdd", + wcroot->wc_id, local_relpath, + 0, op_depth)); + SVN_ERR(svn_sqlite__update(&affected_rows, stmt)); + SVN_ERR_ASSERT(affected_rows > 0); + + SVN_ERR(svn_sqlite__get_statement( + &stmt, wcroot->sdb, + STMT_SELECT_MOVED_DESCENDANTS_SRC)); + SVN_ERR(svn_sqlite__bindf(stmt, "isd", + wcroot->wc_id, local_relpath, + op_depth)); + + SVN_ERR(svn_sqlite__step(&have_row, stmt)); + + while (have_row && !err) + { + err = make_copy_move_moved_to( + wcroot, + svn_sqlite__column_text(stmt, 1, iterpool), + svn_sqlite__column_int(stmt, 0), + op_depth, + svn_sqlite__column_text(stmt, 4, iterpool), + iterpool); + + SVN_ERR(svn_sqlite__step(&have_row, stmt)); + } + + SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt))); + SVN_ERR(make_copy_txn(wcroot, local_relpath, - relpath_depth(local_relpath), scratch_pool)); + INVALID_REPOS_ID, NULL, SVN_INVALID_REVNUM, + relpath_depth(local_relpath), FALSE, scratch_pool)); } if (conflicts) Modified: subversion/trunk/subversion/tests/cmdline/tree_conflict_tests.py URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/tests/cmdline/tree_conflict_tests.py?rev=1660742&r1=1660741&r2=1660742&view=diff ============================================================================== --- subversion/trunk/subversion/tests/cmdline/tree_conflict_tests.py (original) +++ subversion/trunk/subversion/tests/cmdline/tree_conflict_tests.py Wed Feb 18 22:15:50 2015 @@ -1449,7 +1449,6 @@ def update_dir_with_not_present(sbox): run_and_verify_svn(None, [], 'ci', '-m', '', wc_dir) -@XFail() def update_delete_mixed_rev(sbox): "update that deletes mixed-rev" @@ -1484,6 +1483,10 @@ def update_delete_mixed_rev(sbox): status='A ', copied='+', treeconflict='C', wc_rev='-') expected_status.tweak('A/B/F', 'A/B/E', 'A/B/E/beta', 'A/B/lambda', copied='+', wc_rev='-') + + # The entries world doesn't see a changed revision as another add + # while the WC-NG world does... + expected_status.tweak('A/B/E', status='A ', entry_status=' ') run_and_verify_update(wc_dir, expected_output, expected_disk, expected_status, None, None, None, None, None, 1, Modified: subversion/trunk/subversion/tests/libsvn_wc/op-depth-test.c URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/tests/libsvn_wc/op-depth-test.c?rev=1660742&r1=1660741&r2=1660742&view=diff ============================================================================== --- subversion/trunk/subversion/tests/libsvn_wc/op-depth-test.c (original) +++ subversion/trunk/subversion/tests/libsvn_wc/op-depth-test.c Wed Feb 18 22:15:50 2015 @@ -10993,6 +10993,334 @@ move_deep_bump(const svn_test_opts_t *op return SVN_NO_ERROR; } + +static svn_error_t * +make_copy_mixed(const svn_test_opts_t *opts, apr_pool_t *pool) +{ + svn_test__sandbox_t b; + + SVN_ERR(svn_test__sandbox_create(&b, "make_copy_mixed", opts, pool)); + + SVN_ERR(sbox_wc_mkdir(&b, "A")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B/C")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B/C/D")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B/C/E")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B/C/F")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B/G")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B/G/H")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B/G/I")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B/G/J")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B/K")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B/K/L")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B/K/M")); + SVN_ERR(sbox_wc_mkdir(&b, "A/N")); + SVN_ERR(sbox_wc_mkdir(&b, "A/N/O")); + SVN_ERR(sbox_wc_mkdir(&b, "A/N/P")); + SVN_ERR(sbox_wc_mkdir(&b, "A/N/Q")); + SVN_ERR(sbox_wc_mkdir(&b, "A/R")); + SVN_ERR(sbox_wc_mkdir(&b, "A/R/S")); + SVN_ERR(sbox_wc_mkdir(&b, "A/R/S/T")); + SVN_ERR(sbox_wc_commit(&b, "")); + SVN_ERR(sbox_wc_update(&b, "", 1)); + SVN_ERR(sbox_wc_propset(&b, "k", "r2", "")); + SVN_ERR(sbox_wc_commit(&b, "")); + SVN_ERR(sbox_wc_propset(&b, "k", "r3", "")); + SVN_ERR(sbox_wc_commit(&b, "")); + SVN_ERR(sbox_wc_propset(&b, "k", "r4", "")); + SVN_ERR(sbox_wc_commit(&b, "")); + SVN_ERR(sbox_wc_propset(&b, "k", "r5", "")); + SVN_ERR(sbox_wc_commit(&b, "")); + + SVN_ERR(sbox_wc_update(&b, "", 5)); + SVN_ERR(sbox_wc_update(&b, "A", 4)); + SVN_ERR(sbox_wc_update(&b, "A/B", 3)); + SVN_ERR(sbox_wc_update(&b, "A/B/C", 2)); + SVN_ERR(sbox_wc_update(&b, "A/B/K", 1)); + SVN_ERR(sbox_wc_update(&b, "A/N/O", 3)); + + SVN_ERR(sbox_wc_delete(&b, "A/B/C/F")); + SVN_ERR(sbox_wc_delete(&b, "A/B/G/J")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B/G/J")); + + SVN_ERR(sbox_wc_update(&b, "A/N/P", 1)); + SVN_ERR(sbox_wc_update(&b, "A/N/Q", 1)); + SVN_ERR(sbox_wc_delete(&b, "A/N/P")); + SVN_ERR(sbox_wc_mkdir(&b, "A/N/P")); + SVN_ERR(sbox_wc_move(&b, "A/N/Q", "Q")); + SVN_ERR(sbox_wc_move(&b, "A/B/G/H", "H")); + + /* And something that can't be represented */ + SVN_ERR(sbox_wc_update(&b, "A/B/C/E", 1)); + SVN_ERR(sbox_wc_move(&b, "A/B/C/E", "E")); + + { + nodes_row_t nodes[] = { + {0, "", "normal", 5, "", NOT_MOVED, "k"}, + {0, "A", "normal", 4, "A"}, + {0, "A/B", "normal", 3, "A/B"}, + {0, "A/B/C", "normal", 2, "A/B/C"}, + {0, "A/B/C/D", "normal", 2, "A/B/C/D"}, + {0, "A/B/C/E", "normal", 1, "A/B/C/E"}, + {0, "A/B/C/F", "normal", 2, "A/B/C/F"}, + {0, "A/B/G", "normal", 3, "A/B/G"}, + {0, "A/B/G/H", "normal", 3, "A/B/G/H"}, + {0, "A/B/G/I", "normal", 3, "A/B/G/I"}, + {0, "A/B/G/J", "normal", 3, "A/B/G/J"}, + {0, "A/B/K", "normal", 1, "A/B/K"}, + {0, "A/B/K/L", "normal", 1, "A/B/K/L"}, + {0, "A/B/K/M", "normal", 1, "A/B/K/M"}, + {0, "A/N", "normal", 4, "A/N"}, + {0, "A/N/O", "normal", 3, "A/N/O"}, + {0, "A/N/P", "normal", 1, "A/N/P"}, + {0, "A/N/Q", "normal", 1, "A/N/Q"}, + {0, "A/R", "normal", 4, "A/R"}, + {0, "A/R/S", "normal", 4, "A/R/S"}, + {0, "A/R/S/T", "normal", 4, "A/R/S/T"}, + {1, "E", "normal", 1, "A/B/C/E", MOVED_HERE}, + {1, "H", "normal", 3, "A/B/G/H", MOVED_HERE}, + {1, "Q", "normal", 1, "A/N/Q", MOVED_HERE}, + {3, "A/N/P", "normal", NO_COPY_FROM}, + {3, "A/N/Q", "base-deleted", NO_COPY_FROM, "Q"}, + {4, "A/B/C/E", "base-deleted", NO_COPY_FROM, "E"}, + {4, "A/B/C/F", "base-deleted", NO_COPY_FROM}, + {4, "A/B/G/H", "base-deleted", NO_COPY_FROM, "H"}, + {4, "A/B/G/J", "normal", NO_COPY_FROM}, + + {0} + }; + + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + SVN_ERR(svn_wc__db_op_make_copy(b.wc_ctx->db, sbox_wc_path(&b, "A"), + NULL, NULL, pool)); + + { + nodes_row_t nodes[] = { + {0, "", "normal", 5, "", NOT_MOVED, "k"}, + {0, "A", "normal", 4, "A"}, + {0, "A/B", "normal", 3, "A/B"}, + {0, "A/B/C", "normal", 2, "A/B/C"}, + {0, "A/B/C/D", "normal", 2, "A/B/C/D"}, + {0, "A/B/C/E", "normal", 1, "A/B/C/E"}, + {0, "A/B/C/F", "normal", 2, "A/B/C/F"}, + {0, "A/B/G", "normal", 3, "A/B/G"}, + {0, "A/B/G/H", "normal", 3, "A/B/G/H"}, + {0, "A/B/G/I", "normal", 3, "A/B/G/I"}, + {0, "A/B/G/J", "normal", 3, "A/B/G/J"}, + {0, "A/B/K", "normal", 1, "A/B/K"}, + {0, "A/B/K/L", "normal", 1, "A/B/K/L"}, + {0, "A/B/K/M", "normal", 1, "A/B/K/M"}, + {0, "A/N", "normal", 4, "A/N"}, + {0, "A/N/O", "normal", 3, "A/N/O"}, + {0, "A/N/P", "normal", 1, "A/N/P"}, + {0, "A/N/Q", "normal", 1, "A/N/Q"}, + {0, "A/R", "normal", 4, "A/R"}, + {0, "A/R/S", "normal", 4, "A/R/S"}, + {0, "A/R/S/T", "normal", 4, "A/R/S/T"}, + {1, "A", "normal", 4, "A"}, + {1, "A/B", "not-present", 3, "A/B"}, + {1, "A/B/C", "base-deleted", NO_COPY_FROM}, + {1, "A/B/C/D", "base-deleted", NO_COPY_FROM}, + {1, "A/B/C/E", "base-deleted", NO_COPY_FROM, "E"}, + {1, "A/B/C/F", "base-deleted", NO_COPY_FROM}, + {1, "A/B/G", "base-deleted", NO_COPY_FROM}, + {1, "A/B/G/H", "base-deleted", NO_COPY_FROM, "H"}, + {1, "A/B/G/I", "base-deleted", NO_COPY_FROM}, + {1, "A/B/G/J", "base-deleted", NO_COPY_FROM}, + {1, "A/B/K", "base-deleted", NO_COPY_FROM}, + {1, "A/B/K/L", "base-deleted", NO_COPY_FROM}, + {1, "A/B/K/M", "base-deleted", NO_COPY_FROM}, + {1, "A/N", "normal", 4, "A/N"}, + {1, "A/N/O", "not-present", 3, "A/N/O"}, + {1, "A/N/P", "not-present", 1, "A/N/P"}, + {1, "A/N/Q", "not-present", 1, "A/N/Q", FALSE, "Q"}, + {1, "A/R", "normal", 4, "A/R"}, + {1, "A/R/S", "normal", 4, "A/R/S"}, + {1, "A/R/S/T", "normal", 4, "A/R/S/T"}, + {1, "E", "normal", 1, "A/B/C/E", MOVED_HERE}, + {1, "H", "normal", 3, "A/B/G/H", MOVED_HERE}, + {1, "Q", "normal", 1, "A/N/Q", MOVED_HERE}, + {2, "A/B", "normal", 3, "A/B"}, + {2, "A/B/C", "not-present", 2, "A/B/C"}, + {2, "A/B/G", "normal", 3, "A/B/G"}, + {2, "A/B/G/H", "normal", 3, "A/B/G/H"}, + {2, "A/B/G/I", "normal", 3, "A/B/G/I"}, + {2, "A/B/G/J", "normal", 3, "A/B/G/J"}, + {2, "A/B/K", "not-present", 1, "A/B/K"}, + {3, "A/B/C", "normal", 2, "A/B/C"}, + {3, "A/B/C/D", "normal", 2, "A/B/C/D"}, + {3, "A/B/C/E", "not-present", 1, "A/B/C/E"}, + {3, "A/B/C/F", "normal", 2, "A/B/C/F"}, + {3, "A/B/K", "normal", 1, "A/B/K"}, + {3, "A/B/K/L", "normal", 1, "A/B/K/L"}, + {3, "A/B/K/M", "normal", 1, "A/B/K/M"}, + {3, "A/N/O", "normal", 3, "A/N/O"}, + {3, "A/N/P", "normal", NO_COPY_FROM}, + {4, "A/B/C/F", "base-deleted", NO_COPY_FROM}, + {4, "A/B/G/H", "base-deleted", NO_COPY_FROM}, + {4, "A/B/G/J", "normal", NO_COPY_FROM}, + + {0} + }; + + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + return SVN_NO_ERROR; +} + +static svn_error_t * +make_copy_and_delete_mixed(const svn_test_opts_t *opts, apr_pool_t *pool) +{ + svn_test__sandbox_t b; + + SVN_ERR(svn_test__sandbox_create(&b, "make_copy_and_del_mixed", opts, pool)); + + SVN_ERR(sbox_wc_mkdir(&b, "A")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B/C")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B/C/D")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B/C/E")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B/C/F")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B/G")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B/G/H")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B/G/I")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B/G/J")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B/K")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B/K/L")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B/K/M")); + SVN_ERR(sbox_wc_mkdir(&b, "A/N")); + SVN_ERR(sbox_wc_mkdir(&b, "A/N/O")); + SVN_ERR(sbox_wc_mkdir(&b, "A/N/P")); + SVN_ERR(sbox_wc_mkdir(&b, "A/N/Q")); + SVN_ERR(sbox_wc_mkdir(&b, "A/R")); + SVN_ERR(sbox_wc_mkdir(&b, "A/R/S")); + SVN_ERR(sbox_wc_mkdir(&b, "A/R/S/T")); + SVN_ERR(sbox_wc_commit(&b, "")); + SVN_ERR(sbox_wc_update(&b, "", 1)); + SVN_ERR(sbox_wc_propset(&b, "k", "r2", "")); + SVN_ERR(sbox_wc_commit(&b, "")); + SVN_ERR(sbox_wc_propset(&b, "k", "r3", "")); + SVN_ERR(sbox_wc_commit(&b, "")); + SVN_ERR(sbox_wc_propset(&b, "k", "r4", "")); + SVN_ERR(sbox_wc_commit(&b, "")); + SVN_ERR(sbox_wc_propset(&b, "k", "r5", "")); + SVN_ERR(sbox_wc_commit(&b, "")); + + SVN_ERR(sbox_wc_update(&b, "", 5)); + SVN_ERR(sbox_wc_update(&b, "A", 4)); + SVN_ERR(sbox_wc_update(&b, "A/B", 3)); + SVN_ERR(sbox_wc_update(&b, "A/B/C", 2)); + SVN_ERR(sbox_wc_update(&b, "A/B/K", 1)); + SVN_ERR(sbox_wc_update(&b, "A/N/O", 3)); + + SVN_ERR(sbox_wc_delete(&b, "A/B/C/F")); + SVN_ERR(sbox_wc_delete(&b, "A/B/G/J")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B/G/J")); + + SVN_ERR(sbox_wc_update(&b, "A/N/P", 1)); + SVN_ERR(sbox_wc_update(&b, "A/N/Q", 1)); + SVN_ERR(sbox_wc_delete(&b, "A/N/P")); + SVN_ERR(sbox_wc_mkdir(&b, "A/N/P")); + SVN_ERR(sbox_wc_move(&b, "A/N/Q", "Q")); + SVN_ERR(sbox_wc_move(&b, "A/B/G/H", "H")); + + /* And something that can't be represented */ + SVN_ERR(sbox_wc_update(&b, "A/B/C/E", 1)); + SVN_ERR(sbox_wc_move(&b, "A/B/C/E", "E")); + + { + nodes_row_t nodes[] = { + {0, "", "normal", 5, "", NOT_MOVED, "k"}, + {0, "A", "normal", 4, "A"}, + {0, "A/B", "normal", 3, "A/B"}, + {0, "A/B/C", "normal", 2, "A/B/C"}, + {0, "A/B/C/D", "normal", 2, "A/B/C/D"}, + {0, "A/B/C/E", "normal", 1, "A/B/C/E"}, + {0, "A/B/C/F", "normal", 2, "A/B/C/F"}, + {0, "A/B/G", "normal", 3, "A/B/G"}, + {0, "A/B/G/H", "normal", 3, "A/B/G/H"}, + {0, "A/B/G/I", "normal", 3, "A/B/G/I"}, + {0, "A/B/G/J", "normal", 3, "A/B/G/J"}, + {0, "A/B/K", "normal", 1, "A/B/K"}, + {0, "A/B/K/L", "normal", 1, "A/B/K/L"}, + {0, "A/B/K/M", "normal", 1, "A/B/K/M"}, + {0, "A/N", "normal", 4, "A/N"}, + {0, "A/N/O", "normal", 3, "A/N/O"}, + {0, "A/N/P", "normal", 1, "A/N/P"}, + {0, "A/N/Q", "normal", 1, "A/N/Q"}, + {0, "A/R", "normal", 4, "A/R"}, + {0, "A/R/S", "normal", 4, "A/R/S"}, + {0, "A/R/S/T", "normal", 4, "A/R/S/T"}, + {1, "E", "normal", 1, "A/B/C/E", MOVED_HERE}, + {1, "H", "normal", 3, "A/B/G/H", MOVED_HERE}, + {1, "Q", "normal", 1, "A/N/Q", MOVED_HERE}, + {3, "A/N/P", "normal", NO_COPY_FROM}, + {3, "A/N/Q", "base-deleted", NO_COPY_FROM, "Q"}, + {4, "A/B/C/E", "base-deleted", NO_COPY_FROM, "E"}, + {4, "A/B/C/F", "base-deleted", NO_COPY_FROM}, + {4, "A/B/G/H", "base-deleted", NO_COPY_FROM, "H"}, + {4, "A/B/G/J", "normal", NO_COPY_FROM}, + + {0} + }; + + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + SVN_ERR(svn_wc__db_base_remove(b.wc_ctx->db, sbox_wc_path(&b, "A"), + TRUE, FALSE, FALSE, 99, + NULL, NULL, pool)); + + { + nodes_row_t nodes[] = { + {0, "", "normal", 5, "", NOT_MOVED, "k"}, + {0, "A", "not-present", 99, "A"}, + {1, "A", "normal", 4, "A"}, + {1, "A/B", "not-present", 3, "A/B"}, + {1, "A/N", "normal", 4, "A/N"}, + {1, "A/N/O", "not-present", 3, "A/N/O"}, + {1, "A/N/P", "not-present", 1, "A/N/P"}, + {1, "A/N/Q", "not-present", 1, "A/N/Q", FALSE}, + {1, "A/R", "normal", 4, "A/R"}, + {1, "A/R/S", "normal", 4, "A/R/S"}, + {1, "A/R/S/T", "normal", 4, "A/R/S/T"}, + {1, "E", "normal", 1, "A/B/C/E"}, + {1, "H", "normal", 3, "A/B/G/H", MOVED_HERE}, + {1, "Q", "normal", 1, "A/N/Q"}, + {2, "A/B", "normal", 3, "A/B"}, + {2, "A/B/C", "not-present", 2, "A/B/C"}, + {2, "A/B/G", "normal", 3, "A/B/G"}, + {2, "A/B/G/H", "normal", 3, "A/B/G/H"}, + {2, "A/B/G/I", "normal", 3, "A/B/G/I"}, + {2, "A/B/G/J", "normal", 3, "A/B/G/J"}, + {3, "A/B/C", "normal", 2, "A/B/C"}, + {3, "A/B/C/D", "normal", 2, "A/B/C/D"}, + {3, "A/B/C/E", "not-present", 1, "A/B/C/E"}, + {3, "A/B/C/F", "normal", 2, "A/B/C/F"}, + {2, "A/B/K", "not-present", 1, "A/B/K"}, + {3, "A/B/K", "normal", 1, "A/B/K"}, + {3, "A/B/K/L", "normal", 1, "A/B/K/L"}, + {3, "A/B/K/M", "normal", 1, "A/B/K/M"}, + {3, "A/N/O", "normal", 3, "A/N/O"}, + {3, "A/N/P", "normal", NO_COPY_FROM}, + {4, "A/B/C/F", "base-deleted", NO_COPY_FROM}, + {4, "A/B/G/H", "base-deleted", NO_COPY_FROM}, + {4, "A/B/G/J", "normal", NO_COPY_FROM}, + + {0} + }; + + /* This currently fails because Q and E are still marked as moved, + while there is nothing to be moved. */ + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + return SVN_NO_ERROR; +} + /* ---------------------------------------------------------------------- */ /* The list of test functions */ @@ -11202,6 +11530,10 @@ static struct svn_test_descriptor_t test "move edit obstruction"), SVN_TEST_OPTS_PASS(move_deep_bump, "move deep bump"), + SVN_TEST_OPTS_PASS(make_copy_mixed, + "make a copy of a mixed revision tree"), + SVN_TEST_OPTS_XFAIL(make_copy_and_delete_mixed, + "make a copy of a mixed revision tree and del"), SVN_TEST_NULL };