Author: stsp
Date: Fri Oct 14 13:40:29 2011
New Revision: 1183358
URL: http://svn.apache.org/viewvc?rev=1183358&view=rev
Log:
Auto-merge incoming deletions into locally moved directories on update.
Nodes which weren't modified post-move, and which are deleted at their
pre-move location during an update, are now automatically scheduled for
deletion at their post-move location, instead of becoming tree-conflict
victims. However, incoming deletions still cause a tree conflict at the
pre-move location if they want to delete nodes which were modified post-move.
Moved-here directories within which the only post-move modifications are
deleted children are not considered modified and will be deleted.
(This exposes a bug where deleting a moved-here node doesn't transform
the move into a normal delete at the pre-move location. This problem
will be fixed separately.)
* subversion/include/svn_wc.h
(svn_wc_conflict_reason_t): Add svn_wc_conflict_reason_moved_away_and_edited.
This new reason is needed so we can later differentiate the TC case handled
by this commit from pure move vs. move conflicts (i.e. where incoming and
local moves do not agree).
* subversion/tests/cmdline/tree_conflict_tests.py
(d_dels): Seperate out moves into ...
(d_moves): .. this, so tests can adjust their expectations for moves
and deletions differently.
(up_sw_dir_mod_onto_del, up_sw_dir_del_onto_del): As a result of the above
change, these two tests no longer expect a tree conflict for incoming
deletions into locally moved directories.
(up_sw_dir_del_onto_mod, up_sw_dir_del_onto_del,
(merge_dir_mod_onto_not_dir): Make sure test expectations do not change
yet in these test cases by adding d_moves to d_dels.
* subversion/tests/cmdline/update_tests.py
(update_moved_dir_leaf_del, update_moved_dir_edited_leaf_del): New tests.
These verify the new behaviour for files which were modified post-move.
Tests involving directories modifed post-move still need to be added,
but some manual testing has confirmed that they work, too.
* subversion/svn/tree-conflicts.c
(map_conflict_reason_h, map_conflict_reason_x): Add entries for
svn_wc_conflict_reason_moved_away_and_edited.
* subversion/libsvn_wc/update_editor.c
(dir_baton, make_dir_baton): Add MOVED_TO_PATH, for tree-conflict detection.
(open_root, open_directory): Check if the directory was moved away, and
if so store the moved-to path in the dir baton. Also let
check_tree_conflict() know that the node was moved-away, so it can
make an informed decision about whether a conflict should be flagged.
(modcheck_baton): Add IS_COPY.
(modcheck_callback): If IS_COPY is TRUE, only take post-copy modifications
into account, but not the fact that the node was copied/moved here.
(node_has_local_mods): Add IS_COPY parameter, same purpose as above.
(create_tree_conflict, open_directory, open_file): Make assertions
recognize svn_wc_conflict_reason_moved_away_and_edited.
(check_tree_conflict): Do not flag a tree conflict if an incoming delete
maps onto a moved node which was not modified post-move.
Update an assertion to include svn_wc_conflict_reason_moved_away_and_edited.
(delete_entry): Handle moved-away nodes such that check_tree_conflict()
doesn't flag them as a tree-conflict victim if applicable. If an
incoming delete of a moved-away node does not cause a tree-conflict,
schedule the node for deletion at its post-move location, and use
the node's post-move path in notifications.
* subversion/libsvn_wc/tree_conflicts.c
(svn_wc__conflict_reason_map): Add map entry for
svn_wc_conflict_reason_moved_away_and_edited.
Modified:
subversion/trunk/subversion/include/svn_wc.h
subversion/trunk/subversion/libsvn_wc/tree_conflicts.c
subversion/trunk/subversion/libsvn_wc/update_editor.c
subversion/trunk/subversion/svn/tree-conflicts.c
subversion/trunk/subversion/tests/cmdline/tree_conflict_tests.py
subversion/trunk/subversion/tests/cmdline/update_tests.py
Modified: subversion/trunk/subversion/include/svn_wc.h
URL:
http://svn.apache.org/viewvc/subversion/trunk/subversion/include/svn_wc.h?rev=1183358&r1=1183357&r2=1183358&view=diff
==============================================================================
--- subversion/trunk/subversion/include/svn_wc.h (original)
+++ subversion/trunk/subversion/include/svn_wc.h Fri Oct 14 13:40:29 2011
@@ -1554,6 +1554,8 @@ typedef enum svn_wc_conflict_reason_t
svn_wc_conflict_reason_replaced,
/** Object is moved away. @since New in 1.8. */
svn_wc_conflict_reason_moved_away,
+ /** Object is moved away and was edited post-move. @since New in 1.8. */
+ svn_wc_conflict_reason_moved_away_and_edited,
/** Object is moved here. @since New in 1.8. */
svn_wc_conflict_reason_moved_here
Modified: subversion/trunk/subversion/libsvn_wc/tree_conflicts.c
URL:
http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_wc/tree_conflicts.c?rev=1183358&r1=1183357&r2=1183358&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_wc/tree_conflicts.c (original)
+++ subversion/trunk/subversion/libsvn_wc/tree_conflicts.c Fri Oct 14 13:40:29
2011
@@ -76,8 +76,9 @@ const svn_token_map_t svn_wc__conflict_r
{ "added", svn_wc_conflict_reason_added },
{ "replaced", svn_wc_conflict_reason_replaced },
{ "unversioned", svn_wc_conflict_reason_unversioned },
- { "moved-here", svn_wc_conflict_reason_moved_here },
{ "moved-away", svn_wc_conflict_reason_moved_away },
+ { "moved-away-and-edited", svn_wc_conflict_reason_moved_away_and_edited },
+ { "moved-here", svn_wc_conflict_reason_moved_here },
{ NULL }
};
Modified: subversion/trunk/subversion/libsvn_wc/update_editor.c
URL:
http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_wc/update_editor.c?rev=1183358&r1=1183357&r2=1183358&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_wc/update_editor.c (original)
+++ subversion/trunk/subversion/libsvn_wc/update_editor.c Fri Oct 14 13:40:29
2011
@@ -283,6 +283,11 @@ struct dir_baton
/* Absolute path of this directory */
const char *local_abspath;
+ /* Absolute path to the new location of the directory if it was moved away.
+ * This is set on the root of a move operation and all children.
+ * If the directory was not moved away, this is NULL. */
+ const char *moved_to_abspath;
+
/* The repository relative path this directory will correspond to. */
const char *new_relpath;
@@ -617,6 +622,7 @@ make_dir_baton(struct dir_baton **d_p,
d->adding_dir = adding;
d->changed_rev = SVN_INVALID_REVNUM;
d->not_present_files = apr_hash_make(dir_pool);
+ d->moved_to_abspath = NULL;
/* Copy some flags from the parent baton */
if (pb)
@@ -1083,6 +1089,7 @@ open_root(void *edit_baton,
struct dir_baton *db;
svn_boolean_t already_conflicted;
svn_error_t *err;
+ svn_wc__db_status_t status;
/* Note that something interesting is actually happening in this
edit run. */
@@ -1118,7 +1125,6 @@ open_root(void *edit_baton,
if (*eb->target_basename == '\0')
{
/* For an update with a NULL target, this is equivalent to open_dir(): */
- svn_wc__db_status_t status;
/* Read the depth from the entry. */
SVN_ERR(svn_wc__db_base_get_info(&status, NULL, NULL, NULL, NULL, NULL,
@@ -1136,6 +1142,25 @@ open_root(void *edit_baton,
pool));
}
+ /* Check if this directory was moved away. */
+ err = svn_wc__db_read_info(&status, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL,
+ eb->db, db->local_abspath, pool, pool);
+ if (err)
+ {
+ if (err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
+ svn_error_clear(err);
+ else
+ return svn_error_trace(err);
+ }
+ else if (status == svn_wc__db_status_deleted)
+ SVN_ERR(svn_wc__db_scan_deletion(NULL, &db->moved_to_abspath, NULL,
+ NULL, eb->db, db->local_abspath,
+ db->pool, pool));
+
return SVN_NO_ERROR;
}
@@ -1148,6 +1173,7 @@ 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 */
} modcheck_baton_t;
/* An implementation of svn_wc_status_func4_t. */
@@ -1178,8 +1204,17 @@ modcheck_callback(void *baton,
break;
/* Fall through in the found modification case */
- default:
case svn_wc_status_added:
+ if (!mb->is_copy)
+ {
+ 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_replaced:
case svn_wc_status_modified:
mb->found_mod = TRUE;
@@ -1196,20 +1231,24 @@ 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. */
+ * 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. */
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 };
+ modcheck_baton_t modcheck_baton = { NULL, FALSE, FALSE, FALSE };
svn_error_t *err;
modcheck_baton.db = db;
+ modcheck_baton.is_copy = is_copy;
/* 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
@@ -1332,6 +1371,7 @@ create_tree_conflict(svn_wc_conflict_des
SVN_ERR_ASSERT(reason == svn_wc_conflict_reason_edited
|| reason == svn_wc_conflict_reason_deleted
|| reason == svn_wc_conflict_reason_moved_away
+ || reason == svn_wc_conflict_reason_moved_away_and_edited
|| reason == svn_wc_conflict_reason_replaced
|| reason == svn_wc_conflict_reason_obstructed);
@@ -1527,6 +1567,26 @@ check_tree_conflict(svn_wc_conflict_desc
case svn_wc__db_status_deleted:
if (!moved_to_abspath)
reason = svn_wc_conflict_reason_deleted;
+ else if (action == svn_wc_conflict_action_delete)
+ {
+ svn_boolean_t all_edits_are_deletes = FALSE;
+
+ /* The update wants to delete a node which was locally moved
+ * away. We allow this only if the node wasn't modified post-move.
+ * If the only post-move changes within a subtree are deletions,
+ * allow the update to delete the entire subtree. */
+ if (working_kind == svn_kind_dir)
+ SVN_ERR(node_has_local_mods(&modified, &all_edits_are_deletes,
+ TRUE, eb->db, moved_to_abspath,
+ eb->cancel_func, eb->cancel_baton,
+ scratch_pool));
+ else
+ SVN_ERR(svn_wc__internal_file_modified_p(&modified, eb->db,
+ moved_to_abspath,
+ FALSE, scratch_pool));
+ if (modified && !all_edits_are_deletes)
+ reason = svn_wc_conflict_reason_moved_away_and_edited;
+ }
break;
case svn_wc__db_status_incomplete:
@@ -1551,7 +1611,7 @@ check_tree_conflict(svn_wc_conflict_desc
* 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,
+ SVN_ERR(node_has_local_mods(&modified, &all_mods_are_deletes, FALSE,
eb->db, local_abspath,
eb->cancel_func, eb->cancel_baton,
scratch_pool));
@@ -1592,6 +1652,7 @@ check_tree_conflict(svn_wc_conflict_desc
if (reason == svn_wc_conflict_reason_edited
|| reason == svn_wc_conflict_reason_deleted
|| reason == svn_wc_conflict_reason_moved_away
+ || reason == svn_wc_conflict_reason_moved_away_and_edited
|| reason == svn_wc_conflict_reason_replaced)
/* When the node existed before (it was locally deleted, replaced or
* edited), then 'update' cannot add it "again". So it can only send
@@ -1706,6 +1767,7 @@ delete_entry(const char *path,
const char *base = svn_relpath_basename(path, NULL);
const char *local_abspath;
const char *repos_relpath;
+ const char *moved_to_abspath = NULL;
svn_kind_t kind, base_kind;
svn_boolean_t conflicted;
svn_boolean_t have_base;
@@ -1805,6 +1867,13 @@ delete_entry(const char *path,
return SVN_NO_ERROR;
}
+ /* Has the to-be-deleted node been moved away? */
+ if (status == svn_wc__db_status_deleted)
+ SVN_ERR(svn_wc__db_scan_deletion(NULL, &moved_to_abspath, NULL,
+ NULL, eb->db, local_abspath,
+ pool, pool));
+
+
/* Is this path the victim of a newly-discovered tree conflict? If so,
* remember it and notify the client. Then (if it was existing and
* modified), re-schedule the node to be added back again, as a (modified)
@@ -1817,7 +1886,8 @@ delete_entry(const char *path,
SVN_ERR(check_tree_conflict(&tree_conflict, eb, local_abspath,
status, kind, TRUE,
svn_wc_conflict_action_delete, svn_node_none,
- repos_relpath, NULL, pb->pool,
scratch_pool));
+ repos_relpath, moved_to_abspath,
+ pb->pool, scratch_pool));
}
if (tree_conflict != NULL)
@@ -1856,6 +1926,8 @@ delete_entry(const char *path,
}
else if (tree_conflict->reason == svn_wc_conflict_reason_deleted
|| tree_conflict->reason == svn_wc_conflict_reason_moved_away
+ || tree_conflict->reason ==
+ svn_wc_conflict_reason_moved_away_and_edited
|| tree_conflict->reason == svn_wc_conflict_reason_replaced)
{
/* The item does not exist locally because it was already shadowed.
@@ -1867,6 +1939,23 @@ delete_entry(const char *path,
else
SVN_ERR_MALFUNCTION(); /* other reasons are not expected here */
}
+ else if (moved_to_abspath)
+ {
+ /* No tree conflict was flagged, and the node was moved-away.
+ * Automatically merge the incoming deletion with the local move
+ * by deleting the node from the moved-away subtree. */
+ /* ### This should probably use a work queue. */
+ SVN_ERR(svn_wc__db_op_delete(eb->db, moved_to_abspath, NULL,
+ NULL, NULL, /* notify below */
+ eb->cancel_func, eb->cancel_baton,
+ scratch_pool));
+ if (kind == svn_kind_dir)
+ SVN_ERR(svn_io_remove_dir2(moved_to_abspath, TRUE,
+ eb->cancel_func, eb->cancel_baton,
+ scratch_pool));
+ else
+ SVN_ERR(svn_io_remove_file2(moved_to_abspath, TRUE, scratch_pool));
+ }
/* Issue a wq operation to delete the BASE_NODE data and to delete actual
nodes based on that from disk, but leave any WORKING_NODEs on disk.
@@ -1917,7 +2006,8 @@ delete_entry(const char *path,
else
node_kind = svn_node_file;
- do_notification(eb, local_abspath, node_kind, action, scratch_pool);
+ do_notification(eb, moved_to_abspath ? moved_to_abspath : local_abspath,
+ node_kind, action, scratch_pool);
}
svn_pool_destroy(scratch_pool);
@@ -2287,6 +2377,7 @@ open_directory(const char *path,
svn_wc_conflict_description2_t *tree_conflict = NULL;
svn_wc__db_status_t status, base_status;
svn_kind_t wc_kind;
+ svn_error_t *err;
SVN_ERR(make_dir_baton(&db, path, eb, pb, FALSE, pool));
*child_baton = db;
@@ -2361,13 +2452,33 @@ open_directory(const char *path,
/* Is this path a fresh tree conflict victim? If so, skip the tree
with one notification. */
+ /* Check if this directory was moved away. */
+ err = svn_wc__db_read_info(&status, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL,
+ eb->db, db->local_abspath, pool, pool);
+ if (err)
+ {
+ if (err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
+ svn_error_clear(err);
+ else
+ return svn_error_trace(err);
+ }
+ else if (status == svn_wc__db_status_deleted)
+ SVN_ERR(svn_wc__db_scan_deletion(NULL, &db->moved_to_abspath, NULL,
+ NULL, eb->db, db->local_abspath,
+ db->pool, pool));
+
/* Check for conflicts only when we haven't already recorded
* a tree-conflict on a parent node. */
if (!db->shadowed)
SVN_ERR(check_tree_conflict(&tree_conflict, eb, db->local_abspath,
status, wc_kind, TRUE,
svn_wc_conflict_action_edit, svn_node_dir,
- db->new_relpath, NULL, db->pool, pool));
+ db->new_relpath, db->moved_to_abspath,
+ db->pool, pool));
/* Remember the roots of any locally deleted trees. */
if (tree_conflict != NULL)
@@ -2377,6 +2488,8 @@ open_directory(const char *path,
SVN_ERR_ASSERT(
tree_conflict->reason == svn_wc_conflict_reason_deleted ||
tree_conflict->reason == svn_wc_conflict_reason_moved_away ||
+ tree_conflict->reason ==
+ svn_wc_conflict_reason_moved_away_and_edited ||
tree_conflict->reason == svn_wc_conflict_reason_replaced);
/* Continue updating BASE */
@@ -3377,7 +3490,9 @@ open_file(const char *path,
/* Other modifications wouldn't be a tree conflict */
SVN_ERR_ASSERT(
tree_conflict->reason == svn_wc_conflict_reason_deleted ||
- tree_conflict->reason == svn_wc_conflict_reason_moved_away||
+ tree_conflict->reason == svn_wc_conflict_reason_moved_away ||
+ tree_conflict->reason ==
+ svn_wc_conflict_reason_moved_away_and_edited ||
tree_conflict->reason == svn_wc_conflict_reason_replaced);
/* Continue updating BASE */
Modified: subversion/trunk/subversion/svn/tree-conflicts.c
URL:
http://svn.apache.org/viewvc/subversion/trunk/subversion/svn/tree-conflicts.c?rev=1183358&r1=1183357&r2=1183358&view=diff
==============================================================================
--- subversion/trunk/subversion/svn/tree-conflicts.c (original)
+++ subversion/trunk/subversion/svn/tree-conflicts.c Fri Oct 14 13:40:29 2011
@@ -63,6 +63,7 @@ static const svn_token_map_t map_conflic
{ N_("replace"), svn_wc_conflict_reason_replaced },
{ N_("unversioned"), svn_wc_conflict_reason_unversioned },
{ N_("moved away"), svn_wc_conflict_reason_moved_away },
+ { N_("moved away and edited"), svn_wc_conflict_reason_moved_away_and_edited
},
{ N_("moved here"), svn_wc_conflict_reason_moved_here },
{ NULL, 0 }
};
@@ -78,6 +79,7 @@ static const svn_token_map_t map_conflic
{ "replace", svn_wc_conflict_reason_replaced },
{ "unversioned", svn_wc_conflict_reason_unversioned },
{ "moved-away", svn_wc_conflict_reason_moved_away },
+ { "moved-away-and-edited", svn_wc_conflict_reason_moved_away_and_edited },
{ "moved-here", svn_wc_conflict_reason_moved_here },
{ NULL, 0 }
};
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=1183358&r1=1183357&r2=1183358&view=diff
==============================================================================
--- subversion/trunk/subversion/tests/cmdline/tree_conflict_tests.py (original)
+++ subversion/trunk/subversion/tests/cmdline/tree_conflict_tests.py Fri Oct 14
13:40:29 2011
@@ -278,6 +278,9 @@ f_moves = [
d_dels = [
( create_d, ['dD'] ),
+]
+
+d_moves = [
( create_d, ['dM'] ),
]
@@ -604,12 +607,12 @@ def up_sw_dir_mod_onto_del(sbox):
def up_sw_dir_del_onto_mod(sbox):
"up/sw dir: del/rpl/mv onto modify"
# WC state: any (D necessarily exists; children may have any state)
- test_tc_up_sw(sbox, d_dels + d_rpls, d_mods)
+ test_tc_up_sw(sbox, d_dels + d_moves + d_rpls, d_mods)
def up_sw_dir_del_onto_del(sbox):
"up/sw dir: del/rpl/mv onto del/rpl/mv"
# WC state: any (D necessarily exists; children may have any state)
- test_tc_up_sw(sbox, d_dels + d_rpls, d_dels + d_rpls)
+ test_tc_up_sw(sbox, d_dels + d_moves + d_rpls, d_dels + d_rpls)
# This is currently set as XFail over ra_dav because it hits
# issue #3314 'DAV can overwrite directories during copy'
@@ -690,8 +693,8 @@ def merge_file_add_onto_not_none(sbox):
def merge_dir_mod_onto_not_dir(sbox):
"merge dir: modify onto not-dir"
sbox2 = sbox.clone_dependent()
- test_tc_merge(sbox, d_mods, br_scen = d_dels + d_rpl_f)
- test_tc_merge(sbox2, d_mods, wc_scen = d_dels)
+ test_tc_merge(sbox, d_mods, br_scen = d_dels + d_moves + d_rpl_f)
+ test_tc_merge(sbox2, d_mods, wc_scen = d_dels + d_moves)
# Test for issue #3150 'tree conflicts with directories as victims'.
@XFail()
@@ -699,14 +702,14 @@ def merge_dir_mod_onto_not_dir(sbox):
def merge_dir_del_onto_not_same(sbox):
"merge dir: del/rpl/mv onto not-same"
sbox2 = sbox.clone_dependent()
- test_tc_merge(sbox, d_dels + d_rpls, br_scen = d_mods)
- test_tc_merge(sbox2, d_dels + d_rpls, wc_scen = d_mods)
+ test_tc_merge(sbox, d_dels + d_moves + d_rpls, br_scen = d_mods)
+ test_tc_merge(sbox2, d_dels + d_moves + d_rpls, wc_scen = d_mods)
def merge_dir_del_onto_not_dir(sbox):
"merge dir: del/rpl/mv onto not-dir"
sbox2 = sbox.clone_dependent()
- test_tc_merge(sbox, d_dels + d_rpls, br_scen = d_dels + d_rpl_f)
- test_tc_merge(sbox2, d_dels + d_rpls, wc_scen = d_dels)
+ test_tc_merge(sbox, d_dels + d_moves + d_rpls, br_scen = d_dels + d_moves +
d_rpl_f)
+ test_tc_merge(sbox2, d_dels + d_moves + d_rpls, wc_scen = d_dels + d_moves)
def merge_dir_add_onto_not_none(sbox):
"merge dir: add onto not-none"
Modified: subversion/trunk/subversion/tests/cmdline/update_tests.py
URL:
http://svn.apache.org/viewvc/subversion/trunk/subversion/tests/cmdline/update_tests.py?rev=1183358&r1=1183357&r2=1183358&view=diff
==============================================================================
--- subversion/trunk/subversion/tests/cmdline/update_tests.py (original)
+++ subversion/trunk/subversion/tests/cmdline/update_tests.py Fri Oct 14
13:40:29 2011
@@ -5404,6 +5404,78 @@ def update_to_HEAD_plus_1(sbox):
None, None,
None, None, None, wc_dir, '-r', '2')
+def update_moved_dir_leaf_del(sbox):
+ "update locally moved dir with leaf del"
+ sbox.build()
+ wc_dir = sbox.wc_dir
+
+ svntest.main.run_svn(False, 'rm', '-m', 'remove /A/B/E/alpha',
+ sbox.repo_url + "/A/B/E/alpha")
+ sbox.simple_move("A/B/E", "A/B/E2")
+
+ # since alpha isn't locally modified, the incoming delete should auto-merge
+ expected_output = svntest.wc.State(wc_dir, {
+ 'A/B/E2/alpha' : Item(status='D '),
+ })
+ expected_disk = svntest.main.greek_state.copy()
+ expected_disk.remove('A/B/E/alpha', 'A/B/E/beta', 'A/B/E')
+ expected_disk.add({
+ 'A/B/E2' : Item(),
+ 'A/B/E2/beta' : Item(contents="This is the file 'beta'.\n"),
+ })
+ expected_status = svntest.actions.get_virginal_state(wc_dir, 2)
+ expected_status.add({
+ 'A/B/E2' : Item(status='A ', copied='+', wc_rev='-'),
+ 'A/B/E2/beta' : Item(status=' ', copied='+', wc_rev='-'),
+ 'A/B/E2/alpha' : Item(status='D ', copied='+', wc_rev='-'),
+ })
+ expected_status.remove('A/B/E/alpha')
+ expected_status.tweak('A/B/E', 'A/B/E/beta', status='D ')
+ svntest.actions.run_and_verify_update(wc_dir,
+ expected_output,
+ expected_disk,
+ expected_status,
+ None, None, None,
+ None, None, 1)
+
+def update_moved_dir_edited_leaf_del(sbox):
+ "update locally moved dir with edited leaf del"
+ sbox.build()
+ wc_dir = sbox.wc_dir
+
+ svntest.main.run_svn(False, 'rm', '-m', 'remove /A/B/E/alpha',
+ sbox.repo_url + "/A/B/E/alpha")
+ sbox.simple_move("A/B/E", "A/B/E2")
+ svntest.main.file_write(sbox.ospath('A/B/E2/alpha'),
+ "This is a changed 'alpha'.\n")
+
+ # since alpha was modified post-move, the incoming delete should conflict
+ expected_output = svntest.wc.State(wc_dir, {
+ 'A/B/E/alpha' : Item(status=' ', treeconflict='C'),
+ })
+ expected_disk = svntest.main.greek_state.copy()
+ expected_disk.remove('A/B/E/alpha', 'A/B/E/beta', 'A/B/E')
+ expected_disk.add({
+ 'A/B/E2' : Item(),
+ 'A/B/E2/alpha' : Item(contents="This is a changed 'alpha'.\n"),
+ 'A/B/E2/beta' : Item(contents="This is the file 'beta'.\n"),
+ })
+ expected_status = svntest.actions.get_virginal_state(wc_dir, 2)
+ expected_status.tweak('A/B/E', 'A/B/E/beta', status='D ')
+ expected_status.remove('A/B/E/alpha')
+ expected_status.add({
+ 'A/B/E/alpha' : Item(status='! ', treeconflict='C'),
+ 'A/B/E2' : Item(status='A ', copied='+', wc_rev='-'),
+ 'A/B/E2/beta' : Item(status=' ', copied='+', wc_rev='-'),
+ 'A/B/E2/alpha' : Item(status='M ', copied='+', wc_rev='-'),
+ })
+ svntest.actions.run_and_verify_update(wc_dir,
+ expected_output,
+ expected_disk,
+ expected_status,
+ None, None, None,
+ None, None, 1)
+
#######################################################################
# Run the tests
@@ -5471,6 +5543,8 @@ test_list = [ None,
revive_children_of_copy,
skip_access_denied,
update_to_HEAD_plus_1,
+ update_moved_dir_leaf_del,
+ update_moved_dir_edited_leaf_del,
]
if __name__ == '__main__':