diff options
Diffstat (limited to 'subversion/tests/cmdline/merge_reintegrate_tests.py')
-rwxr-xr-x | subversion/tests/cmdline/merge_reintegrate_tests.py | 719 |
1 files changed, 446 insertions, 273 deletions
diff --git a/subversion/tests/cmdline/merge_reintegrate_tests.py b/subversion/tests/cmdline/merge_reintegrate_tests.py index fc46c9c..8b3e91f 100755 --- a/subversion/tests/cmdline/merge_reintegrate_tests.py +++ b/subversion/tests/cmdline/merge_reintegrate_tests.py @@ -48,6 +48,62 @@ from merge_tests import set_up_branch from merge_tests import expected_merge_output #---------------------------------------------------------------------- +def run_reintegrate(src_url, tgt_path): + """Run 'svn merge --reintegrate SRC_URL TGT_PATH'. Raise an error if + there is nothing on stdout, anything on stderr, or a non-zero exit + code. + """ + svntest.actions.run_and_verify_svn(None, svntest.verify.AnyOutput, [], + 'merge', '--reintegrate', + src_url, tgt_path) + +def run_reintegrate_expect_error(src_url, tgt_path, + expected_stdout, expected_stderr): + """Run 'svn merge --reintegrate SRC_URL TGT_PATH'. Raise an error + unless stdout and stderr both match and the exit code is non-zero. + Every line of stderr must match the regex EXPECTED_STDERR. + """ + expected_stderr += "|" + svntest.main.stack_trace_regexp + + # The actions.run_and_verify_* methods are happy if one line of the error + # matches the regex, but we want to check that every line matches. + # So we will pass the stderr to svntest.verify.verify_outputs() + # ourselves, but as the 'actual_stdout' argument, that way each line of + # error must match the regex. + exit_code, out, err = svntest.actions.run_and_verify_svn( + None, expected_stdout, svntest.verify.AnyOutput, + 'merge', '--reintegrate', + src_url, tgt_path) + assert exit_code + svntest.verify.verify_outputs( + "Reintegrate failed but not in the way expected", + err, None, + expected_stderr, None, + True) # Match *all* lines + +def run_and_verify_reintegrate(tgt_dir, src_url, + output_tree, + mergeinfo_output_tree, + elision_output_tree, + disk_tree, status_tree, skip_tree, + error_re_string = None, + check_props = True, + dry_run = True): + """Run 'svn merge --reintegrate SRC_URL TGT_DIR'. Raise an error if + there is nothing on stdout, anything on stderr, or a non-zero exit + code, or if the expected ERROR_RE_STRING or any of the given expected + trees don't match. + """ + svntest.actions.run_and_verify_merge( + tgt_dir, None, None, src_url, None, + output_tree, mergeinfo_output_tree, elision_output_tree, + disk_tree, status_tree, skip_tree, + error_re_string, + None, None, None, None, check_props, dry_run, + '--reintegrate', tgt_dir) + + +#---------------------------------------------------------------------- @SkipUnless(server_has_mergeinfo) @Issue(3640) def basic_reintegrate(sbox): @@ -61,7 +117,7 @@ def basic_reintegrate(sbox): expected_disk, expected_status = set_up_branch(sbox) # Make a change on the branch, to A/mu. Commit in r7. - svntest.main.file_write(os.path.join(wc_dir, "A_COPY", "mu"), + svntest.main.file_write(sbox.ospath('A_COPY/mu'), "Changed on the branch.") expected_output = wc.State(wc_dir, {'A_COPY/mu' : Item(verb='Sending')}) expected_status.tweak('A_COPY/mu', wc_rev=7) @@ -77,7 +133,7 @@ def basic_reintegrate(sbox): None, None, None, None, None, True) # Merge from trunk to branch (ie, r3-6), using normal cherry-harvest. - A_COPY_path = os.path.join(wc_dir, "A_COPY") + A_COPY_path = sbox.ospath('A_COPY') expected_output = wc.State(A_COPY_path, { 'D/H/psi' : Item(status='U '), 'D/G/rho' : Item(status='U '), @@ -172,7 +228,7 @@ def basic_reintegrate(sbox): # *finally*, actually run merge --reintegrate in trunk with the # branch URL. This should bring in the mu change and the tauprime # change. - A_path = os.path.join(wc_dir, "A") + A_path = sbox.ospath('A') expected_output = wc.State(A_path, { 'mu' : Item(status='U '), }) @@ -204,17 +260,15 @@ def basic_reintegrate(sbox): }) k_expected_disk.tweak('', props={SVN_PROP_MERGEINFO : '/A_COPY:2-8'}) expected_skip = wc.State(A_path, {}) - svntest.actions.run_and_verify_merge(A_path, None, None, - sbox.repo_url + '/A_COPY', None, + run_and_verify_reintegrate(A_path, + sbox.repo_url + '/A_COPY', expected_output, expected_mergeinfo_output, expected_elision_output, k_expected_disk, k_expected_status, expected_skip, - None, None, None, None, - None, True, True, - '--reintegrate', A_path) + None, True, True) # Test issue #3640: # @@ -230,7 +284,7 @@ def basic_reintegrate(sbox): sbox.repo_url + '/A_MOVED', '-m', 'Copy A to A_MOVED') svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir) - A_MOVED_path = os.path.join(wc_dir, "A_MOVED") + A_MOVED_path = sbox.ospath('A_MOVED') expected_output = wc.State(A_MOVED_path, { 'mu' : Item(status='U '), }) @@ -263,17 +317,15 @@ def basic_reintegrate(sbox): expected_status.tweak(wc_rev=9) k_expected_disk.tweak('', props={SVN_PROP_MERGEINFO : '/A_COPY:2-9'}) expected_skip = wc.State(A_MOVED_path, {}) - svntest.actions.run_and_verify_merge(A_MOVED_path, None, None, - sbox.repo_url + '/A_COPY', None, + run_and_verify_reintegrate(A_MOVED_path, + sbox.repo_url + '/A_COPY', expected_output, expected_mergeinfo_output, expected_elision_output, k_expected_disk, expected_status, expected_skip, - None, None, None, None, - None, True, True, - '--reintegrate', A_MOVED_path) + None, True, True) #---------------------------------------------------------------------- def reintegrate_with_rename(sbox): @@ -285,7 +337,7 @@ def reintegrate_with_rename(sbox): expected_disk, expected_status = set_up_branch(sbox) # Make a change on the branch, to A/mu. Commit in r7. - svntest.main.file_write(os.path.join(wc_dir, "A_COPY", "mu"), + svntest.main.file_write(sbox.ospath('A_COPY/mu'), "Changed on the branch.") expected_output = wc.State(wc_dir, {'A_COPY/mu' : Item(verb='Sending')}) expected_status.tweak('A_COPY/mu', wc_rev=7) @@ -301,7 +353,7 @@ def reintegrate_with_rename(sbox): None, None, None, None, None, True) # Merge from trunk to branch (ie, r3-6), using normal cherry-harvest. - A_COPY_path = os.path.join(wc_dir, "A_COPY") + A_COPY_path = sbox.ospath('A_COPY') expected_output = wc.State(A_COPY_path, { 'D/H/psi' : Item(status='U '), 'D/G/rho' : Item(status='U '), @@ -452,7 +504,7 @@ def reintegrate_with_rename(sbox): # *finally*, actually run merge --reintegrate in trunk with the # branch URL. This should bring in the mu change and the tauprime # change. - A_path = os.path.join(wc_dir, "A") + A_path = sbox.ospath('A') expected_output = wc.State(A_path, { 'mu' : Item(status='U '), 'D/G/tauprime' : Item(status='A '), @@ -492,17 +544,15 @@ def reintegrate_with_rename(sbox): contents="This is the file 'tau'.\n") }) expected_skip = wc.State(A_path, {}) - svntest.actions.run_and_verify_merge(A_path, None, None, - sbox.repo_url + '/A_COPY', None, + run_and_verify_reintegrate(A_path, + sbox.repo_url + '/A_COPY', expected_output, expected_mergeinfo_output, expected_elision_output, k_expected_disk, k_expected_status, expected_skip, - None, None, None, None, - None, True, True, - '--reintegrate', A_path) + None, True, True) # Finally, commit the result of the merge (r10). expected_output = wc.State(wc_dir, { @@ -527,7 +577,7 @@ def reintegrate_branch_never_merged_to(sbox): expected_disk, expected_status = set_up_branch(sbox) # Make a change on the branch, to A_COPY/mu. Commit in r7. - svntest.main.file_write(os.path.join(wc_dir, "A_COPY", "mu"), + svntest.main.file_write(sbox.ospath('A_COPY/mu'), "Changed on the branch.") expected_output = wc.State(wc_dir, {'A_COPY/mu' : Item(verb='Sending')}) expected_status.tweak('A_COPY/mu', wc_rev=7) @@ -570,7 +620,7 @@ def reintegrate_branch_never_merged_to(sbox): # *finally*, actually run merge --reintegrate in trunk with the # branch URL. This should bring in the mu change and the tauprime # change. - A_path = os.path.join(wc_dir, "A") + A_path = sbox.ospath('A') expected_output = wc.State(A_path, { 'mu' : Item(status='U '), 'D/G/tauprime' : Item(status='A '), @@ -625,17 +675,15 @@ def reintegrate_branch_never_merged_to(sbox): 'D/H/psi' : Item("New content"), }) expected_skip = wc.State(A_path, {}) - svntest.actions.run_and_verify_merge(A_path, None, None, - sbox.repo_url + '/A_COPY', None, + run_and_verify_reintegrate(A_path, + sbox.repo_url + '/A_COPY', expected_output, expected_mergeinfo_output, expected_elision_output, k_expected_disk, k_expected_status, expected_skip, - None, None, None, None, - None, True, True, - '--reintegrate', A_path) + None, True, True) # Finally, commit the result of the merge (r9). expected_output = wc.State(wc_dir, { @@ -655,23 +703,30 @@ def reintegrate_fail_on_modified_wc(sbox): "merge --reintegrate should fail in modified wc" sbox.build() wc_dir = sbox.wc_dir - A_path = os.path.join(wc_dir, "A") + A_path = sbox.ospath('A') + A_COPY_path = sbox.ospath('A_COPY') mu_path = os.path.join(A_path, "mu") ignored_expected_disk, ignored_expected_status = set_up_branch(sbox) + + # Do a 'sync' merge first so that the following merge really needs to be a + # reintegrate, so that an equivalent automatic merge would behave the same. + svntest.main.run_svn(None, 'merge', sbox.repo_url + '/A', A_COPY_path) + sbox.simple_commit() + svntest.main.file_write(mu_path, "Changed on 'trunk' (the merge target).") sbox.simple_update() # avoid mixed-revision error - svntest.actions.run_and_verify_merge( - A_path, None, None, sbox.repo_url + '/A_COPY', None, None, None, None, + run_and_verify_reintegrate( + A_path, sbox.repo_url + '/A_COPY', None, None, None, None, None, None, ".*Cannot merge into a working copy that has local modifications.*", - None, None, None, None, True, False, '--reintegrate', A_path) + True, False) #---------------------------------------------------------------------- def reintegrate_fail_on_mixed_rev_wc(sbox): "merge --reintegrate should fail in mixed-rev wc" sbox.build() wc_dir = sbox.wc_dir - A_path = os.path.join(wc_dir, "A") + A_path = sbox.ospath('A') mu_path = os.path.join(A_path, "mu") ignored_expected_disk, expected_status = set_up_branch(sbox) # Make and commit a change, in order to get a mixed-rev wc. @@ -683,22 +738,41 @@ def reintegrate_fail_on_mixed_rev_wc(sbox): svntest.actions.run_and_verify_commit(wc_dir, expected_output, expected_status, None, wc_dir) # Try merging into that same wc, expecting failure. - svntest.actions.run_and_verify_merge( - A_path, None, None, sbox.repo_url + '/A_COPY', None, None, None, None, + run_and_verify_reintegrate( + A_path, sbox.repo_url + '/A_COPY', None, None, None, None, None, None, ".*Cannot merge into mixed-revision working copy.*", - None, None, None, None, True, False, '--reintegrate', A_path) + True, False) #---------------------------------------------------------------------- def reintegrate_fail_on_switched_wc(sbox): "merge --reintegrate should fail in switched wc" sbox.build() wc_dir = sbox.wc_dir - A_path = os.path.join(wc_dir, "A") + A_path = sbox.ospath('A') + A_COPY_path = sbox.ospath('A_COPY') G_path = os.path.join(A_path, "D", "G") switch_url = sbox.repo_url + "/A/D/H" expected_disk, expected_status = set_up_branch(sbox) + # Do a 'sync' merge first so that the following merge really needs to be a + # reintegrate, so that an equivalent automatic merge would behave the same. + expected_disk.tweak( + 'A_COPY/D/H/psi', + 'A_COPY/D/G/rho', + 'A_COPY/B/E/beta', + 'A_COPY/D/H/omega', + contents="New content") + expected_status.tweak( + 'A_COPY/D/H/psi', + 'A_COPY/D/G/rho', + 'A_COPY/B/E/beta', + 'A_COPY/D/H/omega', + 'A_COPY', + wc_rev=7) + svntest.main.run_svn(None, 'merge', sbox.repo_url + '/A', A_COPY_path) + sbox.simple_commit() + # Switch a subdir of the target. expected_output = svntest.wc.State(wc_dir, { 'A/D/G/pi' : Item(status='D '), @@ -716,10 +790,10 @@ def reintegrate_fail_on_switched_wc(sbox): }) expected_status.remove('A/D/G/pi', 'A/D/G/rho', 'A/D/G/tau') expected_status.add({ - 'A/D/G' : Item(status=' ', wc_rev=6, switched='S'), - 'A/D/G/chi' : Item(status=' ', wc_rev=6), - 'A/D/G/psi' : Item(status=' ', wc_rev=6), - 'A/D/G/omega' : Item(status=' ', wc_rev=6), + 'A/D/G' : Item(status=' ', wc_rev=7, switched='S'), + 'A/D/G/chi' : Item(status=' ', wc_rev=7), + 'A/D/G/psi' : Item(status=' ', wc_rev=7), + 'A/D/G/omega' : Item(status=' ', wc_rev=7), }) svntest.actions.run_and_verify_switch(wc_dir, G_path, @@ -730,11 +804,11 @@ def reintegrate_fail_on_switched_wc(sbox): None, None, None, None, None, False, '--ignore-ancestry') sbox.simple_update() # avoid mixed-revision error - svntest.actions.run_and_verify_merge( - A_path, None, None, sbox.repo_url + '/A_COPY', None, None, None, None, + run_and_verify_reintegrate( + A_path, sbox.repo_url + '/A_COPY', None, None, None, None, None, None, ".*Cannot merge into a working copy with a switched subtree.*", - None, None, None, None, True, False, '--reintegrate', A_path) + True, False) #---------------------------------------------------------------------- # Test for issue #3603 'allow reintegrate merges into WCs with @@ -749,11 +823,11 @@ def reintegrate_on_shallow_wc(sbox): expected_disk, expected_status = set_up_branch(sbox, branch_only = True) # Some paths we'll care about - A_path = os.path.join(wc_dir, "A") - A_D_path = os.path.join(wc_dir, "A", "D") - mu_COPY_path = os.path.join(wc_dir, "A_COPY", "mu") - psi_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "H", "psi") - A_COPY_path = os.path.join(wc_dir, "A_COPY") + A_path = sbox.ospath('A') + A_D_path = sbox.ospath('A/D') + mu_COPY_path = sbox.ospath('A_COPY/mu') + psi_COPY_path = sbox.ospath('A_COPY/D/H/psi') + A_COPY_path = sbox.ospath('A_COPY') # r3 - Make a change on the A_COPY branch that will be # reintegrated back to A. @@ -804,27 +878,26 @@ def reintegrate_on_shallow_wc(sbox): 'D' : Item(), # Don't expect anything under D, its depth is empty! }) expected_A_skip = wc.State(A_path, {}) - svntest.actions.run_and_verify_merge(A_path, None, None, - sbox.repo_url + '/A_COPY', None, + run_and_verify_reintegrate(A_path, + sbox.repo_url + '/A_COPY', expected_output, expected_mergeinfo_output, expected_elision_output, expected_A_disk, expected_A_status, expected_A_skip, - None, None, None, None, - None, 1, 1, "--reintegrate", A_path) + None, 1, 1) # Now revert the reintegrate and make a second change on the # branch in r4, but this time change a subtree that corresponds # to the missing (shallow) portion of the source. The reintegrate - # should still succeed, albeit skipping some paths. + # should still succeed. svntest.actions.run_and_verify_svn(None, None, [], 'revert', '-R', wc_dir) svntest.main.file_write(psi_COPY_path, "more branch work") svntest.main.run_svn(None, 'commit', '-m', 'Some more work on the A_COPY branch', wc_dir) - # Reuse the same expectations as the prior merge, except that - # non-inheritable mergeinfo is set on the root of the missing subtree... + # Reuse the same expectations as the prior merge, except for the mergeinfo + # on the target root that now includes the latest rev on the branch. expected_mergeinfo_output.add({ 'D' : Item(status=' U') }) @@ -832,20 +905,25 @@ def reintegrate_on_shallow_wc(sbox): expected_A_disk.tweak('D', props={SVN_PROP_MERGEINFO : '/A_COPY/D:2-4*'}) # ... a depth-restricted item is skipped ... expected_A_skip.add({ - 'D/H' : Item() + 'D/H' : Item(verb='Skipped missing target') + }) + expected_output.add({ + # Below the skip + 'D/H/psi' : Item(status=' ', treeconflict='U'), }) - # ... and the mergeinfo on the target root includes the latest rev on the branch. + # 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_A_disk.tweak('', props={SVN_PROP_MERGEINFO : '/A_COPY:2-4'}) - svntest.actions.run_and_verify_merge(A_path, None, None, - sbox.repo_url + '/A_COPY', None, + run_and_verify_reintegrate(A_path, + sbox.repo_url + '/A_COPY', expected_output, expected_mergeinfo_output, expected_elision_output, expected_A_disk, expected_A_status, expected_A_skip, - None, None, None, None, - None, 1, 1, "--reintegrate", A_path) + None, 1, 1) #---------------------------------------------------------------------- @SkipUnless(server_has_mergeinfo) @@ -854,13 +932,13 @@ def reintegrate_fail_on_stale_source(sbox): sbox.build() wc_dir = sbox.wc_dir expected_disk, expected_status = set_up_branch(sbox) - 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, 'some text appended to mu\n') svntest.actions.run_and_verify_svn(None, None, [], 'commit', - '-m', 'a change to mu', mu_path); + '-m', 'a change to mu', mu_path) # Unmix the revisions in the working copy. - svntest.actions.run_and_verify_svn(None, None, [], 'update', wc_dir); + svntest.actions.run_and_verify_svn(None, None, [], 'update', wc_dir) # The merge --reintegrate succeeds but since there were no changes # on A_COPY after it was branched the only result is updated mergeinfo # on the reintegrate target. @@ -914,16 +992,15 @@ def reintegrate_fail_on_stale_source(sbox): 'D/H/omega' : Item("New content"), }) expected_skip = wc.State(A_path, { }) - svntest.actions.run_and_verify_merge(A_path, None, None, - sbox.repo_url + '/A_COPY', None, + run_and_verify_reintegrate(A_path, + sbox.repo_url + '/A_COPY', expected_output, expected_mergeinfo_output, expected_elision_output, expected_disk, expected_status, expected_skip, - [], None, None, None, None, True, True, - '--reintegrate', A_path) + [], True, True) #---------------------------------------------------------------------- def merge_file_with_space_in_its_path(sbox): @@ -931,7 +1008,7 @@ def merge_file_with_space_in_its_path(sbox): sbox.build() wc_dir = sbox.wc_dir - some_dir = os.path.join(wc_dir, "some dir") + some_dir = sbox.ospath('some dir') file1 = os.path.join(some_dir, "file1") file2 = os.path.join(some_dir, "file2") @@ -953,9 +1030,7 @@ def merge_file_with_space_in_its_path(sbox): "ci", "-m", "r4", wc_dir) target_url = sbox.repo_url + '/some%20dir/file2' - svntest.actions.run_and_verify_svn(None, None, [], - "merge", "--reintegrate", target_url, - file1) + run_reintegrate(target_url, file1) #---------------------------------------------------------------------- @SkipUnless(server_has_mergeinfo) @@ -963,43 +1038,65 @@ def reintegrate_with_subtree_mergeinfo(sbox): "merge --reintegrate with subtree mergeinfo" # Create a standard greek tree, branch A to A_COPY in r2, A to A_COPY_2 in - # r3, A to A_COPY_3 in r3, and then make some changes under A in r5-8. + # r3, A to A_COPY_3 in r4, and then make some changes under A in r5-8. + # + # A_COPY_3 4--------- + # / + # A -1--------5-6-7-8- + # \ \ + # A_COPY 2-\----------- + # \ + # A_COPY_2 3--------- + sbox.build() wc_dir = sbox.wc_dir expected_disk, expected_status = set_up_branch(sbox, False, 3) # Some paths we'll care about - gamma_COPY_3_path = os.path.join(wc_dir, "A_COPY_3", "D", "gamma") - D_path = os.path.join(wc_dir, "A", "D") - gamma_path = os.path.join(wc_dir, "A", "D", "gamma") - mu_COPY_2_path = os.path.join(wc_dir, "A_COPY_2", "mu") - mu_path = os.path.join(wc_dir, "A", "mu") - mu_COPY_path = os.path.join(wc_dir, "A_COPY", "mu") - A_COPY_path = os.path.join(wc_dir, "A_COPY") - D_COPY_path = os.path.join(wc_dir, "A_COPY") - 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") - gamma_moved_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "gamma_moved") - gamma_moved_path = os.path.join(wc_dir, "A", "D", "gamma_moved") - 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") - D_COPY_path = os.path.join(wc_dir, "A_COPY", "D") - alpha_COPY_path = os.path.join(wc_dir, "A_COPY", "B", "E", "alpha") - A_path = os.path.join(wc_dir, "A") + gamma_COPY_3_path = sbox.ospath('A_COPY_3/D/gamma') + D_path = sbox.ospath('A/D') + gamma_path = sbox.ospath('A/D/gamma') + mu_COPY_2_path = sbox.ospath('A_COPY_2/mu') + mu_path = sbox.ospath('A/mu') + mu_COPY_path = sbox.ospath('A_COPY/mu') + A_COPY_path = sbox.ospath('A_COPY') + D_COPY_path = sbox.ospath('A_COPY') + beta_COPY_path = sbox.ospath('A_COPY/B/E/beta') + gamma_COPY_path = sbox.ospath('A_COPY/D/gamma') + gamma_moved_COPY_path = sbox.ospath('A_COPY/D/gamma_moved') + gamma_moved_path = sbox.ospath('A/D/gamma_moved') + 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') + D_COPY_path = sbox.ospath('A_COPY/D') + alpha_COPY_path = sbox.ospath('A_COPY/B/E/alpha') + A_path = sbox.ospath('A') # Now set up a situation where we try to reintegrate A_COPY back to A but # both of these paths have subtree mergeinfo. Iff the mergeinfo on A_COPY # reflects that the same revisions have been applied across all of A_COPY, - # then the reintegrate merge should succeed. + # then the reintegrate merge should succeed. We'll try that case first. + # + # A_COPY_3 4-------[9]-- + # / \ + # / \ + # A -1--------5-6-7-8---10-------------------WC-- + # \ \ (D) \ /reint. + # \ \ (mu) \ / + # A_COPY 2-\--------------------12---13--14------ + # \ / + # \ / + # A_COPY_2 3-------------[11]-- # + # Key: [#] = cherry-picked revision; (foo) = merge of subtree 'foo' + # Note: These diagrams show an overview and do not capture every detail. + # r9 - Make a text change to A_COPY_3/D/gamma svntest.main.file_write(gamma_COPY_3_path, "New content") expected_output = wc.State(wc_dir, {'A_COPY_3/D/gamma' : Item(verb='Sending')}) expected_status.tweak('A_COPY_3/D/gamma', wc_rev=9) svntest.actions.run_and_verify_commit(wc_dir, expected_output, expected_status, None, wc_dir) - expected_disk.tweak('A_COPY_3/D/gamma', contents="New content") # r10 - Merge r9 from A_COPY_3/D to A/D, creating explicit subtree # mergeinfo under A. For this and every subsequent merge we update the WC @@ -1019,8 +1116,6 @@ def reintegrate_with_subtree_mergeinfo(sbox): expected_status.tweak('A/D', 'A/D/gamma', wc_rev=10) svntest.actions.run_and_verify_commit(wc_dir, expected_output, expected_status, None, wc_dir) - expected_disk.tweak('A/D/gamma', contents="New content") - expected_disk.tweak('A/D', props={SVN_PROP_MERGEINFO : '/A_COPY_3/D:9'}) # r11 - Make a text change to A_COPY_2/mu svntest.main.file_write(mu_COPY_2_path, "New content") @@ -1028,7 +1123,6 @@ def reintegrate_with_subtree_mergeinfo(sbox): expected_status.tweak('A_COPY_2/mu', wc_rev=11) svntest.actions.run_and_verify_commit(wc_dir, expected_output, expected_status, None, wc_dir) - expected_disk.tweak('A_COPY_2/mu', contents="New content") # r12 - Merge r11 from A_COPY_2/mu to A_COPY/mu svntest.actions.run_and_verify_svn(None, exp_noop_up_out(11), [], 'up', @@ -1045,7 +1139,6 @@ def reintegrate_with_subtree_mergeinfo(sbox): expected_status.tweak('A_COPY/mu', wc_rev=12) svntest.actions.run_and_verify_commit(wc_dir, expected_output, expected_status, None, wc_dir) - expected_disk.tweak('A_COPY/mu', contents="New content") # r13 - Do a 'synch' cherry harvest merge of all available revisions # from A to A_COPY @@ -1084,16 +1177,6 @@ def reintegrate_with_subtree_mergeinfo(sbox): wc_rev=13) svntest.actions.run_and_verify_commit(wc_dir, expected_output, expected_status, None, wc_dir) - expected_disk.tweak('A_COPY/B/E/beta', - 'A_COPY/D', - 'A_COPY/D/G/rho', - 'A_COPY/D/H/omega', - 'A_COPY/D/H/psi', - 'A_COPY/D/gamma', - contents="New content") - expected_disk.tweak('A_COPY', props={SVN_PROP_MERGEINFO : '/A:2-12'}) - expected_disk.tweak('A_COPY/D', - props={SVN_PROP_MERGEINFO : '/A/D:2-12\n/A_COPY_3/D:9\n'}) # r14 - Make a text change on A_COPY/B/E/alpha svntest.main.file_write(alpha_COPY_path, "New content") @@ -1101,7 +1184,6 @@ def reintegrate_with_subtree_mergeinfo(sbox): expected_status.tweak('A_COPY/B/E/alpha', wc_rev=14) svntest.actions.run_and_verify_commit(wc_dir, expected_output, expected_status, None, wc_dir) - expected_disk.tweak('A_COPY/B/E/alpha', contents="New content") # Now, reintegrate A_COPY to A. This should succeed. svntest.actions.run_and_verify_svn(None, exp_noop_up_out(14), [], 'up', @@ -1166,22 +1248,32 @@ def reintegrate_with_subtree_mergeinfo(sbox): 'D/H/omega' : Item("New content"), }) expected_A_skip = wc.State(A_COPY_path, {}) - svntest.actions.run_and_verify_merge(A_path, None, None, - sbox.repo_url + '/A_COPY', None, + run_and_verify_reintegrate(A_path, + sbox.repo_url + '/A_COPY', expected_output, expected_mergeinfo_output, expected_elision_output, expected_A_disk, expected_A_status, expected_A_skip, - None, None, None, None, - None, 1, 1, "--reintegrate", A_path) + None, 1, 1) # Make some more changes to A_COPY so that the same revisions have *not* # been uniformly applied from A to A_COPY. In this case the reintegrate # merge should fail, but should provide a helpful message as to where the # problems are. # + # A_COPY_3 4-------[9]-- + # / \ + # / \ [-8]___ + # A -1---------5-6-7-8---10----------------\-------WC-- + # \ \ (D) \ \ /reint. + # \ \ (mu) \ \ / + # A_COPY 2-\--------------------12---13--14--15-------- + # \ / (D) + # \ / + # A_COPY_2 3-------------[11]-- + # First revert the previous reintegrate merge svntest.actions.run_and_verify_svn(None, None, [], 'revert', '-R', wc_dir) @@ -1204,19 +1296,10 @@ def reintegrate_with_subtree_mergeinfo(sbox): # to it from A, the merge should fail. Further we expect an error message # that highlights the fact that A_COPY/D is the offending subtree. # - # The actions.run_and_verify_* methods are happy if one line of the error - # matches the regex, but we want to know that the error actually provides - # specific information about the paths that are stopping --reintegrate from - # working. So we will pass the stderr to svntest.verify.verify_outputs() - # ourselves, but as the 'actual_stdout' argument, that way each line of - # error must match the regex. - exit_code, out, err = svntest.actions.run_and_verify_svn( - None, [], svntest.verify.AnyOutput, - 'merge', '--reintegrate', sbox.repo_url + '/A_COPY', A_path) - - svntest.verify.verify_outputs("Reintegrate failed but not " - "in the way expected", - err, None, + # We want to know that the error provides specific information about the + # paths that are stopping --reintegrate from working. + run_reintegrate_expect_error(sbox.repo_url + '/A_COPY', A_path, + [], "(svn: E195016: Reintegrate can only be used if " "revisions 2 through 15 were previously " "merged from .*/A to the reintegrate source, " @@ -1225,10 +1308,7 @@ def reintegrate_with_subtree_mergeinfo(sbox): "|( Missing ranges: /A/D:8\n)" "|( A_COPY/mu\n)" "|( Missing ranges: /A/mu:2-12\n)" - "|(\n)" - "|(.*apr_err.*)", # In case of debug build - None, - True) # Match *all* lines of stdout + "|(\n)") # Test another common situation that can break reintegrate as a result # of copies and moves: @@ -1247,8 +1327,19 @@ def reintegrate_with_subtree_mergeinfo(sbox): # rev N+3. The renamed subtree on 'branch' now has additional explicit # mergeinfo decribing the synch merge from trunk@N+1 to trunk@N+2. # - # E) Reintegrate 'branch' to 'trunk'. This fails as it appears not all - # of 'trunk' was previously merged to 'branch' + # E) Reintegrate 'branch' to 'trunk'. + # + # Step: A B C D E + # A_COPY_3 ---[9]-- + # / \ (D/g.-> + # / \ [-8]___ D/g.m.) (D/g.m.) + # A ------------10----------------\------16-------18--------WC + # \\ (D) \ \ \ \ /reint. + # \\ (mu) \ \ \ \ / + # A_COPY -\--------------12---13--14--15-------17-------19------ + # \ / (D) + # \ / + # A_COPY_2 --------[11]-- # r16 - A) REPOS-to-REPOS rename of A/D/gamma to A/D/gamma_moved. Since # r874258 WC-to-WC moves won't create mergeinfo on the dest if the source @@ -1260,15 +1351,18 @@ def reintegrate_with_subtree_mergeinfo(sbox): '-m', 'REPOS-to-REPOS move' ) svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir) - expected_disk.remove('A/D/gamma') - expected_disk.add({ - 'A/D/gamma_moved' : Item(props={SVN_PROP_MERGEINFO: '/A_COPY_3/D/gamma:9'}, - contents="New content") - }) expected_status.tweak(wc_rev=16) expected_status.remove('A/D/gamma') expected_status.add({'A/D/gamma_moved' : Item(status=' ', wc_rev=16)}) + # Why is gamma_moved notified as ' G' rather than ' U'? It 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 gamma_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. # r17 - B) Synch merge from A to A_COPY svntest.actions.run_and_verify_svn( None, @@ -1278,7 +1372,7 @@ def reintegrate_with_subtree_mergeinfo(sbox): 'D ' + gamma_COPY_path + '\n', ' U ' + A_COPY_path + '\n', ' U ' + D_COPY_path + '\n', - ' U ' + gamma_moved_COPY_path + '\n']), + ' G ' + gamma_moved_COPY_path + '\n']), [], 'merge', sbox.repo_url + '/A', A_COPY_path) expected_output = wc.State( wc_dir, @@ -1298,11 +1392,6 @@ def reintegrate_with_subtree_mergeinfo(sbox): expected_status.add({'A_COPY/D/gamma_moved' : Item(status=' ', wc_rev=17)}) svntest.actions.run_and_verify_commit(wc_dir, expected_output, expected_status, None, wc_dir) - expected_disk.remove('A_COPY/D/gamma') - expected_disk.add({ - 'A/D/gamma_moved' : Item(props={SVN_PROP_MERGEINFO: '/A_COPY_3/D/gamma:9'}, - contents="New content") - }) # r18 - C) Text mod to A/D/gamma_moved svntest.main.file_write(gamma_moved_path, "Even newer content") @@ -1353,7 +1442,7 @@ def reintegrate_with_subtree_mergeinfo(sbox): '' : Item(status=' U'), 'mu' : Item(status=' G'), 'D' : Item(status=' U'), - 'D/gamma_moved' : Item(status=' U'), + 'D/gamma_moved' : Item(status=' G'), # More issue #4309 (see above) }) expected_elision_output = wc.State(A_path, { }) @@ -1397,38 +1486,49 @@ def reintegrate_with_subtree_mergeinfo(sbox): '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"), - # Why do we expect mergeinfo of '/A_COPY/D/G/tauprime:2-9' on - # A/D/G/tauprime? Because this --reintegrate merge is effectively a - # two URL merge of %URL%/A@9 %URL%/A_COPY@9 to 'A'. Since %URL%/A@9 and - # %URL%/A_COPY@9 have a common ancestor in %URL%/A@1 we expect this 2-URL - # merge to record mergeinfo and a component of that mergeinfo describes - # the merge of %URL%/A_COPY@2 to %URL%/A_COPY@9. We see that above on - # A. But we also get it on A's subtrees with explicit mergeinfo, namely - # A/D/G/tauprime. Now I know what you are thinking, "'A_COPY/D/G/tauprime' - # doesn't even exist until r9!", and you are quite right. But this - # inheritance of bogus mergeinfo is a known problem, see - # http://subversion.tigris.org/issues/show_bug.cgi?id=3157#desc8, - # and is not what this test is about, so we won't fail because of it. + # What's with all this mergeinfo? + # + # '/A/D/gamma_moved:2-7,9-12' - Incoming from the merge source. Yes, + # this mergeinfo describes non-existent path-revs, this is the effect + # of issue #3669 'inheritance can result in mergeinfo describing + # nonexistent sources', but there is already a test for that issue so + # we tolerate it here. + # + # '/A_COPY/D/gamma_moved:17-19' - Describes the merge performed. + # + # '/A_COPY_3/D/gamma:9' - Explicit prior to the merge. + # + #'/A_COPY_3/D/gamma_moved:9' - Incoming from the merge source. + # For the curious, this was originally created in r17 when we merged + # ^/A to A_COPY. This merge added A_COPY/D/gamma_moved, which had + # explicit mergeinfo and due to issue #4309 'wrong notification and + # bogus mergeinfo during merge which adds subtree with mergeinfo' + # this file inherited this bogus mergeinfo from A_COPY/D. Yes, this + # is all quite ugly as the intersection or multiple known issues + # is likely to be. However, given that none of this mergeinfo is + # particularly harmful and that this test is *not* about issues #3669 + # or #4309, we are tolerting it. 'D/gamma_moved' : Item( "Even newer content", props={SVN_PROP_MERGEINFO : + '/A/D/gamma_moved:2-7,9-12\n' '/A_COPY/D/gamma_moved:17-19\n' - '/A_COPY_3/D/gamma:9'}), + '/A_COPY_3/D/gamma:9\n' + '/A_COPY_3/D/gamma_moved:9'}), '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_A_skip = wc.State(A_COPY_path, {}) - svntest.actions.run_and_verify_merge(A_path, None, None, - sbox.repo_url + '/A_COPY', None, + run_and_verify_reintegrate(A_path, + sbox.repo_url + '/A_COPY', expected_output, expected_mergeinfo_output, expected_elision_output, expected_A_disk, expected_A_status, expected_A_skip, - None, None, None, None, - None, 1, 1, "--reintegrate", A_path) + None, 1, 1) #---------------------------------------------------------------------- @SkipUnless(server_has_mergeinfo) @@ -1441,11 +1541,11 @@ def multiple_reintegrates_from_the_same_branch(sbox): expected_disk, expected_status = set_up_branch(sbox) # Some paths we'll care about - A_path = os.path.join(wc_dir, "A") - mu_path = os.path.join(wc_dir, "A", "mu") - A_COPY_path = os.path.join(wc_dir, "A_COPY") - psi_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "H", "psi") - Feature_branch_path = os.path.join(wc_dir, "A_FEATURE_BRANCH") + A_path = sbox.ospath('A') + mu_path = sbox.ospath('A/mu') + A_COPY_path = sbox.ospath('A_COPY') + psi_COPY_path = sbox.ospath('A_COPY/D/H/psi') + Feature_branch_path = sbox.ospath('A_FEATURE_BRANCH') Feature_beta_path = os.path.join(wc_dir, "A_FEATURE_BRANCH", "B", "E", "beta") @@ -1485,9 +1585,7 @@ def multiple_reintegrates_from_the_same_branch(sbox): # r11 - Reintegrate the feature branch back to 'A'. svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir) - svntest.actions.run_and_verify_svn(None, None, [], 'merge', '--reintegrate', - sbox.repo_url + '/A_FEATURE_BRANCH', - A_path) + run_reintegrate(sbox.repo_url + '/A_FEATURE_BRANCH', A_path) svntest.actions.run_and_verify_svn(None, None, [], 'ci', '-m', "Reintegrate feature branch back to 'A'", wc_dir) @@ -1495,7 +1593,7 @@ def multiple_reintegrates_from_the_same_branch(sbox): # r12 - Do a --record-only merge from 'A' to the feature branch so we # don't try to merge r11 from trunk during the next sync merge. svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir) - svntest.actions.run_and_verify_svn(None, None, [], 'merge', + svntest.actions.run_and_verify_svn(None, None, [], 'merge', '-c11', '--record-only', sbox.repo_url + '/A', Feature_branch_path) @@ -1579,17 +1677,15 @@ def multiple_reintegrates_from_the_same_branch(sbox): 'D/H/omega' : Item("New content"), }) expected_skip = wc.State(A_path, { }) - svntest.actions.run_and_verify_merge(A_path, None, None, + run_and_verify_reintegrate(A_path, sbox.repo_url + '/A_FEATURE_BRANCH', - None, expected_output, expected_mergeinfo_output, expected_elision_output, expected_disk, expected_status, expected_skip, - None, None, None, None, - None, 1, 1, '--reintegrate', A_path) + None, 1, 1) svntest.actions.run_and_verify_svn(None, None, [], 'ci', '-m', "2nd Reintegrate feature branch back to 'A'", wc_dir) @@ -1631,11 +1727,11 @@ def reintegrate_with_self_referential_mergeinfo(sbox): wc_disk, wc_status = set_up_branch(sbox, nbr_of_branches=0) # Some paths we'll care about - A_path = os.path.join(wc_dir, "A") - A2_path = os.path.join(wc_dir, "A2") - A2_B_path = os.path.join(wc_dir, "A2", "B") - A2_1_path = os.path.join(wc_dir, "A2.1") - A2_1_mu_path = os.path.join(wc_dir, "A2.1", "mu") + A_path = sbox.ospath('A') + A2_path = sbox.ospath('A2') + A2_B_path = sbox.ospath('A2/B') + A2_1_path = sbox.ospath('A2.1') + A2_1_mu_path = sbox.ospath('A2.1/mu') # r6 Copy A to A2 and then manually set some self-referential mergeinfo on # A2/B and A2. @@ -1737,20 +1833,22 @@ def reintegrate_with_self_referential_mergeinfo(sbox): # ..\..\..\subversion\libsvn_fs_fs\tree.c:2886: (apr_err=160013) # ..\..\..\subversion\libsvn_fs_fs\tree.c:669: (apr_err=160013) # svn: File not found: revision 4, path '/A2' - svntest.actions.run_and_verify_merge(A2_path, None, None, - sbox.repo_url + '/A2.1', None, + run_and_verify_reintegrate(A2_path, + sbox.repo_url + '/A2.1', expected_output, expected_mergeinfo_output, expected_elision_output, expected_disk, expected_status, expected_skip, - None, None, None, None, - None, 1, 0, '--reintegrate', A2_path) + None, 1, 0) #---------------------------------------------------------------------- -# Test for issue #3577 '1.7 subtree mergeinfo recording breaks reintegrate'. -@Issue(3577) +# Test for issue #3577 '1.7 subtree mergeinfo recording breaks reintegrate' +# and issue #4329 'automatic merge uses reintegrate type merge if source is +# fully synced'. +@Issue(3577,4329) +@SkipUnless(server_has_mergeinfo) def reintegrate_with_subtree_merges(sbox): "reintegrate with prior subtree merges to source" @@ -1761,12 +1859,13 @@ def reintegrate_with_subtree_merges(sbox): expected_disk, expected_status = set_up_branch(sbox) # Some paths we'll care about - A_path = os.path.join(wc_dir, "A") - mu_COPY_path = os.path.join(wc_dir, "A_COPY", "mu") - A_COPY_path = os.path.join(wc_dir, "A_COPY") - B_COPY_path = os.path.join(wc_dir, "A_COPY", "B") - 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_path = sbox.ospath('A') + psi_path = sbox.ospath('A/D/H/psi') + mu_COPY_path = sbox.ospath('A_COPY/mu') + A_COPY_path = sbox.ospath('A_COPY') + B_COPY_path = sbox.ospath('A_COPY/B') + rho_COPY_path = sbox.ospath('A_COPY/D/G/rho') + H_COPY_path = sbox.ospath('A_COPY/D/H') # r7 - Make a change on the A_COPY branch that will be # reintegrated back to A. @@ -1821,7 +1920,7 @@ def reintegrate_with_subtree_merges(sbox): 'mu' : Item(status='U '), }) expected_mergeinfo_output = wc.State(A_path, { - '' : Item(status=' G'), + '' : Item(status=' U'), }) expected_elision_output = wc.State(A_path, { }) @@ -1869,16 +1968,37 @@ def reintegrate_with_subtree_merges(sbox): 'D/H/omega' : Item("New content"), }) expected_A_skip = wc.State(A_COPY_path, {}) + run_and_verify_reintegrate(A_path, + sbox.repo_url + '/A_COPY', + expected_output, + expected_mergeinfo_output, + expected_elision_output, + expected_A_disk, + expected_A_status, + expected_A_skip, + None, 1, 1) + + # Test issue #4329. Revert previous merge and commit a new edit to + # A/D/H/psi. Attempt the same merge without the --reintegrate option. + # It should succeed because the automatic merge code should detect that + # a reintegrate-style merge is required, that merge should succeed and + # there should be not conflict on A/D/H/psi. + svntest.actions.run_and_verify_svn(None, None, [], 'revert', '-R', wc_dir) + svntest.main.file_write(psi_path, "Non-conflicting trunk edit.\n") + svntest.main.run_svn(None, 'commit', '-m', + 'An edit on trunk prior to reintegrate.', wc_dir) + sbox.simple_update() + expected_A_status.tweak(wc_rev=9) + expected_A_disk.tweak('', props={SVN_PROP_MERGEINFO: '/A_COPY:2-9'}) + expected_A_disk.tweak('D/H/psi', contents='Non-conflicting trunk edit.\n') svntest.actions.run_and_verify_merge(A_path, None, None, sbox.repo_url + '/A_COPY', None, expected_output, expected_mergeinfo_output, expected_elision_output, - expected_A_disk, - expected_A_status, - expected_A_skip, - None, None, None, None, - None, 1, 1, "--reintegrate", A_path) + expected_A_disk, expected_A_status, + expected_A_skip, None, None, None, + None, None, True, False, A_path) #---------------------------------------------------------------------- # Test for issue #3654 'added subtrees with mergeinfo break reintegrate'. @@ -1890,13 +2010,13 @@ def added_subtrees_with_mergeinfo_break_reintegrate(sbox): wc_dir = sbox.wc_dir # Some paths we'll care about - A_path = os.path.join(wc_dir, "A") - nu_path = os.path.join(wc_dir, "A", "C", "nu") - mu_path = os.path.join(wc_dir, "A", "mu") - A_COPY_path = os.path.join(wc_dir, "A_COPY") - lambda_COPY_path = os.path.join(wc_dir, "A_COPY", "B", "lambda") - A_COPY_2_path = os.path.join(wc_dir, "A_COPY_2") - nu_COPY_2_path = os.path.join(wc_dir, "A_COPY_2", "C", "nu") + A_path = sbox.ospath('A') + nu_path = sbox.ospath('A/C/nu') + mu_path = sbox.ospath('A/mu') + A_COPY_path = sbox.ospath('A_COPY') + lambda_COPY_path = sbox.ospath('A_COPY/B/lambda') + A_COPY_2_path = sbox.ospath('A_COPY_2') + nu_COPY_2_path = sbox.ospath('A_COPY_2/C/nu') # Branch A@1 to A_COPY and A_COPY_2 in r2 and r3 respectively. # Make some changes under 'A' in r4-7. @@ -1952,9 +2072,7 @@ def added_subtrees_with_mergeinfo_break_reintegrate(sbox): # r14 - Reintegrate A_COPY to A. svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir) - svntest.actions.run_and_verify_svn(None, svntest.verify.AnyOutput, [], - 'merge', '--reintegrate', - sbox.repo_url + '/A_COPY', A_path) + run_reintegrate(sbox.repo_url + '/A_COPY', A_path) svntest.actions.run_and_verify_svn(None, None, [], 'ci', '-m', 'Reintegrate A_COPY to A.', wc_dir) @@ -2064,16 +2182,15 @@ def added_subtrees_with_mergeinfo_break_reintegrate(sbox): 'D/H/omega' : Item("New content"), }) expected_skip = wc.State(A_COPY_path, {}) - svntest.actions.run_and_verify_merge(A_path, None, None, - sbox.repo_url + '/A_COPY', None, + run_and_verify_reintegrate(A_path, + sbox.repo_url + '/A_COPY', expected_output, expected_mergeinfo_output, expected_elision_output, expected_disk, expected_status, expected_skip, - None, None, None, None, - None, 1, 1, "--reintegrate", A_path) + None, 1, 1) #---------------------------------------------------------------------- # Test for issue #3648 '2-URL merges incorrectly reverse-merge mergeinfo @@ -2086,10 +2203,10 @@ def two_URL_merge_removes_valid_mergeinfo_from_target(sbox): wc_dir = sbox.wc_dir # Some paths we'll care about - lambda_COPY_path = os.path.join(wc_dir, "A_COPY", "B", "lambda") - mu_path = os.path.join(wc_dir, "A", "mu") - A_COPY_path = os.path.join(wc_dir, "A_COPY") - A_COPY_2_path = os.path.join(wc_dir, "A_COPY_2") + lambda_COPY_path = sbox.ospath('A_COPY/B/lambda') + mu_path = sbox.ospath('A/mu') + A_COPY_path = sbox.ospath('A_COPY') + A_COPY_2_path = sbox.ospath('A_COPY_2') # Branch A@1 to A_COPY r2 # Branch A@1 to A_COPY_2 in r3. @@ -2245,14 +2362,14 @@ def reintegrate_creates_bogus_mergeinfo(sbox): sbox.build() wc_dir=sbox.wc_dir - mu_path = os.path.join(sbox.wc_dir, "A", "mu") - lambda_path = os.path.join(sbox.wc_dir, "A", "B", "lambda") - alpha_path = os.path.join(sbox.wc_dir, "A", "B", "E", "alpha") - beta_path = os.path.join(sbox.wc_dir, "A", "B", "E", "beta") - A_path = os.path.join(sbox.wc_dir, "A") - A_path_1 = os.path.join(sbox.wc_dir, "A@1") - A_COPY_path = os.path.join(sbox.wc_dir, "A_COPY") - A_COPY_psi_path = os.path.join(sbox.wc_dir, "A_COPY", "D", "H", "psi") + mu_path = sbox.ospath('A/mu') + lambda_path = sbox.ospath('A/B/lambda') + alpha_path = sbox.ospath('A/B/E/alpha') + beta_path = sbox.ospath('A/B/E/beta') + A_path = sbox.ospath('A') + A_path_1 = sbox.ospath('A@1') + A_COPY_path = sbox.ospath('A_COPY') + A_COPY_psi_path = sbox.ospath('A_COPY/D/H/psi') A_COPY_url = sbox.repo_url + "/A_COPY" # Make 2 commits under /A pushing the repo to rev3 @@ -2280,7 +2397,7 @@ def reintegrate_creates_bogus_mergeinfo(sbox): svntest.main.run_svn(None, "up", wc_dir) # Reintegrate A_COPY to A. The resulting merginfo on A should be - # /A_COPY:4-10 + # /A_COPY:4-6 expected_output = wc.State(A_path, { 'D/H/psi' : Item(status='U '), }) @@ -2313,14 +2430,14 @@ def reintegrate_creates_bogus_mergeinfo(sbox): }) expected_skip = wc.State(A_COPY_path, {}) - svntest.actions.run_and_verify_merge(A_path, None, None, - A_COPY_url, None, + run_and_verify_reintegrate(A_path, + A_COPY_url, expected_output, expected_mergeinfo_output, expected_elision_output, expected_disk, None, expected_skip, - None, None, None, None, None, - 1, 1, "--reintegrate", A_path) + None, + 1, 1) #---------------------------------------------------------------------- @@ -2335,7 +2452,7 @@ def no_source_subtree_mergeinfo(sbox): sbox.build() wc_dir=sbox.wc_dir - svntest.main.file_write(os.path.join(wc_dir, 'A', 'B', 'E', 'alpha'), + svntest.main.file_write(sbox.ospath('A/B/E/alpha'), 'AAA\n' + 'BBB\n' + 'CCC\n') @@ -2344,32 +2461,32 @@ def no_source_subtree_mergeinfo(sbox): # Create branch-1 svntest.main.run_svn(None, 'copy', - os.path.join(wc_dir, 'A', 'B'), - os.path.join(wc_dir, 'A', 'B1')) + sbox.ospath('A/B'), + sbox.ospath('A/B1')) svntest.main.run_svn(None, 'commit', '-m', 'log message', wc_dir) # Create branch-1 svntest.main.run_svn(None, 'copy', - os.path.join(wc_dir, 'A', 'B'), - os.path.join(wc_dir, 'A', 'B2')) + sbox.ospath('A/B'), + sbox.ospath('A/B2')) svntest.main.run_svn(None, 'commit', '-m', 'log message', wc_dir) # Change on trunk - svntest.main.file_write(os.path.join(wc_dir, 'A', 'B', 'E', 'alpha'), + svntest.main.file_write(sbox.ospath('A/B/E/alpha'), 'AAAxx\n' + 'BBB\n' + 'CCC\n') svntest.main.run_svn(None, 'commit', '-m', 'log message', wc_dir) # Change on branch-1 - svntest.main.file_write(os.path.join(wc_dir, 'A', 'B1', 'E', 'alpha'), + svntest.main.file_write(sbox.ospath('A/B1/E/alpha'), 'AAA\n' + 'BBBxx\n' + 'CCC\n') svntest.main.run_svn(None, 'commit', '-m', 'log message', wc_dir) # Change on branch-2 - svntest.main.file_write(os.path.join(wc_dir, 'A', 'B2', 'E', 'alpha'), + svntest.main.file_write(sbox.ospath('A/B2/E/alpha'), 'AAA\n' + 'BBB\n' + 'CCCxx\n') @@ -2377,45 +2494,52 @@ def no_source_subtree_mergeinfo(sbox): svntest.main.run_svn(None, 'update', wc_dir) # Merge trunk to branch-1 - svntest.main.run_svn(None, 'merge', '^/A/B', os.path.join(wc_dir, 'A', 'B1')) + svntest.main.run_svn(None, 'merge', '^/A/B', sbox.ospath('A/B1')) svntest.main.run_svn(None, 'commit', '-m', 'log message', wc_dir) svntest.main.run_svn(None, 'update', wc_dir) # Reintegrate branch-1 subtree to trunk subtree - svntest.main.run_svn(None, 'merge', '--reintegrate', - '^/A/B1/E', os.path.join(wc_dir, 'A', 'B', 'E')) + run_reintegrate('^/A/B1/E', sbox.ospath('A/B/E')) svntest.main.run_svn(None, 'commit', '-m', 'log message', wc_dir) svntest.main.run_svn(None, 'update', wc_dir) # Merge trunk to branch-2 - svntest.main.run_svn(None, 'merge', '^/A/B', os.path.join(wc_dir, 'A', 'B2')) + svntest.main.run_svn(None, 'merge', '^/A/B', sbox.ospath('A/B2')) svntest.main.run_svn(None, 'commit', '-m', 'log message', wc_dir) svntest.main.run_svn(None, 'update', wc_dir) # Reverse merge branch-1 subtree to branch-2 subtree, this removes # the subtree mergeinfo from branch 2 svntest.main.run_svn(None, 'merge', '-r8:2', - '^/A/B1/E', os.path.join(wc_dir, 'A', 'B2', 'E')) + '^/A/B1/E', sbox.ospath('A/B2/E')) svntest.main.run_svn(None, 'commit', '-m', 'log message', wc_dir) svntest.main.run_svn(None, 'update', wc_dir) + # Verify that merge results in no subtree mergeinfo + svntest.actions.run_and_verify_svn(None, [], [], 'propget', 'svn:mergeinfo', + sbox.repo_url + '/A/B2/E') + # Merge trunk to branch-2 - svntest.main.run_svn(None, 'merge', '^/A/B', os.path.join(wc_dir, 'A', 'B2')) + svntest.main.run_svn(None, 'merge', '^/A/B', sbox.ospath('A/B2')) svntest.main.run_svn(None, 'commit', '-m', 'log message', wc_dir) svntest.main.run_svn(None, 'update', wc_dir) + # Verify that there is still no subtree mergeinfo + svntest.actions.run_and_verify_svn(None, [], [], 'propget', 'svn:mergeinfo', + sbox.repo_url + '/A/B2/E') + # Reintegrate branch-2 to trunk, this fails in 1.6.x from 1.6.13. # The error message states revisions /A/B/E:3-11 are missing from # /A/B2/E and yet the mergeinfo on /A/B2 is /A/B:3-11 and /A/B2/E # has no mergeinfo. - expected_output = wc.State(os.path.join(wc_dir, 'A', 'B'), { + expected_output = wc.State(sbox.ospath('A/B'), { 'E' : Item(status=' U'), 'E/alpha' : Item(status='U '), }) - expected_mergeinfo = wc.State(os.path.join(wc_dir, 'A', 'B'), { + expected_mergeinfo = wc.State(sbox.ospath('A/B'), { '' : Item(status=' U'), }) - expected_elision = wc.State(os.path.join(wc_dir, 'A', 'B'), { + expected_elision = wc.State(sbox.ospath('A/B'), { }) expected_disk = wc.State('', { '' : Item(props={SVN_PROP_MERGEINFO : '/A/B2:4-12'}), @@ -2427,24 +2551,15 @@ def no_source_subtree_mergeinfo(sbox): 'F' : Item(), 'lambda' : Item("This is the file 'lambda'.\n"), }) - expected_skip = wc.State(os.path.join(wc_dir, 'A', 'B'), { + expected_skip = wc.State(sbox.ospath('A/B'), { }) - svntest.actions.run_and_verify_merge(os.path.join(wc_dir, 'A', 'B'), - None, None, '^/A/B2', None, + run_and_verify_reintegrate(sbox.ospath('A/B'), + '^/A/B2', expected_output, expected_mergeinfo, expected_elision, expected_disk, None, expected_skip, - None, None, None, None, None, - 1, 1, '--reintegrate', - os.path.join(wc_dir, 'A', 'B')) - # For 1.6 testsuite use: - # svntest.actions.run_and_verify_merge(os.path.join(wc_dir, 'A', 'B'), - # None, None, '^/A/B2', - # expected_output, - # expected_disk, - # None, expected_skip, - # None, None, None, None, None, - # 1, 1, '--reintegrate') + None, + 1, 1) #---------------------------------------------------------------------- @SkipUnless(server_has_mergeinfo) @@ -2457,15 +2572,15 @@ def reintegrate_replaced_source(sbox): wc_dir = sbox.wc_dir expected_disk, expected_status = set_up_branch(sbox) - A_path = os.path.join(sbox.wc_dir, "A") - 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") - mu_COPY_path = os.path.join(sbox.wc_dir, "A_COPY", "mu") + A_path = sbox.ospath('A') + A_COPY_path = sbox.ospath('A_COPY') + beta_COPY_path = sbox.ospath('A_COPY/B/E/beta') + mu_COPY_path = sbox.ospath('A_COPY/mu') # Using cherrypick merges, simulate a series of sync merges from A to # A_COPY with a replace of A_COPY along the way. # - # r6 - Merge r3 from A to A_COPY + # r7 - Merge r3 from A to A_COPY svntest.main.run_svn(None, 'up', wc_dir) svntest.main.run_svn(None, 'merge', sbox.repo_url + '/A', A_COPY_path, '-c3') @@ -2500,7 +2615,7 @@ def reintegrate_replaced_source(sbox): wc_dir) # r12 - Do a final sync merge of A to A_COPY in preparation for - # reintegration. + # reintegration. svntest.main.run_svn(None, 'up', wc_dir) svntest.main.run_svn(None, 'merge', sbox.repo_url + '/A', A_COPY_path) svntest.main.run_svn(None, 'ci', '-m', 'Sycn A_COPY with A', wc_dir) @@ -2561,17 +2676,16 @@ def reintegrate_replaced_source(sbox): 'D/H/omega' : Item("New content"), }) expected_skip = wc.State(A_path, { }) - svntest.actions.run_and_verify_merge(A_path, None, None, - sbox.repo_url + '/A_COPY', None, + run_and_verify_reintegrate(A_path, + sbox.repo_url + '/A_COPY', expected_output, expected_mergeinfo_output, expected_elision_output, expected_disk, expected_status, expected_skip, - [], None, None, None, None, True, True, - '--reintegrate', A_path) - + [], True, True) + #---------------------------------------------------------------------- @SkipUnless(svntest.main.is_posix_os) @Issue(4052) @@ -2607,10 +2721,66 @@ def reintegrate_symlink_deletion(sbox): ## reintegrate # ### TODO: verify something here - svntest.main.run_svn(None, 'merge', '--reintegrate', - A_COPY_url, A_path) + run_reintegrate(A_COPY_url, A_path) + +#---------------------------------------------------------------------- +def no_op_reintegrate(sbox): + """no-op reintegrate""" + + # Make A_COPY branch in r2, and do a few more commits to A in r3-6. + sbox.build() + wc_dir = sbox.wc_dir + A_path = sbox.ospath('A') + A_COPY_path = sbox.ospath('A_COPY') + expected_disk, expected_status = set_up_branch(sbox) + + # Sync merge from trunk to branch + svntest.main.run_svn(None, 'merge', sbox.repo_url + '/A', A_COPY_path) + sbox.simple_commit() + sbox.simple_update() + # Reintegrate; there are no relevant changes on the branch. + # ### TODO: Check the result more carefully than merely that it completed. + run_reintegrate(sbox.repo_url + '/A_COPY', A_path) + +#---------------------------------------------------------------------- +def renamed_branch_reintegrate(sbox): + """reintegrate a branch that has been renamed""" + + # The idea of this test is to ensure that the reintegrate merge is able to + # cope when one or both of the branches have been renamed. + # + # A -1-----3-4-5-6----------------------9-------- + # \ \ / reintegrate + # A_COPY 2--------------7-------- / + # sync \ / + # RENAMED rename 8---------------- + + # TODO: Make some changes between the sync/rename/reintegrate steps so + # the reintegrate merge actually has to do something. + # TODO: Rename the other branch as well. + + # Make A_COPY branch in r2, and do a few more commits to A in r3-6. + sbox.build() + + wc_dir = sbox.wc_dir + A_path = sbox.ospath('A') + A_COPY_path = sbox.ospath('A_COPY') + expected_disk, expected_status = set_up_branch(sbox) + + # Sync merge from trunk to branch + svntest.main.run_svn(None, 'merge', sbox.repo_url + '/A', A_COPY_path) + sbox.simple_commit() + sbox.simple_update() + + # Rename the branch + sbox.simple_move('A_COPY', 'RENAMED') + sbox.simple_commit() + sbox.simple_update() + # Reintegrate; there are no relevant changes on the branch. + # ### TODO: Check the result more carefully than merely that it completed. + run_reintegrate(sbox.repo_url + '/RENAMED@8', A_path) ######################################################################## # Run the tests @@ -2630,12 +2800,15 @@ test_list = [ None, reintegrate_with_subtree_mergeinfo, multiple_reintegrates_from_the_same_branch, reintegrate_with_self_referential_mergeinfo, + reintegrate_with_subtree_merges, added_subtrees_with_mergeinfo_break_reintegrate, two_URL_merge_removes_valid_mergeinfo_from_target, reintegrate_creates_bogus_mergeinfo, no_source_subtree_mergeinfo, reintegrate_replaced_source, reintegrate_symlink_deletion, + no_op_reintegrate, + renamed_branch_reintegrate, ] if __name__ == '__main__': |