Author: rhuijben
Date: Sat Mar 14 14:30:17 2015
New Revision: 1666690
URL: http://svn.apache.org/r1666690
Log:
In the libsvn_client merge code: When reporting and recording that we skip a
node because it is shadowed on an ancestor, properly record that the
ancestor is skipped instead of the descendant itself.
* subversion/libsvn_client/merge.c
(record_skip): Only record a skip, if we really skipped this node, and
not one of its ancestors. To do this handle this add parent baton
argument.
(merge_file_changed,
merge_file_added,
merge_file_deleted): Update caller.
(merge_dir_opened): Not only skip the directory add, but also record it
as skipped.
(merge_dir_changed,
merge_dir_added,
merge_dir_deleted,
merge_node_absent): Update caller.
* subversion/tests/cmdline/merge_tree_conflict_tests.py
(merge_obstruction_recording): New function.
(test_list): Add merge_obstruction_recording.
Modified:
subversion/trunk/subversion/libsvn_client/merge.c
subversion/trunk/subversion/tests/cmdline/merge_tree_conflict_tests.py
Modified: subversion/trunk/subversion/libsvn_client/merge.c
URL:
http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_client/merge.c?rev=1666690&r1=1666689&r2=1666690&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_client/merge.c (original)
+++ subversion/trunk/subversion/libsvn_client/merge.c Sat Mar 14 14:30:17 2015
@@ -1267,13 +1267,14 @@ record_skip(merge_cmd_baton_t *merge_b,
svn_node_kind_t kind,
svn_wc_notify_action_t action,
svn_wc_notify_state_t state,
+ struct merge_dir_baton_t *pdb,
apr_pool_t *scratch_pool)
{
if (merge_b->record_only)
return SVN_NO_ERROR; /* ### Why? - Legacy compatibility */
- if (merge_b->merge_source.ancestral
- || merge_b->reintegrate_merge)
+ if ((merge_b->merge_source.ancestral || merge_b->reintegrate_merge)
+ && !(pdb && pdb->shadowed))
{
store_path(merge_b->skipped_abspaths, local_abspath);
}
@@ -2020,7 +2021,8 @@ merge_file_changed(const char *relpath,
/* We haven't notified for this node yet: report a skip */
SVN_ERR(record_skip(merge_b, local_abspath, svn_node_file,
svn_wc_notify_update_shadowed_update,
- fb->skip_reason, scratch_pool));
+ fb->skip_reason, fb->parent_baton,
+ scratch_pool));
}
return SVN_NO_ERROR;
@@ -2190,7 +2192,8 @@ merge_file_added(const char *relpath,
/* We haven't notified for this node yet: report a skip */
SVN_ERR(record_skip(merge_b, local_abspath, svn_node_file,
svn_wc_notify_update_shadowed_add,
- fb->skip_reason, scratch_pool));
+ fb->skip_reason, fb->parent_baton,
+ scratch_pool));
}
return SVN_NO_ERROR;
@@ -2401,7 +2404,8 @@ merge_file_deleted(const char *relpath,
/* We haven't notified for this node yet: report a skip */
SVN_ERR(record_skip(merge_b, local_abspath, svn_node_file,
svn_wc_notify_update_shadowed_delete,
- fb->skip_reason, scratch_pool));
+ fb->skip_reason, fb->parent_baton,
+ scratch_pool));
}
return SVN_NO_ERROR;
@@ -2790,6 +2794,12 @@ merge_dir_opened(void **new_dir_baton,
db->tree_conflict_reason = added ? svn_wc_conflict_reason_added
:
svn_wc_conflict_reason_obstructed;
+
+ if ((merge_b->merge_source.ancestral ||
merge_b->reintegrate_merge)
+ && !(pdb && pdb->shadowed))
+ {
+ store_path(merge_b->skipped_abspaths, local_abspath);
+ }
}
}
@@ -2917,7 +2927,8 @@ merge_dir_changed(const char *relpath,
/* We haven't notified for this node yet: report a skip */
SVN_ERR(record_skip(merge_b, local_abspath, svn_node_dir,
svn_wc_notify_update_shadowed_update,
- db->skip_reason, scratch_pool));
+ db->skip_reason, db->parent_baton,
+ scratch_pool));
}
return SVN_NO_ERROR;
@@ -3002,7 +3013,8 @@ merge_dir_added(const char *relpath,
/* We haven't notified for this node yet: report a skip */
SVN_ERR(record_skip(merge_b, local_abspath, svn_node_dir,
svn_wc_notify_update_shadowed_add,
- db->skip_reason, scratch_pool));
+ db->skip_reason, db->parent_baton,
+ scratch_pool));
}
return SVN_NO_ERROR;
@@ -3169,7 +3181,8 @@ merge_dir_deleted(const char *relpath,
/* We haven't notified for this node yet: report a skip */
SVN_ERR(record_skip(merge_b, local_abspath, svn_node_dir,
svn_wc_notify_update_shadowed_delete,
- db->skip_reason, scratch_pool));
+ db->skip_reason, db->parent_baton,
+ scratch_pool));
}
return SVN_NO_ERROR;
@@ -3351,13 +3364,14 @@ merge_node_absent(const char *relpath,
apr_pool_t *scratch_pool)
{
merge_cmd_baton_t *merge_b = processor->baton;
+ struct merge_dir_baton_t *db = dir_baton;
const char *local_abspath = svn_dirent_join(merge_b->target->abspath,
relpath, scratch_pool);
SVN_ERR(record_skip(merge_b, local_abspath, svn_node_unknown,
svn_wc_notify_skip, svn_wc_notify_state_missing,
- scratch_pool));
+ db, scratch_pool));
return SVN_NO_ERROR;
}
Modified: subversion/trunk/subversion/tests/cmdline/merge_tree_conflict_tests.py
URL:
http://svn.apache.org/viewvc/subversion/trunk/subversion/tests/cmdline/merge_tree_conflict_tests.py?rev=1666690&r1=1666689&r2=1666690&view=diff
==============================================================================
--- subversion/trunk/subversion/tests/cmdline/merge_tree_conflict_tests.py
(original)
+++ subversion/trunk/subversion/tests/cmdline/merge_tree_conflict_tests.py Sat
Mar 14 14:30:17 2015
@@ -2087,6 +2087,116 @@ def merge_conflict_details(sbox):
svntest.actions.run_and_verify_info(expected_info, sbox.ospath('B'),
'--depth', 'infinity')
+def merge_obstruction_recording(sbox):
+ "merge obstruction recording"
+
+ sbox.build(empty=True)
+ wc_dir = sbox.wc_dir
+
+ sbox.simple_mkdir('trunk')
+ sbox.simple_mkdir('branches')
+ sbox.simple_commit() #r1
+
+ svntest.actions.run_and_verify_svn(None, [],
+ 'copy', sbox.repo_url + '/trunk',
+ sbox.repo_url + '/branches/branch',
+ '-mCopy') # r2
+
+ sbox.simple_mkdir('trunk/dir')
+ sbox.simple_add_text('The file on trunk\n', 'trunk/dir/file.txt')
+ sbox.simple_commit() #r3
+
+ sbox.simple_update()
+
+ sbox.simple_mkdir('branches/branch/dir')
+ sbox.simple_add_text('The file on branch\n', 'branches/branch/dir/file.txt')
+ sbox.simple_commit() #r4
+
+ sbox.simple_update()
+
+ svntest.actions.run_and_verify_svn(None, [],
+ 'switch', '^/branches/branch', wc_dir,
+ '--ignore-ancestry')
+
+ expected_output = wc.State(wc_dir, {
+ 'dir' : Item(status=' ', treeconflict='C'),
+ 'dir/file.txt' : Item(status=' ', treeconflict='A'),
+ })
+ expected_mergeinfo_output = wc.State(wc_dir, {
+ '' : Item(status=' U'),
+ 'dir' : Item(status=' U'), # Because dir already exists
+ })
+ expected_elision_output = wc.State(wc_dir, {
+ })
+ expected_disk = wc.State('', {
+ 'dir/file.txt' : Item(contents="The file on branch\n"),
+ 'dir' : Item(props={'svn:mergeinfo':''}),
+ '.' : Item(props={'svn:mergeinfo':'/trunk:2-4'}),
+ })
+ expected_status = wc.State(wc_dir, {
+ '' : Item(status=' M', wc_rev='4'),
+ 'dir' : Item(status=' M', treeconflict='C', wc_rev='4'),
+ 'dir/file.txt' : Item(status=' ', wc_rev='4'),
+ })
+ expected_skip = wc.State('', {
+ })
+ svntest.actions.run_and_verify_merge(wc_dir, '1', '4', sbox.repo_url +
'/trunk',
+ None,
+ expected_output,
+ expected_mergeinfo_output,
+ expected_elision_output,
+ expected_disk,
+ expected_status,
+ expected_skip,
+ check_props=True)
+ expected_info = [
+ {
+ "Path" : re.escape(sbox.ospath('dir')),
+ "Tree conflict": re.escape(
+ 'local dir obstruction, incoming dir add upon merge' +
+ ' Source left: (none) ^/trunk/dir@1' +
+ ' Source right: (dir) ^/trunk/dir@4')
+ },
+ ]
+
+ svntest.actions.run_and_verify_info(expected_info, sbox.ospath('dir'))
+
+ # How should the user handle this conflict?
+ # ### Would be nice if we could just accept mine (leave as is, fix mergeinfo)
+ # ### or accept theirs (delete what is here and insert copy
+ svntest.actions.run_and_verify_svn(None, [],
+ 'resolve', '--accept=working',
+ sbox.ospath('dir'))
+
+ # Redo the skipped merge as record only merge
+ expected_output = [
+ '--- Recording mergeinfo for merge of r4 into \'%s\':\n' % \
+ sbox.ospath('dir'),
+ ' G %s\n' % sbox.ospath('dir'),
+ ]
+ # ### Why are r1-r3 not recorded?
+ # ### Guess: Because dir's history only exists since r4.
+ svntest.actions.run_and_verify_svn(expected_output, [],
+ 'merge', '--record-only',
+ sbox.repo_url + '/trunk/dir',
+ sbox.ospath('dir'),
+ '-c', '1-4')
+
+ expected_disk = wc.State('', {
+ 'dir' : Item(props={'svn:mergeinfo':'/trunk/dir:4'}),
+ 'dir/file.txt' : Item(contents="The file on branch\n"),
+ '.' : Item(props={'svn:mergeinfo':'/trunk:2-4'}),
+ })
+ svntest.actions.verify_disk(wc_dir, expected_disk, check_props=True)
+
+ # Because r1-r3 are not recorded, the mergeinfo is not elided :(
+
+ # Even something like a two url merge wouldn't work, because dir
+ # didn't exist below trunk in r1 either.
+
+ # A resolver action could be smarter though...
+
+
########################################################################
# Run the tests
@@ -2118,6 +2228,7 @@ test_list = [ None,
merge_replace_causes_tree_conflict2,
merge_replace_on_del_fails,
merge_conflict_details,
+ merge_obstruction_recording,
]
if __name__ == '__main__':