#!/usr/bin/env python # # merge_tests.py: testing tree conflicts during merge # # Subversion is a tool for revision control. # See http://subversion.apache.org for more information. # # ==================================================================== # Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. ###################################################################### # General modules import shutil, sys, re, os import time # Our testing module import svntest from svntest import main, wc, verify, actions # (abbreviation) Item = wc.StateItem Skip = svntest.testcase.Skip_deco SkipUnless = svntest.testcase.SkipUnless_deco XFail = svntest.testcase.XFail_deco Issues = svntest.testcase.Issues_deco Issue = svntest.testcase.Issue_deco Wimp = svntest.testcase.Wimp_deco from svntest.main import SVN_PROP_MERGEINFO from svntest.main import server_has_mergeinfo from merge_tests import set_up_branch from merge_tests import svn_copy from merge_tests import svn_merge #---------------------------------------------------------------------- @SkipUnless(server_has_mergeinfo) def delete_file_and_dir(sbox): "merge that deletes items" sbox.build() wc_dir = sbox.wc_dir # Rev 2 copy B to B2 B_path = os.path.join(wc_dir, 'A', 'B') B2_path = os.path.join(wc_dir, 'A', 'B2') B_url = sbox.repo_url + '/A/B' svntest.actions.run_and_verify_svn(None, None, [], 'copy', B_path, B2_path) expected_output = wc.State(wc_dir, { 'A/B2' : Item(verb='Adding'), }) expected_status = svntest.actions.get_virginal_state(wc_dir, 1) expected_status.add({ 'A/B2' : Item(status=' ', wc_rev=2), 'A/B2/E' : Item(status=' ', wc_rev=2), 'A/B2/E/alpha' : Item(status=' ', wc_rev=2), 'A/B2/E/beta' : Item(status=' ', wc_rev=2), 'A/B2/F' : Item(status=' ', wc_rev=2), 'A/B2/lambda' : Item(status=' ', wc_rev=2), }) svntest.actions.run_and_verify_commit(wc_dir, expected_output, expected_status, None, wc_dir) # Rev 3 delete E and lambda from B E_path = os.path.join(B_path, 'E') lambda_path = os.path.join(B_path, 'lambda') svntest.actions.run_and_verify_svn(None, None, [], 'delete', E_path, lambda_path) expected_output = wc.State(wc_dir, { 'A/B/E' : Item(verb='Deleting'), 'A/B/lambda' : Item(verb='Deleting'), }) expected_status.remove('A/B/E', 'A/B/E/alpha', 'A/B/E/beta', 'A/B/lambda') svntest.actions.run_and_verify_commit(wc_dir, expected_output, expected_status, None, wc_dir) def modify_B2(): # Local mods in B2 B2_E_path = os.path.join(B2_path, 'E') B2_lambda_path = os.path.join(B2_path, 'lambda') svntest.actions.run_and_verify_svn(None, None, [], 'propset', 'foo', 'foo_val', B2_E_path, B2_lambda_path) expected_status.tweak( 'A/B2/E', 'A/B2/lambda', status=' M' ) svntest.actions.run_and_verify_status(wc_dir, expected_status) modify_B2() # Merge rev 3 into B2 # The local mods to the paths modified in r3 cause the paths to be # tree-conflicted upon deletion, resulting in only the mergeinfo change # to the target of the merge 'B2'. expected_output = wc.State(B2_path, { '' : Item(), 'lambda' : Item(status=' ', treeconflict='C'), 'E' : Item(status=' ', treeconflict='C'), }) expected_mergeinfo_output = wc.State(B2_path, { '' : Item(status=' U'), }) expected_elision_output = wc.State(B2_path, { }) expected_disk = wc.State('', { '' : Item(props={SVN_PROP_MERGEINFO : '/A/B:3'}), 'E' : Item(props={'foo' : 'foo_val'}), 'E/alpha' : Item("This is the file 'alpha'.\n"), 'E/beta' : Item("This is the file 'beta'.\n"), 'F' : Item(), 'lambda' : Item("This is the file 'lambda'.\n", props={'foo' : 'foo_val'}), }) expected_status2 = wc.State(B2_path, { '' : Item(status=' M'), 'E' : Item(status=' M', treeconflict='C'), 'E/alpha' : Item(status=' '), 'E/beta' : Item(status=' '), 'F' : Item(status=' '), 'lambda' : Item(status=' M', treeconflict='C'), }) expected_status2.tweak(wc_rev=2) expected_skip = wc.State('', { }) svntest.actions.run_and_verify_merge(B2_path, '2', '3', B_url, None, expected_output, expected_mergeinfo_output, expected_elision_output, expected_disk, expected_status2, expected_skip, None, None, None, None, None, True) #---------------------------------------------------------------------- # This is a regression for issue #1176. @Issue(1176) @SkipUnless(server_has_mergeinfo) def merge_catches_nonexistent_target(sbox): "merge should not die if a target file is absent" sbox.build() wc_dir = sbox.wc_dir # Copy G to a new directory, Q. Create Q/newfile. Commit a change # to Q/newfile. Now merge that change... into G. Merge should not # error, rather, it should report the tree conflict and continue. G_path = os.path.join(wc_dir, 'A', 'D', 'G') Q_path = os.path.join(wc_dir, 'A', 'D', 'Q') newfile_path = os.path.join(Q_path, 'newfile') Q_url = sbox.repo_url + '/A/D/Q' # Copy dir A/D/G to A/D/Q svntest.actions.run_and_verify_svn(None, None, [], 'cp', G_path, Q_path) svntest.main.file_append(newfile_path, 'This is newfile.\n') svntest.actions.run_and_verify_svn(None, None, [], 'add', newfile_path) # Add newfile to dir G, creating r2. expected_output = wc.State(wc_dir, { 'A/D/Q' : Item(verb='Adding'), 'A/D/Q/newfile' : Item(verb='Adding'), }) expected_status = svntest.actions.get_virginal_state(wc_dir, 1) expected_status.add({ 'A/D/Q' : Item(status=' ', wc_rev=2), 'A/D/Q/pi' : Item(status=' ', wc_rev=2), 'A/D/Q/rho' : Item(status=' ', wc_rev=2), 'A/D/Q/tau' : Item(status=' ', wc_rev=2), 'A/D/Q/newfile' : Item(status=' ', wc_rev=2), }) ### right now, we cannot denote that Q/newfile is a local-add rather than ### a child of the A/D/Q copy. thus, it appears in the status output as a ### (M)odified child. svntest.actions.run_and_verify_commit(wc_dir, expected_output, expected_status, None, wc_dir) # Change newfile, creating r3. svntest.main.file_append(newfile_path, 'A change to newfile.\n') expected_output = wc.State(wc_dir, { 'A/D/Q/newfile' : Item(verb='Sending'), }) expected_status.tweak('A/D/Q/newfile', wc_rev=3) svntest.actions.run_and_verify_commit(wc_dir, expected_output, expected_status, None, wc_dir) # Merge the change to newfile (from r3) into G, where newfile # doesn't exist. This is a tree conflict (use case 4, see # notes/tree-conflicts/detection.txt). os.chdir(G_path) expected_output = wc.State('', { 'newfile' : Item(status=' ', treeconflict='C'), }) expected_mergeinfo_output = wc.State('', { '' : Item(status=' U'), }) expected_elision_output = wc.State('', { }) expected_status = wc.State('', { '' : Item(status=' M' ), 'pi' : Item(status=' ' ), 'rho' : Item(status=' ' ), 'tau' : Item(status=' ' ), }) expected_status.tweak(wc_rev=1) expected_status.add({ 'newfile': Item(status='! ', treeconflict='C' ) }) expected_status.tweak('', status=' M') expected_disk = wc.State('', { '' : Item(props={SVN_PROP_MERGEINFO : '/A/D/Q:3'}), 'pi' : Item("This is the file 'pi'.\n"), 'rho' : Item("This is the file 'rho'.\n"), 'tau' : Item("This is the file 'tau'.\n"), }) expected_skip = wc.State('', { }) svntest.actions.run_and_verify_merge('', '2', '3', Q_url, None, expected_output, expected_mergeinfo_output, expected_elision_output, expected_disk, expected_status, expected_skip, None, None, None, None, None, True) expected_status.add({ 'newfile' : Item(status='! ', treeconflict='C'), }) svntest.actions.run_and_verify_unquiet_status('', expected_status) #---------------------------------------------------------------------- @SkipUnless(server_has_mergeinfo) def merge_tree_deleted_in_target(sbox): "merge on deleted directory in target" sbox.build() wc_dir = sbox.wc_dir # Copy B to a new directory, I. Modify B/E/alpha, Remove I/E. Now # merge that change... into I. Merge should report a tree conflict. B_path = os.path.join(wc_dir, 'A', 'B') I_path = os.path.join(wc_dir, 'A', 'I') alpha_path = os.path.join(B_path, 'E', 'alpha') B_url = sbox.repo_url + '/A/B' I_url = sbox.repo_url + '/A/I' # Copy B to I, creating r1. svntest.actions.run_and_verify_svn(None, None, [], 'cp', B_url, I_url, '-m', 'rev 2') # Change some files, creating r2. svntest.main.file_append(alpha_path, 'A change to alpha.\n') svntest.main.file_append(os.path.join(B_path, 'lambda'), 'change lambda.\n') svntest.actions.run_and_verify_svn(None, None, [], 'ci', '-m', 'rev 3', B_path) # Remove E, creating r3. E_url = sbox.repo_url + '/A/I/E' svntest.actions.run_and_verify_svn(None, None, [], 'rm', E_url, '-m', 'rev 4') svntest.actions.run_and_verify_svn(None, None, [], 'up', os.path.join(wc_dir,'A')) expected_output = wc.State(I_path, { 'lambda' : Item(status='U '), 'E' : Item(status=' ', treeconflict='C'), }) expected_mergeinfo_output = wc.State(I_path, { '' : Item(status=' U'), }) expected_elision_output = wc.State(I_path, { }) expected_disk = wc.State('', { '' : Item(props={SVN_PROP_MERGEINFO : '/A/B:3'}), 'F' : Item(), 'lambda' : Item("This is the file 'lambda'.\nchange lambda.\n"), }) expected_status = wc.State(I_path, { '' : Item(status=' M'), 'F' : Item(status=' '), 'lambda' : Item(status='M '), }) expected_status.tweak(wc_rev=4) expected_status.add({ 'E' : Item(status='! ', treeconflict='C' ) }) expected_skip = wc.State(I_path, { }) svntest.actions.run_and_verify_merge(I_path, '2', '3', B_url, None, expected_output, expected_mergeinfo_output, expected_elision_output, expected_disk, expected_status, expected_skip, None, None, None, None, None, 1, 0) expected_status.add({ 'E' : Item(status='! ', treeconflict='C'), }) svntest.actions.run_and_verify_unquiet_status(I_path, expected_status) #---------------------------------------------------------------------- # Regression test for issue #2403: Incorrect 3-way merge of "added" # binary file which already exists (unmodified) in the WC @SkipUnless(server_has_mergeinfo) @Issue(2403) def three_way_merge_add_of_existing_binary_file(sbox): "3-way merge of 'file add' into existing binary" sbox.build() wc_dir = sbox.wc_dir # Create a branch of A, creating revision 2. A_url = sbox.repo_url + "/A" branch_A_url = sbox.repo_url + "/copy-of-A" svntest.actions.run_and_verify_svn(None, None, [], "cp", A_url, branch_A_url, "-m", "Creating copy-of-A") # Add a binary file to the WC. theta_contents = open(os.path.join(sys.path[0], "theta.bin"), 'rb').read() # Write PNG file data into 'A/theta'. A_path = os.path.join(wc_dir, 'A') theta_path = os.path.join(wc_dir, 'A', 'theta') svntest.main.file_write(theta_path, theta_contents, 'wb') svntest.main.run_svn(None, "add", theta_path) # Commit the new binary file to the repos, creating revision 3. expected_output = svntest.wc.State(wc_dir, { "A/theta" : Item(verb="Adding (bin)"), }) expected_status = svntest.actions.get_virginal_state(wc_dir, 1) expected_status.add({ "A/theta" : Item(status=" ", wc_rev=3), }) svntest.actions.run_and_verify_commit(wc_dir, expected_output, expected_status, None, wc_dir) # In the working copy, attempt to 'svn merge branch_A_url@2 A_url@3 A'. # We should *not* see a conflict during the merge, but an 'A'. # And after the merge, the status should not report any differences. expected_output = wc.State(wc_dir, { "A/theta" : Item(status=" ", treeconflict='C'), }) expected_elision_output = wc.State(wc_dir, { }) # As greek_state is rooted at / instead of /A (our merge target), we # need a sub-tree of it rather than straight copy. expected_disk = svntest.main.greek_state.subtree("A") expected_disk.add({ "" : Item(props={SVN_PROP_MERGEINFO : '/A:2-3'}), "theta" : Item(theta_contents, props={"svn:mime-type" : "application/octet-stream"}), }) expected_status = svntest.actions.get_virginal_state(wc_dir, 1) expected_status.add({ "A/theta" : Item(status=" ", wc_rev=3, treeconflict='C'), }) expected_status.tweak("A", status=" M") expected_status.remove("") # top-level of the WC expected_status.remove("iota") expected_skip = wc.State("", { }) # If we merge into wc_dir alone, theta appears at the WC root, # which is in the wrong location -- append "/A" to stay on target. # # Note we don't bother checking expected mergeinfo output because # three-way merges record mergeinfo multiple times on the same # path, 'A' in this case. The first recording is reported as ' U' # but the second is reported as ' G'. Our expected tree structures # can't handle checking for multiple values for the same key. svntest.actions.run_and_verify_merge(A_path, "2", "3", branch_A_url, A_url, expected_output, None, # expected_mergeinfo_output expected_elision_output, expected_disk, expected_status, expected_skip, None, None, None, None, None, 1, 0, '--allow-mixed-revisions', A_path) #---------------------------------------------------------------------- # Issue #2515 @Issue(2515) def merge_added_dir_to_deleted_in_target(sbox): "merge an added dir on a deleted dir in target" sbox.build() wc_dir = sbox.wc_dir # copy B to a new directory, I. # delete F in I. # add J to B/F. # merge add to I. B_url = sbox.repo_url + '/A/B' I_url = sbox.repo_url + '/A/I' F_url = sbox.repo_url + '/A/I/F' J_url = sbox.repo_url + '/A/B/F/J' I_path = os.path.join(wc_dir, 'A', 'I') svntest.actions.run_and_verify_svn(None, None, [], 'cp', B_url, I_url, '-m', 'rev 2') svntest.actions.run_and_verify_svn(None, None, [], 'rm', F_url, '-m', 'rev 3') svntest.actions.run_and_verify_svn(None, None, [], 'mkdir', '-m', 'rev 4', J_url) svntest.actions.run_and_verify_svn(None, None, [], 'up', os.path.join(wc_dir,'A')) expected_output = wc.State(I_path, { 'F' : Item(status=' ', treeconflict='C'), }) expected_mergeinfo_output = wc.State(I_path, { '' : Item(status=' U'), }) expected_elision_output = wc.State(I_path, { }) expected_disk = wc.State('', { 'E' : Item(), 'E/alpha' : Item("This is the file 'alpha'.\n"), 'E/beta' : Item("This is the file 'beta'.\n"), 'lambda' : Item("This is the file 'lambda'.\n"), }) expected_skip = wc.State(I_path, { }) svntest.actions.run_and_verify_merge(I_path, '2', '4', B_url, None, expected_output, expected_mergeinfo_output, expected_elision_output, expected_disk, None, expected_skip, None, None, None, None, None, 0, 0) #---------------------------------------------------------------------- # Issue 2584 @Issue(2584) @SkipUnless(server_has_mergeinfo) def merge_add_over_versioned_file_conflicts(sbox): "conflict from merge of add over versioned file" sbox.build() wc_dir = sbox.wc_dir E_path = os.path.join(wc_dir, 'A', 'B', 'E') alpha_path = os.path.join(E_path, 'alpha') new_alpha_path = os.path.join(wc_dir, 'A', 'C', 'alpha') # Create a new "alpha" file, with enough differences to cause a conflict. svntest.main.file_write(new_alpha_path, 'new alpha content\n') # Add and commit the new "alpha" file, creating revision 2. svntest.main.run_svn(None, "add", new_alpha_path) expected_output = svntest.wc.State(wc_dir, { 'A/C/alpha' : Item(verb='Adding'), }) expected_status = svntest.actions.get_virginal_state(wc_dir, 1) expected_status.add({ 'A/C/alpha' : Item(status=' ', wc_rev=2), }) svntest.actions.run_and_verify_commit(wc_dir, expected_output, expected_status, None, wc_dir) # Merge r1:2 from A/C to A/B/E. This will attempt to add A/C/alpha, # but since A/B/E/alpha already exists we get a tree conflict. expected_output = wc.State(E_path, { 'alpha' : Item(status=' ', treeconflict='C'), }) expected_mergeinfo_output = wc.State(E_path, { '' : Item(status=' U'), }) expected_elision_output = wc.State(E_path, { }) expected_disk = wc.State('', { 'alpha' : Item("This is the file 'alpha'.\n"), 'beta' : Item("This is the file 'beta'.\n"), }) expected_status = wc.State(E_path, { '' : Item(status=' M', wc_rev=1), 'alpha' : Item(status=' ', wc_rev=1, treeconflict='C'), 'beta' : Item(status=' ', wc_rev=1), }) expected_skip = wc.State(E_path, { }) svntest.actions.run_and_verify_merge(E_path, '1', '2', sbox.repo_url + '/A/C', None, expected_output, expected_mergeinfo_output, expected_elision_output, expected_disk, expected_status, expected_skip) #---------------------------------------------------------------------- @SkipUnless(server_has_mergeinfo) @Issue(2829) def mergeinfo_recording_in_skipped_merge(sbox): "mergeinfo recording in skipped merge" ## See http://subversion.tigris.org/issues/show_bug.cgi?id=2829. ## # Create a WC with a single branch sbox.build() wc_dir = sbox.wc_dir wc_disk, wc_status = set_up_branch(sbox, True, 1) # Some paths we'll care about A_url = sbox.repo_url + '/A' A_COPY_path = os.path.join(wc_dir, 'A_COPY') mu_path = os.path.join(wc_dir, 'A', 'mu') alpha_path = os.path.join(wc_dir, 'A', 'B', 'E', 'alpha') A_COPY_B_E_path = os.path.join(wc_dir, 'A_COPY', 'B', 'E') A_COPY_alpha_path = os.path.join(wc_dir, 'A_COPY', 'B', 'E', 'alpha') A_COPY_beta_path = os.path.join(wc_dir, 'A_COPY', 'B', 'E', 'beta') # Make a modification to A/mu svntest.main.file_write(mu_path, "This is the file 'mu' modified.\n") expected_output = wc.State(wc_dir, {'A/mu' : Item(verb='Sending')}) wc_status.add({'A/mu' : Item(status=' ', wc_rev=3)}) svntest.actions.run_and_verify_commit(wc_dir, expected_output, wc_status, None, wc_dir) # Make a modification to A/B/E/alpha svntest.main.file_write(alpha_path, "This is the file 'alpha' modified.\n") expected_output = wc.State(wc_dir, {'A/B/E/alpha' : Item(verb='Sending')}) wc_status.add({'A/B/E/alpha' : Item(status=' ', wc_rev=4)}) svntest.actions.run_and_verify_commit(wc_dir, expected_output, wc_status, None, wc_dir) # Delete A_COPY/B/E svntest.actions.run_and_verify_svn(None, None, [], 'rm', A_COPY_B_E_path) # Merge /A to /A_COPY ie., r1 to r4 expected_output = wc.State(A_COPY_path, { 'mu' : Item(status='U '), 'B/E' : Item(status=' ', treeconflict='C'), }) expected_mergeinfo_output = wc.State(A_COPY_path, { '' : Item(status=' U'), }) expected_elision_output = wc.State(A_COPY_path, { }) expected_status = wc.State(A_COPY_path, { '' : Item(status=' M', wc_rev=2), 'mu' : Item(status='M ', wc_rev=2), 'B' : Item(status=' ', wc_rev=2), 'B/lambda' : Item(status=' ', wc_rev=2), 'B/F' : Item(status=' ', wc_rev=2), 'B/E' : Item(status='D ', wc_rev=2, treeconflict='C'), 'B/E/alpha': Item(status='D ', wc_rev=2), 'B/E/beta' : Item(status='D ', wc_rev=2), 'C' : Item(status=' ', wc_rev=2), 'D' : Item(status=' ', wc_rev=2), 'D/gamma' : Item(status=' ', wc_rev=2), 'D/G' : Item(status=' ', wc_rev=2), 'D/G/pi' : Item(status=' ', wc_rev=2), 'D/G/rho' : Item(status=' ', wc_rev=2), 'D/G/tau' : Item(status=' ', wc_rev=2), 'D/H' : Item(status=' ', wc_rev=2), 'D/H/chi' : Item(status=' ', wc_rev=2), 'D/H/omega': Item(status=' ', wc_rev=2), 'D/H/psi' : Item(status=' ', wc_rev=2), }) expected_disk = wc.State('', { '' : Item(props={SVN_PROP_MERGEINFO : '/A:2-4'}), 'mu' : Item("This is the file 'mu' modified.\n"), 'C' : Item(), 'D' : Item(), 'B' : Item(), 'B/lambda' : Item(contents="This is the file 'lambda'.\n"), 'B/F' : Item(), 'B/E' : Item(), 'D/gamma' : Item("This is the file 'gamma'.\n"), '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/H' : Item(), 'D/H/chi' : Item("This is the file 'chi'.\n"), 'D/H/omega': Item("This is the file 'omega'.\n"), 'D/H/psi' : Item("This is the file 'psi'.\n"), }) if svntest.main.wc_is_singledb(sbox.wc_dir): # Delete removes directories in single-db expected_disk.remove('B/E') expected_skip = wc.State(A_COPY_path, {}) svntest.actions.run_and_verify_merge(A_COPY_path, None, None, A_url, None, expected_output, expected_mergeinfo_output, expected_elision_output, expected_disk, expected_status, expected_skip, None, None, None, None, None, 1, 1) #---------------------------------------------------------------------- def del_differing_file(sbox): "merge tries to delete a file of different content" # Setup a standard greek tree in r1. sbox.build() saved_cwd = os.getcwd() os.chdir(sbox.wc_dir) sbox.wc_dir = '' source = 'A/D/G' s_rev_orig = 1 # Delete files in the source sbox.simple_rm(source+"/tau") sbox.simple_commit(source) s_rev_tau = 2 sbox.simple_rm(source+"/pi") sbox.simple_commit(source) s_rev_pi = 3 # Copy a file, modify it, and merge a deletion to it. target = 'A/D/G2' svn_copy(s_rev_orig, source, target) svntest.main.file_append(target+"/tau", "An extra line in the target.\n") svntest.actions.run_and_verify_svn(None, None, [], 'propset', 'newprop', 'v', target+"/pi") dir_D = os.path.join('A','D') dir_G2 = os.path.join(dir_D, 'G2') tau = os.path.join(dir_D,'G2','tau') pi = os.path.join(dir_D, 'G2', 'pi') # Should complain and "skip" it. svn_merge(s_rev_tau, source, target, [ "--- Merging r2 into '%s':\n" % dir_G2, " C %s\n" % tau, "--- Recording mergeinfo for merge of r2 into '%s':\n" % (dir_G2), " U %s\n" % (dir_G2), "Summary of conflicts:\n", " Tree conflicts: 1\n"]) svn_merge(s_rev_pi, source, target, [ "--- Merging r3 into '%s':\n" % dir_G2, " C %s\n" % pi, "--- Recording mergeinfo for merge of r3 into '%s':\n" % (dir_G2), " G %s\n" % (dir_G2), "Summary of conflicts:\n", " Tree conflicts: 1\n"]) # Copy a file, modify it, commit, and merge a deletion to it. target = 'A/D/G3' svn_copy(s_rev_orig, source, target) svntest.main.file_append(target+"/tau", "An extra line in the target.\n") svntest.actions.run_and_verify_svn(None, None, [], 'propset', 'newprop', 'v', target+"/pi") sbox.simple_commit(target) dir_G3 = os.path.join(dir_D, 'G3') tau = os.path.join(dir_D,'G3','tau') pi = os.path.join(dir_D, 'G3', 'pi') # Should complain and "skip" it. svn_merge(s_rev_tau, source, target, [ "--- Merging r2 into '%s':\n" % dir_G3, " C %s\n" % tau, "--- Recording mergeinfo for merge of r2 into '%s':\n" % (dir_G3), " U %s\n" % (dir_G3), "Summary of conflicts:\n", " Tree conflicts: 1\n"]) svn_merge(s_rev_pi, source, target, [ "--- Merging r3 into '%s':\n" % dir_G3, " C %s\n" % pi, "--- Recording mergeinfo for merge of r3 into '%s':\n" % (dir_G3), " G %s\n" % (dir_G3), "Summary of conflicts:\n", " Tree conflicts: 1\n"]) os.chdir(saved_cwd) #---------------------------------------------------------------------- # This test used to involve tree conflicts, hence its name. @Issue(3146) def tree_conflicts_and_obstructions(sbox): "tree conflicts and obstructions" ## See http://subversion.tigris.org/issues/show_bug.cgi?id=3146. ## sbox.build() wc_dir = sbox.wc_dir trunk_url = sbox.repo_url + '/A/B/E' branch_path = os.path.join(wc_dir, 'branch') br_alpha_moved = os.path.join(branch_path, 'alpha-moved') # Create a branch svntest.actions.run_and_verify_svn(None, None, [], 'cp', trunk_url, sbox.repo_url + '/branch', '-m', "Creating the Branch") svntest.actions.run_and_verify_svn(None, None, [], 'mv', trunk_url + '/alpha', trunk_url + '/alpha-moved', '-m', "Move alpha to alpha-moved") # Update to revision 2. svntest.actions.run_and_verify_svn(None, None, [], 'update', wc_dir) svntest.main.file_write(br_alpha_moved, "I am blocking myself from trunk\n") branch_path = os.path.join(wc_dir, "branch") # Merge the obstructions into the branch. expected_output = svntest.wc.State(branch_path, { 'alpha' : Item(status='D '), }) expected_mergeinfo_output = wc.State(branch_path, { '' : Item(status=' U'), }) expected_elision_output = wc.State(branch_path, { }) expected_disk = wc.State('', { 'beta' : Item("This is the file 'beta'.\n"), 'alpha-moved' : Item("I am blocking myself from trunk\n"), }) expected_status = wc.State(branch_path, { '' : Item(status=' M', wc_rev=3), 'alpha' : Item(status='D ', wc_rev=3), 'beta' : Item(status=' ', wc_rev=3), }) expected_skip = wc.State(branch_path, { 'alpha-moved' : Item(), }) svntest.actions.run_and_verify_merge(branch_path, '1', 'HEAD', trunk_url, None, expected_output, expected_mergeinfo_output, expected_elision_output, expected_disk, expected_status, expected_skip) #---------------------------------------------------------------------- # Detect tree conflicts among files and directories, # edited or deleted in a deep directory structure. # # See use cases 4-6 in notes/tree-conflicts/use-cases.txt for background. # Note that we do not try to track renames. The only difference from # the behavior of Subversion 1.4 and 1.5 is the conflicted status of the # parent directory. # convenience definitions leaf_edit = svntest.actions.deep_trees_leaf_edit tree_del = svntest.actions.deep_trees_tree_del leaf_del = svntest.actions.deep_trees_leaf_del disk_after_leaf_edit = svntest.actions.deep_trees_after_leaf_edit disk_after_leaf_del = svntest.actions.deep_trees_after_leaf_del disk_after_tree_del = svntest.actions.deep_trees_after_tree_del disk_after_leaf_del_no_ci = svntest.actions.deep_trees_after_leaf_del_no_ci disk_after_tree_del_no_ci = svntest.actions.deep_trees_after_tree_del_no_ci deep_trees_conflict_output = svntest.actions.deep_trees_conflict_output j = os.path.join DeepTreesTestCase = svntest.actions.DeepTreesTestCase alpha_beta_gamma = svntest.wc.State('', { 'F/alpha' : Item(), 'DF/D1/beta' : Item(), 'DDF/D1/D2/gamma' : Item(), }) #---------------------------------------------------------------------- def tree_conflicts_on_merge_local_ci_4_1(sbox): "tree conflicts 4.1: tree del, leaf edit" # use case 4, as in notes/tree-conflicts/use-cases.txt # 4.1) local tree delete, incoming leaf edit expected_output = deep_trees_conflict_output expected_disk = disk_after_tree_del expected_status = svntest.wc.State('', { '' : Item(status=' M', wc_rev='3'), 'F' : Item(status=' ', wc_rev='3'), 'D' : Item(status=' ', wc_rev='3'), 'DF' : Item(status=' ', wc_rev='3'), 'DD' : Item(status=' ', wc_rev='3'), 'DDF' : Item(status=' ', wc_rev='3'), 'DDD' : Item(status=' ', wc_rev='3'), 'D/D1' : Item(status='! ', treeconflict='C'), 'F/alpha' : Item(status='! ', treeconflict='C'), 'DD/D1' : Item(status='! ', treeconflict='C'), 'DF/D1' : Item(status='! ', treeconflict='C'), 'DDD/D1' : Item(status='! ', treeconflict='C'), 'DDF/D1' : Item(status='! ', treeconflict='C'), }) expected_skip = svntest.wc.State('', { }) svntest.actions.deep_trees_run_tests_scheme_for_merge(sbox, [ DeepTreesTestCase("local_tree_del_incoming_leaf_edit", tree_del, leaf_edit, expected_output, expected_disk, expected_status, expected_skip) ], True) #---------------------------------------------------------------------- def tree_conflicts_on_merge_local_ci_4_2(sbox): "tree conflicts 4.2: tree del, leaf del" # 4.2) local tree delete, incoming leaf delete expected_output = deep_trees_conflict_output expected_disk = disk_after_tree_del expected_status = svntest.wc.State('', { '' : Item(status=' M', wc_rev='3'), 'F' : Item(status=' ', wc_rev='3'), 'D' : Item(status=' ', wc_rev='3'), 'DF' : Item(status=' ', wc_rev='3'), 'DD' : Item(status=' ', wc_rev='3'), 'DDF' : Item(status=' ', wc_rev='3'), 'DDD' : Item(status=' ', wc_rev='3'), 'F/alpha' : Item(status='! ', treeconflict='C'), 'D/D1' : Item(status='! ', treeconflict='C'), 'DF/D1' : Item(status='! ', treeconflict='C'), 'DD/D1' : Item(status='! ', treeconflict='C'), 'DDF/D1' : Item(status='! ', treeconflict='C'), 'DDD/D1' : Item(status='! ', treeconflict='C'), }) expected_skip = svntest.wc.State('', { }) svntest.actions.deep_trees_run_tests_scheme_for_merge(sbox, [ DeepTreesTestCase("local_tree_del_incoming_leaf_del", tree_del, leaf_del, expected_output, expected_disk, expected_status, expected_skip) ], True) #---------------------------------------------------------------------- @XFail() @Issue(2282) def tree_conflicts_on_merge_local_ci_5_1(sbox): "tree conflicts 5.1: leaf edit, tree del" # use case 5, as in notes/tree-conflicts/use-cases.txt # 5.1) local leaf edit, incoming tree delete expected_output = deep_trees_conflict_output expected_disk = disk_after_leaf_edit # We should detect 6 tree conflicts, and nothing should be deleted (when # we skip tree conflict victims). expected_status = svntest.wc.State('', { '' : Item(status=' M', wc_rev='3'), 'D' : Item(status=' ', wc_rev='3'), 'D/D1' : Item(status=' ', treeconflict='C', wc_rev='4'), 'D/D1/delta' : Item(status='D ', wc_rev='4'), 'DD' : Item(status=' ', wc_rev='3'), 'DD/D1' : Item(status=' ', treeconflict='C', wc_rev='4'), 'DD/D1/D2' : Item(status='D ', wc_rev='3'), 'DD/D1/D2/epsilon' : Item(status='D ', wc_rev='4'), 'DDD' : Item(status=' ', wc_rev='3'), 'DDD/D1' : Item(status=' ', treeconflict='C', wc_rev='4'), 'DDD/D1/D2' : Item(status='D ', wc_rev='3'), 'DDD/D1/D2/D3' : Item(status='D ', wc_rev='3'), 'DDD/D1/D2/D3/zeta' : Item(status='D ', wc_rev='4'), 'DDF' : Item(status=' ', wc_rev='3'), 'DDF/D1' : Item(status=' ', treeconflict='C', wc_rev='4'), 'DDF/D1/D2' : Item(status='D ', wc_rev='3'), 'DDF/D1/D2/gamma' : Item(status='D ', wc_rev='4'), 'DF' : Item(status=' ', wc_rev='3'), 'DF/D1' : Item(status=' ', treeconflict='C', wc_rev='4'), 'DF/D1/beta' : Item(status='D ', wc_rev='4'), 'F' : Item(status=' ', wc_rev='3'), 'F/alpha' : Item(status=' ', treeconflict='C', wc_rev='4'), }) expected_skip = svntest.wc.State('', { }) svntest.actions.deep_trees_run_tests_scheme_for_merge(sbox, [ DeepTreesTestCase("local_leaf_edit_incoming_tree_del", leaf_edit, tree_del, expected_output, expected_disk, expected_status, expected_skip) ], True) #---------------------------------------------------------------------- @XFail() @Issue(2282) def tree_conflicts_on_merge_local_ci_5_2(sbox): "tree conflicts 5.2: leaf del, tree del" # 5.2) local leaf del, incoming tree delete expected_output = deep_trees_conflict_output expected_disk = disk_after_leaf_del expected_status = svntest.wc.State('', { '' : Item(status=' M', wc_rev='3'), 'D' : Item(status=' ', wc_rev='3'), 'F' : Item(status=' ', wc_rev='3'), 'DD' : Item(status=' ', wc_rev='3'), 'DD/D1' : Item(status='! ', treeconflict='C'), 'DF' : Item(status=' ', wc_rev='3'), 'DF/D1' : Item(status='! ', treeconflict='C'), 'DDD' : Item(status=' ', wc_rev='3'), 'DDD/D1' : Item(status='! ', treeconflict='C'), 'DDD/D1/D2' : Item(status='D ', wc_rev='3'), 'DDF' : Item(status=' ', wc_rev='3'), 'DDF/D1' : Item(status='! ', treeconflict='C'), 'DDF/D1/D2' : Item(status='D ', wc_rev='3'), 'D/D1' : Item(status='! ', treeconflict='C'), 'F/alpha' : Item(status='! ', treeconflict='C'), }) expected_skip = svntest.wc.State('', { }) svntest.actions.deep_trees_run_tests_scheme_for_merge(sbox, [ DeepTreesTestCase("local_leaf_del_incoming_tree_del", leaf_del, tree_del, expected_output, expected_disk, expected_status, expected_skip) ], True) #---------------------------------------------------------------------- def tree_conflicts_on_merge_local_ci_6(sbox): "tree conflicts 6: tree del, tree del" # use case 6, as in notes/tree-conflicts/use-cases.txt # local tree delete, incoming tree delete expected_output = deep_trees_conflict_output expected_disk = disk_after_tree_del expected_status = svntest.wc.State('', { '' : Item(status=' M', wc_rev='3'), 'D' : Item(status=' ', wc_rev='3'), 'F' : Item(status=' ', wc_rev='3'), 'DD' : Item(status=' ', wc_rev='3'), 'DF' : Item(status=' ', wc_rev='3'), 'DDD' : Item(status=' ', wc_rev='3'), 'DDF' : Item(status=' ', wc_rev='3'), 'D/D1' : Item(status='! ', treeconflict='C'), 'F/alpha' : Item(status='! ', treeconflict='C'), 'DD/D1' : Item(status='! ', treeconflict='C'), 'DF/D1' : Item(status='! ', treeconflict='C'), 'DDD/D1' : Item(status='! ', treeconflict='C'), 'DDF/D1' : Item(status='! ', treeconflict='C'), }) expected_skip = svntest.wc.State('', { }) svntest.actions.deep_trees_run_tests_scheme_for_merge(sbox, [ DeepTreesTestCase("local_tree_del_incoming_tree_del", tree_del, tree_del, expected_output, expected_disk, expected_status, expected_skip) ], True) #---------------------------------------------------------------------- def tree_conflicts_on_merge_no_local_ci_4_1(sbox): "tree conflicts 4.1: tree del (no ci), leaf edit" sbox.build() # use case 4, as in notes/tree-conflicts/use-cases.txt # 4.1) local tree delete, incoming leaf edit expected_output = deep_trees_conflict_output expected_disk = disk_after_tree_del_no_ci(sbox.wc_dir) expected_status = svntest.wc.State('', { '' : Item(status=' M', wc_rev='3'), 'D' : Item(status=' ', wc_rev='3'), 'D/D1' : Item(status='D ', treeconflict='C', wc_rev='3'), 'DD' : Item(status=' ', wc_rev='3'), 'DD/D1' : Item(status='D ', treeconflict='C', wc_rev='3'), 'DD/D1/D2' : Item(status='D ', wc_rev='3'), 'DDD' : Item(status=' ', wc_rev='3'), 'DDD/D1' : Item(status='D ', treeconflict='C', wc_rev='3'), 'DDD/D1/D2' : Item(status='D ', wc_rev='3'), 'DDD/D1/D2/D3' : Item(status='D ', wc_rev='3'), 'DDF' : Item(status=' ', wc_rev='3'), 'DDF/D1' : Item(status='D ', treeconflict='C', wc_rev='3'), 'DDF/D1/D2' : Item(status='D ', wc_rev='3'), 'DDF/D1/D2/gamma' : Item(status='D ', wc_rev='3'), 'DF' : Item(status=' ', wc_rev='3'), 'DF/D1' : Item(status='D ', treeconflict='C', wc_rev='3'), 'DF/D1/beta' : Item(status='D ', wc_rev='3'), 'F' : Item(status=' ', wc_rev='3'), 'F/alpha' : Item(status='D ', treeconflict='C', wc_rev='3'), }) expected_skip = svntest.wc.State('', { }) svntest.actions.deep_trees_run_tests_scheme_for_merge(sbox, [ DeepTreesTestCase( "local_tree_del_incoming_leaf_edit", tree_del, leaf_edit, expected_output, expected_disk, expected_status, expected_skip, ) ], False) #---------------------------------------------------------------------- def tree_conflicts_on_merge_no_local_ci_4_2(sbox): "tree conflicts 4.2: tree del (no ci), leaf del" sbox.build() # 4.2) local tree delete, incoming leaf delete expected_output = deep_trees_conflict_output expected_disk = disk_after_tree_del_no_ci(sbox.wc_dir) expected_status = svntest.wc.State('', { '' : Item(status=' M', wc_rev='3'), 'D' : Item(status=' ', wc_rev='3'), 'D/D1' : Item(status='D ', treeconflict='C', wc_rev='3'), 'DD' : Item(status=' ', wc_rev='3'), 'DD/D1' : Item(status='D ', treeconflict='C', wc_rev='3'), 'DD/D1/D2' : Item(status='D ', wc_rev='3'), 'DDD' : Item(status=' ', wc_rev='3'), 'DDD/D1' : Item(status='D ', treeconflict='C', wc_rev='3'), 'DDD/D1/D2' : Item(status='D ', wc_rev='3'), 'DDD/D1/D2/D3' : Item(status='D ', wc_rev='3'), 'DDF' : Item(status=' ', wc_rev='3'), 'DDF/D1' : Item(status='D ', treeconflict='C', wc_rev='3'), 'DDF/D1/D2' : Item(status='D ', wc_rev='3'), 'DDF/D1/D2/gamma' : Item(status='D ', wc_rev='3'), 'DF' : Item(status=' ', wc_rev='3'), 'DF/D1' : Item(status='D ', treeconflict='C', wc_rev='3'), 'DF/D1/beta' : Item(status='D ', wc_rev='3'), 'F' : Item(status=' ', wc_rev='3'), 'F/alpha' : Item(status='D ', treeconflict='C', wc_rev='3'), }) expected_skip = svntest.wc.State('', { }) svntest.actions.deep_trees_run_tests_scheme_for_merge(sbox, [ DeepTreesTestCase( "local_tree_del_incoming_leaf_del", tree_del, leaf_del, expected_output, expected_disk, expected_status, expected_skip, ) ], False) #---------------------------------------------------------------------- def tree_conflicts_on_merge_no_local_ci_5_1(sbox): "tree conflicts 5.1: leaf edit (no ci), tree del" # use case 5, as in notes/tree-conflicts/use-cases.txt # 5.1) local leaf edit, incoming tree delete expected_output = deep_trees_conflict_output expected_disk = disk_after_leaf_edit expected_status = svntest.wc.State('', { '' : Item(status=' M', wc_rev='3'), 'D' : Item(status=' ', wc_rev='3'), 'D/D1' : Item(status=' M', treeconflict='C', wc_rev='3'), 'D/D1/delta' : Item(status='A ', wc_rev='0'), 'DD' : Item(status=' ', wc_rev='3'), 'DD/D1' : Item(status=' ', treeconflict='C', wc_rev='3'), 'DD/D1/D2' : Item(status=' M', wc_rev='3'), 'DD/D1/D2/epsilon' : Item(status='A ', wc_rev='0'), 'DDD' : Item(status=' ', wc_rev='3'), 'DDD/D1' : Item(status=' ', treeconflict='C', wc_rev='3'), 'DDD/D1/D2' : Item(status=' ', wc_rev='3'), 'DDD/D1/D2/D3' : Item(status=' M', wc_rev='3'), 'DDD/D1/D2/D3/zeta' : Item(status='A ', wc_rev='0'), 'DDF' : Item(status=' ', wc_rev='3'), 'DDF/D1' : Item(status=' ', treeconflict='C', wc_rev='3'), 'DDF/D1/D2' : Item(status=' ', wc_rev='3'), 'DDF/D1/D2/gamma' : Item(status='MM', wc_rev='3'), 'DF' : Item(status=' ', wc_rev='3'), 'DF/D1' : Item(status=' ', treeconflict='C', wc_rev='3'), 'DF/D1/beta' : Item(status='MM', wc_rev='3'), 'F' : Item(status=' ', wc_rev='3'), 'F/alpha' : Item(status='MM', treeconflict='C', wc_rev='3'), }) expected_skip = svntest.wc.State('', { }) svntest.actions.deep_trees_run_tests_scheme_for_merge(sbox, [ DeepTreesTestCase( "local_leaf_edit_incoming_tree_del", leaf_edit, tree_del, expected_output, expected_disk, expected_status, expected_skip, ) ], False) #---------------------------------------------------------------------- @XFail() @Issue(2282) def tree_conflicts_on_merge_no_local_ci_5_2(sbox): "tree conflicts 5.2: leaf del (no ci), tree del" # 5.2) local leaf del, incoming tree delete expected_output = deep_trees_conflict_output expected_disk = disk_after_leaf_del_no_ci(sbox.wc_dir) expected_status = svntest.wc.State('', { '' : Item(status=' M', wc_rev='3'), 'D' : Item(status=' ', wc_rev='3'), 'D/D1' : Item(status='D ', wc_rev='3', treeconflict='C'), 'F' : Item(status=' ', wc_rev='3'), 'F/alpha' : Item(status='D ', wc_rev='3', treeconflict='C'), 'DD' : Item(status=' ', wc_rev='3'), 'DD/D1' : Item(status='D ', wc_rev='3', treeconflict='C'), 'DD/D1/D2' : Item(status='D ', wc_rev='3'), 'DF' : Item(status=' ', wc_rev='3'), 'DF/D1' : Item(status='D ', wc_rev='3', treeconflict='C'), 'DF/D1/beta' : Item(status='D ', wc_rev='3'), 'DDD' : Item(status=' ', wc_rev='3'), 'DDD/D1' : Item(status='D ', wc_rev='3', treeconflict='C'), 'DDD/D1/D2' : Item(status='D ', wc_rev='3'), 'DDD/D1/D2/D3' : Item(status='D ', wc_rev='3'), 'DDF' : Item(status=' ', wc_rev='3'), 'DDF/D1' : Item(status='D ', wc_rev='3', treeconflict='C'), 'DDF/D1/D2' : Item(status='D ', wc_rev='3'), 'DDF/D1/D2/gamma' : Item(status='D ', wc_rev='3'), }) expected_skip = svntest.wc.State('', { }) svntest.actions.deep_trees_run_tests_scheme_for_merge(sbox, [ DeepTreesTestCase( "local_leaf_del_incoming_tree_del", leaf_del, tree_del, expected_output, expected_disk, expected_status, expected_skip, ) ], False) #---------------------------------------------------------------------- def tree_conflicts_on_merge_no_local_ci_6(sbox): "tree conflicts 6: tree del (no ci), tree del" sbox.build() # use case 6, as in notes/tree-conflicts/use-cases.txt # local tree delete, incoming tree delete expected_output = deep_trees_conflict_output expected_disk = disk_after_tree_del_no_ci(sbox.wc_dir) expected_status = svntest.wc.State('', { '' : Item(status=' M', wc_rev='3'), 'D' : Item(status=' ', wc_rev='3'), 'D/D1' : Item(status='D ', wc_rev='3', treeconflict='C'), 'F' : Item(status=' ', wc_rev='3'), 'F/alpha' : Item(status='D ', wc_rev='3', treeconflict='C'), 'DD' : Item(status=' ', wc_rev='3'), 'DD/D1' : Item(status='D ', wc_rev='3', treeconflict='C'), 'DD/D1/D2' : Item(status='D ', wc_rev='3'), 'DF' : Item(status=' ', wc_rev='3'), 'DF/D1' : Item(status='D ', wc_rev='3', treeconflict='C'), 'DF/D1/beta' : Item(status='D ', wc_rev='3'), 'DDD' : Item(status=' ', wc_rev='3'), 'DDD/D1' : Item(status='D ', wc_rev='3', treeconflict='C'), 'DDD/D1/D2' : Item(status='D ', wc_rev='3'), 'DDD/D1/D2/D3' : Item(status='D ', wc_rev='3'), 'DDF' : Item(status=' ', wc_rev='3'), 'DDF/D1' : Item(status='D ', wc_rev='3', treeconflict='C'), 'DDF/D1/D2' : Item(status='D ', wc_rev='3'), 'DDF/D1/D2/gamma' : Item(status='D ', wc_rev='3'), }) expected_skip = svntest.wc.State('', { }) svntest.actions.deep_trees_run_tests_scheme_for_merge(sbox, [ DeepTreesTestCase( "local_tree_del_incoming_tree_del", tree_del, tree_del, expected_output, expected_disk, expected_status, expected_skip, ) ], False) #---------------------------------------------------------------------- def tree_conflicts_merge_edit_onto_missing(sbox): "tree conflicts: tree missing, leaf edit" # local tree missing (via shell delete), incoming leaf edit # Note: In 1.7 merge tracking aware merges raise an error if the # merge target has subtrees missing due to a shell delete. To # preserve the original intent of this test we'll run the merge # with the --ignore-ancestry option, which neither considers nor # records mergeinfo. With this option the merge should "succeed" # while skipping the missing paths. Of course with no mergeinfo # recorded and everything skipped, there is nothing to commit, so # unlike most of the tree conflict tests we don't bother with the # final commit step. sbox.build() expected_output = wc.State('', { }) expected_disk = disk_after_tree_del # Don't expect any mergeinfo property changes because we run # the merge with the --ignore-ancestry option. expected_status = svntest.wc.State('', { '' : Item(status=' ', wc_rev=3), 'F' : Item(status=' ', wc_rev=3), 'F/alpha' : Item(status='! ', wc_rev=3), 'D' : Item(status=' ', wc_rev=3), 'D/D1' : Item(status='! ', wc_rev='?'), 'DF' : Item(status=' ', wc_rev=3), 'DF/D1' : Item(status='! ', wc_rev='?'), 'DF/D1/beta' : Item(status=' '), 'DD' : Item(status=' ', wc_rev=3), 'DD/D1' : Item(status='! ', wc_rev='?'), 'DD/D1/D2' : Item(status=' '), 'DDF' : Item(status=' ', wc_rev=3), 'DDF/D1' : Item(status='! ', wc_rev='?'), 'DDF/D1/D2' : Item(status=' '), 'DDF/D1/D2/gamma' : Item(status=' '), 'DDD' : Item(status=' ', wc_rev=3), 'DDD/D1' : Item(status='! ', wc_rev='?'), 'DDD/D1/D2' : Item(status=' '), 'DDD/D1/D2/D3' : Item(status=' '), }) if svntest.main.wc_is_singledb(sbox.wc_dir): expected_status.tweak('D/D1', wc_rev=3, entry_rev='?') expected_status.tweak('DF/D1', wc_rev=3, entry_rev='?') expected_status.tweak('DF/D1/beta', wc_rev=3, status='! ') expected_status.tweak('DD/D1', wc_rev=3, entry_rev='?') expected_status.tweak('DD/D1/D2', wc_rev=3, status='! ') expected_status.tweak('DDF/D1', wc_rev=3, entry_rev='?') expected_status.tweak('DDF/D1/D2', wc_rev=3, status='! ') expected_status.tweak('DDF/D1/D2/gamma', wc_rev=3, status='! ') expected_status.tweak('DDD/D1', wc_rev=3, entry_rev='?') expected_status.tweak('DDD/D1/D2', wc_rev=3, status='! ') expected_status.tweak('DDD/D1/D2/D3', wc_rev=3, status='! ') expected_skip = svntest.wc.State('', { 'F/alpha' : Item(), # BH: After fixing several issues in the obstruction handling # I get the following Skip notifications. Please review! 'D/D1' : Item(), 'DD/D1' : Item(), 'DF/D1' : Item(), 'DDD/D1' : Item(), 'DDF/D1' : Item(), }) svntest.actions.deep_trees_run_tests_scheme_for_merge(sbox, [ DeepTreesTestCase( "local_tree_missing_incoming_leaf_edit", svntest.actions.deep_trees_rmtree, leaf_edit, expected_output, expected_disk, expected_status, expected_skip, ) ], False, do_commit_conflicts=False, ignore_ancestry=True) #---------------------------------------------------------------------- def tree_conflicts_merge_del_onto_missing(sbox): "tree conflicts: tree missing, leaf del" # local tree missing (via shell delete), incoming leaf edit # Note: In 1.7 merge tracking aware merges raise an error if the # merge target has subtrees missing due to a shell delete. To # preserve the original intent of this test we'll run the merge # with the --ignore-ancestry option, which neither considers nor # records mergeinfo. With this option the merge should "succeed" # while skipping the missing paths. Of course with no mergeinfo # recorded and everything skipped, there is nothing to commit, so # unlike most of the tree conflict tests we don't bother with the # final commit step. sbox.build() expected_output = wc.State('', { }) expected_disk = disk_after_tree_del # Don't expect any mergeinfo property changes because we run # the merge with the --ignore-ancestry option. expected_status = svntest.wc.State('', { '' : Item(status=' ', wc_rev=3), 'F' : Item(status=' ', wc_rev=3), 'F/alpha' : Item(status='! ', wc_rev=3), 'D' : Item(status=' ', wc_rev=3), 'D/D1' : Item(status='! ', wc_rev='?'), 'DF' : Item(status=' ', wc_rev=3), 'DF/D1' : Item(status='! ', wc_rev='?'), 'DF/D1/beta' : Item(status=' '), 'DD' : Item(status=' ', wc_rev=3), 'DD/D1' : Item(status='! ', wc_rev='?'), 'DD/D1/D2' : Item(status=' '), 'DDF' : Item(status=' ', wc_rev=3), 'DDF/D1' : Item(status='! ', wc_rev='?'), 'DDF/D1/D2' : Item(status=' '), 'DDF/D1/D2/gamma' : Item(status=' '), 'DDD' : Item(status=' ', wc_rev=3), 'DDD/D1' : Item(status='! ', wc_rev='?'), 'DDD/D1/D2' : Item(status=' '), 'DDD/D1/D2/D3' : Item(status=' '), }) if svntest.main.wc_is_singledb(sbox.wc_dir): expected_status.tweak('D/D1', wc_rev=3) expected_status.tweak('DF/D1', wc_rev=3) expected_status.tweak('DF/D1/beta', wc_rev=3, status='! ') expected_status.tweak('DD/D1', wc_rev=3) expected_status.tweak('DD/D1/D2', wc_rev=3, status='! ') expected_status.tweak('DDF/D1', wc_rev=3) expected_status.tweak('DDF/D1/D2', wc_rev=3, status='! ') expected_status.tweak('DDF/D1/D2/gamma', wc_rev=3, status='! ') expected_status.tweak('DDD/D1', wc_rev=3) expected_status.tweak('DDD/D1/D2', wc_rev=3, status='! ') expected_status.tweak('DDD/D1/D2/D3', wc_rev=3, status='! ') expected_skip = svntest.wc.State('', { 'F/alpha' : Item(), 'D/D1' : Item(), # BH: After fixing several issues in the obstruction handling # I get the following Skip notifications. Please review! 'D/D1' : Item(), 'DD/D1' : Item(), 'DF/D1' : Item(), 'DDD/D1' : Item(), 'DDF/D1' : Item(), }) svntest.actions.deep_trees_run_tests_scheme_for_merge(sbox, [ DeepTreesTestCase( "local_tree_missing_incoming_leaf_del", svntest.actions.deep_trees_rmtree, leaf_del, expected_output, expected_disk, expected_status, expected_skip, ) ], False, do_commit_conflicts=False, ignore_ancestry=True) #---------------------------------------------------------------------- def merge_replace_setup(sbox): "helper for merge_replace_causes_tree_conflict*()." # svntest.factory.make(sbox,r""" # # make a branch of A # svn cp $URL/A $URL/branch # svn up # # ACTIONS ON THE MERGE SOURCE (branch) # # various deletes of files and dirs # svn delete branch/mu branch/B/E branch/D/G/pi branch/D/H # svn ci # svn up # # # replacements. # # file-with-file # echo "replacement for mu" > branch/mu # svn add branch/mu # # dir-with-dir # svn mkdir branch/B/E # svn ps propname propval branch/B/E # # file-with-dir # svn mkdir branch/D/G/pi # svn ps propname propval branch/D/G/pi # # dir-with-file # echo "replacement for H" > branch/D/H # svn add branch/D/H # svn ci # """) sbox.build() wc_dir = sbox.wc_dir url = sbox.repo_url branch_B_E = os.path.join(wc_dir, 'branch', 'B', 'E') branch_D_G_pi = os.path.join(wc_dir, 'branch', 'D', 'G', 'pi') branch_D_H = os.path.join(wc_dir, 'branch', 'D', 'H') branch_mu = os.path.join(wc_dir, 'branch', 'mu') url_A = url + '/A' url_branch = url + '/branch' # make a branch of A # svn cp $URL/A $URL/branch expected_stdout = verify.UnorderedOutput([ '\n', 'Committed revision 2.\n', ]) actions.run_and_verify_svn2('OUTPUT', expected_stdout, [], 0, 'cp', url_A, url_branch, '-m', 'copy log') # svn up expected_output = svntest.wc.State(wc_dir, { 'branch' : Item(status='A '), 'branch/B' : Item(status='A '), 'branch/B/F' : Item(status='A '), 'branch/B/E' : Item(status='A '), 'branch/B/E/beta' : Item(status='A '), 'branch/B/E/alpha' : Item(status='A '), 'branch/B/lambda' : Item(status='A '), 'branch/D' : Item(status='A '), 'branch/D/H' : Item(status='A '), 'branch/D/H/psi' : Item(status='A '), 'branch/D/H/chi' : Item(status='A '), 'branch/D/H/omega' : Item(status='A '), 'branch/D/G' : Item(status='A '), 'branch/D/G/tau' : Item(status='A '), 'branch/D/G/pi' : Item(status='A '), 'branch/D/G/rho' : Item(status='A '), 'branch/D/gamma' : Item(status='A '), 'branch/C' : Item(status='A '), 'branch/mu' : Item(status='A '), }) expected_disk = svntest.main.greek_state.copy() expected_disk.add({ 'branch' : Item(), 'branch/D' : Item(), 'branch/D/G' : Item(), 'branch/D/G/rho' : Item(contents="This is the file 'rho'.\n"), 'branch/D/G/tau' : Item(contents="This is the file 'tau'.\n"), 'branch/D/G/pi' : Item(contents="This is the file 'pi'.\n"), 'branch/D/H' : Item(), 'branch/D/H/omega' : Item(contents="This is the file 'omega'.\n"), 'branch/D/H/chi' : Item(contents="This is the file 'chi'.\n"), 'branch/D/H/psi' : Item(contents="This is the file 'psi'.\n"), 'branch/D/gamma' : Item(contents="This is the file 'gamma'.\n"), 'branch/B' : Item(), 'branch/B/E' : Item(), 'branch/B/E/alpha' : Item(contents="This is the file 'alpha'.\n"), 'branch/B/E/beta' : Item(contents="This is the file 'beta'.\n"), 'branch/B/F' : Item(), 'branch/B/lambda' : Item(contents="This is the file 'lambda'.\n"), 'branch/mu' : Item(contents="This is the file 'mu'.\n"), 'branch/C' : Item(), }) expected_status = actions.get_virginal_state(wc_dir, 2) expected_status.add({ 'branch' : Item(status=' ', wc_rev='2'), 'branch/D' : Item(status=' ', wc_rev='2'), 'branch/D/gamma' : Item(status=' ', wc_rev='2'), 'branch/D/H' : Item(status=' ', wc_rev='2'), 'branch/D/H/omega' : Item(status=' ', wc_rev='2'), 'branch/D/H/chi' : Item(status=' ', wc_rev='2'), 'branch/D/H/psi' : Item(status=' ', wc_rev='2'), 'branch/D/G' : Item(status=' ', wc_rev='2'), 'branch/D/G/tau' : Item(status=' ', wc_rev='2'), 'branch/D/G/pi' : Item(status=' ', wc_rev='2'), 'branch/D/G/rho' : Item(status=' ', wc_rev='2'), 'branch/B' : Item(status=' ', wc_rev='2'), 'branch/B/F' : Item(status=' ', wc_rev='2'), 'branch/B/E' : Item(status=' ', wc_rev='2'), 'branch/B/E/beta' : Item(status=' ', wc_rev='2'), 'branch/B/E/alpha' : Item(status=' ', wc_rev='2'), 'branch/B/lambda' : Item(status=' ', wc_rev='2'), 'branch/C' : Item(status=' ', wc_rev='2'), 'branch/mu' : Item(status=' ', wc_rev='2'), }) actions.run_and_verify_update(wc_dir, expected_output, expected_disk, expected_status, None, None, None, None, None, False, wc_dir) # ACTIONS ON THE MERGE SOURCE (branch) # various deletes of files and dirs # svn delete branch/mu branch/B/E branch/D/G/pi branch/D/H expected_stdout = verify.UnorderedOutput([ 'D ' + branch_mu + '\n', 'D ' + os.path.join(branch_B_E, 'alpha') + '\n', 'D ' + os.path.join(branch_B_E, 'beta') + '\n', 'D ' + branch_B_E + '\n', 'D ' + branch_D_G_pi + '\n', 'D ' + os.path.join(branch_D_H, 'chi') + '\n', 'D ' + os.path.join(branch_D_H, 'omega') + '\n', 'D ' + os.path.join(branch_D_H, 'psi') + '\n', 'D ' + branch_D_H + '\n', ]) actions.run_and_verify_svn2('OUTPUT', expected_stdout, [], 0, 'delete', branch_mu, branch_B_E, branch_D_G_pi, branch_D_H) # svn ci expected_output = svntest.wc.State(wc_dir, { 'branch/D/G/pi' : Item(verb='Deleting'), 'branch/D/H' : Item(verb='Deleting'), 'branch/mu' : Item(verb='Deleting'), 'branch/B/E' : Item(verb='Deleting'), }) expected_status.remove('branch/mu', 'branch/D/H', 'branch/D/H/omega', 'branch/D/H/chi', 'branch/D/H/psi', 'branch/D/G/pi', 'branch/B/E', 'branch/B/E/beta', 'branch/B/E/alpha') actions.run_and_verify_commit(wc_dir, expected_output, expected_status, None, wc_dir) # svn up expected_output = svntest.wc.State(wc_dir, {}) expected_disk.remove('branch/mu', 'branch/D/H', 'branch/D/H/omega', 'branch/D/H/chi', 'branch/D/H/psi', 'branch/D/G/pi', 'branch/B/E', 'branch/B/E/alpha', 'branch/B/E/beta') expected_status.tweak(wc_rev='3') actions.run_and_verify_update(wc_dir, expected_output, expected_disk, expected_status, None, None, None, None, None, False, wc_dir) # replacements. # file-with-file # echo "replacement for mu" > branch/mu main.file_write(branch_mu, 'replacement for mu') # svn add branch/mu expected_stdout = ['A ' + branch_mu + '\n'] actions.run_and_verify_svn2('OUTPUT', expected_stdout, [], 0, 'add', branch_mu) # dir-with-dir # svn mkdir branch/B/E expected_stdout = ['A ' + branch_B_E + '\n'] actions.run_and_verify_svn2('OUTPUT', expected_stdout, [], 0, 'mkdir', branch_B_E) # svn ps propname propval branch/B/E expected_stdout = ["property 'propname' set on '" + branch_B_E + "'\n"] actions.run_and_verify_svn2('OUTPUT', expected_stdout, [], 0, 'ps', 'propname', 'propval', branch_B_E) # file-with-dir # svn mkdir branch/D/G/pi expected_stdout = ['A ' + branch_D_G_pi + '\n'] actions.run_and_verify_svn2('OUTPUT', expected_stdout, [], 0, 'mkdir', branch_D_G_pi) # svn ps propname propval branch/D/G/pi expected_stdout = ["property 'propname' set on '" + branch_D_G_pi + "'\n"] actions.run_and_verify_svn2('OUTPUT', expected_stdout, [], 0, 'ps', 'propname', 'propval', branch_D_G_pi) # dir-with-file # echo "replacement for H" > branch/D/H main.file_write(branch_D_H, 'replacement for H') # svn add branch/D/H expected_stdout = ['A ' + branch_D_H + '\n'] actions.run_and_verify_svn2('OUTPUT', expected_stdout, [], 0, 'add', branch_D_H) # svn ci expected_output = svntest.wc.State(wc_dir, { 'branch/D/G/pi' : Item(verb='Adding'), 'branch/D/H' : Item(verb='Adding'), 'branch/mu' : Item(verb='Adding'), 'branch/B/E' : Item(verb='Adding'), }) expected_status.add({ 'branch/D/G/pi' : Item(status=' ', wc_rev='4'), 'branch/D/H' : Item(status=' ', wc_rev='4'), 'branch/B/E' : Item(status=' ', wc_rev='4'), 'branch/mu' : Item(status=' ', wc_rev='4'), }) actions.run_and_verify_commit(wc_dir, expected_output, expected_status, None, wc_dir) return expected_disk, expected_status #---------------------------------------------------------------------- # ra_serf causes duplicate notifications with this test: @XFail(svntest.main.is_ra_type_dav_serf) @Issue(3802) def merge_replace_causes_tree_conflict(sbox): "replace vs. edit tree-conflicts" expected_disk, expected_status = merge_replace_setup(sbox) # svntest.factory.make(sbox,r""" # # ACTIONS ON THE MERGE TARGET (A) # # local mods to conflict with merge source # echo modified > A/mu # svn ps propname otherpropval A/B/E # echo modified > A/D/G/pi # svn ps propname propval A/D/H # svn merge $URL/A $URL/branch A # svn st # """, prev_status=expected_status, prev_disk=expected_disk) wc_dir = sbox.wc_dir url = sbox.repo_url A = os.path.join(wc_dir, 'A') A_B_E = os.path.join(wc_dir, 'A', 'B', 'E') A_D_G_pi = os.path.join(wc_dir, 'A', 'D', 'G', 'pi') A_D_H = os.path.join(wc_dir, 'A', 'D', 'H') A_mu = os.path.join(wc_dir, 'A', 'mu') url_A = url + '/A' url_branch = url + '/branch' # ACTIONS ON THE MERGE TARGET (A) # local mods to conflict with merge source # echo modified > A/mu main.file_write(A_mu, 'modified') # svn ps propname otherpropval A/B/E expected_stdout = ["property 'propname' set on '" + A_B_E + "'\n"] actions.run_and_verify_svn2('OUTPUT', expected_stdout, [], 0, 'ps', 'propname', 'otherpropval', A_B_E) # echo modified > A/D/G/pi main.file_write(A_D_G_pi, 'modified') # svn ps propname propval A/D/H expected_stdout = ["property 'propname' set on '" + A_D_H + "'\n"] actions.run_and_verify_svn2('OUTPUT', expected_stdout, [], 0, 'ps', 'propname', 'propval', A_D_H) # svn merge $URL/A $URL/branch A expected_stdout = verify.UnorderedOutput([ "--- Merging differences between repository URLs into '" + A + "':\n", ' C ' + A_B_E + '\n', ' C ' + A_mu + '\n', ' C ' + A_D_G_pi + '\n', ' C ' + A_D_H + '\n', "--- Recording mergeinfo for merge between repository URLs into '" \ + A + "':\n", ' U ' + A + '\n', 'Summary of conflicts:\n', ' Tree conflicts: 4\n', ]) actions.run_and_verify_svn2('OUTPUT', expected_stdout, [], 0, 'merge', url_A, url_branch, A) # svn st expected_status.tweak('A', status=' M') expected_status.tweak('A/D/G/pi', 'A/mu', status='M ', treeconflict='C') expected_status.tweak('A/D/H', status=' M', treeconflict='C') ### A/B/E gets both a property and tree conflict flagged. Is this OK? expected_status.tweak('A/B/E', status=' C', treeconflict='C') actions.run_and_verify_status(wc_dir, expected_status) #---------------------------------------------------------------------- @XFail() @Issue(3806) def merge_replace_causes_tree_conflict2(sbox): "replace vs. delete tree-conflicts" expected_disk, expected_status = merge_replace_setup(sbox) # svntest.factory.make(sbox,r""" # # ACTIONS ON THE MERGE TARGET (A) # # local mods to conflict with merge source # # Delete each of the files and dirs to be replaced by the merge. # svn delete A/mu A/B/E A/D/G/pi A/D/H # # Merge them one by one to see all the errors. # svn merge $URL/A/mu $URL/branch/mu A/mu # svn merge $URL/A/B $URL/branch/B A/B # svn merge --depth=immediates $URL/A/D $URL/branch/D A/D # svn merge $URL/A/D/G $URL/branch/D/G A/D/G # svn st # """, prev_disk=expected_disk, prev_status=expected_status) wc_dir = sbox.wc_dir url = sbox.repo_url A_B = os.path.join(wc_dir, 'A', 'B') A_B_E = os.path.join(wc_dir, 'A', 'B', 'E') A_D = os.path.join(wc_dir, 'A', 'D') A_D_G = os.path.join(wc_dir, 'A', 'D', 'G') A_D_G_pi = os.path.join(wc_dir, 'A', 'D', 'G', 'pi') A_D_H = os.path.join(wc_dir, 'A', 'D', 'H') A = os.path.join(wc_dir, 'A') A_mu = os.path.join(wc_dir, 'A', 'mu') url_A_B = url + '/A/B' url_A_D = url + '/A/D' url_A_D_G = url + '/A/D/G' url_A = url + '/A' url_branch_B = url + '/branch/B' url_branch_D = url + '/branch/D' url_branch_D_G = url + '/branch/D/G' url_branch = url + '/branch' # ACTIONS ON THE MERGE TARGET (A) # local mods to conflict with merge source # Delete each of the files and dirs to be replaced by the merge. # svn delete A/mu A/B/E A/D/G/pi A/D/H expected_stdout = verify.UnorderedOutput([ 'D ' + A_mu + '\n', 'D ' + os.path.join(A_B_E, 'alpha') + '\n', 'D ' + os.path.join(A_B_E, 'beta') + '\n', 'D ' + A_B_E + '\n', 'D ' + A_D_G_pi + '\n', 'D ' + os.path.join(A_D_H, 'chi') + '\n', 'D ' + os.path.join(A_D_H, 'omega') + '\n', 'D ' + os.path.join(A_D_H, 'psi') + '\n', 'D ' + A_D_H + '\n', ]) actions.run_and_verify_svn2('OUTPUT', expected_stdout, [], 0, 'delete', A_mu, A_B_E, A_D_G_pi, A_D_H) expected_status.tweak('A/B/E', 'A/B/E/alpha', 'A/B/E/beta', 'A/D/G/pi', 'A/D/H', 'A/D/H/chi', 'A/D/H/omega', 'A/D/H/psi', status='D ') # Merge them one by one to see all the errors. ### A file-with-file replacement onto a deleted file. # svn merge $URL/A/mu $URL/branch/mu A/mu expected_stdout = verify.UnorderedOutput([ "--- Merging differences between repository URLs into '" + A + "':\n", ' C ' + A_mu + '\n', "--- Recording mergeinfo for merge between repository URLs into '" + A + "':\n", " U " + A + "\n", 'Summary of conflicts:\n', ' Tree conflicts: 1\n', ]) actions.run_and_verify_svn2('OUTPUT', expected_stdout, [], 0, 'merge', url_A, url_branch, A, '--depth=files') # New mergeinfo describing the merge. expected_status.tweak('A', status=' M') # Currently this fails because the local status is 'D'eleted rather than # 'R'eplaced with history: # # D C merge_tree_conflict_tests-23\A\mu # > local delete, incoming replace upon merge expected_status.tweak('A/mu', status='R ', wc_rev='-', copied='+', treeconflict='C') ### A dir-with-dir replacement onto a deleted directory. # svn merge $URL/A/B $URL/branch/B A/B expected_stdout = verify.UnorderedOutput([ "--- Merging differences between repository URLs into '" + A_B + "':\n", ' C ' + A_B_E + '\n', "--- Recording mergeinfo for merge between repository URLs into '" + A_B + "':\n", " U " + A_B + "\n", 'Summary of conflicts:\n', ' Tree conflicts: 1\n', ]) actions.run_and_verify_svn2('OUTPUT', expected_stdout, [], 0, 'merge', url_A_B, url_branch_B, A_B) # New mergeinfo describing the merge. expected_status.tweak('A/B', status=' M') # Currently this fails because the local status shows a property mod (and # the TC type is listed as incoming delete, not incoming replace): # # RM + C merge_tree_conflict_tests-23\A\B\E # > local delete, incoming delete upon merge expected_status.tweak('A/B/E', status='R ', wc_rev='-', copied='+', treeconflict='C') ### A dir-with-file replacement onto a deleted directory. # svn merge --depth=immediates $URL/A/D $URL/branch/D A/D expected_stdout = verify.UnorderedOutput([ "--- Merging differences between repository URLs into '" + A_D + "':\n", ' C ' + A_D_H + '\n', "--- Recording mergeinfo for merge between repository URLs into '" + A_D + "':\n", " U " + A_D + "\n", " U " + A_D_G + "\n", 'Summary of conflicts:\n', ' Tree conflicts: 1\n', ]) actions.run_and_verify_svn2('OUTPUT', expected_stdout, [], 0, 'merge', '--depth=immediates', url_A_D, url_branch_D, A_D) # New mergeinfo describing the merge. expected_status.tweak('A/D', 'A/D/G', status=' M') # Currently this fails because the local status is 'D'eleted rather than # 'R'eplaced with history: # # D C merge_tree_conflict_tests-23\A\D\H # > local delete, incoming replace upon merge expected_status.tweak('A/D/H', status='R ', wc_rev='-', copied='+', treeconflict='C') ### A file-with-dir replacement onto a deleted file. # svn merge $URL/A/D/G $URL/branch/D/G A/D/G expected_stdout = verify.UnorderedOutput([ "--- Merging differences between repository URLs into '" + A_D_G + "':\n", ' C ' + A_D_G_pi + '\n', "--- Recording mergeinfo for merge between repository URLs into '" + A_D_G + "':\n", "--- Eliding mergeinfo from '" + A_D_G_pi + "':\n", " U " + A_D_G_pi + "\n", "--- Eliding mergeinfo from '" + A_D_G_pi + "':\n", " U " + A_D_G_pi + "\n", " G " + A_D_G + "\n", 'Summary of conflicts:\n', ' Tree conflicts: 1\n', ]) actions.run_and_verify_svn2('OUTPUT', expected_stdout, [], 0, 'merge', url_A_D_G, url_branch_D_G, A_D_G) # New mergeinfo describing the merge. expected_status.tweak('A/D/G', status=' M') # Currently this fails because the local status shows a property mod (and # the TC type is listed as incoming delete, not incoming replace): # # RM + C merge_tree_conflict_tests-23\A\D\G\pi # > local delete, incoming delete upon merge expected_status.tweak('A/D/G/pi', status='R ', wc_rev='-', copied='+', treeconflict='C') # Check the resulting status: actions.run_and_verify_status(wc_dir, expected_status) # Check the tree conflict types: expected_stdout = '(R.*)|(Summary of conflicts.*)|( Tree conflicts.*)' \ '|(.*local delete, incoming replace upon merge.*)' tree_conflicted_path = [A_B_E, A_mu, A_D_G_pi, A_D_H] for path in tree_conflicted_path: actions.run_and_verify_svn2('OUTPUT', expected_stdout, [], 0, 'st', '--depth=empty', path) ######################################################################## # Run the tests # list all tests here, starting with None: test_list = [ None, delete_file_and_dir, merge_catches_nonexistent_target, merge_tree_deleted_in_target, three_way_merge_add_of_existing_binary_file, merge_added_dir_to_deleted_in_target, merge_add_over_versioned_file_conflicts, mergeinfo_recording_in_skipped_merge, del_differing_file, tree_conflicts_and_obstructions, tree_conflicts_on_merge_local_ci_4_1, tree_conflicts_on_merge_local_ci_4_2, tree_conflicts_on_merge_local_ci_5_1, tree_conflicts_on_merge_local_ci_5_2, tree_conflicts_on_merge_local_ci_6, tree_conflicts_on_merge_no_local_ci_4_1, tree_conflicts_on_merge_no_local_ci_4_2, tree_conflicts_on_merge_no_local_ci_5_1, tree_conflicts_on_merge_no_local_ci_5_2, tree_conflicts_on_merge_no_local_ci_6, tree_conflicts_merge_edit_onto_missing, tree_conflicts_merge_del_onto_missing, merge_replace_causes_tree_conflict, merge_replace_causes_tree_conflict2, ] if __name__ == '__main__': svntest.main.run_tests(test_list) # NOTREACHED ### End of file.