diff options
Diffstat (limited to 'subversion/tests/cmdline/merge_tests.py')
-rwxr-xr-x | subversion/tests/cmdline/merge_tests.py | 3636 |
1 files changed, 2755 insertions, 881 deletions
diff --git a/subversion/tests/cmdline/merge_tests.py b/subversion/tests/cmdline/merge_tests.py index 4c92913..d116118 100755 --- a/subversion/tests/cmdline/merge_tests.py +++ b/subversion/tests/cmdline/merge_tests.py @@ -32,6 +32,8 @@ import time import svntest from svntest import main, wc, verify, actions +from prop_tests import binary_mime_type_on_text_file_warning + # (abbreviation) Item = wc.StateItem Skip = svntest.testcase.Skip_deco @@ -47,17 +49,38 @@ from svntest.main import server_has_mergeinfo from svntest.actions import fill_file_with_lines from svntest.actions import make_conflict_marker_text from svntest.actions import inject_conflict_into_expected_state +from svntest.verify import RegexListOutput -def expected_merge_output(rev_ranges, additional_lines=None, foreign=False, - elides=False, two_url=False): +def expected_merge_output(rev_ranges, additional_lines=[], foreign=False, + elides=False, two_url=False, target=None, + text_conflicts=0, prop_conflicts=0, tree_conflicts=0, + text_resolved=0, prop_resolved=0, tree_resolved=0, + skipped_paths=0): """Generate an (inefficient) regex representing the expected merge - output and mergeinfo notifications from REV_RANGES (a list of 'range' lists - of the form [start, end] or [single_rev] --> [single_rev - 1, single_rev]), - and ADDITIONAL_LINES (a list of strings). If REV_RANGES is None then only - the standard notification for a 3-way merge is expected. If ELIDES is true - add to the regex an expression representing elision notification. If TWO_URL - us true tweak the regex to expect the appropriate mergeinfo notification - for a 3-way merge.""" + output and mergeinfo notifications from REV_RANGES and ADDITIONAL_LINES. + + REV_RANGES is a list of revision ranges for which mergeinfo is being + recorded. Each range is of the form [start, end] (where both START and + END are inclusive, unlike in '-rX:Y') or the form [single_rev] (which is + like '-c SINGLE_REV'). If REV_RANGES is None then only the standard + notification for a 3-way merge is expected. + + ADDITIONAL_LINES is a list of strings to match the other lines of output; + these are basically regular expressions except that backslashes will be + escaped herein. If ADDITIONAL_LINES is a single string, it is interpreted + the same as a list containing that string. + + If ELIDES is true, add to the regex an expression representing elision + notification. If TWO_URL is true, tweak the regex to expect the + appropriate mergeinfo notification for a 3-way merge. + + TARGET is the local path to the target, as it should appear in + notifications; if None, it is not checked. + + TEXT_CONFLICTS, PROP_CONFLICTS, TREE_CONFLICTS and SKIPPED_PATHS specify + the number of each kind of conflict to expect. + """ + if rev_ranges is None: lines = [svntest.main.merge_notify_line(None, None, False, foreign)] else: @@ -69,8 +92,8 @@ def expected_merge_output(rev_ranges, additional_lines=None, foreign=False, else: end_rev = None lines += [svntest.main.merge_notify_line(start_rev, end_rev, - True, foreign)] - lines += [svntest.main.mergeinfo_notify_line(start_rev, end_rev)] + True, foreign, target)] + lines += [svntest.main.mergeinfo_notify_line(start_rev, end_rev, target)] if (elides): lines += ["--- Eliding mergeinfo from .*\n"] @@ -78,22 +101,25 @@ def expected_merge_output(rev_ranges, additional_lines=None, foreign=False, if (two_url): lines += ["--- Recording mergeinfo for merge between repository URLs .*\n"] - if isinstance(additional_lines, list): - # Address "The Backslash Plague" - # - # If ADDITIONAL_LINES are present there are possibly paths in it with - # multiple components and on Windows these components are separated with - # '\'. These need to be escaped properly in the regexp for the match to - # work correctly. See http://aspn.activestate.com/ASPN/docs/ActivePython - # /2.2/howto/regex/regex.html#SECTION000420000000000000000. - if sys.platform == 'win32': - for i in range(0, len(additional_lines)): - additional_lines[i] = additional_lines[i].replace("\\", "\\\\") - lines.extend(additional_lines) - else: - if sys.platform == 'win32' and additional_lines != None: - additional_lines = additional_lines.replace("\\", "\\\\") - lines.append(str(additional_lines)) + # Address "The Backslash Plague" + # + # If ADDITIONAL_LINES are present there are possibly paths in it with + # multiple components and on Windows these components are separated with + # '\'. These need to be escaped properly in the regexp for the match to + # work correctly. See http://aspn.activestate.com/ASPN/docs/ActivePython + # /2.2/howto/regex/regex.html#SECTION000420000000000000000. + if isinstance(additional_lines, str): + additional_lines = [additional_lines] + if sys.platform == 'win32': + additional_lines = [line.replace("\\", "\\\\") for line in additional_lines] + lines += additional_lines + + lines += svntest.main.summary_of_conflicts( + text_conflicts, prop_conflicts, tree_conflicts, + text_resolved, prop_resolved, tree_resolved, + skipped_paths, + as_regex=True) + return "|".join(lines) def check_mergeinfo_recursively(root_path, subpaths_mergeinfo): @@ -179,8 +205,8 @@ def textual_merges_galore(sbox): # url = os.path.join(svntest.main.test_area_url, sbox.repo_dir) # Change mu and rho for revision 2 - mu_path = os.path.join(wc_dir, 'A', 'mu') - rho_path = os.path.join(wc_dir, 'A', 'D', 'G', 'rho') + mu_path = sbox.ospath('A/mu') + rho_path = sbox.ospath('A/D/G/rho') mu_text = fill_file_with_lines(mu_path, 2) rho_text = fill_file_with_lines(rho_path, 2) @@ -208,9 +234,9 @@ def textual_merges_galore(sbox): # Now commit some more mods from the original working copy, to # produce revision 3. - lambda_path = os.path.join(wc_dir, 'A', 'B', 'lambda') - pi_path = os.path.join(wc_dir, 'A', 'D', 'G', 'pi') - tau_path = os.path.join(wc_dir, 'A', 'D', 'G', 'tau') + lambda_path = sbox.ospath('A/B/lambda') + pi_path = sbox.ospath('A/D/G/pi') + tau_path = sbox.ospath('A/D/G/tau') lambda_text = fill_file_with_lines(lambda_path, 2) pi_text = fill_file_with_lines(pi_path, 2) @@ -414,8 +440,8 @@ def add_with_history(sbox): sbox.build() wc_dir = sbox.wc_dir - C_path = os.path.join(wc_dir, 'A', 'C') - F_path = os.path.join(wc_dir, 'A', 'B', 'F') + C_path = sbox.ospath('A/C') + F_path = sbox.ospath('A/B/F') F_url = sbox.repo_url + '/A/B/F' Q_path = os.path.join(F_path, 'Q') @@ -493,6 +519,13 @@ def add_with_history(sbox): expected_skip = wc.State(C_path, { }) + # Add some unversioned directory obstructions to the incoming + # additions. This should be tolerated and *not* result in any + # difference between the --dry-run and actual merge. + # See http://svn.haxx.se/dev/archive-2012-11/0696.shtml + os.mkdir(sbox.ospath('A/C/Q')) + os.mkdir(sbox.ospath('A/C/Q2')) + svntest.actions.run_and_verify_merge(C_path, '1', '2', F_url, None, expected_output, expected_mergeinfo_output, @@ -543,9 +576,9 @@ def simple_property_merges(sbox): wc_dir = sbox.wc_dir # Add a property to a file and a directory - alpha_path = os.path.join(wc_dir, 'A', 'B', 'E', 'alpha') - beta_path = os.path.join(wc_dir, 'A', 'B', 'E', 'beta') - E_path = os.path.join(wc_dir, 'A', 'B', 'E') + alpha_path = sbox.ospath('A/B/E/alpha') + beta_path = sbox.ospath('A/B/E/beta') + E_path = sbox.ospath('A/B/E') svntest.actions.run_and_verify_svn(None, None, [], 'propset', 'foo', 'foo_val', @@ -616,7 +649,7 @@ def simple_property_merges(sbox): pristine_status.tweak(wc_rev=4) # Merge B 3:4 into B2 - B2_path = os.path.join(wc_dir, 'A', 'B2') + B2_path = sbox.ospath('A/B2') expected_output = wc.State(B2_path, { 'E' : Item(status=' U'), 'E/alpha' : Item(status=' U'), @@ -734,7 +767,7 @@ def simple_property_merges(sbox): # inherited from A2 (created by its copy from A) allows us to avoid # a repeated merge. alpha_url = sbox.repo_url + '/A/B/E/alpha' - alpha_path = os.path.join(wc_dir, 'B', 'E', 'alpha') + alpha_path = sbox.ospath('B/E/alpha') # Cannot use run_and_verify_merge with a file target svntest.actions.run_and_verify_svn(None, [], [], 'merge', '-r', '3:4', @@ -769,9 +802,9 @@ def merge_similar_unrelated_trees(sbox): # Modify some stuff in the second one. Now merge # (firstdir:seconddir->thirddir). - base1_path = os.path.join(wc_dir, 'base1') - base2_path = os.path.join(wc_dir, 'base2') - apply_path = os.path.join(wc_dir, 'apply') + base1_path = sbox.ospath('base1') + base2_path = sbox.ospath('base2') + apply_path = sbox.ospath('apply') base1_url = os.path.join(sbox.repo_url + '/base1') base2_url = os.path.join(sbox.repo_url + '/base2') @@ -835,7 +868,8 @@ def merge_similar_unrelated_trees(sbox): #---------------------------------------------------------------------- def merge_one_file_helper(sbox, arg_flav, record_only = 0): - "ARG_FLAV is one of 'r' (revision range) or 'c' (single change)." + """ARG_FLAV is one of 'r' (revision range) or 'c' (single change) or + '*' (no revision specified).""" if arg_flav not in ('r', 'c', '*'): raise svntest.Failure("Unrecognized flavor of merge argument") @@ -845,7 +879,7 @@ def merge_one_file_helper(sbox, arg_flav, record_only = 0): rho_rel_path = os.path.join('A', 'D', 'G', 'rho') rho_path = os.path.join(wc_dir, rho_rel_path) - G_path = os.path.join(wc_dir, 'A', 'D', 'G') + G_path = sbox.ospath('A/D/G') rho_url = sbox.repo_url + '/A/D/G/rho' # Change rho for revision 2 @@ -972,9 +1006,13 @@ def merge_record_only(sbox): merge_one_file_helper(sbox, 'r', 1) #---------------------------------------------------------------------- -# This is a regression for the enhancement added in issue #785. +# This is a regression test for the enhancement added in issue #785 "add +# friendly enhancement to 'svn merge'", which is about inferring that +# the default target of "svn merge [-r...] FILE" should not be "." but +# rather should be "FILE". def merge_with_implicit_target_helper(sbox, arg_flav): - "ARG_FLAV is one of 'r' (revision range) or 'c' (single change)." + """ARG_FLAV is one of 'r' (revision range) or 'c' (single change) or + '*' (no revision specified).""" if arg_flav not in ('r', 'c', '*'): raise svntest.Failure("Unrecognized flavor of merge argument") @@ -983,7 +1021,7 @@ def merge_with_implicit_target_helper(sbox, arg_flav): wc_dir = sbox.wc_dir # Change mu for revision 2 - mu_path = os.path.join(wc_dir, 'A', 'mu') + mu_path = sbox.ospath('A/mu') orig_mu_text = svntest.tree.get_text(mu_path) added_mu_text = "" for x in range(2,11): @@ -1114,7 +1152,7 @@ def merge_with_prev(sbox): wc_dir = sbox.wc_dir # Change mu for revision 2 - mu_path = os.path.join(wc_dir, 'A', 'mu') + mu_path = sbox.ospath('A/mu') orig_mu_text = svntest.tree.get_text(mu_path) added_mu_text = "" for x in range(2,11): @@ -1122,7 +1160,7 @@ def merge_with_prev(sbox): added_mu_text += "\n" svntest.main.file_append(mu_path, added_mu_text) - zot_path = os.path.join(wc_dir, 'A', 'zot') + zot_path = sbox.ospath('A/zot') svntest.main.file_append(zot_path, "bar") svntest.main.run_svn(None, 'add', zot_path) @@ -1217,7 +1255,7 @@ def merge_binary_file(sbox): # Add a binary file to the project theta_contents = open(os.path.join(sys.path[0], "theta.bin"), 'rb').read() # Write PNG file data into 'A/theta'. - theta_path = os.path.join(wc_dir, 'A', 'theta') + theta_path = sbox.ospath('A/theta') svntest.main.file_write(theta_path, theta_contents, 'wb') svntest.main.run_svn(None, 'add', theta_path) @@ -1311,7 +1349,7 @@ def merge_in_new_file_and_diff(sbox): svntest.actions.run_and_verify_svn(None, None, [], 'update', wc_dir) - new_file_path = os.path.join(wc_dir, 'A', 'B', 'E', 'newfile') + new_file_path = sbox.ospath('A/B/E/newfile') svntest.main.file_write(new_file_path, "newfile\n") # Add the new file, and commit revision 3. @@ -1320,7 +1358,7 @@ def merge_in_new_file_and_diff(sbox): 'ci', '-m', "Changing the trunk.", wc_dir) - branch_path = os.path.join(wc_dir, "branch") + branch_path = sbox.ospath('branch') url_branch_path = branch_path.replace(os.path.sep, '/') # Merge our addition into the branch. @@ -1356,6 +1394,13 @@ def merge_in_new_file_and_diff(sbox): # Finally, run diff. expected_output = [ + "Index: " + url_branch_path + "/newfile\n", + "===================================================================\n", + "--- "+ url_branch_path + "/newfile (revision 0)\n", + "+++ "+ url_branch_path + "/newfile (working copy)\n", + "@@ -0,0 +1 @@\n", + "+newfile\n", + "Index: " + url_branch_path + "\n", "===================================================================\n", "--- "+ url_branch_path + "\t(revision 2)\n", @@ -1365,12 +1410,7 @@ def merge_in_new_file_and_diff(sbox): "___________________________________________________________________\n", "Added: " + SVN_PROP_MERGEINFO + "\n", " Merged /A/B/E:r2-3\n", - "Index: " + url_branch_path + "/newfile\n", - "===================================================================\n", - "--- "+ url_branch_path + "/newfile (revision 0)\n", - "+++ "+ url_branch_path + "/newfile (working copy)\n", - "@@ -0,0 +1 @@\n", - "+newfile\n"] + ] svntest.actions.run_and_verify_svn(None, expected_output, [], 'diff', '--show-copies-as-adds', branch_path) @@ -1387,8 +1427,8 @@ def merge_skips_obstructions(sbox): sbox.build() wc_dir = sbox.wc_dir - C_path = os.path.join(wc_dir, 'A', 'C') - F_path = os.path.join(wc_dir, 'A', 'B', 'F') + C_path = sbox.ospath('A/C') + F_path = sbox.ospath('A/B/F') F_url = sbox.repo_url + '/A/B/F' Q_path = os.path.join(F_path, 'Q') @@ -1445,7 +1485,7 @@ def merge_skips_obstructions(sbox): 'Q/bar' : Item(status=' ', wc_rev='-', copied='+'), }) expected_skip = wc.State(C_path, { - 'foo' : Item(), + 'foo' : Item(verb='Skipped'), }) # Unversioned: svntest.main.file_append(os.path.join(C_path, "foo"), "foo") @@ -1473,6 +1513,7 @@ def merge_skips_obstructions(sbox): expected_output = wc.State(C_path, { 'foo' : Item(status='A '), + 'Q/bar' : Item(status=' ', treeconflict='A'), # Skipped }) expected_mergeinfo_output = wc.State(C_path, { '' : Item(status=' U'), @@ -1489,8 +1530,7 @@ def merge_skips_obstructions(sbox): 'foo' : Item(status='A ', wc_rev='-', copied='+'), }) expected_skip = wc.State(C_path, { - 'Q' : Item(), - 'Q/bar' : Item(), + 'Q' : Item(verb='Skipped'), }) svntest.actions.run_and_verify_merge(C_path, '1', '2', F_url, None, @@ -1508,8 +1548,8 @@ def merge_skips_obstructions(sbox): svntest.actions.run_and_verify_svn(None, None, [], 'revert', '-R', wc_dir) svntest.actions.run_and_verify_status(wc_dir, pre_merge_status) - iota_path = os.path.join(wc_dir, 'iota') - G_path = os.path.join(wc_dir, 'A', 'D', 'G') + iota_path = sbox.ospath('iota') + G_path = sbox.ospath('A/D/G') svntest.actions.run_and_verify_svn(None, None, [], 'rm', iota_path, G_path) expected_output = wc.State(wc_dir, { @@ -1550,8 +1590,8 @@ def merge_skips_obstructions(sbox): # No-op merge still sets mergeinfo expected_status.tweak('', status=' M') expected_skip = wc.State(wc_dir, { - 'iota' : Item(), - 'A/D/G' : Item(), + 'iota' : Item(verb='Skipped'), + 'A/D/G' : Item(verb='Skipped'), }) svntest.actions.run_and_verify_merge(wc_dir, '2', '3', sbox.repo_url, None, @@ -1572,7 +1612,7 @@ def merge_skips_obstructions(sbox): expected_status.tweak('', status=' ') svntest.actions.run_and_verify_status(wc_dir, expected_status) - lambda_path = os.path.join(wc_dir, 'A', 'B', 'lambda') + lambda_path = sbox.ospath('A/B/lambda') svntest.main.file_append(lambda_path, "more text") expected_output = wc.State(wc_dir, { 'A/B/lambda' : Item(verb='Sending'), @@ -1612,7 +1652,7 @@ def merge_skips_obstructions(sbox): expected_disk.remove('A/D/G') expected_disk.tweak('', props={SVN_PROP_MERGEINFO : '/:4'}) expected_skip = wc.State(wc_dir, { - 'A/B/lambda' : Item(), + 'A/B/lambda' : Item(verb='Skipped'), }) # No-op merge still sets mergeinfo. expected_status_short = expected_status.copy(wc_dir) @@ -1660,6 +1700,9 @@ def merge_skips_obstructions(sbox): expected_disk.remove('A/B/lambda') expected_status.tweak('A/B/lambda', status='! ') expected_status.tweak('', status=' ') + expected_skip = wc.State(wc_dir, { + 'A/B/lambda' : Item(verb='Skipped missing target'), + }) # Why do we need to --ignore-ancestry? Because the previous merge of r4, # despite being inoperative, set mergeinfo for r4 on the WC. With the # advent of merge tracking this repeat merge attempt would not be attempted. @@ -1690,9 +1733,7 @@ def merge_into_missing(sbox): sbox.build() wc_dir = sbox.wc_dir - single_db = svntest.main.wc_is_singledb(wc_dir) - - F_path = os.path.join(wc_dir, 'A', 'B', 'F') + F_path = sbox.ospath('A/B/F') F_url = sbox.repo_url + '/A/B/F' Q_path = os.path.join(F_path, 'Q') foo_path = os.path.join(F_path, 'foo') @@ -1753,23 +1794,16 @@ def merge_into_missing(sbox): expected_status = wc.State(F_path, { '' : Item(status=' ', wc_rev=1), 'foo' : Item(status='! ', wc_rev=2), - 'Q' : Item(status='! ', wc_rev='?'), - }) - expected_skip = wc.State(F_path, { - 'Q' : Item(), - 'foo' : Item(), - }) - - if single_db: - # Revision not lost - expected_status.tweak('Q', wc_rev=2) + 'Q' : Item(status='! ', wc_rev=2), # Missing data still available - expected_status.add({ - 'Q/R' : Item(status='! ', wc_rev='3'), - 'Q/R/bar' : Item(status='! ', wc_rev='3'), - 'Q/baz' : Item(status='! ', wc_rev='3'), + 'Q/R' : Item(status='! ', wc_rev=3), + 'Q/R/bar' : Item(status='! ', wc_rev=3), + 'Q/baz' : Item(status='! ', wc_rev=3), + }) + expected_skip = wc.State(F_path, { + 'Q' : Item(verb='Skipped missing target'), + 'foo' : Item(verb='Skipped missing target'), }) - # Use --ignore-ancestry because merge tracking aware merges raise an # error when the merge target is missing subtrees due to OS-level # deletes. @@ -1792,18 +1826,13 @@ def merge_into_missing(sbox): expected_status = wc.State(F_path, { '' : Item(status=' ', wc_rev=1), 'foo' : Item(status='! ', wc_rev=2), - 'Q' : Item(status='! ', wc_rev='?'), - }) - expected_mergeinfo_output = wc.State(F_path, { - }) - - if single_db: + 'Q' : Item(status='! ', wc_rev='2'), # Revision is known and we can record mergeinfo - expected_status.tweak('Q', wc_rev='2', entry_rev='?') - expected_status.add({ - 'Q/R' : Item(status='! ', wc_rev='3'), - 'Q/R/bar' : Item(status='! ', wc_rev='3'), - 'Q/baz' : Item(status='! ', wc_rev='3'), + 'Q/R' : Item(status='! ', wc_rev='3'), + 'Q/R/bar' : Item(status='! ', wc_rev='3'), + 'Q/baz' : Item(status='! ', wc_rev='3'), + }) + expected_mergeinfo_output = wc.State(F_path, { }) svntest.actions.run_and_verify_merge(F_path, '1', '2', F_url, None, @@ -1834,18 +1863,11 @@ def merge_into_missing(sbox): expected_status.add({ 'A/B/F' : Item(status=' ', wc_rev=1), 'A/B/F/foo' : Item(status='! ', wc_rev=2), - 'A/B/F/Q' : Item(status='! ', wc_rev='?'), - }) - if single_db: - # Revision known and mergeinfo recorded - expected_status.tweak('A/B/F/Q', wc_rev='2') - # Missing data still available - expected_status.add({ - 'A/B/F/Q' : Item(status='! ', wc_rev='2'), - 'A/B/F/Q/baz' : Item(status='! ', wc_rev='3'), - 'A/B/F/Q/R' : Item(status='! ', wc_rev='3'), - 'A/B/F/Q/R/bar' : Item(status='! ', wc_rev='3'), - }) + 'A/B/F/Q' : Item(status='! ', wc_rev=2), + 'A/B/F/Q/baz' : Item(status='! ', wc_rev='3'), + 'A/B/F/Q/R' : Item(status='! ', wc_rev='3'), + 'A/B/F/Q/R/bar' : Item(status='! ', wc_rev='3'), + }) svntest.actions.run_and_verify_status(wc_dir, expected_status) @@ -1860,7 +1882,7 @@ def dry_run_adds_file_with_prop(sbox): wc_dir = sbox.wc_dir # Commit a new file which has a property. - zig_path = os.path.join(wc_dir, 'A', 'B', 'E', 'zig') + zig_path = sbox.ospath('A/B/E/zig') svntest.main.file_append(zig_path, "zig contents") svntest.actions.run_and_verify_svn(None, None, [], 'add', zig_path) svntest.actions.run_and_verify_svn(None, None, [], @@ -1880,7 +1902,7 @@ def dry_run_adds_file_with_prop(sbox): None, wc_dir) # Do a regular merge of that change into a different dir. - F_path = os.path.join(wc_dir, 'A', 'B', 'F') + F_path = sbox.ospath('A/B/F') E_url = sbox.repo_url + '/A/B/E' expected_output = wc.State(F_path, { @@ -1920,7 +1942,7 @@ def merge_binary_with_common_ancestry(sbox): wc_dir = sbox.wc_dir # Create the common ancestry path - I_path = os.path.join(wc_dir, 'I') + I_path = sbox.ospath('I') svntest.main.run_svn(None, 'mkdir', I_path) # Add a binary file to the common ancestry path @@ -1949,7 +1971,7 @@ def merge_binary_with_common_ancestry(sbox): wc_dir) # Create the first branch - J_path = os.path.join(wc_dir, 'J') + J_path = sbox.ospath('J') svntest.main.run_svn(None, 'copy', I_path, J_path) # Commit the first branch @@ -1968,7 +1990,7 @@ def merge_binary_with_common_ancestry(sbox): wc_dir) # Create the path where the files will be merged - K_path = os.path.join(wc_dir, 'K') + K_path = sbox.ospath('K') svntest.main.run_svn(None, 'mkdir', K_path) # Commit the new path @@ -2019,13 +2041,13 @@ def merge_binary_with_common_ancestry(sbox): wc_dir) # Create the second branch from the modified ancestry - L_path = os.path.join(wc_dir, 'L') + L_path = sbox.ospath('L') svntest.main.run_svn(None, 'copy', I_path, L_path) # Commit the second branch expected_output = wc.State(wc_dir, { 'L' : Item(verb='Adding'), - 'L/theta' : Item(verb='Adding (bin)'), + 'L/theta' : Item(verb='Replacing'), }) expected_status.add({ @@ -2138,7 +2160,7 @@ def merge_funny_chars_on_path(sbox): None, wc_dir) # Do a regular merge of that change into a different dir. - F_path = os.path.join(wc_dir, 'A', 'B', 'F') + F_path = sbox.ospath('A/B/F') E_url = sbox.repo_url + '/A/B/E' expected_output_dic = {} @@ -2284,7 +2306,7 @@ def merge_prop_change_to_deleted_target(sbox): wc_dir = sbox.wc_dir # Add a property to alpha. - alpha_path = os.path.join(wc_dir, 'A', 'B', 'E', 'alpha') + alpha_path = sbox.ospath('A/B/E/alpha') svntest.actions.run_and_verify_svn(None, None, [], 'propset', 'foo', 'foo_val', alpha_path) @@ -2333,8 +2355,8 @@ def set_up_dir_replace(sbox): sbox.build() wc_dir = sbox.wc_dir - C_path = os.path.join(wc_dir, 'A', 'C') - F_path = os.path.join(wc_dir, 'A', 'B', 'F') + C_path = sbox.ospath('A/C') + F_path = sbox.ospath('A/B/F') F_url = sbox.repo_url + '/A/B/F' foo_path = os.path.join(F_path, 'foo') @@ -2439,15 +2461,15 @@ def set_up_dir_replace(sbox): # A merge that replaces a directory # Tests for Issue #2144 and Issue #2607 @SkipUnless(server_has_mergeinfo) -@Issue(2144) +@Issue(2144,2607) def merge_dir_replace(sbox): "merge a replacement of a directory" set_up_dir_replace(sbox) wc_dir = sbox.wc_dir - C_path = os.path.join(wc_dir, 'A', 'C') - F_path = os.path.join(wc_dir, 'A', 'B', 'F') + C_path = sbox.ospath('A/C') + F_path = sbox.ospath('A/B/F') F_url = sbox.repo_url + '/A/B/F' foo_path = os.path.join(F_path, 'foo') new_file2 = os.path.join(foo_path, "new file 2") @@ -2560,8 +2582,8 @@ def merge_dir_and_file_replace(sbox): set_up_dir_replace(sbox) wc_dir = sbox.wc_dir - C_path = os.path.join(wc_dir, 'A', 'C') - F_path = os.path.join(wc_dir, 'A', 'B', 'F') + C_path = sbox.ospath('A/C') + F_path = sbox.ospath('A/B/F') F_url = sbox.repo_url + '/A/B/F' foo_path = os.path.join(F_path, 'foo') new_file2 = os.path.join(foo_path, "new file 2") @@ -2677,7 +2699,7 @@ def merge_file_with_space_in_its_name(sbox): # For issue #2144 sbox.build() wc_dir = sbox.wc_dir - new_file = os.path.join(wc_dir, "new file") + new_file = sbox.ospath('new file') # Make r2. svntest.main.file_append(new_file, "Initial text in the file.\n") @@ -2712,7 +2734,7 @@ def merge_dir_branches(sbox): wc_dir = sbox.wc_dir wc_uuid = svntest.actions.get_wc_uuid(wc_dir) - F_path = os.path.join(wc_dir, 'A', 'B', 'F') + F_path = sbox.ospath('A/B/F') F_url = sbox.repo_url + '/A/B/F' C_url = sbox.repo_url + '/A/C' @@ -2733,7 +2755,7 @@ def merge_dir_branches(sbox): None, wc_dir) # Create an unversioned foo - foo_path = os.path.join(wc_dir, 'foo') + foo_path = sbox.ospath('foo') os.mkdir(foo_path) # Merge from C to F onto the wc_dir @@ -2767,9 +2789,9 @@ def safe_property_merge(sbox): wc_dir = sbox.wc_dir # Add a property to two files and a directory, commit as r2. - alpha_path = os.path.join(wc_dir, 'A', 'B', 'E', 'alpha') - beta_path = os.path.join(wc_dir, 'A', 'B', 'E', 'beta') - E_path = os.path.join(wc_dir, 'A', 'B', 'E') + alpha_path = sbox.ospath('A/B/E/alpha') + beta_path = sbox.ospath('A/B/E/beta') + E_path = sbox.ospath('A/B/E') svntest.actions.run_and_verify_svn(None, None, [], 'propset', 'foo', 'foo_val', @@ -2821,9 +2843,9 @@ def safe_property_merge(sbox): svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir) # Make local propchanges to E, alpha and beta in the branch. - alpha_path2 = os.path.join(wc_dir, 'A', 'B2', 'E', 'alpha') - beta_path2 = os.path.join(wc_dir, 'A', 'B2', 'E', 'beta') - E_path2 = os.path.join(wc_dir, 'A', 'B2', 'E') + alpha_path2 = sbox.ospath('A/B2/E/alpha') + beta_path2 = sbox.ospath('A/B2/E/beta') + E_path2 = sbox.ospath('A/B2/E') svntest.actions.run_and_verify_svn(None, None, [], 'propset', 'foo', 'branchval', @@ -2834,7 +2856,7 @@ def safe_property_merge(sbox): # Now merge the recent B change to the branch. Because we already # have local propmods, we should get property conflicts. - B2_path = os.path.join(wc_dir, 'A', 'B2') + B2_path = sbox.ospath('A/B2') expected_output = wc.State(B2_path, { 'E' : Item(status=' C'), @@ -2898,8 +2920,8 @@ def property_merge_from_branch(sbox): wc_dir = sbox.wc_dir # Add a property to a file and a directory, commit as r2. - alpha_path = os.path.join(wc_dir, 'A', 'B', 'E', 'alpha') - E_path = os.path.join(wc_dir, 'A', 'B', 'E') + alpha_path = sbox.ospath('A/B/E/alpha') + E_path = sbox.ospath('A/B/E') svntest.actions.run_and_verify_svn(None, None, [], 'propset', 'foo', 'foo_val', @@ -2945,8 +2967,8 @@ def property_merge_from_branch(sbox): svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir) # Make different propchanges changes to the B2 branch and commit as r5. - alpha_path2 = os.path.join(wc_dir, 'A', 'B2', 'E', 'alpha') - E_path2 = os.path.join(wc_dir, 'A', 'B2', 'E') + alpha_path2 = sbox.ospath('A/B2/E/alpha') + E_path2 = sbox.ospath('A/B2/E') svntest.actions.run_and_verify_svn(None, None, [], 'propset', 'foo', 'branchval', @@ -2965,7 +2987,7 @@ def property_merge_from_branch(sbox): # Now merge the recent B change to the branch. There are no local # mods anywhere, but we should still get property conflicts anyway! - B2_path = os.path.join(wc_dir, 'A', 'B2') + B2_path = sbox.ospath('A/B2') expected_output = wc.State(B2_path, { 'E' : Item(status=' C'), @@ -3027,7 +3049,7 @@ def property_merge_undo_redo(sbox): wc_dir = sbox.wc_dir # Add a property to a file, commit as r2. - alpha_path = os.path.join(wc_dir, 'A', 'B', 'E', 'alpha') + alpha_path = sbox.ospath('A/B/E/alpha') svntest.actions.run_and_verify_svn(None, None, [], 'propset', 'foo', 'foo_val', alpha_path) @@ -3118,11 +3140,11 @@ def cherry_pick_text_conflict(sbox): sbox.build() wc_dir = sbox.wc_dir - A_path = os.path.join(wc_dir, 'A') + A_path = sbox.ospath('A') A_url = sbox.repo_url + '/A' mu_path = os.path.join(A_path, 'mu') branch_A_url = sbox.repo_url + '/copy-of-A' - branch_mu_path = os.path.join(wc_dir, 'copy-of-A', 'mu') + branch_mu_path = sbox.ospath('copy-of-A/mu') # Create a branch of A. svntest.actions.run_and_verify_svn(None, None, [], 'cp', @@ -3228,7 +3250,7 @@ def merge_file_replace(sbox): wc_dir = sbox.wc_dir # File scheduled for deletion - rho_path = os.path.join(wc_dir, 'A', 'D', 'G', 'rho') + rho_path = sbox.ospath('A/D/G/rho') svntest.actions.run_and_verify_svn(None, None, [], 'rm', rho_path) expected_status = svntest.actions.get_virginal_state(wc_dir, 1) @@ -3320,7 +3342,7 @@ def merge_file_replace_to_mixed_rev_wc(sbox): wc_dir = sbox.wc_dir # File scheduled for deletion - rho_path = os.path.join(wc_dir, 'A', 'D', 'G', 'rho') + rho_path = sbox.ospath('A/D/G/rho') svntest.actions.run_and_verify_svn(None, None, [], 'rm', rho_path) expected_status = svntest.actions.get_virginal_state(wc_dir, 1) @@ -3597,8 +3619,10 @@ def merge_conflict_markers_matching_eol(sbox): wc_dir = sbox.wc_dir filecount = 1 - mu_path = os.path.join(wc_dir, 'A', 'mu') + mu_path = sbox.ospath('A/mu') + # CRLF is a string that will match a CRLF sequence read from a text file. + # ### On Windows, we assume CRLF will be read as LF, so it's a poor test. if os.name == 'nt': crlf = '\n' else: @@ -3736,8 +3760,10 @@ def merge_eolstyle_handling(sbox): sbox.build() wc_dir = sbox.wc_dir - mu_path = os.path.join(wc_dir, 'A', 'mu') + mu_path = sbox.ospath('A/mu') + # CRLF is a string that will match a CRLF sequence read from a text file. + # ### On Windows, we assume CRLF will be read as LF, so it's a poor test. if os.name == 'nt': crlf = '\n' else: @@ -3950,7 +3976,7 @@ def avoid_repeated_merge_using_inherited_merge_info(sbox): sbox.build() wc_dir = sbox.wc_dir - A_path = os.path.join(wc_dir, 'A') + A_path = sbox.ospath('A') A_B_path = os.path.join(A_path, 'B') A_B_E_path = os.path.join(A_B_path, 'E') A_B_F_path = os.path.join(A_B_path, 'F') @@ -4069,7 +4095,7 @@ def avoid_repeated_merge_on_subtree_with_merge_info(sbox): sbox.build() wc_dir = sbox.wc_dir - A_path = os.path.join(wc_dir, 'A') + A_path = sbox.ospath('A') A_B_path = os.path.join(A_path, 'B') A_B_E_path = os.path.join(A_B_path, 'E') A_B_F_path = os.path.join(A_B_path, 'F') @@ -4331,9 +4357,9 @@ def obey_reporter_api_semantics_while_doing_subtree_merges(sbox): sbox.build() wc_dir = sbox.wc_dir - A_path = os.path.join(wc_dir, 'A') - A_D_path = os.path.join(wc_dir, 'A', 'D') - copy_of_A_D_path = os.path.join(wc_dir, 'A', 'copy-of-D') + A_path = sbox.ospath('A') + A_D_path = sbox.ospath('A/D') + copy_of_A_D_path = sbox.ospath('A/copy-of-D') svntest.main.run_svn(None, "cp", A_D_path, copy_of_A_D_path) @@ -4463,6 +4489,12 @@ def set_up_branch(sbox, branch_only = False, nbr_of_branches = 1): r(5 + NBR_OF_BRANCHES) - A/D/H/omega Return (expected_disk, expected_status).''' + # With the default parameters, the branching looks like this: + # + # A -1-----3-4-5-6-- + # \ + # A_COPY 2----------- + wc_dir = sbox.wc_dir expected_status = svntest.actions.get_virginal_state(wc_dir, 1) @@ -4556,7 +4588,7 @@ def set_up_branch(sbox, branch_only = False, nbr_of_branches = 1): # Make some changes under A which we'll later merge under A_COPY: # r(nbr_of_branches + 2) - modify and commit A/D/H/psi - svntest.main.file_write(os.path.join(wc_dir, "A", "D", "H", "psi"), + svntest.main.file_write(sbox.ospath('A/D/H/psi'), "New content") expected_output = wc.State(wc_dir, {'A/D/H/psi' : Item(verb='Sending')}) expected_status.tweak('A/D/H/psi', wc_rev=nbr_of_branches + 2) @@ -4565,7 +4597,7 @@ def set_up_branch(sbox, branch_only = False, nbr_of_branches = 1): expected_disk.tweak('A/D/H/psi', contents="New content") # r(nbr_of_branches + 3) - modify and commit A/D/G/rho - svntest.main.file_write(os.path.join(wc_dir, "A", "D", "G", "rho"), + svntest.main.file_write(sbox.ospath('A/D/G/rho'), "New content") expected_output = wc.State(wc_dir, {'A/D/G/rho' : Item(verb='Sending')}) expected_status.tweak('A/D/G/rho', wc_rev=nbr_of_branches + 3) @@ -4574,7 +4606,7 @@ def set_up_branch(sbox, branch_only = False, nbr_of_branches = 1): expected_disk.tweak('A/D/G/rho', contents="New content") # r(nbr_of_branches + 4) - modify and commit A/B/E/beta - svntest.main.file_write(os.path.join(wc_dir, "A", "B", "E", "beta"), + svntest.main.file_write(sbox.ospath('A/B/E/beta'), "New content") expected_output = wc.State(wc_dir, {'A/B/E/beta' : Item(verb='Sending')}) expected_status.tweak('A/B/E/beta', wc_rev=nbr_of_branches + 4) @@ -4583,7 +4615,7 @@ def set_up_branch(sbox, branch_only = False, nbr_of_branches = 1): expected_disk.tweak('A/B/E/beta', contents="New content") # r(nbr_of_branches + 5) - modify and commit A/D/H/omega - svntest.main.file_write(os.path.join(wc_dir, "A", "D", "H", "omega"), + svntest.main.file_write(sbox.ospath('A/D/H/omega'), "New content") expected_output = wc.State(wc_dir, {'A/D/H/omega' : Item(verb='Sending')}) expected_status.tweak('A/D/H/omega', wc_rev=nbr_of_branches + 5) @@ -4619,13 +4651,13 @@ def mergeinfo_inheritance(sbox): wc_disk, wc_status = set_up_branch(sbox) # Some paths we'll care about - A_COPY_path = os.path.join(wc_dir, "A_COPY") - B_COPY_path = os.path.join(wc_dir, "A_COPY", "B") - beta_COPY_path = os.path.join(wc_dir, "A_COPY", "B", "E", "beta") - E_COPY_path = os.path.join(wc_dir, "A_COPY", "B", "E") - omega_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "H", "omega") - D_COPY_path = os.path.join(wc_dir, "A_COPY", "D") - G_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "G") + A_COPY_path = sbox.ospath('A_COPY') + B_COPY_path = sbox.ospath('A_COPY/B') + beta_COPY_path = sbox.ospath('A_COPY/B/E/beta') + E_COPY_path = sbox.ospath('A_COPY/B/E') + omega_COPY_path = sbox.ospath('A_COPY/D/H/omega') + D_COPY_path = sbox.ospath('A_COPY/D') + G_COPY_path = sbox.ospath('A_COPY/D/G') # Now start merging... @@ -4890,56 +4922,54 @@ def mergeinfo_inheritance(sbox): # In single-db mode you can't create a disconnected working copy by just # copying a subdir - if svntest.main.wc_is_singledb(wc_dir): - return - - # Copy the subtree A_COPY/B/E from the working copy, making the - # disconnected WC E_only. - other_wc = sbox.add_wc_path('E_only') - svntest.actions.duplicate_dir(E_COPY_path, other_wc) - - # Update the disconnected WC it so it will get the most recent mergeinfo - # from the repos when merging. - svntest.actions.run_and_verify_svn(None, exp_noop_up_out(7), [], 'up', - other_wc) - - # Merge r5:4 into the root of the disconnected WC. - # E_only has no explicit mergeinfo and since it's the root of the WC - # cannot inherit any mergeinfo from a working copy ancestor path. Nor - # does it have any mergeinfo explicitly set on it in the repository. - # An ancestor path on the repository side, A_COPY/B does have the merge - # info '/A/B:5' however and E_only should inherit this, resulting in - # empty mergeinfo after the removal of r5 (A_COPY has mergeinfo of - # '/A:3' so this empty mergeinfo is needed to override that. - expected_output = wc.State(other_wc, - {'beta' : Item(status='U ')}) - expected_mergeinfo_output = wc.State(other_wc, { - '' : Item(status=' G') - }) - expected_elision_output = wc.State(other_wc, { - }) - expected_status = wc.State(other_wc, { - '' : Item(status=' M', wc_rev=7), - 'alpha' : Item(status=' ', wc_rev=7), - 'beta' : Item(status='M ', wc_rev=7), - }) - expected_disk = wc.State('', { - '' : Item(props={SVN_PROP_MERGEINFO : ''}), - 'alpha' : Item("This is the file 'alpha'.\n"), - 'beta' : Item("This is the file 'beta'.\n"), - }) - expected_skip = wc.State(other_wc, { }) - svntest.actions.run_and_verify_merge(other_wc, '5', '4', - sbox.repo_url + '/A/B/E', None, - expected_output, - expected_mergeinfo_output, - expected_elision_output, - expected_disk, - expected_status, - expected_skip, - None, None, None, None, - None, 1) + ## Copy the subtree A_COPY/B/E from the working copy, making the + ## disconnected WC E_only. + #other_wc = sbox.add_wc_path('E_only') + #svntest.actions.duplicate_dir(E_COPY_path, other_wc) + # + ## Update the disconnected WC it so it will get the most recent mergeinfo + ## from the repos when merging. + #svntest.actions.run_and_verify_svn(None, exp_noop_up_out(7), [], 'up', + # other_wc) + # + ## Merge r5:4 into the root of the disconnected WC. + ## E_only has no explicit mergeinfo and since it's the root of the WC + ## cannot inherit any mergeinfo from a working copy ancestor path. Nor + ## does it have any mergeinfo explicitly set on it in the repository. + ## An ancestor path on the repository side, A_COPY/B does have the merge + ## info '/A/B:5' however and E_only should inherit this, resulting in + ## empty mergeinfo after the removal of r5 (A_COPY has mergeinfo of + ## '/A:3' so this empty mergeinfo is needed to override that. + #expected_output = wc.State(other_wc, + # {'beta' : Item(status='U ')}) + #expected_mergeinfo_output = wc.State(other_wc, { + # '' : Item(status=' G') + # }) + #expected_elision_output = wc.State(other_wc, { + # }) + #expected_status = wc.State(other_wc, { + # '' : Item(status=' M', wc_rev=7), + # 'alpha' : Item(status=' ', wc_rev=7), + # 'beta' : Item(status='M ', wc_rev=7), + # }) + #expected_disk = wc.State('', { + # '' : Item(props={SVN_PROP_MERGEINFO : ''}), + # 'alpha' : Item("This is the file 'alpha'.\n"), + # 'beta' : Item("This is the file 'beta'.\n"), + # }) + #expected_skip = wc.State(other_wc, { }) + # + #svntest.actions.run_and_verify_merge(other_wc, '5', '4', + # sbox.repo_url + '/A/B/E', None, + # expected_output, + # expected_mergeinfo_output, + # expected_elision_output, + # expected_disk, + # expected_status, + # expected_skip, + # None, None, None, None, + # None, 1) #---------------------------------------------------------------------- @SkipUnless(server_has_mergeinfo) @@ -4957,9 +4987,9 @@ def mergeinfo_elision(sbox): wc_disk, wc_status = set_up_branch(sbox) # Some paths we'll care about - A_COPY_path = os.path.join(wc_dir, "A_COPY") - beta_COPY_path = os.path.join(wc_dir, "A_COPY", "B", "E", "beta") - G_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "G") + A_COPY_path = sbox.ospath('A_COPY') + beta_COPY_path = sbox.ospath('A_COPY/B/E/beta') + G_COPY_path = sbox.ospath('A_COPY/D/G') # Now start merging... @@ -5211,9 +5241,9 @@ def mergeinfo_inheritance_and_discontinuous_ranges(sbox): # Some paths we'll care about A_url = sbox.repo_url + '/A' - A_COPY_path = os.path.join(wc_dir, "A_COPY") - D_COPY_path = os.path.join(wc_dir, "A_COPY", "D") - A_COPY_rho_path = os.path.join(wc_dir, "A_COPY", "D", "G", "rho") + A_COPY_path = sbox.ospath('A_COPY') + D_COPY_path = sbox.ospath('A_COPY/D') + A_COPY_rho_path = sbox.ospath('A_COPY/D/G/rho') expected_disk, expected_status = set_up_branch(sbox) @@ -5324,9 +5354,9 @@ def merge_to_target_with_copied_children(sbox): expected_disk, expected_status = set_up_branch(sbox) # Some paths we'll care about - D_COPY_path = os.path.join(wc_dir, "A_COPY", "D") - G_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "G") - rho_COPY_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "G", "rho_copy") + D_COPY_path = sbox.ospath('A_COPY/D') + G_COPY_path = sbox.ospath('A_COPY/D/G') + rho_COPY_COPY_path = sbox.ospath('A_COPY/D/G/rho_copy') # URL to URL copy A_COPY/D/G/rho to A_COPY/D/G/rho_copy svntest.actions.run_and_verify_svn(None, None, [], 'copy', @@ -5410,11 +5440,11 @@ def merge_to_switched_path(sbox): wc_disk, wc_status = set_up_branch(sbox) # Some paths we'll care about - A_COPY_path = os.path.join(wc_dir, "A_COPY") - A_COPY_D_path = os.path.join(wc_dir, "A_COPY", "D") - G_COPY_path = os.path.join(wc_dir, "A", "D", "G_COPY") - A_COPY_D_G_path = os.path.join(wc_dir, "A_COPY", "D", "G") - A_COPY_D_G_rho_path = os.path.join(wc_dir, "A_COPY", "D", "G", "rho") + A_COPY_path = sbox.ospath('A_COPY') + A_COPY_D_path = sbox.ospath('A_COPY/D') + G_COPY_path = sbox.ospath('A/D/G_COPY') + A_COPY_D_G_path = sbox.ospath('A_COPY/D/G') + A_COPY_D_G_rho_path = sbox.ospath('A_COPY/D/G/rho') expected = svntest.verify.UnorderedOutput( ["A " + os.path.join(G_COPY_path, "pi") + "\n", @@ -5440,7 +5470,7 @@ def merge_to_switched_path(sbox): None, wc_dir) # r8 - modify and commit A/D/G_COPY/rho - svntest.main.file_write(os.path.join(wc_dir, "A", "D", "G_COPY", "rho"), + svntest.main.file_write(sbox.ospath('A/D/G_COPY/rho'), "New *and* improved rho content") expected_output = wc.State(wc_dir, {'A/D/G_COPY/rho' : Item(verb='Sending')}) wc_status.tweak('A/D/G_COPY/rho', wc_rev=8) @@ -5573,7 +5603,7 @@ def merge_to_switched_path(sbox): # 3188: Mergeinfo on switched targets/subtrees should # elide to repos @SkipUnless(server_has_mergeinfo) -@Issue(2823,2839,3187,3188) +@Issue(2823,2839,3187,3188,4056) def merge_to_path_with_switched_children(sbox): "merge to path with switched children" @@ -5603,18 +5633,18 @@ def merge_to_path_with_switched_children(sbox): wc_disk, wc_status = set_up_branch(sbox, False, 3) # Some paths we'll care about - D_path = os.path.join(wc_dir, "A", "D") - A_COPY_path = os.path.join(wc_dir, "A_COPY") - A_COPY_beta_path = os.path.join(wc_dir, "A_COPY", "B", "E", "beta") - A_COPY_chi_path = os.path.join(wc_dir, "A_COPY", "D", "H", "chi") - A_COPY_omega_path = os.path.join(wc_dir, "A_COPY", "D", "H", "omega") - A_COPY_psi_path = os.path.join(wc_dir, "A_COPY", "D", "H", "psi") - A_COPY_G_path = os.path.join(wc_dir, "A_COPY", "D", "G") - A_COPY_rho_path = os.path.join(wc_dir, "A_COPY", "D", "G", "rho") - A_COPY_H_path = os.path.join(wc_dir, "A_COPY", "D", "H") - A_COPY_D_path = os.path.join(wc_dir, "A_COPY", "D") - A_COPY_gamma_path = os.path.join(wc_dir, "A_COPY", "D", "gamma") - H_COPY_2_path = os.path.join(wc_dir, "A_COPY_2", "D", "H") + D_path = sbox.ospath('A/D') + A_COPY_path = sbox.ospath('A_COPY') + A_COPY_beta_path = sbox.ospath('A_COPY/B/E/beta') + A_COPY_chi_path = sbox.ospath('A_COPY/D/H/chi') + A_COPY_omega_path = sbox.ospath('A_COPY/D/H/omega') + A_COPY_psi_path = sbox.ospath('A_COPY/D/H/psi') + A_COPY_G_path = sbox.ospath('A_COPY/D/G') + A_COPY_rho_path = sbox.ospath('A_COPY/D/G/rho') + A_COPY_H_path = sbox.ospath('A_COPY/D/H') + A_COPY_D_path = sbox.ospath('A_COPY/D') + A_COPY_gamma_path = sbox.ospath('A_COPY/D/gamma') + H_COPY_2_path = sbox.ospath('A_COPY_2/D/H') svntest.actions.run_and_verify_svn(None, exp_noop_up_out(8), [], 'up', wc_dir) @@ -5668,18 +5698,18 @@ def merge_to_path_with_switched_children(sbox): 'omega' : Item(status=' U') }) expected_elision_output = wc.State(A_COPY_H_path, { + 'omega' : Item(status=' U') }) expected_status = wc.State(A_COPY_H_path, { '' : Item(status=' M', wc_rev=8), 'psi' : Item(status=' ', wc_rev=8, switched='S'), - 'omega' : Item(status='MM', wc_rev=8), + 'omega' : Item(status='M ', wc_rev=8), 'chi' : Item(status=' ', wc_rev=8), }) expected_disk = wc.State('', { - '' : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:8*'}), + '' : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:8'}), 'psi' : Item("This is the file 'psi'.\n"), - 'omega' : Item("New content", - props={SVN_PROP_MERGEINFO : '/A/D/H/omega:8'}), + 'omega' : Item("New content"), 'chi' : Item("This is the file 'chi'.\n"), }) expected_skip = wc.State(A_COPY_H_path, { }) @@ -5713,7 +5743,7 @@ def merge_to_path_with_switched_children(sbox): '' : Item(status=' M', wc_rev=8), 'H' : Item(status=' M', wc_rev=8), 'H/chi' : Item(status=' ', wc_rev=8), - 'H/omega' : Item(status='MM', wc_rev=8), + 'H/omega' : Item(status='M ', wc_rev=8), 'H/psi' : Item(status=' ', wc_rev=8, switched='S'), 'G' : Item(status=' M', wc_rev=8, switched='S'), 'G/pi' : Item(status=' ', wc_rev=8), @@ -5723,10 +5753,9 @@ def merge_to_path_with_switched_children(sbox): }) expected_disk_D = wc.State('', { '' : Item(props={SVN_PROP_MERGEINFO : '/A/D:6*'}), - 'H' : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:8*'}), + 'H' : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:8'}), 'H/chi' : Item("This is the file 'chi'.\n"), - 'H/omega' : Item("New content", - props={SVN_PROP_MERGEINFO : '/A/D/H/omega:8'}), + 'H/omega' : Item("New content"), 'H/psi' : Item("This is the file 'psi'.\n",), 'G' : Item(props={SVN_PROP_MERGEINFO : '/A/D/G:6*'}), 'G/pi' : Item("This is the file 'pi'.\n"), @@ -5756,12 +5785,12 @@ def merge_to_path_with_switched_children(sbox): expected_mergeinfo_output = wc.State(A_COPY_D_path, { '' : Item(status=' G'), 'H' : Item(status=' G'), - 'H/psi' : Item(status=' G') + 'H/psi' : Item(status=' U') }) expected_elision_output = wc.State(A_COPY_D_path, { }) - expected_disk_D.tweak('', props={SVN_PROP_MERGEINFO : '/A/D:5-6*'}) - expected_disk_D.tweak('H', props={SVN_PROP_MERGEINFO : '/A/D/H:5*,8*'}) + expected_disk_D.tweak('', props={SVN_PROP_MERGEINFO : '/A/D:5,6*'}) + expected_disk_D.tweak('H', props={SVN_PROP_MERGEINFO : '/A/D/H:5*,8'}) expected_disk_D.tweak('H/psi', contents="New content", props={SVN_PROP_MERGEINFO :'/A/D/H/psi:5'}) expected_status_D.tweak('H/psi', status='MM') @@ -5804,7 +5833,7 @@ def merge_to_path_with_switched_children(sbox): 'D/H' : Item(status=' M', wc_rev=8), 'D/H/chi' : Item(status=' ', wc_rev=8), 'D/H/psi' : Item(status='MM', wc_rev=8, switched='S'), - 'D/H/omega' : Item(status='MM', wc_rev=8), + 'D/H/omega' : Item(status='M ', wc_rev=8), }) expected_disk = wc.State('', { '' : Item(props={SVN_PROP_MERGEINFO : '/A:5-8'}), @@ -5816,19 +5845,18 @@ def merge_to_path_with_switched_children(sbox): 'B/lambda' : Item("This is the file 'lambda'.\n"), 'B/F' : Item(), 'C' : Item(), - 'D' : Item(props={SVN_PROP_MERGEINFO : '/A/D:5-6*'}), + 'D' : Item(props={SVN_PROP_MERGEINFO : '/A/D:5,6*'}), 'D/G' : Item(props={SVN_PROP_MERGEINFO : '/A/D/G:6*'}), 'D/G/pi' : Item("This is the file 'pi'.\n"), 'D/G/rho' : Item("New content", props={SVN_PROP_MERGEINFO : '/A/D/G/rho:6'}), 'D/G/tau' : Item("This is the file 'tau'.\n"), 'D/gamma' : Item("This is the file 'gamma'.\n"), - 'D/H' : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:5*,8*'}), + 'D/H' : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:5*,8'}), 'D/H/chi' : Item("This is the file 'chi'.\n"), 'D/H/psi' : Item("New content", props={SVN_PROP_MERGEINFO : '/A/D/H/psi:5'}), - 'D/H/omega' : Item("New content", - props={SVN_PROP_MERGEINFO : '/A/D/H/omega:8'}), + 'D/H/omega' : Item("New content"), }) expected_skip = wc.State(A_COPY_path, { }) svntest.actions.run_and_verify_merge(A_COPY_path, '4', '8', @@ -5839,7 +5867,6 @@ def merge_to_path_with_switched_children(sbox): expected_disk, expected_status, expected_skip, None, None, None, None, None, 1) - # Commit changes thus far. expected_output = svntest.wc.State(wc_dir, { 'A_COPY' : Item(verb='Sending'), @@ -5866,17 +5893,16 @@ def merge_to_path_with_switched_children(sbox): wc_disk.tweak("A_COPY/B/E/beta", contents="New content") wc_disk.tweak("A_COPY/D", - props={SVN_PROP_MERGEINFO : '/A/D:5-6*'}) + props={SVN_PROP_MERGEINFO : '/A/D:5,6*'}) wc_disk.tweak("A_COPY/D/G", props={SVN_PROP_MERGEINFO : '/A/D/G:6*'}) wc_disk.tweak("A_COPY/D/G/rho", contents="New content", props={SVN_PROP_MERGEINFO : '/A/D/G/rho:6'}) wc_disk.tweak("A_COPY/D/H", - props={SVN_PROP_MERGEINFO : '/A/D/H:5*,8*'}) + props={SVN_PROP_MERGEINFO : '/A/D/H:5*,8'}) wc_disk.tweak("A_COPY/D/H/omega", - contents="New content", - props={SVN_PROP_MERGEINFO : '/A/D/H/omega:8'}) + contents="New content") wc_disk.tweak("A_COPY_2", props={}) svntest.actions.run_and_verify_switch(sbox.wc_dir, A_COPY_psi_path, sbox.repo_url + "/A_COPY/D/H/psi", @@ -5915,8 +5941,7 @@ def merge_to_path_with_switched_children(sbox): expected_disk = wc.State('', { '' : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:5-8'}), 'psi' : Item("New content"), - 'omega' : Item("New content", - props={SVN_PROP_MERGEINFO : '/A/D/H/omega:8'}), + 'omega' : Item("New content"), 'chi' : Item("This is the file 'chi'.\n"), }) expected_skip = wc.State(A_COPY_H_path, { }) @@ -5966,14 +5991,11 @@ def merge_to_path_with_switched_children(sbox): expected_status_D.tweak('H/psi', wc_rev=10, switched=None) expected_status_D.tweak('H/omega', wc_rev=9) expected_status_D.tweak('G', 'G/rho', switched='S', wc_rev=9) - expected_disk_D.tweak('', props={SVN_PROP_MERGEINFO : '/A/D:5-6*,10*', + expected_disk_D.tweak('', props={SVN_PROP_MERGEINFO : '/A/D:5,6*,10', "prop:name" : "propval"}) expected_disk_D.tweak('G/rho', props={SVN_PROP_MERGEINFO : '/A/D/G/rho:6'}) expected_disk_D.tweak('H', props={SVN_PROP_MERGEINFO : '/A/D/H:5-8'}) - - expected_disk_D.tweak('H/omega', - props={SVN_PROP_MERGEINFO : '/A/D/H/omega:8'}) expected_disk_D.tweak('H/psi', contents="New content", props={}) svntest.actions.run_and_verify_merge(A_COPY_D_path, '9', '10', sbox.repo_url + '/A/D', None, @@ -6034,7 +6056,6 @@ def merge_to_path_with_switched_children(sbox): 'D/G' : Item(status=' U'), 'D/G/rho' : Item(status=' U'), 'D/H' : Item(status=' U'), - 'D/H/omega' : Item(status=' U'), }) expected_elision_output = wc.State(A_COPY_path, { '' : Item(status=' U'), @@ -6042,7 +6063,6 @@ def merge_to_path_with_switched_children(sbox): 'D/G' : Item(status=' U'), 'D/G/rho' : Item(status=' U'), 'D/H' : Item(status=' U'), - 'D/H/omega' : Item(status=' U'), }) expected_status = wc.State(A_COPY_path, { '' : Item(status=' M', wc_rev=10), @@ -6063,7 +6083,7 @@ def merge_to_path_with_switched_children(sbox): 'D/H' : Item(status=' M', wc_rev=10), 'D/H/chi' : Item(status=' ', wc_rev=10), 'D/H/psi' : Item(status='M ', wc_rev=10), - 'D/H/omega' : Item(status='MM', wc_rev=10), + 'D/H/omega' : Item(status='M ', wc_rev=10), }) expected_disk = wc.State('', { 'B' : Item(), @@ -6108,7 +6128,7 @@ def merge_with_implicit_target_file(sbox): # Make a change to A/mu, then revert it using 'svn merge -r 2:1 A/mu' # change A/mu and commit - A_path = os.path.join(wc_dir, 'A') + A_path = sbox.ospath('A') mu_path = os.path.join(A_path, 'mu') svntest.main.file_append(mu_path, "A whole new line.\n") @@ -6161,10 +6181,10 @@ def empty_mergeinfo(sbox): wc_disk, wc_status = set_up_branch(sbox) # Some paths we'll care about - A_COPY_path = os.path.join(wc_dir, "A_COPY") - H_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "H") - psi_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "H", "psi") - rho_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "G", "rho") + A_COPY_path = sbox.ospath('A_COPY') + H_COPY_path = sbox.ospath('A_COPY/D/H') + psi_COPY_path = sbox.ospath('A_COPY/D/H/psi') + rho_COPY_path = sbox.ospath('A_COPY/D/G/rho') # Test area A -- Merge r2:4 into A_COPY then reverse merge 4:2 to # A_COPY/D/G. A_COPY/D/G should end up with empty mergeinfo to @@ -6299,9 +6319,9 @@ def prop_add_to_child_with_mergeinfo(sbox): expected_disk, expected_status = set_up_branch(sbox) # Some paths we'll care about - beta_path = os.path.join(wc_dir, "A", "B", "E", "beta") - beta_COPY_path = os.path.join(wc_dir, "A_COPY", "B", "E", "beta") - B_COPY_path = os.path.join(wc_dir, "A_COPY", "B") + beta_path = sbox.ospath('A/B/E/beta') + beta_COPY_path = sbox.ospath('A_COPY/B/E/beta') + B_COPY_path = sbox.ospath('A_COPY/B') # Set a non-mergeinfo prop on a file. svntest.actions.run_and_verify_svn(None, @@ -6390,7 +6410,7 @@ def foreign_repos_does_not_update_mergeinfo(sbox): # Merge r3:4 (using implied peg revisions) from 'other' repos into # A_COPY/D/G. Merge should succeed, but no mergeinfo should be set. - G_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "G") + G_COPY_path = sbox.ospath('A_COPY/D/G') svntest.actions.run_and_verify_svn(None, expected_merge_output([[4]], 'U ' + @@ -6402,7 +6422,7 @@ def foreign_repos_does_not_update_mergeinfo(sbox): # Merge r4:5 (using explicit peg revisions) from 'other' repos into # A_COPY/B/E. Merge should succeed, but no mergeinfo should be set. - E_COPY_path = os.path.join(wc_dir, "A_COPY", "B", "E") + E_COPY_path = sbox.ospath('A_COPY/B/E') svntest.actions.run_and_verify_svn(None, expected_merge_output([[5]], 'U ' + @@ -6471,7 +6491,7 @@ def foreign_repos_does_not_update_mergeinfo(sbox): svntest.actions.run_and_verify_svn(None, None, [], 'merge', other_repo_url + '/A@1', other_repo_url + '/A_COPY@7', - os.path.join(wc_dir, 'A_COPY')) + sbox.ospath('A_COPY')) #...which means there should be no mergeinfo anywhere in WC_DIR, since # this test never created any. svntest.actions.run_and_verify_svn(None, [], [], 'pg', @@ -6493,10 +6513,10 @@ def avoid_reflected_revs(sbox): wc_disk, wc_status = set_up_branch(sbox, True, 1) # Some paths we'll care about - A_path = os.path.join(wc_dir, 'A') - A_COPY_path = os.path.join(wc_dir, 'A_COPY') - tfile1_path = os.path.join(wc_dir, 'A', 'tfile1') - tfile2_path = os.path.join(wc_dir, 'A', 'tfile2') + A_path = sbox.ospath('A') + A_COPY_path = sbox.ospath('A_COPY') + tfile1_path = sbox.ospath('A/tfile1') + tfile2_path = sbox.ospath('A/tfile2') bfile1_path = os.path.join(A_COPY_path, 'bfile1') bfile2_path = os.path.join(A_COPY_path, 'bfile2') @@ -6507,7 +6527,7 @@ def avoid_reflected_revs(sbox): bfile2_content = "This is bfile2\n" # We'll consider A as the trunk and A_COPY as the feature branch - # Create a tfile1 in A + # r3 - Create a tfile1 in A svntest.main.file_write(tfile1_path, tfile1_content) svntest.actions.run_and_verify_svn(None, None, [], 'add', tfile1_path) expected_output = wc.State(wc_dir, {'A/tfile1' : Item(verb='Adding')}) @@ -6515,7 +6535,7 @@ def avoid_reflected_revs(sbox): svntest.actions.run_and_verify_commit(wc_dir, expected_output, wc_status, None, wc_dir) - # Create a bfile1 in A_COPY + # r4 - Create a bfile1 in A_COPY svntest.main.file_write(bfile1_path, bfile1_content) svntest.actions.run_and_verify_svn(None, None, [], 'add', bfile1_path) expected_output = wc.State(wc_dir, {'A_COPY/bfile1' : Item(verb='Adding')}) @@ -6523,7 +6543,7 @@ def avoid_reflected_revs(sbox): svntest.actions.run_and_verify_commit(wc_dir, expected_output, wc_status, None, wc_dir) - # Create one more file in A + # r5 - Create one more file in A svntest.main.file_write(tfile2_path, tfile2_content) svntest.actions.run_and_verify_svn(None, None, [], 'add', tfile2_path) expected_output = wc.State(wc_dir, {'A/tfile2' : Item(verb='Adding')}) @@ -6600,7 +6620,6 @@ def avoid_reflected_revs(sbox): None, A_COPY_path, '--allow-mixed-revisions') - # Sync up with the trunk ie., A svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir) expected_output = wc.State(wc_dir, { 'A_COPY' : Item(verb='Sending'), @@ -6641,7 +6660,6 @@ def avoid_reflected_revs(sbox): None, A_COPY_path, '--allow-mixed-revisions') - svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir) expected_output = wc.State(wc_dir, { 'A_COPY' : Item(verb='Sending'), @@ -6650,7 +6668,7 @@ def avoid_reflected_revs(sbox): svntest.actions.run_and_verify_commit(wc_dir, expected_output, None, None, wc_dir) - # Add bfile2 to A_COPY + # r8 - Add bfile2 to A_COPY svntest.main.file_write(bfile2_path, bfile2_content) svntest.actions.run_and_verify_svn(None, None, [], 'add', bfile2_path) expected_output = wc.State(wc_dir, {'A_COPY/bfile2' : Item(verb='Adding')}) @@ -6666,7 +6684,6 @@ def avoid_reflected_revs(sbox): # Merge 2:8 from A_COPY(feature branch) to A(trunk). expected_output = wc.State(A_path, { - '' : Item(status='C '), 'bfile2' : Item(status='A '), 'bfile1' : Item(status='A '), }) @@ -6676,7 +6693,7 @@ def avoid_reflected_revs(sbox): expected_elision_output = wc.State(A_path, { }) expected_status = wc.State(A_path, { - '' : Item(status='CM', wc_rev=6), + '' : Item(status=' M', wc_rev=6), 'bfile2' : Item(status='A ', wc_rev='-', copied='+'), 'bfile1' : Item(status='A ', wc_rev='-', copied='+'), 'tfile2' : Item(status=' ', wc_rev=6), @@ -6751,7 +6768,7 @@ def update_loses_mergeinfo(sbox): sbox.build() wc_dir = sbox.wc_dir - A_C_wc_dir = os.path.join(wc_dir, 'A', 'C') + A_C_wc_dir = sbox.ospath('A/C') A_B_url = sbox.repo_url + '/A/B' A_B_J_url = sbox.repo_url + '/A/B/J' A_B_K_url = sbox.repo_url + '/A/B/K' @@ -6871,7 +6888,7 @@ def merge_loses_mergeinfo(sbox): sbox.build() wc_dir = sbox.wc_dir - A_C_wc_dir = os.path.join(wc_dir, 'A', 'C') + A_C_wc_dir = sbox.ospath('A/C') A_B_url = sbox.repo_url + '/A/B' A_B_J_url = sbox.repo_url + '/A/B/J' A_B_K_url = sbox.repo_url + '/A/B/K' @@ -6926,9 +6943,7 @@ def merge_loses_mergeinfo(sbox): expected_elision_output = wc.State(A_C_wc_dir, { '' : Item(status=' U'), }) - expected_disk = wc.State('', {'J': Item()}) - if svntest.main.wc_is_singledb(wc_dir): - expected_disk.remove('J') + expected_disk = wc.State('', {}) expected_status = wc.State(A_C_wc_dir, { '' : Item(wc_rev=4, status=' M'), 'J' : Item(wc_rev=4, status='D ') @@ -6947,11 +6962,8 @@ def merge_loses_mergeinfo(sbox): expected_output = wc.State(A_C_wc_dir, {'K' : Item(status='A ')}) expected_disk = wc.State('', { 'K' : Item(), - 'J' : Item(), '' : Item(props={SVN_PROP_MERGEINFO : '/A/B:3'}), }) - if svntest.main.wc_is_singledb(wc_dir): - expected_disk.remove('J') expected_status = wc.State(A_C_wc_dir, { '' : Item(wc_rev=4, status=' M'), 'K' : Item(status='A ', @@ -6984,8 +6996,8 @@ def single_file_replace_style_merge_capability(sbox): sbox.build() wc_dir = sbox.wc_dir - iota_path = os.path.join(wc_dir, 'iota') - mu_path = os.path.join(wc_dir, 'A', 'mu') + iota_path = sbox.ospath('iota') + mu_path = sbox.ospath('A/mu') # delete mu and replace it with a copy of iota svntest.main.run_svn(None, 'rm', mu_path) @@ -7006,8 +7018,7 @@ def single_file_replace_style_merge_capability(sbox): # Merge the file mu alone to rev1 svntest.actions.run_and_verify_svn(None, expected_merge_output(None, - ['D ' + mu_path + '\n', - 'A ' + mu_path + '\n']), + ['R ' + mu_path + '\n']), [], 'merge', mu_path + '@2', @@ -7031,7 +7042,7 @@ def merge_to_out_of_date_target(sbox): svntest.actions.duplicate_dir(wc_dir, other_wc) # Some paths we'll care about - A_COPY_H_path = os.path.join(wc_dir, "A_COPY", "D", "H") + A_COPY_H_path = sbox.ospath('A_COPY/D/H') other_A_COPY_H_path = os.path.join(other_wc, "A_COPY", "D", "H") # Merge -c3 into A_COPY/D/H of first WC. @@ -7140,10 +7151,10 @@ def merge_with_depth_files(sbox): wc_dir = sbox.wc_dir # Some paths we'll care about - mu_path = os.path.join(wc_dir, 'A', 'mu') - gamma_path = os.path.join(wc_dir, 'A', 'D', 'gamma') - Acopy_path = os.path.join(wc_dir, 'A_copy') - Acopy_mu_path = os.path.join(wc_dir, 'A_copy', 'mu') + mu_path = sbox.ospath('A/mu') + gamma_path = sbox.ospath('A/D/gamma') + Acopy_path = sbox.ospath('A_copy') + Acopy_mu_path = sbox.ospath('A_copy/mu') A_url = sbox.repo_url + '/A' Acopy_url = sbox.repo_url + '/A_copy' @@ -7260,7 +7271,7 @@ def merge_with_depth_files(sbox): # # Test issue #3407 'Shallow merges incorrectly set mergeinfo on children'. @SkipUnless(server_has_mergeinfo) -@Issues(2976,3392,3407) +@Issues(2976,3392,3407,4057) def merge_away_subtrees_noninheritable_ranges(sbox): "subtrees can lose non-inheritable ranges" @@ -7269,16 +7280,16 @@ def merge_away_subtrees_noninheritable_ranges(sbox): wc_disk, wc_status = set_up_branch(sbox, nbr_of_branches=2) # Some paths we'll care about - H_path = os.path.join(wc_dir, "A", "D", "H") - D_COPY_path = os.path.join(wc_dir, "A_COPY", "D") - A_COPY_path = os.path.join(wc_dir, "A_COPY") - nu_path = os.path.join(wc_dir, "A", "nu") - mu_path = os.path.join(wc_dir, "A", "mu") - mu_2_path = os.path.join(wc_dir, "A_COPY_2", "mu") - D_COPY_2_path = os.path.join(wc_dir, "A_COPY_2", "D") - H_COPY_2_path = os.path.join(wc_dir, "A_COPY_2", "D", "H") - mu_COPY_path = os.path.join(wc_dir, "A_COPY", "mu") - nu_COPY_path = os.path.join(wc_dir, "A_COPY", "nu") + H_path = sbox.ospath('A/D/H') + D_COPY_path = sbox.ospath('A_COPY/D') + A_COPY_path = sbox.ospath('A_COPY') + nu_path = sbox.ospath('A/nu') + mu_path = sbox.ospath('A/mu') + mu_2_path = sbox.ospath('A_COPY_2/mu') + D_COPY_2_path = sbox.ospath('A_COPY_2/D') + H_COPY_2_path = sbox.ospath('A_COPY_2/D/H') + mu_COPY_path = sbox.ospath('A_COPY/mu') + nu_COPY_path = sbox.ospath('A_COPY/nu') # Make a change to directory A/D/H and commit as r8. svntest.actions.run_and_verify_svn(None, exp_noop_up_out(7), [], @@ -7403,16 +7414,16 @@ def merge_away_subtrees_noninheritable_ranges(sbox): # Now merge -c10 from A to A_COPY. svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir) - expected_output = wc.State('.', { + expected_output = wc.State('', { 'nu': Item(status='A '), }) - expected_mergeinfo_output = wc.State('.', { + expected_mergeinfo_output = wc.State('', { '' : Item(status=' U'), 'nu' : Item(status=' U'), }) - expected_elision_output = wc.State('.', { + expected_elision_output = wc.State('', { }) - expected_status = wc.State('.', { + expected_status = wc.State('', { '' : Item(status=' M'), 'nu' : Item(status='A ', copied='+'), 'B' : Item(status=' '), @@ -7462,7 +7473,7 @@ def merge_away_subtrees_noninheritable_ranges(sbox): expected_skip = wc.State('.', { }) saved_cwd = os.getcwd() os.chdir(A_COPY_path) - svntest.actions.run_and_verify_merge('.', '9', '10', + svntest.actions.run_and_verify_merge('', '9', '10', sbox.repo_url + '/A', None, expected_output, expected_mergeinfo_output, @@ -7510,7 +7521,7 @@ def merge_away_subtrees_noninheritable_ranges(sbox): }) expected_elision_output = wc.State('.', { }) - expected_status = wc.State('.', { + expected_status = wc.State('', { '' : Item(status=' M'), 'B' : Item(status=' '), 'mu' : Item(status='MM'), @@ -7579,8 +7590,9 @@ def merge_away_subtrees_noninheritable_ranges(sbox): svntest.actions.run_and_verify_svn(None, None, [], 'revert', '-R', wc_dir) svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir) - # Merge r8 from A/D/H to A_COPY_D/H at depth empty, creating non-inheritable - # mergeinfo on the target. Commit this merge as r13. + # Merge r8 from A/D/H to A_COPY_D/H at depth empty. Since r8 affects only + # A_COPY/D/H itself, the resulting mergeinfo is inheritable. Commit this + # merge as r13. expected_output = wc.State(H_COPY_2_path, { '' : Item(status=' U'), }) @@ -7596,7 +7608,7 @@ def merge_away_subtrees_noninheritable_ranges(sbox): 'chi' : Item(status=' ', wc_rev=12), }) expected_disk = wc.State('', { - '' : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:8*', + '' : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:8', "prop:name" : "propval"}), 'psi' : Item("This is the file 'psi'.\n"), 'omega' : Item("This is the file 'omega'.\n"), @@ -7613,7 +7625,7 @@ def merge_away_subtrees_noninheritable_ranges(sbox): None, None, None, None, None, 1, 1, '--depth', 'empty', H_COPY_2_path) svntest.actions.run_and_verify_svn(None, None, [], 'commit', '-m', - 'log msg', wc_dir); + 'log msg', wc_dir) svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir) # Now reverse the prior merge. Issue #3392 manifests itself here with # a mergeinfo parsing error: @@ -7703,16 +7715,16 @@ def merge_to_sparse_directories(sbox): wc_disk, wc_status = set_up_branch(sbox, False, 1) # Some paths we'll care about - A_path = os.path.join(wc_dir, "A") - D_path = os.path.join(wc_dir, "A", "D") - I_path = os.path.join(wc_dir, "A", "C", "I") - G_path = os.path.join(wc_dir, "A", "D", "G") - A_COPY_path = os.path.join(wc_dir, "A_COPY") + A_path = sbox.ospath('A') + D_path = sbox.ospath('A/D') + I_path = sbox.ospath('A/C/I') + G_path = sbox.ospath('A/D/G') + A_COPY_path = sbox.ospath('A_COPY') # Make a few more changes to the merge source... # r7 - modify and commit A/mu - svntest.main.file_write(os.path.join(wc_dir, "A", "mu"), + svntest.main.file_write(sbox.ospath('A/mu'), "New content") expected_output = wc.State(wc_dir, {'A/mu' : Item(verb='Sending')}) wc_status.tweak('A/mu', wc_rev=7) @@ -7777,10 +7789,17 @@ def merge_to_sparse_directories(sbox): # by the merge, 'B' and 'D', get non-inheritable mergeinfo for r4:9. # The root and 'D' do should also get the changes # that affect them directly (the prop adds from r8 and r9). + # + # Currently this fails due to r1424469. For a full explanation see + # http://svn.haxx.se/dev/archive-2012-12/0472.shtml + # and http://svn.haxx.se/dev/archive-2012-12/0475.shtml expected_output = wc.State(immediates_dir, { 'D' : Item(status=' U'), 'mu' : Item(status='U '), '' : Item(status=' U'), + # Shadowed below skips + 'D/H/omega' : Item(status=' ', treeconflict='U'), + 'B/E/beta' : Item(status=' ', treeconflict='U'), }) expected_mergeinfo_output = wc.State(immediates_dir, { '' : Item(status=' U'), @@ -7806,8 +7825,8 @@ def merge_to_sparse_directories(sbox): "prop:name" : "propval"}), }) expected_skip = svntest.wc.State(immediates_dir, { - 'D/H' : Item(), - 'B/E' : Item(), + 'D/H' : Item(verb='Skipped missing target'), + 'B/E' : Item(verb='Skipped missing target'), }) svntest.actions.run_and_verify_merge(immediates_dir, '4', '9', sbox.repo_url + '/A', None, @@ -7842,6 +7861,9 @@ def merge_to_sparse_directories(sbox): expected_output = wc.State(files_dir, { 'mu' : Item(status='U '), '' : Item(status=' U'), + # Below the skips + 'D/H/omega' : Item(status=' ', treeconflict='U'), + 'B/E/beta' : Item(status=' ', treeconflict='U'), }) expected_mergeinfo_output = wc.State(files_dir, { '' : Item(status=' U'), @@ -7860,8 +7882,8 @@ def merge_to_sparse_directories(sbox): props={SVN_PROP_MERGEINFO : '/A/mu:5-9'}), }) expected_skip = svntest.wc.State(files_dir, { - 'D' : Item(), - 'B' : Item(), + 'D' : Item(verb='Skipped missing target'), + 'B' : Item(verb='Skipped missing target'), }) svntest.actions.run_and_verify_merge(files_dir, '4', '9', sbox.repo_url + '/A', None, @@ -7889,6 +7911,9 @@ def merge_to_sparse_directories(sbox): # the one change that affects it directly (the prop add from r9). expected_output = wc.State(empty_dir, { '' : Item(status=' U'), + # Below the skips + 'B/E/beta' : Item(status=' ', treeconflict='U'), + 'D/H/omega' : Item(status=' ', treeconflict='U'), }) expected_mergeinfo_output = wc.State(empty_dir, { '' : Item(status=' U'), @@ -7903,9 +7928,9 @@ def merge_to_sparse_directories(sbox): "prop:name" : "propval"}), }) expected_skip = svntest.wc.State(empty_dir, { - 'mu' : Item(), - 'D' : Item(), - 'B' : Item(), + 'mu' : Item(verb='Skipped missing target'), + 'D' : Item(verb='Skipped missing target'), + 'B' : Item(verb='Skipped missing target'), }) svntest.actions.run_and_verify_merge(empty_dir, '4', '9', sbox.repo_url + '/A', None, @@ -7987,7 +8012,9 @@ def merge_to_sparse_directories(sbox): def merge_old_and_new_revs_from_renamed_dir(sbox): "merge -rold(before rename):head renamed dir" - ## See http://svn.haxx.se/dev/archive-2007-09/0706.shtml ## + # See the email on dev@ from Paul Burba, 2007-09-27, "RE: svn commit: + # r26803 - [...]", <http://svn.haxx.se/dev/archive-2007-09/0706.shtml> or + # <http://subversion.tigris.org/ds/viewMessage.do?dsForumId=462&dsMessageId=927127>. # Create a WC with a single branch sbox.build() @@ -7997,9 +8024,9 @@ def merge_old_and_new_revs_from_renamed_dir(sbox): # Some paths we'll care about A_url = sbox.repo_url + '/A' A_MOVED_url = sbox.repo_url + '/A_MOVED' - A_COPY_path = os.path.join(wc_dir, 'A_COPY') - mu_path = os.path.join(wc_dir, 'A', 'mu') - A_MOVED_mu_path = os.path.join(wc_dir, 'A_MOVED', 'mu') + A_COPY_path = sbox.ospath('A_COPY') + mu_path = sbox.ospath('A/mu') + A_MOVED_mu_path = sbox.ospath('A_MOVED/mu') # Make a modification to A/mu svntest.main.file_write(mu_path, "This is the file 'mu' modified.\n") @@ -8074,10 +8101,10 @@ def merge_old_and_new_revs_from_renamed_dir(sbox): # because /A_MOVED has renames in its history between the boundaries # of the requested merge range. expected_output = wc.State(A_COPY_path, { - 'mu' : Item(status='G '), # mu gets touched twice + 'mu' : Item(status='G ', prev_status='U '), # mu gets touched twice }) expected_mergeinfo_output = wc.State(A_COPY_path, { - '' : Item(status=' G'), + '' : Item(status=' G', prev_status=' U'), }) expected_elision_output = wc.State(A_COPY_path, { }) @@ -8162,13 +8189,13 @@ def merge_with_child_having_different_rev_ranges_to_merge(sbox): # Create a WC sbox.build() wc_dir = sbox.wc_dir - A_path = os.path.join(wc_dir, 'A') - mu_path = os.path.join(wc_dir, 'A', 'mu') + A_path = sbox.ospath('A') + mu_path = sbox.ospath('A/mu') A_url = sbox.repo_url + '/A' A_mu_url = sbox.repo_url + '/A/mu' A_COPY_url = sbox.repo_url + '/A_COPY' - A_COPY_path = os.path.join(wc_dir, 'A_COPY') - A_COPY_mu_path = os.path.join(wc_dir, 'A_COPY', 'mu') + A_COPY_path = sbox.ospath('A_COPY') + A_COPY_mu_path = sbox.ospath('A_COPY/mu') thirty_line_dummy_text = 'line1\n' for i in range(2, 31): thirty_line_dummy_text += 'line' + str(i) + '\n' @@ -8240,7 +8267,7 @@ def merge_with_child_having_different_rev_ranges_to_merge(sbox): expected_skip = wc.State(A_COPY_path, {}) expected_output = wc.State(A_COPY_path, { '' : Item(status=' U'), - 'mu' : Item(status='G '), + 'mu' : Item(status='G ', prev_status='G '), # Updated twice }) expected_mergeinfo_output = wc.State(A_COPY_path, { '' : Item(status=' U'), @@ -8373,7 +8400,7 @@ def merge_with_child_having_different_rev_ranges_to_merge(sbox): svntest.main.file_write(A_COPY_mu_path, tweaked_17th_line_1) expected_output = wc.State(A_COPY_path, { '' : Item(status=' G'), - 'mu' : Item(status='G '), + 'mu' : Item(status='G ', prev_status='G '), }) expected_mergeinfo_output = wc.State(A_COPY_path, { '' : Item(status=' G'), @@ -8412,9 +8439,9 @@ def merge_old_and_new_revs_from_renamed_file(sbox): mu_url = sbox.repo_url + '/A/mu' mu_MOVED_url = sbox.repo_url + '/A/mu_MOVED' mu_COPY_url = sbox.repo_url + '/A/mu_COPY' - mu_COPY_path = os.path.join(wc_dir, 'A', 'mu_COPY') - mu_path = os.path.join(wc_dir, 'A', 'mu') - mu_MOVED_path = os.path.join(wc_dir, 'A', 'mu_MOVED') + mu_COPY_path = sbox.ospath('A/mu_COPY') + mu_path = sbox.ospath('A/mu') + mu_MOVED_path = sbox.ospath('A/mu_MOVED') # Copy mu to mu_COPY svntest.actions.run_and_verify_svn(None, ['\n', 'Committed revision 2.\n'], @@ -8480,9 +8507,9 @@ def merge_with_auto_rev_range_detection(sbox): # Some paths we'll care about A_url = sbox.repo_url + '/A' A_COPY_url = sbox.repo_url + '/A_COPY' - B1_path = os.path.join(wc_dir, 'A', 'B1') - B1_mu_path = os.path.join(wc_dir, 'A', 'B1', 'mu') - A_COPY_path = os.path.join(wc_dir, 'A_COPY') + B1_path = sbox.ospath('A/B1') + B1_mu_path = sbox.ospath('A/B1/mu') + A_COPY_path = sbox.ospath('A_COPY') # Create B1 inside A svntest.actions.run_and_verify_svn(None, ["A " + B1_path + "\n"], @@ -8606,15 +8633,15 @@ def cherry_picking(sbox): wc_disk, wc_status = set_up_branch(sbox) # Some paths we'll care about - H_path = os.path.join(wc_dir, "A", "D", "H") - G_path = os.path.join(wc_dir, "A", "D", "G") - A_COPY_path = os.path.join(wc_dir, "A_COPY") - D_COPY_path = os.path.join(wc_dir, "A_COPY", "D") - G_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "G") - H_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "H") - rho_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "G", "rho") - omega_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "H", "omega") - psi_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "H", "psi") + H_path = sbox.ospath('A/D/H') + G_path = sbox.ospath('A/D/G') + A_COPY_path = sbox.ospath('A_COPY') + D_COPY_path = sbox.ospath('A_COPY/D') + G_COPY_path = sbox.ospath('A_COPY/D/G') + H_COPY_path = sbox.ospath('A_COPY/D/H') + rho_COPY_path = sbox.ospath('A_COPY/D/G/rho') + omega_COPY_path = sbox.ospath('A_COPY/D/H/omega') + psi_COPY_path = sbox.ospath('A_COPY/D/H/psi') # Update working copy expected_output = svntest.wc.State(wc_dir, {}) @@ -8793,11 +8820,11 @@ def propchange_of_subdir_raises_conflict(sbox): # Some paths we'll care about B_url = sbox.repo_url + '/A/B' - E_path = os.path.join(wc_dir, 'A', 'B', 'E') - lambda_path = os.path.join(wc_dir, 'A', 'B', 'lambda') - A_COPY_B_path = os.path.join(wc_dir, 'A_COPY', 'B') - A_COPY_B_E_path = os.path.join(wc_dir, 'A_COPY', 'B', 'E') - A_COPY_lambda_path = os.path.join(wc_dir, 'A_COPY', 'B', 'E', 'lambda') + E_path = sbox.ospath('A/B/E') + lambda_path = sbox.ospath('A/B/lambda') + A_COPY_B_path = sbox.ospath('A_COPY/B') + A_COPY_B_E_path = sbox.ospath('A_COPY/B/E') + A_COPY_lambda_path = sbox.ospath('A_COPY/B/E/lambda') # Set a property on A/B/E and Make a modification to A/B/lambda svntest.main.run_svn(None, 'propset', 'x', 'x', E_path) @@ -8907,9 +8934,9 @@ def reverse_merge_prop_add_on_child(sbox): wc_disk, wc_status = set_up_branch(sbox, True, 1) # Some paths we'll care about - G_path = os.path.join(wc_dir, "A", "D", "G") - D_COPY_path = os.path.join(wc_dir, "A_COPY", "D") - G_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "G") + G_path = sbox.ospath('A/D/G') + D_COPY_path = sbox.ospath('A_COPY/D') + G_COPY_path = sbox.ospath('A_COPY/D/G') # Make some prop changes to some dirs. svntest.actions.run_and_verify_svn(None, @@ -9019,9 +9046,9 @@ def merge_target_with_non_inheritable_mergeinfo(sbox): # Some paths we'll care about B_url = sbox.repo_url + '/A/B' - lambda_path = os.path.join(wc_dir, 'A', 'B', 'lambda') - newfile_path = os.path.join(wc_dir, 'A', 'B', 'E', 'newfile') - A_COPY_B_path = os.path.join(wc_dir, 'A_COPY', 'B') + lambda_path = sbox.ospath('A/B/lambda') + newfile_path = sbox.ospath('A/B/E/newfile') + A_COPY_B_path = sbox.ospath('A_COPY/B') # Make a modifications to A/B/lambda and add A/B/E/newfile svntest.main.file_write(lambda_path, "This is the file 'lambda' modified.\n") @@ -9131,7 +9158,7 @@ def self_reverse_merge(sbox): wc_dir = sbox.wc_dir # Make changes to the working copy - mu_path = os.path.join(wc_dir, 'A', 'mu') + mu_path = sbox.ospath('A/mu') svntest.main.file_append(mu_path, 'appended mu text') # Created expected output tree for 'svn ci' @@ -9211,9 +9238,9 @@ def ignore_ancestry_and_mergeinfo(sbox): # Some paths we'll care about A_B_url = sbox.repo_url + '/A/B' - A_COPY_B_path = os.path.join(wc_dir, 'A_COPY', 'B') - lambda_path = os.path.join(wc_dir, 'A', 'B', 'lambda') - A_COPY_lambda_path = os.path.join(wc_dir, 'A_COPY', 'B', 'lambda') + A_COPY_B_path = sbox.ospath('A_COPY/B') + lambda_path = sbox.ospath('A/B/lambda') + A_COPY_lambda_path = sbox.ospath('A_COPY/B/lambda') # Make modifications to A/B/lambda svntest.main.file_write(lambda_path, "This is the file 'lambda' modified.\n") @@ -9308,6 +9335,7 @@ def ignore_ancestry_and_mergeinfo(sbox): #---------------------------------------------------------------------- @SkipUnless(server_has_mergeinfo) +@Issue(3032) def merge_from_renamed_branch_fails_while_avoiding_repeat_merge(sbox): "merge from renamed branch" #Copy A/C to A/COPY_C results in r2. @@ -9326,9 +9354,9 @@ def merge_from_renamed_branch_fails_while_avoiding_repeat_merge(sbox): A_C_url = sbox.repo_url + '/A/C' A_COPY_C_url = sbox.repo_url + '/A/COPY_C' A_RENAMED_C_url = sbox.repo_url + '/A/RENAMED_C' - A_C_path = os.path.join(wc_dir, 'A', 'C') - A_RENAMED_C_path = os.path.join(wc_dir, 'A', 'RENAMED_C') - A_RENAMED_C_file1_path = os.path.join(wc_dir, 'A', 'RENAMED_C', 'file1') + A_C_path = sbox.ospath('A/C') + A_RENAMED_C_path = sbox.ospath('A/RENAMED_C') + A_RENAMED_C_file1_path = sbox.ospath('A/RENAMED_C/file1') svntest.main.run_svn(None, 'cp', A_C_url, A_COPY_C_url, '-m', 'copy...') svntest.main.run_svn(None, 'mv', A_COPY_C_url, A_RENAMED_C_url, '-m', @@ -9420,8 +9448,8 @@ def merge_source_normalization_and_subtree_merges(sbox): wc_dir = sbox.wc_dir # Some paths we'll care about - D_COPY_path = os.path.join(wc_dir, "A_COPY", "D") - G_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "G") + D_COPY_path = sbox.ospath('A_COPY/D') + G_COPY_path = sbox.ospath('A_COPY/D/G') # Use our helper to copy 'A' to 'A_COPY' then make some changes under 'A' wc_disk, wc_status = set_up_branch(sbox) @@ -9463,7 +9491,7 @@ def merge_source_normalization_and_subtree_merges(sbox): 'update', wc_dir) # r8 - Make a text mod to 'A_MOVED/D/G/tau' - svntest.main.file_write(os.path.join(wc_dir, "A_MOVED", "D", "G", "tau"), + svntest.main.file_write(sbox.ospath('A_MOVED/D/G/tau'), "New content") expected_output = wc.State(wc_dir, {'A_MOVED/D/G/tau' : Item(verb='Sending')}) @@ -9575,12 +9603,12 @@ def new_subtrees_should_not_break_merge(sbox): wc_disk, wc_status = set_up_branch(sbox) # Some paths we'll care about - A_COPY_path = os.path.join(wc_dir, "A_COPY") - D_COPY_path = os.path.join(wc_dir, "A_COPY", "D") - nu_path = os.path.join(wc_dir, "A", "D", "H", "nu") - nu_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "H", "nu") - rho_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "G", "rho") - H_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "H") + A_COPY_path = sbox.ospath('A_COPY') + D_COPY_path = sbox.ospath('A_COPY/D') + nu_path = sbox.ospath('A/D/H/nu') + nu_COPY_path = sbox.ospath('A_COPY/D/H/nu') + rho_COPY_path = sbox.ospath('A_COPY/D/G/rho') + H_COPY_path = sbox.ospath('A_COPY/D/H') # Create 'A/D/H/nu', commit it as r7, make a text mod to it in r8. svntest.main.file_write(nu_path, "This is the file 'nu'.\n") @@ -10007,12 +10035,12 @@ def dont_add_mergeinfo_from_own_history(sbox): wc_disk, wc_status = set_up_branch(sbox) # Some paths we'll care about - A_path = os.path.join(wc_dir, "A") - A_MOVED_path = os.path.join(wc_dir, "A_MOVED") - mu_path = os.path.join(wc_dir, "A", "mu") - mu_MOVED_path = os.path.join(wc_dir, "A_MOVED", "mu") - A_COPY_path = os.path.join(wc_dir, "A_COPY") - mu_COPY_path = os.path.join(wc_dir, "A_COPY", "mu") + A_path = sbox.ospath('A') + A_MOVED_path = sbox.ospath('A_MOVED') + mu_path = sbox.ospath('A/mu') + mu_MOVED_path = sbox.ospath('A_MOVED/mu') + A_COPY_path = sbox.ospath('A_COPY') + mu_COPY_path = sbox.ospath('A_COPY/mu') # Merge r3 from 'A' to 'A_COPY', make a text mod to 'A_COPY/mu' and # commit both as r7. This results in mergeinfo of '/A:3' on 'A_COPY'. @@ -10315,50 +10343,50 @@ def dont_add_mergeinfo_from_own_history(sbox): # # Create the new 'A' by exporting the old 'A@1'. expected_output = svntest.verify.UnorderedOutput( - ["A " + os.path.join(wc_dir, "A") + "\n", - "A " + os.path.join(wc_dir, "A", "B") + "\n", - "A " + os.path.join(wc_dir, "A", "B", "lambda") + "\n", - "A " + os.path.join(wc_dir, "A", "B", "E") + "\n", - "A " + os.path.join(wc_dir, "A", "B", "E", "alpha") + "\n", - "A " + os.path.join(wc_dir, "A", "B", "E", "beta") + "\n", - "A " + os.path.join(wc_dir, "A", "B", "F") + "\n", - "A " + os.path.join(wc_dir, "A", "mu") + "\n", - "A " + os.path.join(wc_dir, "A", "C") + "\n", - "A " + os.path.join(wc_dir, "A", "D") + "\n", - "A " + os.path.join(wc_dir, "A", "D", "gamma") + "\n", - "A " + os.path.join(wc_dir, "A", "D", "G") + "\n", - "A " + os.path.join(wc_dir, "A", "D", "G", "pi") + "\n", - "A " + os.path.join(wc_dir, "A", "D", "G", "rho") + "\n", - "A " + os.path.join(wc_dir, "A", "D", "G", "tau") + "\n", - "A " + os.path.join(wc_dir, "A", "D", "H") + "\n", - "A " + os.path.join(wc_dir, "A", "D", "H", "chi") + "\n", - "A " + os.path.join(wc_dir, "A", "D", "H", "omega") + "\n", - "A " + os.path.join(wc_dir, "A", "D", "H", "psi") + "\n", + ["A " + sbox.ospath('A') + "\n", + "A " + sbox.ospath('A/B') + "\n", + "A " + sbox.ospath('A/B/lambda') + "\n", + "A " + sbox.ospath('A/B/E') + "\n", + "A " + sbox.ospath('A/B/E/alpha') + "\n", + "A " + sbox.ospath('A/B/E/beta') + "\n", + "A " + sbox.ospath('A/B/F') + "\n", + "A " + sbox.ospath('A/mu') + "\n", + "A " + sbox.ospath('A/C') + "\n", + "A " + sbox.ospath('A/D') + "\n", + "A " + sbox.ospath('A/D/gamma') + "\n", + "A " + sbox.ospath('A/D/G') + "\n", + "A " + sbox.ospath('A/D/G/pi') + "\n", + "A " + sbox.ospath('A/D/G/rho') + "\n", + "A " + sbox.ospath('A/D/G/tau') + "\n", + "A " + sbox.ospath('A/D/H') + "\n", + "A " + sbox.ospath('A/D/H/chi') + "\n", + "A " + sbox.ospath('A/D/H/omega') + "\n", + "A " + sbox.ospath('A/D/H/psi') + "\n", "Exported revision 1.\n",] ) svntest.actions.run_and_verify_svn(None, expected_output, [], 'export', sbox.repo_url + '/A@1', A_path) expected_output = svntest.verify.UnorderedOutput( - ["A " + os.path.join(wc_dir, "A") + "\n", - "A " + os.path.join(wc_dir, "A", "B") + "\n", - "A " + os.path.join(wc_dir, "A", "B", "lambda") + "\n", - "A " + os.path.join(wc_dir, "A", "B", "E") + "\n", - "A " + os.path.join(wc_dir, "A", "B", "E", "alpha") + "\n", - "A " + os.path.join(wc_dir, "A", "B", "E", "beta") + "\n", - "A " + os.path.join(wc_dir, "A", "B", "F") + "\n", - "A " + os.path.join(wc_dir, "A", "mu") + "\n", - "A " + os.path.join(wc_dir, "A", "C") + "\n", - "A " + os.path.join(wc_dir, "A", "D") + "\n", - "A " + os.path.join(wc_dir, "A", "D", "gamma") + "\n", - "A " + os.path.join(wc_dir, "A", "D", "G") + "\n", - "A " + os.path.join(wc_dir, "A", "D", "G", "pi") + "\n", - "A " + os.path.join(wc_dir, "A", "D", "G", "rho") + "\n", - "A " + os.path.join(wc_dir, "A", "D", "G", "tau") + "\n", - "A " + os.path.join(wc_dir, "A", "D", "H") + "\n", - "A " + os.path.join(wc_dir, "A", "D", "H", "chi") + "\n", - "A " + os.path.join(wc_dir, "A", "D", "H", "omega") + "\n", - "A " + os.path.join(wc_dir, "A", "D", "H", "psi") + "\n",] + ["A " + sbox.ospath('A') + "\n", + "A " + sbox.ospath('A/B') + "\n", + "A " + sbox.ospath('A/B/lambda') + "\n", + "A " + sbox.ospath('A/B/E') + "\n", + "A " + sbox.ospath('A/B/E/alpha') + "\n", + "A " + sbox.ospath('A/B/E/beta') + "\n", + "A " + sbox.ospath('A/B/F') + "\n", + "A " + sbox.ospath('A/mu') + "\n", + "A " + sbox.ospath('A/C') + "\n", + "A " + sbox.ospath('A/D') + "\n", + "A " + sbox.ospath('A/D/gamma') + "\n", + "A " + sbox.ospath('A/D/G') + "\n", + "A " + sbox.ospath('A/D/G/pi') + "\n", + "A " + sbox.ospath('A/D/G/rho') + "\n", + "A " + sbox.ospath('A/D/G/tau') + "\n", + "A " + sbox.ospath('A/D/H') + "\n", + "A " + sbox.ospath('A/D/H/chi') + "\n", + "A " + sbox.ospath('A/D/H/omega') + "\n", + "A " + sbox.ospath('A/D/H/psi') + "\n",] ) svntest.actions.run_and_verify_svn(None, expected_output, [], 'add', A_path) @@ -10485,12 +10513,12 @@ def merge_range_predates_history(sbox): sbox.build() wc_dir = sbox.wc_dir - iota_path = os.path.join(wc_dir, "iota") - trunk_file_path = os.path.join(wc_dir, "trunk", "file") + iota_path = sbox.ospath('iota') + trunk_file_path = sbox.ospath('trunk/file') trunk_url = sbox.repo_url + "/trunk" branches_url = sbox.repo_url + "/branches" - branch_path = os.path.join(wc_dir, "branches", "branch") - branch_file_path = os.path.join(wc_dir, "branches", "branch", "file") + branch_path = sbox.ospath('branches/branch') + branch_file_path = sbox.ospath('branches/branch/file') branch_url = sbox.repo_url + "/branches/branch" # Tweak a file and commit. (r2) @@ -10536,15 +10564,15 @@ def foreign_repos(sbox): wc_dir2 = sbox2.wc_dir # Convenience variables for working copy paths. - Z_path = os.path.join(wc_dir, 'A', 'D', 'G', 'Z') - B_path = os.path.join(wc_dir, 'A', 'B') - Q_path = os.path.join(wc_dir, 'Q') - H_path = os.path.join(wc_dir, 'A', 'D', 'H') - iota_path = os.path.join(wc_dir, 'iota') - beta_path = os.path.join(wc_dir, 'A', 'B', 'E', 'beta') - alpha_path = os.path.join(wc_dir, 'A', 'B', 'E', 'alpha') - zeta_path = os.path.join(wc_dir, 'A', 'D', 'G', 'Z', 'zeta') - fred_path = os.path.join(wc_dir, 'A', 'C', 'fred') + Z_path = sbox.ospath('A/D/G/Z') + B_path = sbox.ospath('A/B') + Q_path = sbox.ospath('Q') + H_path = sbox.ospath('A/D/H') + iota_path = sbox.ospath('iota') + beta_path = sbox.ospath('A/B/E/beta') + alpha_path = sbox.ospath('A/B/E/alpha') + zeta_path = sbox.ospath('A/D/G/Z/zeta') + fred_path = sbox.ospath('A/C/fred') # Add new directories, with and without properties. svntest.main.run_svn(None, 'mkdir', Q_path, Z_path) @@ -10649,8 +10677,8 @@ def foreign_repos_uuid(sbox): wc2_uuid = svntest.actions.get_wc_uuid(wc_dir2) # Convenience variables for working copy paths. - zeta_path = os.path.join(wc_dir, 'A', 'D', 'G', 'zeta') - Z_path = os.path.join(wc_dir, 'A', 'Z') + zeta_path = sbox.ospath('A/D/G/zeta') + Z_path = sbox.ospath('A/Z') # Add new file and directory. zeta_contents = "This is the file 'zeta'.\n" @@ -10722,13 +10750,13 @@ def foreign_repos_2_url(sbox): wc_dir2 = sbox2.wc_dir # Convenience variables for working copy paths. - Z_path = os.path.join(wc_dir, 'A', 'D', 'G', 'Z') - Q_path = os.path.join(wc_dir, 'A', 'Q') - H_path = os.path.join(wc_dir, 'A', 'D', 'H') - beta_path = os.path.join(wc_dir, 'A', 'B', 'E', 'beta') - alpha_path = os.path.join(wc_dir, 'A', 'B', 'E', 'alpha') - zeta_path = os.path.join(wc_dir, 'A', 'D', 'G', 'Z', 'zeta') - fred_path = os.path.join(wc_dir, 'A', 'C', 'fred') + Z_path = sbox.ospath('A/D/G/Z') + Q_path = sbox.ospath('A/Q') + H_path = sbox.ospath('A/D/H') + beta_path = sbox.ospath('A/B/E/beta') + alpha_path = sbox.ospath('A/B/E/alpha') + zeta_path = sbox.ospath('A/D/G/Z/zeta') + fred_path = sbox.ospath('A/C/fred') # First, "tag" the current state of the repository. svntest.main.run_svn(None, 'copy', sbox.repo_url + '/A', @@ -10824,7 +10852,7 @@ def merge_added_subtree(sbox): # svn cp A A_COPY A_url = url + "/A" A_COPY_url = url + "/A_COPY" - A_path = os.path.join(wc_dir, "A") + A_path = sbox.ospath('A') svntest.actions.run_and_verify_svn("",["\n", "Committed revision 2.\n"], [], "cp", "-m", "", A_url, A_COPY_url) @@ -10880,9 +10908,7 @@ def merge_added_subtree(sbox): svntest.actions.run_and_verify_svn("", None, [], "cp", A_COPY_url + '/D2', os.path.join(A_path, "D2")) - actual_tree = svntest.tree.build_tree_from_wc(A_path, 0) - svntest.tree.compare_trees("expected disk", - actual_tree, expected_disk.old_tree()) + svntest.actions.verify_disk(A_path, expected_disk) svntest.actions.run_and_verify_status(A_path, expected_status) # Remove the copy artifacts @@ -10916,7 +10942,7 @@ def merge_unknown_url(sbox): wc_dir = sbox.wc_dir # remove a path from the repo and commit. - iota_path = os.path.join(wc_dir, 'iota') + iota_path = sbox.ospath('iota') svntest.actions.run_and_verify_svn(None, None, [], 'rm', iota_path) svntest.actions.run_and_verify_svn("", None, [], "ci", wc_dir, "-m", "log message") @@ -10937,7 +10963,7 @@ def reverse_merge_away_all_mergeinfo(sbox): wc_disk, wc_status = set_up_branch(sbox) # Some paths we'll care about - A_COPY_H_path = os.path.join(wc_dir, "A_COPY", "D", "H") + A_COPY_H_path = sbox.ospath('A_COPY/D/H') # Merge r4:8 from A/D/H into A_COPY/D/H. expected_output = wc.State(A_COPY_H_path, { @@ -11027,19 +11053,31 @@ def reverse_merge_away_all_mergeinfo(sbox): # merge'. Specifically see # http://subversion.tigris.org/issues/show_bug.cgi?id=3067#desc5 @SkipUnless(server_has_mergeinfo) -@Issues(3138,3067) +@Issues(3138,3067,4217) def dont_merge_revs_into_subtree_that_predate_it(sbox): "dont merge revs into a subtree that predate it" + # +-> merge -c7 A/D/H/nu@7 H_COPY/nu + # | +-> merge -c2 A/D/H H_COPY + # | | +-> merge A/D/H H_COPY + # | | | + # A/D/H A---------------------- + # +-psi +-M-------------M------ + # +-nu A-D C---M-D + # H_COPY C---------G-G + # +-psi +---------+-. + # +-nu +-------G---. + # 1 2 3 4 5 6 7 8 9 w w w + # Create our good 'ole greek tree. sbox.build() wc_dir = sbox.wc_dir # Some paths we'll care about - psi_path = os.path.join(wc_dir, "A", "D", "H", "psi") - nu_path = os.path.join(wc_dir, "A", "D", "H", "nu") - H_COPY_path = os.path.join(wc_dir, "H_COPY") - nu_COPY_path = os.path.join(wc_dir, "H_COPY", "nu") + psi_path = sbox.ospath('A/D/H/psi') + nu_path = sbox.ospath('A/D/H/nu') + H_COPY_path = sbox.ospath('H_COPY') + nu_COPY_path = sbox.ospath('H_COPY/nu') expected_status = svntest.actions.get_virginal_state(wc_dir, 1) expected_disk = svntest.main.greek_state.copy() @@ -11145,13 +11183,12 @@ def dont_merge_revs_into_subtree_that_predate_it(sbox): H_COPY_path) # H_COPY needs r6-9 applied while H_COPY/nu needs only 6,8-9. - # This means r6 will be done as a separate editor drive targeted - # on H_COPY. But r6 was only the copy of A/D/H to H_COPY and - # so is a no-op and there will no notification for r6. svntest.actions.run_and_verify_svn( None, expected_merge_output( - [[6,9]], ['U ' + os.path.join(H_COPY_path, "psi") + '\n', + [[7,9], # Merge notification + [6,9]], # Mergeinfo notification + ['U ' + os.path.join(H_COPY_path, "psi") + '\n', 'D ' + os.path.join(H_COPY_path, "nu") + '\n', ' U ' + H_COPY_path + '\n',]), [], 'merge', sbox.repo_url + '/A/D/H', H_COPY_path, '--force') @@ -11182,11 +11219,11 @@ def set_up_renamed_subtree(sbox): wc_dir = sbox.wc_dir # Some paths we'll care about - psi_path = os.path.join(wc_dir, "A", "D", "H", "psi") - omega_path = os.path.join(wc_dir, "A", "D", "H", "omega") - psi_moved_path = os.path.join(wc_dir, "A", "D", "H", "psi_moved") - psi_COPY_moved_path = os.path.join(wc_dir, "H_COPY", "psi_moved") - H_COPY_path = os.path.join(wc_dir, "H_COPY") + psi_path = sbox.ospath('A/D/H/psi') + omega_path = sbox.ospath('A/D/H/omega') + psi_moved_path = sbox.ospath('A/D/H/psi_moved') + psi_COPY_moved_path = sbox.ospath('H_COPY/psi_moved') + H_COPY_path = sbox.ospath('H_COPY') expected_status = svntest.actions.get_virginal_state(wc_dir, 1) expected_disk = svntest.main.greek_state.copy() @@ -11275,7 +11312,7 @@ def merge_chokes_on_renamed_subtrees(sbox): wc_dir, expected_disk, expected_status = set_up_renamed_subtree(sbox) # Some paths we'll care about - psi_COPY_moved_path = os.path.join(wc_dir, "H_COPY", "psi_moved") + psi_COPY_moved_path = sbox.ospath('H_COPY/psi_moved') # Cherry harvest all available revsions from 'A/D/H/psi_moved' to @@ -11307,13 +11344,13 @@ def dont_explicitly_record_implicit_mergeinfo(sbox): sbox.build() wc_dir = sbox.wc_dir - A_path = os.path.join(sbox.wc_dir, 'A') - A_copy_path = os.path.join(sbox.wc_dir, 'A_copy') - A_copy2_path = os.path.join(sbox.wc_dir, 'A_copy2') - A_copy_mu_path = os.path.join(sbox.wc_dir, 'A_copy', 'mu') - A_copy2_mu_path = os.path.join(sbox.wc_dir, 'A_copy2', 'mu') - nu_path = os.path.join(sbox.wc_dir, 'A', 'D', 'H', 'nu') - nu_copy_path = os.path.join(sbox.wc_dir, 'A_copy', 'D', 'H', 'nu') + A_path = sbox.ospath('A') + A_copy_path = sbox.ospath('A_copy') + A_copy2_path = sbox.ospath('A_copy2') + A_copy_mu_path = sbox.ospath('A_copy/mu') + A_copy2_mu_path = sbox.ospath('A_copy2/mu') + nu_path = sbox.ospath('A/D/H/nu') + nu_copy_path = sbox.ospath('A_copy/D/H/nu') def _commit_and_update(rev, action): svntest.actions.run_and_verify_svn(None, None, [], @@ -11658,8 +11695,8 @@ def merge_broken_link(sbox): # Create our good 'ole greek tree. sbox.build() wc_dir = sbox.wc_dir - src_path = os.path.join(wc_dir, 'A', 'B', 'E') - copy_path = os.path.join(wc_dir, 'A', 'B', 'E_COPY') + src_path = sbox.ospath('A/B/E') + copy_path = sbox.ospath('A/B/E_COPY') link_path = os.path.join(src_path, 'beta_link') os.symlink('beta_broken', link_path) @@ -11693,16 +11730,16 @@ def subtree_merges_dont_intersect_with_targets(sbox): wc_disk, wc_status = set_up_branch(sbox, False, 2) # Some paths we'll care about. - A_COPY_path = os.path.join(wc_dir, "A_COPY") - A_COPY_2_path = os.path.join(wc_dir, "A_COPY_2") - H_COPY_2_path = os.path.join(wc_dir, "A_COPY_2", "D", "H") - gamma_path = os.path.join(wc_dir, "A", "D", "gamma") - psi_path = os.path.join(wc_dir, "A", "D", "H", "psi") - psi_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "H", "psi") - gamma_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "gamma") - psi_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "H", "psi") - psi_COPY_2_path = os.path.join(wc_dir, "A_COPY_2", "D", "H", "psi") - rho_COPY_2_path = os.path.join(wc_dir, "A_COPY_2", "D", "G", "rho") + A_COPY_path = sbox.ospath('A_COPY') + A_COPY_2_path = sbox.ospath('A_COPY_2') + H_COPY_2_path = sbox.ospath('A_COPY_2/D/H') + gamma_path = sbox.ospath('A/D/gamma') + psi_path = sbox.ospath('A/D/H/psi') + psi_COPY_path = sbox.ospath('A_COPY/D/H/psi') + gamma_COPY_path = sbox.ospath('A_COPY/D/gamma') + psi_COPY_path = sbox.ospath('A_COPY/D/H/psi') + psi_COPY_2_path = sbox.ospath('A_COPY_2/D/H/psi') + rho_COPY_2_path = sbox.ospath('A_COPY_2/D/G/rho') # Make a tweak to A/D/gamma and A/D/H/psi in r8. svntest.main.file_write(gamma_path, "New content") @@ -12009,11 +12046,11 @@ def subtree_source_missing_in_requested_range(sbox): wc_disk, wc_status = set_up_branch(sbox, False, 1) # Some paths we'll care about. - psi_path = os.path.join(wc_dir, "A", "D", "H", "psi") - omega_path = os.path.join(wc_dir, "A", "D", "H", "omega") - A_COPY_path = os.path.join(wc_dir, "A_COPY") - psi_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "H", "psi") - omega_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "H", "omega") + psi_path = sbox.ospath('A/D/H/psi') + omega_path = sbox.ospath('A/D/H/omega') + A_COPY_path = sbox.ospath('A_COPY') + psi_COPY_path = sbox.ospath('A_COPY/D/H/psi') + omega_COPY_path = sbox.ospath('A_COPY/D/H/omega') # r7 Delete A/D/H/psi. svntest.actions.run_and_verify_svn(None, None, [], @@ -12105,7 +12142,7 @@ def subtree_source_missing_in_requested_range(sbox): expected_output = wc.State(A_COPY_path, { 'D/H/omega' : Item(status='U '), 'D/H/psi' : Item(status='U '), - 'D/H/omega' : Item(status='G '), + 'D/H/omega' : Item(status='G ', prev_status='G '), }) expected_mergeinfo_output = wc.State(A_COPY_path, { '' : Item(status=' U'), @@ -12330,7 +12367,7 @@ def subtrees_with_empty_mergeinfo(sbox): wc_dir, expected_disk, expected_status = set_up_renamed_subtree(sbox) # Some paths we'll care about - H_COPY_path = os.path.join(wc_dir, "H_COPY") + H_COPY_path = sbox.ospath('H_COPY') # Cherry harvest all available revsions from 'A/D/H' to 'H_COPY'. # @@ -12383,10 +12420,10 @@ def commit_to_subtree_added_by_merge(sbox): wc_dir = sbox.wc_dir # Some paths we'll care about - N_path = os.path.join(wc_dir, "A", "D", "H", "N") - nu_path = os.path.join(wc_dir, "A", "D", "H", "N", "nu") - nu_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "H", "N", "nu") - H_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "H") + N_path = sbox.ospath('A/D/H/N') + nu_path = sbox.ospath('A/D/H/N/nu') + nu_COPY_path = sbox.ospath('A_COPY/D/H/N/nu') + H_COPY_path = sbox.ospath('A_COPY/D/H') # Copy 'A' to 'A_COPY' in r2. wc_disk, wc_status = set_up_branch(sbox, True) @@ -12508,24 +12545,52 @@ def svn_copy(s_rev, path1, path2): svntest.actions.run_and_verify_svn(None, None, [], 'copy', '--parents', '-r', s_rev, path1, path2) -def svn_merge(rev_spec, source, target, exp_out=None, *args): - """Merge a single change from path 'source' to path 'target'. - SRC_CHANGE_NUM is either a number (to cherry-pick that specific change) - or a command-line option revision range string such as '-r10:20'. - *ARGS are additional arguments passed to svn merge.""" +def svn_merge(rev_range, source, target, lines=None, elides=[], + text_conflicts=0, prop_conflicts=0, tree_conflicts=0, + text_resolved=0, prop_resolved=0, tree_resolved=0, + args=[]): + """Merge a single change from path SOURCE to path TARGET and verify the + output and that there is no error. (The changes made are not verified.) + + REV_RANGE is either a number (to cherry-pick that specific change) or a + two-element list [X,Y] to pick the revision range '-r(X-1):Y'. + + LINES is a list of regular expressions to match other lines of output; if + LINES is 'None' then match all normal (non-conflicting) merges. + + ELIDES is a list of paths on which mergeinfo elision should be reported. + + TEXT_CONFLICTS, PROP_CONFLICTS and TREE_CONFLICTS specify the number of + each kind of conflict to expect. + + ARGS are additional arguments passed to svn merge. + """ + source = local_path(source) target = local_path(target) - if isinstance(rev_spec, int): - rev_spec = '-c' + str(rev_spec) - if exp_out is None: - target_re = re.escape(target) - exp_1 = "--- Merging r.* into '" + target_re + ".*':" - exp_2 = "(A |D |[UG] | [UG]|[UG][UG]) " + target_re + ".*" - exp_3 = "--- Recording mergeinfo for merge of r.* into '" + \ - target_re + ".*':" - exp_out = svntest.verify.RegexOutput(exp_1 + "|" + exp_2 + "|" + exp_3) + elides = [local_path(p) for p in elides] + if isinstance(rev_range, int): + mi_rev_range = [rev_range] + rev_arg = '-c' + str(rev_range) + else: + mi_rev_range = rev_range + rev_arg = '-r' + str(rev_range[0] - 1) + ':' + str(rev_range[1]) + if lines is None: + lines = ["(A |D |[UG] | [UG]|[UG][UG]) " + target + ".*\n"] + else: + # Expect mergeinfo on the target; caller must supply matches for any + # subtree mergeinfo paths. + lines.append(" [UG] " + target + "\n") + exp_out = expected_merge_output([mi_rev_range], lines, target=target, + elides=elides, + text_conflicts=text_conflicts, + prop_conflicts=prop_conflicts, + tree_conflicts=tree_conflicts, + text_resolved=text_resolved, + prop_resolved=prop_resolved, + tree_resolved=tree_resolved) svntest.actions.run_and_verify_svn(None, exp_out, [], - 'merge', rev_spec, source, target, *args) + 'merge', rev_arg, source, target, *args) #---------------------------------------------------------------------- # Tests for merging the deletion of a node, where the node to be deleted @@ -12557,7 +12622,8 @@ def del_identical_file(sbox): svn_copy(s_rev_mod, source, target) sbox.simple_commit(target) # Should be deleted quietly. - svn_merge(s_rev_del, source, target, '--- Merging|D |--- Recording| U') + svn_merge(s_rev_del, source, target, + ['D %s\n' % local_path('A/D/G2/tau')]) # Make a differing copy, locally modify it so it's the same, # and merge a deletion to it. @@ -12566,7 +12632,8 @@ def del_identical_file(sbox): sbox.simple_commit(target) svn_modfile(target+"/tau") # Should be deleted quietly. - svn_merge(s_rev_del, source, target, '--- Merging|D |--- Recording| U') + svn_merge(s_rev_del, source, target, + ['D %s\n' % local_path('A/D/G3/tau')]) os.chdir(saved_cwd) @@ -12593,10 +12660,11 @@ def del_sched_add_hist_file(sbox): svn_copy(s_rev_orig, source, target) sbox.simple_commit(target) s_rev = 3 - svn_merge(s_rev_add, source, target, '--- Merging|A |--- Recording| U') + svn_merge(s_rev_add, source, target, + ['A %s\n' % local_path('A/D/G2/file')]) # Should be deleted quietly. svn_merge(-s_rev_add, source, target, - '--- Reverse-merging|D |--- Recording| U| G|--- Eliding') + ['D %s\n' % local_path('A/D/G2/file')], elides=['A/D/G2']) os.chdir(saved_cwd) @@ -12617,9 +12685,9 @@ def subtree_merges_dont_cause_spurious_conflicts(sbox): wc_dir = sbox.wc_dir # Some paths we'll care about - rho_path = os.path.join(wc_dir, "A", "D", "G", "rho") - A_COPY_path = os.path.join(wc_dir, "A_COPY") - psi_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "H", "psi") + rho_path = sbox.ospath('A/D/G/rho') + A_COPY_path = sbox.ospath('A_COPY') + psi_COPY_path = sbox.ospath('A_COPY/D/H/psi') # Make a branch to merge to. wc_disk, wc_status = set_up_branch(sbox, False, 1) @@ -12837,13 +12905,13 @@ def merge_target_and_subtrees_need_nonintersecting_ranges(sbox): wc_dir = sbox.wc_dir # Some paths we'll care about - nu_path = os.path.join(wc_dir, "A", "D", "G", "nu") - A_COPY_path = os.path.join(wc_dir, "A_COPY") - nu_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "G", "nu") - omega_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "H", "omega") - beta_COPY_path = os.path.join(wc_dir, "A_COPY", "B", "E", "beta") - rho_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "G", "rho") - psi_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "H", "psi") + nu_path = sbox.ospath('A/D/G/nu') + A_COPY_path = sbox.ospath('A_COPY') + nu_COPY_path = sbox.ospath('A_COPY/D/G/nu') + omega_COPY_path = sbox.ospath('A_COPY/D/H/omega') + beta_COPY_path = sbox.ospath('A_COPY/B/E/beta') + rho_COPY_path = sbox.ospath('A_COPY/D/G/rho') + psi_COPY_path = sbox.ospath('A_COPY/D/H/psi') # Make a branch to merge to. wc_disk, wc_status = set_up_branch(sbox, False, 1) @@ -12991,6 +13059,8 @@ def merge_target_and_subtrees_need_nonintersecting_ranges(sbox): None, 1) #---------------------------------------------------------------------- +# Part of this test is a regression test for issue #3250 "Repeated merging +# of conflicting properties fails". @Issue(3250) def merge_two_edits_to_same_prop(sbox): "merge two successive edits to the same property" @@ -13037,12 +13107,21 @@ def merge_two_edits_to_same_prop(sbox): # some other target within the same merge requiring only a part of the # revision range. - # We test issue #3250 here - # Revert changes to target branch wc + # ==================================================================== + + # We test issue #3250 here: that is, test that we can make two successive + # conflicting changes to the same property on the same node (here a file; + # in #3250 it was on a dir). + # + # ### But we no longer support merging into a node that's already in + # conflict, and the 'rev3' merge here has been tweaked to resolve + # the conflict, so it no longer tests the original #3250 scenario. + # + # Revert changes to branch wc svntest.actions.run_and_verify_svn(None, None, [], 'revert', '--recursive', A_COPY_path) - # In the target branch, make two successive changes to the same property + # In the branch, make two successive changes to the same property sbox.simple_propset('p', 'new-val-3', 'A_COPY/mu') sbox.simple_commit('A_COPY/mu') rev3 = initial_rev + 3 @@ -13050,38 +13129,25 @@ def merge_two_edits_to_same_prop(sbox): sbox.simple_commit('A_COPY/mu') rev4 = initial_rev + 4 - # Merge the two changes together to source. - svn_merge('-r'+str(rev3-1)+':'+str(rev4), A_COPY_path, A_path, [ - "--- Merging r9 through r10 into '%s':\n" % A_path, + # Merge the two changes together to trunk. + svn_merge([rev3, rev4], A_COPY_path, A_path, [ " C %s\n" % mu_path, - "--- Recording mergeinfo for merge of r9 through r10 into '%s':\n" \ - % A_path, - " U A\n", - "Summary of conflicts:\n", - " Property conflicts: 1\n"], - '--allow-mixed-revisions') - - # Revert changes to source wc, to test next scenario of #3250 + ], prop_conflicts=1, args=['--allow-mixed-revisions']) + + # Revert changes to trunk wc, to test next scenario of #3250 svntest.actions.run_and_verify_svn(None, None, [], 'revert', '--recursive', A_path) - # Merge the first change, then the second, to source. + # Merge the first change, then the second, to trunk. svn_merge(rev3, A_COPY_path, A_path, [ - "--- Merging r9 into '%s':\n" % A_path, " C %s\n" % mu_path, - "--- Recording mergeinfo for merge of r9 into '%s':\n" % A_path, - " U A\n", - "Summary of conflicts:\n", - " Property conflicts: 1\n"], - '--allow-mixed-revisions') + "Resolved .* '%s'\n" % mu_path, + ], prop_resolved=1, + args=['--allow-mixed-revisions', + '--accept=working']) svn_merge(rev4, A_COPY_path, A_path, [ - "--- Merging r10 into '%s':\n" % A_path, " C %s\n" % mu_path, - "--- Recording mergeinfo for merge of r10 into '%s':\n" % A_path, - " G A\n", - "Summary of conflicts:\n", - " Property conflicts: 1\n"], - '--allow-mixed-revisions') + ], prop_conflicts=1, args=['--allow-mixed-revisions']) os.chdir(was_cwd) @@ -13130,8 +13196,8 @@ def merge_an_eol_unification_and_set_svn_eol_style(sbox): sbox.simple_commit('A_COPY') # Merge the two changes together to the target branch. - svn_merge('-r'+str(rev1)+':'+str(rev3), 'A', 'A_COPY', None, - '--allow-mixed-revisions') + svn_merge([rev2, rev3], 'A', 'A_COPY', + args=['--allow-mixed-revisions']) # That merge should succeed. # Surprise: setting svn:eol-style='LF' instead of 'native' doesn't fail. @@ -13163,10 +13229,10 @@ def merge_adds_mergeinfo_correctly(sbox): wc_disk, wc_status = set_up_branch(sbox, False, 2) # Some paths we'll care about - A_COPY_path = os.path.join(wc_dir, "A_COPY") - D_COPY_path = os.path.join(wc_dir, "A_COPY", "D") - A_COPY_2_path = os.path.join(wc_dir, "A_COPY_2") - D_COPY_2_path = os.path.join(wc_dir, "A_COPY_2", "D") + A_COPY_path = sbox.ospath('A_COPY') + D_COPY_path = sbox.ospath('A_COPY/D') + A_COPY_2_path = sbox.ospath('A_COPY_2') + D_COPY_2_path = sbox.ospath('A_COPY_2/D') # Update working copy to allow full inheritance and elision. svntest.actions.run_and_verify_svn(None, exp_noop_up_out(7), [], @@ -13417,6 +13483,12 @@ def natural_history_filtering(sbox): # # To set up a situation where this can occur we'll do the following: # + # trunk -1-----3-4-5-6-------8----------- A + # \ \ \ + # branch1 2-----------\-------9-------- A_COPY + # \ \ + # branch2 7--------10---- A_COPY_2 + # # 1) Create a 'trunk'. # # 2) Copy 'trunk' to 'branch1'. @@ -13438,9 +13510,9 @@ def natural_history_filtering(sbox): wc_dir = sbox.wc_dir # Some paths we'll care about - A_COPY_path = os.path.join(wc_dir, "A_COPY") - A_COPY_2_path = os.path.join(wc_dir, "A_COPY_2") - chi_path = os.path.join(wc_dir, "A", "D", "H", "chi") + A_COPY_path = sbox.ospath('A_COPY') + A_COPY_2_path = sbox.ospath('A_COPY_2') + chi_path = sbox.ospath('A/D/H/chi') # r1-r6: Setup a 'trunk' (A) and a 'branch' (A_COPY). wc_disk, wc_status = set_up_branch(sbox, False, 1) @@ -13713,9 +13785,9 @@ def subtree_gets_changes_even_if_ultimately_deleted(sbox): wc_dir = sbox.wc_dir # Some paths we'll care about - H_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "H") - psi_path = os.path.join(wc_dir, "A", "D", "H", "psi") - psi_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "H", "psi") + H_COPY_path = sbox.ospath('A_COPY/D/H') + psi_path = sbox.ospath('A/D/H/psi') + psi_COPY_path = sbox.ospath('A_COPY/D/H/psi') # r2 - r6: Copy A to A_COPY and then make some text changes under A. set_up_branch(sbox) @@ -13736,11 +13808,10 @@ def subtree_gets_changes_even_if_ultimately_deleted(sbox): # r9: Merge r3,7 from A/D/H to A_COPY/D/H, then reverse merge r7 from # A/D/H/psi to A_COPY/D/H/psi. expected_output = wc.State(H_COPY_path, { - 'psi' : Item(status='U '), - 'psi' : Item(status='G '), + 'psi' : Item(status='G ', prev_status='U '), # Touched twice }) expected_mergeinfo_output = wc.State(H_COPY_path, { - '' : Item(status=' G'), + '' : Item(status=' G', prev_status=' U'), }) expected_elision_output = wc.State(H_COPY_path, { }) @@ -13788,7 +13859,7 @@ def subtree_gets_changes_even_if_ultimately_deleted(sbox): svntest.main.run_svn(None, 'up', wc_dir) expected_output = wc.State(H_COPY_path, { 'omega' : Item(status='U '), - 'psi' : Item(status='D '), + 'psi' : Item(status='D ', prev_status='U '), }) expected_mergeinfo_output = wc.State(H_COPY_path, { '' : Item(status=' U'), @@ -13826,10 +13897,10 @@ def no_self_referential_filtering_on_added_path(sbox): wc_dir = sbox.wc_dir # Some paths we'll care about - C_COPY_path = os.path.join(wc_dir, "A_COPY", "C") - A_path = os.path.join(wc_dir, "A") - C_path = os.path.join(wc_dir, "A", "C") - A_COPY_2_path = os.path.join(wc_dir, "A_COPY_2") + C_COPY_path = sbox.ospath('A_COPY/C') + A_path = sbox.ospath('A') + C_path = sbox.ospath('A/C') + A_COPY_2_path = sbox.ospath('A_COPY_2') # r1-r7: Setup a 'trunk' and two 'branches'. wc_disk, wc_status = set_up_branch(sbox, False, 2) @@ -13897,6 +13968,14 @@ def no_self_referential_filtering_on_added_path(sbox): 'C' : Item(status='D '), 'C_MOVED' : Item(status='A '), }) + # Why is C_MOVED notified as ' G' rather than ' U'? C_MOVED was + # added by the merge and there is only a single editor drive, so + # how can any prop changes be merged to it? The answer is that + # the merge code does some quiet housekeeping, merging C_MOVED's + # inherited mergeinfo into its incoming mergeinfo, see + # http://subversion.tigris.org/issues/show_bug.cgi?id=4309 + # This test is not covering issue #4309 so we let the current + # behavior pass. expected_mergeinfo_output = wc.State(A_COPY_2_path, { '' : Item(status=' G'), 'C_MOVED' : Item(status=' G'), @@ -13934,11 +14013,11 @@ def no_self_referential_filtering_on_added_path(sbox): 'B/E/beta' : Item("New content"), 'B/lambda' : Item("This is the file 'lambda'.\n"), 'B/F' : Item(), + # What's up with the mergeinfo 'C_MOVED' : Item(props={SVN_PROP_MERGEINFO : '/A/C_MOVED:10\n' + '/A_COPY/C:8\n' + '/A_COPY/C_MOVED:8', 'propname' : 'propval'}), - 'C' : Item(), 'D' : Item(), 'D/G' : Item(), 'D/G/pi' : Item("This is the file 'pi'.\n"), @@ -13950,8 +14029,6 @@ def no_self_referential_filtering_on_added_path(sbox): 'D/H/psi' : Item("New content"), 'D/H/omega' : Item("New content"), }) - if svntest.main.wc_is_singledb(wc_dir): - expected_A_COPY_2_disk.remove('C') expected_A_COPY_2_skip = wc.State(A_COPY_2_path, { }) svntest.actions.run_and_verify_merge(A_COPY_2_path, None, None, sbox.repo_url + '/A', None, @@ -13982,21 +14059,21 @@ def merge_range_prior_to_rename_source_existence(sbox): wc_dir = sbox.wc_dir # Some paths we'll care about - nu_path = os.path.join(wc_dir, "A", "D", "H", "nu") - nu_moved_path = os.path.join(wc_dir, "A", "D", "H", "nu_moved") - A_path = os.path.join(wc_dir, "A") - alpha_path = os.path.join(wc_dir, "A", "B", "E", "alpha") - A_COPY_path = os.path.join(wc_dir, "A_COPY") - A_COPY_2_path = os.path.join(wc_dir, "A_COPY_2") - B_COPY_path = os.path.join(wc_dir, "A_COPY", "B") - B_COPY_2_path = os.path.join(wc_dir, "A_COPY_2", "B") - alpha_COPY_path = os.path.join(wc_dir, "A_COPY", "B", "E", "alpha") - beta_COPY_path = os.path.join(wc_dir, "A_COPY", "B", "E", "beta") - gamma_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "gamma") - rho_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "G", "rho") - omega_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "H", "omega") - psi_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "H", "psi") - nu_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "H", "nu") + nu_path = sbox.ospath('A/D/H/nu') + nu_moved_path = sbox.ospath('A/D/H/nu_moved') + A_path = sbox.ospath('A') + alpha_path = sbox.ospath('A/B/E/alpha') + A_COPY_path = sbox.ospath('A_COPY') + A_COPY_2_path = sbox.ospath('A_COPY_2') + B_COPY_path = sbox.ospath('A_COPY/B') + B_COPY_2_path = sbox.ospath('A_COPY_2/B') + alpha_COPY_path = sbox.ospath('A_COPY/B/E/alpha') + beta_COPY_path = sbox.ospath('A_COPY/B/E/beta') + gamma_COPY_path = sbox.ospath('A_COPY/D/gamma') + rho_COPY_path = sbox.ospath('A_COPY/D/G/rho') + omega_COPY_path = sbox.ospath('A_COPY/D/H/omega') + psi_COPY_path = sbox.ospath('A_COPY/D/H/psi') + nu_COPY_path = sbox.ospath('A_COPY/D/H/nu') # Setup our basic 'trunk' and 'branch': # r2 - Copy A to A_COPY @@ -14086,11 +14163,13 @@ def merge_range_prior_to_rename_source_existence(sbox): 'move', sbox.repo_url + '/A/D/H/nu', sbox.repo_url + '/A/D/H/nu_moved', '-m', 'Move nu to nu_moved') - svntest.actions.run_and_verify_svn(None, - ["Updating '%s':\n" % (wc_dir), - "D " + nu_path + "\n", - "A " + nu_moved_path + "\n", - "Updated to revision 12.\n"], + expected_output = svntest.verify.UnorderedOutput( + ["Updating '%s':\n" % (wc_dir), + "D " + nu_path + "\n", + "A " + nu_moved_path + "\n", + "Updated to revision 12.\n"], + ) + svntest.actions.run_and_verify_svn(None, expected_output, [], 'up', wc_dir) # Now merge -r7:12 from A to A_COPY. @@ -14324,8 +14403,8 @@ def set_up_natural_history_gap(sbox): wc_disk, wc_status = set_up_branch(sbox, False, 0) # Some paths we'll care about. - A_COPY_path = os.path.join(wc_dir, "A_COPY") - gamma_path = os.path.join(wc_dir, "A", "D", "gamma") + A_COPY_path = sbox.ospath('A_COPY') + gamma_path = sbox.ospath('A/D/gamma') # r6: Delete 'A' exit_code, out, err = svntest.actions.run_and_verify_svn( @@ -14394,7 +14473,7 @@ def dont_merge_gaps_in_history(sbox): set_up_natural_history_gap(sbox) # Some paths we'll care about. - A_COPY_path = os.path.join(wc_dir, "A_COPY") + A_COPY_path = sbox.ospath('A_COPY') # Now merge all available changes from 'A' to 'A_COPY'. The only # available revisions are r8 and r9. Only r9 effects the source/target @@ -14501,7 +14580,7 @@ def handle_gaps_in_implicit_mergeinfo(sbox): expected_disk, expected_status = set_up_natural_history_gap(sbox) # Some paths we'll care about. - A_COPY_path = os.path.join(wc_dir, "A_COPY") + A_COPY_path = sbox.ospath('A_COPY') # Merge r4 to 'A_COPY' from A@4, which is *not* part of A_COPY's history. expected_output = wc.State(A_COPY_path, { @@ -14624,9 +14703,9 @@ def mergeinfo_deleted_by_a_merge_should_disappear(sbox): wc_dir = sbox.wc_dir # Some paths we'll care about - D_COPY_path = os.path.join(wc_dir, "A_COPY", "D") - A_COPY_path = os.path.join(wc_dir, "A_COPY") - A_COPY_2_path = os.path.join(wc_dir, "A_COPY_2") + D_COPY_path = sbox.ospath('A_COPY/D') + A_COPY_path = sbox.ospath('A_COPY') + A_COPY_2_path = sbox.ospath('A_COPY_2') # r2 - r6: Copy A to A_COPY and then make some text changes under A. wc_disk, wc_status = set_up_branch(sbox) @@ -14746,9 +14825,9 @@ def noop_file_merge(sbox): wc_dir = sbox.wc_dir # Some paths we'll care about - A_COPY_path = os.path.join(wc_dir, "A_COPY") - beta_COPY_path = os.path.join(wc_dir, "A_COPY", "B", "E", "beta") - chi_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "H", "chi") + A_COPY_path = sbox.ospath('A_COPY') + beta_COPY_path = sbox.ospath('A_COPY/B/E/beta') + chi_COPY_path = sbox.ospath('A_COPY/D/H/chi') # r2 - r6: Copy A to A_COPY and then make some text changes under A. wc_disk, wc_status = set_up_branch(sbox) @@ -14764,7 +14843,7 @@ def noop_file_merge(sbox): [], 'merge', '-c5', sbox.repo_url + '/A', A_COPY_path) svntest.actions.run_and_verify_svn(None, None, [], 'commit', '-m', 'Merge r5 from A to A_COPY', - wc_dir); + wc_dir) # Update working copy to allow full inheritance and elision. svntest.actions.run_and_verify_svn(None, exp_noop_up_out(7), [], @@ -14895,15 +14974,15 @@ def record_only_merge(sbox): wc_disk, wc_status = set_up_branch(sbox) # Some paths we'll care about - nu_path = os.path.join(wc_dir, "A", "C", "nu") - A_COPY_path = os.path.join(wc_dir, "A_COPY") - A2_path = os.path.join(wc_dir, "A2") - Z_path = os.path.join(wc_dir, "A", "B", "Z") - Z_COPY_path = os.path.join(wc_dir, "A_COPY", "B", "Z") - rho_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "G", "rho") - omega_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "H", "omega") - H_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "H") - nu_COPY_path = os.path.join(wc_dir, "A_COPY", "C", "nu") + nu_path = sbox.ospath('A/C/nu') + A_COPY_path = sbox.ospath('A_COPY') + A2_path = sbox.ospath('A2') + Z_path = sbox.ospath('A/B/Z') + Z_COPY_path = sbox.ospath('A_COPY/B/Z') + rho_COPY_path = sbox.ospath('A_COPY/D/G/rho') + omega_COPY_path = sbox.ospath('A_COPY/D/H/omega') + H_COPY_path = sbox.ospath('A_COPY/D/H') + nu_COPY_path = sbox.ospath('A_COPY/C/nu') # r7 - Copy the branch A_COPY@2 to A2 and update the WC. svntest.actions.run_and_verify_svn(None, None, [], @@ -15077,8 +15156,6 @@ def record_only_merge(sbox): #---------------------------------------------------------------------- # Test for issue #3514 'svn merge --accept [ base | theirs-full ] # doesn't work' -# -# This test is marked as XFail until issue #3514 is fixed. @Issue(3514) def merge_automatic_conflict_resolution(sbox): "automatic conflict resolutions work with merge" @@ -15089,8 +15166,8 @@ def merge_automatic_conflict_resolution(sbox): # Some paths we'll care about - A_COPY_path = os.path.join(wc_dir, "A_COPY") - psi_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "H", "psi") + A_COPY_path = sbox.ospath('A_COPY') + psi_COPY_path = sbox.ospath('A_COPY/D/H/psi') # r7 - Make a change on A_COPY that will conflict with r3 on A svntest.main.file_write(psi_COPY_path, "BASE.\n") @@ -15176,7 +15253,8 @@ def merge_automatic_conflict_resolution(sbox): 'revert', '--recursive', wc_dir) # Test --accept mine-conflict and mine-full - expected_output = wc.State(A_COPY_path, {'D/H/psi' : Item(status='U ')}) + ### TODO: Also test that the output has a 'Resolved' line for this path. + expected_output = wc.State(A_COPY_path, {'D/H/psi' : Item(status='C ')}) expected_disk.tweak('D/H/psi', contents="BASE.\n") expected_status.tweak('D/H/psi', status=' ') svntest.actions.run_and_verify_merge(A_COPY_path, '2', '3', @@ -15211,7 +15289,8 @@ def merge_automatic_conflict_resolution(sbox): 'revert', '--recursive', wc_dir) # Test --accept theirs-conflict and theirs-full - expected_output = wc.State(A_COPY_path, {'D/H/psi' : Item(status='U ')}) + ### TODO: Also test that the output has a 'Resolved' line for this path. + expected_output = wc.State(A_COPY_path, {'D/H/psi' : Item(status='C ')}) expected_disk.tweak('D/H/psi', contents="New content") expected_status.tweak('D/H/psi', status='M ') svntest.actions.run_and_verify_merge(A_COPY_path, '2', '3', @@ -15245,7 +15324,8 @@ def merge_automatic_conflict_resolution(sbox): svntest.actions.run_and_verify_svn(None, None, [], 'revert', '--recursive', wc_dir) # Test --accept base - expected_output = wc.State(A_COPY_path, {'D/H/psi' : Item(status='U ')}) + ### TODO: Also test that the output has a 'Resolved' line for this path. + expected_output = wc.State(A_COPY_path, {'D/H/psi' : Item(status='C ')}) expected_elision_output = wc.State(A_COPY_path, { }) expected_disk.tweak('D/H/psi', contents="This is the file 'psi'.\n") @@ -15275,10 +15355,10 @@ def skipped_files_get_correct_mergeinfo(sbox): wc_dir = sbox.wc_dir # Some paths we'll care about - A_COPY_path = os.path.join(wc_dir, "A_COPY") - H_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "H") - psi_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "H", "psi") - psi_path = os.path.join(wc_dir, "A", "D", "H", "psi") + A_COPY_path = sbox.ospath('A_COPY') + H_COPY_path = sbox.ospath('A_COPY/D/H') + psi_COPY_path = sbox.ospath('A_COPY/D/H/psi') + psi_path = sbox.ospath('A/D/H/psi') # Setup our basic 'trunk' and 'branch': # r2 - Copy A to A_COPY @@ -15356,9 +15436,10 @@ def skipped_files_get_correct_mergeinfo(sbox): 'D/gamma' : Item("This is the file 'gamma'.\n"), 'D/H' : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:2*,3,4-8*'}), }) - expected_skip = wc.State(A_COPY_path, - {'D/H/psi' : Item(), - 'D/H/omega' : Item()}) + expected_skip = wc.State( + A_COPY_path, + {'D/H/psi' : Item(verb='Skipped missing target'), + 'D/H/omega' : Item(verb='Skipped missing target')}) expected_output = wc.State(A_COPY_path, {'B/E/beta' : Item(status='U '), 'D/G/rho' : Item(status='U ')}) @@ -15392,7 +15473,7 @@ def committed_case_only_move_and_revert(sbox): wc_disk, wc_status = set_up_branch(sbox, True) # Some paths we'll care about - A_COPY_path = os.path.join(wc_dir, "A_COPY") + A_COPY_path = sbox.ospath('A_COPY') # r3: A case-only file rename on the server svntest.actions.run_and_verify_svn(None, @@ -15513,14 +15594,13 @@ def committed_case_only_move_and_revert(sbox): }) expected_disk.tweak('', props={SVN_PROP_MERGEINFO : '/A:3,5'}) expected_disk.add({'c' : Item()}) - if svntest.main.wc_is_singledb(wc_dir): - expected_disk.remove('C') + expected_disk.remove('C') expected_status.tweak('MU', status=' ', wc_rev=4, copied=None) expected_status.remove('mu') expected_status.tweak('C', status='D ') expected_status.tweak('', wc_rev=4) expected_status.add({'c' : Item(status='A ', copied='+', wc_rev='-')}) - # This merge succeeds, but A_COPY/c is in a strange state, added with + # This merge succeeds. It used to leave a strange state, added with # history but missing: # # M merge_tests-139\A_COPY @@ -15552,8 +15632,8 @@ def merge_into_wc_for_deleted_branch(sbox): wc_disk, wc_status = set_up_branch(sbox) # Some paths we'll care about - A_COPY_path = os.path.join(wc_dir, "A_COPY") - gamma_path = os.path.join(wc_dir, "A", "D", "gamma") + A_COPY_path = sbox.ospath('A_COPY') + gamma_path = sbox.ospath('A/D/gamma') # r7 - Delete the branch on the repository, obviously it still # exists in our WC. @@ -15656,7 +15736,7 @@ def foreign_repos_del_and_props(sbox): wc_dir = sbox.wc_dir wc2_dir = sbox.add_wc_path('wc2') - (r2_path, r2_url) = sbox.add_repo_path('fgn'); + (r2_path, r2_url) = sbox.add_repo_path('fgn') svntest.main.create_repos(r2_path) svntest.actions.run_and_verify_svn(None, None, [], 'checkout', @@ -15664,17 +15744,17 @@ def foreign_repos_del_and_props(sbox): svntest.actions.run_and_verify_svn(None, None, [], 'propset', 'svn:eol-style', 'native', - os.path.join(wc_dir, 'iota')) + sbox.ospath('iota')) svntest.actions.run_and_verify_svn(None, None, [], 'cp', - os.path.join(wc_dir, 'A/D'), - os.path.join(wc_dir, 'D')) + sbox.ospath('A/D'), + sbox.ospath('D')) svntest.actions.run_and_verify_svn(None, None, [], 'rm', - os.path.join(wc_dir, 'A/D'), - os.path.join(wc_dir, 'D/G')) + sbox.ospath('A/D'), + sbox.ospath('D/G')) - new_file = os.path.join(wc_dir, 'new-file') + new_file = sbox.ospath('new-file') svntest.main.file_write(new_file, 'new-file') svntest.actions.run_and_verify_svn(None, None, [], 'add', new_file) @@ -15744,8 +15824,8 @@ def immediate_depth_merge_creates_minimal_subtree_mergeinfo(sbox): wc_dir = sbox.wc_dir wc_disk, wc_status = set_up_branch(sbox) - B_path = os.path.join(wc_dir, "A", "B") - B_COPY_path = os.path.join(wc_dir, "A_COPY", "B") + B_path = sbox.ospath('A/B') + B_COPY_path = sbox.ospath('A_COPY/B') svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir) @@ -15816,9 +15896,9 @@ def record_only_merge_creates_self_referential_mergeinfo(sbox): wc_dir = sbox.wc_dir # Some paths we'll care about - mu_path = os.path.join(wc_dir, 'A', 'mu') - A_path = os.path.join(wc_dir, 'A') - A_branch_path = os.path.join(wc_dir, 'A-branch') + mu_path = sbox.ospath('A/mu') + A_path = sbox.ospath('A') + A_branch_path = sbox.ospath('A-branch') # Make a change to A/mu in r2. svntest.main.file_write(mu_path, "Trunk edit\n") @@ -15905,11 +15985,11 @@ def dav_skelta_mode_causes_spurious_conflicts(sbox): wc_dir = sbox.wc_dir # Some paths we'll care about - mu_path = os.path.join(wc_dir, "A", "mu") - A_path = os.path.join(wc_dir, "A") - C_path = os.path.join(wc_dir, "A", "C") - A_branch_path = os.path.join(wc_dir, "A-branch") - C_branch_path = os.path.join(wc_dir, "A-branch", "C") + mu_path = sbox.ospath('A/mu') + A_path = sbox.ospath('A') + C_path = sbox.ospath('A/C') + A_branch_path = sbox.ospath('A-branch') + C_branch_path = sbox.ospath('A-branch/C') # r2 - Set some intial properties: # @@ -16057,7 +16137,7 @@ def merge_into_locally_added_file(sbox): shutil.copy(pi_path, new_path) svntest.main.file_append(pi_path, "foo\n") - sbox.simple_commit(); # r2 + sbox.simple_commit() # r2 sbox.simple_add('A/D/G/new') @@ -16102,7 +16182,7 @@ def merge_into_locally_added_directory(sbox): new_dir_path = sbox.ospath("A/D/new_dir") svntest.main.file_append_binary(pi_path, "foo\n") - sbox.simple_commit(); # r2 + sbox.simple_commit() # r2 os.mkdir(new_dir_path) svntest.main.file_append_binary(os.path.join(new_dir_path, 'pi'), @@ -16165,11 +16245,11 @@ def merge_with_os_deleted_subtrees(sbox): set_up_branch(sbox) # Some paths we'll care about - A_COPY_path = os.path.join(wc_dir, "A_COPY") - C_COPY_path = os.path.join(wc_dir, "A_COPY", "C") - psi_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "H", "psi") - mu_COPY_path = os.path.join(wc_dir, "A_COPY", "mu") - G_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "G") + A_COPY_path = sbox.ospath('A_COPY') + C_COPY_path = sbox.ospath('A_COPY/C') + psi_COPY_path = sbox.ospath('A_COPY/D/H/psi') + mu_COPY_path = sbox.ospath('A_COPY/mu') + G_COPY_path = sbox.ospath('A_COPY/D/G') # Remove several subtrees from disk. svntest.main.safe_rmtree(C_COPY_path) @@ -16188,7 +16268,7 @@ def merge_with_os_deleted_subtrees(sbox): err_re = "svn: E195016: Merge tracking not allowed with missing subtrees; " + \ "try restoring these items first:" + \ "|(\n)" + \ - "|(.*apr_err.*\n)" # In case of debug build + "|" + svntest.main.stack_trace_regexp # Case 1: Infinite depth merge into infinite depth WC target. # Every missing subtree under the target should be reported as missing. @@ -16236,7 +16316,7 @@ def merge_with_os_deleted_subtrees(sbox): # Test for issue #3668 'inheritance can result in self-referential # mergeinfo' and issue #3669 'inheritance can result in mergeinfo # describing nonexistent sources' -@Issue(3668) +@Issue(3668,3669) @XFail() def no_self_referential_or_nonexistent_inherited_mergeinfo(sbox): "don't inherit bogus mergeinfo" @@ -16249,12 +16329,12 @@ def no_self_referential_or_nonexistent_inherited_mergeinfo(sbox): set_up_branch(sbox, nbr_of_branches=1) # Some paths we'll care about - nu_path = os.path.join(wc_dir, "A", "C", "nu") - nu_COPY_path = os.path.join(wc_dir, "A_COPY", "C", "nu") - J_path = os.path.join(wc_dir, "A", "D", "J") - J_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "J") - zeta_path = os.path.join(wc_dir, "A", "D", "J", "zeta") - A_COPY_path = os.path.join(wc_dir, "A_COPY") + nu_path = sbox.ospath('A/C/nu') + nu_COPY_path = sbox.ospath('A_COPY/C/nu') + J_path = sbox.ospath('A/D/J') + J_COPY_path = sbox.ospath('A_COPY/D/J') + zeta_path = sbox.ospath('A/D/J/zeta') + A_COPY_path = sbox.ospath('A_COPY') # r7 - Add the file A/C/nu svntest.main.file_write(nu_path, "This is the file 'nu'.\n") @@ -16300,7 +16380,7 @@ def no_self_referential_or_nonexistent_inherited_mergeinfo(sbox): # This test is marked as XFail because the following two merges # create mergeinfo with both non-existent path-revs and self-referential - # mergeinfo.c + # mergeinfo. # # Merge all available revisions from A/C/nu to A_COPY/C/nu. # The target has no explicit mergeinfo of its own but inherits mergeinfo @@ -16367,9 +16447,12 @@ def no_self_referential_or_nonexistent_inherited_mergeinfo(sbox): None, 1) #---------------------------------------------------------------------- -# Test for issue #3756 'subtree merge can inherit invalid working mergeinfo'. +# Test for issue #3756 'subtree merge can inherit invalid working mergeinfo', +# issue #3668 'inheritance can result in self-referential mergeinfo', and +# issue #3669 'inheritance can result in mergeinfo describing nonexistent +# sources'. @XFail() -@Issue(3756) +@Issue(3756,3668,3669) def subtree_merges_inherit_invalid_working_mergeinfo(sbox): "don't inherit bogus working mergeinfo" @@ -16381,9 +16464,9 @@ def subtree_merges_inherit_invalid_working_mergeinfo(sbox): set_up_branch(sbox, nbr_of_branches=1) # Some paths we'll care about - nu_path = os.path.join(wc_dir, "A", "C", "nu") - nu_COPY_path = os.path.join(wc_dir, "A_COPY", "C", "nu") - A_COPY_path = os.path.join(wc_dir, "A_COPY") + nu_path = sbox.ospath('A/C/nu') + nu_COPY_path = sbox.ospath('A_COPY/C/nu') + A_COPY_path = sbox.ospath('A_COPY') # r7 - Add the file A/C/nu svntest.main.file_write(nu_path, "This is the file 'nu'.\n") @@ -16418,7 +16501,7 @@ def subtree_merges_inherit_invalid_working_mergeinfo(sbox): # resulting mergeinfo on 'A_COPY/C/nu' should be only '/A/C/nu:9'. # # Currently this test is marked as XFail because the resulting mergeinfo is - # '/A/C/nu:3,9' and thus includes a non-existent path-rev. + # '/A/C/nu:3,7,9' and thus includes a non-existent path-rev. svntest.actions.run_and_verify_svn( "Merge failed unexpectedly", svntest.verify.AnyOutput, [], 'merge', sbox.repo_url + '/A', @@ -16447,11 +16530,12 @@ def merge_change_to_file_with_executable(sbox): wc_dir = sbox.wc_dir trunk_url = sbox.repo_url + '/A/B/E' - alpha_path = os.path.join(wc_dir, "A", "B", "E", "alpha") - beta_path = os.path.join(wc_dir, "A", "B", "E", "beta") + alpha_path = sbox.ospath('A/B/E/alpha') + beta_path = sbox.ospath('A/B/E/beta') # Force one of the files to be a binary type - svntest.actions.run_and_verify_svn(None, None, [], + svntest.actions.run_and_verify_svn2(None, None, + binary_mime_type_on_text_file_warning, 0, 'propset', 'svn:mime-type', 'application/octet-stream', alpha_path) @@ -16497,8 +16581,8 @@ def merge_change_to_file_with_executable(sbox): sbox.repo_url + '/branch', wc_dir) # Recalculate the paths - alpha_path = os.path.join(wc_dir, "alpha") - beta_path = os.path.join(wc_dir, "beta") + alpha_path = sbox.ospath('alpha') + beta_path = sbox.ospath('beta') expected_output = wc.State(wc_dir, { 'beta' : Item(status='U '), @@ -16564,7 +16648,7 @@ def dry_run_merge_conflicting_binary(sbox): # Add a binary file to the project theta_contents = open(os.path.join(sys.path[0], "theta.bin"), 'rb').read() # Write PNG file data into 'A/theta'. - theta_path = os.path.join(wc_dir, 'A', 'theta') + theta_path = sbox.ospath('A/theta') svntest.main.file_write(theta_path, theta_contents, 'wb') svntest.main.run_svn(None, 'add', theta_path) @@ -16679,11 +16763,9 @@ def foreign_repos_prop_conflict(sbox): # Now, merge the propchange to the *second* working copy. expected_output = [' C %s\n' % (os.path.join(other_wc_dir, - "A", "D", "G")), - 'Summary of conflicts:\n', - ' Property conflicts: 1\n', - ] - expected_output = expected_merge_output([[3]], expected_output, True) + "A", "D", "G"))] + expected_output = expected_merge_output([[3]], expected_output, True, + prop_conflicts=1) svntest.actions.run_and_verify_svn(None, expected_output, [], 'merge', '-c3', @@ -16691,20 +16773,153 @@ def foreign_repos_prop_conflict(sbox): other_wc_dir) #---------------------------------------------------------------------- +# Test for issue #3975 'adds with explicit mergeinfo don't get mergeinfo +# describing merge which added them' +@Issue(3975) +@SkipUnless(server_has_mergeinfo) +def merge_adds_subtree_with_mergeinfo(sbox): + "merge adds subtree with mergeinfo" + + sbox.build() + os.chdir(sbox.wc_dir) + sbox.wc_dir = '' + wc_dir = sbox.wc_dir + wc_disk, wc_status = set_up_branch(sbox, False, 2) + + A_path = sbox.ospath('A') + nu_path = sbox.ospath('A/C/nu') + nu_COPY_path = sbox.ospath('A_COPY/C/nu') + A_COPY2_path = sbox.ospath('A_COPY_2') + + # r8 - Add the file A_COPY/C/nu. + svntest.main.file_write(nu_COPY_path, "This is the file 'nu'.\n") + svntest.actions.run_and_verify_svn(None, None, [], 'add', nu_COPY_path) + svntest.actions.run_and_verify_svn(None, None, [], 'ci', '-m', + 'Add a file on the A_COPY branch', + wc_dir) + + # r9 - Cherry pick r8 from A_COPY to A. + svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir) + svntest.actions.run_and_verify_svn(None, None, [], 'merge', + sbox.repo_url + '/A_COPY', + A_path, '-c8') + svntest.actions.run_and_verify_svn(None, None, [], 'ci', '-m', + 'Merge r8 from A_COPY to A', wc_dir) + + # r10 - Make a modification to A_COPY/C/nu + svntest.main.file_append(nu_COPY_path, + "More work on the A_COPY branch.\n") + svntest.actions.run_and_verify_svn(None, None, [], 'ci', '-m', + 'Some work on the A_COPY branch', wc_dir) + + # r9 - Cherry pick r10 from A_COPY/C/nu to A/C/nu. Make some + # changes to A/C/nu before committing the merge. + svntest.actions.run_and_verify_svn(None, None, [], 'merge', + sbox.repo_url + '/A_COPY/C/nu', + nu_path, '-c10') + svntest.main.file_append(nu_path, "A faux conflict resolution.\n") + svntest.actions.run_and_verify_svn(None, None, [], 'ci', '-m', + 'Merge r8 from A_COPY to A', wc_dir) + + # Sync merge A to A_COPY_2 + svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir) + expected_output = wc.State(A_COPY2_path, { + 'B/E/beta' : Item(status='U '), + 'C/nu' : Item(status='A '), + 'D/G/rho' : Item(status='U '), + 'D/H/omega' : Item(status='U '), + 'D/H/psi' : Item(status='U '), + '' : Item(status=' U'), + }) + expected_mergeinfo_output = wc.State(A_COPY2_path, { + '' : Item(status=' G'), + 'C/nu' : Item(status=' U'), + }) + expected_elision_output = wc.State(A_COPY2_path, { + }) + expected_status = wc.State(A_COPY2_path, { + '' : Item(status=' M'), + 'B' : Item(status=' '), + 'mu' : Item(status=' '), + 'B/E' : Item(status=' '), + 'B/E/alpha' : Item(status=' '), + 'B/E/beta' : Item(status='M '), + 'B/lambda' : Item(status=' '), + 'B/F' : Item(status=' '), + 'C' : Item(status=' '), + 'C/nu' : Item(status='A ', copied='+'), + 'D' : Item(status=' '), + 'D/G' : Item(status=' '), + 'D/G/pi' : Item(status=' '), + 'D/G/rho' : Item(status='M '), + 'D/G/tau' : Item(status=' '), + 'D/gamma' : Item(status=' '), + 'D/H' : Item(status=' '), + 'D/H/chi' : Item(status=' '), + 'D/H/psi' : Item(status='M '), + 'D/H/omega' : Item(status='M '), + }) + expected_status.tweak(wc_rev=11) + expected_status.tweak('C/nu', wc_rev='-') + expected_disk = wc.State('', { + '' : Item(props={SVN_PROP_MERGEINFO : '/A:3-11\n/A_COPY:8'}), + 'B' : Item(), + 'mu' : Item("This is the file 'mu'.\n"), + 'B/E' : Item(), + 'B/E/alpha' : Item("This is the file 'alpha'.\n"), + 'B/E/beta' : Item("New content"), + 'B/lambda' : Item("This is the file 'lambda'.\n"), + 'B/F' : Item(), + 'C' : Item(), + # C/nu will pick up the mergeinfo A_COPY/C/nu:8 which is self-referential. + # This is issue #3668 'inheritance can result in self-referential + # mergeinfo', but we'll allow it in this test since issue #3668 is + # tested elsewhere and is not the point of *this* test. + 'C/nu' : Item("This is the file 'nu'.\n" \ + "More work on the A_COPY branch.\n" \ + "A faux conflict resolution.\n", + props={SVN_PROP_MERGEINFO : + '/A/C/nu:9-11\n/A_COPY/C/nu:8,10'}), + 'D' : Item(), + 'D/G' : Item(), + 'D/G/pi' : Item("This is the file 'pi'.\n"), + 'D/G/rho' : Item("New content"), + 'D/G/tau' : Item("This is the file 'tau'.\n"), + 'D/gamma' : Item("This is the file 'gamma'.\n"), + 'D/H' : Item(), + 'D/H/chi' : Item("This is the file 'chi'.\n"), + 'D/H/psi' : Item("New content"), + 'D/H/omega' : Item("New content"), + }) + expected_skip = wc.State('.', { }) + svntest.actions.run_and_verify_merge(A_COPY2_path, None, None, + sbox.repo_url + '/A', None, + expected_output, + expected_mergeinfo_output, + expected_elision_output, + expected_disk, + expected_status, + expected_skip, + None, None, None, None, + None, 1, False) + +#---------------------------------------------------------------------- # A test for issue #3978 'reverse merge which adds subtree fails'. -@Issue(3978) +@Issue(3978,4057) @SkipUnless(server_has_mergeinfo) def reverse_merge_adds_subtree(sbox): "reverse merge adds subtree" sbox.build() + os.chdir(sbox.wc_dir) + sbox.wc_dir = '' wc_dir = sbox.wc_dir wc_disk, wc_status = set_up_branch(sbox) - A_path = os.path.join(wc_dir, 'A') - chi_path = os.path.join(wc_dir, 'A', 'D', 'H', 'chi') - A_COPY_path = os.path.join(wc_dir, 'A_COPY') - H_COPY_path = os.path.join(wc_dir, 'A_COPY', 'D', 'H') + A_path = sbox.ospath('A') + chi_path = sbox.ospath('A/D/H/chi') + A_COPY_path = sbox.ospath('A_COPY') + H_COPY_path = sbox.ospath('A_COPY/D/H') # r7 - Delete A\D\H\chi svntest.actions.run_and_verify_svn(None, None, [], 'delete', chi_path) @@ -16719,6 +16934,9 @@ def reverse_merge_adds_subtree(sbox): 'Cherry-pick r7 from A to A_COPY', wc_dir) # r9 - File depth sync merge from A/D/H to A_COPY/D/H/ + # This shallow merge does not create non-inheritable mergeinfo because of + # the issue #4057 fix; all subtrees affected by the diff are present, so + # non-inheritable mergeinfo is not required. svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir) svntest.actions.run_and_verify_svn(None, None, [], 'merge', sbox.repo_url + '/A/D/H', @@ -16756,7 +16974,6 @@ def reverse_merge_adds_subtree(sbox): # ..\..\..\subversion\libsvn_subr\kitchensink.c:57: (apr_err=200022) # svn: E200022: Negative revision number found parsing '-7' svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir) - svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir) expected_output = wc.State(A_COPY_path, { 'D/H/chi' : Item(status='A '), }) @@ -16805,12 +17022,10 @@ def reverse_merge_adds_subtree(sbox): 'D/G/rho' : Item("This is the file 'rho'.\n"), 'D/G/tau' : Item("This is the file 'tau'.\n"), 'D/gamma' : Item("This is the file 'gamma'.\n"), - 'D/H' : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:2-6*,8*'}), + 'D/H' : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:2-6,8'}), 'D/H/chi' : Item("This is the file 'chi'.\n"), - 'D/H/psi' : Item("New content", - props={SVN_PROP_MERGEINFO : '/A/D/H/psi:2-8'}), - 'D/H/omega' : Item("New content", - props={SVN_PROP_MERGEINFO : '/A/D/H/omega:2-8'}), + 'D/H/psi' : Item("New content"), + 'D/H/omega' : Item("New content"), }) expected_skip = wc.State('.', { }) svntest.actions.run_and_verify_merge(A_COPY_path, 7, 6, @@ -16833,11 +17048,13 @@ def merged_deletion_causes_tree_conflict(sbox): "merged deletion causes spurious tree conflict" sbox.build() + os.chdir(sbox.wc_dir) + sbox.wc_dir = '' wc_dir = sbox.wc_dir - A_path = os.path.join(wc_dir, 'A') - psi_path = os.path.join(wc_dir, 'A', 'D', 'H', 'psi') - H_branch_path = os.path.join(wc_dir, 'branch', 'D', 'H') + A_path = sbox.ospath('A') + psi_path = sbox.ospath('A/D/H/psi') + H_branch_path = sbox.ospath('branch/D/H') # r2 - Set svn:eol-style native on A/D/H/psi svntest.actions.run_and_verify_svn(None, None, [], 'ps', 'svn:eol-style', @@ -16853,7 +17070,7 @@ def merged_deletion_causes_tree_conflict(sbox): '-m', 'Copy ^/A to ^/branch') svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir) - # r4 - Delete A/D/H/psi + # r4 - Delete A/D/H/psi svntest.actions.run_and_verify_svn(None, None, [], 'delete', psi_path) svntest.actions.run_and_verify_svn(None, None, [], 'ci', '-m', 'Delete a a path with native eol-style', @@ -16897,135 +17114,6 @@ def merged_deletion_causes_tree_conflict(sbox): None, 1, False) #---------------------------------------------------------------------- -# Test for issue #3975 'adds with explicit mergeinfo don't get mergeinfo -# describing merge which added them' -@Issue(3975) -@SkipUnless(server_has_mergeinfo) -def merge_adds_subtree_with_mergeinfo(sbox): - "merge adds subtree with mergeinfo" - - sbox.build() - wc_dir = sbox.wc_dir - wc_disk, wc_status = set_up_branch(sbox, False, 2) - - A_path = os.path.join(wc_dir, 'A') - nu_path = os.path.join(wc_dir, 'A', 'C', 'nu') - nu_COPY_path = os.path.join(wc_dir, 'A_COPY', 'C', 'nu') - A_COPY2_path = os.path.join(wc_dir, 'A_COPY_2') - - # r8 - Add the file A_COPY/C/nu. - svntest.main.file_write(nu_COPY_path, "This is the file 'nu'.\n") - svntest.actions.run_and_verify_svn(None, None, [], 'add', nu_COPY_path) - svntest.actions.run_and_verify_svn(None, None, [], 'ci', '-m', - 'Add a file on the A_COPY branch', - wc_dir) - - # r9 - Cherry pick r8 from A_COPY to A. - svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir) - svntest.actions.run_and_verify_svn(None, None, [], 'merge', - sbox.repo_url + '/A_COPY', - A_path, '-c8') - svntest.actions.run_and_verify_svn(None, None, [], 'ci', '-m', - 'Merge r8 from A_COPY to A', wc_dir) - - # r10 - Make a modification to A_COPY/C/nu - svntest.main.file_append(nu_COPY_path, - "More work on the A_COPY branch.\n") - svntest.actions.run_and_verify_svn(None, None, [], 'ci', '-m', - 'Some work on the A_COPY branch', wc_dir) - - # r9 - Cherry pick r10 from A_COPY/C/nu to A/C/nu. Make some - # changes to A/C/nu before committing the merge. - svntest.actions.run_and_verify_svn(None, None, [], 'merge', - sbox.repo_url + '/A_COPY/C/nu', - nu_path, '-c10') - svntest.main.file_append(nu_path, "A faux conflict resolution.\n") - svntest.actions.run_and_verify_svn(None, None, [], 'ci', '-m', - 'Merge r8 from A_COPY to A', wc_dir) - - # Sync merge A to A_COPY_2 - svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir) - expected_output = wc.State(A_COPY2_path, { - 'B/E/beta' : Item(status='U '), - 'C/nu' : Item(status='A '), - 'D/G/rho' : Item(status='U '), - 'D/H/omega' : Item(status='U '), - 'D/H/psi' : Item(status='U '), - '' : Item(status=' U'), - }) - expected_mergeinfo_output = wc.State(A_COPY2_path, { - '' : Item(status=' G'), - 'C/nu' : Item(status=' U'), - }) - expected_elision_output = wc.State(A_COPY2_path, { - }) - expected_status = wc.State(A_COPY2_path, { - '' : Item(status=' M'), - 'B' : Item(status=' '), - 'mu' : Item(status=' '), - 'B/E' : Item(status=' '), - 'B/E/alpha' : Item(status=' '), - 'B/E/beta' : Item(status='M '), - 'B/lambda' : Item(status=' '), - 'B/F' : Item(status=' '), - 'C' : Item(status=' '), - 'C/nu' : Item(status='A ', copied='+'), - 'D' : Item(status=' '), - 'D/G' : Item(status=' '), - 'D/G/pi' : Item(status=' '), - 'D/G/rho' : Item(status='M '), - 'D/G/tau' : Item(status=' '), - 'D/gamma' : Item(status=' '), - 'D/H' : Item(status=' '), - 'D/H/chi' : Item(status=' '), - 'D/H/psi' : Item(status='M '), - 'D/H/omega' : Item(status='M '), - }) - expected_status.tweak(wc_rev=11) - expected_status.tweak('C/nu', wc_rev='-') - expected_disk = wc.State('', { - '' : Item(props={SVN_PROP_MERGEINFO : '/A:3-11\n/A_COPY:8'}), - 'B' : Item(), - 'mu' : Item("This is the file 'mu'.\n"), - 'B/E' : Item(), - 'B/E/alpha' : Item("This is the file 'alpha'.\n"), - 'B/E/beta' : Item("New content"), - 'B/lambda' : Item("This is the file 'lambda'.\n"), - 'B/F' : Item(), - 'C' : Item(), - # C/nu will pick up the mergeinfo A_COPY/C/nu:8 which is self-referential. - # This is issue #3668 'inheritance can result in self-referential - # mergeinfo', but we'll allow it in this test since issue #3668 is - # tested elsewhere and is not the point of *this* test. - 'C/nu' : Item("This is the file 'nu'.\n" \ - "More work on the A_COPY branch.\n" \ - "A faux conflict resolution.\n", - props={SVN_PROP_MERGEINFO : - '/A/C/nu:9-11\n/A_COPY/C/nu:8,10'}), - 'D' : Item(), - 'D/G' : Item(), - 'D/G/pi' : Item("This is the file 'pi'.\n"), - 'D/G/rho' : Item("New content"), - 'D/G/tau' : Item("This is the file 'tau'.\n"), - 'D/gamma' : Item("This is the file 'gamma'.\n"), - 'D/H' : Item(), - 'D/H/chi' : Item("This is the file 'chi'.\n"), - 'D/H/psi' : Item("New content"), - 'D/H/omega' : Item("New content"), - }) - expected_skip = wc.State('.', { }) - svntest.actions.run_and_verify_merge(A_COPY2_path, None, None, - sbox.repo_url + '/A', None, - expected_output, - expected_mergeinfo_output, - expected_elision_output, - expected_disk, - expected_status, - expected_skip, - None, None, None, None, - None, 1, False) - -#---------------------------------------------------------------------- # A test for issue #3976 'record-only merges which add new subtree mergeinfo # don't record mergeinfo describing merge'. @Issue(3976) @@ -17034,12 +17122,14 @@ def record_only_merge_adds_new_subtree_mergeinfo(sbox): "record only merge adds new subtree mergeinfo" sbox.build() + os.chdir(sbox.wc_dir) + sbox.wc_dir = '' wc_dir = sbox.wc_dir wc_disk, wc_status = set_up_branch(sbox) - psi_path = os.path.join(wc_dir, 'A', 'D', 'H', 'psi') - psi_COPY_path = os.path.join(wc_dir, 'A_COPY', 'D', 'H', 'psi') - H_COPY2_path = os.path.join(wc_dir, 'A_COPY_2', 'D', 'H') + psi_path = sbox.ospath('A/D/H/psi') + psi_COPY_path = sbox.ospath('A_COPY/D/H/psi') + H_COPY2_path = sbox.ospath('A_COPY_2/D/H') # r7 - Copy ^/A_COPY to ^/A_COPY_2 svntest.actions.run_and_verify_svn(None, None, [], @@ -17111,6 +17201,374 @@ def record_only_merge_adds_new_subtree_mergeinfo(sbox): None, 1, False) #---------------------------------------------------------------------- +# Setup helper for issue #4056 and issue #4057 tests. +def noninheritable_mergeinfo_test_set_up(sbox): + '''Starting with standard greek tree, copy 'A' to 'branch' in r2 and + then made a file edit to A/B/lambda in r3. + Return (expected_output, expected_mergeinfo_output, expected_elision_output, + expected_status, expected_disk, expected_skip) for a merge of + r3 from ^/A/B to branch/B.''' + + sbox.build() + wc_dir = sbox.wc_dir + + lambda_path = sbox.ospath('A/B/lambda') + B_branch_path = sbox.ospath('branch/B') + + # r2 - Branch ^/A to ^/branch. + svntest.main.run_svn(None, 'copy', sbox.repo_url + '/A', + sbox.repo_url + '/branch', '-m', 'make a branch') + + # r3 - Make an edit to A/B/lambda. + svntest.main.file_write(lambda_path, "trunk edit.\n") + svntest.main.run_svn(None, 'commit', '-m', 'file edit', wc_dir) + svntest.main.run_svn(None, 'up', wc_dir) + + expected_output = wc.State(B_branch_path, { + 'lambda' : Item(status='U '), + }) + expected_mergeinfo_output = wc.State(B_branch_path, { + '' : Item(status=' U'), + 'lambda' : Item(status=' U'), + }) + expected_elision_output = wc.State(B_branch_path, { + 'lambda' : Item(status=' U'), + }) + expected_status = wc.State(B_branch_path, { + '' : Item(status=' M'), + 'lambda' : Item(status='M '), + 'E' : Item(status=' '), + 'E/alpha' : Item(status=' '), + 'E/beta' : Item(status=' '), + 'F' : Item(status=' '), + }) + expected_status.tweak(wc_rev='3') + expected_disk = wc.State('', { + '' : Item(props={SVN_PROP_MERGEINFO : '/A/B:3'}), + 'lambda' : Item("trunk edit.\n"), + 'E' : Item(), + 'E/alpha' : Item("This is the file 'alpha'.\n"), + 'E/beta' : Item("This is the file 'beta'.\n"), + 'F' : Item(), + }) + expected_skip = wc.State(B_branch_path, {}) + + return expected_output, expected_mergeinfo_output, expected_elision_output, \ + expected_status, expected_disk, expected_skip + + +#---------------------------------------------------------------------- +# Test for issue #4056 "don't record non-inheritable mergeinfo if missing +# subtrees are not touched by the full-depth diff". +@Issue(4056) +@SkipUnless(server_has_mergeinfo) +def unnecessary_noninheritable_mergeinfo_missing_subtrees(sbox): + "missing subtrees untouched by infinite depth merge" + + B_branch_path = sbox.ospath('branch/B') + + # Setup a simple branch to which + expected_output, expected_mergeinfo_output, expected_elision_output, \ + expected_status, expected_disk, expected_skip = \ + noninheritable_mergeinfo_test_set_up(sbox) + + # Create a shallow merge target; set depth of branch/B to files. + svntest.main.run_svn(None, 'up', '--set-depth=files', B_branch_path) + expected_status.remove('E', 'E/alpha', 'E/beta', 'F') + expected_disk.remove('E', 'E/alpha', 'E/beta', 'F') + + # Merge r3 from ^/A/B to branch/B + # + # Merge is smart enough to realize that despite the shallow merge target, + # the diff can only affect branch/B/lambda, which is still present, so there + # is no need to record non-inheritable mergeinfo on the target + # or any subtree mergeinfo whatsoever: + # + # >svn pg svn:mergeinfo -vR + # Properties on 'branch\B': + # svn:mergeinfo + # /A/B:3 <-- Nothing was skipped, so doesn't need + # to be non-inheritable. + svntest.actions.run_and_verify_merge(B_branch_path, + '2', '3', + sbox.repo_url + '/A/B', None, + expected_output, + expected_mergeinfo_output, + expected_elision_output, + expected_disk, + expected_status, + expected_skip, + None, None, None, None, None, 1, 1, + B_branch_path) + +#---------------------------------------------------------------------- +# Test for issue #4057 "don't record non-inheritable mergeinfo in shallow +# merge if entire diff is within requested depth". +@Issue(4057) +@SkipUnless(server_has_mergeinfo) +def unnecessary_noninheritable_mergeinfo_shallow_merge(sbox): + "shallow merge reaches all necessary subtrees" + + B_branch_path = sbox.ospath('branch/B') + E_path = sbox.ospath('A/B/E') + + # Setup a simple branch to which + expected_output, expected_mergeinfo_output, expected_elision_output, \ + expected_status, expected_disk, expected_skip = \ + noninheritable_mergeinfo_test_set_up(sbox) + + # Merge r3 from ^/A/B to branch/B at operational depth=files + # + # Previously this failed because merge wasn't smart enough to + # realize that despite being a shallow merge, the diff can + # only affect branch/B/lambda, which is within the specified + # depth, so there is no need to record non-inheritable mergeinfo + # or subtree mergeinfo: + # + # >svn pg svn:mergeinfo -vR + # Properties on 'branch\B': + # svn:mergeinfo + # /A/B:3* <-- Should be inheritable + # Properties on 'branch\B\lambda': + # svn:mergeinfo + # /A/B/lambda:3 <-- Not necessary + expected_skip = wc.State(B_branch_path, {}) + svntest.actions.run_and_verify_merge(B_branch_path, '2', '3', + sbox.repo_url + '/A/B', None, + expected_output, + expected_mergeinfo_output, + expected_elision_output, + expected_disk, + expected_status, + expected_skip, + None, None, None, None, None, 1, 1, + '--depth', 'files', B_branch_path) + + # Revert the merge and then make a prop change to A/B/E in r4. + svntest.actions.run_and_verify_svn(None, None, [], + 'revert', '--recursive', sbox.wc_dir) + svntest.actions.run_and_verify_svn(None, + ["property 'prop:name' set on '" + + E_path + "'\n"], [], 'ps', + 'prop:name', 'propval', E_path) + svntest.actions.run_and_verify_svn(None, None, [], + 'ci', '-m', 'A new property on a dir', + sbox.wc_dir) + svntest.actions.run_and_verify_svn(None, None, [], + 'up', sbox.wc_dir) + + # Merge r4 from ^/A/B to branch/B at operational depth=immediates + # + # Previously this failed because the mergetracking logic didn't realize + # that despite being a shallow merge, the diff only affected branch/B/E, + # which was within the specified depth, so there was no need to record + # non-inheritable mergeinfo or subtree mergeinfo: + # + # >svn pg svn:mergeinfo -vR + # Properties on 'branch\B': + # svn:mergeinfo + # /A/B:4* <-- Should be inheritable + # Properties on 'branch\B\E': + # svn:mergeinfo + # /A/B/E:4 <-- Not necessary + expected_output = wc.State(B_branch_path, { + 'E' : Item(status=' U'), + }) + expected_mergeinfo_output = wc.State(B_branch_path, { + '' : Item(status=' U'), + 'E' : Item(status=' U'), + }) + expected_elision_output = wc.State(B_branch_path, { + 'E' : Item(status=' U'), + }) + expected_status = wc.State(B_branch_path, { + '' : Item(status=' M'), + 'lambda' : Item(status=' '), + 'E' : Item(status=' M'), + 'E/alpha' : Item(status=' '), + 'E/beta' : Item(status=' '), + 'F' : Item(status=' '), + }) + expected_status.tweak(wc_rev='4') + expected_disk = wc.State('', { + '' : Item(props={SVN_PROP_MERGEINFO : '/A/B:4'}), + 'lambda' : Item("This is the file 'lambda'.\n"), + 'E' : Item(props={'prop:name' : 'propval'}), + 'E/alpha' : Item("This is the file 'alpha'.\n"), + 'E/beta' : Item("This is the file 'beta'.\n"), + 'F' : Item(), + }) + svntest.actions.run_and_verify_merge(B_branch_path, '3', '4', + sbox.repo_url + '/A/B', None, + expected_output, + expected_mergeinfo_output, + expected_elision_output, + expected_disk, + expected_status, + expected_skip, + None, None, None, None, None, 1, 1, + '--depth', 'immediates', B_branch_path) + +#---------------------------------------------------------------------- +# Test for issue #4132, "merge of replaced source asserts". +# The original use-case is the following merges, which both asserted: +# svn merge -cr1295005 ^/subversion/trunk@1295000 ../src +# svn merge -cr1295004 ^/subversion/trunk/@r1295004 ../src +@Issue(4132) +def svnmucc_abuse_1(sbox): + "svnmucc: merge a replacement" + + sbox.build() + os.chdir(sbox.wc_dir) + sbox.wc_dir = '' + wc_dir = sbox.wc_dir + + ## Using A/ as our trunk, since one cannot replace the root. + + ## r2: open a branch + sbox.simple_repo_copy('A', 'A_COPY') + + ## r3: padding (to make the revnums-mod-10 match) + sbox.simple_repo_copy('iota', 'padding') + + ## r4: trunk: accidental change + sbox.simple_append('A/mu', 'accidental change') + sbox.simple_commit() + + ## r5: fail to revert it + svntest.actions.run_and_verify_svnmucc(None, None, [], + '-m', 'r5', + '-U', sbox.repo_url, + 'rm', 'A', + 'cp', 'HEAD', 'A', 'A') + + ## r6: really revert it + svntest.actions.run_and_verify_svnmucc(None, None, [], + '-m', 'r6', + '-U', sbox.repo_url, + 'rm', 'A', + 'cp', '3', 'A', 'A') + + ## Attempt to merge that. + # This used to assert: + # --- Recording mergeinfo for merge of r5 into \ + # 'svn-test-work/working_copies/merge_tests-125/A_COPY': + # subversion/libsvn_subr/mergeinfo.c:1172: (apr_err=235000) + # svn: E235000: In file 'subversion/libsvn_subr/mergeinfo.c' \ + # line 1172: assertion failed (IS_VALID_FORWARD_RANGE(first)) + # + # Then, prior to the fix asserted this way: + # + # >svn merge -c5 ^/A@r5 A_COPY + # subversion\libsvn_client\merge.c:4871: (apr_err=235000) + # svn: E235000: In file 'subversion\libsvn_client\merge.c' + # line 4871: assertion failed (*gap_start < *gap_end) + sbox.simple_update() + svntest.main.run_svn(None, 'merge', '-c', 'r5', '^/A@r5', + sbox.ospath('A_COPY')) + +#---------------------------------------------------------------------- +# Test for issue #4138 'replacement in merge source not notified correctly'. +@SkipUnless(server_has_mergeinfo) +@Issue(4138) +def merge_source_with_replacement(sbox): + "replacement in merge source not notified correctly" + + sbox.build() + os.chdir(sbox.wc_dir) + sbox.wc_dir = '' + wc_dir = sbox.wc_dir + + # Some paths we'll care about. + A_path = sbox.ospath('A') + omega_path = sbox.ospath('A/D/H/omega') + A_COPY_path = sbox.ospath('A_COPY') + beta_COPY_path = sbox.ospath('A_COPY/B/E/beta') + psi_COPY_path = sbox.ospath('A_COPY/D/H/psi') + rho_COPY_path = sbox.ospath('A_COPY/D/G/rho') + omega_COPY_path = sbox.ospath('A_COPY/D/H/omega') + + # branch A@1 to A_COPY in r2, then make a few edits under A in r3-6: + wc_disk, wc_status = set_up_branch(sbox) + + # r7 Delete A, replace it with A@5, effectively reverting the change + # made to A/D/H/omega in r6: + svntest.main.run_svn(None, 'up', wc_dir) + svntest.main.run_svn(None, 'del', A_path) + svntest.main.run_svn(None, 'copy', sbox.repo_url + '/A@5', A_path) + svntest.main.run_svn(None, 'ci', '-m', + 'Replace A with older version of itself', wc_dir) + + # r8: Make an edit to A/D/H/omega: + svntest.main.file_write(omega_path, "New content for 'omega'.\n") + svntest.main.run_svn(None, 'ci', '-m', 'file edit', wc_dir) + + # Update and sync merge ^/A to A_COPY. + # + # text text text text text + # edit edit edit edit edit + # psi rho beta omega omega + # A@r1---r3----r4----r5----r6---X r7---r8---------> + # | | ^ | + # | v | | + # | +---replacement---+ | + # copy | + # | sync-merge + # | | + # v v + # r2---A_COPY-----------------------------------------> + svntest.main.run_svn(None, 'up', wc_dir) + # This test previously failed because the merge notifications make it look + # like r6 from ^/A was merged and recorded: + # + # >svn merge ^^/A A_COPY + # --- Merging r2 through r5 into 'A_COPY': + # U A_COPY\B\E\beta + # U A_COPY\D\G\rho + # U A_COPY\D\H\psi + # --- Recording mergeinfo for merge of r2 through r5 into 'A_COPY': + # U A_COPY + # --- Merging r6 through r8 into 'A_COPY': + # U A_COPY\D\H\omega + # --- Recording mergeinfo for merge of r6 through r8 into 'A_COPY': + # G A_COPY + expected_output = expected_merge_output( + [[2,5],[7,8]], + ['U ' + beta_COPY_path + '\n', + 'U ' + rho_COPY_path + '\n', + 'U ' + omega_COPY_path + '\n', + 'U ' + psi_COPY_path + '\n', + ' U ' + A_COPY_path + '\n', + ' G ' + A_COPY_path + '\n',]) + svntest.actions.run_and_verify_svn(None, expected_output, [], + 'merge', sbox.repo_url + '/A', + A_COPY_path) + + # Misleading notifications are one thing, incorrect mergeinfo is quite + # another. + svntest.actions.run_and_verify_svn(None, + [A_COPY_path + ' - /A:2-5,7-8\n'], + [], 'pg', SVN_PROP_MERGEINFO, + '-R', A_COPY_path) + + # Commit the above merge and then reverse merge it. Again r6 is not + # being merged and should not be part of the notifications. + sbox.simple_commit() + sbox.simple_update() + expected_output = expected_merge_output( + [[5,2],[8,7]], + ['U ' + beta_COPY_path + '\n', + 'U ' + rho_COPY_path + '\n', + 'U ' + omega_COPY_path + '\n', + 'U ' + psi_COPY_path + '\n', + ' U ' + A_COPY_path + '\n', + ' G ' + A_COPY_path + '\n',], + elides=True) + svntest.actions.run_and_verify_svn(None, expected_output, [], + 'merge', sbox.repo_url + '/A', + A_COPY_path, '-r8:1') + +#---------------------------------------------------------------------- # Test for issue #4144 'Reverse merge with replace in source applies # diffs in forward order'. @SkipUnless(server_has_mergeinfo) @@ -17119,18 +17577,20 @@ def reverse_merge_with_rename(sbox): "reverse merge applies revs in reverse order" sbox.build() + os.chdir(sbox.wc_dir) + sbox.wc_dir = '' wc_dir = sbox.wc_dir # Some paths we'll care about. - A_path = os.path.join(sbox.wc_dir, 'A') - omega_path = os.path.join(sbox.wc_dir, 'trunk', 'D', 'H', 'omega') - A_COPY_path = os.path.join(sbox.wc_dir, 'A_COPY') - beta_COPY_path = os.path.join(sbox.wc_dir, 'A_COPY', 'B', 'E', 'beta') - psi_COPY_path = os.path.join(sbox.wc_dir, 'A_COPY', 'D', 'H', 'psi') - rho_COPY_path = os.path.join(sbox.wc_dir, 'A_COPY', 'D', 'G', 'rho') - omega_COPY_path = os.path.join(sbox.wc_dir, 'A_COPY', 'D', 'H', 'omega') - - # branch A@1 to A_COPY in r2, then make a few edits under A in r3-6: + A_path = sbox.ospath('A') + omega_path = sbox.ospath('trunk/D/H/omega') + A_COPY_path = sbox.ospath('A_COPY') + beta_COPY_path = sbox.ospath('A_COPY/B/E/beta') + psi_COPY_path = sbox.ospath('A_COPY/D/H/psi') + rho_COPY_path = sbox.ospath('A_COPY/D/G/rho') + omega_COPY_path = sbox.ospath('A_COPY/D/H/omega') + + # branch A@1 to A_COPY in r2, then make a few edits under A in r3-6: wc_disk, wc_status = set_up_branch(sbox) # r7 - Rename ^/A to ^/trunk. @@ -17201,15 +17661,17 @@ def reverse_merge_with_rename(sbox): def merge_adds_then_deletes_subtree(sbox): "merge adds then deletes subtree" - # Some paths we'll care about. - A_path = os.path.join(sbox.wc_dir, 'A') - nu_path = os.path.join(sbox.wc_dir, 'A', 'C', 'nu') - C_branch_path = os.path.join(sbox.wc_dir, 'branch', 'C') - nu_branch_path = os.path.join(sbox.wc_dir, 'branch', 'C', 'nu') - sbox.build() + os.chdir(sbox.wc_dir) + sbox.wc_dir = '' wc_dir = sbox.wc_dir + # Some paths we'll care about. + A_path = sbox.ospath('A') + nu_path = sbox.ospath('A/C/nu') + C_branch_path = sbox.ospath('branch/C') + nu_branch_path = sbox.ospath('branch/C/nu') + # Make a branch. svntest.actions.run_and_verify_svn(None, None, [], 'copy', sbox.repo_url + '/A', @@ -17271,19 +17733,21 @@ def merge_adds_then_deletes_subtree(sbox): def merge_with_added_subtrees_with_mergeinfo(sbox): "merge with added subtrees with mergeinfo" - # Some paths we'll care about. - A_path = os.path.join(sbox.wc_dir, 'A') - Y_path = os.path.join(sbox.wc_dir, 'A', 'C', 'X', 'Y') - Z_path = os.path.join(sbox.wc_dir, 'A', 'C', 'X', 'Y', 'Z') - nu_path = os.path.join(sbox.wc_dir, 'A', 'C', 'X', 'Y', 'Z', 'nu') - A_COPY_path = os.path.join(sbox.wc_dir, 'A_COPY') - Y_COPY_path = os.path.join(sbox.wc_dir, 'A_COPY', 'C', 'X', 'Y') - W_COPY_path = os.path.join(sbox.wc_dir, 'A_COPY', 'C', 'X', 'Y', 'Z', 'W') - A_COPY2_path = os.path.join(sbox.wc_dir, 'A_COPY_2') - sbox.build() + os.chdir(sbox.wc_dir) + sbox.wc_dir = '' wc_dir = sbox.wc_dir + # Some paths we'll care about. + A_path = sbox.ospath('A') + Y_path = sbox.ospath('A/C/X/Y') + Z_path = sbox.ospath('A/C/X/Y/Z') + nu_path = sbox.ospath('A/C/X/Y/Z/nu') + A_COPY_path = sbox.ospath('A_COPY') + Y_COPY_path = sbox.ospath('A_COPY/C/X/Y') + W_COPY_path = sbox.ospath('A_COPY/C/X/Y/Z/W') + A_COPY2_path = sbox.ospath('A_COPY_2') + # Make two branches of ^/A and then make a few edits under A in r4-7: wc_disk, wc_status = set_up_branch(sbox, nbr_of_branches=2) @@ -17341,7 +17805,7 @@ def merge_with_added_subtrees_with_mergeinfo(sbox): # vvvvvvvvvvvvvvvvvvvv # U A_COPY_2\C\X\Y\Z # ^^^^^^^^^^^^^^^^^^^^ - # + # # >svn pl -vR A_COPY_2 # Properties on 'A_COPY_2': # svn:mergeinfo @@ -17370,8 +17834,8 @@ def merge_with_added_subtrees_with_mergeinfo(sbox): }) expected_mergeinfo_output = wc.State(A_COPY2_path, { '' : Item(status=' U'), - 'C/X/Y' : Item(status=' G'), # Added with explicit mergeinfo so mergeinfo - }) # describing the merge shows as mer'G'ed. + 'C/X/Y' : Item(status=' U'), # Added with explicit mergeinfo + }) expected_elision_output = wc.State(A_COPY2_path, { }) expected_status = wc.State(A_COPY2_path, { @@ -17438,6 +17902,1400 @@ def merge_with_added_subtrees_with_mergeinfo(sbox): None, None, None, None, None, 1, 0) +#---------------------------------------------------------------------- +@SkipUnless(server_has_mergeinfo) +def merge_with_externals_with_mergeinfo(sbox): + "merge with externals with mergeinfo" + + sbox.build() + os.chdir(sbox.wc_dir) + sbox.wc_dir = '' + wc_dir = sbox.wc_dir + + # Some paths we'll care about. + A_path = sbox.ospath('A') + A_COPY_path = sbox.ospath('A_COPY') + file_external_path = sbox.ospath('A/file-external') + mu_COPY_path = sbox.ospath('A_COPY/mu') + mu_path = sbox.ospath('A/mu') + + # Make a branch of ^/A and then make a few edits under A in r3-6: + wc_disk, wc_status = set_up_branch(sbox) + + svntest.main.file_write(mu_COPY_path, "branch edit") + svntest.actions.run_and_verify_svn(None, None, [], 'ci', '-m', + 'file edit on the branch', wc_dir) + svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir) + + # Create a file external under 'A' and set some bogus mergeinfo + # on it (the fact that this mergeinfo is bogus has no bearing on + # this test). + svntest.actions.run_and_verify_svn(None, None, [], 'propset', + 'svn:externals', + '^/iota file-external', A_path) + svntest.actions.run_and_verify_svn(None, None, [], 'ci', '-m', + 'set file external', wc_dir) + svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir) + svntest.actions.run_and_verify_svn(None, None, [], 'ps', SVN_PROP_MERGEINFO, + "/bogus-mergeinfo:5", file_external_path) + svntest.actions.run_and_verify_svn(None, None, [], 'ci', '-m', + 'set mergeinfo on file external', + file_external_path) + + # Sync merge ^/A to A_COPY and then reintegrate A_COPY back to A. + svntest.actions.run_and_verify_svn(None, None, [], 'merge', + sbox.repo_url + '/A', A_COPY_path) + svntest.actions.run_and_verify_svn(None, None, [], 'ci', '-m', + 'sync merge', wc_dir) + # This was segfaulting, see + # http://svn.haxx.se/dev/archive-2012-10/0364.shtml + svntest.actions.run_and_verify_svn( + None, + expected_merge_output(None, + ['U ' + mu_path + '\n', + ' U ' + A_path + '\n'], + two_url=True), + [], 'merge', '--reintegrate', sbox.repo_url + '/A_COPY', + A_path) + +#---------------------------------------------------------------------- +# Test merging 'binary' files with keyword expansion enabled. +# Tests issue #4221 'Trivial merge of a binary file with svn:keywords +# raises a conflict', among other cases. +@SkipUnless(server_has_mergeinfo) +@Issue(4221) +def merge_binary_file_with_keywords(sbox): + "merge binary file with keywords" + + sbox.build() + os.chdir(sbox.wc_dir) + sbox.wc_dir = '' + + # Some binary files, and some binary files that will become text files. + # 'mod_src' means a content change on the branch (the merge source); + # 'mod_tgt' means a content change on the original (the merge target); + # 'to_txt' means svn:mime-type removed on the branch (the merge source). + file_mod_both = 'A/B/E/alpha' + file_mod_src = 'A/D/G/pi' + file_mod_tgt = 'A/D/G/rho' + file_mod_none = 'A/D/G/tau' + file_mod_both_to_txt = 'A/B/E/beta' + file_mod_src_to_txt = 'A/D/H/chi' + file_mod_tgt_to_txt = 'A/D/H/psi' + file_mod_none_to_txt = 'A/D/H/omega' + files_bin = [ file_mod_both, file_mod_src, file_mod_tgt, file_mod_none ] + files_txt = [ file_mod_both_to_txt, file_mod_src_to_txt, + file_mod_tgt_to_txt, file_mod_none_to_txt ] + files = files_bin + files_txt + + # make some 'binary' files with keyword expansion enabled + for f in files: + svntest.main.file_append(sbox.ospath(f), "With $Revision: $ keyword.\n") + svntest.main.run_svn(binary_mime_type_on_text_file_warning, + 'propset', 'svn:mime-type', + 'application/octet-stream', sbox.ospath(f)) + sbox.simple_propset('svn:keywords', 'Revision', f) + sbox.simple_commit() + + # branch the files + sbox.simple_repo_copy('A', 'A2') + sbox.simple_update() + + # Modify the branched (source) and/or original (target) versions. Remove + # the svn:mime-type from the 'to_txt' files on the branch. + # The original bug in issue #4221 gave a conflict if we modified either + # version or neither (using a single-file merge test case). + for f in [ file_mod_both, file_mod_both_to_txt, + file_mod_src, file_mod_src_to_txt ]: + f_branch = 'A2' + f[1:] + svntest.main.file_append(sbox.ospath(f_branch), "Incoming mod.\n") + for f in [ file_mod_both, file_mod_both_to_txt, + file_mod_tgt, file_mod_tgt_to_txt ]: + svntest.main.file_append(sbox.ospath(f), "Mod on merge target.\n") + for f in files_txt: + f_branch = 'A2' + f[1:] + sbox.simple_propdel('svn:mime-type', f_branch) + sbox.simple_commit() + sbox.simple_update() + + # merge back + svntest.actions.run_and_verify_svn( + None, + expected_merge_output([[3,4]], + ['C ' + sbox.ospath(file_mod_both) + '\n', + 'U ' + sbox.ospath(file_mod_src) + '\n', + #' ' + sbox.ospath(file_mod_tgt) + '\n', + #' ' + sbox.ospath(file_mod_none) + '\n', + 'CU ' + sbox.ospath(file_mod_both_to_txt) + '\n', + 'UU ' + sbox.ospath(file_mod_src_to_txt) + '\n', + ' U ' + sbox.ospath(file_mod_tgt_to_txt) + '\n', + ' U ' + sbox.ospath(file_mod_none_to_txt) + '\n', + ' U A\n'], + text_conflicts=2), + [], 'merge', '^/A2', 'A') + +#---------------------------------------------------------------------- +# Test for issue #4155 'Merge conflict text of expanded keyword incorrect +# when svn:keyword property value removed'. Failed in 1.7.0 through 1.7.8. +@SkipUnless(server_has_mergeinfo) +@Issue(4155) +def merge_conflict_when_keywords_removed(sbox): + "merge conflict when keywords removed" + + sbox.build() + os.chdir(sbox.wc_dir) + sbox.wc_dir = '' + + # make a file with keyword expansion enabled + svntest.main.file_write('A/keyfile', "$Date$ $Revision$\n") + sbox.simple_add('A/keyfile') + sbox.simple_propset('svn:keywords', 'Date Revision', 'A/keyfile') + sbox.simple_commit() + sbox.simple_update() + + # branch the file + sbox.simple_repo_copy('A', 'A2') + sbox.simple_update() + + # + svntest.main.file_append('A/keyfile', " some changes\n") + sbox.simple_commit() + + # sync merge + svntest.actions.run_and_verify_svn( + None, + expected_merge_output([[3,4]], + ['U '+ sbox.ospath('A2/keyfile') + '\n', + ' U A2\n']), + [], 'merge', '^/A', 'A2') + sbox.simple_commit() + sbox.simple_update() + + # modify the original version: disable those KW & enable 'Id' + sbox.simple_propset('svn:keywords', 'Id', 'A/keyfile') + svntest.main.file_append('A/keyfile', "$Id$\n") + sbox.simple_commit() + + # sync merge again + svntest.actions.run_and_verify_svn( + None, + expected_merge_output([[5,6]], + ['UU ' + sbox.ospath('A2/keyfile') + '\n', + ' U A2\n']), + [], 'merge', '--accept=postpone', '^/A', 'A2') + +@SkipUnless(server_has_mergeinfo) +@Issue(4139, 3274, 3503) +def merge_target_selection(sbox): + "merge target selection handling" + + sbox.build() + os.chdir(sbox.wc_dir) + sbox.wc_dir = '' + + # r2 + sbox.simple_mkdir('dir') + sbox.simple_add_text('\1\2\3\4\5', 'dir/binary-file') + sbox.simple_add_text('abcde', 'dir/text-file') + sbox.simple_commit() + + # r3 + sbox.simple_copy('dir', 'branch') + sbox.simple_commit() + + # r4 + svntest.main.file_write(sbox.ospath('dir/binary-file'), + '\9\8\7\6\5\4\3\2\1') + sbox.simple_commit() + + sbox.simple_update() + + os.chdir(sbox.ospath('branch')) + + # Merge the directory (no target) + expected_output = [ + '--- Merging r4 into \'.\':\n', + 'U binary-file\n', + '--- Recording mergeinfo for merge of r4 into \'.\':\n', + ' U .\n', + ] + svntest.actions.run_and_verify_svn(None, expected_output, [], + 'merge', '^/dir', '-c', '4') + + svntest.main.run_svn(None, 'revert', '-R', '.') + + # Merge the file (no target) + expected_output = [ + '--- Merging r4 into \'binary-file\':\n', + 'U binary-file\n', + '--- Recording mergeinfo for merge of r4 into \'binary-file\':\n', + ' U binary-file\n', + ] + svntest.actions.run_and_verify_svn(None, expected_output, [], + 'merge', '^/dir/binary-file', '-c', '4') + + svntest.main.run_svn(None, 'revert', '-R', '.') + + # Merge the directory (explicit target) + expected_output = [ + '--- Merging r4 into \'.\':\n', + 'U binary-file\n', + '--- Recording mergeinfo for merge of r4 into \'.\':\n', + ' U .\n', + ] + svntest.actions.run_and_verify_svn(None, expected_output, [], + 'merge', '^/dir', '-c', '4', '.') + + svntest.main.run_svn(None, 'revert', '-R', '.') + + # Merge the file (explicit target) + expected_output = [ + '--- Merging r4 into \'binary-file\':\n', + 'U binary-file\n', + '--- Recording mergeinfo for merge of r4 into \'binary-file\':\n', + ' U binary-file\n', + ] + svntest.actions.run_and_verify_svn(None, expected_output, [], + 'merge', '^/dir/binary-file', '-c', '4', 'binary-file') + + svntest.main.run_svn(None, 'revert', '-R', '.') + + # Merge the file (wrong target) + expected_output = [ + '--- Merging r4 into \'.\':\n', + ' C .\n', + '--- Recording mergeinfo for merge of r4 into \'.\':\n', + ' U .\n', + ] + svntest.main.summary_of_conflicts(tree_conflicts=1) + svntest.actions.run_and_verify_svn(None, expected_output, [], + 'merge', '^/dir/binary-file', '-c', '4', '.') + + svntest.main.run_svn(None, 'revert', '-R', '.') + + # Merge the dir (wrong target) + expected_output = [ + '--- Merging r4 into \'binary-file\':\n', + ' C %s\n' % os.path.join('binary-file'), + '--- Recording mergeinfo for merge of r4 into \'binary-file\':\n', + ' U binary-file\n', + ] + svntest.main.summary_of_conflicts(tree_conflicts=1) + svntest.actions.run_and_verify_svn(None, expected_output, [], + 'merge', '^/dir', '-c', '4', 'binary-file') + +@Issue(3405) +def merge_properties_on_adds(sbox): + "merged directory properties are added" + + sbox.build() + os.chdir(sbox.wc_dir) + sbox.wc_dir = '' + + sbox.simple_copy('A/D/G', 'G') + + sbox.simple_mkdir('A/D/G/M') + sbox.simple_mkdir('A/D/G/M/N') + sbox.simple_add_text('QQ', 'A/D/G/file', 'A/D/G/M/file') + sbox.simple_propset('key', 'value', + 'A/D/G/M', 'A/D/G/file', 'A/D/G/M/N', 'A/D/G/M/file') + sbox.simple_commit() + sbox.simple_update() + + svntest.actions.run_and_verify_svn(None, None, [], + 'merge', '^/A/D/G', sbox.ospath('G')) + + expected_output = svntest.verify.UnorderedOutput([ + 'Properties on \'%s\':\n' % sbox.ospath('G'), + ' svn:mergeinfo\n', + 'Properties on \'%s\':\n' % sbox.ospath('G/M'), + ' key\n', + 'Properties on \'%s\':\n' % sbox.ospath('G/file'), + ' key\n', + 'Properties on \'%s\':\n' % sbox.ospath('G/M/N'), + ' key\n', + 'Properties on \'%s\':\n' % sbox.ospath('G/M/file'), + ' key\n', + ]) + svntest.actions.run_and_verify_svn(None, expected_output, [], + 'proplist', '-R', sbox.ospath('G')) + + expected_output = svntest.verify.UnorderedOutput([ + 'Properties on \'%s\':\n' % sbox.ospath('G/M'), + ' key\n', + 'Properties on \'%s\':\n' % sbox.ospath('G/file'), + ' key\n', + 'Properties on \'%s\':\n' % sbox.ospath('G/M/N'), + ' key\n', + 'Properties on \'%s\':\n' % sbox.ospath('G/M/file'), + ' key\n', + ]) + + # I merged the tree, which should include history but only the files have + # the properties stored in PRISTINE. All directories have the properties + # as local changes in ACTUAL. + svntest.actions.run_and_verify_svn(None, expected_output, [], + 'proplist', '-R', sbox.ospath('G'), + '-r', 'BASE') + + # Note that this is not a regression. This has been the case since 1.0. + # ### We just made status, update and merge handle this without users + # ### knowing about this limitation. + + # ### My guess is that the base merge support on svn_wc_merge_props() + # ### was originally designed to resolve this problem, but I can't + # ### find a released version where this was actually implemented. + + # For fun, also check the status: 'svn status' suppresses the M from AM. + + # G = sbox.ospath('G') + # + # expected_status = wc.State('G', { + # '' : Item(status=' M', wc_rev='2'), + # 'pi' : Item(status=' ', wc_rev='2'), + # 'tau' : Item(status=' ', wc_rev='2'), + # 'file' : Item(status='A ', copied='+', wc_rev='-'), # Copied, no changes + # 'M' : Item(status='A ', copied='+', wc_rev='-'), # Copied, changes + # 'M/file' : Item(status=' ', copied='+', wc_rev='-'), # Copied, no changes + # 'M/N' : Item(status=' M', copied='+', wc_rev='-'), # Local changes + # 'rho' : Item(status=' ', wc_rev='2'), + # }) + # svntest.actions.run_and_verify_status(G, expected_status) + + +# ====================================================================== +# Functions for parsing mergeinfo + +def parse_changes_list(changes_string): + """Parse a string containing a list of revision numbers in the form + of the '--change' command-line argument (e.g. '1,3,-5,7-10'). + Return a list of elements of the form [[1], [3], [-5], [7,10]]. + """ + rev_ranges = [] + for rr in changes_string.split(','): + if '-' in rr[1:]: + revs = rr.split('-') + rev_ranges.append([int(revs[0]), int(revs[1])]) + else: + rev_ranges.append([int(rr)]) + return rev_ranges + +def parse_rev_args(arg_list): + """Return a list of [rX:rY] or [rZ] elements representing ARG_LIST + whose elements are strings in the form '-rX:Y' or '-cZ,X-Y,...'. + """ + rev_ranges = [] + for arg in arg_list: + kind = arg[:2] + val = arg[2:] + if kind == '-r': + if ':' in val: + revs = map(int, val.split(':')) + if revs[0] < revs[1]: + rev_ranges.append([revs[0] + 1, revs[1]]) + else: + rev_ranges.append([revs[0], revs[1] + 1]) + else: + rev_ranges.append([int(val)]) + elif kind == '-c': + rev_ranges.extend(parse_changes_list(val)) + else: + raise ValueError("revision arg '" + arg + "' in '" + arg_list + + "' does not start with -r or -c") + return rev_ranges + +class RangeList(list): + """Represents of a list of revision ranges, as a list of one- or + two-element lists, each of the form [X] meaning "--revision (X-1):X" + or [X,Y] meaning "--revision (X-1):Y". + """ + def __init__(self, arg): + """ + """ + self.as_given = arg + if isinstance(arg, str): + list.__init__(self, parse_changes_list(arg)) + elif isinstance(arg, list): + list.__init__(self, parse_rev_args(arg)) + else: + raise ValueError("RangeList needs a string or a list, not '" + str(arg) + "'") + +def expected_merge_output2(tgt_ospath, + recorded_ranges, + merged_ranges=None, + prop_conflicts=0, + prop_resolved=0): + """Return an ExpectedOutput instance corresponding to the expected + output of a merge into TGT_OSPATH, with one 'recording + mergeinfo...' notification per specified revision range in + RECORDED_RANGES and one 'merging...' notification per revision + range in MERGED_RANGES. + + RECORDED_RANGES is a mergeinfo-string or a RangeList. + + MERGED_RANGES is a list of mergeinfo-strings or a list of + RangeLists. If None, it means [[r] for r in RECORDED_RANGES]. + """ + # Convert RECORDED_RANGES to a RangeList. + if isinstance(recorded_ranges, str): + recorded_ranges = RangeList(recorded_ranges) + # Convert MERGED_RANGES to a list of RangeLists. + if merged_ranges is None: + merged_ranges = [[r] for r in recorded_ranges] + elif len(merged_ranges) > 0 and isinstance(merged_ranges[0], str): + # List of mergeinfo-strings => list of rangelists + merged_ranges = [RangeList(r) for r in merged_ranges] + + status_letters_re = (prop_conflicts or prop_resolved) and ' [UC]' or ' U' + status_letters_mi = ' [UG]' + lines = [] + for i, rr in enumerate(recorded_ranges): + # Merging ... + for sr in merged_ranges[i]: + revstart = sr[0] + revend = len(sr) > 1 and sr[1] or None + lines += [svntest.main.merge_notify_line(revstart, revend, + target=tgt_ospath)] + lines += [status_letters_re + ' ' + re.escape(tgt_ospath) + '\n'] + # Recording mergeinfo ... + revstart = rr[0] + revend = len(rr) > 1 and rr[1] or None + lines += [svntest.main.mergeinfo_notify_line(revstart, revend, + target=tgt_ospath)] + lines += [status_letters_mi + ' ' + re.escape(tgt_ospath) + '\n'] + + # Summary of conflicts + lines += svntest.main.summary_of_conflicts(prop_conflicts=prop_conflicts, + prop_resolved=prop_resolved, + as_regex=True) + + # The 'match_all=False' is because we also expect some + # 'Resolved conflicted state of ...' lines. + return RegexListOutput(lines, match_all=False) + +def expected_out_and_err(tgt_ospath, + recorded_ranges, + merged_ranges=None, + prop_conflicts=0, + prop_resolved=0, + expect_error=True): + """Return a tuple (expected_out, expected_err) giving the expected + output and expected error output for a merge into TGT_OSPATH. See + expected_merge_output2() for details of RECORDED_RANGES and + MERGED_RANGES and PROP_CONFLICTS. EXPECT_ERROR should be true iff + we expect the merge to abort with an error about conflicts being + raised. + """ + expected_out = expected_merge_output2(tgt_ospath, recorded_ranges, + merged_ranges, + prop_conflicts, prop_resolved) + if expect_error: + expected_err = RegexListOutput([ + '^svn: E155015: .* conflicts were produced .* into$', + "^'.*" + re.escape(tgt_ospath) + "' --$", + '^resolve all conflicts .* remaining$', + '^unmerged revisions$'], + match_all=False) + else: + expected_err = [] + + return expected_out, expected_err + +def check_mergeinfo(expected_mergeinfo, tgt_ospath): + """Read the mergeinfo on TGT_OSPATH; verify that it matches + EXPECTED_MERGEINFO (list of lines). + """ + svntest.actions.run_and_verify_svn( + None, expected_mergeinfo, [], 'pg', SVN_PROP_MERGEINFO, tgt_ospath) + +def simple_merge(src_path, tgt_ospath, rev_args): + """Merge from ^/SRC_PATH to TGT_OSPATH using revision arguments REV_ARGS + (list of '-r...' or '-c...' strings); expect a single-target merge + with no conflicts or errors. + """ + rev_ranges = RangeList(rev_args) + + expected_out = expected_merge_output(rev_ranges, + [' U ' + tgt_ospath + '\n', + ' [UG] ' + tgt_ospath + '\n'], + target=tgt_ospath) + src_url = '^/' + src_path + svntest.actions.run_and_verify_svn( + None, expected_out, [], + 'merge', src_url, tgt_ospath, '--accept', 'postpone', *rev_args) + +@SkipUnless(server_has_mergeinfo) +@Issue(4306) +# Test for issue #4306 'multiple editor drive file merges record wrong +# mergeinfo during conflicts' +def conflict_aborted_mergeinfo_described_partial_merge(sbox): + "conflicted split merge can be repeated" + + sbox.build() + os.chdir(sbox.wc_dir) + sbox.wc_dir = '' + + trunk = 'A' + branch = 'A2' + file = 'mu' + dir = 'B' + trunk_file = 'A/mu' + trunk_dir = 'A/B' + + # r2: initial state + for rev in range(4, 11): + sbox.simple_propset('prop-' + str(rev), 'Old pval ' + str(rev), + trunk_file, trunk_dir) + sbox.simple_commit() + + # r3: branch + sbox.simple_copy(trunk, branch) + sbox.simple_commit() + + zero_rev = 3 + + def edit_file_or_dir(path, rev, val): + """Make a local edit to the file at PATH.""" + sbox.simple_propset('prop-' + str(rev), val + ' pval ' + str(rev), path) + + # r4 through r10: simple edits + for rev in range(4, 11): + edit_file_or_dir(trunk_file, rev, 'Edited') + edit_file_or_dir(trunk_dir, rev, 'Edited') + sbox.simple_commit() + + # r14: merge some changes to the branch so that later merges will be split + svntest.actions.run_and_verify_svn(None, None, [], 'merge', '-c5,9', + '^/' + trunk, sbox.ospath(branch), + '--accept', 'theirs-conflict') + sbox.simple_commit() + sbox.simple_update() + + def revert_branch(): + svntest.actions.run_and_verify_svn(None, None, [], 'revert', '-R', + sbox.ospath(branch)) + + def try_merge(relpath, conflict_rev, rev_args, + expected_out_err, expected_mi): + """Revert RELPATH in the branch; make a change that will conflict + with CONFLICT_REV if not None; merge RELPATH in the trunk + to RELPATH in the branch using revision arguments REV_ARGS (list of + '-r...' or '-c...' strings). + + EXPECTED_OUT_ERR_MI is a tuple: (expected_out, expected_err, + expected_mi). EXPECTED_OUT and EXPECTED_ERR are instances of + ExpectedOutput. + + Expect to find mergeinfo EXPECTED_MI if not None. EXPECTED_MI is + a single mergeinfo-string. + """ + src_path = trunk + '/' + relpath + tgt_path = branch + '/' + relpath + tgt_ospath = sbox.ospath(tgt_path) + + expected_out, expected_err = expected_out_err + + revert_branch() + + # Arrange for the merge to conflict at CONFLICT_REV. + if conflict_rev: + edit_file_or_dir(tgt_path, conflict_rev, 'Conflict') + + src_url = '^/' + src_path + svntest.actions.run_and_verify_svn( + None, expected_out, expected_err, + 'merge', src_url, tgt_ospath, '--accept', 'postpone', + *rev_args) + + if expected_mi is not None: + expected_mergeinfo = ['/' + src_path + ':' + expected_mi + '\n'] + check_mergeinfo(expected_mergeinfo, tgt_ospath) + + # In a mergeinfo-aware merge, each specified revision range is split + # internally into sub-ranges, to avoid any already-merged revisions. + # + # From white-box inspection, we see there are code paths that treat + # the last specified range and the last sub-range specially. The + # first specified range or sub-range is not treated specially in terms + # of the code paths, although it might be in terms of data flow. + # + # We test merges that raise a conflict in the first and last sub-range + # of the first and last specified range. + + for target in [file, dir]: + + tgt_ospath = sbox.ospath(branch + '/' + target) + + # First test: Merge "everything" to the branch. + # + # This merge is split into three sub-ranges: r3-4, r6-8, r10-head. + # We have arranged that the merge will raise a conflict in the first + # sub-range. Since we are postponing conflict resolution, the merge + # should stop after the first sub-range, allowing us to resolve and + # repeat the merge at which point the next sub-range(s) can be merged. + # The mergeinfo on the target then should only reflect that the first + # sub-range (r3-4) has been merged. + # + # Previously the merge failed after merging only r3-4 (as it should) + # but mergeinfo for the whole range was recorded, preventing subsequent + # repeat merges from applying the rest of the source changes. + expect = expected_out_and_err(tgt_ospath, + '3-4', ['3-4'], + prop_conflicts=1) + try_merge(target, 4, [], expect, '3-5,9') + + # Try a multiple-range merge that raises a conflict in the + # first sub-range in the first specified range; + expect = expected_out_and_err(tgt_ospath, + '4', ['4'], + prop_conflicts=1) + try_merge(target, 4, ['-c4-6,8-10'], expect, '4-5,9') + # last sub-range in the first specified range; + expect = expected_out_and_err(tgt_ospath, + '4-6', ['4,6'], + prop_conflicts=1) + try_merge(target, 6, ['-c4-6,8-10'], expect, '4-6,9') + # first sub-range in the last specified range; + expect = expected_out_and_err(tgt_ospath, + '4-6,8', ['4,6', '8'], + prop_conflicts=1) + try_merge(target, 8, ['-c4-6,8-10'], expect, '4-6,8-9') + # last sub-range in the last specified range. + # (Expect no error, because 'svn merge' does not throw an error if + # there is no more merging to do when a conflict occurs.) + expect = expected_out_and_err(tgt_ospath, + '4-6,8-10', ['4,6', '8,10'], + prop_conflicts=1, expect_error=False) + try_merge(target, 10, ['-c4-6,8-10'], expect, '4-6,8-10') + + # Try similar merges but involving ranges in reverse order. + expect = expected_out_and_err(tgt_ospath, + '8', ['8'], + prop_conflicts=1) + try_merge(target, 8, ['-c8-10,4-6'], expect, '5,8-9') + expect = expected_out_and_err(tgt_ospath, + '8-10', ['8,10'], + prop_conflicts=1) + try_merge(target, 10, ['-c8-10,4-6'], expect, '5,8-10') + expect = expected_out_and_err(tgt_ospath, + '8-10,4', ['8,10', '4'], + prop_conflicts=1) + try_merge(target, 4, ['-c8-10,4-6'], expect, '4-5,8-10') + expect = expected_out_and_err(tgt_ospath, + '8-10,4-6', ['8,10', '4,6'], + prop_conflicts=1, expect_error=False) + try_merge(target, 6, ['-c8-10,4-6'], expect, '4-6,8-10') + + # Try some reverse merges, with ranges in forward and reverse order. + # + # Reverse merges start with all source changes merged except 5 and 9. + revert_branch() + simple_merge(trunk + '/' + target, sbox.ospath(branch + '/' + target), + ['-c-5,-9,4,6-8,10']) + sbox.simple_commit() + sbox.simple_update() + + expect = expected_out_and_err(tgt_ospath, + '6-4,10-8', ['-6,-4', '-10,-8'], + expect_error=False) + try_merge(target, None, ['-r6:3', '-r10:7'], expect, '7') + expect = expected_out_and_err(tgt_ospath, + '-6', ['-6'], + prop_conflicts=1) + try_merge(target, 6, ['-r6:3', '-r10:7'], expect, '4,7-8,10') + expect = expected_out_and_err(tgt_ospath, + '6-4', ['-6,-4'], + prop_conflicts=1) + try_merge(target, 4, ['-r6:3', '-r10:7'], expect, '7-8,10') + expect = expected_out_and_err(tgt_ospath, + '6-4,-10', ['-6,-4', '-10'], + prop_conflicts=1) + try_merge(target, 10, ['-r6:3', '-r10:7'], expect, '7-8') + expect = expected_out_and_err(tgt_ospath, + '6-4,10-8', ['-6,-4', '-10,-8'], + prop_conflicts=1, expect_error=False) + try_merge(target, 8, ['-r6:3', '-r10:7'], expect, '7') + +@SkipUnless(server_has_mergeinfo) +@Issue(4310) +# Test for issue #4310 "each editor drive gets its own notification +# during 'svn merge'" +def multiple_editor_drive_merge_notifications(sbox): + "each editor drive gets its own notification" + + sbox.build() + os.chdir(sbox.wc_dir) + sbox.wc_dir = '' + + iota_branch_path = sbox.ospath('iota-copy') + C_branch_path = sbox.ospath('branch') + + # Branch a file and a directory: + + # r2 + sbox.simple_copy('iota', 'iota-copy') + sbox.simple_commit() + + # r3 + sbox.simple_copy('A/C', 'branch') + sbox.simple_commit() + + # r4-8 - Set five non-conflicting properties on the branch parents. + for i in range(0,5): + sbox.simple_propset('foo' + str(i) , 'bar', 'iota') + sbox.simple_propset('foo' + str(i) , 'bar', 'A/C') + sbox.simple_commit() + + # Cherry pick merge r5 and r6 to each branch and commit. + svntest.actions.run_and_verify_svn(None, None, [], 'merge', '^/iota', + '-c', '5,7', iota_branch_path) + svntest.actions.run_and_verify_svn(None, None, [], 'merge', '^/A/C', + '-c', '5,7', C_branch_path) + sbox.simple_commit() + + # Now auto merge all eligible revisions to each branch. + # First the directory target: + # + # TODO: We don't use run_and_verify_merge here because it has limitations + # re checking the merge notification headers -- which need to be improved + # at some point. + svntest.actions.run_and_verify_svn( + None, + ["--- Merging r2 through r4 into '" + C_branch_path + "':\n", + " U " + C_branch_path + "\n", + "--- Merging r6 into '" + C_branch_path + "':\n", + " U " + C_branch_path + "\n", + "--- Merging r8 through r9 into '" + C_branch_path + "':\n", + " U " + C_branch_path + "\n", + "--- Recording mergeinfo for merge of r2 through r9 into '" + + C_branch_path + "':\n", + " U " + C_branch_path + "\n"], + [], 'merge', sbox.repo_url + '/A/C', C_branch_path) + + # Then the file target: + # Previously this failed because only the first range notification was + # printed: + # + # >svn merge ^/iota iota-copy + # --- Merging r2 through r4 into 'iota-copy': + # U iota-copy + # U iota-copy + # U iota-copy + # --- Recording mergeinfo for merge of r2 through r9 into 'iota-copy': + # U iota-copy + # + # This is what we expect: + # + # --- Merging r2 through r4 into 'iota-copy': + # U iota-copy + # --- Merging r6 into 'iota-copy': <-- 2nd editor drive + # U iota-copy + # --- Merging r8 through r9 into 'iota-copy': <-- 3rd editor drive + # U iota-copy + # --- Recording mergeinfo for merge of r2 through r9 into 'iota-copy': + # U iota-copy + svntest.actions.run_and_verify_svn( + None, + ["--- Merging r2 through r4 into '" + iota_branch_path + "':\n", + " U " + iota_branch_path + "\n", + "--- Merging r6 into '" + iota_branch_path + "':\n", + " U " + iota_branch_path + "\n", + "--- Merging r8 through r9 into '" + iota_branch_path + "':\n", + " U " + iota_branch_path + "\n", + "--- Recording mergeinfo for merge of r2 through r9 into '" + + iota_branch_path + "':\n", + " U " + iota_branch_path + "\n"], + [], 'merge', sbox.repo_url + '/iota', iota_branch_path) + +#---------------------------------------------------------------------- +@SkipUnless(server_has_mergeinfo) +@Issue(4317) +# Test for issue #4317 "redundant notifications in single editor drive merge". +def single_editor_drive_merge_notifications(sbox): + "single editor drive merge notifications" + sbox.build() + os.chdir(sbox.wc_dir) + sbox.wc_dir = '' + wc_dir = sbox.wc_dir + + A_copy_path = sbox.ospath('A_COPY') + D_copy_path = sbox.ospath('A_COPY/D') + psi_copy_path = sbox.ospath('A_COPY/D/H/psi') + omega_copy_path = sbox.ospath('A_COPY/D/H/omega') + beta_copy_path = sbox.ospath('A_COPY/B/E/beta') + + # r2 - r6: Copy A to A_COPY and then make some text changes under A. + set_up_branch(sbox) + + # r7 - Subtree merge + svntest.actions.run_and_verify_svn(None, None, [], 'merge', '^/A/D', + '-c4', D_copy_path) + sbox.simple_commit() + sbox.simple_update() + + # Previously this failed because of redundant merge notifications + # for r4-7: + # + # >svn merge ^/A A_COPY + # --- Merging r2 through r3 into 'A_COPY\D': + # U A_COPY\D\H\psi + # --- Merging r5 through r7 into 'A_COPY\D': + # U A_COPY\D\H\omega + # --- Merging r4 through r7 into 'A_COPY': + # U A_COPY\B\E\beta + # --- Recording mergeinfo for merge of r2 through r7 into 'A_COPY': + # U A_COPY + # --- Recording mergeinfo for merge of r2 through r7 into 'A_COPY\D': + # U A_COPY\D + # --- Eliding mergeinfo from 'A_COPY\D': + # U A_COPY\D + # + # The order of 'beta' and 'omega' can vary, so use UnorderedOutput. This + # raises the possibility that the test could spuriously pass if the 'U'pdate + # notifications aren't grouped with the correct headers, but that's not what + # is being tested here. + expected_output = svntest.verify.UnorderedOutput( + ["--- Merging r2 through r3 into '" + A_copy_path + "':\n", + "U " + psi_copy_path + "\n", + "--- Merging r4 through r7 into '" + A_copy_path + "':\n", + "U " + omega_copy_path + "\n", + "U " + beta_copy_path + "\n", + "--- Recording mergeinfo for merge of r2 through r7 into '" + + A_copy_path + "':\n", + " U " + A_copy_path + "\n", + "--- Recording mergeinfo for merge of r2 through r7 into '" + + D_copy_path + "':\n", + " U " + D_copy_path + "\n", + "--- Eliding mergeinfo from '" + D_copy_path + "':\n", + " U " + D_copy_path + "\n"]) + svntest.actions.run_and_verify_svn(None, expected_output, [], 'merge', + sbox.repo_url + '/A', A_copy_path) + + # r8 and r9 - Commit and do reverse subtree merge. + sbox.simple_commit() + sbox.simple_update() + svntest.actions.run_and_verify_svn(None, None, [], 'merge', '^/A/D', + '-c-4', D_copy_path) + sbox.simple_commit() + + # Now try a reverse merge. There should only be one notification for + # r7-5: + sbox.simple_update() + expected_output = svntest.verify.UnorderedOutput( + ["--- Reverse-merging r7 through r5 into '" + A_copy_path + "':\n", + "U " + beta_copy_path + "\n", + "U " + omega_copy_path + "\n", + "--- Reverse-merging r4 through r3 into '" + A_copy_path + "':\n", + "U " + psi_copy_path + "\n", + "--- Recording mergeinfo for reverse merge of r7 through r3 into '" + + A_copy_path + "':\n", + " U " + A_copy_path + "\n", + "--- Recording mergeinfo for reverse merge of r7 through r3 into '" + + D_copy_path + "':\n", + " U " + D_copy_path + "\n", + "--- Eliding mergeinfo from '" + D_copy_path + "':\n", + " U " + D_copy_path + "\n"]) + svntest.actions.run_and_verify_svn(None, expected_output, [], 'merge', + '-r9:2', sbox.repo_url + '/A', + A_copy_path) + +@SkipUnless(server_has_mergeinfo) +@Issue(4316) # 'Merge errors out after resolving conflicts' +# Very similar to conflict_aborted_mergeinfo_described_partial_merge() +# (test number 135), except here we tell the merge to resolve the +# conflicts that are generated part way through a multi-revision-range +# merge, and we expect it to continue with the rest of the merge. +def conflicted_split_merge_with_resolve(sbox): + "conflicted split merge with resolve" + + sbox.build() + os.chdir(sbox.wc_dir) + sbox.wc_dir = '' + + trunk = 'A' + branch = 'A2' + file = 'mu' + dir = 'B' + trunk_file = 'A/mu' + trunk_dir = 'A/B' + + # r2: initial state + for rev in range(4, 11): + sbox.simple_propset('prop-' + str(rev), 'Old pval ' + str(rev), + trunk_file, trunk_dir) + sbox.simple_commit() + + # r3: branch + sbox.simple_update() + sbox.simple_copy(trunk, branch) + sbox.simple_commit() + + zero_rev = 3 + + def edit_file_or_dir(path, rev, val): + """Make a local edit to the file at PATH.""" + sbox.simple_propset('prop-' + str(rev), val + ' pval ' + str(rev), path) + + # r4 through r10: simple edits + for rev in range(4, 11): + edit_file_or_dir(trunk_file, rev, 'Edited') + edit_file_or_dir(trunk_dir, rev, 'Edited') + sbox.simple_commit() + + # r14: merge some changes to the branch so that later merges will be split + svntest.actions.run_and_verify_svn(None, None, [], 'merge', '-c5,9', + '^/' + trunk, sbox.ospath(branch), + '--accept', 'theirs-conflict') + sbox.simple_commit() + sbox.simple_update() + + def revert_branch(): + svntest.actions.run_and_verify_svn(None, None, [], 'revert', '-R', + sbox.ospath(branch)) + + def try_merge(relpath, conflict_rev, rev_args, + expected_out_err, expected_mi): + """Revert RELPATH in the branch; make a change that will conflict + with CONFLICT_REV if not None; merge RELPATH in the trunk + to RELPATH in the branch using revision arguments REV_ARGS (list of + '-r...' or '-c...' strings). + + EXPECTED_OUT_ERR_MI is a tuple: (expected_out, expected_err, + expected_mi). EXPECTED_OUT and EXPECTED_ERR are instances of + ExpectedOutput. + + Expect to find mergeinfo EXPECTED_MI if not None. EXPECTED_MI is + a single mergeinfo-string. + """ + src_path = trunk + '/' + relpath + tgt_path = branch + '/' + relpath + tgt_ospath = sbox.ospath(tgt_path) + + expected_out, expected_err = expected_out_err + + revert_branch() + + # Arrange for the merge to conflict at CONFLICT_REV. + if conflict_rev: + edit_file_or_dir(tgt_path, conflict_rev, 'Conflict') + + src_url = '^/' + src_path + '@11' + svntest.actions.run_and_verify_svn( + None, expected_out, expected_err, + 'merge', src_url, tgt_ospath, '--accept', 'mine-full', + *rev_args) + + if expected_mi is not None: + expected_mergeinfo = ['/' + src_path + ':' + expected_mi + '\n'] + check_mergeinfo(expected_mergeinfo, tgt_ospath) + + # In a mergeinfo-aware merge, each specified revision range is split + # internally into sub-ranges, to avoid any already-merged revisions. + # + # From white-box inspection, we see there are code paths that treat + # the last specified range and the last sub-range specially. The + # first specified range or sub-range is not treated specially in terms + # of the code paths, although it might be in terms of data flow. + # + # We test merges that raise a conflict in the first and last sub-range + # of the first and last specified range. + + for target in [file, dir]: + + tgt_ospath = sbox.ospath(branch + '/' + target) + + # First test: Merge "everything" to the branch. + # + # This merge is split into three sub-ranges: r3-4, r6-8, r10-head. + # We have arranged that the merge will raise a conflict in the first + # sub-range. Since we are postponing conflict resolution, the merge + # should stop after the first sub-range, allowing us to resolve and + # repeat the merge at which point the next sub-range(s) can be merged. + # The mergeinfo on the target then should only reflect that the first + # sub-range (r3-4) has been merged. + expect = expected_out_and_err(tgt_ospath, + '3-4,6-11', + ['3-4', '6-8,10-11'], + prop_resolved=1, expect_error=False) + try_merge(target, 4, [], expect, '3-11') + + # Try a multiple-range merge that raises a conflict in the + # first sub-range in the first specified range; + expect = expected_out_and_err(tgt_ospath, + '4,6,8-10', + ['4', '6', '8,10'], + prop_resolved=1, expect_error=False) + try_merge(target, 4, ['-c4-6,8-10'], expect, '4-6,8-10') + # last sub-range in the first specified range; + expect = expected_out_and_err(tgt_ospath, + '4-6,8-10', ['4,6', '8,10'], + prop_resolved=1, expect_error=False) + try_merge(target, 6, ['-c4-6,8-10'], expect, '4-6,8-10') + # first sub-range in the last specified range; + expect = expected_out_and_err(tgt_ospath, + '4-6,8,10', + ['4,6', '8', '10'], + prop_resolved=1, expect_error=False) + try_merge(target, 8, ['-c4-6,8-10'], expect, '4-6,8-10') + # last sub-range in the last specified range. + # (Expect no error, because 'svn merge' does not throw an error if + # there is no more merging to do when a conflict occurs.) + expect = expected_out_and_err(tgt_ospath, + '4-6,8-10', ['4,6', '8,10'], + prop_resolved=1, expect_error=False) + try_merge(target, 10, ['-c4-6,8-10'], expect, '4-6,8-10') + + # Try similar merges but involving ranges in reverse order. + expect = expected_out_and_err(tgt_ospath, + '8', ['8'], + prop_resolved=1, expect_error=False) + try_merge(target, 8, ['-c8-10,4-6'], expect, '4-6,8-10') + expect = expected_out_and_err(tgt_ospath, + '8-10', ['8,10'], + prop_resolved=1, expect_error=False) + try_merge(target, 10, ['-c8-10,4-6'], expect, '4-6,8-10') + expect = expected_out_and_err(tgt_ospath, + '8-10,4', ['8,10', '4'], + prop_resolved=1, expect_error=False) + try_merge(target, 4, ['-c8-10,4-6'], expect, '4-6,8-10') + expect = expected_out_and_err(tgt_ospath, + '8-10,4-6', ['8,10', '4,6'], + prop_resolved=1, expect_error=False) + try_merge(target, 6, ['-c8-10,4-6'], expect, '4-6,8-10') + + # Try some reverse merges, with ranges in forward and reverse order. + # + # Reverse merges start with all source changes merged except 5 and 9. + revert_branch() + simple_merge(trunk + '/' + target, sbox.ospath(branch + '/' + target), + ['-c-5,-9,4,6-8,10']) + sbox.simple_commit() + sbox.simple_update() + + expect = expected_out_and_err(tgt_ospath, + '6-4,10-8', ['-6,-4', '-10,-8'], + expect_error=False) + try_merge(target, None, ['-r6:3', '-r10:7'], expect, '7') + expect = expected_out_and_err(tgt_ospath, + '-6,-4,10-8', + ['-6', '-4', '-10,-8'], + prop_resolved=1, expect_error=False) + try_merge(target, 6, ['-r6:3', '-r10:7'], expect, '7') + expect = expected_out_and_err(tgt_ospath, + '6-4,10-8', ['-6,-4', '-10,-8'], + prop_resolved=1, expect_error=False) + try_merge(target, 4, ['-r6:3', '-r10:7'], expect, '7') + expect = expected_out_and_err(tgt_ospath, + '6-4,-10,-8', + ['-6,-4', '-10', '-8'], + prop_resolved=1, expect_error=False) + try_merge(target, 10, ['-r6:3', '-r10:7'], expect, '7') + expect = expected_out_and_err(tgt_ospath, + '6-4,10-8', ['-6,-4', '-10,-8'], + prop_resolved=1, expect_error=False) + try_merge(target, 8, ['-r6:3', '-r10:7'], expect, '7') + +#---------------------------------------------------------------------- +# Test for issue 4367 'merge to shallow WC, repeat merge to infinite +# depth WC is broken'. +@SkipUnless(server_has_mergeinfo) +@Issues(4367) +def merge_to_empty_target_merge_to_infinite_target(sbox): + "repeat merge to infinite depth WC conflicts" + + sbox.build() + wc_dir = sbox.wc_dir + wc_disk, wc_status = set_up_branch(sbox, branch_only=True) + A_COPY_path = sbox.ospath('A_COPY') + C_COPY_path = sbox.ospath('A_COPY/C') + E_path = sbox.ospath('A/B/E') + J_path = sbox.ospath('A/C/J') + K_path = sbox.ospath('A/C/J/K') + nu1_path = sbox.ospath('A/C/J/nu1') + nu2_path = sbox.ospath('A/C/J/K/nu2') + L_path = sbox.ospath('A/B/L') + nu3_path = sbox.ospath('A/B/L/nu3') + + B1_path = sbox.ospath('A/B/B1') + B1a_path = sbox.ospath('A/B/B1/B1a') + test1_path = sbox.ospath('A/B/B1/test.txt') + test2_path = sbox.ospath('A/B/B1/B1a/test.txt') + + C1_path = sbox.ospath('A/C/C1') + test3_path = sbox.ospath('A/C/C1/test.txt') + + # r3 - Add some subtrees: + # A /A/B/B1 + # A /A/B/B1/B1a + # A /A/B/B1/B1a/test.txt + # A /A/B/B1/test.txt + svntest.main.run_svn(None, 'mkdir', B1_path) + svntest.main.run_svn(None, 'mkdir', B1a_path) + svntest.main.file_append(test1_path, "New file.\n") + svntest.main.file_append(test2_path, "New file.\n") + svntest.main.run_svn(None, 'add', test1_path, test2_path) + sbox.simple_commit() + + # r4 - Add some another subtree. + # A /A/C/C1 + # A /A/C/C1/test.txt + svntest.main.run_svn(None, 'mkdir', C1_path) + svntest.main.file_append(test3_path, "New file.\n") + svntest.main.run_svn(None, 'add', test3_path) + sbox.simple_commit() + + # r5 - Delete part of the subtree added in r3. + # D /A/B/B1/B1a + svntest.main.run_svn(None, 'del', B1a_path) + sbox.simple_commit() + + # r6 - Set depth of A_COPY to empty, merge all available revs from ^/A. + svntest.actions.run_and_verify_svn(None, None, [], 'up', + '--set-depth=empty', A_COPY_path) + svntest.actions.run_and_verify_svn(None, None, [], 'up', + '--set-depth=infinity', C_COPY_path) + svntest.actions.run_and_verify_svn(None, None, [], 'merge', '^/A', + A_COPY_path) + sbox.simple_commit() + + # Update A_COPY back to depth infinity and retry the prior merge. + svntest.actions.run_and_verify_svn(None, None, [], 'up', + '--set-depth=infinity', A_COPY_path) + + expected_output = wc.State(A_COPY_path, { + 'B/B1' : Item(status='A '), + 'B/B1/test.txt' : Item(status='A '), + 'B/B1/B1a' : Item(status='D ', prev_status='A '), + 'B/B1/B1a/test.txt' : Item(status='A '), + }) + expected_mergeinfo_output = wc.State(A_COPY_path, { + '' : Item(status=' U'), + 'B' : Item(status=' G'), + }) + expected_elision_output = wc.State(A_COPY_path, { + 'B' : Item(status=' U'), + }) + expected_status = wc.State(A_COPY_path, { + '' : Item(status=' M'), + 'B' : Item(status=' '), + 'mu' : Item(status=' '), + 'B/B1' : Item(status='A ', copied='+'), + 'B/B1/test.txt' : Item(status=' ', copied='+'), + 'B/B1/B1a' : Item(status='D ', copied='+'), + 'B/B1/B1a/test.txt' : Item(status='D ', copied='+'), + 'B/E' : Item(status=' '), + 'B/E/alpha' : Item(status=' '), + 'B/E/beta' : Item(status=' '), + 'B/lambda' : Item(status=' '), + 'B/F' : Item(status=' '), + 'C' : Item(status=' '), + 'C/C1' : Item(status=' '), + 'C/C1/test.txt' : Item(status=' '), + 'D' : Item(status=' '), + 'D/G' : Item(status=' '), + 'D/G/pi' : Item(status=' '), + 'D/G/rho' : Item(status=' '), + 'D/G/tau' : Item(status=' '), + 'D/gamma' : Item(status=' '), + 'D/H' : Item(status=' '), + 'D/H/chi' : Item(status=' '), + 'D/H/psi' : Item(status=' '), + 'D/H/omega' : Item(status=' '), + }) + expected_status.tweak(wc_rev=6) + expected_status.tweak('B/B1', 'B/B1/test.txt', 'B/B1/B1a', + 'B/B1/B1a/test.txt', wc_rev='-') + expected_disk = wc.State('', { + '' : Item(props={SVN_PROP_MERGEINFO : '/A:2-6'}), + 'B' : Item(), + 'mu' : Item("This is the file 'mu'.\n"), + 'B/B1' : Item(), + 'B/B1/test.txt' : Item("New file.\n"), + 'B/E' : Item(), + 'B/E/alpha' : Item("This is the file 'alpha'.\n"), + 'B/E/beta' : Item("This is the file 'beta'.\n"), + 'B/lambda' : Item("This is the file 'lambda'.\n"), + 'B/F' : Item(), + 'C' : Item(props={SVN_PROP_MERGEINFO : '/A/C:2-5'}), + 'C/C1' : Item(), + 'C/C1/test.txt' : Item("New file.\n"), + 'D' : Item(), + 'D/G' : Item(), + 'D/G/pi' : Item("This is the file 'pi'.\n"), + 'D/G/rho' : Item("This is the file 'rho'.\n"), + 'D/G/tau' : Item("This is the file 'tau'.\n"), + 'D/gamma' : Item("This is the file 'gamma'.\n"), + 'D/H' : Item(), + 'D/H/chi' : Item("This is the file 'chi'.\n"), + 'D/H/psi' : Item("This is the file 'psi'.\n"), + 'D/H/omega' : Item("This is the file 'omega'.\n"), + }) + expected_skip = wc.State(A_COPY_path, { }) + svntest.actions.run_and_verify_merge(A_COPY_path, None, None, + sbox.repo_url + '/A', None, + expected_output, + expected_mergeinfo_output, + expected_elision_output, + expected_disk, + expected_status, + expected_skip, + None, None, None, None, + None, 1, 0) + + # Commit the merge. + #sbox.simple_commit() + +def merge_dir_delete_force(sbox): + "merge a directory delete with --force" + + sbox.build() + + sbox.simple_rm('A/D/G') + sbox.simple_commit() # r2 + + sbox.simple_update(revision=1) + + # Just merging r2 on r1 succeeds + svntest.actions.run_and_verify_svn(sbox.wc_dir, None, [], + 'merge', '-c2', '^/', sbox.wc_dir, + '--ignore-ancestry') + + # Bring working copy to r1 again + svntest.actions.run_and_verify_svn(sbox.wc_dir, None, [], + 'revert', '-R', sbox.wc_dir) + + # But when using --force this same merge caused a segfault in 1.8.0-1.8.8 + svntest.actions.run_and_verify_svn(sbox.wc_dir, None, [], + 'merge', '-c2', '^/', sbox.wc_dir, + '--ignore-ancestry', '--force') + +def conflict_naming(sbox): + "verify conflict file naming" + + sbox.build() + wc_dir = sbox.wc_dir + sbox.simple_append('file.txt', 'This is the initial content\n') + sbox.simple_add('file.txt') + sbox.simple_commit() + + sbox.simple_append('file.txt', 'This is the new content\n', truncate=True) + sbox.simple_commit() + + sbox.simple_append('file.txt', 'This is conflicting content\n', truncate=True) + + # Update - no preserve ext + expected_status = svntest.actions.get_virginal_state(wc_dir, 2) + expected_disk = svntest.main.greek_state.copy() + expected_output = svntest.wc.State(wc_dir, { + 'file.txt' : Item(status='C ') + }) + expected_status.add({ + 'file.txt' : Item(status='C ', wc_rev='2') + }) + + expected_disk.add({ + 'file.txt.r3' : Item(contents="This is the new content\n"), + 'file.txt.r2' : Item(contents="This is the initial content\n"), + 'file.txt' : Item(contents="<<<<<<< .mine\n" \ + "This is conflicting content\n" \ + "=======\n" \ + "This is the initial content\n" \ + ">>>>>>> .r2\n"), + 'file.txt.mine' : Item(contents="This is conflicting content\n"), + }) + svntest.actions.run_and_verify_update(wc_dir, + expected_output, expected_disk, + expected_status, + None, None, None, + None, None, None, + wc_dir, '-r', '2') + + sbox.simple_revert('file.txt') + sbox.simple_update('', revision=3) + sbox.simple_append('file.txt', 'This is conflicting content\n', truncate=True) + + # Update - preserve ext + expected_status = svntest.actions.get_virginal_state(wc_dir, 2) + expected_disk = svntest.main.greek_state.copy() + expected_output = svntest.wc.State(wc_dir, { + 'file.txt' : Item(status='C ') + }) + expected_status.add({ + 'file.txt' : Item(status='C ', wc_rev='2') + }) + + expected_disk.add({ + 'file.txt.r3.txt' : Item(contents="This is the new content\n"), + 'file.txt.r2.txt' : Item(contents="This is the initial content\n"), + 'file.txt' : Item(contents="<<<<<<< .mine.txt\n" \ + "This is conflicting content\n" \ + "=======\n" \ + "This is the initial content\n" \ + ">>>>>>> .r2.txt\n"), + 'file.txt.mine.txt' : Item(contents="This is conflicting content\n"), + }) + svntest.actions.run_and_verify_update( + wc_dir, + expected_output, expected_disk, expected_status, + None, None, None, None, None, None, + wc_dir, '-r', '2', + '--config-option', + 'config:miscellany:preserved-conflict-file-exts=' + + 'c txt h') + + sbox.simple_revert('file.txt') + sbox.simple_update('', revision=3) + sbox.simple_append('file.txt', 'This is conflicting content\n', truncate=True) + + # Merge - no preserve ext + expected_status = svntest.actions.get_virginal_state(wc_dir, 3) + expected_disk = svntest.main.greek_state.copy() + expected_status.add({ + 'file.txt' : Item(status='C ', wc_rev='3') + }) + expected_disk.add({ + 'file.txt.merge-left.r3' : Item(contents="This is the new content\n"), + 'file.txt.merge-right.r2': Item(contents="This is the initial content\n"), + 'file.txt' : Item(contents="<<<<<<< .working\n" \ + "This is conflicting content\n" \ + "=======\n" \ + "This is the initial content\n" \ + ">>>>>>> .merge-right.r2\n"), + 'file.txt.working' : Item(contents="This is conflicting content\n"), + }) + + svntest.actions.run_and_verify_svn(wc_dir, None, [], + 'merge', '-c-3', '^/', sbox.ospath('')) + svntest.actions.run_and_verify_status(wc_dir, expected_status) + svntest.actions.verify_disk(wc_dir, expected_disk) + + sbox.simple_revert('file.txt') + sbox.simple_append('file.txt', 'This is conflicting content\n', truncate=True) + + # Merge - preserve ext + expected_status = svntest.actions.get_virginal_state(wc_dir, 3) + expected_disk = svntest.main.greek_state.copy() + expected_status.add({ + 'file.txt' : Item(status='C ', wc_rev='3') + }) + expected_disk.add({ + 'file.txt.merge-left.r3.txt' : Item(contents="This is the new content\n"), + 'file.txt.merge-right.r2.txt': Item(contents="This is the initial content\n"), + 'file.txt' : Item(contents="<<<<<<< .working.txt\n" \ + "This is conflicting content\n" \ + "=======\n" \ + "This is the initial content\n" \ + ">>>>>>> .merge-right.r2.txt\n"), + 'file.txt.working.txt' : Item(contents="This is conflicting content\n"), + }) + + svntest.actions.run_and_verify_svn( + wc_dir, None, [], + 'merge', '-c-3', '^/', sbox.ospath(''), + '--config-option', + 'config:miscellany:preserved-conflict-file-exts=' + + 'c txt h') + svntest.actions.run_and_verify_status(wc_dir, expected_status) + svntest.actions.verify_disk(wc_dir, expected_disk) + ######################################################################## # Run the tests @@ -17562,13 +19420,29 @@ test_list = [ None, merge_change_to_file_with_executable, dry_run_merge_conflicting_binary, foreign_repos_prop_conflict, + merge_adds_subtree_with_mergeinfo, reverse_merge_adds_subtree, merged_deletion_causes_tree_conflict, - merge_adds_subtree_with_mergeinfo, record_only_merge_adds_new_subtree_mergeinfo, + unnecessary_noninheritable_mergeinfo_missing_subtrees, + unnecessary_noninheritable_mergeinfo_shallow_merge, + svnmucc_abuse_1, + merge_source_with_replacement, reverse_merge_with_rename, merge_adds_then_deletes_subtree, merge_with_added_subtrees_with_mergeinfo, + merge_with_externals_with_mergeinfo, + merge_binary_file_with_keywords, + merge_conflict_when_keywords_removed, + merge_target_selection, + merge_properties_on_adds, + conflict_aborted_mergeinfo_described_partial_merge, + multiple_editor_drive_merge_notifications, + single_editor_drive_merge_notifications, + conflicted_split_merge_with_resolve, + merge_to_empty_target_merge_to_infinite_target, + merge_dir_delete_force, + conflict_naming, ] if __name__ == '__main__': |