Create '1.9.x-commit-fixes' branch based of the '1.9.x-db-verify' branch
* subversion/libsvn_client/commit.c (r1661363)
(post_process_commit_item): Request removing locks on restructuring
operations. Even though the node itself has no locks, shadowed
descendants might. (And the repository handles this the same way)
* subversion/libsvn_wc/wc-queries.sql:
(STMT_COMMIT_DESCENDANTS_TO_BASE): Properly set moved_to to NULL. (r1660781)
(STMT_SELECT_MOVED_DESCENDANTS_SHD, (r1661718)
STMT_SELECT_MOVED_DESCENDANTS_SRC): Also scan the node itself for moves.
* subversion/libsvn_wc/wc_db.c
(db_base_remove): Use internal api.
(moved_descendant_collect): New function, extracted from...
(moved_descendant_commit): ... this. Switch key/value to avoid overwriting.
(commit_node):
Remove unused argument. (r1661730)
Handle deletes (r1661504)
Fix lock removal (r1660781, r1661363)
Properly break moves on both sides (r1660781)
(svn_wc__db_global_commit): Update caller. Remove unused argument. (r1661730)
(process_committed_leaf): Use commit_node for deletes (r1661504)
* subversion/libsvn_wc/wc_db.h
(svn_wc__db_global_commit): Remove unused argument. (r1661730)
* subversion/tests/cmdline/lock_tests.py (r1661335,1661363)
(break_delete_add): Remove XFail
(delete_dir_with_lots_of_locked_files): Use multi-lock operation.
(delete_locks_on_depth_commit): New test.
(test_list): Add delete_locks_on_depth_commit.
* subversion/tests/libsvn_wc/op-depth-test.c
(verify_db_callback,
verify_db): New functions. (r1661718)
(insert_dirs): Support adding moves. (r1661755)
(commit_moved_descendant,
commit_moved_away_descendant): Extend tests.
(test_global_commit): New test. (r1661755)
(test_funcs): Add test_global_commit.
* subversion/tests/libsvn_wc/wc-test-queries.sql
(STMT_INSERT_NODE): Add data to more columns. (r1661755)
git-svn-id: https://svn.apache.org/repos/asf/subversion/branches/1.9.x-commit-fixes@1661930 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/subversion/libsvn_client/commit.c b/subversion/libsvn_client/commit.c
index 76a68da..138e9e0 100644
--- a/subversion/libsvn_client/commit.c
+++ b/subversion/libsvn_client/commit.c
@@ -240,7 +240,9 @@
loop_recurse = TRUE;
remove_lock = (! keep_locks && (item->state_flags
- & SVN_CLIENT_COMMIT_ITEM_LOCK_TOKEN));
+ & (SVN_CLIENT_COMMIT_ITEM_LOCK_TOKEN
+ | SVN_CLIENT_COMMIT_ITEM_ADD
+ | SVN_CLIENT_COMMIT_ITEM_DELETE)));
/* 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
diff --git a/subversion/libsvn_wc/wc-queries.sql b/subversion/libsvn_wc/wc-queries.sql
index 257decc..88ba209 100644
--- a/subversion/libsvn_wc/wc-queries.sql
+++ b/subversion/libsvn_wc/wc-queries.sql
@@ -378,6 +378,7 @@
revision = ?6,
dav_cache = NULL,
moved_here = NULL,
+ moved_to = NULL,
presence = CASE presence
WHEN MAP_NORMAL THEN MAP_NORMAL
WHEN MAP_EXCLUDED THEN MAP_EXCLUDED
@@ -1688,7 +1689,7 @@
WHERE d.wc_id = ?1 AND d.local_relpath = ?2
AND d.op_depth < ?3)
WHERE s.wc_id = ?1 AND s.op_depth = ?3
- AND IS_STRICT_DESCENDANT_OF(s.local_relpath, ?2)
+ AND (s.local_relpath = ?2 OR IS_STRICT_DESCENDANT_OF(s.local_relpath, ?2))
AND s.moved_to IS NOT NULL
/* This statement is very similar to STMT_SELECT_MOVED_DESCENDANTS_SHD,
@@ -1703,7 +1704,7 @@
AND d.local_relpath = s.local_relpath
AND d.op_depth > ?3)
WHERE n.wc_id = ?1 AND n.op_depth = ?3
- AND IS_STRICT_DESCENDANT_OF(n.local_relpath, ?2)
+ AND (n.local_relpath = ?2 OR IS_STRICT_DESCENDANT_OF(n.local_relpath, ?2))
AND s.moved_to IS NOT NULL
-- STMT_COMMIT_UPDATE_ORIGIN
diff --git a/subversion/libsvn_wc/wc_db.c b/subversion/libsvn_wc/wc_db.c
index 118eefd..f0e06d3 100644
--- a/subversion/libsvn_wc/wc_db.c
+++ b/subversion/libsvn_wc/wc_db.c
@@ -2251,12 +2251,9 @@
if (status == svn_wc__db_status_normal
&& keep_as_working)
{
- SVN_ERR(svn_wc__db_op_make_copy(db,
- svn_dirent_join(wcroot->abspath,
- local_relpath,
- scratch_pool),
- NULL, NULL,
- scratch_pool));
+ SVN_ERR(svn_wc__db_op_make_copy_internal(wcroot, local_relpath,
+ NULL, NULL,
+ scratch_pool));
keep_working = TRUE;
}
else
@@ -11383,35 +11380,18 @@
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,
+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));
@@ -11423,21 +11403,56 @@
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);
- svn_hash_sets(children, src_relpath, to_relpath);
+ if (!*map)
+ *map = apr_hash_make(result_pool);
+
+ 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));
@@ -11445,11 +11460,12 @@
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);
@@ -11476,9 +11492,11 @@
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);
@@ -11537,7 +11555,6 @@
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,
@@ -11559,6 +11576,7 @@
const char *repos_relpath;
int op_depth;
svn_wc__db_status_t old_presence;
+ svn_boolean_t moved_here;
/* If we are adding a file or directory, then we need to get
repository information from the parent node since "this node" does
@@ -11588,6 +11606,7 @@
/* Figure out the new node's kind. It will be whatever is in WORKING_NODE,
or there will be a BASE_NODE that has it. */
+ old_presence = svn_sqlite__column_token(stmt_info, 3, presence_map);
new_kind = svn_sqlite__column_token(stmt_info, 4, kind_map);
/* What will the new depth be? */
@@ -11606,26 +11625,35 @@
svn_sqlite__column_text(stmt_info, 2, NULL)) == 0);
}
- /* Find the appropriate new properties -- ACTUAL overrides any properties
- in WORKING that arrived as part of a copy/move.
+ if (old_presence != svn_wc__db_status_base_deleted)
+ {
+ /* Find the appropriate new properties -- ACTUAL overrides any properties
+ in WORKING that arrived as part of a copy/move.
- Note: we'll keep them as a big blob of data, rather than
- deserialize/serialize them. */
- if (have_act)
- prop_blob.data = svn_sqlite__column_blob(stmt_act, 1, &prop_blob.len,
- scratch_pool);
- if (prop_blob.data == NULL)
- prop_blob.data = svn_sqlite__column_blob(stmt_info, 14, &prop_blob.len,
- scratch_pool);
+ Note: we'll keep them as a big blob of data, rather than
+ deserialize/serialize them. */
+ if (have_act)
+ prop_blob.data = svn_sqlite__column_blob(stmt_act, 1, &prop_blob.len,
+ scratch_pool);
+ if (prop_blob.data == NULL)
+ prop_blob.data = svn_sqlite__column_blob(stmt_info, 14, &prop_blob.len,
+ scratch_pool);
- inherited_prop_blob.data = svn_sqlite__column_blob(stmt_info, 16,
- &inherited_prop_blob.len,
- scratch_pool);
+ inherited_prop_blob.data = svn_sqlite__column_blob(
+ stmt_info, 16,
+ &inherited_prop_blob.len,
+ scratch_pool);
- if (keep_changelist && have_act)
- changelist = svn_sqlite__column_text(stmt_act, 0, scratch_pool);
+ if (keep_changelist && have_act)
+ changelist = svn_sqlite__column_text(stmt_act, 0, scratch_pool);
- old_presence = svn_sqlite__column_token(stmt_info, 3, presence_map);
+ moved_here = svn_sqlite__column_int(stmt_info, 15);
+ }
+ else
+ {
+ moved_here = FALSE;
+ changelist = NULL;
+ }
/* ### other stuff? */
@@ -11636,6 +11664,24 @@
{
int affected_rows;
+ SVN_ERR_ASSERT(op_depth == relpath_depth(local_relpath));
+
+ /* First clear the moves that we are going to delete in a bit */
+ {
+ apr_hash_t *old_moves;
+ apr_hash_index_t *hi;
+ SVN_ERR(moved_descendant_collect(&old_moves, wcroot, local_relpath, 0,
+ scratch_pool, scratch_pool));
+
+ if (old_moves)
+ for (hi = apr_hash_first(scratch_pool, old_moves);
+ hi; hi = apr_hash_next(hi))
+ {
+ SVN_ERR(clear_moved_here(wcroot, apr_hash_this_key(hi),
+ scratch_pool));
+ }
+ }
+
/* This removes all layers of this node and at the same time determines
if we need to remove shadowed layers below our descendants. */
@@ -11668,26 +11714,40 @@
be integrated, they really affect a different op-depth and
completely different nodes (via a different recursion pattern). */
- /* Collapse descendants of the current op_depth in layer 0 */
- SVN_ERR(descendant_commit(wcroot, local_relpath, op_depth,
- repos_id, repos_relpath, new_revision,
- scratch_pool));
+ if (old_presence != svn_wc__db_status_base_deleted)
+ {
+ /* Collapse descendants of the current op_depth to layer 0,
+ this includes moved-from/to clearing */
+ SVN_ERR(descendant_commit(wcroot, local_relpath, op_depth,
+ repos_id, repos_relpath, new_revision,
+ scratch_pool));
+ }
- /* And make the recorded local moves represent moves of the node we just
- committed. */
- SVN_ERR(moved_descendant_commit(wcroot, local_relpath, 0,
+ if (old_presence != svn_wc__db_status_base_deleted)
+ {
+ apr_hash_t *moves = NULL;
+
+ SVN_ERR(moved_descendant_collect(&moves, wcroot, local_relpath, 0,
+ scratch_pool, scratch_pool));
+
+ /* And make the recorded local moves represent moves of the node we
+ just committed. */
+ SVN_ERR(moved_descendant_commit(wcroot, local_relpath,
repos_id, repos_relpath, new_revision,
- scratch_pool));
+ moves, scratch_pool));
+ }
- /* This node is no longer modified, so no node was moved here */
- SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
- STMT_CLEAR_MOVED_TO_FROM_DEST));
- SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
- local_relpath));
+ if (moved_here)
+ {
+ /* This node is no longer modified, so no node was moved here */
+ SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
+ STMT_CLEAR_MOVED_TO_FROM_DEST));
+ SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
+ local_relpath));
- SVN_ERR(svn_sqlite__step_done(stmt));
+ SVN_ERR(svn_sqlite__step_done(stmt));
+ }
}
-
/* Update or add the BASE_NODE row with all the new information. */
if (*local_relpath == '\0')
@@ -11696,38 +11756,56 @@
parent_relpath = svn_relpath_dirname(local_relpath, scratch_pool);
/* Preserve any incomplete status */
- new_presence = (old_presence == svn_wc__db_status_incomplete
- ? svn_wc__db_status_incomplete
- : svn_wc__db_status_normal);
-
- SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
- STMT_APPLY_CHANGES_TO_BASE_NODE));
- /* symlink_target not yet used */
- SVN_ERR(svn_sqlite__bindf(stmt, "issisrtstrisnbn",
- wcroot->wc_id, local_relpath,
- parent_relpath,
- repos_id,
- repos_relpath,
- new_revision,
- presence_map, new_presence,
- new_depth_str,
- kind_map, new_kind,
- changed_rev,
- changed_date,
- changed_author,
- prop_blob.data, prop_blob.len));
-
- SVN_ERR(svn_sqlite__bind_checksum(stmt, 13, new_checksum,
- scratch_pool));
- SVN_ERR(svn_sqlite__bind_properties(stmt, 15, new_dav_cache,
- scratch_pool));
- if (inherited_prop_blob.data != NULL)
+ if (old_presence != svn_wc__db_status_base_deleted)
{
- SVN_ERR(svn_sqlite__bind_blob(stmt, 17, inherited_prop_blob.data,
- inherited_prop_blob.len));
- }
+ new_presence = (old_presence == svn_wc__db_status_incomplete
+ ? svn_wc__db_status_incomplete
+ : svn_wc__db_status_normal);
- SVN_ERR(svn_sqlite__step_done(stmt));
+ SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
+ STMT_APPLY_CHANGES_TO_BASE_NODE));
+ /* symlink_target not yet used */
+ SVN_ERR(svn_sqlite__bindf(stmt, "issisrtstrisnbn",
+ wcroot->wc_id, local_relpath,
+ parent_relpath,
+ repos_id,
+ repos_relpath,
+ new_revision,
+ presence_map, new_presence,
+ new_depth_str,
+ kind_map, new_kind,
+ changed_rev,
+ changed_date,
+ changed_author,
+ prop_blob.data, prop_blob.len));
+
+ SVN_ERR(svn_sqlite__bind_checksum(stmt, 13, new_checksum,
+ scratch_pool));
+ SVN_ERR(svn_sqlite__bind_properties(stmt, 15, new_dav_cache,
+ scratch_pool));
+ if (inherited_prop_blob.data != NULL)
+ {
+ SVN_ERR(svn_sqlite__bind_blob(stmt, 17, inherited_prop_blob.data,
+ inherited_prop_blob.len));
+ }
+
+ SVN_ERR(svn_sqlite__step_done(stmt));
+ }
+ else
+ {
+ struct insert_base_baton_t ibb;
+ blank_ibb(&ibb);
+
+ ibb.repos_id = repos_id;
+ ibb.status = svn_wc__db_status_not_present;
+ ibb.kind = new_kind;
+ ibb.repos_relpath = repos_relpath;
+ ibb.revision = new_revision;
+
+ SVN_ERR(insert_base_node(&ibb, wcroot, local_relpath, scratch_pool));
+
+ keep_changelist = FALSE; /* Nothing there */
+ }
if (have_act)
{
@@ -11755,23 +11833,21 @@
}
}
- if (new_kind == svn_node_dir)
- {
- /* When committing a directory, we should have its new children. */
- /* ### one day. just not today. */
-#if 0
- SVN_ERR_ASSERT(new_children != NULL);
-#endif
-
- /* ### process the children */
- }
-
if (!no_unlock)
{
svn_sqlite__stmt_t *lock_stmt;
+ svn_boolean_t op_root = (op_depth > 0
+ && (relpath_depth(local_relpath) == op_depth));
+ /* If we are committing an add of a delete, we can assume we own
+ all locks at or below REPOS_RELPATH (or the server would have
+ denied the commit). As we must have passed these to the server
+ we can now safely remove them.
+ */
SVN_ERR(svn_sqlite__get_statement(&lock_stmt, wcroot->sdb,
- STMT_DELETE_LOCK_RECURSIVELY));
+ op_root
+ ? STMT_DELETE_LOCK_RECURSIVELY
+ : STMT_DELETE_LOCK));
SVN_ERR(svn_sqlite__bindf(lock_stmt, "is", repos_id, repos_relpath));
SVN_ERR(svn_sqlite__step_done(lock_stmt));
}
@@ -11791,7 +11867,6 @@
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,
@@ -11803,7 +11878,6 @@
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(new_revision));
- SVN_ERR_ASSERT(new_checksum == NULL || new_children == NULL);
SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
local_abspath, scratch_pool, scratch_pool));
@@ -11812,7 +11886,7 @@
SVN_WC__DB_WITH_TXN(
commit_node(wcroot, local_relpath,
new_revision, changed_revision, changed_date, changed_author,
- new_checksum, new_children, new_dav_cache, keep_changelist,
+ new_checksum, new_dav_cache, keep_changelist,
no_unlock, work_items, scratch_pool),
wcroot);
@@ -16186,19 +16260,7 @@
scratch_pool));
}
- if (status == svn_wc__db_status_deleted)
- {
- return svn_error_trace(
- db_base_remove(wcroot, local_relpath, db,
- FALSE /* keep_as_working */,
- FALSE /* queue_deletes */,
- TRUE /* remove_locks */,
- (! via_recurse)
- ? new_revnum : SVN_INVALID_REVNUM,
- NULL, NULL,
- scratch_pool));
- }
- else if (status == svn_wc__db_status_not_present)
+ if (status == svn_wc__db_status_not_present)
{
/* We are committing the leaf of a copy operation.
We leave the not-present marker to allow pulling in excluded
@@ -16211,9 +16273,11 @@
SVN_ERR_ASSERT(status == svn_wc__db_status_normal
|| status == svn_wc__db_status_incomplete
- || status == svn_wc__db_status_added);
+ || status == svn_wc__db_status_added
+ || status == svn_wc__db_status_deleted);
- if (kind != svn_node_dir)
+ if (kind != svn_node_dir
+ && status != svn_wc__db_status_deleted)
{
/* If we sent a delta (meaning: post-copy modification),
then this file will appear in the queue and so we should have
@@ -16263,7 +16327,6 @@
new_revnum, new_changed_rev,
new_changed_date, new_changed_author,
checksum,
- NULL /* new_children */,
new_dav_cache,
!remove_changelist,
!remove_lock,
diff --git a/subversion/libsvn_wc/wc_db.h b/subversion/libsvn_wc/wc_db.h
index c0ccd69..1bccd8e 100644
--- a/subversion/libsvn_wc/wc_db.h
+++ b/subversion/libsvn_wc/wc_db.h
@@ -2517,11 +2517,6 @@
CHANGED_AUTHOR is the (server-side) author of CHANGED_REVISION. It may be
NULL if the revprop is missing on the revision.
- One or both of NEW_CHECKSUM and NEW_CHILDREN should be NULL. For new:
- files: NEW_CHILDREN should be NULL
- dirs: NEW_CHECKSUM should be NULL
- symlinks: both should be NULL
-
WORK_ITEMS will be place into the work queue.
*/
svn_error_t *
@@ -2532,7 +2527,6 @@
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,
diff --git a/subversion/tests/cmdline/lock_tests.py b/subversion/tests/cmdline/lock_tests.py
index 0b90748..1f93f69 100755
--- a/subversion/tests/cmdline/lock_tests.py
+++ b/subversion/tests/cmdline/lock_tests.py
@@ -1997,7 +1997,6 @@
'unlock', pi_path)
svntest.actions.run_and_verify_status(wc_dir, expected_status)
-@XFail()
def break_delete_add(sbox):
"break a lock, delete and add the file"
@@ -2384,22 +2383,20 @@
nfiles = 75 # NOTE: test XPASSES with 50 files!!!
locked_paths = []
for i in range(nfiles):
- locked_paths.append("A/locked_files/file-%i" % i)
+ locked_paths.append(sbox.ospath("A/locked_files/file-%i" % i))
# Create files at these paths
os.mkdir(sbox.ospath("A/locked_files"))
for file_path in locked_paths:
- svntest.main.file_write(sbox.ospath(file_path), "This is a file\n")
+ svntest.main.file_write(file_path, "This is '%s'.\n" % (file_path,))
sbox.simple_add("A/locked_files")
sbox.simple_commit()
sbox.simple_update()
# lock all the files
- for file_path in locked_paths:
- svntest.actions.run_and_verify_svn(None, [], 'lock',
- '--username', 'jrandom',
- '-m', 'lock %s' % file_path,
- sbox.ospath(file_path))
+ svntest.actions.run_and_verify_svn(None, [], 'lock',
+ '-m', 'All locks',
+ *locked_paths)
# Locally delete A
sbox.simple_rm("A")
@@ -2411,6 +2408,62 @@
# This problem was introduced on the 1.8.x branch in r1606976.
sbox.simple_commit()
+def delete_locks_on_depth_commit(sbox):
+ "delete locks on depth-limited commit"
+
+ sbox.build()
+ wc_dir = sbox.wc_dir
+
+ svntest.actions.run_and_verify_svn(None, [], 'lock',
+ '-m', 'All files',
+ *(sbox.ospath(x)
+ for x in ['iota', 'A/B/E/alpha',
+ 'A/B/E/beta', 'A/B/lambda',
+ 'A/D/G/pi', 'A/D/G/rho',
+ 'A/D/G/tau', 'A/D/H/chi',
+ 'A/D/H/omega', 'A/D/H/psi',
+ 'A/D/gamma', 'A/mu']))
+
+ sbox.simple_rm("A")
+
+ expected_output = svntest.wc.State(wc_dir, {
+ 'A' : Item(verb='Deleting'),
+ })
+
+ expected_status = svntest.wc.State(wc_dir, {
+ '' : Item(status=' ', wc_rev='1'),
+ 'iota' : Item(status=' ', wc_rev='1'),
+ })
+
+ svntest.actions.run_and_verify_commit(wc_dir, expected_output,
+ expected_status, [],
+ wc_dir, '--depth', 'immediates')
+
+ sbox.simple_update() # r2
+
+ svntest.actions.run_and_verify_svn(None, [], 'cp',
+ sbox.repo_url + '/A@1', sbox.ospath('A'))
+
+ expected_output = [
+ 'Adding %s\n' % sbox.ospath('A'),
+ 'svn: The depth of this commit is \'immediates\', but copies ' \
+ 'are always performed recursively in the repository.\n',
+ 'Committing transaction...\n',
+ 'Committed revision 3.\n',
+ ]
+
+ # Verifying the warning line... so can't use verify_commit()
+ svntest.actions.run_and_verify_svn(expected_output, [],
+ 'commit', wc_dir, '--depth', 'immediates',
+ '-mm')
+
+ # Verify that all locks are gone at the server and at the client
+ expected_status = svntest.actions.get_virginal_state(wc_dir, 3)
+ expected_status.tweak('', 'iota', wc_rev=2)
+ svntest.actions.run_and_verify_status(wc_dir, expected_status)
+
+
+
########################################################################
# Run the tests
@@ -2477,6 +2530,7 @@
lock_commit_bump,
copy_dir_with_locked_file,
delete_dir_with_lots_of_locked_files,
+ delete_locks_on_depth_commit,
]
if __name__ == '__main__':
diff --git a/subversion/tests/libsvn_wc/op-depth-test.c b/subversion/tests/libsvn_wc/op-depth-test.c
index 74af109..d314762 100644
--- a/subversion/tests/libsvn_wc/op-depth-test.c
+++ b/subversion/tests/libsvn_wc/op-depth-test.c
@@ -563,6 +563,35 @@
}
+static svn_error_t *
+verify_db_callback(void *baton,
+ const char *wc_abspath,
+ const char *local_relpath,
+ int op_depth,
+ int id,
+ const char *msg,
+ apr_pool_t *scratch_pool)
+{
+ if (op_depth >= 0)
+ return svn_error_createf(SVN_ERR_WC_CORRUPT, NULL,
+ "Verify: %s: %s (%d): SV%04d %s",
+ wc_abspath, local_relpath, op_depth, id, msg);
+ else
+ return svn_error_createf(SVN_ERR_WC_CORRUPT, NULL,
+ "DB-VRFY: %s: %s: SV%04d %s",
+ wc_abspath, local_relpath, id, msg);
+}
+
+static svn_error_t *
+verify_db(svn_test__sandbox_t *b)
+{
+ SVN_ERR(svn_wc__db_verify_db_full(b->wc_ctx->db, b->wc_abspath,
+ verify_db_callback, NULL, b->pool));
+
+ return SVN_NO_ERROR;
+}
+
+
/* ---------------------------------------------------------------------- */
/* The test functions */
@@ -1267,6 +1296,34 @@
? svn_relpath_dirname(nodes->local_relpath,
b->pool)
: NULL));
+
+ if (nodes->moved_to)
+ SVN_ERR(svn_sqlite__bind_text(stmt, 7, nodes->moved_to));
+ if (nodes->moved_here)
+ SVN_ERR(svn_sqlite__bind_int(stmt, 8, 1));
+ if (nodes->props)
+ {
+ int i;
+ apr_hash_t *props = apr_hash_make(b->pool);
+ apr_array_header_t *names = svn_cstring_split(nodes->props, ",",
+ TRUE, b->pool);
+
+ for (i = 0; i < names->nelts; i++)
+ {
+ const char *name = APR_ARRAY_IDX(names, i, const char *);
+ svn_hash_sets(props, name, svn_string_create(name, b->pool));
+ }
+
+ SVN_ERR(svn_sqlite__bind_properties(stmt, 9, props, b->pool));
+ }
+ else if (nodes->repo_relpath
+ && strcmp(nodes->presence, "normal") == 0)
+ {
+ SVN_ERR(svn_sqlite__bind_text(stmt, 9, "()"));
+ }
+
+ /* File externals? */
+
SVN_ERR(svn_sqlite__step_done(stmt));
++nodes;
}
@@ -6917,6 +6974,31 @@
shadowed, like in this case. The commit processing doesn't
support this yet though*/
+ {
+ nodes_row_t nodes[] = {
+ {0, "", "normal", 0, ""},
+ {0, "A", "normal", 1, "A"},
+ {0, "A/A", "normal", 2, "A/A"},
+ {0, "A/A/A", "normal", 2, "A/A/A"},
+ {0, "A/A/A/A", "normal", 2, "A/A/A/A"},
+ {0, "A/A/A/A/A", "normal", 2, "A/A/A/A/A"},
+ {0, "A/A/A/A/A/A", "normal", 2, "A/A/A/A/A/A"},
+ {0, "A_copied", "normal", 2, "A_copied"},
+ {0, "A_copied/A", "normal", 2, "A_copied/A"},
+ {0, "A_copied/A/A", "normal", 2, "A_copied/A/A"},
+ {0, "A_copied/A/A/A", "normal", 2, "A_copied/A/A/A"},
+ {0, "A_copied/A/A/A/A", "normal", 2, "A_copied/A/A/A/A"},
+ {0, "A_copied/A/A/A/A/A","normal", 2, "A_copied/A/A/A/A/A"},
+ {0, "AAA_moved", "normal", 2, "AAA_moved"},
+ {0, "AAA_moved/A", "normal", 2, "AAA_moved/A"},
+ {0, "AAA_moved/A/A", "normal", 2, "AAA_moved/A/A"},
+ {0, "AAA_moved/A/A/A", "normal", 2, "AAA_moved/A/A/A"},
+
+ {0}
+ };
+ SVN_ERR(check_db_rows(&b, "", nodes));
+ }
+
return SVN_NO_ERROR;
}
@@ -6939,10 +7021,63 @@
SVN_ERR(sbox_wc_delete(&b, "A/A"));
SVN_ERR(sbox_wc_copy(&b, "A_copied/A", "A/A"));
+ {
+ nodes_row_t nodes[] = {
+ {0, "", "normal", 0, ""},
+ {0, "A", "normal", 1, "A"},
+ {0, "A/A", "normal", 1, "A/A"},
+ {0, "A/A/A", "normal", 1, "A/A/A"},
+ {0, "A/A/A/A", "normal", 1, "A/A/A/A"},
+ {0, "A/A/A/A/A", "normal", 1, "A/A/A/A/A"},
+ {0, "A/A/A/A/A/A", "normal", 1, "A/A/A/A/A/A"},
+ {1, "A_copied", "normal", 1, "A"},
+ {1, "A_copied/A", "normal", 1, "A/A"},
+ {1, "A_copied/A/A", "normal", 1, "A/A/A"},
+ {1, "A_copied/A/A/A", "normal", 1, "A/A/A/A"},
+ {1, "A_copied/A/A/A/A", "normal", 1, "A/A/A/A/A"},
+ {1, "A_copied/A/A/A/A/A", "normal", 1, "A/A/A/A/A/A"},
+ {1, "AAA_moved", "normal", 1, "A/A/A", MOVED_HERE},
+ {1, "AAA_moved/A", "normal", 1, "A/A/A/A", MOVED_HERE},
+ {1, "AAA_moved/A/A", "normal", 1, "A/A/A/A/A", MOVED_HERE},
+ {1, "AAA_moved/A/A/A", "normal", 1, "A/A/A/A/A/A", MOVED_HERE},
+ {2, "A/A", "normal", 1, "A/A"},
+ {2, "A/A/A", "normal", 1, "A/A/A", FALSE, "AAA_moved"},
+ {2, "A/A/A/A", "normal", 1, "A/A/A/A"},
+ {2, "A/A/A/A/A", "normal", 1, "A/A/A/A/A"},
+ {2, "A/A/A/A/A/A", "normal", 1, "A/A/A/A/A/A"},
+ {0}
+ };
+ SVN_ERR(check_db_rows(&b, "", nodes));
+ }
+
/* And now I want to make sure that I can't commit A, without also
committing AAA_moved, as that would break the move*/
SVN_ERR(sbox_wc_commit(&b, "A"));
+ {
+ nodes_row_t nodes[] = {
+ {0, "", "normal", 0, ""},
+ {0, "A", "normal", 1, "A"},
+ {0, "A/A", "normal", 2, "A/A"},
+ {0, "A/A/A", "normal", 2, "A/A/A"},
+ {0, "A/A/A/A", "normal", 2, "A/A/A/A"},
+ {0, "A/A/A/A/A", "normal", 2, "A/A/A/A/A"},
+ {0, "A/A/A/A/A/A", "normal", 2, "A/A/A/A/A/A"},
+ {1, "A_copied", "normal", 1, "A"},
+ {1, "A_copied/A", "normal", 1, "A/A"},
+ {1, "A_copied/A/A", "normal", 1, "A/A/A"},
+ {1, "A_copied/A/A/A", "normal", 1, "A/A/A/A"},
+ {1, "A_copied/A/A/A/A", "normal", 1, "A/A/A/A/A"},
+ {1, "A_copied/A/A/A/A/A", "normal", 1, "A/A/A/A/A/A"},
+ {1, "AAA_moved", "normal", 1, "A/A/A"},
+ {1, "AAA_moved/A", "normal", 1, "A/A/A/A"},
+ {1, "AAA_moved/A/A", "normal", 1, "A/A/A/A/A"},
+ {1, "AAA_moved/A/A/A", "normal", 1, "A/A/A/A/A/A"},
+ {0}
+ };
+ SVN_ERR(check_db_rows(&b, "", nodes));
+ }
+
return svn_error_create(SVN_ERR_TEST_FAILED, NULL,
"The commit should have failed");
@@ -10993,6 +11128,138 @@
return SVN_NO_ERROR;
}
+static svn_error_t *
+test_global_commit(const svn_test_opts_t *opts, apr_pool_t *pool)
+{
+ svn_test__sandbox_t b;
+
+ SVN_ERR(svn_test__sandbox_create(&b, "global_commit", opts, pool));
+
+ {
+ nodes_row_t before[] = {
+ { 0, "", "normal", 2, "" },
+ { 0, "A", "normal", 2, "A" },
+ { 0, "A/B", "normal", 2, "A/B" },
+ { 0, "A/B/C", "normal", 2, "A/B/C" },
+ { 0, "A/B/D", "normal", 2, "A/B/D" },
+ { 0, "A/B/D/E", "normal", 2, "A/B/D/E" },
+ { 0, "A/F", "normal", 2, "A/F" },
+ { 0, "A/F/G", "normal", 2, "A/F/G" },
+ { 0, "A/F/H", "normal", 2, "A/F/H" },
+ { 0, "A/F/E", "normal", 2, "A/F/E" },
+ { 0, "A/X", "normal", 2, "A/X" },
+ { 0, "A/X/Y", "incomplete", 2, "A/X/Y" },
+ { 1, "C", "normal", 2, "A/B/C", MOVED_HERE},
+ { 1, "E", "normal", 2, "A/B/D/E", MOVED_HERE},
+ { 2, "A/B", "normal", 3, "some", MOVED_HERE },
+ { 2, "A/B/C", "base-deleted", NO_COPY_FROM, "C" },
+ { 2, "A/B/D", "normal", 3, "some/D", MOVED_HERE},
+ { 2, "A/B/D/E", "not-present", 3, "some/D/E", FALSE, "E", TRUE},
+ { 3, "A/B/C", "normal", NO_COPY_FROM},
+ { 2, "A/F", "normal", 1, "S2" },
+ { 2, "A/F/G", "normal", 1, "S2/G" },
+ { 2, "A/F/H", "not-present", 1, "S2/H" },
+ { 2, "A/F/E", "base-deleted", NO_COPY_FROM },
+ { 1, "some", "normal", 3, "some", FALSE, "A/B"},
+ { 0 }
+ };
+ SVN_ERR(insert_dirs(&b, before));
+ SVN_ERR(check_db_rows(&b, "", before)); /* Check move insertion logic */
+ SVN_ERR(verify_db(&b));
+ }
+
+ /* This should break the moves */
+ SVN_ERR(svn_wc__db_global_commit(b.wc_ctx->db,
+ sbox_wc_path(&b, "A/B"),
+ 5, 5, 700, "me", NULL, NULL,
+ FALSE, FALSE, NULL, pool));
+ {
+ nodes_row_t after[] = {
+ { 0, "", "normal", 2, "" },
+ { 0, "A", "normal", 2, "A" },
+ { 0, "A/B", "normal", 5, "A/B" },
+ { 0, "A/B/D", "normal", 5, "A/B/D"},
+ { 0, "A/B/D/E", "not-present", 5, "A/B/D/E"},
+ { 0, "A/F", "normal", 2, "A/F" },
+ { 0, "A/F/G", "normal", 2, "A/F/G" },
+ { 0, "A/F/H", "normal", 2, "A/F/H" },
+ { 0, "A/F/E", "normal", 2, "A/F/E" },
+ { 0, "A/X", "normal", 2, "A/X" },
+ { 0, "A/X/Y", "incomplete", 2, "A/X/Y" },
+ { 1, "C", "normal", 2, "A/B/C"},
+ { 1, "E", "normal", 2, "A/B/D/E"},
+ { 1, "some", "normal", 3, "some"},
+ { 3, "A/B/C", "normal", NO_COPY_FROM},
+ { 2, "A/F", "normal", 1, "S2" },
+ { 2, "A/F/G", "normal", 1, "S2/G" },
+ { 2, "A/F/H", "not-present", 1, "S2/H" },
+ { 2, "A/F/E", "base-deleted", NO_COPY_FROM },
+ { 0 }
+ };
+
+ SVN_ERR(check_db_rows(&b, "", after));
+ SVN_ERR(verify_db(&b));
+ }
+
+ SVN_ERR(svn_wc__db_global_commit(b.wc_ctx->db,
+ sbox_wc_path(&b, "A/F"),
+ 6, 6, 800, "me", NULL, NULL,
+ FALSE, FALSE, NULL, pool));
+
+ {
+ nodes_row_t after[] = {
+ { 0, "", "normal", 2, "" },
+ { 0, "A", "normal", 2, "A" },
+ { 0, "A/B", "normal", 5, "A/B" },
+ { 0, "A/B/D", "normal", 5, "A/B/D"},
+ { 0, "A/B/D/E", "not-present", 5, "A/B/D/E"},
+ { 0, "A/F", "normal", 6, "A/F" },
+ { 0, "A/F/G", "normal", 6, "A/F/G" },
+ { 0, "A/F/H", "not-present", 6, "A/F/H" },
+ { 0, "A/X", "normal", 2, "A/X" },
+ { 0, "A/X/Y", "incomplete", 2, "A/X/Y" },
+ { 1, "C", "normal", 2, "A/B/C"},
+ { 1, "E", "normal", 2, "A/B/D/E"},
+ { 1, "some", "normal", 3, "some"},
+ { 3, "A/B/C", "normal", NO_COPY_FROM },
+ { 0 }
+ };
+
+ SVN_ERR(check_db_rows(&b, "", after));
+ SVN_ERR(verify_db(&b));
+ }
+
+ SVN_ERR(svn_wc__db_global_commit(b.wc_ctx->db,
+ sbox_wc_path(&b, "A/B/C"),
+ 7, 7, 900, "me", NULL, NULL,
+ FALSE, FALSE, NULL, pool));
+
+ {
+ nodes_row_t after[] = {
+ { 0, "", "normal", 2, "" },
+ { 0, "A", "normal", 2, "A" },
+ { 0, "A/B", "normal", 5, "A/B" },
+ { 0, "A/B/C", "normal", 7, "A/B/C"},
+ { 0, "A/B/D", "normal", 5, "A/B/D"},
+ { 0, "A/B/D/E", "not-present", 5, "A/B/D/E"},
+ { 0, "A/F", "normal", 6, "A/F" },
+ { 0, "A/F/G", "normal", 6, "A/F/G" },
+ { 0, "A/F/H", "not-present", 6, "A/F/H" },
+ { 0, "A/X", "normal", 2, "A/X" },
+ { 0, "A/X/Y", "incomplete", 2, "A/X/Y" },
+ { 1, "some", "normal", 3, "some"},
+ { 1, "E", "normal", 2, "A/B/D/E"},
+ { 1, "C", "normal", 2, "A/B/C"},
+ { 0 }
+ };
+
+ SVN_ERR(check_db_rows(&b, "", after));
+ SVN_ERR(verify_db(&b));
+ }
+
+ return SVN_NO_ERROR;
+}
+
/* ---------------------------------------------------------------------- */
/* The list of test functions */
@@ -11202,6 +11469,8 @@
"move edit obstruction"),
SVN_TEST_OPTS_PASS(move_deep_bump,
"move deep bump"),
+ SVN_TEST_OPTS_PASS(test_global_commit,
+ "test global commit"),
SVN_TEST_NULL
};
diff --git a/subversion/tests/libsvn_wc/wc-test-queries.sql b/subversion/tests/libsvn_wc/wc-test-queries.sql
index fb66194..613819a 100644
--- a/subversion/tests/libsvn_wc/wc-test-queries.sql
+++ b/subversion/tests/libsvn_wc/wc-test-queries.sql
@@ -44,8 +44,10 @@
-- STMT_INSERT_NODE
INSERT INTO nodes (local_relpath, op_depth, presence, repos_path,
- revision, parent_relpath, wc_id, repos_id, kind, depth)
- VALUES (?1, ?2, ?3, ?4, ?5, ?6, 1,
+ revision, parent_relpath, moved_to, moved_here,
+ properties, wc_id, repos_id, kind,
+ depth)
+ VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, 1,
CASE WHEN ?3 != 'base-deleted' THEN 1 END,
'dir',
CASE WHEN ?3 in ('normal', 'incomplete')