Author: rhuijben
Date: Fri Jan 11 12:05:55 2013
New Revision: 1432006
URL: http://svn.apache.org/viewvc?rev=1432006&view=rev
Log:
To fix issue #4288, detect obstructions when opening nodes for updating.
Continue processing property changes and the pristine text (And children),
but don't touch the in-wc node.
* subversion/libsvn_wc/update_editor.c
(dir_baton): Add edit_obstructed boolean.
(make_dir_baton): Handle as shadowed below an obstruction.
(file_baton): Add edit_obstructed boolean.
(make_file_baton): Handle as shadowed below an obstruction.
(modcheck_baton_t): Remove variables.
(modcheck_callback): Remove unused child-of-copy handling. Handle missing
and obstructed as a local change.
(node_has_local_mods): Remove unused flag.
(check_tree_conflict): Add expected kind argument. Compare in-wc kind for
status normal-edit processing. Update caller.
(delete_entry): Update caller. Handle obstructed. Handle parent is
obstructed.
(add_directory): Update caller. Fix reference in comment. Use same notify for
exists as file.
(open_directory): Update caller. Handle new conflict type.
(close_directory): Handle edit_obstructed.
(add_file): Update caller.
(open_file): Update caller. Handle new conflict type.
(merge_file): Add edit_obstructed to assertion.
(close_file): Handle edit_obstructed.
* subversion/tests/cmdline/update_tests.py
(update_replace_obstruction): Rename test to...
(update_edit_delete_obstruction): ... this, remove XFail and fill in the
expected results. Add obstructions that are just modified.
Modified:
subversion/trunk/subversion/libsvn_wc/update_editor.c
subversion/trunk/subversion/tests/cmdline/update_tests.py
Modified: subversion/trunk/subversion/libsvn_wc/update_editor.c
URL:
http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_wc/update_editor.c?rev=1432006&r1=1432005&r2=1432006&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_wc/update_editor.c (original)
+++ subversion/trunk/subversion/libsvn_wc/update_editor.c Fri Jan 11 12:05:55
2013
@@ -322,6 +322,10 @@ struct dir_baton
marked as deleted. */
svn_boolean_t shadowed;
+ /* Set on a node when the existing node is obstructed, and the edit operation
+ continues as semi-shadowed update */
+ svn_boolean_t edit_obstructed;
+
/* The (new) changed_* information, cached to avoid retrieving it later */
svn_revnum_t changed_rev;
apr_time_t changed_date;
@@ -621,7 +625,7 @@ make_dir_baton(struct dir_baton **d_p,
if (pb)
{
d->skip_this = pb->skip_this;
- d->shadowed = pb->shadowed;
+ d->shadowed = pb->shadowed || pb->edit_obstructed;
}
/* The caller of this function needs to fill these in. */
@@ -734,6 +738,10 @@ struct file_baton
in the working copy (replaced, deleted, etc.). */
svn_boolean_t shadowed;
+ /* Set on a node when the existing node is obstructed, and the edit operation
+ continues as semi-shadowed update */
+ svn_boolean_t edit_obstructed;
+
/* The (new) changed_* information, cached to avoid retrieving it later */
svn_revnum_t changed_rev;
apr_time_t changed_date;
@@ -830,7 +838,7 @@ make_file_baton(struct file_baton **f_p,
f->obstruction_found = FALSE;
f->add_existed = FALSE;
f->skip_this = pb->skip_this;
- f->shadowed = pb->shadowed;
+ f->shadowed = pb->shadowed || pb->edit_obstructed;
f->dir_baton = pb;
f->changed_rev = SVN_INVALID_REVNUM;
@@ -1232,8 +1240,6 @@ typedef struct modcheck_baton_t {
svn_wc__db_t *db; /* wc_db to access nodes */
svn_boolean_t found_mod; /* whether a modification has been found */
svn_boolean_t found_not_delete; /* Found a not-delete modification */
- svn_boolean_t is_copy; /* check for post-copy modifications only */
- const char *modcheck_root_abspath;
} modcheck_baton_t;
/* An implementation of svn_wc_status_func4_t. */
@@ -1261,30 +1267,13 @@ modcheck_callback(void *baton,
case svn_wc_status_missing:
case svn_wc_status_obstructed:
- if (status->prop_status != svn_wc_status_modified)
- break;
-
mb->found_mod = TRUE;
mb->found_not_delete = TRUE;
/* Exit from the status walker: We know what we want to know */
return svn_error_create(SVN_ERR_CEASE_INVOCATION, NULL, NULL);
- case svn_wc_status_added:
- /* Ignore the copy status if we expect a copy, but don't ignore
- other changes */
- if (!mb->is_copy
- || status->text_status == svn_wc_status_modified
- || status->prop_status == svn_wc_status_modified
- || strcmp(mb->modcheck_root_abspath, local_abspath) != 0)
- {
- mb->found_mod = TRUE;
- mb->found_not_delete = TRUE;
- /* Exit from the status walker: We know what we want to know */
- return svn_error_create(SVN_ERR_CEASE_INVOCATION, NULL, NULL);
- }
- break;
-
default:
+ case svn_wc_status_added:
case svn_wc_status_replaced:
case svn_wc_status_modified:
mb->found_mod = TRUE;
@@ -1301,25 +1290,20 @@ modcheck_callback(void *baton,
* tree rooted at LOCAL_ABSPATH, using DB. If *MODIFIED
* is set to true and all the local modifications were deletes then set
* *ALL_EDITS_ARE_DELETES to true, set it to false otherwise. LOCAL_ABSPATH
- * may be a file or a directory. If IS_COPY is TRUE, LOCAL_ABSPATH is a
- * copied (or moved) node, and nodes at or within LOCAL_ABSPATH are only
- * considered modified if they were modified post-copy. */
+ * may be a file or a directory. */
static svn_error_t *
node_has_local_mods(svn_boolean_t *modified,
svn_boolean_t *all_edits_are_deletes,
- svn_boolean_t is_copy,
svn_wc__db_t *db,
const char *local_abspath,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *scratch_pool)
{
- modcheck_baton_t modcheck_baton = { NULL, FALSE, FALSE, FALSE };
+ modcheck_baton_t modcheck_baton = { NULL, FALSE, FALSE };
svn_error_t *err;
modcheck_baton.db = db;
- modcheck_baton.is_copy = is_copy;
- modcheck_baton.modcheck_root_abspath = local_abspath;
/* Walk the WC tree for status with depth infinity, looking for any local
* modifications. If it's a "sparse" directory, that's OK: there can be
@@ -1353,9 +1337,9 @@ node_has_local_mods(svn_boolean_t *modif
* The edit baton EB gives information including whether the operation is
* an update or a switch.
*
- * WORKING_STATUS and WORKING_KIND are the current node status of LOCAL_ABSPATH
+ * WORKING_STATUS is the current node status of LOCAL_ABSPATH
* and EXISTS_IN_REPOS specifies whether a BASE_NODE representation for exists
- * for this node.
+ * for this node. In that case the on disk type is compared to EXPECTED_KIND.
*
* If a tree conflict reason was found for the incoming action, the resulting
* tree conflict info is returned in *PCONFLICT. PCONFLICT must be non-NULL,
@@ -1370,6 +1354,7 @@ check_tree_conflict(svn_skel_t **pconfli
const char *local_abspath,
svn_wc__db_status_t working_status,
svn_boolean_t exists_in_repos,
+ svn_node_kind_t expected_kind,
svn_wc_conflict_action_t action,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
@@ -1444,10 +1429,29 @@ check_tree_conflict(svn_skel_t **pconfli
* missing information (hopefully). */
case svn_wc__db_status_normal:
if (action == svn_wc_conflict_action_edit)
- /* An edit onto a local edit or onto *no* local changes is no
- * tree-conflict. (It's possibly a text- or prop-conflict,
- * but we don't handle those here.) */
- return SVN_NO_ERROR;
+ {
+ /* An edit onto a local edit or onto *no* local changes is no
+ * tree-conflict. (It's possibly a text- or prop-conflict,
+ * but we don't handle those here.)
+ *
+ * Except when there is a local obstruction
+ */
+ if (exists_in_repos)
+ {
+ svn_node_kind_t disk_kind;
+
+ SVN_ERR(svn_io_check_path(local_abspath, &disk_kind,
+ scratch_pool));
+
+ if (disk_kind != expected_kind && disk_kind != svn_node_none)
+ {
+ reason = svn_wc_conflict_reason_obstructed;
+ break;
+ }
+
+ }
+ return SVN_NO_ERROR;
+ }
/* Replace is handled as delete and then specifically in
add_directory() and add_file(), so we only expect deletes here */
@@ -1461,7 +1465,7 @@ check_tree_conflict(svn_skel_t **pconfli
* not visit the subdirectories of a directory that it wants to delete.
* Therefore, we need to start a separate crawl here. */
- SVN_ERR(node_has_local_mods(&modified, &all_mods_are_deletes, FALSE,
+ SVN_ERR(node_has_local_mods(&modified, &all_mods_are_deletes,
eb->db, local_abspath,
eb->cancel_func, eb->cancel_baton,
scratch_pool));
@@ -1500,6 +1504,7 @@ check_tree_conflict(svn_skel_t **pconfli
/* Sanity checks. Note that if there was no action on the node, this function
* would not have been called in the first place.*/
if (reason == svn_wc_conflict_reason_edited
+ || reason == svn_wc_conflict_reason_obstructed
|| reason == svn_wc_conflict_reason_deleted
|| reason == svn_wc_conflict_reason_moved_away
|| reason == svn_wc_conflict_reason_replaced)
@@ -1716,10 +1721,13 @@ delete_entry(const char *path,
/* Check for conflicts only when we haven't already recorded
* a tree-conflict on a parent node. */
- if (!pb->shadowed)
+ if (!pb->shadowed && !pb->edit_obstructed)
{
SVN_ERR(check_tree_conflict(&tree_conflict, eb, local_abspath,
status, TRUE,
+ (kind == svn_kind_dir)
+ ? svn_node_dir
+ : svn_node_file,
svn_wc_conflict_action_delete,
pb->pool, scratch_pool));
}
@@ -1742,7 +1750,8 @@ delete_entry(const char *path,
tree_conflict,
scratch_pool, scratch_pool));
- if (reason == svn_wc_conflict_reason_edited)
+ if (reason == svn_wc_conflict_reason_edited
+ || reason == svn_wc_conflict_reason_obstructed)
{
/* The item exists locally and has some sort of local mod.
* It no longer exists in the repository at its target URL@REV.
@@ -1816,7 +1825,7 @@ delete_entry(const char *path,
svn_wc_notify_action_t action = svn_wc_notify_update_delete;
svn_node_kind_t node_kind;
- if (pb->shadowed)
+ if (pb->shadowed || pb->edit_obstructed)
action = svn_wc_notify_update_shadowed_delete;
if (kind == svn_kind_dir)
@@ -2091,7 +2100,7 @@ add_directory(const char *path,
{
SVN_ERR(check_tree_conflict(&tree_conflict, eb,
db->local_abspath,
- status, FALSE,
+ status, FALSE, svn_node_none,
svn_wc_conflict_action_add,
pool, pool));
}
@@ -2160,7 +2169,7 @@ add_directory(const char *path,
/* If this add was obstructed by dir scheduled for addition without
- history let close_file() handle the notification because there
+ history let close_directory() handle the notification because there
might be properties to deal with. If PATH was added inside a locally
deleted tree, then suppress notification, a tree conflict was already
issued. */
@@ -2170,7 +2179,7 @@ add_directory(const char *path,
if (db->shadowed)
action = svn_wc_notify_update_shadowed_add;
- else if (db->obstruction_found)
+ else if (db->obstruction_found || db->add_existed)
action = svn_wc_notify_exists;
else
action = svn_wc_notify_update_add;
@@ -2277,7 +2286,7 @@ open_directory(const char *path,
* a tree-conflict on a parent node. */
if (!db->shadowed)
SVN_ERR(check_tree_conflict(&tree_conflict, eb, db->local_abspath,
- status, TRUE,
+ status, TRUE, svn_node_dir,
svn_wc_conflict_action_edit,
db->pool, pool));
@@ -2294,10 +2303,14 @@ open_directory(const char *path,
db->pool, db->pool));
SVN_ERR_ASSERT(reason == svn_wc_conflict_reason_deleted
|| reason == svn_wc_conflict_reason_moved_away
- || reason == svn_wc_conflict_reason_replaced);
+ || reason == svn_wc_conflict_reason_replaced
+ || reason == svn_wc_conflict_reason_obstructed);
/* Continue updating BASE */
- db->shadowed = TRUE;
+ if (reason == svn_wc_conflict_reason_obstructed)
+ db->edit_obstructed = TRUE;
+ else
+ db->shadowed = TRUE;
}
/* Mark directory as being at target_revision and URL, but incomplete. */
@@ -2776,7 +2789,7 @@ close_directory(void *dir_baton,
svn_wc_notify_t *notify;
svn_wc_notify_action_t action;
- if (db->shadowed)
+ if (db->shadowed || db->edit_obstructed)
action = svn_wc_notify_update_shadowed_update;
else if (db->obstruction_found || db->add_existed)
action = svn_wc_notify_exists;
@@ -3165,7 +3178,7 @@ add_file(const char *path,
{
SVN_ERR(check_tree_conflict(&tree_conflict, eb,
fb->local_abspath,
- status, FALSE,
+ status, FALSE, svn_node_none,
svn_wc_conflict_action_add,
scratch_pool, scratch_pool));
}
@@ -3329,13 +3342,11 @@ open_file(const char *path,
return SVN_NO_ERROR;
}
- fb->shadowed = pb->shadowed;
-
/* Check for conflicts only when we haven't already recorded
* a tree-conflict on a parent node. */
if (!fb->shadowed)
SVN_ERR(check_tree_conflict(&tree_conflict, eb, fb->local_abspath,
- status, TRUE,
+ status, TRUE, svn_node_file,
svn_wc_conflict_action_edit,
fb->pool, scratch_pool));
@@ -3349,13 +3360,17 @@ open_file(const char *path,
SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, NULL,
eb->db, fb->local_abspath,
tree_conflict,
- fb->pool, fb->pool));
+ scratch_pool, scratch_pool));
SVN_ERR_ASSERT(reason == svn_wc_conflict_reason_deleted
|| reason == svn_wc_conflict_reason_moved_away
- || reason == svn_wc_conflict_reason_replaced);
+ || reason == svn_wc_conflict_reason_replaced
+ || reason == svn_wc_conflict_reason_obstructed);
/* Continue updating BASE */
- fb->shadowed = TRUE;
+ if (reason == svn_wc_conflict_reason_obstructed)
+ fb->edit_obstructed = TRUE;
+ else
+ fb->shadowed = TRUE;
}
svn_pool_destroy(scratch_pool);
@@ -3766,7 +3781,9 @@ merge_file(svn_skel_t **work_items,
svn_boolean_t is_locally_modified;
enum svn_wc_merge_outcome_t merge_outcome = svn_wc_merge_unchanged;
- SVN_ERR_ASSERT(! fb->shadowed && !fb->obstruction_found);
+ SVN_ERR_ASSERT(! fb->shadowed
+ && ! fb->obstruction_found
+ && ! fb->edit_obstructed);
/*
When this function is called on file F, we assume the following
@@ -4137,7 +4154,7 @@ close_file(void *file_baton,
SVN_ERR_ASSERT(new_base_props != NULL && new_actual_props != NULL);
/* Merge the text. This will queue some additional work. */
- if (!fb->obstruction_found)
+ if (!fb->obstruction_found && !fb->edit_obstructed)
{
svn_error_t *err;
err = merge_file(&work_item, &conflict_skel,
@@ -4368,7 +4385,7 @@ close_file(void *file_baton,
if (fb->edited)
{
- if (fb->shadowed)
+ if (fb->shadowed || fb->edit_obstructed)
action = fb->adding_file
? svn_wc_notify_update_shadowed_add
: svn_wc_notify_update_shadowed_update;
Modified: subversion/trunk/subversion/tests/cmdline/update_tests.py
URL:
http://svn.apache.org/viewvc/subversion/trunk/subversion/tests/cmdline/update_tests.py?rev=1432006&r1=1432005&r2=1432006&view=diff
==============================================================================
--- subversion/trunk/subversion/tests/cmdline/update_tests.py (original)
+++ subversion/trunk/subversion/tests/cmdline/update_tests.py Fri Jan 11
12:05:55 2013
@@ -5815,35 +5815,75 @@ def update_with_parents_and_exclude(sbox
sbox.ospath('A/B'))
@Issue(4288)
-@XFail()
-def update_replace_obstruction(sbox):
- "delete shouldn't cause update failure"
+def update_edit_delete_obstruction(sbox):
+ "obstructions shouldn't cause update failures"
sbox.build()
wc_dir = sbox.wc_dir
# r2
sbox.simple_rm('A/B','iota')
+ svntest.main.file_append(sbox.ospath('A/mu'), "File change")
+ sbox.simple_propset('key', 'value', 'A/D', 'A/D/G')
sbox.simple_commit()
# r3
- sbox.simple_mkdir('iota')
- sbox.simple_copy('A/D/gamma', 'A/B')
- sbox.simple_commit()
+ #sbox.simple_mkdir('iota')
+ #sbox.simple_copy('A/D/gamma', 'A/B')
+ #sbox.simple_commit()
sbox.simple_update('', 1)
# Create obstructions
svntest.main.safe_rmtree(sbox.ospath('A/B'))
+ svntest.main.file_append(sbox.ospath('A/B'), "Obstruction")
+
+ svntest.main.safe_rmtree(sbox.ospath('A/D'))
+ svntest.main.file_append(sbox.ospath('A/D'), "Obstruction")
+
os.remove(sbox.ospath('iota'))
os.mkdir(sbox.ospath('iota'))
- svntest.main.file_append(sbox.ospath('A/B'), "Obstruction")
- # ### This expected result is not OK yet, currently the update
- # ### just fails with an error before doing any work
- expected_status = svntest.actions.get_virginal_state(wc_dir, 2)
- expected_disk = svntest.main.greek_state.copy()
+ os.remove(sbox.ospath('A/mu'))
+ os.mkdir(sbox.ospath('A/mu'))
+
+ expected_status = svntest.wc.State(wc_dir, {
+ '' : Item(status=' ', wc_rev='2'),
+ 'A' : Item(status=' ', wc_rev='2'),
+ 'A/mu' : Item(status='~ ', treeconflict='C', wc_rev='2'),
+ 'A/D' : Item(status='~ ', treeconflict='C', wc_rev='2'),
+ 'A/D/G' : Item(status='! ', wc_rev='2'),
+ 'A/D/G/pi' : Item(status='! ', wc_rev='2'),
+ 'A/D/G/tau' : Item(status='! ', wc_rev='2'),
+ 'A/D/G/rho' : Item(status='! ', wc_rev='2'),
+ 'A/D/H' : Item(status='! ', wc_rev='2'),
+ 'A/D/H/omega' : Item(status='! ', wc_rev='2'),
+ 'A/D/H/chi' : Item(status='! ', wc_rev='2'),
+ 'A/D/H/psi' : Item(status='! ', wc_rev='2'),
+ 'A/D/gamma' : Item(status='! ', wc_rev='2'),
+ 'A/C' : Item(status=' ', wc_rev='2'),
+ 'A/B' : Item(status='~ ', treeconflict='C', wc_rev='-'),
+ 'A/B/F' : Item(status='! ', wc_rev='-'),
+ 'A/B/E' : Item(status='! ', wc_rev='-'),
+ 'A/B/E/beta' : Item(status='! ', wc_rev='-'),
+ 'A/B/E/alpha' : Item(status='! ', wc_rev='-'),
+ 'A/B/lambda' : Item(status='! ', wc_rev='-'),
+ 'iota' : Item(status='~ ', treeconflict='C', wc_rev='-'),
+ })
+ expected_disk = svntest.wc.State('', {
+ 'A/D' : Item(contents="Obstruction", props={'key':'value'}),
+ 'A/C' : Item(),
+ 'A/B' : Item(contents="Obstruction"),
+ 'A/mu' : Item(),
+ 'iota' : Item(),
+ })
+
expected_output = svntest.wc.State(wc_dir, {
+ 'iota' : Item(status=' ', treeconflict='C'),
+ 'A/mu' : Item(status=' ', treeconflict='C'),
+ 'A/D' : Item(status=' ', treeconflict='C'),
+ 'A/D/G' : Item(status=' ', treeconflict='U'),
+ 'A/B' : Item(status=' ', treeconflict='C'),
})
# And now update to delete B and iota
@@ -5930,7 +5970,7 @@ test_list = [ None,
update_move_text_mod,
update_nested_move_text_mod,
update_with_parents_and_exclude,
- update_replace_obstruction,
+ update_edit_delete_obstruction,
]
if __name__ == '__main__':