Index: subversion/libsvn_client/commit.c =================================================================== diff --git a/subversion/trunk/subversion/libsvn_client/commit.c b/subversion/trunk/subversion/libsvn_client/commit.c --- a/subversion/trunk/subversion/libsvn_client/commit.c (Revision 1491372) +++ b/subversion/trunk/subversion/libsvn_client/commit.c (Arbeitskopie) @@ -240,6 +240,12 @@ post_process_commit_item(svn_wc_committed_queue_t remove_lock = (! keep_locks && (item->state_flags & SVN_CLIENT_COMMIT_ITEM_LOCK_TOKEN)); + // When the node was deleted (or replaced), we need to always remove the + // locks, as they're invalidated on the server. We cannot honor the + // SVN_CLIENT_COMMIT_ITEM_LOCK_TOKEN flag here because it does not tell + // us whether we have locked children. + remove_lock |= (item->state_flags & SVN_CLIENT_COMMIT_ITEM_DELETE) != 0; + return svn_wc_queue_committed3(queue, wc_ctx, item->path, loop_recurse, item->incoming_prop_changes, remove_lock, !keep_changelists, Index: subversion/libsvn_wc/adm_ops.c =================================================================== diff --git a/subversion/trunk/subversion/libsvn_wc/adm_ops.c b/subversion/trunk/subversion/libsvn_wc/adm_ops.c --- a/subversion/trunk/subversion/libsvn_wc/adm_ops.c (Revision 1491372) +++ b/subversion/trunk/subversion/libsvn_wc/adm_ops.c (Arbeitskopie) @@ -148,6 +148,7 @@ process_committed_leaf(svn_wc__db_t *db, db, local_abspath, FALSE /* keep_as_working */, FALSE /* queue_deletes */, + TRUE /* remove_locks */, (! via_recurse) ? new_revnum : SVN_INVALID_REVNUM, NULL, NULL, Index: subversion/libsvn_wc/crop.c =================================================================== diff --git a/subversion/trunk/subversion/libsvn_wc/crop.c b/subversion/trunk/subversion/libsvn_wc/crop.c --- a/subversion/trunk/subversion/libsvn_wc/crop.c (Revision 1491372) +++ b/subversion/trunk/subversion/libsvn_wc/crop.c (Arbeitskopie) @@ -110,6 +110,7 @@ crop_children(svn_wc__db_t *db, SVN_ERR(svn_wc__db_base_remove(db, child_abspath, FALSE /* keep_as_working */, FALSE /* queue_deletes */, + FALSE /* remove_locks */, SVN_INVALID_REVNUM, NULL, NULL, iterpool)); Index: subversion/libsvn_wc/externals.c =================================================================== diff --git a/subversion/trunk/subversion/libsvn_wc/externals.c b/subversion/trunk/subversion/libsvn_wc/externals.c --- a/subversion/trunk/subversion/libsvn_wc/externals.c (Revision 1491372) +++ b/subversion/trunk/subversion/libsvn_wc/externals.c (Arbeitskopie) @@ -1413,6 +1413,7 @@ svn_wc__external_remove(svn_wc_context_t *wc_ctx, SVN_ERR(svn_wc__db_base_remove(wc_ctx->db, local_abspath, FALSE /* keep_as_working */, TRUE /* queue_deletes */, + FALSE /* remove_locks */, SVN_INVALID_REVNUM, NULL, NULL, scratch_pool)); SVN_ERR(svn_wc__wq_run(wc_ctx->db, local_abspath, Index: subversion/libsvn_wc/update_editor.c =================================================================== diff --git a/subversion/trunk/subversion/libsvn_wc/update_editor.c b/subversion/trunk/subversion/libsvn_wc/update_editor.c --- a/subversion/trunk/subversion/libsvn_wc/update_editor.c (Revision 1491372) +++ b/subversion/trunk/subversion/libsvn_wc/update_editor.c (Arbeitskopie) @@ -1813,6 +1813,7 @@ delete_entry(const char *path, SVN_ERR(svn_wc__db_base_remove(eb->db, local_abspath, FALSE /* keep_as_working */, FALSE /* queue_deletes */, + FALSE /* remove_locks */, SVN_INVALID_REVNUM /* not_present_rev */, NULL, NULL, scratch_pool)); @@ -1909,7 +1910,7 @@ delete_entry(const char *path, { /* Delete, and do not leave a not-present node. */ SVN_ERR(svn_wc__db_base_remove(eb->db, local_abspath, - keep_as_working, queue_deletes, + keep_as_working, queue_deletes, FALSE, SVN_INVALID_REVNUM /* not_present_rev */, tree_conflict, NULL, scratch_pool)); @@ -1918,7 +1919,7 @@ delete_entry(const char *path, { /* Delete, leaving a not-present node. */ SVN_ERR(svn_wc__db_base_remove(eb->db, local_abspath, - keep_as_working, queue_deletes, + keep_as_working, queue_deletes, FALSE, *eb->target_revision, tree_conflict, NULL, scratch_pool)); @@ -4701,6 +4702,7 @@ close_edit(void *edit_baton, SVN_ERR(svn_wc__db_base_remove(eb->db, eb->target_abspath, FALSE /* keep_as_working */, FALSE /* queue_deletes */, + FALSE /* remove_locks */, SVN_INVALID_REVNUM, NULL, NULL, scratch_pool)); } Index: subversion/libsvn_wc/wc-queries.sql =================================================================== diff --git a/subversion/trunk/subversion/libsvn_wc/wc-queries.sql b/subversion/trunk/subversion/libsvn_wc/wc-queries.sql --- a/subversion/trunk/subversion/libsvn_wc/wc-queries.sql (Revision 1491372) +++ b/subversion/trunk/subversion/libsvn_wc/wc-queries.sql (Arbeitskopie) @@ -472,6 +472,10 @@ WHERE wc_id = ?1 DELETE FROM lock WHERE repos_id = ?1 AND repos_relpath = ?2 +-- STMT_DELETE_LOCK_RECURSIVELY +DELETE FROM lock +WHERE repos_id = ?1 AND (repos_relpath = ?2 OR IS_STRICT_DESCENDANT_OF(repos_relpath, ?2)) + -- STMT_CLEAR_BASE_NODE_RECURSIVE_DAV_CACHE UPDATE nodes SET dav_cache = NULL WHERE dav_cache IS NOT NULL AND wc_id = ?1 AND op_depth = 0 Index: subversion/libsvn_wc/wc_db.c =================================================================== diff --git a/subversion/trunk/subversion/libsvn_wc/wc_db.c b/subversion/trunk/subversion/libsvn_wc/wc_db.c --- a/subversion/trunk/subversion/libsvn_wc/wc_db.c (Revision 1491372) +++ b/subversion/trunk/subversion/libsvn_wc/wc_db.c (Arbeitskopie) @@ -2086,6 +2086,7 @@ db_base_remove(svn_wc__db_wcroot_t *wcroot, 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_skel_t *conflict, svn_skel_t *work_items, @@ -2106,6 +2107,16 @@ db_base_remove(svn_wc__db_wcroot_t *wcroot, wcroot, local_relpath, scratch_pool, scratch_pool)); + if (remove_locks) + { + svn_sqlite__stmt_t *lock_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 (status == svn_wc__db_status_normal && keep_as_working) { @@ -2333,6 +2344,7 @@ 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_skel_t *conflict, svn_skel_t *work_items, @@ -2349,7 +2361,7 @@ svn_wc__db_base_remove(svn_wc__db_t *db, SVN_WC__DB_WITH_TXN(db_base_remove(wcroot, local_relpath, db, keep_as_working, queue_deletes, - not_present_revision, + remove_locks, not_present_revision, conflict, work_items, scratch_pool), wcroot); @@ -10814,7 +10826,7 @@ commit_node(svn_wc__db_wcroot_t *wcroot, svn_sqlite__stmt_t *lock_stmt; SVN_ERR(svn_sqlite__get_statement(&lock_stmt, wcroot->sdb, - STMT_DELETE_LOCK)); + STMT_DELETE_LOCK_RECURSIVELY)); SVN_ERR(svn_sqlite__bindf(lock_stmt, "is", repos_id, repos_relpath)); SVN_ERR(svn_sqlite__step_done(lock_stmt)); } @@ -11058,7 +11070,7 @@ bump_node_revision(svn_wc__db_wcroot_t *wcroot, revision != new_rev))) { return svn_error_trace(db_base_remove(wcroot, local_relpath, - db, FALSE, FALSE, + db, FALSE, FALSE, FALSE, SVN_INVALID_REVNUM, NULL, NULL, scratch_pool)); } Index: subversion/libsvn_wc/wc_db.h =================================================================== diff --git a/subversion/trunk/subversion/libsvn_wc/wc_db.h b/subversion/trunk/subversion/libsvn_wc/wc_db.h --- a/subversion/trunk/subversion/libsvn_wc/wc_db.h (Revision 1491372) +++ b/subversion/trunk/subversion/libsvn_wc/wc_db.h (Arbeitskopie) @@ -702,6 +702,9 @@ svn_wc__db_base_add_not_present_node(svn_wc__db_t (With KEEP_AS_WORKING TRUE, this is a no-op, as everything is automatically shadowed by the created copy) + If REMOVE_LOCKS is TRUE, all locks of this node and any subnodes + are also removed. This is to be done during commit of deleted nodes. + If NOT_PRESENT_REVISION specifies a valid revision a not-present node is installed in BASE node with kind NOT_PRESENT_KIND after deleting. @@ -715,6 +718,7 @@ 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_skel_t *conflict, svn_skel_t *work_items, Index: subversion/libsvn_wc/workqueue.c =================================================================== diff --git a/subversion/trunk/subversion/libsvn_wc/workqueue.c b/subversion/trunk/subversion/libsvn_wc/workqueue.c --- a/subversion/trunk/subversion/libsvn_wc/workqueue.c (Revision 1491372) +++ b/subversion/trunk/subversion/libsvn_wc/workqueue.c (Arbeitskopie) @@ -143,6 +143,7 @@ run_base_remove(work_item_baton_t *wqb, SVN_ERR(svn_wc__db_base_remove(db, local_abspath, FALSE /* keep_as_working */, TRUE /* queue_deletes */, + FALSE /* remove_locks */, not_present_rev, NULL, NULL, scratch_pool)); Index: subversion/tests/cmdline/lock_tests.py =================================================================== diff --git a/subversion/trunk/subversion/tests/cmdline/lock_tests.py b/subversion/trunk/subversion/tests/cmdline/lock_tests.py --- a/subversion/trunk/subversion/tests/cmdline/lock_tests.py (Revision 1491372) +++ b/subversion/trunk/subversion/tests/cmdline/lock_tests.py (Arbeitskopie) @@ -1840,6 +1840,56 @@ def commit_stolen_lock(sbox): err_re, wc_dir) +# When removing directories, the locks of contained files were not +# correctly removed from the working copy database, thus they later +# magically reappeared when new files or directories with the same +# pathes were added. +@Issue(4364) +def drop_locks_on_parent_deletion(sbox): + "drop locks when the parent is deleted" + + sbox.build() + wc_dir = sbox.wc_dir + + # lock some files, and remove them. + sbox.simple_lock('A/B/lambda') + sbox.simple_lock('A/B/E/alpha') + sbox.simple_lock('A/B/E/beta') + sbox.simple_rm('A/B') + + expected_status = svntest.actions.get_virginal_state(wc_dir, 1) + expected_status.remove_subtree('A/B') + + svntest.actions.run_and_verify_commit(wc_dir, + [], + expected_status, + None, + wc_dir) + + # now re-add entities to the deleted pathes. + sbox.simple_mkdir('A/B') + sbox.simple_add_text('new file replacing old file', 'A/B/lambda') + sbox.simple_add_text('file replacing former dir', 'A/B/F') + # The bug also resurrected locks on directories when their path + # matched a former file. + sbox.simple_mkdir('A/B/E', 'A/B/E/alpha') + + expected_status = svntest.actions.get_virginal_state(wc_dir, 1) + expected_status.tweak('A/B', + 'A/B/E', + 'A/B/E/alpha', + 'A/B/F', + 'A/B/lambda', + wc_rev='3') + expected_status.remove('A/B/E/beta') + + svntest.actions.run_and_verify_commit(wc_dir, + [], + expected_status, + None, + wc_dir) + + ######################################################################## # Run the tests @@ -1892,6 +1942,7 @@ test_list = [ None, locks_stick_over_switch, lock_unlock_deleted, commit_stolen_lock, + drop_locks_on_parent_deletion, ] if __name__ == '__main__': Index: subversion/tests/libsvn_wc/op-depth-test.c =================================================================== diff --git a/subversion/trunk/subversion/tests/libsvn_wc/op-depth-test.c b/subversion/trunk/subversion/tests/libsvn_wc/op-depth-test.c --- a/subversion/trunk/subversion/tests/libsvn_wc/op-depth-test.c (Revision 1491372) +++ b/subversion/trunk/subversion/tests/libsvn_wc/op-depth-test.c (Arbeitskopie) @@ -1109,6 +1109,7 @@ base_dir_insert_remove(svn_test__sandbox_t *b, SVN_ERR(svn_wc__db_base_remove(b->wc_ctx->db, dir_abspath, FALSE /* keep_as_Working */, FALSE /* queue_deletes */, + FALSE /* remove_locks */, SVN_INVALID_REVNUM, NULL, NULL, b->pool)); SVN_ERR(svn_wc__wq_run(b->wc_ctx->db, dir_abspath,