1#!/usr/bin/env python 2# 3# merge_tests.py: testing merge 4# 5# Subversion is a tool for revision control. 6# See http://subversion.apache.org for more information. 7# 8# ==================================================================== 9# Licensed to the Apache Software Foundation (ASF) under one 10# or more contributor license agreements. See the NOTICE file 11# distributed with this work for additional information 12# regarding copyright ownership. The ASF licenses this file 13# to you under the Apache License, Version 2.0 (the 14# "License"); you may not use this file except in compliance 15# with the License. You may obtain a copy of the License at 16# 17# http://www.apache.org/licenses/LICENSE-2.0 18# 19# Unless required by applicable law or agreed to in writing, 20# software distributed under the License is distributed on an 21# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 22# KIND, either express or implied. See the License for the 23# specific language governing permissions and limitations 24# under the License. 25###################################################################### 26 27# General modules 28import shutil, sys, re, os 29import time 30 31# Our testing module 32import svntest 33from svntest import main, wc, verify, actions 34 35from prop_tests import binary_mime_type_on_text_file_warning 36 37# (abbreviation) 38Item = wc.StateItem 39Skip = svntest.testcase.Skip_deco 40SkipUnless = svntest.testcase.SkipUnless_deco 41XFail = svntest.testcase.XFail_deco 42Issues = svntest.testcase.Issues_deco 43Issue = svntest.testcase.Issue_deco 44Wimp = svntest.testcase.Wimp_deco 45exp_noop_up_out = svntest.actions.expected_noop_update_output 46 47from svntest.main import SVN_PROP_MERGEINFO 48from svntest.main import server_has_mergeinfo 49from svntest.actions import fill_file_with_lines 50from svntest.actions import make_conflict_marker_text 51from svntest.actions import inject_conflict_into_expected_state 52from svntest.verify import RegexListOutput 53 54from svntest.mergetrees import expected_merge_output, \ 55 check_mergeinfo_recursively, \ 56 set_up_dir_replace, \ 57 set_up_branch, \ 58 local_path, \ 59 svn_mkfile, \ 60 svn_modfile, \ 61 svn_copy, \ 62 svn_merge, \ 63 noninheritable_mergeinfo_test_set_up 64 65###################################################################### 66# Tests 67# 68# Each test must return on success or raise on failure. 69 70#---------------------------------------------------------------------- 71@SkipUnless(server_has_mergeinfo) 72def textual_merges_galore(sbox): 73 "performing a merge, with mixed results" 74 75 ## The Plan: 76 ## 77 ## The goal is to test that "svn merge" does the right thing in the 78 ## following cases: 79 ## 80 ## 1 : _ : Received changes already present in unmodified local file 81 ## 2 : U : No local mods, received changes folded in without trouble 82 ## 3 : G : Received changes already exist as local mods 83 ## 4 : G : Received changes do not conflict with local mods 84 ## 5 : C : Received changes conflict with local mods 85 ## 86 ## So first modify these files and commit: 87 ## 88 ## Revision 2: 89 ## ----------- 90 ## A/mu ............... add ten or so lines 91 ## A/D/G/rho .......... add ten or so lines 92 ## 93 ## Now check out an "other" working copy, from revision 2. 94 ## 95 ## Next further modify and commit some files from the original 96 ## working copy: 97 ## 98 ## Revision 3: 99 ## ----------- 100 ## A/B/lambda ......... add ten or so lines 101 ## A/D/G/pi ........... add ten or so lines 102 ## A/D/G/tau .......... add ten or so lines 103 ## A/D/G/rho .......... add an additional ten or so lines 104 ## 105 ## In the other working copy (which is at rev 2), update rho back 106 ## to revision 1, while giving other files local mods. This sets 107 ## things up so that "svn merge -r 1:3" will test all of the above 108 ## cases except case 4: 109 ## 110 ## case 1: A/mu .......... do nothing, the only change was in rev 2 111 ## case 2: A/B/lambda .... do nothing, so we accept the merge easily 112 ## case 3: A/D/G/pi ...... add same ten lines as committed in rev 3 113 ## case 5: A/D/G/tau ..... add ten or so lines at the end 114 ## [none]: A/D/G/rho ..... ignore what happens to this file for now 115 ## 116 ## Now run 117 ## 118 ## $ cd wc.other 119 ## $ svn merge -r 1:3 url-to-repo 120 ## 121 ## ...and expect the right output. 122 ## 123 ## Now revert rho, then update it to revision 2, then *prepend* a 124 ## bunch of lines, which will be separated by enough distance from 125 ## the changes about to be received that the merge will be clean. 126 ## 127 ## $ cd wc.other/A/D/G 128 ## $ svn merge -r 2:3 url-to-repo/A/D/G 129 ## 130 ## Which tests case 4. (Ignore the changes to the other files, 131 ## we're only interested in rho here.) 132 133 sbox.build() 134 wc_dir = sbox.wc_dir 135 # url = os.path.join(svntest.main.test_area_url, sbox.repo_dir) 136 137 # Change mu and rho for revision 2 138 mu_path = sbox.ospath('A/mu') 139 rho_path = sbox.ospath('A/D/G/rho') 140 mu_text = fill_file_with_lines(mu_path, 2) 141 rho_text = fill_file_with_lines(rho_path, 2) 142 143 # Create expected output tree for initial commit 144 expected_output = wc.State(wc_dir, { 145 'A/mu' : Item(verb='Sending'), 146 'A/D/G/rho' : Item(verb='Sending'), 147 }) 148 149 # Create expected status tree; all local revisions should be at 1, 150 # but mu and rho should be at revision 2. 151 expected_status = svntest.actions.get_virginal_state(wc_dir, 1) 152 expected_status.tweak('A/mu', 'A/D/G/rho', wc_rev=2) 153 154 # Initial commit. 155 svntest.actions.run_and_verify_commit(wc_dir, 156 expected_output, 157 expected_status) 158 159 # Make the "other" working copy 160 other_wc = sbox.add_wc_path('other') 161 svntest.actions.duplicate_dir(wc_dir, other_wc) 162 163 # Now commit some more mods from the original working copy, to 164 # produce revision 3. 165 lambda_path = sbox.ospath('A/B/lambda') 166 pi_path = sbox.ospath('A/D/G/pi') 167 tau_path = sbox.ospath('A/D/G/tau') 168 169 lambda_text = fill_file_with_lines(lambda_path, 2) 170 pi_text = fill_file_with_lines(pi_path, 2) 171 tau_text = fill_file_with_lines(tau_path, 2) 172 additional_rho_text = fill_file_with_lines(rho_path, 2) 173 174 # Created expected output tree for 'svn ci' 175 expected_output = wc.State(wc_dir, { 176 'A/B/lambda' : Item(verb='Sending'), 177 'A/D/G/pi' : Item(verb='Sending'), 178 'A/D/G/tau' : Item(verb='Sending'), 179 'A/D/G/rho' : Item(verb='Sending'), 180 }) 181 182 # Create expected status tree. 183 expected_status = svntest.actions.get_virginal_state(wc_dir, 1) 184 expected_status.tweak('A/mu', wc_rev=2) 185 expected_status.tweak('A/B/lambda', 'A/D/G/pi', 'A/D/G/tau', 'A/D/G/rho', 186 wc_rev=3) 187 188 # Commit revision 3. 189 svntest.actions.run_and_verify_commit(wc_dir, 190 expected_output, 191 expected_status) 192 193 # Make local mods in wc.other 194 other_pi_path = os.path.join(other_wc, 'A', 'D', 'G', 'pi') 195 other_rho_path = os.path.join(other_wc, 'A', 'D', 'G', 'rho') 196 other_tau_path = os.path.join(other_wc, 'A', 'D', 'G', 'tau') 197 198 # For A/mu and A/B/lambda, we do nothing. For A/D/G/pi, we add the 199 # same ten lines as were already committed in revision 3. 200 # (Remember, wc.other is only at revision 2, so it doesn't have 201 # these changes.) 202 svntest.main.file_append(other_pi_path, pi_text) 203 204 # We skip A/D/G/rho in this merge; it will be tested with a separate 205 # merge command. Temporarily put it back to revision 1, so this 206 # merge succeeds cleanly. 207 svntest.actions.run_and_verify_svn(None, [], 208 'up', '-r', '1', other_rho_path) 209 210 # For A/D/G/tau, we append few different lines, to conflict with the 211 # few lines appended in revision 3. 212 other_tau_text = fill_file_with_lines(other_tau_path, 2, 213 line_descrip="Conflicting line") 214 215 # Do the first merge, revs 1:3. This tests all the cases except 216 # case 4, which we'll handle in a second pass. 217 expected_output = wc.State(other_wc, {'A/B/lambda' : Item(status='U '), 218 'A/D/G/rho' : Item(status='U '), 219 'A/D/G/tau' : Item(status='C '), 220 }) 221 expected_mergeinfo_output = wc.State(other_wc, {'' : Item(status=' U')}) 222 expected_elision_output = wc.State(other_wc, {}) 223 expected_disk = svntest.main.greek_state.copy() 224 expected_disk.tweak('A/mu', 225 contents=expected_disk.desc['A/mu'].contents 226 + mu_text) 227 expected_disk.tweak('A/B/lambda', 228 contents=expected_disk.desc['A/B/lambda'].contents 229 + lambda_text) 230 expected_disk.tweak('A/D/G/rho', 231 contents=expected_disk.desc['A/D/G/rho'].contents 232 + rho_text + additional_rho_text) 233 expected_disk.tweak('A/D/G/pi', 234 contents=expected_disk.desc['A/D/G/pi'].contents 235 + pi_text) 236 237 expected_status = svntest.actions.get_virginal_state(other_wc, 1) 238 expected_status.tweak('', status=' M') 239 expected_status.tweak('A/mu', wc_rev=2) 240 expected_status.tweak('A/B/lambda', status='M ') 241 expected_status.tweak('A/D/G/pi', status='M ') 242 expected_status.tweak('A/D/G/rho', status='M ') 243 244 inject_conflict_into_expected_state('A/D/G/tau', expected_disk, 245 expected_status, other_tau_text, tau_text, 246 1, 3) 247 248 expected_skip = wc.State('', { }) 249 250 tau_conflict_support_files = ["tau\.working", 251 "tau\.merge-right\.r3", 252 "tau\.merge-left\.r1"] 253 254 svntest.actions.run_and_verify_merge(other_wc, '1', '3', 255 sbox.repo_url, None, 256 expected_output, 257 expected_mergeinfo_output, 258 expected_elision_output, 259 expected_disk, 260 expected_status, 261 expected_skip, 262 [], False, True, 263 '--allow-mixed-revisions', other_wc, 264 extra_files=list(tau_conflict_support_files)) 265 266 # Now reverse merge r3 into A/D/G/rho, give it non-conflicting local 267 # mods, then merge in the 2:3 change. ### Not bothering to do the 268 # whole expected_foo routine for these intermediate operations; 269 # they're not what we're here to test, after all, so it's enough to 270 # know that they worked. Is this a bad practice? ### 271 # 272 # run_and_verify_merge doesn't support merging to a file WCPATH 273 # so use run_and_verify_svn. 274 ### TODO: We can use run_and_verify_merge() here now. 275 svntest.actions.run_and_verify_svn( 276 expected_merge_output([[-3]], 277 ['G ' + other_rho_path + '\n', 278 ' G ' + other_rho_path + '\n',]), 279 [], 'merge', '-c-3', 280 sbox.repo_url + '/A/D/G/rho', 281 other_rho_path) 282 283 # Now *prepend* ten or so lines to A/D/G/rho. Since rho had ten 284 # lines appended in revision 2, and then another ten in revision 3, 285 # these new local mods will be separated from the rev 3 changes by 286 # enough distance that they won't conflict, so the merge should be 287 # clean. 288 other_rho_text = "" 289 for x in range(1,10): 290 other_rho_text = other_rho_text + 'Unobtrusive line ' + repr(x) + ' in rho\n' 291 current_other_rho_text = open(other_rho_path).read() 292 svntest.main.file_write(other_rho_path, 293 other_rho_text + current_other_rho_text) 294 295 # We expect no merge attempt for pi and tau because they inherit 296 # mergeinfo from the WC root. There is explicit mergeinfo on rho 297 # ('/A/D/G/rho:2') so expect it to be merged (cleanly). 298 G_path = os.path.join(other_wc, 'A', 'D', 'G') 299 expected_output = wc.State(os.path.join(other_wc, 'A', 'D', 'G'), 300 {'rho' : Item(status='G ')}) 301 expected_mergeinfo_output = wc.State(G_path, { 302 '' : Item(status=' G'), 303 'rho' : Item(status=' G') 304 }) 305 expected_elision_output = wc.State(G_path, { 306 '' : Item(status=' U'), 307 'rho' : Item(status=' U') 308 }) 309 expected_disk = wc.State("", { 310 'pi' : Item("This is the file 'pi'.\n"), 311 'rho' : Item("This is the file 'rho'.\n"), 312 'tau' : Item("This is the file 'tau'.\n"), 313 }) 314 expected_disk.tweak('rho', 315 contents=other_rho_text 316 + expected_disk.desc['rho'].contents 317 + rho_text 318 + additional_rho_text) 319 expected_disk.tweak('pi', 320 contents=expected_disk.desc['pi'].contents 321 + pi_text) 322 323 expected_status = wc.State(os.path.join(other_wc, 'A', 'D', 'G'), 324 { '' : Item(wc_rev=1, status=' '), 325 'rho' : Item(wc_rev=1, status='M '), 326 'pi' : Item(wc_rev=1, status='M '), 327 'tau' : Item(wc_rev=1, status='C '), 328 }) 329 330 inject_conflict_into_expected_state('tau', expected_disk, expected_status, 331 other_tau_text, tau_text, 1, 3) 332 333 # Do the merge, but check svn:mergeinfo props separately since 334 # run_and_verify_merge would attempt to proplist tau's conflict 335 # files if we asked it to check props. 336 svntest.actions.run_and_verify_merge( 337 os.path.join(other_wc, 'A', 'D', 'G'), 338 '2', '3', 339 sbox.repo_url + '/A/D/G', None, 340 expected_output, 341 expected_mergeinfo_output, 342 expected_elision_output, 343 expected_disk, 344 expected_status, 345 expected_skip, 346 extra_files=list(tau_conflict_support_files)) 347 348 349 svntest.actions.run_and_verify_svn([], '.*W200017: Property.*not found', 350 'propget', SVN_PROP_MERGEINFO, 351 os.path.join(other_wc, 352 "A", "D", "G", "rho")) 353 354 355#---------------------------------------------------------------------- 356# Merge should copy-with-history when adding files or directories 357@SkipUnless(server_has_mergeinfo) 358def add_with_history(sbox): 359 "merge and add new files/dirs with history" 360 361 sbox.build() 362 wc_dir = sbox.wc_dir 363 364 C_path = sbox.ospath('A/C') 365 F_path = sbox.ospath('A/B/F') 366 F_url = sbox.repo_url + '/A/B/F' 367 368 Q_path = os.path.join(F_path, 'Q') 369 Q2_path = os.path.join(F_path, 'Q2') 370 foo_path = os.path.join(F_path, 'foo') 371 foo2_path = os.path.join(F_path, 'foo2') 372 bar_path = os.path.join(F_path, 'Q', 'bar') 373 bar2_path = os.path.join(F_path, 'Q', 'bar2') 374 375 svntest.main.run_svn(None, 'mkdir', Q_path) 376 svntest.main.run_svn(None, 'mkdir', Q2_path) 377 svntest.main.file_append(foo_path, "foo") 378 svntest.main.file_append(foo2_path, "foo2") 379 svntest.main.file_append(bar_path, "bar") 380 svntest.main.file_append(bar2_path, "bar2") 381 svntest.main.run_svn(None, 'add', foo_path, foo2_path, bar_path, bar2_path) 382 svntest.main.run_svn(None, 'propset', 'x', 'x', Q2_path) 383 svntest.main.run_svn(None, 'propset', 'y', 'y', foo2_path) 384 svntest.main.run_svn(None, 'propset', 'z', 'z', bar2_path) 385 386 expected_output = wc.State(wc_dir, { 387 'A/B/F/Q' : Item(verb='Adding'), 388 'A/B/F/Q2' : Item(verb='Adding'), 389 'A/B/F/Q/bar' : Item(verb='Adding'), 390 'A/B/F/Q/bar2': Item(verb='Adding'), 391 'A/B/F/foo' : Item(verb='Adding'), 392 'A/B/F/foo2' : Item(verb='Adding'), 393 }) 394 expected_status = svntest.actions.get_virginal_state(wc_dir, 1) 395 expected_status.add({ 396 'A/B/F/Q' : Item(status=' ', wc_rev=2), 397 'A/B/F/Q2' : Item(status=' ', wc_rev=2), 398 'A/B/F/Q/bar' : Item(status=' ', wc_rev=2), 399 'A/B/F/Q/bar2': Item(status=' ', wc_rev=2), 400 'A/B/F/foo' : Item(status=' ', wc_rev=2), 401 'A/B/F/foo2' : Item(status=' ', wc_rev=2), 402 }) 403 svntest.actions.run_and_verify_commit(wc_dir, 404 expected_output, 405 expected_status) 406 407 expected_output = wc.State(C_path, { 408 'Q' : Item(status='A '), 409 'Q2' : Item(status='A '), 410 'Q/bar' : Item(status='A '), 411 'Q/bar2' : Item(status='A '), 412 'foo' : Item(status='A '), 413 'foo2' : Item(status='A '), 414 }) 415 expected_mergeinfo_output = wc.State(C_path, { 416 '' : Item(status=' U'), 417 }) 418 expected_elision_output = wc.State(C_path, { 419 }) 420 expected_disk = wc.State('', { 421 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B/F:2'}), 422 'Q' : Item(), 423 'Q2' : Item(props={'x' : 'x'}), 424 'Q/bar' : Item("bar"), 425 'Q/bar2' : Item("bar2", props={'z' : 'z'}), 426 'foo' : Item("foo"), 427 'foo2' : Item("foo2", props={'y' : 'y'}), 428 }) 429 expected_status = wc.State(C_path, { 430 '' : Item(status=' M', wc_rev=1), 431 'Q' : Item(status='A ', wc_rev='-', copied='+'), 432 'Q2' : Item(status='A ', wc_rev='-', copied='+'), 433 'Q/bar' : Item(status=' ', wc_rev='-', copied='+'), 434 'Q/bar2' : Item(status=' ', wc_rev='-', copied='+'), 435 'foo' : Item(status='A ', wc_rev='-', copied='+'), 436 'foo2' : Item(status='A ', wc_rev='-', copied='+'), 437 }) 438 439 expected_skip = wc.State(C_path, { }) 440 441 # Add some unversioned directory obstructions to the incoming 442 # additions. This should be tolerated and *not* result in any 443 # difference between the --dry-run and actual merge. 444 # See http://svn.haxx.se/dev/archive-2012-11/0696.shtml 445 os.mkdir(sbox.ospath('A/C/Q')) 446 os.mkdir(sbox.ospath('A/C/Q2')) 447 448 svntest.actions.run_and_verify_merge(C_path, '1', '2', F_url, None, 449 expected_output, 450 expected_mergeinfo_output, 451 expected_elision_output, 452 expected_disk, 453 expected_status, 454 expected_skip, 455 check_props=True) 456 457 expected_output = svntest.wc.State(wc_dir, { 458 'A/C' : Item(verb='Sending'), 459 'A/C/Q' : Item(verb='Adding'), 460 'A/C/Q2' : Item(verb='Adding'), 461 'A/C/foo' : Item(verb='Adding'), 462 'A/C/foo2' : Item(verb='Adding'), 463 }) 464 expected_status = svntest.actions.get_virginal_state(wc_dir, 1) 465 expected_status.add({ 466 'A/C' : Item(status=' ', wc_rev=3), 467 'A/B/F/Q' : Item(status=' ', wc_rev=2), 468 'A/B/F/Q2' : Item(status=' ', wc_rev=2), 469 'A/B/F/Q/bar' : Item(status=' ', wc_rev=2), 470 'A/B/F/Q/bar2': Item(status=' ', wc_rev=2), 471 'A/B/F/foo' : Item(status=' ', wc_rev=2), 472 'A/B/F/foo2' : Item(status=' ', wc_rev=2), 473 'A/C/Q' : Item(status=' ', wc_rev=3), 474 'A/C/Q2' : Item(status=' ', wc_rev=3), 475 'A/C/Q/bar' : Item(status=' ', wc_rev=3), 476 'A/C/Q/bar2' : Item(status=' ', wc_rev=3), 477 'A/C/foo' : Item(status=' ', wc_rev=3), 478 'A/C/foo2' : Item(status=' ', wc_rev=3), 479 }) 480 svntest.actions.run_and_verify_commit(wc_dir, 481 expected_output, 482 expected_status) 483 484#---------------------------------------------------------------------- 485# Issue 953 486@SkipUnless(server_has_mergeinfo) 487@Issue(953) 488def simple_property_merges(sbox): 489 "some simple property merges" 490 491 sbox.build() 492 wc_dir = sbox.wc_dir 493 494 # Add a property to a file and a directory 495 alpha_path = sbox.ospath('A/B/E/alpha') 496 beta_path = sbox.ospath('A/B/E/beta') 497 E_path = sbox.ospath('A/B/E') 498 499 svntest.actions.set_prop('foo', 'foo_val', alpha_path) 500 # A binary, non-UTF8 property value 501 svntest.actions.set_prop('foo', b'foo\201val', beta_path) 502 svntest.actions.set_prop('foo', 'foo_val', E_path) 503 504 # Commit change as rev 2 505 expected_output = svntest.wc.State(wc_dir, { 506 'A/B/E' : Item(verb='Sending'), 507 'A/B/E/alpha' : Item(verb='Sending'), 508 'A/B/E/beta' : Item(verb='Sending'), 509 }) 510 expected_status = svntest.actions.get_virginal_state(wc_dir, 1) 511 expected_status.tweak('A/B/E', 'A/B/E/alpha', 'A/B/E/beta', 512 wc_rev=2, status=' ') 513 svntest.actions.run_and_verify_commit(wc_dir, 514 expected_output, expected_status) 515 svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir) 516 517 # Copy B to B2 as rev 3 518 B_url = sbox.repo_url + '/A/B' 519 B2_url = sbox.repo_url + '/A/B2' 520 521 svntest.actions.run_and_verify_svn(None, [], 522 'copy', '-m', 'copy B to B2', 523 B_url, B2_url) 524 svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir) 525 526 # Modify a property and add a property for the file and directory 527 svntest.actions.set_prop('foo', 'mod_foo', alpha_path) 528 svntest.actions.set_prop('bar', 'bar_val', alpha_path) 529 svntest.actions.set_prop('foo', b'mod\201foo', beta_path) 530 svntest.actions.set_prop('bar', b'bar\201val', beta_path) 531 svntest.actions.set_prop('foo', 'mod_foo', E_path) 532 svntest.actions.set_prop('bar', 'bar_val', E_path) 533 534 # Commit change as rev 4 535 expected_status = svntest.actions.get_virginal_state(wc_dir, 3) 536 expected_status.tweak('A/B/E', 'A/B/E/alpha', 'A/B/E/beta', 537 wc_rev=4, status=' ') 538 expected_status.add({ 539 'A/B2' : Item(status=' ', wc_rev=3), 540 'A/B2/E' : Item(status=' ', wc_rev=3), 541 'A/B2/E/alpha' : Item(status=' ', wc_rev=3), 542 'A/B2/E/beta' : Item(status=' ', wc_rev=3), 543 'A/B2/F' : Item(status=' ', wc_rev=3), 544 'A/B2/lambda' : Item(status=' ', wc_rev=3), 545 }) 546 svntest.actions.run_and_verify_commit(wc_dir, 547 expected_output, expected_status) 548 svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir) 549 550 pristine_status = expected_status 551 pristine_status.tweak(wc_rev=4) 552 553 # Merge B 3:4 into B2 554 B2_path = sbox.ospath('A/B2') 555 expected_output = wc.State(B2_path, { 556 'E' : Item(status=' U'), 557 'E/alpha' : Item(status=' U'), 558 'E/beta' : Item(status=' U'), 559 }) 560 expected_mergeinfo_output = wc.State(B2_path, { 561 '' : Item(status=' U'), 562 }) 563 expected_elision_output = wc.State(B2_path, { 564 }) 565 expected_disk = wc.State('', { 566 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B:4'}), 567 'E' : Item(), 568 'E/alpha' : Item("This is the file 'alpha'.\n"), 569 'E/beta' : Item("This is the file 'beta'.\n"), 570 'F' : Item(), 571 'lambda' : Item("This is the file 'lambda'.\n"), 572 }) 573 expected_disk.tweak('E', 'E/alpha', 574 props={'foo' : 'mod_foo', 'bar' : 'bar_val'}) 575 expected_disk.tweak('E/beta', 576 props={'foo' : b'mod\201foo', 'bar' : b'bar\201val'}) 577 expected_status = wc.State(B2_path, { 578 '' : Item(status=' M'), 579 'E' : Item(status=' M'), 580 'E/alpha' : Item(status=' M'), 581 'E/beta' : Item(status=' M'), 582 'F' : Item(status=' '), 583 'lambda' : Item(status=' '), 584 }) 585 expected_status.tweak(wc_rev=4) 586 expected_skip = wc.State('', { }) 587 svntest.actions.run_and_verify_merge(B2_path, '3', '4', B_url, None, 588 expected_output, 589 expected_mergeinfo_output, 590 expected_elision_output, 591 expected_disk, 592 expected_status, 593 expected_skip, 594 check_props=True) 595 596 # Revert merge 597 svntest.actions.run_and_verify_svn(None, [], 598 'revert', '--recursive', wc_dir) 599 svntest.actions.run_and_verify_status(wc_dir, pristine_status) 600 601 # Merge B 2:1 into B2 (B2's mergeinfo should get elided away) 602 expected_status.tweak('', status=' ') 603 expected_disk.remove('') 604 expected_disk.tweak('E', 'E/alpha', 'E/beta', props={}) 605 expected_elision_output = wc.State(B2_path, { 606 '' : Item(status=' U'), 607 }) 608 svntest.actions.run_and_verify_merge(B2_path, '2', '1', B_url, None, 609 expected_output, 610 expected_mergeinfo_output, 611 expected_elision_output, 612 expected_disk, 613 expected_status, 614 expected_skip, 615 check_props=True) 616 617 def error_message(property, old_value, new_value): 618 return "Trying to change property '%s'\n" \ 619 "but the property has been locally deleted.\n" \ 620 "<<<<<<< (local property value)\n" \ 621 "||||||| (incoming 'changed from' value)\n" \ 622 "%s=======\n" \ 623 "%s>>>>>>> (incoming 'changed to' value)\n" % (property, old_value, new_value) 624 625 # Merge B 3:4 into B2 now causes a conflict 626 expected_disk.add({ 627 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B:4'}), 628 'E/dir_conflicts.prej' 629 : Item(error_message('foo', 'foo_val', 'mod_foo')), 630 'E/alpha.prej' 631 : Item(error_message('foo', 'foo_val', 'mod_foo')), 632 'E/beta.prej' 633 : Item(error_message('foo', 'foo?\\81val', 'mod?\\81foo')), 634 }) 635 expected_disk.tweak('E', 'E/alpha', props={'bar' : 'bar_val'}) 636 expected_disk.tweak('E/beta', props={'bar' : b'bar\201val'}) 637 expected_status.tweak('', status=' M') 638 expected_status.tweak('E', 'E/alpha', 'E/beta', status=' C') 639 expected_output.tweak('E', 'E/alpha', 'E/beta', status=' C') 640 expected_elision_output = wc.State(B2_path, { 641 }) 642 svntest.actions.run_and_verify_merge(B2_path, '3', '4', B_url, None, 643 expected_output, 644 expected_mergeinfo_output, 645 expected_elision_output, 646 expected_disk, 647 expected_status, 648 expected_skip, 649 check_props=True) 650 651 # issue 1109 : single file property merge. This test performs a merge 652 # that should be a no-op (adding properties that are already present). 653 svntest.actions.run_and_verify_svn(None, [], 654 'revert', '--recursive', wc_dir) 655 svntest.actions.run_and_verify_status(wc_dir, pristine_status) 656 657 # Copy A at rev 4 to A2 to make revision 5. 658 A_url = sbox.repo_url + '/A' 659 A2_url = sbox.repo_url + '/A2' 660 svntest.actions.run_and_verify_svn(['Committing transaction...\n', 661 'Committed revision 5.\n'], [], 662 'copy', '-m', 'copy A to A2', 663 A_url, A2_url) 664 665 # Re-root the WC at A2. 666 svntest.main.safe_rmtree(wc_dir) 667 svntest.actions.run_and_verify_svn(None, [], 'checkout', 668 A2_url, wc_dir) 669 670 # Attempt to re-merge rev 4 of the original A's alpha. Mergeinfo 671 # inherited from A2 (created by its copy from A) allows us to avoid 672 # a repeated merge. 673 alpha_url = sbox.repo_url + '/A/B/E/alpha' 674 alpha_path = sbox.ospath('B/E/alpha') 675 676 # Cannot use run_and_verify_merge with a file target 677 svntest.actions.run_and_verify_svn([], [], 'merge', '-r', '3:4', 678 alpha_url, alpha_path) 679 680 exit_code, output, err = svntest.actions.run_and_verify_svn(None, [], 681 'pl', alpha_path) 682 683 saw_foo = 0 684 saw_bar = 0 685 for line in output: 686 if re.match("\\s*foo\\s*$", line): 687 saw_foo = 1 688 if re.match("\\s*bar\\s*$", line): 689 saw_bar = 1 690 691 if not saw_foo or not saw_bar: 692 raise svntest.Failure("Expected properties not found") 693 694#---------------------------------------------------------------------- 695# This is a regression for issue #1176. 696@Issue(1176) 697def merge_similar_unrelated_trees(sbox): 698 "merging similar trees ancestrally unrelated" 699 700 ## See https://issues.apache.org/jira/browse/SVN-1249. ## 701 702 sbox.build() 703 wc_dir = sbox.wc_dir 704 705 # Simple test. Make three directories with the same content. 706 # Modify some stuff in the second one. Now merge 707 # (firstdir:seconddir->thirddir). 708 709 base1_path = sbox.ospath('base1') 710 base2_path = sbox.ospath('base2') 711 apply_path = sbox.ospath('apply') 712 713 base1_url = os.path.join(sbox.repo_url + '/base1') 714 base2_url = os.path.join(sbox.repo_url + '/base2') 715 716 # Make a tree of stuff ... 717 os.mkdir(base1_path) 718 svntest.main.file_append(os.path.join(base1_path, 'iota'), 719 "This is the file iota\n") 720 os.mkdir(os.path.join(base1_path, 'A')) 721 svntest.main.file_append(os.path.join(base1_path, 'A', 'mu'), 722 "This is the file mu\n") 723 os.mkdir(os.path.join(base1_path, 'A', 'B')) 724 svntest.main.file_append(os.path.join(base1_path, 'A', 'B', 'alpha'), 725 "This is the file alpha\n") 726 svntest.main.file_append(os.path.join(base1_path, 'A', 'B', 'beta'), 727 "This is the file beta\n") 728 729 # ... Copy it twice ... 730 shutil.copytree(base1_path, base2_path) 731 shutil.copytree(base1_path, apply_path) 732 733 # ... Gonna see if merge is naughty or nice! 734 svntest.main.file_append(os.path.join(base2_path, 'A', 'mu'), 735 "A new line in mu.\n") 736 os.rename(os.path.join(base2_path, 'A', 'B', 'beta'), 737 os.path.join(base2_path, 'A', 'B', 'zeta')) 738 739 svntest.actions.run_and_verify_svn(None, [], 740 'add', base1_path, base2_path, apply_path) 741 742 svntest.actions.run_and_verify_svn(None, [], 743 'ci', '-m', 'rev 2', wc_dir) 744 745 expected_output = wc.State(apply_path, { 746 'A/mu' : Item(status='U '), 747 'A/B/zeta' : Item(status='A '), 748 'A/B/beta' : Item(status='D '), 749 }) 750 751 # run_and_verify_merge doesn't support 'svn merge URL URL path' 752 ### TODO: We can use run_and_verify_merge() here now. 753 svntest.actions.run_and_verify_svn(None, [], 754 'merge', 755 '--ignore-ancestry', 756 base1_url, base2_url, 757 apply_path) 758 759 expected_status = wc.State(apply_path, { 760 '' : Item(status=' '), 761 'A' : Item(status=' '), 762 'A/mu' : Item(status='M '), 763 'A/B' : Item(status=' '), 764 'A/B/zeta' : Item(status='A ', copied='+'), 765 'A/B/alpha' : Item(status=' '), 766 'A/B/beta' : Item(status='D '), 767 'iota' : Item(status=' '), 768 }) 769 expected_status.tweak(wc_rev=2) 770 expected_status.tweak('A/B/zeta', wc_rev='-') 771 svntest.actions.run_and_verify_status(apply_path, expected_status) 772 773#---------------------------------------------------------------------- 774def merge_one_file_helper(sbox, arg_flav, record_only = 0): 775 """ARG_FLAV is one of 'r' (revision range) or 'c' (single change) or 776 '*' (no revision specified).""" 777 778 if arg_flav not in ('r', 'c', '*'): 779 raise svntest.Failure("Unrecognized flavor of merge argument") 780 781 sbox.build() 782 wc_dir = sbox.wc_dir 783 784 rho_rel_path = os.path.join('A', 'D', 'G', 'rho') 785 rho_path = os.path.join(wc_dir, rho_rel_path) 786 G_path = sbox.ospath('A/D/G') 787 rho_url = sbox.repo_url + '/A/D/G/rho' 788 789 # Change rho for revision 2 790 svntest.main.file_append(rho_path, 'A new line in rho.\n') 791 792 expected_output = wc.State(wc_dir, { rho_rel_path : Item(verb='Sending'), }) 793 expected_status = svntest.actions.get_virginal_state(wc_dir, 1) 794 expected_status.tweak('A/D/G/rho', wc_rev=2) 795 svntest.actions.run_and_verify_commit(wc_dir, 796 expected_output, 797 expected_status) 798 799 # Backdate rho to revision 1, so we can merge in the rev 2 changes. 800 svntest.actions.run_and_verify_svn(None, [], 801 'up', '-r', '1', rho_path) 802 803 # Try one merge with an explicit target; it should succeed. 804 ### Yes, it would be nice to use run_and_verify_merge(), but it 805 # appears to be impossible to get the expected_foo trees working 806 # right. I think something is still assuming a directory target. 807 if arg_flav == 'r': 808 svntest.actions.run_and_verify_svn( 809 expected_merge_output([[2]], 810 ['U ' + rho_path + '\n', 811 ' U ' + rho_path + '\n']), 812 [], 'merge', '-r', '1:2', rho_url, rho_path) 813 elif arg_flav == 'c': 814 svntest.actions.run_and_verify_svn( 815 expected_merge_output([[2]], 816 ['U ' + rho_path + '\n', 817 ' U ' + rho_path + '\n']), 818 [], 'merge', '-c', '2', rho_url, rho_path) 819 elif arg_flav == '*': 820 svntest.actions.run_and_verify_svn( 821 expected_merge_output([[2]], 822 ['U ' + rho_path + '\n', 823 ' U ' + rho_path + '\n']), 824 [], 'merge', rho_url, rho_path) 825 826 expected_status.tweak(wc_rev=1) 827 expected_status.tweak('A/D/G/rho', status='MM') 828 svntest.actions.run_and_verify_status(wc_dir, expected_status) 829 830 # Inspect rho, make sure it's right. 831 rho_text = svntest.tree.get_text(rho_path) 832 if rho_text != "This is the file 'rho'.\nA new line in rho.\n": 833 raise svntest.Failure("Unexpected text in merged '" + rho_path + "'") 834 835 # Restore rho to pristine revision 1, for another merge. 836 svntest.actions.run_and_verify_svn(None, [], 'revert', rho_path) 837 expected_status.tweak('A/D/G/rho', status=' ') 838 svntest.actions.run_and_verify_status(wc_dir, expected_status) 839 840 # Cd into the directory and run merge with no targets. 841 # It should still merge into rho. 842 saved_cwd = os.getcwd() 843 os.chdir(G_path) 844 845 # Cannot use run_and_verify_merge with a file target 846 merge_cmd = ['merge'] 847 if arg_flav == 'r': 848 merge_cmd += ['-r', '1:2'] 849 elif arg_flav == 'c': 850 merge_cmd += ['-c', '2'] 851 852 if record_only: 853 expected_output = expected_merge_output([[2]], 854 [' U rho\n']) 855 merge_cmd.append('--record-only') 856 rho_expected_status = ' M' 857 else: 858 expected_output = expected_merge_output([[2]], 859 ['U rho\n', 860 ' U rho\n']) 861 rho_expected_status = 'MM' 862 merge_cmd.append(rho_url) 863 864 svntest.actions.run_and_verify_svn(expected_output, [], *merge_cmd) 865 866 # Inspect rho, make sure it's right. 867 rho_text = svntest.tree.get_text('rho') 868 if record_only: 869 expected_text = "This is the file 'rho'.\n" 870 else: 871 expected_text = "This is the file 'rho'.\nA new line in rho.\n" 872 if rho_text != expected_text: 873 print("") 874 raise svntest.Failure("Unexpected text merged to 'rho' in '" + 875 G_path + "'") 876 os.chdir(saved_cwd) 877 878 expected_status.tweak('A/D/G/rho', status=rho_expected_status) 879 svntest.actions.run_and_verify_status(wc_dir, expected_status) 880 881#---------------------------------------------------------------------- 882@SkipUnless(server_has_mergeinfo) 883@Issue(1150) 884def merge_one_file_using_r(sbox): 885 "merge one file using the -r option" 886 merge_one_file_helper(sbox, 'r') 887 888#---------------------------------------------------------------------- 889@SkipUnless(server_has_mergeinfo) 890@Issue(1150) 891def merge_one_file_using_c(sbox): 892 "merge one file using the -c option" 893 merge_one_file_helper(sbox, 'c') 894 895#---------------------------------------------------------------------- 896@SkipUnless(server_has_mergeinfo) 897def merge_one_file_using_implicit_revs(sbox): 898 "merge one file without explicit revisions" 899 merge_one_file_helper(sbox, '*') 900 901#---------------------------------------------------------------------- 902@SkipUnless(server_has_mergeinfo) 903def merge_record_only(sbox): 904 "mark a revision range as merged" 905 merge_one_file_helper(sbox, 'r', 1) 906 907#---------------------------------------------------------------------- 908# This is a regression test for the enhancement added in issue #785 "add 909# friendly enhancement to 'svn merge'", which is about inferring that 910# the default target of "svn merge [-r...] FILE" should not be "." but 911# rather should be "FILE". 912def merge_with_implicit_target_helper(sbox, arg_flav): 913 """ARG_FLAV is one of 'r' (revision range) or 'c' (single change) or 914 '*' (no revision specified).""" 915 916 if arg_flav not in ('r', 'c', '*'): 917 raise svntest.Failure("Unrecognized flavor of merge argument") 918 919 sbox.build() 920 wc_dir = sbox.wc_dir 921 922 # Change mu for revision 2 923 mu_path = sbox.ospath('A/mu') 924 orig_mu_text = svntest.tree.get_text(mu_path) 925 added_mu_text = "" 926 for x in range(2,11): 927 added_mu_text = added_mu_text + 'This is line ' + repr(x) + ' in mu\n' 928 svntest.main.file_append(mu_path, added_mu_text) 929 930 # Create expected output tree for initial commit 931 expected_output = wc.State(wc_dir, { 932 'A/mu' : Item(verb='Sending'), 933 }) 934 935 # Create expected status tree; all local revisions should be at 1, 936 # but mu should be at revision 2. 937 expected_status = svntest.actions.get_virginal_state(wc_dir, 1) 938 expected_status.tweak('A/mu', wc_rev=2) 939 940 # Initial commit. 941 svntest.actions.run_and_verify_commit(wc_dir, 942 expected_output, 943 expected_status) 944 945 # Make the "other" working copy, at r1 946 other_wc = sbox.add_wc_path('other') 947 svntest.actions.duplicate_dir(wc_dir, other_wc) 948 svntest.main.run_svn(None, 'up', '-r', 1, other_wc) 949 950 # Try the merge without an explicit target; it should succeed. 951 # Can't use run_and_verify_merge cuz it expects a directory argument. 952 mu_url = sbox.repo_url + '/A/mu' 953 954 os.chdir(os.path.join(other_wc, 'A')) 955 956 # merge using filename for sourcepath 957 # Cannot use run_and_verify_merge with a file target 958 if arg_flav == 'r': 959 svntest.actions.run_and_verify_svn(expected_merge_output([[2]], 960 ['U mu\n', 961 ' U mu\n']), 962 [], 963 'merge', '-r', '1:2', 'mu') 964 elif arg_flav == 'c': 965 svntest.actions.run_and_verify_svn(expected_merge_output([[2]], 966 ['U mu\n', 967 ' U mu\n']), 968 [], 969 'merge', '-c', '2', 'mu') 970 971 elif arg_flav == '*': 972 # Without a peg revision, the default merge range of BASE:1 (which 973 # is a no-op) will be chosen. Let's do it both ways (no-op first, 974 # of course). 975 svntest.actions.run_and_verify_svn(None, [], 'merge', 'mu') 976 svntest.actions.run_and_verify_svn(expected_merge_output([[2]], 977 ['U mu\n', 978 ' U mu\n']), 979 [], 980 'merge', 'mu@2') 981 982 # sanity-check resulting file 983 if svntest.tree.get_text('mu') != orig_mu_text + added_mu_text: 984 raise svntest.Failure("Unexpected text in 'mu'") 985 986 # merge using URL for sourcepath 987 if arg_flav == 'r': 988 svntest.actions.run_and_verify_svn(expected_merge_output([[-2]], 989 ['G mu\n', 990 ' U mu\n', 991 ' G mu\n',], 992 elides=True), 993 [], 994 'merge', '-r', '2:1', mu_url) 995 elif arg_flav == 'c': 996 svntest.actions.run_and_verify_svn(expected_merge_output([[-2]], 997 ['G mu\n', 998 ' U mu\n', 999 ' G mu\n'], 1000 elides=True), 1001 [], 1002 'merge', '-c', '-2', mu_url) 1003 elif arg_flav == '*': 1004 # Implicit merge source URL and revision range detection is for 1005 # forward merges only (e.g. non-reverts). Undo application of 1006 # r2 to enable continuation of the test case. 1007 svntest.actions.run_and_verify_svn(expected_merge_output([[-2]], 1008 ['G mu\n', 1009 ' U mu\n', 1010 ' G mu\n'], 1011 elides=True), 1012 [], 1013 'merge', '-c', '-2', mu_url) 1014 1015 # sanity-check resulting file 1016 if svntest.tree.get_text('mu') != orig_mu_text: 1017 raise svntest.Failure("Unexpected text '%s' in 'mu', expected '%s'" % 1018 (svntest.tree.get_text('mu'), orig_mu_text)) 1019 1020#---------------------------------------------------------------------- 1021@SkipUnless(server_has_mergeinfo) 1022@Issue(785) 1023def merge_with_implicit_target_using_r(sbox): 1024 "merging a file w/no explicit target path using -r" 1025 merge_with_implicit_target_helper(sbox, 'r') 1026 1027#---------------------------------------------------------------------- 1028@Issue(785) 1029def merge_with_implicit_target_using_c(sbox): 1030 "merging a file w/no explicit target path using -c" 1031 merge_with_implicit_target_helper(sbox, 'c') 1032 1033#---------------------------------------------------------------------- 1034@Issue(785) 1035def merge_with_implicit_target_and_revs(sbox): 1036 "merging a file w/no explicit target path or revs" 1037 merge_with_implicit_target_helper(sbox, '*') 1038 1039#---------------------------------------------------------------------- 1040def merge_with_prev(sbox): 1041 "merge operations using PREV revision" 1042 1043 sbox.build() 1044 wc_dir = sbox.wc_dir 1045 1046 # Change mu for revision 2 1047 mu_path = sbox.ospath('A/mu') 1048 orig_mu_text = svntest.tree.get_text(mu_path) 1049 added_mu_text = "" 1050 for x in range(2,11): 1051 added_mu_text = added_mu_text + '\nThis is line ' + repr(x) + ' in mu' 1052 added_mu_text += "\n" 1053 svntest.main.file_append(mu_path, added_mu_text) 1054 1055 zot_path = sbox.ospath('A/zot') 1056 1057 svntest.main.file_append(zot_path, "bar") 1058 svntest.main.run_svn(None, 'add', zot_path) 1059 1060 # Create expected output tree for initial commit 1061 expected_output = wc.State(wc_dir, { 1062 'A/mu' : Item(verb='Sending'), 1063 'A/zot' : Item(verb='Adding'), 1064 }) 1065 1066 # Create expected status tree; all local revisions should be at 1, 1067 # but mu should be at revision 2. 1068 expected_status = svntest.actions.get_virginal_state(wc_dir, 1) 1069 expected_status.tweak('A/mu', wc_rev=2) 1070 expected_status.add({'A/zot' : Item(status=' ', wc_rev=2)}) 1071 1072 # Initial commit. 1073 svntest.actions.run_and_verify_commit(wc_dir, 1074 expected_output, 1075 expected_status) 1076 1077 # Make some other working copies 1078 other_wc = sbox.add_wc_path('other') 1079 svntest.actions.duplicate_dir(wc_dir, other_wc) 1080 1081 another_wc = sbox.add_wc_path('another') 1082 svntest.actions.duplicate_dir(wc_dir, another_wc) 1083 1084 was_cwd = os.getcwd() 1085 1086 os.chdir(os.path.join(other_wc, 'A')) 1087 1088 # Try to revert the last change to mu via svn merge 1089 # Cannot use run_and_verify_merge with a file target 1090 svntest.actions.run_and_verify_svn(expected_merge_output([[-2]], 1091 ['U mu\n', 1092 ' U mu\n'], 1093 elides=True), 1094 [], 1095 'merge', '-r', 'HEAD:PREV', 'mu') 1096 1097 # sanity-check resulting file 1098 if svntest.tree.get_text('mu') != orig_mu_text: 1099 raise svntest.Failure("Unexpected text in 'mu'") 1100 1101 os.chdir(was_cwd) 1102 1103 other_status = expected_status 1104 other_status.wc_dir = other_wc 1105 other_status.tweak('A/mu', status='M ', wc_rev=2) 1106 other_status.tweak('A/zot', wc_rev=2) 1107 svntest.actions.run_and_verify_status(other_wc, other_status) 1108 1109 os.chdir(another_wc) 1110 1111 # ensure 'A' will be at revision 2 1112 svntest.actions.run_and_verify_svn(None, [], 'up') 1113 1114 # now try a revert on a directory, and verify that it removed the zot 1115 # file we had added previously 1116 svntest.actions.run_and_verify_svn(None, [], 1117 'merge', '-r', 'COMMITTED:PREV', 1118 'A', 'A') 1119 1120 if svntest.tree.get_text('A/zot') != None: 1121 raise svntest.Failure("Unexpected text in 'A/zot'") 1122 1123 os.chdir(was_cwd) 1124 1125 another_status = expected_status 1126 another_status.wc_dir = another_wc 1127 another_status.tweak(wc_rev=2) 1128 another_status.tweak('A/mu', status='M ') 1129 another_status.tweak('A/zot', status='D ') 1130 svntest.actions.run_and_verify_status(another_wc, another_status) 1131 1132#---------------------------------------------------------------------- 1133# Regression test for issue #1319: 'svn merge' should *not* 'C' when 1134# merging a change into a binary file, unless it has local mods, or has 1135# different contents from the left side of the merge. 1136@SkipUnless(server_has_mergeinfo) 1137@Issue(1319) 1138def merge_binary_file(sbox): 1139 "merge change into unchanged binary file" 1140 1141 sbox.build() 1142 wc_dir = sbox.wc_dir 1143 1144 # Add a binary file to the project 1145 theta_contents = open(os.path.join(sys.path[0], "theta.bin"), 'rb').read() 1146 # Write PNG file data into 'A/theta'. 1147 theta_path = sbox.ospath('A/theta') 1148 svntest.main.file_write(theta_path, theta_contents, 'wb') 1149 1150 svntest.main.run_svn(None, 'add', theta_path) 1151 1152 # Commit the new binary file, creating revision 2. 1153 expected_output = svntest.wc.State(wc_dir, { 1154 'A/theta' : Item(verb='Adding (bin)'), 1155 }) 1156 expected_status = svntest.actions.get_virginal_state(wc_dir, 1) 1157 expected_status.add({ 1158 'A/theta' : Item(status=' ', wc_rev=2), 1159 }) 1160 svntest.actions.run_and_verify_commit(wc_dir, expected_output, 1161 expected_status) 1162 1163 # Make the "other" working copy 1164 other_wc = sbox.add_wc_path('other') 1165 svntest.actions.duplicate_dir(wc_dir, other_wc) 1166 1167 # Change the binary file in first working copy, commit revision 3. 1168 svntest.main.file_append(theta_path, "some extra junk") 1169 expected_output = wc.State(wc_dir, { 1170 'A/theta' : Item(verb='Sending'), 1171 }) 1172 expected_status = svntest.actions.get_virginal_state(wc_dir, 1) 1173 expected_status.add({ 1174 'A/theta' : Item(status=' ', wc_rev=3), 1175 }) 1176 svntest.actions.run_and_verify_commit(wc_dir, expected_output, 1177 expected_status) 1178 1179 # In second working copy, attempt to 'svn merge -r 2:3'. 1180 # We should *not* see a conflict during the update, but a 'U'. 1181 # And after the merge, the status should be 'M'. 1182 expected_output = wc.State(other_wc, { 1183 'A/theta' : Item(status='U '), 1184 }) 1185 expected_mergeinfo_output = wc.State(other_wc, { 1186 '' : Item(status=' U'), 1187 }) 1188 expected_elision_output = wc.State(other_wc, { 1189 }) 1190 expected_disk = svntest.main.greek_state.copy() 1191 expected_disk.add({ 1192 '' : Item(props={SVN_PROP_MERGEINFO : '/:3'}), 1193 'A/theta' : Item(theta_contents + b"some extra junk", 1194 props={'svn:mime-type' : 'application/octet-stream'}), 1195 }) 1196 expected_status = svntest.actions.get_virginal_state(other_wc, 1) 1197 expected_status.add({ 1198 '' : Item(status=' M', wc_rev=1), 1199 'A/theta' : Item(status='M ', wc_rev=2), 1200 }) 1201 expected_skip = wc.State('', { }) 1202 1203 svntest.actions.run_and_verify_merge(other_wc, '2', '3', 1204 sbox.repo_url, None, 1205 expected_output, 1206 expected_mergeinfo_output, 1207 expected_elision_output, 1208 expected_disk, 1209 expected_status, 1210 expected_skip, 1211 [], 1212 True, True, '--allow-mixed-revisions', 1213 other_wc) 1214 1215#---------------------------------------------------------------------- 1216# Regression test for Issue #1297: 1217# A merge that creates a new file followed by an immediate diff 1218# The diff should succeed. 1219@SkipUnless(server_has_mergeinfo) 1220@Issue(1297) 1221def merge_in_new_file_and_diff(sbox): 1222 "diff after merge that creates a new file" 1223 1224 sbox.build() 1225 wc_dir = sbox.wc_dir 1226 1227 trunk_url = sbox.repo_url + '/A/B/E' 1228 1229 # Create a branch 1230 svntest.actions.run_and_verify_svn(None, [], 'cp', 1231 trunk_url, 1232 sbox.repo_url + '/branch', 1233 '-m', "Creating the Branch") 1234 1235 # Update to revision 2. 1236 svntest.actions.run_and_verify_svn(None, [], 1237 'update', wc_dir) 1238 1239 new_file_path = sbox.ospath('A/B/E/newfile') 1240 svntest.main.file_write(new_file_path, "newfile\n") 1241 1242 # Add the new file, and commit revision 3. 1243 svntest.actions.run_and_verify_svn(None, [], "add", new_file_path) 1244 svntest.actions.run_and_verify_svn(None, [], 1245 'ci', '-m', 1246 "Changing the trunk.", wc_dir) 1247 1248 branch_path = sbox.ospath('branch') 1249 url_branch_path = branch_path.replace(os.path.sep, '/') 1250 1251 # Merge our addition into the branch. 1252 expected_output = svntest.wc.State(branch_path, { 1253 'newfile' : Item(status='A '), 1254 }) 1255 expected_mergeinfo_output = svntest.wc.State(branch_path, { 1256 '' : Item(status=' U'), 1257 }) 1258 expected_elision_output = wc.State(branch_path, { 1259 }) 1260 expected_disk = wc.State('', { 1261 'alpha' : Item("This is the file 'alpha'.\n"), 1262 'beta' : Item("This is the file 'beta'.\n"), 1263 'newfile' : Item("newfile\n"), 1264 }) 1265 expected_status = wc.State(branch_path, { 1266 '' : Item(status=' M', wc_rev=2), 1267 'alpha' : Item(status=' ', wc_rev=2), 1268 'beta' : Item(status=' ', wc_rev=2), 1269 'newfile' : Item(status='A ', wc_rev='-', copied='+') 1270 }) 1271 expected_skip = wc.State('', { }) 1272 1273 svntest.actions.run_and_verify_merge(branch_path, 1274 '1', 'HEAD', trunk_url, None, 1275 expected_output, 1276 expected_mergeinfo_output, 1277 expected_elision_output, 1278 expected_disk, 1279 expected_status, 1280 expected_skip) 1281 1282 # Finally, run diff. 1283 expected_output = [ 1284 "Index: " + url_branch_path + "/newfile\n", 1285 "===================================================================\n", 1286 "--- "+ url_branch_path + "/newfile (nonexistent)\n", 1287 "+++ "+ url_branch_path + "/newfile (working copy)\n", 1288 "@@ -0,0 +1 @@\n", 1289 "+newfile\n", 1290 1291 "Index: " + url_branch_path + "\n", 1292 "===================================================================\n", 1293 "--- "+ url_branch_path + "\t(revision 2)\n", 1294 "+++ "+ url_branch_path + "\t(working copy)\n", 1295 "\n", 1296 "Property changes on: " + url_branch_path + "\n", 1297 "___________________________________________________________________\n", 1298 "Added: " + SVN_PROP_MERGEINFO + "\n", 1299 "## -0,0 +0,1 ##\n", 1300 " Merged /A/B/E:r2-3\n", 1301 ] 1302 svntest.actions.run_and_verify_svn(expected_output, [], 'diff', 1303 '--show-copies-as-adds', branch_path) 1304 1305 1306#---------------------------------------------------------------------- 1307# Issue #1425: 'svn merge' should skip over any unversioned obstructions. 1308# This test involves tree conflicts. - but attempting to test for 1309# pre-tree-conflict behaviour 1310@SkipUnless(server_has_mergeinfo) 1311@Issues(1425, 2898) 1312def merge_skips_obstructions(sbox): 1313 "merge should skip over unversioned obstructions" 1314 1315 sbox.build() 1316 wc_dir = sbox.wc_dir 1317 1318 C_path = sbox.ospath('A/C') 1319 F_path = sbox.ospath('A/B/F') 1320 F_url = sbox.repo_url + '/A/B/F' 1321 1322 Q_path = os.path.join(F_path, 'Q') 1323 foo_path = os.path.join(F_path, 'foo') 1324 bar_path = os.path.join(F_path, 'Q', 'bar') 1325 1326 svntest.main.run_svn(None, 'mkdir', Q_path) 1327 svntest.main.file_append(foo_path, "foo") 1328 svntest.main.file_append(bar_path, "bar") 1329 svntest.main.run_svn(None, 'add', foo_path, bar_path) 1330 1331 expected_output = wc.State(wc_dir, { 1332 'A/B/F/Q' : Item(verb='Adding'), 1333 'A/B/F/Q/bar' : Item(verb='Adding'), 1334 'A/B/F/foo' : Item(verb='Adding'), 1335 }) 1336 expected_status = svntest.actions.get_virginal_state(wc_dir, 1) 1337 expected_status.add({ 1338 'A/B/F/Q' : Item(status=' ', wc_rev=2), 1339 'A/B/F/Q/bar' : Item(status=' ', wc_rev=2), 1340 'A/B/F/foo' : Item(status=' ', wc_rev=2), 1341 }) 1342 svntest.actions.run_and_verify_commit(wc_dir, 1343 expected_output, 1344 expected_status) 1345 1346 pre_merge_status = expected_status 1347 1348 # Revision 2 now has A/B/F/foo, A/B/F/Q, A/B/F/Q/bar. Let's merge 1349 # those 'F' changes into empty dir 'C'. But first, create an 1350 # unversioned 'foo' within C, and make sure 'svn merge' doesn't 1351 # error when the addition of foo is obstructed. 1352 1353 expected_output = wc.State(C_path, { 1354 'Q' : Item(status='A '), 1355 'Q/bar' : Item(status='A '), 1356 }) 1357 expected_mergeinfo_output = wc.State(C_path, { 1358 '' : Item(status=' U'), 1359 }) 1360 expected_elision_output = wc.State(C_path, { 1361 }) 1362 expected_disk = wc.State('', { 1363 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B/F:2'}), 1364 'Q' : Item(), 1365 'Q/bar' : Item("bar"), 1366 'foo' : Item("foo"), 1367 }) 1368 expected_status = wc.State(C_path, { 1369 '' : Item(status=' M', wc_rev=1), 1370 'Q' : Item(status='A ', wc_rev='-', copied='+'), 1371 'Q/bar' : Item(status=' ', wc_rev='-', copied='+'), 1372 }) 1373 expected_skip = wc.State(C_path, { 1374 'foo' : Item(verb='Skipped'), 1375 }) 1376 # Unversioned: 1377 svntest.main.file_append(os.path.join(C_path, "foo"), "foo") 1378 1379 svntest.actions.run_and_verify_merge(C_path, '1', '2', F_url, None, 1380 expected_output, 1381 expected_mergeinfo_output, 1382 expected_elision_output, 1383 expected_disk, 1384 expected_status, 1385 expected_skip, 1386 [], True) 1387 1388 # Revert the local mods, and this time make "Q" obstructed. An 1389 # unversioned file called "Q" will obstruct the adding of the 1390 # directory of the same name. 1391 1392 svntest.actions.run_and_verify_svn(None, [], 1393 'revert', '-R', wc_dir) 1394 os.unlink(os.path.join(C_path, "foo")) 1395 svntest.main.safe_rmtree(os.path.join(C_path, "Q")) 1396 svntest.main.file_append(os.path.join(C_path, "Q"), "foo") # unversioned 1397 svntest.actions.run_and_verify_status(wc_dir, pre_merge_status) 1398 1399 expected_output = wc.State(C_path, { 1400 'foo' : Item(status='A '), 1401 'Q/bar' : Item(status=' ', treeconflict='A'), # Skipped 1402 }) 1403 expected_mergeinfo_output = wc.State(C_path, { 1404 '' : Item(status=' U'), 1405 }) 1406 expected_elision_output = wc.State(C_path, { 1407 }) 1408 expected_disk = wc.State('', { 1409 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B/F:2'}), 1410 'Q' : Item("foo"), 1411 'foo' : Item("foo"), 1412 }) 1413 expected_status = wc.State(C_path, { 1414 '' : Item(status=' M', wc_rev=1), 1415 'foo' : Item(status='A ', wc_rev='-', copied='+'), 1416 }) 1417 expected_skip = wc.State(C_path, { 1418 'Q' : Item(verb='Skipped'), 1419 }) 1420 1421 svntest.actions.run_and_verify_merge(C_path, '1', '2', F_url, None, 1422 expected_output, 1423 expected_mergeinfo_output, 1424 expected_elision_output, 1425 expected_disk, 1426 expected_status, 1427 expected_skip, 1428 [], True) 1429 1430 # Revert the local mods, and commit the deletion of iota and A/D/G. (r3) 1431 os.unlink(os.path.join(C_path, "foo")) 1432 svntest.actions.run_and_verify_svn(None, [], 'revert', '-R', wc_dir) 1433 svntest.actions.run_and_verify_status(wc_dir, pre_merge_status) 1434 1435 iota_path = sbox.ospath('iota') 1436 G_path = sbox.ospath('A/D/G') 1437 svntest.actions.run_and_verify_svn(None, [], 'rm', iota_path, G_path) 1438 1439 expected_output = wc.State(wc_dir, { 1440 'A/D/G' : Item(verb='Deleting'), 1441 'iota' : Item(verb='Deleting'), 1442 }) 1443 expected_status = pre_merge_status 1444 expected_status.remove('iota', 'A/D/G', 'A/D/G/pi', 'A/D/G/rho', 'A/D/G/tau') 1445 svntest.actions.run_and_verify_commit(wc_dir, 1446 expected_output, 1447 expected_status) 1448 1449 # Now create unversioned iota and A/D/G, try running a merge -r2:3. 1450 # The merge process should skip over these targets, since they're 1451 # unversioned. 1452 1453 svntest.main.file_append(iota_path, "foo") # unversioned 1454 os.mkdir(G_path) # unversioned 1455 1456 expected_output = wc.State(wc_dir, { 1457 }) 1458 expected_mergeinfo_output = wc.State(wc_dir, { 1459 '' : Item(status=' U'), 1460 }) 1461 expected_elision_output = wc.State(wc_dir, { 1462 }) 1463 expected_disk = svntest.main.greek_state.copy() 1464 expected_disk.remove('A/D/G/pi', 'A/D/G/rho', 'A/D/G/tau') 1465 expected_disk.add({ 1466 '' : Item(props={SVN_PROP_MERGEINFO : '/:3'}), 1467 'A/B/F/Q' : Item(), 1468 'A/B/F/Q/bar' : Item("bar"), 1469 'A/B/F/foo' : Item("foo"), 1470 'A/C/Q' : Item("foo"), 1471 }) 1472 expected_disk.tweak('iota', contents="foo") 1473 # No-op merge still sets mergeinfo 1474 expected_status.tweak('', status=' M') 1475 expected_skip = wc.State(wc_dir, { 1476 'iota' : Item(verb='Skipped'), 1477 'A/D/G' : Item(verb='Skipped'), 1478 }) 1479 svntest.actions.run_and_verify_merge(wc_dir, '2', '3', 1480 sbox.repo_url, None, 1481 expected_output, 1482 expected_mergeinfo_output, 1483 expected_elision_output, 1484 expected_disk, 1485 expected_status.copy(wc_dir), 1486 expected_skip, 1487 [], 1488 True, False, '--allow-mixed-revisions', 1489 wc_dir) 1490 1491 # Revert the local mods, and commit a change to A/B/lambda (r4), and then 1492 # commit the deletion of the same file. (r5) 1493 svntest.main.safe_rmtree(G_path) 1494 svntest.actions.run_and_verify_svn(None, [], 'revert', '-R', wc_dir) 1495 expected_status.tweak('', status=' ') 1496 svntest.actions.run_and_verify_status(wc_dir, expected_status) 1497 1498 lambda_path = sbox.ospath('A/B/lambda') 1499 svntest.main.file_append(lambda_path, "more text") 1500 expected_output = wc.State(wc_dir, { 1501 'A/B/lambda' : Item(verb='Sending'), 1502 }) 1503 expected_status.tweak('A/B/lambda', wc_rev=4) 1504 svntest.actions.run_and_verify_commit(wc_dir, 1505 expected_output, 1506 expected_status) 1507 1508 svntest.actions.run_and_verify_svn(None, [], 'rm', lambda_path) 1509 1510 expected_output = wc.State(wc_dir, { 1511 'A/B/lambda' : Item(verb='Deleting'), 1512 }) 1513 expected_status.remove('A/B/lambda') 1514 svntest.actions.run_and_verify_commit(wc_dir, 1515 expected_output, 1516 expected_status) 1517 1518 # lambda is gone, so create an unversioned lambda in its place. 1519 # Then attempt to merge -r3:4, which is a change to lambda. The merge 1520 # should simply skip the unversioned file. 1521 1522 svntest.main.file_append(lambda_path, "foo") # unversioned 1523 1524 expected_output = wc.State(wc_dir, { }) 1525 expected_mergeinfo_output = wc.State(wc_dir, { 1526 '' : Item(status=' U'), 1527 }) 1528 expected_elision_output = wc.State(wc_dir, { 1529 }) 1530 expected_disk.add({ 1531 'A/B/lambda' : Item("foo"), 1532 }) 1533 expected_disk.remove('A/D/G') 1534 expected_disk.tweak('', props={SVN_PROP_MERGEINFO : '/:4'}) 1535 expected_skip = wc.State(wc_dir, { 1536 'A/B/lambda' : Item(verb='Skipped'), 1537 }) 1538 # No-op merge still sets mergeinfo. 1539 expected_status_short = expected_status.copy(wc_dir) 1540 expected_status_short.tweak('', status=' M') 1541 1542 svntest.actions.run_and_verify_merge(wc_dir, '3', '4', 1543 sbox.repo_url, None, 1544 expected_output, 1545 expected_mergeinfo_output, 1546 expected_elision_output, 1547 expected_disk, 1548 expected_status_short, 1549 expected_skip, 1550 [], 1551 True, False, '--allow-mixed-revisions', 1552 wc_dir) 1553 1554 # OK, so let's commit the new lambda (r6), and then delete the 1555 # working file. Then re-run the -r3:4 merge, and see how svn deals 1556 # with a file being under version control, but missing. 1557 1558 svntest.actions.run_and_verify_svn(None, [], 'add', lambda_path) 1559 1560 # Mergeinfo prop changed so update to avoid out of date error. 1561 svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir) 1562 1563 expected_output = wc.State(wc_dir, { 1564 '' : Item(verb='Sending'), 1565 'A/B/lambda' : Item(verb='Adding'), 1566 }) 1567 expected_mergeinfo_output = wc.State(wc_dir, {}) 1568 expected_elision_output = wc.State(wc_dir, {}) 1569 expected_status.tweak(wc_rev=5) 1570 expected_status.add({ 1571 'A/B/lambda' : Item(wc_rev=6, status=' '), 1572 }) 1573 expected_status.tweak('', status=' ', wc_rev=6) 1574 svntest.actions.run_and_verify_commit(wc_dir, 1575 expected_output, 1576 expected_status) 1577 os.unlink(lambda_path) 1578 1579 expected_output = wc.State(wc_dir, { }) 1580 expected_disk.remove('A/B/lambda') 1581 expected_status.tweak('A/B/lambda', status='! ') 1582 expected_status.tweak('', status=' ') 1583 expected_skip = wc.State(wc_dir, { 1584 'A/B/lambda' : Item(verb='Skipped missing target'), 1585 }) 1586 # Why do we need to --ignore-ancestry? Because the previous merge of r4, 1587 # despite being inoperative, set mergeinfo for r4 on the WC. With the 1588 # advent of merge tracking this repeat merge attempt would not be attempted. 1589 # By using --ignore-ancestry we disregard the mergeinfo and *really* try to 1590 # merge into a missing path. This is another facet of issue #2898. 1591 svntest.actions.run_and_verify_merge(wc_dir, '3', '4', 1592 sbox.repo_url, None, 1593 expected_output, 1594 expected_mergeinfo_output, 1595 expected_elision_output, 1596 expected_disk, 1597 expected_status.copy(wc_dir), 1598 expected_skip, 1599 [], 1600 1, 0, '--ignore-ancestry', 1601 '--allow-mixed-revisions', wc_dir) 1602 1603#---------------------------------------------------------------------- 1604# At one time, a merge that added items with the same name as missing 1605# items would attempt to add the items and fail, leaving the working 1606# copy locked and broken. 1607 1608# This test involves tree conflicts. 1609@SkipUnless(server_has_mergeinfo) 1610def merge_into_missing(sbox): 1611 "merge into missing must not break working copy" 1612 1613 sbox.build() 1614 wc_dir = sbox.wc_dir 1615 1616 F_path = sbox.ospath('A/B/F') 1617 F_url = sbox.repo_url + '/A/B/F' 1618 Q_path = os.path.join(F_path, 'Q') 1619 foo_path = os.path.join(F_path, 'foo') 1620 1621 svntest.actions.run_and_verify_svn(None, [], 'mkdir', Q_path) 1622 svntest.main.file_append(foo_path, "foo") 1623 svntest.actions.run_and_verify_svn(None, [], 'add', foo_path) 1624 1625 expected_output = wc.State(wc_dir, { 1626 'A/B/F/Q' : Item(verb='Adding'), 1627 'A/B/F/foo' : Item(verb='Adding'), 1628 }) 1629 expected_status = svntest.actions.get_virginal_state(wc_dir, 1) 1630 expected_status.add({ 1631 'A/B/F/Q' : Item(status=' ', wc_rev=2), 1632 'A/B/F/foo' : Item(status=' ', wc_rev=2), 1633 }) 1634 svntest.actions.run_and_verify_commit(wc_dir, 1635 expected_output, 1636 expected_status) 1637 1638 R_path = os.path.join(Q_path, 'R') 1639 bar_path = os.path.join(R_path, 'bar') 1640 baz_path = os.path.join(Q_path, 'baz') 1641 svntest.actions.run_and_verify_svn(None, [], 'mkdir', R_path) 1642 svntest.main.file_append(bar_path, "bar") 1643 svntest.actions.run_and_verify_svn(None, [], 'add', bar_path) 1644 svntest.main.file_append(baz_path, "baz") 1645 svntest.actions.run_and_verify_svn(None, [], 'add', baz_path) 1646 1647 expected_output = wc.State(wc_dir, { 1648 'A/B/F/Q/R' : Item(verb='Adding'), 1649 'A/B/F/Q/R/bar' : Item(verb='Adding'), 1650 'A/B/F/Q/baz' : Item(verb='Adding'), 1651 }) 1652 expected_status.add({ 1653 'A/B/F/Q/R' : Item(status=' ', wc_rev=3), 1654 'A/B/F/Q/R/bar' : Item(status=' ', wc_rev=3), 1655 'A/B/F/Q/baz' : Item(status=' ', wc_rev=3), 1656 }) 1657 svntest.actions.run_and_verify_commit(wc_dir, 1658 expected_output, 1659 expected_status) 1660 1661 os.unlink(foo_path) 1662 svntest.main.safe_rmtree(Q_path) 1663 1664 expected_output = wc.State(F_path, { 1665 }) 1666 expected_mergeinfo_output = wc.State(F_path, { 1667 }) 1668 expected_elision_output = wc.State(F_path, { 1669 }) 1670 expected_disk = wc.State('', { 1671 }) 1672 expected_status = wc.State(F_path, { 1673 '' : Item(status=' ', wc_rev=1), 1674 'foo' : Item(status='! ', wc_rev=2), 1675 'Q' : Item(status='! ', wc_rev=2), 1676 # Missing data still available 1677 'Q/R' : Item(status='! ', wc_rev=3), 1678 'Q/R/bar' : Item(status='! ', wc_rev=3), 1679 'Q/baz' : Item(status='! ', wc_rev=3), 1680 }) 1681 expected_skip = wc.State(F_path, { 1682 'Q' : Item(verb='Skipped missing target'), 1683 'foo' : Item(verb='Skipped missing target'), 1684 }) 1685 # Use --ignore-ancestry because merge tracking aware merges raise an 1686 # error when the merge target is missing subtrees due to OS-level 1687 # deletes. 1688 1689 ### Need to real and dry-run separately since real merge notifies Q 1690 ### twice! 1691 svntest.actions.run_and_verify_merge(F_path, '1', '2', F_url, None, 1692 expected_output, 1693 expected_mergeinfo_output, 1694 expected_elision_output, 1695 expected_disk, 1696 expected_status, 1697 expected_skip, 1698 [], False, False, 1699 '--dry-run', 1700 '--ignore-ancestry', 1701 '--allow-mixed-revisions', 1702 F_path) 1703 1704 expected_status = wc.State(F_path, { 1705 '' : Item(status=' ', wc_rev=1), 1706 'foo' : Item(status='! ', wc_rev=2), 1707 'Q' : Item(status='! ', wc_rev='2'), 1708 # Revision is known and we can record mergeinfo 1709 'Q/R' : Item(status='! ', wc_rev='3'), 1710 'Q/R/bar' : Item(status='! ', wc_rev='3'), 1711 'Q/baz' : Item(status='! ', wc_rev='3'), 1712 }) 1713 expected_mergeinfo_output = wc.State(F_path, { 1714 }) 1715 1716 svntest.actions.run_and_verify_merge(F_path, '1', '2', F_url, None, 1717 expected_output, 1718 expected_mergeinfo_output, 1719 expected_elision_output, 1720 expected_disk, 1721 expected_status, 1722 expected_skip, 1723 [], False, False, 1724 '--ignore-ancestry', 1725 '--allow-mixed-revisions', 1726 F_path) 1727 1728 # This merge fails when it attempts to descend into the missing 1729 # directory. That's OK, there is no real need to support merge into 1730 # an incomplete working copy, so long as when it fails it doesn't 1731 # break the working copy. 1732 svntest.main.run_svn('Working copy not locked', 1733 'merge', '-r1:3', '--dry-run', F_url, F_path) 1734 1735 svntest.main.run_svn('Working copy not locked', 1736 'merge', '-r1:3', F_url, F_path) 1737 1738 # Check working copy is not locked. 1739 expected_status = svntest.actions.get_virginal_state(wc_dir, 1) 1740 expected_status.add({ 1741 'A/B/F' : Item(status=' ', wc_rev=1), 1742 'A/B/F/foo' : Item(status='! ', wc_rev=2), 1743 'A/B/F/Q' : Item(status='! ', wc_rev=2), 1744 'A/B/F/Q/baz' : Item(status='! ', wc_rev='3'), 1745 'A/B/F/Q/R' : Item(status='! ', wc_rev='3'), 1746 'A/B/F/Q/R/bar' : Item(status='! ', wc_rev='3'), 1747 }) 1748 1749 svntest.actions.run_and_verify_status(wc_dir, expected_status) 1750 1751#---------------------------------------------------------------------- 1752# A test for issue 1738 1753@Issue(1738) 1754@SkipUnless(server_has_mergeinfo) 1755def dry_run_adds_file_with_prop(sbox): 1756 "merge --dry-run adding a new file with props" 1757 1758 sbox.build() 1759 wc_dir = sbox.wc_dir 1760 1761 # Commit a new file which has a property. 1762 zig_path = sbox.ospath('A/B/E/zig') 1763 svntest.main.file_append(zig_path, "zig contents") 1764 svntest.actions.run_and_verify_svn(None, [], 'add', zig_path) 1765 svntest.actions.run_and_verify_svn(None, [], 1766 'propset', 'foo', 'foo_val', 1767 zig_path) 1768 1769 expected_output = wc.State(wc_dir, { 1770 'A/B/E/zig' : Item(verb='Adding'), 1771 }) 1772 expected_status = svntest.actions.get_virginal_state(wc_dir, 1) 1773 expected_status.add({ 1774 'A/B/E/zig' : Item(status=' ', wc_rev=2), 1775 }) 1776 svntest.actions.run_and_verify_commit(wc_dir, 1777 expected_output, 1778 expected_status) 1779 1780 # Do a regular merge of that change into a different dir. 1781 F_path = sbox.ospath('A/B/F') 1782 E_url = sbox.repo_url + '/A/B/E' 1783 1784 expected_output = wc.State(F_path, { 1785 'zig' : Item(status='A '), 1786 }) 1787 expected_mergeinfo_output = wc.State(F_path, { 1788 '' : Item(status=' U'), 1789 }) 1790 expected_elision_output = wc.State(F_path, { 1791 }) 1792 expected_disk = wc.State('', { 1793 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B/E:2'}), 1794 'zig' : Item("zig contents", {'foo':'foo_val'}), 1795 }) 1796 expected_skip = wc.State('', { }) 1797 expected_status = None # status is optional 1798 1799 svntest.actions.run_and_verify_merge(F_path, '1', '2', E_url, None, 1800 expected_output, 1801 expected_mergeinfo_output, 1802 expected_elision_output, 1803 expected_disk, 1804 expected_status, 1805 expected_skip, 1806 [], True, True) 1807 1808#---------------------------------------------------------------------- 1809# Regression test for issue #1673 1810# Merge a binary file from two URL with a common ancestry 1811@Issue(1673) 1812def merge_binary_with_common_ancestry(sbox): 1813 "merge binary files with common ancestry" 1814 1815 sbox.build() 1816 wc_dir = sbox.wc_dir 1817 1818 # Create the common ancestry path 1819 I_path = sbox.ospath('I') 1820 svntest.main.run_svn(None, 'mkdir', I_path) 1821 1822 # Add a binary file to the common ancestry path 1823 theta_contents = open(os.path.join(sys.path[0], "theta.bin"), 'rb').read() 1824 theta_I_path = os.path.join(I_path, 'theta') 1825 svntest.main.file_write(theta_I_path, theta_contents, mode='wb') 1826 svntest.main.run_svn(None, 'add', theta_I_path) 1827 svntest.main.run_svn(None, 'propset', 'svn:mime-type', 1828 'application/octet-stream', theta_I_path) 1829 1830 # Commit the ancestry 1831 expected_output = wc.State(wc_dir, { 1832 'I' : Item(verb='Adding'), 1833 'I/theta' : Item(verb='Adding (bin)'), 1834 }) 1835 1836 expected_status = svntest.actions.get_virginal_state(wc_dir, 1) 1837 expected_status.add({ 1838 'I' : Item(status=' ', wc_rev=2), 1839 'I/theta' : Item(status=' ', wc_rev=2), 1840 }) 1841 1842 svntest.actions.run_and_verify_commit(wc_dir, 1843 expected_output, expected_status) 1844 1845 # Create the first branch 1846 J_path = sbox.ospath('J') 1847 svntest.main.run_svn(None, 'copy', I_path, J_path) 1848 1849 # Commit the first branch 1850 expected_output = wc.State(wc_dir, { 1851 'J' : Item(verb='Adding'), 1852 }) 1853 1854 expected_status.add({ 1855 'J' : Item(status=' ', wc_rev=3), 1856 'J/theta' : Item(status=' ', wc_rev=3), 1857 }) 1858 1859 svntest.actions.run_and_verify_commit(wc_dir, 1860 expected_output, expected_status) 1861 1862 # Create the path where the files will be merged 1863 K_path = sbox.ospath('K') 1864 svntest.main.run_svn(None, 'mkdir', K_path) 1865 1866 # Commit the new path 1867 expected_output = wc.State(wc_dir, { 1868 'K' : Item(verb='Adding'), 1869 }) 1870 1871 expected_status.add({ 1872 'K' : Item(status=' ', wc_rev=4), 1873 }) 1874 1875 svntest.actions.run_and_verify_commit(wc_dir, 1876 expected_output, expected_status) 1877 1878 # Copy 'I/theta' to 'K/'. This file will be merged later. 1879 theta_K_path = os.path.join(K_path, 'theta') 1880 svntest.main.run_svn(None, 'copy', theta_I_path, theta_K_path) 1881 1882 # Commit the new file 1883 expected_output = wc.State(wc_dir, { 1884 'K/theta' : Item(verb='Adding (bin)'), 1885 }) 1886 1887 expected_status.add({ 1888 'K/theta' : Item(status=' ', wc_rev=5), 1889 }) 1890 1891 svntest.actions.run_and_verify_commit(wc_dir, 1892 expected_output, expected_status) 1893 1894 # Modify the original ancestry 'I/theta' 1895 svntest.main.file_append(theta_I_path, "some extra junk") 1896 1897 # Commit the modification 1898 expected_output = wc.State(wc_dir, { 1899 'I/theta' : Item(verb='Sending'), 1900 }) 1901 1902 expected_status.tweak('I/theta', wc_rev=6) 1903 1904 svntest.actions.run_and_verify_commit(wc_dir, 1905 expected_output, expected_status) 1906 1907 # Create the second branch from the modified ancestry 1908 L_path = sbox.ospath('L') 1909 svntest.main.run_svn(None, 'copy', I_path, L_path) 1910 1911 # Commit the second branch 1912 expected_output = wc.State(wc_dir, { 1913 'L' : Item(verb='Adding'), 1914 'L/theta' : Item(verb='Replacing'), 1915 }) 1916 1917 expected_status.add({ 1918 'L' : Item(status=' ', wc_rev=7), 1919 'L/theta' : Item(status=' ', wc_rev=7), 1920 }) 1921 1922 svntest.actions.run_and_verify_commit(wc_dir, 1923 expected_output, expected_status) 1924 1925 # Now merge first ('J/') and second ('L/') branches into 'K/' 1926 saved_cwd = os.getcwd() 1927 1928 os.chdir(K_path) 1929 theta_J_url = sbox.repo_url + '/J/theta' 1930 theta_L_url = sbox.repo_url + '/L/theta' 1931 svntest.actions.run_and_verify_svn(expected_merge_output(None, 1932 ['U theta\n', 1933 ' U theta\n', 1934 ' G theta\n',], 1935 two_url=True), 1936 [], 1937 'merge', theta_J_url, theta_L_url) 1938 os.chdir(saved_cwd) 1939 1940 expected_status.tweak('K/theta', status='MM') 1941 svntest.actions.run_and_verify_status(wc_dir, expected_status) 1942 1943#---------------------------------------------------------------------- 1944# A test for issue 1905 1945@Issue(1905) 1946@SkipUnless(server_has_mergeinfo) 1947def merge_funny_chars_on_path(sbox): 1948 "merge with funny characters" 1949 1950 sbox.build() 1951 wc_dir = sbox.wc_dir 1952 1953 # In following lists: 'd' stands for directory, 'f' for file 1954 # targets to be added by recursive add 1955 add_by_add = [ 1956 ('d', 'dir_10', 'F%lename'), 1957 ('d', 'dir%20', 'F lename'), 1958 ('d', 'dir 30', 'Filename'), 1959 ('d', 'dir 40', None), 1960 ('f', 'F lename', None), 1961 ] 1962 1963 # targets to be added by 'svn mkdir' + add 1964 add_by_mkdir = [ 1965 ('d', 'dir_11', 'F%lename'), 1966 ('d', 'dir%21', 'Filename'), 1967 ('d', 'dir 31', 'F lename'), 1968 ('d', 'dir 41', None), 1969 ] 1970 1971 for target in add_by_add: 1972 if target[0] == 'd': 1973 target_dir = os.path.join(wc_dir, 'A', 'B', 'E', target[1]) 1974 os.mkdir(target_dir) 1975 if target[2]: 1976 target_path = os.path.join(wc_dir, 'A', 'B', 'E', '%s' % target[1], 1977 target[2]) 1978 svntest.main.file_append(target_path, "%s/%s" % (target[1], target[2])) 1979 svntest.actions.run_and_verify_svn(None, [], 'add', target_dir) 1980 elif target[0] == 'f': 1981 target_path = os.path.join(wc_dir, 'A', 'B', 'E', '%s' % target[1]) 1982 svntest.main.file_append(target_path, "%s" % target[1]) 1983 svntest.actions.run_and_verify_svn(None, [], 'add', target_path) 1984 else: 1985 raise svntest.Failure 1986 1987 1988 for target in add_by_mkdir: 1989 if target[0] == 'd': 1990 target_dir = os.path.join(wc_dir, 'A', 'B', 'E', target[1]) 1991 svntest.actions.run_and_verify_svn(None, [], 'mkdir', target_dir) 1992 if target[2]: 1993 target_path = os.path.join(wc_dir, 'A', 'B', 'E', '%s' % target[1], 1994 target[2]) 1995 svntest.main.file_append(target_path, "%s/%s" % (target[1], target[2])) 1996 svntest.actions.run_and_verify_svn(None, [], 'add', target_path) 1997 1998 expected_output_dic = {} 1999 expected_status_dic = {} 2000 2001 for targets in add_by_add,add_by_mkdir: 2002 for target in targets: 2003 key = 'A/B/E/%s' % target[1] 2004 expected_output_dic[key] = Item(verb='Adding') 2005 expected_status_dic[key] = Item(status=' ', wc_rev=2) 2006 2007 if target[2]: 2008 key = 'A/B/E/%s/%s' % (target[1], target[2]) 2009 expected_output_dic[key] = Item(verb='Adding') 2010 expected_status_dic[key] = Item(status=' ', wc_rev=2) 2011 2012 2013 expected_output = wc.State(wc_dir, expected_output_dic) 2014 2015 expected_status = svntest.actions.get_virginal_state(wc_dir, 1) 2016 expected_status.add(expected_status_dic) 2017 2018 svntest.actions.run_and_verify_commit(wc_dir, 2019 expected_output, 2020 expected_status) 2021 2022 # Do a regular merge of that change into a different dir. 2023 F_path = sbox.ospath('A/B/F') 2024 E_url = sbox.repo_url + '/A/B/E' 2025 2026 expected_output_dic = {} 2027 expected_disk_dic = {} 2028 2029 for targets in add_by_add,add_by_mkdir: 2030 for target in targets: 2031 key = '%s' % target[1] 2032 expected_output_dic[key] = Item(status='A ') 2033 if target[0] == 'd': 2034 expected_disk_dic[key] = Item(None, {}) 2035 elif target[0] == 'f': 2036 expected_disk_dic[key] = Item("%s" % target[1], {}) 2037 else: 2038 raise svntest.Failure 2039 if target[2]: 2040 key = '%s/%s' % (target[1], target[2]) 2041 expected_output_dic[key] = Item(status='A ') 2042 expected_disk_dic[key] = Item('%s/%s' % (target[1], target[2]), {}) 2043 2044 expected_output = wc.State(F_path, expected_output_dic) 2045 expected_mergeinfo_output = wc.State(F_path, { 2046 '' : Item(status=' U'), 2047 }) 2048 expected_elision_output = wc.State(F_path, { 2049 }) 2050 expected_disk = wc.State('', expected_disk_dic) 2051 expected_skip = wc.State('', { }) 2052 expected_status = None # status is optional 2053 2054 svntest.actions.run_and_verify_merge(F_path, '1', '2', E_url, None, 2055 expected_output, 2056 expected_mergeinfo_output, 2057 expected_elision_output, 2058 expected_disk, 2059 expected_status, 2060 expected_skip, 2061 [], 2062 False, # don't check props 2063 True) # but do a dry-run 2064 2065 expected_output_dic = {} 2066 2067 for targets in add_by_add,add_by_mkdir: 2068 for target in targets: 2069 key = '%s' % target[1] 2070 expected_output_dic[key] = Item(verb='Adding') 2071 2072 expected_output = wc.State(F_path, expected_output_dic) 2073 expected_output.add({ 2074 '' : Item(verb='Sending'), 2075 }) 2076 2077 svntest.actions.run_and_verify_commit(F_path, 2078 expected_output, 2079 None) 2080 2081#----------------------------------------------------------------------- 2082# Regression test for issue #2064 2083@Issue(2064) 2084def merge_keyword_expansions(sbox): 2085 "merge changes to keyword expansion property" 2086 2087 sbox.build() 2088 2089 wcpath = sbox.wc_dir 2090 tpath = os.path.join(wcpath, "t") 2091 bpath = os.path.join(wcpath, "b") 2092 t_fpath = os.path.join(tpath, 'f') 2093 b_fpath = os.path.join(bpath, 'f') 2094 2095 os.mkdir(tpath) 2096 svntest.main.run_svn(None, "add", tpath) 2097 # Commit r2. 2098 svntest.actions.run_and_verify_svn(None, [], 2099 "ci", "-m", "r2", wcpath) 2100 2101 # Copy t to b. 2102 svntest.main.run_svn(None, "cp", tpath, bpath) 2103 # Commit r3 2104 svntest.actions.run_and_verify_svn(None, [], 2105 "ci", "-m", "r3", wcpath) 2106 2107 # Add a file to t. 2108 svntest.main.file_append(t_fpath, "$Revision$") 2109 svntest.actions.run_and_verify_svn(None, [], 2110 'add', t_fpath) 2111 # Ask for keyword expansion in the file. 2112 svntest.actions.run_and_verify_svn(None, [], 2113 'propset', 'svn:keywords', 'Revision', 2114 t_fpath) 2115 # Commit r4 2116 svntest.actions.run_and_verify_svn(None, [], 2117 'ci', '-m', 'r4', wcpath) 2118 2119 # Update the wc before the merge. 2120 svntest.actions.run_and_verify_svn(None, [], 2121 'update', wcpath) 2122 2123 expected_status = svntest.actions.get_virginal_state(wcpath, 4) 2124 expected_status.add({ 2125 't' : Item(status=' ', wc_rev=4), 2126 't/f' : Item(status=' ', wc_rev=4), 2127 'b' : Item(status=' ', wc_rev=4), 2128 }) 2129 svntest.actions.run_and_verify_status(wcpath, expected_status) 2130 2131 # Do the merge. 2132 2133 expected_output = wc.State(bpath, { 2134 'f' : Item(status='A '), 2135 }) 2136 expected_mergeinfo_output = wc.State(bpath, { 2137 '' : Item(status=' U'), 2138 }) 2139 expected_elision_output = wc.State(bpath, { 2140 }) 2141 expected_disk = wc.State('', { 2142 'f' : Item("$Revision: 4 $"), 2143 }) 2144 expected_status = wc.State(bpath, { 2145 '' : Item(status=' M', wc_rev=4), 2146 'f' : Item(status='A ', wc_rev='-', copied='+'), 2147 }) 2148 expected_skip = wc.State(bpath, { }) 2149 2150 svntest.actions.run_and_verify_merge(bpath, '2', 'HEAD', 2151 sbox.repo_url + '/t', None, 2152 expected_output, 2153 expected_mergeinfo_output, 2154 expected_elision_output, 2155 expected_disk, 2156 expected_status, 2157 expected_skip) 2158 2159#---------------------------------------------------------------------- 2160@Issue(2132) 2161def merge_prop_change_to_deleted_target(sbox): 2162 "merge prop change into deleted target" 2163 # For issue #2132. 2164 sbox.build() 2165 wc_dir = sbox.wc_dir 2166 2167 # Add a property to alpha. 2168 alpha_path = sbox.ospath('A/B/E/alpha') 2169 svntest.actions.run_and_verify_svn(None, [], 2170 'propset', 'foo', 'foo_val', 2171 alpha_path) 2172 2173 # Commit the property add as r2. 2174 expected_output = svntest.wc.State(wc_dir, { 2175 'A/B/E/alpha' : Item(verb='Sending'), 2176 }) 2177 expected_status = svntest.actions.get_virginal_state(wc_dir, 1) 2178 expected_status.tweak('A/B/E/alpha', wc_rev=2, status=' ') 2179 svntest.actions.run_and_verify_commit(wc_dir, 2180 expected_output, expected_status) 2181 svntest.actions.run_and_verify_svn(None, [], 2182 'up', wc_dir) 2183 2184 # Remove alpha entirely. 2185 svntest.actions.run_and_verify_svn(None, [], 'rm', alpha_path) 2186 expected_output = wc.State(wc_dir, { 2187 'A/B/E/alpha' : Item(verb='Deleting'), 2188 }) 2189 expected_status.tweak(wc_rev=2) 2190 expected_status.remove('A/B/E/alpha') 2191 svntest.actions.run_and_verify_commit(wc_dir, 2192 expected_output, 2193 expected_status, 2194 [], alpha_path) 2195 2196 # Try merging the original propset, which applies to a target that 2197 # no longer exists. The bug would only reproduce when run from 2198 # inside the wc, so we cd in there. We have to use 2199 # --ignore-ancestry here because our merge logic will otherwise 2200 # prevent a merge of changes we already have. 2201 os.chdir(wc_dir) 2202 svntest.actions.run_and_verify_svn(svntest.verify.AnyOutput, [], 'merge', 2203 '-r1:2', '--ignore-ancestry', '.') 2204 2205#---------------------------------------------------------------------- 2206# A merge that replaces a directory 2207# Tests for Issue #2144 and Issue #2607 2208@SkipUnless(server_has_mergeinfo) 2209@Issue(2144,2607) 2210def merge_dir_replace(sbox): 2211 "merge a replacement of a directory" 2212 2213 set_up_dir_replace(sbox) 2214 wc_dir = sbox.wc_dir 2215 2216 C_path = sbox.ospath('A/C') 2217 F_path = sbox.ospath('A/B/F') 2218 F_url = sbox.repo_url + '/A/B/F' 2219 foo_path = os.path.join(F_path, 'foo') 2220 new_file2 = os.path.join(foo_path, "new file 2") 2221 2222 # Recreate foo in F and add a new folder and two files 2223 bar_path = os.path.join(foo_path, 'bar') 2224 foo_file = os.path.join(foo_path, "file foo") 2225 new_file3 = os.path.join(bar_path, "new file 3") 2226 2227 # Make a couple of directories, and add some files within them. 2228 svntest.actions.run_and_verify_svn(None, [], 'mkdir', foo_path) 2229 svntest.actions.run_and_verify_svn(None, [], 'mkdir', bar_path) 2230 svntest.main.file_append(new_file3, "Initial text in new file 3.\n") 2231 svntest.main.run_svn(None, "add", new_file3) 2232 svntest.main.file_append(foo_file, "Initial text in file foo.\n") 2233 svntest.main.run_svn(None, "add", foo_file) 2234 2235 # Commit the new content, creating r5. 2236 expected_output = wc.State(wc_dir, { 2237 'A/B/F/foo' : Item(verb='Adding'), 2238 'A/B/F/foo/file foo' : Item(verb='Adding'), 2239 'A/B/F/foo/bar' : Item(verb='Adding'), 2240 'A/B/F/foo/bar/new file 3' : Item(verb='Adding'), 2241 }) 2242 expected_status = svntest.actions.get_virginal_state(wc_dir, 1) 2243 expected_status.add({ 2244 'A/B/F/foo' : Item(status=' ', wc_rev=5), 2245 'A/B/F/foo/file foo' : Item(status=' ', wc_rev=5), 2246 'A/B/F/foo/bar' : Item(status=' ', wc_rev=5), 2247 'A/B/F/foo/bar/new file 3' : Item(status=' ', wc_rev=5), 2248 'A/C' : Item(status=' ', wc_rev=3), 2249 'A/C/foo' : Item(status=' ', wc_rev=3), 2250 'A/C/foo/new file' : Item(status=' ', wc_rev=3), 2251 'A/C/foo/new file 2' : Item(status=' ', wc_rev=3), 2252 }) 2253 svntest.actions.run_and_verify_commit(wc_dir, 2254 expected_output, 2255 expected_status) 2256 # Merge replacement of foo onto C 2257 expected_output = wc.State(C_path, { 2258 'foo' : Item(status='R '), 2259 'foo/file foo' : Item(status='A '), 2260 'foo/bar' : Item(status='A '), 2261 'foo/bar/new file 3' : Item(status='A '), 2262 }) 2263 expected_mergeinfo_output = wc.State(C_path, { 2264 '' : Item(status=' U'), 2265 }) 2266 expected_elision_output = wc.State(C_path, { 2267 }) 2268 expected_disk = wc.State('', { 2269 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B/F:2-5'}), 2270 'foo' : Item(), 2271 'foo/file foo' : Item("Initial text in file foo.\n"), 2272 'foo/bar' : Item(), 2273 'foo/bar/new file 3' : Item("Initial text in new file 3.\n"), 2274 }) 2275 expected_status = wc.State(C_path, { 2276 '' : Item(status=' M', wc_rev=3), 2277 'foo' : Item(status='R ', wc_rev='-', copied='+'), 2278 'foo/new file 2' : Item(status='D ', wc_rev='3'), 2279 'foo/file foo' : Item(status=' ', wc_rev='-', copied='+'), 2280 'foo/bar' : Item(status=' ', wc_rev='-', copied='+'), 2281 'foo/bar/new file 3' : Item(status=' ', wc_rev='-', copied='+'), 2282 'foo/new file' : Item(status='D ', wc_rev='3'), 2283 }) 2284 expected_skip = wc.State(C_path, { }) 2285 svntest.actions.run_and_verify_merge(C_path, '2', '5', F_url, None, 2286 expected_output, 2287 expected_mergeinfo_output, 2288 expected_elision_output, 2289 expected_disk, 2290 expected_status, 2291 expected_skip, 2292 [], True, 2293 False) # don't do a dry-run 2294 # the output differs 2295 2296 # Commit merge of foo onto C 2297 expected_output = svntest.wc.State(wc_dir, { 2298 'A/C' : Item(verb='Sending'), 2299 'A/C/foo' : Item(verb='Replacing'), 2300 }) 2301 expected_status = svntest.actions.get_virginal_state(wc_dir, 1) 2302 expected_status.add({ 2303 'A/B/F/foo' : Item(status=' ', wc_rev=5), 2304 'A/B/F/foo/file foo' : Item(status=' ', wc_rev=5), 2305 'A/B/F/foo/bar' : Item(status=' ', wc_rev=5), 2306 'A/B/F/foo/bar/new file 3' : Item(status=' ', wc_rev=5), 2307 'A/C' : Item(status=' ', wc_rev=6), 2308 'A/C/foo' : Item(status=' ', wc_rev=6), 2309 'A/C/foo/file foo' : Item(status=' ', wc_rev=6), 2310 'A/C/foo/bar' : Item(status=' ', wc_rev=6), 2311 'A/C/foo/bar/new file 3' : Item(status=' ', wc_rev=6), 2312 }) 2313 svntest.actions.run_and_verify_commit(wc_dir, 2314 expected_output, 2315 expected_status) 2316 2317#---------------------------------------------------------------------- 2318# A merge that replaces a directory and one of its children 2319# Tests for Issue #2690 2320@Issue(2690) 2321def merge_dir_and_file_replace(sbox): 2322 "replace both dir and one of its children" 2323 2324 set_up_dir_replace(sbox) 2325 wc_dir = sbox.wc_dir 2326 2327 C_path = sbox.ospath('A/C') 2328 F_path = sbox.ospath('A/B/F') 2329 F_url = sbox.repo_url + '/A/B/F' 2330 foo_path = os.path.join(F_path, 'foo') 2331 new_file2 = os.path.join(foo_path, "new file 2") 2332 2333 # Recreate foo and 'new file 2' in F and add a new folder with a file 2334 bar_path = os.path.join(foo_path, 'bar') 2335 new_file3 = os.path.join(bar_path, "new file 3") 2336 svntest.actions.run_and_verify_svn(None, [], 'mkdir', foo_path) 2337 svntest.actions.run_and_verify_svn(None, [], 'mkdir', bar_path) 2338 svntest.main.file_append(new_file3, "Initial text in new file 3.\n") 2339 svntest.main.run_svn(None, "add", new_file3) 2340 svntest.main.file_append(new_file2, "New text in new file 2.\n") 2341 svntest.main.run_svn(None, "add", new_file2) 2342 2343 expected_output = wc.State(wc_dir, { 2344 'A/B/F/foo' : Item(verb='Adding'), 2345 'A/B/F/foo/new file 2' : Item(verb='Adding'), 2346 'A/B/F/foo/bar' : Item(verb='Adding'), 2347 'A/B/F/foo/bar/new file 3' : Item(verb='Adding'), 2348 }) 2349 expected_status = svntest.actions.get_virginal_state(wc_dir, 1) 2350 expected_status.add({ 2351 'A/B/F/foo' : Item(status=' ', wc_rev=5), 2352 'A/B/F/foo/new file 2' : Item(status=' ', wc_rev=5), 2353 'A/B/F/foo/bar' : Item(status=' ', wc_rev=5), 2354 'A/B/F/foo/bar/new file 3' : Item(status=' ', wc_rev=5), 2355 'A/C/foo' : Item(status=' ', wc_rev=3), 2356 'A/C/foo/new file' : Item(status=' ', wc_rev=3), 2357 'A/C/foo/new file 2' : Item(status=' ', wc_rev=3), 2358 }) 2359 expected_status.tweak('A/C', wc_rev=3) # From mergeinfo 2360 svntest.actions.run_and_verify_commit(wc_dir, 2361 expected_output, 2362 expected_status) 2363 # Merge replacement of foo onto C 2364 expected_output = wc.State(C_path, { 2365 'foo' : Item(status='R '), 2366 'foo/new file 2' : Item(status='A '), 2367 'foo/bar' : Item(status='A '), 2368 'foo/bar/new file 3' : Item(status='A '), 2369 }) 2370 expected_mergeinfo_output = wc.State(C_path, { 2371 '' : Item(status=' U'), 2372 }) 2373 expected_elision_output = wc.State(C_path, { 2374 }) 2375 expected_disk = wc.State('', { 2376 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B/F:2-5'}), 2377 'foo' : Item(), 2378 'foo/new file 2' : Item("New text in new file 2.\n"), 2379 'foo/bar' : Item(), 2380 'foo/bar/new file 3' : Item("Initial text in new file 3.\n"), 2381 }) 2382 expected_status = wc.State(C_path, { 2383 '' : Item(status=' M', wc_rev=3), 2384 'foo' : Item(status='R ', wc_rev='-', copied='+'), 2385 'foo/new file 2' : Item(status=' ', wc_rev='-', copied='+'), 2386 'foo/bar' : Item(status=' ', wc_rev='-', copied='+'), 2387 'foo/bar/new file 3' : Item(status=' ', wc_rev='-', copied='+'), 2388 'foo/new file' : Item(status='D ', wc_rev=3), 2389 }) 2390 expected_skip = wc.State(C_path, { }) 2391 svntest.actions.run_and_verify_merge(C_path, '2', '5', F_url, None, 2392 expected_output, 2393 expected_mergeinfo_output, 2394 expected_elision_output, 2395 expected_disk, 2396 expected_status, 2397 expected_skip, 2398 [], 2399 True, 2400 False) # don't do a dry-run 2401 # the output differs 2402 2403 # Commit merge of foo onto C 2404 expected_output = svntest.wc.State(wc_dir, { 2405 'A/C' : Item(verb='Sending'), 2406 'A/C/foo' : Item(verb='Replacing'), 2407 }) 2408 2409 expected_status = svntest.actions.get_virginal_state(wc_dir, 1) 2410 expected_status.add({ 2411 'A/B/F/foo' : Item(status=' ', wc_rev=5), 2412 'A/B/F/foo/new file 2' : Item(status=' ', wc_rev=5), 2413 'A/B/F/foo/bar' : Item(status=' ', wc_rev=5), 2414 'A/B/F/foo/bar/new file 3' : Item(status=' ', wc_rev=5), 2415 'A/C' : Item(status=' ', wc_rev=6), 2416 'A/C/foo' : Item(status=' ', wc_rev=6), 2417 'A/C/foo/new file 2' : Item(status=' ', wc_rev=6), 2418 'A/C/foo/bar' : Item(status=' ', wc_rev=6), 2419 'A/C/foo/bar/new file 3' : Item(status=' ', wc_rev=6), 2420 }) 2421 svntest.actions.run_and_verify_commit(wc_dir, 2422 expected_output, 2423 expected_status) 2424 2425 # Confirm the files are present in the repository. 2426 new_file_2_url = sbox.repo_url + '/A/C/foo/new file 2' 2427 svntest.actions.run_and_verify_svn(["New text in new file 2.\n"], 2428 [], 'cat', 2429 new_file_2_url) 2430 new_file_3_url = sbox.repo_url + '/A/C/foo/bar/new file 3' 2431 svntest.actions.run_and_verify_svn(["Initial text in new file 3.\n"], 2432 [], 'cat', 2433 new_file_3_url) 2434 2435#---------------------------------------------------------------------- 2436@Issue(2144) 2437def merge_file_with_space_in_its_name(sbox): 2438 "merge a file whose name contains a space" 2439 # For issue #2144 2440 sbox.build() 2441 wc_dir = sbox.wc_dir 2442 new_file = sbox.ospath('new file') 2443 2444 # Make r2. 2445 svntest.main.file_append(new_file, "Initial text in the file.\n") 2446 svntest.main.run_svn(None, "add", new_file) 2447 svntest.actions.run_and_verify_svn(None, [], 2448 "ci", "-m", "r2", wc_dir) 2449 2450 # Make r3. 2451 svntest.main.file_append(new_file, "Next line of text in the file.\n") 2452 svntest.actions.run_and_verify_svn(None, [], 2453 "ci", "-m", "r3", wc_dir) 2454 2455 # Try to reverse merge. 2456 # 2457 # The reproduction recipe requires that no explicit merge target be 2458 # passed, so we run merge from inside the wc dir where the target 2459 # file (i.e., the URL basename) lives. 2460 os.chdir(wc_dir) 2461 target_url = sbox.repo_url + '/new%20file' 2462 svntest.actions.run_and_verify_svn(None, [], 2463 "merge", "-r3:2", target_url) 2464 2465#---------------------------------------------------------------------- 2466# A merge between two branches using no revision number with the dir being 2467# created already existing as an unversioned directory. 2468# Tests for Issue #2222 2469@Issue(2222) 2470def merge_dir_branches(sbox): 2471 "merge between branches" 2472 2473 sbox.build() 2474 wc_dir = sbox.wc_dir 2475 wc_uuid = svntest.actions.get_wc_uuid(wc_dir) 2476 2477 F_path = sbox.ospath('A/B/F') 2478 F_url = sbox.repo_url + '/A/B/F' 2479 C_url = sbox.repo_url + '/A/C' 2480 2481 # Create foo in F 2482 foo_path = os.path.join(F_path, 'foo') 2483 svntest.actions.run_and_verify_svn(None, [], 'mkdir', foo_path) 2484 2485 expected_output = wc.State(wc_dir, { 2486 'A/B/F/foo' : Item(verb='Adding'), 2487 }) 2488 expected_status = svntest.actions.get_virginal_state(wc_dir, 1) 2489 expected_status.add({ 2490 'A/B/F/foo' : Item(status=' ', wc_rev=2), 2491 }) 2492 svntest.actions.run_and_verify_commit(wc_dir, 2493 expected_output, 2494 expected_status) 2495 2496 # Create an unversioned foo 2497 foo_path = sbox.ospath('foo') 2498 os.mkdir(foo_path) 2499 2500 # Merge from C to F onto the wc_dir 2501 # We can't use run_and_verify_merge because it doesn't support this 2502 # syntax of the merge command. 2503 ### TODO: We can use run_and_verify_merge() here now. 2504 expected_output = expected_merge_output(None, "A " + foo_path + "\n") 2505 svntest.actions.run_and_verify_svn(expected_output, [], 2506 'merge', '--allow-mixed-revisions', 2507 C_url, F_url, wc_dir) 2508 2509 # Run info to check the copied rev to make sure it's right 2510 expected_info = {"Path" : re.escape(foo_path), # escape backslashes 2511 "URL" : sbox.repo_url + "/foo", 2512 "Repository Root" : sbox.repo_url, 2513 "Repository UUID" : wc_uuid, 2514 "Revision" : "2", 2515 "Node Kind" : "directory", 2516 "Schedule" : "add", 2517 "Copied From URL" : F_url + "/foo", 2518 "Copied From Rev" : "2", 2519 } 2520 svntest.actions.run_and_verify_info([expected_info], foo_path) 2521 2522 2523#---------------------------------------------------------------------- 2524def safe_property_merge(sbox): 2525 "property merges don't overwrite existing prop-mods" 2526 2527 sbox.build() 2528 wc_dir = sbox.wc_dir 2529 2530 # Add a property to two files and a directory, commit as r2. 2531 alpha_path = sbox.ospath('A/B/E/alpha') 2532 beta_path = sbox.ospath('A/B/E/beta') 2533 E_path = sbox.ospath('A/B/E') 2534 2535 svntest.actions.run_and_verify_svn(None, [], 2536 'propset', 'foo', 'foo_val', 2537 alpha_path, beta_path) 2538 svntest.actions.run_and_verify_svn(None, [], 2539 'propset', 'foo', 'foo_val', 2540 E_path) 2541 2542 expected_output = svntest.wc.State(wc_dir, { 2543 'A/B/E' : Item(verb='Sending'), 2544 'A/B/E/alpha' : Item(verb='Sending'), 2545 'A/B/E/beta' : Item(verb='Sending'), 2546 }) 2547 expected_status = svntest.actions.get_virginal_state(wc_dir, 1) 2548 expected_status.tweak('A/B/E', 'A/B/E/alpha', 'A/B/E/beta', 2549 wc_rev=2, status=' ') 2550 svntest.actions.run_and_verify_commit(wc_dir, 2551 expected_output, expected_status) 2552 svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir) 2553 2554 # Copy B to B2 as rev 3 (making a branch) 2555 B_url = sbox.repo_url + '/A/B' 2556 B2_url = sbox.repo_url + '/A/B2' 2557 2558 svntest.actions.run_and_verify_svn(None, [], 2559 'copy', '-m', 'copy B to B2', 2560 B_url, B2_url) 2561 svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir) 2562 2563 # Change the properties underneath B again, and commit as r4 2564 svntest.actions.run_and_verify_svn(None, [], 2565 'propset', 'foo', 'foo_val2', 2566 alpha_path) 2567 svntest.actions.run_and_verify_svn(None, [], 2568 'propdel', 'foo', 2569 beta_path) 2570 svntest.actions.run_and_verify_svn(None, [], 2571 'propset', 'foo', 'foo_val2', 2572 E_path) 2573 expected_output = svntest.wc.State(wc_dir, { 2574 'A/B/E' : Item(verb='Sending'), 2575 'A/B/E/alpha' : Item(verb='Sending'), 2576 'A/B/E/beta' : Item(verb='Sending'), 2577 }) 2578 svntest.actions.run_and_verify_commit(wc_dir, 2579 expected_output, None) 2580 svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir) 2581 2582 # Make local propchanges to E, alpha and beta in the branch. 2583 alpha_path2 = sbox.ospath('A/B2/E/alpha') 2584 beta_path2 = sbox.ospath('A/B2/E/beta') 2585 E_path2 = sbox.ospath('A/B2/E') 2586 2587 svntest.actions.run_and_verify_svn(None, [], 2588 'propset', 'foo', 'branchval', 2589 alpha_path2, beta_path2) 2590 svntest.actions.run_and_verify_svn(None, [], 2591 'propset', 'foo', 'branchval', 2592 E_path2) 2593 2594 # Now merge the recent B change to the branch. Because we already 2595 # have local propmods, we should get property conflicts. 2596 B2_path = sbox.ospath('A/B2') 2597 2598 expected_output = wc.State(B2_path, { 2599 'E' : Item(status=' C'), 2600 'E/alpha' : Item(status=' C'), 2601 'E/beta' : Item(status=' C'), 2602 }) 2603 expected_mergeinfo_output = wc.State(B2_path, { 2604 '' : Item(status=' U'), 2605 }) 2606 expected_elision_output = wc.State(B2_path, { 2607 }) 2608 expected_disk = wc.State('', { 2609 '' : Item(props={SVN_PROP_MERGEINFO : "/A/B:4"}), 2610 'E' : Item(), 2611 'E/alpha' : Item("This is the file 'alpha'.\n"), 2612 'E/beta' : Item("This is the file 'beta'.\n"), 2613 'F' : Item(), 2614 'lambda' : Item("This is the file 'lambda'.\n"), 2615 }) 2616 expected_disk.tweak('E', 'E/alpha', 'E/beta', 2617 props={'foo' : 'branchval'}) # local mods still present 2618 2619 expected_status = wc.State(B2_path, { 2620 '' : Item(status=' M'), 2621 'E' : Item(status=' C'), 2622 'E/alpha' : Item(status=' C'), 2623 'E/beta' : Item(status=' C'), 2624 'F' : Item(status=' '), 2625 'lambda' : Item(status=' '), 2626 }) 2627 expected_status.tweak(wc_rev=4) 2628 2629 expected_skip = wc.State('', { }) 2630 2631 # should have 3 'prej' files left behind, describing prop conflicts: 2632 extra_files = ['alpha.*\.prej', 'beta.*\.prej', 'dir_conflicts.*\.prej'] 2633 2634 svntest.actions.run_and_verify_merge(B2_path, '3', '4', B_url, None, 2635 expected_output, 2636 expected_mergeinfo_output, 2637 expected_elision_output, 2638 expected_disk, 2639 expected_status, 2640 expected_skip, 2641 [], True, False, 2642 extra_files=extra_files) 2643 2644#---------------------------------------------------------------------- 2645# Test for issue 2035, whereby 'svn merge' wouldn't always mark 2646# property conflicts when it should. 2647@Issue(2035) 2648@SkipUnless(server_has_mergeinfo) 2649def property_merge_from_branch(sbox): 2650 "property merge conflict even without local mods" 2651 2652 sbox.build() 2653 wc_dir = sbox.wc_dir 2654 2655 # Add a property to a file and a directory, commit as r2. 2656 alpha_path = sbox.ospath('A/B/E/alpha') 2657 E_path = sbox.ospath('A/B/E') 2658 2659 svntest.actions.run_and_verify_svn(None, [], 2660 'propset', 'foo', 'foo_val', 2661 alpha_path) 2662 svntest.actions.run_and_verify_svn(None, [], 2663 'propset', 'foo', 'foo_val', 2664 E_path) 2665 2666 expected_output = svntest.wc.State(wc_dir, { 2667 'A/B/E' : Item(verb='Sending'), 2668 'A/B/E/alpha' : Item(verb='Sending'), 2669 }) 2670 expected_status = svntest.actions.get_virginal_state(wc_dir, 1) 2671 expected_status.tweak('A/B/E', 'A/B/E/alpha', wc_rev=2, status=' ') 2672 svntest.actions.run_and_verify_commit(wc_dir, 2673 expected_output, expected_status) 2674 svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir) 2675 2676 # Copy B to B2 as rev 3 (making a branch) 2677 B_url = sbox.repo_url + '/A/B' 2678 B2_url = sbox.repo_url + '/A/B2' 2679 2680 svntest.actions.run_and_verify_svn(None, [], 2681 'copy', '-m', 'copy B to B2', 2682 B_url, B2_url) 2683 svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir) 2684 2685 # Change the properties underneath B again, and commit as r4 2686 svntest.actions.run_and_verify_svn(None, [], 2687 'propset', 'foo', 'foo_val2', 2688 alpha_path) 2689 svntest.actions.run_and_verify_svn(None, [], 2690 'propset', 'foo', 'foo_val2', 2691 E_path) 2692 expected_output = svntest.wc.State(wc_dir, { 2693 'A/B/E' : Item(verb='Sending'), 2694 'A/B/E/alpha' : Item(verb='Sending'), 2695 }) 2696 svntest.actions.run_and_verify_commit(wc_dir, 2697 expected_output, None) 2698 svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir) 2699 2700 # Make different propchanges changes to the B2 branch and commit as r5. 2701 alpha_path2 = sbox.ospath('A/B2/E/alpha') 2702 E_path2 = sbox.ospath('A/B2/E') 2703 2704 svntest.actions.run_and_verify_svn(None, [], 2705 'propset', 'foo', 'branchval', 2706 alpha_path2) 2707 svntest.actions.run_and_verify_svn(None, [], 2708 'propset', 'foo', 'branchval', 2709 E_path2) 2710 expected_output = svntest.wc.State(wc_dir, { 2711 'A/B2/E' : Item(verb='Sending'), 2712 'A/B2/E/alpha' : Item(verb='Sending'), 2713 }) 2714 svntest.actions.run_and_verify_commit(wc_dir, 2715 expected_output, None) 2716 svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir) 2717 2718 # Now merge the recent B change to the branch. There are no local 2719 # mods anywhere, but we should still get property conflicts anyway! 2720 B2_path = sbox.ospath('A/B2') 2721 2722 expected_output = wc.State(B2_path, { 2723 'E' : Item(status=' C'), 2724 'E/alpha' : Item(status=' C'), 2725 }) 2726 expected_mergeinfo_output = wc.State(B2_path, { 2727 '' : Item(status=' U'), 2728 }) 2729 expected_elision_output = wc.State(B2_path, { 2730 }) 2731 expected_disk = wc.State('', { 2732 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B:4'}), 2733 'E' : Item(), 2734 'E/alpha' : Item("This is the file 'alpha'.\n"), 2735 'E/beta' : Item("This is the file 'beta'.\n"), 2736 'F' : Item(), 2737 'lambda' : Item("This is the file 'lambda'.\n"), 2738 }) 2739 expected_disk.tweak('E', 'E/alpha', 2740 props={'foo' : 'branchval'}) 2741 2742 expected_status = wc.State(B2_path, { 2743 '' : Item(status=' M'), 2744 'E' : Item(status=' C'), 2745 'E/alpha' : Item(status=' C'), 2746 'E/beta' : Item(status=' '), 2747 'F' : Item(status=' '), 2748 'lambda' : Item(status=' '), 2749 }) 2750 expected_status.tweak(wc_rev=5) 2751 2752 expected_skip = wc.State('', { }) 2753 2754 # should have 2 'prej' files left behind, describing prop conflicts: 2755 extra_files = ['alpha.*\.prej', 'dir_conflicts.*\.prej'] 2756 2757 svntest.actions.run_and_verify_merge(B2_path, '3', '4', B_url, None, 2758 expected_output, 2759 expected_mergeinfo_output, 2760 expected_elision_output, 2761 expected_disk, 2762 expected_status, 2763 expected_skip, 2764 [], True, False, 2765 extra_files=extra_files) 2766 2767#---------------------------------------------------------------------- 2768# Another test for issue 2035, whereby sometimes 'svn merge' marked 2769# property conflicts when it shouldn't! 2770@Issue(2035) 2771def property_merge_undo_redo(sbox): 2772 "undo, then redo a property merge" 2773 2774 sbox.build() 2775 wc_dir = sbox.wc_dir 2776 2777 # Add a property to a file, commit as r2. 2778 alpha_path = sbox.ospath('A/B/E/alpha') 2779 svntest.actions.run_and_verify_svn(None, [], 2780 'propset', 'foo', 'foo_val', 2781 alpha_path) 2782 2783 expected_output = svntest.wc.State(wc_dir, { 2784 'A/B/E/alpha' : Item(verb='Sending'), 2785 }) 2786 2787 expected_status = svntest.actions.get_virginal_state(wc_dir, 1) 2788 expected_status.tweak('A/B/E/alpha', wc_rev=2, status=' ') 2789 svntest.actions.run_and_verify_commit(wc_dir, 2790 expected_output, expected_status) 2791 svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir) 2792 2793 # Use 'svn merge' to undo the commit. ('svn merge -r2:1') 2794 # Result should be a single local-prop-mod. 2795 expected_output = wc.State(wc_dir, {'A/B/E/alpha' : Item(status=' U'), }) 2796 expected_mergeinfo_output = wc.State(wc_dir, { 2797 '' : Item(status=' U'), 2798 }) 2799 expected_elision_output = wc.State(wc_dir, { 2800 '' : Item(status=' U'), 2801 }) 2802 expected_disk = svntest.main.greek_state.copy() 2803 2804 expected_status = svntest.actions.get_virginal_state(wc_dir, 2) 2805 expected_status.tweak('A/B/E/alpha', status=' M') 2806 2807 expected_skip = wc.State('', { }) 2808 2809 svntest.actions.run_and_verify_merge(wc_dir, '2', '1', 2810 sbox.repo_url, None, 2811 expected_output, 2812 expected_mergeinfo_output, 2813 expected_elision_output, 2814 expected_disk, 2815 expected_status, 2816 expected_skip, 2817 [], True, False) 2818 2819 # Change mind, re-apply the change ('svn merge -r1:2'). 2820 # This should merge cleanly into existing prop-mod, status shows nothing. 2821 expected_output = wc.State(wc_dir, {'A/B/E/alpha' : Item(status=' C'), }) 2822 expected_mergeinfo_output = wc.State(wc_dir, {}) 2823 expected_elision_output = wc.State(wc_dir, {}) 2824 expected_elision_output = wc.State(wc_dir, {}) 2825 expected_disk = svntest.main.greek_state.copy() 2826 expected_disk.add({'A/B/E/alpha.prej' 2827 : Item("Trying to add new property 'foo'\n" 2828 + "but the property has been locally deleted.\n" 2829 + "Incoming property value:\nfoo_val\n")}) 2830 2831 expected_status = svntest.actions.get_virginal_state(wc_dir, 2) 2832 expected_status.tweak('A/B/E/alpha', status=' C') 2833 2834 expected_skip = wc.State('', { }) 2835 2836 # Re-merge r1. We have to use --ignore-ancestry here. Otherwise 2837 # the merge logic will claim we already have this change (because it 2838 # was unable to record the previous undoing merge). 2839 svntest.actions.run_and_verify_merge(wc_dir, '1', '2', 2840 sbox.repo_url, None, 2841 expected_output, 2842 expected_mergeinfo_output, 2843 expected_elision_output, 2844 expected_disk, 2845 expected_status, 2846 expected_skip, 2847 [], True, False, 2848 '--ignore-ancestry', wc_dir) 2849 2850 2851 2852#---------------------------------------------------------------------- 2853@SkipUnless(server_has_mergeinfo) 2854def cherry_pick_text_conflict(sbox): 2855 "cherry-pick a dependent change, get conflict" 2856 2857 sbox.build() 2858 wc_dir = sbox.wc_dir 2859 2860 A_path = sbox.ospath('A') 2861 A_url = sbox.repo_url + '/A' 2862 mu_path = os.path.join(A_path, 'mu') 2863 branch_A_url = sbox.repo_url + '/copy-of-A' 2864 branch_mu_path = sbox.ospath('copy-of-A/mu') 2865 2866 # Create a branch of A. 2867 svntest.actions.run_and_verify_svn(None, [], 'cp', 2868 A_url, branch_A_url, 2869 '-m', "Creating copy-of-A") 2870 2871 # Update to get the branch. 2872 svntest.actions.run_and_verify_svn(None, [], 2873 'update', wc_dir) 2874 2875 # Change mu's text on the branch, producing r3 through r6. 2876 for rev in range(3, 7): 2877 svntest.main.file_append(branch_mu_path, ("r%d\n" % rev) * 3) 2878 svntest.actions.run_and_verify_svn(None, [], 2879 'ci', '-m', 2880 'Add lines to mu in r%d.' % rev, wc_dir) 2881 2882 # Mark r5 as merged into trunk, to create disparate revision ranges 2883 # which need to be merged. 2884 svntest.actions.run_and_verify_svn( 2885 expected_merge_output([[5]], 2886 [' U ' + A_path + '\n']), 2887 [], 'merge', '-c5', '--record-only', 2888 branch_A_url, A_path) 2889 2890 2891 # Try to merge r4:6 into trunk, without r3. It should fail. 2892 expected_output = wc.State(A_path, { 2893 'mu' : Item(status='C '), 2894 }) 2895 expected_mergeinfo_output = wc.State(A_path, { 2896 '' : Item(status=' G') 2897 }) 2898 expected_elision_output = wc.State(A_path, { 2899 }) 2900 expected_disk = wc.State('', { 2901 'mu' : Item("This is the file 'mu'.\n" 2902 + make_conflict_marker_text('', "r3\n" * 3 + "r4\n" * 3, 3, 4, 2903 old_text='r3\n' * 3)), 2904 'B' : Item(), 2905 'B/lambda' : Item("This is the file 'lambda'.\n"), 2906 'B/E' : Item(), 2907 'B/E/alpha' : Item("This is the file 'alpha'.\n"), 2908 'B/E/beta' : Item("This is the file 'beta'.\n"), 2909 'B/F' : Item(), 2910 'C' : Item(), 2911 'D' : Item(), 2912 'D/gamma' : Item("This is the file 'gamma'.\n"), 2913 'D/H' : Item(), 2914 'D/H/chi' : Item("This is the file 'chi'.\n"), 2915 'D/H/psi' : Item("This is the file 'psi'.\n"), 2916 'D/H/omega' : Item("This is the file 'omega'.\n"), 2917 'D/G' : Item(), 2918 'D/G/pi' : Item("This is the file 'pi'.\n"), 2919 'D/G/rho' : Item("This is the file 'rho'.\n"), 2920 'D/G/tau' : Item("This is the file 'tau'.\n"), 2921 }) 2922 expected_status = wc.State(A_path, { 2923 '' : Item(status=' M'), 2924 'mu' : Item(status='C '), 2925 'B' : Item(status=' '), 2926 'B/lambda' : Item(status=' '), 2927 'B/E' : Item(status=' '), 2928 'B/E/alpha' : Item(status=' '), 2929 'B/E/beta' : Item(status=' '), 2930 'B/F' : Item(status=' '), 2931 'C' : Item(status=' '), 2932 'D' : Item(status=' '), 2933 'D/gamma' : Item(status=' '), 2934 'D/H' : Item(status=' '), 2935 'D/H/chi' : Item(status=' '), 2936 'D/H/psi' : Item(status=' '), 2937 'D/H/omega' : Item(status=' '), 2938 'D/G' : Item(status=' '), 2939 'D/G/pi' : Item(status=' '), 2940 'D/G/rho' : Item(status=' '), 2941 'D/G/tau' : Item(status=' '), 2942 }) 2943 expected_status.tweak(wc_rev=2) 2944 expected_skip = wc.State('', { }) 2945 expected_error = ".*conflicts were produced while merging r3:4.*" 2946 svntest.actions.run_and_verify_merge(A_path, '3', '6', branch_A_url, None, 2947 expected_output, 2948 expected_mergeinfo_output, 2949 expected_elision_output, 2950 expected_disk, 2951 expected_status, 2952 expected_skip, 2953 expected_error, 2954 extra_files= 2955 ["mu\.working", 2956 "mu\.merge-right\.r4", 2957 "mu\.merge-left\.r3"]) 2958 2959#---------------------------------------------------------------------- 2960# Test for issue 2135 2961@Issue(2135) 2962def merge_file_replace(sbox): 2963 "merge a replacement of a file" 2964 2965 sbox.build() 2966 wc_dir = sbox.wc_dir 2967 2968 # File scheduled for deletion 2969 rho_path = sbox.ospath('A/D/G/rho') 2970 svntest.actions.run_and_verify_svn(None, [], 'rm', rho_path) 2971 2972 expected_status = svntest.actions.get_virginal_state(wc_dir, 1) 2973 expected_status.tweak('A/D/G/rho', status='D ') 2974 svntest.actions.run_and_verify_status(wc_dir, expected_status) 2975 2976 expected_output = svntest.wc.State(wc_dir, { 2977 'A/D/G/rho': Item(verb='Deleting'), 2978 }) 2979 2980 expected_status.remove('A/D/G/rho') 2981 2982 # Commit rev 2 2983 svntest.actions.run_and_verify_commit(wc_dir, 2984 expected_output, 2985 expected_status) 2986 # Create and add a new file. 2987 svntest.main.file_write(rho_path, "new rho\n") 2988 svntest.actions.run_and_verify_svn(None, [], 'add', rho_path) 2989 2990 # Commit revsion 3 2991 expected_status.add({ 2992 'A/D/G/rho' : Item(status='A ', wc_rev='0') 2993 }) 2994 svntest.actions.run_and_verify_status(wc_dir, expected_status) 2995 expected_output = svntest.wc.State(wc_dir, { 2996 'A/D/G/rho': Item(verb='Adding'), 2997 }) 2998 2999 svntest.actions.run_and_verify_commit(wc_dir, 3000 expected_output, 3001 None) 3002 3003 # Update working copy 3004 expected_output = svntest.wc.State(wc_dir, {}) 3005 expected_disk = svntest.main.greek_state.copy() 3006 expected_disk.tweak('A/D/G/rho', contents='new rho\n' ) 3007 expected_status.tweak(wc_rev='3') 3008 expected_status.tweak('A/D/G/rho', status=' ') 3009 3010 svntest.actions.run_and_verify_update(wc_dir, 3011 expected_output, 3012 expected_disk, 3013 expected_status) 3014 3015 # merge changes from r3:1 3016 expected_output = svntest.wc.State(wc_dir, { 3017 'A/D/G/rho': Item(status='R ') 3018 }) 3019 expected_mergeinfo_output = svntest.wc.State(wc_dir, { 3020 '' : Item(status=' U') 3021 }) 3022 expected_elision_output = wc.State(wc_dir, { 3023 '' : Item(status=' U') 3024 }) 3025 expected_status.tweak('A/D/G/rho', status='R ', copied='+', wc_rev='-') 3026 expected_skip = wc.State(wc_dir, { }) 3027 expected_disk.tweak('A/D/G/rho', contents="This is the file 'rho'.\n") 3028 svntest.actions.run_and_verify_merge(wc_dir, '3', '1', 3029 sbox.repo_url, None, 3030 expected_output, 3031 expected_mergeinfo_output, 3032 expected_elision_output, 3033 expected_disk, 3034 expected_status, 3035 expected_skip) 3036 3037 # Now commit merged wc 3038 expected_output = svntest.wc.State(wc_dir, { 3039 'A/D/G/rho': Item(verb='Replacing'), 3040 }) 3041 expected_status.tweak('A/D/G/rho', status=' ', copied=None, wc_rev='4') 3042 svntest.actions.run_and_verify_commit(wc_dir, 3043 expected_output, 3044 expected_status) 3045 3046#---------------------------------------------------------------------- 3047# Test for issue 2522 3048# Same as merge_file_replace, but without update before merge. 3049@Issue(2522) 3050def merge_file_replace_to_mixed_rev_wc(sbox): 3051 "merge a replacement of a file to mixed rev wc" 3052 3053 sbox.build() 3054 wc_dir = sbox.wc_dir 3055 3056 # File scheduled for deletion 3057 rho_path = sbox.ospath('A/D/G/rho') 3058 svntest.actions.run_and_verify_svn(None, [], 'rm', rho_path) 3059 3060 expected_status = svntest.actions.get_virginal_state(wc_dir, 1) 3061 expected_status.tweak('A/D/G/rho', status='D ') 3062 svntest.actions.run_and_verify_status(wc_dir, expected_status) 3063 3064 expected_output = svntest.wc.State(wc_dir, { 3065 'A/D/G/rho': Item(verb='Deleting'), 3066 }) 3067 3068 expected_status.remove('A/D/G/rho') 3069 3070 # Commit rev 2 3071 svntest.actions.run_and_verify_commit(wc_dir, 3072 expected_output, 3073 expected_status) 3074 3075 # Update working copy 3076 expected_disk = svntest.main.greek_state.copy() 3077 expected_disk.remove('A/D/G/rho' ) 3078 expected_output = svntest.wc.State(wc_dir, {}) 3079 expected_status.tweak(wc_rev='2') 3080 3081 svntest.actions.run_and_verify_update(wc_dir, 3082 expected_output, 3083 expected_disk, 3084 expected_status) 3085 3086 # Create and add a new file. 3087 svntest.main.file_write(rho_path, "new rho\n") 3088 svntest.actions.run_and_verify_svn(None, [], 'add', rho_path) 3089 3090 # Commit revsion 3 3091 expected_status.add({ 3092 'A/D/G/rho' : Item(status='A ', wc_rev='0') 3093 }) 3094 svntest.actions.run_and_verify_status(wc_dir, expected_status) 3095 expected_output = svntest.wc.State(wc_dir, { 3096 'A/D/G/rho': Item(verb='Adding'), 3097 }) 3098 3099 expected_disk.add({'A/D/G/rho' : Item(contents='new rho\n')} ) 3100 expected_status.tweak(wc_rev='2') 3101 expected_status.tweak('A/D/G/rho', status=' ', wc_rev='3') 3102 3103 svntest.actions.run_and_verify_commit(wc_dir, 3104 expected_output, 3105 expected_status) 3106 3107 # merge changes from r3:1... 3108 # 3109 # ...but first: 3110 # 3111 # Since "." is at revision 2, r3 is not part of "."'s implicit mergeinfo. 3112 # Merge tracking permits only reverse merges from explicit or implicit 3113 # mergeinfo, so only r2 would be reverse merged if we left the WC as is. 3114 # Normally we'd simply update the whole working copy, but since that would 3115 # defeat the purpose of this test (see the comment below), instead we'll 3116 # update only "." using --depth empty. This preserves the intent of the 3117 # original mixed-rev test for this issue, but allows the merge tracking 3118 # logic to consider r3 as valid for reverse merging. 3119 svntest.actions.run_and_verify_svn(None, [], 3120 'up', '--depth', 'empty', wc_dir) 3121 expected_status.tweak('', wc_rev=3) 3122 expected_output = svntest.wc.State(wc_dir, { 3123 'A/D/G/rho': Item(status='R ') 3124 }) 3125 expected_mergeinfo_output = svntest.wc.State(wc_dir, { 3126 '' : Item(status=' U') 3127 }) 3128 expected_elision_output = wc.State(wc_dir, { 3129 '' : Item(status=' U') 3130 }) 3131 expected_status.tweak('A/D/G/rho', status='R ', copied='+', wc_rev='-') 3132 expected_skip = wc.State(wc_dir, { }) 3133 expected_disk.tweak('A/D/G/rho', contents="This is the file 'rho'.\n") 3134 svntest.actions.run_and_verify_merge(wc_dir, '3', '1', 3135 sbox.repo_url, None, 3136 expected_output, 3137 expected_mergeinfo_output, 3138 expected_elision_output, 3139 expected_disk, 3140 expected_status, 3141 expected_skip, 3142 [], 3143 True, False, '--allow-mixed-revisions', 3144 wc_dir) 3145 3146 # When issue #2522 was filed, svn used to break the WC if we didn't 3147 # update here. But nowadays, this no longer happens, so the separate 3148 # update step which was done here originally has been removed. 3149 3150 # Now commit merged wc 3151 expected_output = svntest.wc.State(wc_dir, { 3152 'A/D/G/rho': Item(verb='Replacing'), 3153 }) 3154 expected_status.tweak('A/D/G/rho', status=' ', copied=None, wc_rev='4') 3155 svntest.actions.run_and_verify_commit(wc_dir, 3156 expected_output, 3157 expected_status) 3158 3159#---------------------------------------------------------------------- 3160# use -x -w option for ignoring whitespace during merge 3161@SkipUnless(server_has_mergeinfo) 3162def merge_ignore_whitespace(sbox): 3163 "ignore whitespace when merging" 3164 3165 sbox.build() 3166 wc_dir = sbox.wc_dir 3167 3168 # commit base version of iota 3169 file_name = "iota" 3170 file_path = os.path.join(wc_dir, file_name) 3171 file_url = sbox.repo_url + '/iota' 3172 3173 svntest.main.file_write(file_path, 3174 "Aa\n" 3175 "Bb\n" 3176 "Cc\n") 3177 expected_output = svntest.wc.State(wc_dir, { 3178 'iota' : Item(verb='Sending'), 3179 }) 3180 svntest.actions.run_and_verify_commit(wc_dir, expected_output, None) 3181 3182 # change the file, mostly whitespace changes + an extra line 3183 svntest.main.file_write(file_path, "A a\nBb \n Cc\nNew line in iota\n") 3184 expected_output = wc.State(wc_dir, { file_name : Item(verb='Sending'), }) 3185 expected_status = svntest.actions.get_virginal_state(wc_dir, 1) 3186 expected_status.tweak(file_name, wc_rev=3) 3187 svntest.actions.run_and_verify_commit(wc_dir, 3188 expected_output, 3189 expected_status) 3190 3191 # Backdate iota to revision 2, so we can merge in the rev 3 changes. 3192 svntest.actions.run_and_verify_svn(None, [], 3193 'up', '-r', '2', file_path) 3194 # Make some local whitespace changes, these should not conflict 3195 # with the remote whitespace changes as both will be ignored. 3196 svntest.main.file_write(file_path, " Aa\nB b\nC c\n") 3197 3198 # Lines changed only by whitespace - both in local or remote - 3199 # should be ignored 3200 expected_output = wc.State(sbox.wc_dir, { file_name : Item(status='G ') }) 3201 expected_mergeinfo_output = wc.State(sbox.wc_dir, { 3202 '' : Item(status=' U'), 3203 }) 3204 expected_elision_output = wc.State(sbox.wc_dir, { 3205 }) 3206 expected_disk = svntest.main.greek_state.copy() 3207 expected_disk.tweak(file_name, 3208 contents=" Aa\n" 3209 "B b\n" 3210 "C c\n" 3211 "New line in iota\n") 3212 expected_status = svntest.actions.get_virginal_state(sbox.wc_dir, 1) 3213 expected_status.tweak('', status=' M', wc_rev=1) 3214 expected_status.tweak(file_name, status='M ', wc_rev=2) 3215 expected_skip = wc.State('', { }) 3216 3217 svntest.actions.run_and_verify_merge(sbox.wc_dir, '2', '3', 3218 sbox.repo_url, None, 3219 expected_output, 3220 expected_mergeinfo_output, 3221 expected_elision_output, 3222 expected_disk, 3223 expected_status, 3224 expected_skip, 3225 [], False, False, 3226 '--allow-mixed-revisions', 3227 '-x', '-w', wc_dir) 3228 3229#---------------------------------------------------------------------- 3230# use -x --ignore-eol-style option for ignoring eolstyle during merge 3231@SkipUnless(server_has_mergeinfo) 3232def merge_ignore_eolstyle(sbox): 3233 "ignore eolstyle when merging" 3234 3235 sbox.build() 3236 wc_dir = sbox.wc_dir 3237 3238 # commit base version of iota 3239 file_name = "iota" 3240 file_path = os.path.join(wc_dir, file_name) 3241 file_url = sbox.repo_url + '/iota' 3242 3243 svntest.main.file_write(file_path, 3244 "Aa\r\n" 3245 "Bb\r\n" 3246 "Cc\r\n", 3247 "wb") 3248 expected_output = svntest.wc.State(wc_dir, { 3249 'iota' : Item(verb='Sending'), 3250 }) 3251 svntest.actions.run_and_verify_commit(wc_dir, expected_output, None) 3252 3253 # change the file, mostly eol changes + an extra line 3254 svntest.main.file_write(file_path, 3255 "Aa\r" 3256 "Bb\n" 3257 "Cc\r" 3258 "New line in iota\n", 3259 "wb") 3260 expected_output = wc.State(wc_dir, { file_name : Item(verb='Sending'), }) 3261 expected_status = svntest.actions.get_virginal_state(wc_dir, 1) 3262 expected_status.tweak(file_name, wc_rev=3) 3263 svntest.actions.run_and_verify_commit(wc_dir, 3264 expected_output, 3265 expected_status) 3266 3267 # Backdate iota to revision 2, so we can merge in the rev 3 changes. 3268 svntest.actions.run_and_verify_svn(None, [], 3269 'up', '-r', '2', file_path) 3270 # Make some local eol changes, these should not conflict 3271 # with the remote eol changes as both will be ignored. 3272 svntest.main.file_write(file_path, 3273 "Aa\n" 3274 "Bb\r" 3275 "Cc\n", 3276 "wb") 3277 3278 # Lines changed only by eolstyle - both in local or remote - 3279 # should be ignored 3280 expected_output = wc.State(sbox.wc_dir, { file_name : Item(status='G ') }) 3281 expected_mergeinfo_output = wc.State(sbox.wc_dir, { 3282 '' : Item(status=' U'), 3283 }) 3284 expected_elision_output = wc.State(sbox.wc_dir, { 3285 }) 3286 expected_disk = svntest.main.greek_state.copy() 3287 expected_disk.tweak(file_name, 3288 contents="Aa\n" 3289 "Bb\r" 3290 "Cc\n" 3291 "New line in iota\n") 3292 expected_status = svntest.actions.get_virginal_state(sbox.wc_dir, 1) 3293 expected_status.tweak('', status=' M') 3294 expected_status.tweak(file_name, status='M ', wc_rev=2) 3295 expected_skip = wc.State('', { }) 3296 3297 svntest.actions.run_and_verify_merge2(sbox.wc_dir, '2', '3', 3298 sbox.repo_url, None, 3299 expected_output, 3300 expected_mergeinfo_output, 3301 expected_elision_output, 3302 expected_disk, 3303 expected_status, 3304 expected_skip, 3305 [], False, False, True, 3306 '--allow-mixed-revisions', 3307 '-x', '--ignore-eol-style', wc_dir) 3308 3309#---------------------------------------------------------------------- 3310# eol-style handling during merge with conflicts, scenario 1: 3311# when a merge creates a conflict on a file, make sure the file and files 3312# r<left>, r<right> and .mine are in the eol-style defined for that file. 3313# 3314# This test for 'svn update' can be found in update_tests.py as 3315# conflict_markers_matching_eol. 3316@SkipUnless(server_has_mergeinfo) 3317def merge_conflict_markers_matching_eol(sbox): 3318 "conflict markers should match the file's eol style" 3319 3320 sbox.build() 3321 wc_dir = sbox.wc_dir 3322 filecount = 1 3323 3324 mu_path = sbox.ospath('A/mu') 3325 3326 if os.name == 'nt': 3327 native_nl = '\r\n' 3328 else: 3329 native_nl = '\n' 3330 crlf = '\r\n' 3331 3332 # Checkout a second working copy 3333 wc_backup = sbox.add_wc_path('backup') 3334 svntest.actions.run_and_verify_svn(None, [], 'checkout', 3335 sbox.repo_url, wc_backup) 3336 3337 # set starting revision 3338 cur_rev = 1 3339 3340 expected_disk = svntest.main.greek_state.copy() 3341 expected_status = svntest.actions.get_virginal_state(wc_dir, cur_rev) 3342 expected_backup_status = svntest.actions.get_virginal_state(wc_backup, 3343 cur_rev) 3344 3345 path_backup = os.path.join(wc_backup, 'A', 'mu') 3346 3347 # do the test for each eol-style 3348 for eol, eolchar in zip(['CRLF', 'CR','native', 'LF'], 3349 [crlf, '\015', native_nl, '\012']): 3350 # rewrite file mu and set the eol-style property. 3351 svntest.main.file_write(mu_path, "This is the file 'mu'."+ eolchar, 'wb') 3352 svntest.main.run_svn(None, 'propset', 'svn:eol-style', eol, mu_path) 3353 3354 expected_disk.add({ 3355 'A/mu' : Item("This is the file 'mu'." + eolchar) 3356 }) 3357 expected_output = svntest.wc.State(wc_dir, { 3358 'A/mu' : Item(verb='Sending'), 3359 }) 3360 expected_status.tweak(wc_rev = cur_rev) 3361 expected_status.add({ 3362 'A/mu' : Item(status=' ', wc_rev = cur_rev + 1), 3363 }) 3364 3365 # Commit the original change and note the 'base' revision number 3366 svntest.actions.run_and_verify_commit(wc_dir, expected_output, 3367 expected_status) 3368 cur_rev = cur_rev + 1 3369 base_rev = cur_rev 3370 3371 svntest.main.run_svn(None, 'update', wc_backup) 3372 3373 # Make a local mod to mu 3374 svntest.main.file_append_binary(mu_path, 3375 'Original appended text for mu' + eolchar) 3376 3377 # Commit the original change and note the 'theirs' revision number 3378 svntest.main.run_svn(None, 'commit', '-m', 'test log', wc_dir) 3379 cur_rev = cur_rev + 1 3380 theirs_rev = cur_rev 3381 3382 # Make a local mod to mu, will conflict with the previous change 3383 svntest.main.file_append_binary(path_backup, 3384 'Conflicting appended text for mu' 3385 + eolchar) 3386 3387 # Create expected output tree for an update of the wc_backup. 3388 expected_backup_output = svntest.wc.State(wc_backup, { 3389 'A/mu' : Item(status='C '), 3390 }) 3391 3392 # Create expected disk tree for the update. 3393 expected_backup_disk = expected_disk.copy() 3394 3395 # verify content of resulting conflicted file 3396 expected_backup_disk.add({ 3397 'A/mu' : Item(contents= "This is the file 'mu'." + eolchar + 3398 "<<<<<<< .working" + eolchar + 3399 "Conflicting appended text for mu" + eolchar + 3400 "||||||| .merge-left.r" + str(cur_rev - 1) + eolchar + 3401 "=======" + eolchar + 3402 "Original appended text for mu" + eolchar + 3403 ">>>>>>> .merge-right.r" + str(cur_rev) + eolchar), 3404 }) 3405 # verify content of base(left) file 3406 expected_backup_disk.add({ 3407 'A/mu.merge-left.r' + str(base_rev) : 3408 Item(contents= "This is the file 'mu'." + eolchar) 3409 }) 3410 # verify content of theirs(right) file 3411 expected_backup_disk.add({ 3412 'A/mu.merge-right.r' + str(theirs_rev) : 3413 Item(contents= "This is the file 'mu'." + eolchar + 3414 "Original appended text for mu" + eolchar) 3415 }) 3416 # verify content of mine file 3417 expected_backup_disk.add({ 3418 'A/mu.working' : Item(contents= "This is the file 'mu'." + 3419 eolchar + 3420 "Conflicting appended text for mu" + eolchar) 3421 }) 3422 3423 # Create expected status tree for the update. 3424 expected_backup_status.add({ 3425 'A/mu' : Item(status=' ', wc_rev=cur_rev), 3426 }) 3427 expected_backup_status.tweak('A/mu', status='C ') 3428 expected_backup_status.tweak(wc_rev = cur_rev - 1) 3429 expected_backup_status.tweak('', status= ' M') 3430 expected_mergeinfo_output = wc.State(wc_backup, { 3431 '' : Item(status=' U'), 3432 }) 3433 expected_elision_output = wc.State(wc_backup, { 3434 }) 3435 expected_backup_skip = wc.State('', { }) 3436 3437 svntest.actions.run_and_verify_merge2(wc_backup, cur_rev - 1, cur_rev, 3438 sbox.repo_url, None, 3439 expected_backup_output, 3440 expected_mergeinfo_output, 3441 expected_elision_output, 3442 expected_backup_disk, 3443 expected_backup_status, 3444 expected_backup_skip, 3445 keep_eol_style=True) 3446 3447 # cleanup for next run 3448 svntest.main.run_svn(None, 'revert', '-R', wc_backup) 3449 svntest.main.run_svn(None, 'update', wc_dir) 3450 3451#---------------------------------------------------------------------- 3452# eol-style handling during merge, scenario 2: 3453# if part of that merge is a propchange (add, change, delete) of 3454# svn:eol-style, make sure the correct eol-style is applied before 3455# calculating the merge (and conflicts if any) 3456# 3457# This test for 'svn update' can be found in update_tests.py as 3458# update_eolstyle_handling. 3459@SkipUnless(server_has_mergeinfo) 3460def merge_eolstyle_handling(sbox): 3461 "handle eol-style propchange during merge" 3462 3463 sbox.build() 3464 wc_dir = sbox.wc_dir 3465 3466 mu_path = sbox.ospath('A/mu') 3467 3468 crlf = '\r\n' 3469 3470 # Checkout a second working copy 3471 wc_backup = sbox.add_wc_path('backup') 3472 svntest.actions.run_and_verify_svn(None, [], 'checkout', 3473 sbox.repo_url, wc_backup) 3474 path_backup = os.path.join(wc_backup, 'A', 'mu') 3475 3476 # Test 1: add the eol-style property and commit, change mu in the second 3477 # working copy and merge the last revision; there should be no conflict! 3478 svntest.main.run_svn(None, 'propset', 'svn:eol-style', "CRLF", mu_path) 3479 svntest.main.run_svn(None, 3480 'commit', '-m', 'set eol-style property', wc_dir) 3481 3482 svntest.main.file_append_binary(path_backup, 'Added new line of text.\012') 3483 3484 expected_backup_disk = svntest.main.greek_state.copy() 3485 expected_backup_disk.tweak( 3486 'A/mu', contents= "This is the file 'mu'." + crlf + 3487 "Added new line of text." + crlf) 3488 expected_backup_output = svntest.wc.State(wc_backup, { 3489 'A/mu' : Item(status='GU'), 3490 }) 3491 expected_mergeinfo_output = svntest.wc.State(wc_backup, { 3492 '' : Item(status=' U'), 3493 }) 3494 expected_elision_output = wc.State(wc_backup, { 3495 }) 3496 expected_backup_status = svntest.actions.get_virginal_state(wc_backup, 1) 3497 expected_backup_status.tweak('', status=' M') 3498 expected_backup_status.tweak('A/mu', status='MM') 3499 3500 expected_backup_skip = wc.State('', { }) 3501 3502 svntest.actions.run_and_verify_merge2(wc_backup, '1', '2', sbox.repo_url, 3503 None, 3504 expected_backup_output, 3505 expected_mergeinfo_output, 3506 expected_elision_output, 3507 expected_backup_disk, 3508 expected_backup_status, 3509 expected_backup_skip, 3510 keep_eol_style=True) 3511 3512 # Test 2: now change the eol-style property to another value and commit, 3513 # merge this revision in the still changed mu in the second working copy; 3514 # there should be no conflict! 3515 svntest.main.run_svn(None, 'propset', 'svn:eol-style', "CR", mu_path) 3516 svntest.main.run_svn(None, 3517 'commit', '-m', 'set eol-style property', wc_dir) 3518 3519 expected_backup_disk = svntest.main.greek_state.copy() 3520 expected_backup_disk.add({ 3521 'A/mu' : Item(contents= "This is the file 'mu'.\015" + 3522 "Added new line of text.\015") 3523 }) 3524 expected_backup_output = svntest.wc.State(wc_backup, { 3525 'A/mu' : Item(status='GU'), 3526 }) 3527 expected_mergeinfo_output = svntest.wc.State(wc_backup, { 3528 '' : Item(status=' G'), 3529 }) 3530 expected_backup_status = svntest.actions.get_virginal_state(wc_backup, 1) 3531 expected_backup_status.tweak('', status=' M') 3532 expected_backup_status.tweak('A/mu', status='MM') 3533 svntest.actions.run_and_verify_merge2(wc_backup, '2', '3', sbox.repo_url, 3534 None, 3535 expected_backup_output, 3536 expected_mergeinfo_output, 3537 expected_elision_output, 3538 expected_backup_disk, 3539 expected_backup_status, 3540 expected_backup_skip, 3541 keep_eol_style=True) 3542 3543 # Test 3: now delete the eol-style property and commit, merge this revision 3544 # in the still changed mu in the second working copy; there should be no 3545 # conflict! 3546 # EOL of mu should be unchanged (=CRLF). 3547 svntest.main.run_svn(None, 'propdel', 'svn:eol-style', mu_path) 3548 svntest.main.run_svn(None, 3549 'commit', '-m', 'del eol-style property', wc_dir) 3550 3551 expected_backup_disk = svntest.main.greek_state.copy() 3552 expected_backup_disk.add({ 3553 'A/mu' : Item(contents= "This is the file 'mu'.\015" + 3554 "Added new line of text.\015") 3555 }) 3556 expected_backup_output = svntest.wc.State(wc_backup, { 3557 'A/mu' : Item(status=' G'), 3558 }) 3559 expected_backup_status = svntest.actions.get_virginal_state(wc_backup, 1) 3560 expected_backup_status.tweak('', status=' M') 3561 expected_backup_status.tweak('A/mu', status='M ') 3562 svntest.actions.run_and_verify_merge2(wc_backup, '3', '4', sbox.repo_url, 3563 None, 3564 expected_backup_output, 3565 expected_mergeinfo_output, 3566 expected_elision_output, 3567 expected_backup_disk, 3568 expected_backup_status, 3569 expected_backup_skip, 3570 keep_eol_style=True) 3571 3572#---------------------------------------------------------------------- 3573def create_deep_trees(wc_dir): 3574 """Create A/B/F/E by moving A/B/E to A/B/F/E. 3575 Copy A/B/F/E to A/B/F/E1. 3576 Copy A/B to A/copy-of-B, and return the expected status. 3577 At the end of this function WC would be at r4""" 3578 3579 A_path = os.path.join(wc_dir, 'A') 3580 A_B_path = os.path.join(A_path, 'B') 3581 A_B_E_path = os.path.join(A_B_path, 'E') 3582 A_B_F_path = os.path.join(A_B_path, 'F') 3583 A_B_F_E_path = os.path.join(A_B_F_path, 'E') 3584 A_B_F_E1_path = os.path.join(A_B_F_path, 'E1') 3585 3586 # Deepen the directory structure we're working with by moving E to 3587 # underneath F and committing, creating revision 2. 3588 svntest.main.run_svn(None, 'mv', A_B_E_path, A_B_F_path) 3589 3590 expected_output = wc.State(wc_dir, { 3591 'A/B/E' : Item(verb='Deleting'), 3592 'A/B/F/E' : Item(verb='Adding') 3593 }) 3594 expected_status = svntest.actions.get_virginal_state(wc_dir, 1) 3595 expected_status.remove('A/B/E', 'A/B/E/alpha', 'A/B/E/beta') 3596 expected_status.add({ 3597 'A/B/F/E' : Item(status=' ', wc_rev=2), 3598 'A/B/F/E/alpha' : Item(status=' ', wc_rev=2), 3599 'A/B/F/E/beta' : Item(status=' ', wc_rev=2), 3600 }) 3601 svntest.actions.run_and_verify_commit(wc_dir, expected_output, 3602 expected_status) 3603 3604 svntest.main.run_svn(None, 'cp', A_B_F_E_path, A_B_F_E1_path) 3605 3606 3607 expected_output = wc.State(wc_dir, { 3608 'A/B/F/E1' : Item(verb='Adding') 3609 }) 3610 expected_status.add({ 3611 'A/B/F/E1' : Item(status=' ', wc_rev=3), 3612 'A/B/F/E1/alpha' : Item(status=' ', wc_rev=3), 3613 'A/B/F/E1/beta' : Item(status=' ', wc_rev=3), 3614 }) 3615 svntest.actions.run_and_verify_commit(wc_dir, expected_output, 3616 expected_status) 3617 3618 # Bring the entire WC up to date with rev 3. 3619 svntest.actions.run_and_verify_svn(None, [], 'update', wc_dir) 3620 expected_status.tweak(wc_rev=3) 3621 3622 # Copy B and commit, creating revision 4. 3623 copy_of_B_path = os.path.join(A_path, 'copy-of-B') 3624 svntest.main.run_svn(None, "cp", A_B_path, copy_of_B_path) 3625 expected_output = svntest.wc.State(wc_dir, { 3626 'A/copy-of-B' : Item(verb='Adding'), 3627 }) 3628 expected_status.add({ 3629 'A/copy-of-B' : Item(status=' ', wc_rev=4), 3630 'A/copy-of-B/F' : Item(status=' ', wc_rev=4), 3631 'A/copy-of-B/F/E' : Item(status=' ', wc_rev=4), 3632 'A/copy-of-B/F/E/alpha' : Item(status=' ', wc_rev=4), 3633 'A/copy-of-B/F/E/beta' : Item(status=' ', wc_rev=4), 3634 'A/copy-of-B/F/E1' : Item(status=' ', wc_rev=4), 3635 'A/copy-of-B/F/E1/alpha' : Item(status=' ', wc_rev=4), 3636 'A/copy-of-B/F/E1/beta' : Item(status=' ', wc_rev=4), 3637 'A/copy-of-B/lambda' : Item(status=' ', wc_rev=4), 3638 }) 3639 svntest.actions.run_and_verify_commit(wc_dir, expected_output, 3640 expected_status) 3641 3642 expected_disk = svntest.main.greek_state.copy() 3643 expected_disk.remove('A/B/E', 'A/B/E/alpha', 'A/B/E/beta') 3644 expected_disk.add({ 3645 'A/B/F/E' : Item(), 3646 'A/B/F/E/alpha' : Item(contents="This is the file 'alpha'.\n"), 3647 'A/B/F/E/beta' : Item(contents="This is the file 'beta'.\n"), 3648 'A/B/F/E1' : Item(), 3649 'A/B/F/E1/alpha' : Item(contents="This is the file 'alpha'.\n"), 3650 'A/B/F/E1/beta' : Item(contents="This is the file 'beta'.\n"), 3651 'A/copy-of-B' : Item(), 3652 'A/copy-of-B/F' : Item(props={}), 3653 'A/copy-of-B/F/E' : Item(), 3654 'A/copy-of-B/F/E/alpha' : Item(contents="This is the file 'alpha'.\n"), 3655 'A/copy-of-B/F/E/beta' : Item(contents="This is the file 'beta'.\n"), 3656 'A/copy-of-B/F/E1' : Item(), 3657 'A/copy-of-B/F/E1/alpha' : Item(contents="This is the file 'alpha'.\n"), 3658 'A/copy-of-B/F/E1/beta' : Item(contents="This is the file 'beta'.\n"), 3659 'A/copy-of-B/lambda' : Item(contents="This is the file 'lambda'.\n"), 3660 }) 3661 svntest.actions.verify_disk(wc_dir, expected_disk, True) 3662 3663 # Bring the entire WC up to date with rev 4. 3664 svntest.actions.run_and_verify_svn(None, [], 'update', wc_dir) 3665 3666 svntest.actions.verify_disk(wc_dir, expected_disk, True) 3667 3668 expected_status.tweak(wc_rev=4) 3669 expected_disk.tweak('A/copy-of-B/F/E', 'A/copy-of-B/F/E1', status=' M') 3670 return expected_status 3671 3672#---------------------------------------------------------------------- 3673@SkipUnless(server_has_mergeinfo) 3674def avoid_repeated_merge_using_inherited_merge_info(sbox): 3675 "use inherited mergeinfo to avoid repeated merge" 3676 3677 sbox.build() 3678 wc_dir = sbox.wc_dir 3679 3680 A_path = sbox.ospath('A') 3681 A_B_path = os.path.join(A_path, 'B') 3682 A_B_E_path = os.path.join(A_B_path, 'E') 3683 A_B_F_path = os.path.join(A_B_path, 'F') 3684 copy_of_B_path = os.path.join(A_path, 'copy-of-B') 3685 3686 # Create a deeper directory structure. 3687 expected_status = create_deep_trees(wc_dir) 3688 3689 # Edit alpha and commit it, creating revision 5. 3690 alpha_path = os.path.join(A_B_F_path, 'E', 'alpha') 3691 new_content_for_alpha = 'new content to alpha\n' 3692 svntest.main.file_write(alpha_path, new_content_for_alpha) 3693 expected_output = svntest.wc.State(wc_dir, { 3694 'A/B/F/E/alpha' : Item(verb='Sending'), 3695 }) 3696 expected_status.tweak('A/B/F/E/alpha', wc_rev=5) 3697 svntest.actions.run_and_verify_commit(wc_dir, expected_output, 3698 expected_status) 3699 3700 # Bring the entire WC up to date with rev 5. 3701 svntest.actions.run_and_verify_svn(None, [], 'update', wc_dir) 3702 3703 # Merge changes from rev 5 of B (to alpha) into copy_of_B. 3704 expected_output = wc.State(copy_of_B_path, { 3705 'F/E/alpha' : Item(status='U '), 3706 }) 3707 expected_mergeinfo_output = wc.State(copy_of_B_path, { 3708 '' : Item(status=' U'), 3709 }) 3710 expected_elision_output = wc.State(copy_of_B_path, { 3711 }) 3712 expected_status = wc.State(copy_of_B_path, { 3713 '' : Item(status=' M', wc_rev=5), 3714 'F/E' : Item(status=' ', wc_rev=5), 3715 'F/E/alpha' : Item(status='M ', wc_rev=5), 3716 'F/E/beta' : Item(status=' ', wc_rev=5), 3717 'F/E1' : Item(status=' ', wc_rev=5), 3718 'F/E1/alpha' : Item(status=' ', wc_rev=5), 3719 'F/E1/beta' : Item(status=' ', wc_rev=5), 3720 'lambda' : Item(status=' ', wc_rev=5), 3721 'F' : Item(status=' ', wc_rev=5), 3722 }) 3723 expected_disk = wc.State('', { 3724 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B:5'}), 3725 'F/E' : Item(), 3726 'F/E/alpha' : Item(new_content_for_alpha), 3727 'F/E/beta' : Item("This is the file 'beta'.\n"), 3728 'F/E1' : Item(), 3729 'F/E1/alpha' : Item("This is the file 'alpha'.\n"), 3730 'F/E1/beta' : Item("This is the file 'beta'.\n"), 3731 'F' : Item(), 3732 'lambda' : Item("This is the file 'lambda'.\n") 3733 }) 3734 expected_skip = wc.State(copy_of_B_path, { }) 3735 3736 svntest.actions.run_and_verify_merge(copy_of_B_path, '4', '5', 3737 sbox.repo_url + '/A/B', None, 3738 expected_output, 3739 expected_mergeinfo_output, 3740 expected_elision_output, 3741 expected_disk, 3742 expected_status, 3743 expected_skip, 3744 check_props=True) 3745 3746 # Commit the result of the merge, creating revision 6. 3747 expected_output = svntest.wc.State(copy_of_B_path, { 3748 '' : Item(verb='Sending'), 3749 'F/E/alpha' : Item(verb='Sending'), 3750 }) 3751 svntest.actions.run_and_verify_commit(copy_of_B_path, expected_output, 3752 None) 3753 3754 # Update the WC to bring /A/copy_of_B/F from rev 4 to rev 6. 3755 # Without this update, a subsequent merge will not find any merge 3756 # info for /A/copy_of_B/F -- nor its parent dir in the repos -- at 3757 # rev 4. Mergeinfo wasn't introduced until rev 6. 3758 copy_of_B_F_E_path = os.path.join(copy_of_B_path, 'F', 'E') 3759 svntest.actions.run_and_verify_svn(None, [], 'update', wc_dir) 3760 3761 # Attempt to re-merge changes to alpha from rev 4. Use the merge 3762 # info inherited from the grandparent (copy-of-B) of our merge 3763 # target (/A/copy-of-B/F/E) to avoid a repeated merge. 3764 expected_status = wc.State(copy_of_B_F_E_path, { 3765 '' : Item(status=' ', wc_rev=6), 3766 'alpha' : Item(status=' ', wc_rev=6), 3767 'beta' : Item(status=' ', wc_rev=6), 3768 }) 3769 svntest.actions.run_and_verify_svn( 3770 expected_merge_output([[5]], 3771 [' U ' + copy_of_B_F_E_path + '\n', 3772 ' G ' + copy_of_B_F_E_path + '\n'], 3773 elides=True), 3774 [], 'merge', '-r4:5', 3775 sbox.repo_url + '/A/B/F/E', 3776 copy_of_B_F_E_path) 3777 svntest.actions.run_and_verify_status(copy_of_B_F_E_path, 3778 expected_status) 3779 3780#---------------------------------------------------------------------- 3781@SkipUnless(server_has_mergeinfo) 3782@Issue(2821) 3783def avoid_repeated_merge_on_subtree_with_merge_info(sbox): 3784 "use subtree's mergeinfo to avoid repeated merge" 3785 # Create deep trees A/B/F/E and A/B/F/E1 and copy A/B to A/copy-of-B 3786 # with the help of 'create_deep_trees' 3787 # As /A/copy-of-B/F/E1 is not a child of /A/copy-of-B/F/E, 3788 # set_path should not be called on /A/copy-of-B/F/E1 while 3789 # doing a implicit subtree merge on /A/copy-of-B/F/E. 3790 sbox.build() 3791 wc_dir = sbox.wc_dir 3792 3793 A_path = sbox.ospath('A') 3794 A_B_path = os.path.join(A_path, 'B') 3795 A_B_E_path = os.path.join(A_B_path, 'E') 3796 A_B_F_path = os.path.join(A_B_path, 'F') 3797 A_B_F_E_path = os.path.join(A_B_F_path, 'E') 3798 copy_of_B_path = os.path.join(A_path, 'copy-of-B') 3799 copy_of_B_F_path = os.path.join(A_path, 'copy-of-B', 'F') 3800 A_copy_of_B_F_E_alpha_path = os.path.join(A_path, 'copy-of-B', 'F', 3801 'E', 'alpha') 3802 3803 # Create a deeper directory structure. 3804 expected_status = create_deep_trees(wc_dir) 3805 3806 # Edit alpha and commit it, creating revision 5. 3807 alpha_path = os.path.join(A_B_F_E_path, 'alpha') 3808 new_content_for_alpha1 = 'new content to alpha\n' 3809 svntest.main.file_write(alpha_path, new_content_for_alpha1) 3810 3811 expected_output = svntest.wc.State(wc_dir, { 3812 'A/B/F/E/alpha' : Item(verb='Sending'), 3813 }) 3814 expected_status.tweak('A/B/F/E/alpha', wc_rev=5) 3815 svntest.actions.run_and_verify_commit(wc_dir, expected_output, 3816 expected_status) 3817 3818 for path_and_mergeinfo in (('E', '/A/B/F/E:5'), 3819 ('E1', '/A/B/F/E:5')): 3820 path_name = os.path.join(copy_of_B_path, 'F', path_and_mergeinfo[0]) 3821 3822 # Merge r5 to path_name. 3823 expected_output = wc.State(path_name, { 3824 'alpha' : Item(status='U '), 3825 }) 3826 expected_mergeinfo_output = wc.State(path_name, { 3827 '' : Item(status=' U'), 3828 }) 3829 expected_elision_output = wc.State(path_name, {}) 3830 expected_status = wc.State(path_name, { 3831 '' : Item(status=' M', wc_rev=4), 3832 'alpha' : Item(status='M ', wc_rev=4), 3833 'beta' : Item(status=' ', wc_rev=4), 3834 }) 3835 expected_disk = wc.State('', { 3836 '' : Item(props={SVN_PROP_MERGEINFO : path_and_mergeinfo[1]}), 3837 'alpha' : Item(new_content_for_alpha1), 3838 'beta' : Item("This is the file 'beta'.\n"), 3839 }) 3840 expected_skip = wc.State(path_name, { }) 3841 3842 svntest.actions.run_and_verify_merge(path_name, '4', '5', 3843 sbox.repo_url + '/A/B/F/E', None, 3844 expected_output, 3845 expected_mergeinfo_output, 3846 expected_elision_output, 3847 expected_disk, 3848 expected_status, 3849 expected_skip, 3850 check_props=True) 3851 3852 # Commit the result of the merge, creating new revision. 3853 expected_output = svntest.wc.State(path_name, { 3854 '' : Item(verb='Sending'), 3855 'alpha' : Item(verb='Sending'), 3856 }) 3857 svntest.actions.run_and_verify_commit(path_name, 3858 expected_output, None, [], wc_dir) 3859 3860 # Edit A/B/F/E/alpha and commit it, creating revision 8. 3861 new_content_for_alpha = 'new content to alpha\none more line\n' 3862 svntest.main.file_write(alpha_path, new_content_for_alpha) 3863 3864 expected_output = svntest.wc.State(A_B_F_E_path, { 3865 'alpha' : Item(verb='Sending'), 3866 }) 3867 expected_status = wc.State(A_B_F_E_path, { 3868 '' : Item(status=' ', wc_rev=4), 3869 'alpha' : Item(status=' ', wc_rev=8), 3870 'beta' : Item(status=' ', wc_rev=4), 3871 }) 3872 svntest.actions.run_and_verify_commit(A_B_F_E_path, expected_output, 3873 expected_status, [], wc_dir) 3874 3875 # Update the WC to bring /A/copy_of_B to rev 8. 3876 # Without this update expected_status tree would be cumbersome to 3877 # understand. 3878 svntest.actions.run_and_verify_svn(None, [], 'update', wc_dir) 3879 3880 # Merge changes from rev 4:8 of A/B into A/copy_of_B. A/copy_of_B/F/E1 3881 # has explicit mergeinfo and exists at r4 in the merge source, so it 3882 # should be treated as a subtree with intersecting mergeinfo and its 3883 # mergeinfo updated. 3884 expected_output = wc.State(copy_of_B_path, { 3885 'F/E/alpha' : Item(status='U ') 3886 }) 3887 expected_mergeinfo_output = wc.State(copy_of_B_path, { 3888 '' : Item(status=' U'), 3889 'F/E' : Item(status=' U') 3890 }) 3891 expected_elision_output = wc.State(copy_of_B_path, { 3892 'F/E' : Item(status=' U') 3893 }) 3894 expected_status = wc.State(copy_of_B_path, { 3895 # The subtree mergeinfo on F/E1 is not updated because 3896 # this merge does not affect that subtree. 3897 '' : Item(status=' M', wc_rev=8), 3898 'F/E' : Item(status=' M', wc_rev=8), 3899 'F/E/alpha' : Item(status='M ', wc_rev=8), 3900 'F/E/beta' : Item(status=' ', wc_rev=8), 3901 'F/E1' : Item(status=' ', wc_rev=8), 3902 'F/E1/alpha' : Item(status=' ', wc_rev=8), 3903 'F/E1/beta' : Item(status=' ', wc_rev=8), 3904 'lambda' : Item(status=' ', wc_rev=8), 3905 'F' : Item(status=' ', wc_rev=8) 3906 }) 3907 expected_disk = wc.State('', { 3908 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B:5-8'}), 3909 'F/E' : Item(props={}), # elision! 3910 'F/E/alpha' : Item(new_content_for_alpha), 3911 'F/E/beta' : Item("This is the file 'beta'.\n"), 3912 'F' : Item(), 3913 'F/E1' : Item(props={SVN_PROP_MERGEINFO : 3914 '/A/B/F/E:5'}), 3915 'F/E1/alpha' : Item(new_content_for_alpha1), 3916 'F/E1/beta' : Item("This is the file 'beta'.\n"), 3917 'lambda' : Item("This is the file 'lambda'.\n") 3918 }) 3919 expected_skip = wc.State(copy_of_B_path, { }) 3920 svntest.actions.run_and_verify_merge(copy_of_B_path, '4', '8', 3921 sbox.repo_url + '/A/B', None, 3922 expected_output, 3923 expected_mergeinfo_output, 3924 expected_elision_output, 3925 expected_disk, 3926 expected_status, 3927 expected_skip, 3928 check_props=True) 3929 3930 # Test for part of Issue #2821, see 3931 # https://issues.apache.org/jira/browse/SVN-2821#desc22 3932 # 3933 # Revert all local changes. 3934 svntest.actions.run_and_verify_svn(None, [], 'revert', '-R', wc_dir) 3935 3936 # Make a text mod to A/copy-of-B/F/E/alpha 3937 newer_content_for_alpha = "Conflicting content" 3938 svntest.main.file_write(A_copy_of_B_F_E_alpha_path, 3939 newer_content_for_alpha) 3940 3941 # Re-merge r5 to A/copy-of-B/F, this *should* be a no-op as the mergeinfo 3942 # on A/copy-of-B/F/E should prevent any attempt to merge r5 into that 3943 # subtree. The merge will leave a few local changes as mergeinfo is set 3944 # on A/copy-of-B/F, the mergeinfo on A/copy-of-B/F/E elides to it. The 3945 # mergeinfo on A/copy-of-B/F/E1 remains unchanged as that subtree was 3946 # untouched by the merge. 3947 expected_output = wc.State(copy_of_B_F_path, {}) 3948 expected_mergeinfo_output = wc.State(copy_of_B_F_path, { 3949 '' : Item(status=' U'), 3950 }) 3951 expected_elision_output = wc.State(copy_of_B_F_path, { 3952 'E' : Item(status=' U') 3953 }) 3954 expected_status = wc.State(copy_of_B_F_path, { 3955 '' : Item(status=' M', wc_rev=8), 3956 'E' : Item(status=' M', wc_rev=8), 3957 'E/alpha' : Item(status='M ', wc_rev=8), 3958 'E/beta' : Item(status=' ', wc_rev=8), 3959 'E1' : Item(status=' ', wc_rev=8), 3960 'E1/alpha' : Item(status=' ', wc_rev=8), 3961 'E1/beta' : Item(status=' ', wc_rev=8), 3962 }) 3963 expected_disk = wc.State('', { 3964 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B/F:5'}), 3965 'E' : Item(props={}), 3966 'E/alpha' : Item(newer_content_for_alpha), 3967 'E/beta' : Item("This is the file 'beta'.\n"), 3968 'E1' : Item(props={SVN_PROP_MERGEINFO : 3969 '/A/B/F/E:5'}), 3970 'E1/alpha' : Item(new_content_for_alpha1), 3971 'E1/beta' : Item("This is the file 'beta'.\n") 3972 }) 3973 expected_skip = wc.State(copy_of_B_F_path, { }) 3974 svntest.actions.run_and_verify_merge(copy_of_B_F_path, '4', '5', 3975 sbox.repo_url + '/A/B/F', None, 3976 expected_output, 3977 expected_mergeinfo_output, 3978 expected_elision_output, 3979 expected_disk, 3980 expected_status, 3981 expected_skip, 3982 check_props=True) 3983 3984#---------------------------------------------------------------------- 3985def tweak_src_then_merge_to_dest(sbox, src_path, dst_path, 3986 canon_src_path, contents, cur_rev): 3987 """Edit src and commit it. This results in new_rev. 3988 Merge new_rev to dst_path. Return new_rev.""" 3989 3990 wc_dir = sbox.wc_dir 3991 new_rev = cur_rev + 1 3992 svntest.main.file_write(src_path, contents) 3993 3994 expected_output = svntest.wc.State(src_path, { 3995 '': Item(verb='Sending'), 3996 }) 3997 3998 expected_status = wc.State(src_path, 3999 { '': Item(wc_rev=new_rev, status=' ')}) 4000 4001 svntest.actions.run_and_verify_commit(src_path, expected_output, 4002 expected_status) 4003 4004 # Update the WC to new_rev so that it would be easier to expect everyone 4005 # to be at new_rev. 4006 svntest.actions.run_and_verify_svn(None, [], 'update', wc_dir) 4007 4008 # Merge new_rev of src_path to dst_path. 4009 4010 expected_status = wc.State(dst_path, 4011 { '': Item(wc_rev=new_rev, status='MM')}) 4012 4013 merge_url = sbox.repo_url + '/' + canon_src_path 4014 if sys.platform == 'win32': 4015 merge_url = merge_url.replace('\\', '/') 4016 4017 svntest.actions.run_and_verify_svn( 4018 expected_merge_output([[new_rev]], 4019 ['U ' + dst_path + '\n', 4020 ' U ' + dst_path + '\n']), 4021 [], 'merge', '-c', str(new_rev), merge_url, dst_path) 4022 4023 svntest.actions.run_and_verify_status(dst_path, expected_status) 4024 4025 return new_rev 4026 4027#---------------------------------------------------------------------- 4028@SkipUnless(server_has_mergeinfo) 4029def obey_reporter_api_semantics_while_doing_subtree_merges(sbox): 4030 "drive reporter api in depth first order" 4031 4032 # Copy /A/D to /A/copy-of-D it results in rONE. 4033 # Create children at different hierarchies having some merge-info 4034 # to test the set_path calls on a reporter in a depth-first order. 4035 # On all 'file' descendants of /A/copy-of-D/ we run merges. 4036 # We create /A/D/umlaut directly over URL it results in rev rTWO. 4037 # When we merge rONE+1:TWO of /A/D on /A/copy-of-D it should merge smoothly. 4038 4039 sbox.build() 4040 wc_dir = sbox.wc_dir 4041 4042 A_path = sbox.ospath('A') 4043 A_D_path = sbox.ospath('A/D') 4044 copy_of_A_D_path = sbox.ospath('A/copy-of-D') 4045 4046 svntest.main.run_svn(None, "cp", A_D_path, copy_of_A_D_path) 4047 4048 expected_output = svntest.wc.State(wc_dir, { 4049 'A/copy-of-D' : Item(verb='Adding'), 4050 }) 4051 expected_status = svntest.actions.get_virginal_state(wc_dir, 1) 4052 expected_status.add({ 4053 'A/copy-of-D' : Item(status=' ', wc_rev=2), 4054 'A/copy-of-D/G' : Item(status=' ', wc_rev=2), 4055 'A/copy-of-D/G/pi' : Item(status=' ', wc_rev=2), 4056 'A/copy-of-D/G/rho' : Item(status=' ', wc_rev=2), 4057 'A/copy-of-D/G/tau' : Item(status=' ', wc_rev=2), 4058 'A/copy-of-D/H' : Item(status=' ', wc_rev=2), 4059 'A/copy-of-D/H/chi' : Item(status=' ', wc_rev=2), 4060 'A/copy-of-D/H/omega' : Item(status=' ', wc_rev=2), 4061 'A/copy-of-D/H/psi' : Item(status=' ', wc_rev=2), 4062 'A/copy-of-D/gamma' : Item(status=' ', wc_rev=2), 4063 }) 4064 svntest.actions.run_and_verify_commit(wc_dir, expected_output, 4065 expected_status) 4066 4067 4068 cur_rev = 2 4069 for path in (["A", "D", "G", "pi"], 4070 ["A", "D", "G", "rho"], 4071 ["A", "D", "G", "tau"], 4072 ["A", "D", "H", "chi"], 4073 ["A", "D", "H", "omega"], 4074 ["A", "D", "H", "psi"], 4075 ["A", "D", "gamma"]): 4076 path_name = os.path.join(wc_dir, *path) 4077 canon_path_name = os.path.join(*path) 4078 path[1] = "copy-of-D" 4079 copy_of_path_name = os.path.join(wc_dir, *path) 4080 var_name = 'new_content_for_' + path[len(path) - 1] 4081 file_contents = "new content to " + path[len(path) - 1] + "\n" 4082 globals()[var_name] = file_contents 4083 cur_rev = tweak_src_then_merge_to_dest(sbox, path_name, 4084 copy_of_path_name, canon_path_name, 4085 file_contents, cur_rev) 4086 4087 copy_of_A_D_wc_rev = cur_rev 4088 svntest.actions.run_and_verify_svn(['Committing transaction...\n', 4089 'Committed revision ' + str(cur_rev+1) + 4090 '.\n'], 4091 [], 4092 'mkdir', sbox.repo_url + '/A/D/umlaut', 4093 '-m', "log msg") 4094 rev_to_merge_to_copy_of_D = cur_rev + 1 4095 4096 # All the file descendants of /A/copy-of-D/ have already been merged 4097 # so the only notification we expect is for the added 'umlaut'. 4098 expected_output = wc.State(copy_of_A_D_path, { 4099 'umlaut' : Item(status='A '), 4100 }) 4101 expected_mergeinfo_output = wc.State(copy_of_A_D_path, { 4102 '' : Item(status=' U'), 4103 }) 4104 expected_elision_output = wc.State(copy_of_A_D_path, { 4105 }) 4106 # No subtree with explicit mergeinfo is affected by this merge, so they 4107 # all remain unchanged from before the merge. The only mergeinfo updated 4108 # is that on the target 'A/copy-of-D. 4109 expected_status = wc.State(copy_of_A_D_path, { 4110 '' : Item(status=' M', wc_rev=copy_of_A_D_wc_rev), 4111 'G' : Item(status=' ', wc_rev=copy_of_A_D_wc_rev), 4112 'G/pi' : Item(status='MM', wc_rev=copy_of_A_D_wc_rev), 4113 'G/rho' : Item(status='MM', wc_rev=copy_of_A_D_wc_rev), 4114 'G/tau' : Item(status='MM', wc_rev=copy_of_A_D_wc_rev), 4115 'H' : Item(status=' ', wc_rev=copy_of_A_D_wc_rev), 4116 'H/chi' : Item(status='MM', wc_rev=copy_of_A_D_wc_rev), 4117 'H/omega' : Item(status='MM', wc_rev=copy_of_A_D_wc_rev), 4118 'H/psi' : Item(status='MM', wc_rev=copy_of_A_D_wc_rev), 4119 'gamma' : Item(status='MM', wc_rev=copy_of_A_D_wc_rev), 4120 'umlaut' : Item(status='A ', copied='+', wc_rev='-'), 4121 }) 4122 4123 merged_rangelist = "3-%d" % rev_to_merge_to_copy_of_D 4124 4125 expected_disk = wc.State('', { 4126 '' : Item(props={SVN_PROP_MERGEINFO : '/A/D:' + merged_rangelist}), 4127 'G' : Item(), 4128 'G/pi' : Item(new_content_for_pi, 4129 props={SVN_PROP_MERGEINFO : '/A/D/G/pi:3'}), 4130 'G/rho' : Item(new_content_for_rho, 4131 props={SVN_PROP_MERGEINFO : '/A/D/G/rho:4'}), 4132 'G/tau' : Item(new_content_for_tau, 4133 props={SVN_PROP_MERGEINFO : '/A/D/G/tau:5'}), 4134 'H' : Item(), 4135 'H/chi' : Item(new_content_for_chi, 4136 props={SVN_PROP_MERGEINFO : '/A/D/H/chi:6'}), 4137 'H/omega' : Item(new_content_for_omega, 4138 props={SVN_PROP_MERGEINFO : '/A/D/H/omega:7'}), 4139 'H/psi' : Item(new_content_for_psi, 4140 props={SVN_PROP_MERGEINFO : '/A/D/H/psi:8'}), 4141 'gamma' : Item(new_content_for_gamma, 4142 props={SVN_PROP_MERGEINFO : '/A/D/gamma:9'}), 4143 'umlaut' : Item(), 4144 }) 4145 expected_skip = wc.State(copy_of_A_D_path, { }) 4146 svntest.actions.run_and_verify_merge(copy_of_A_D_path, 4147 2, 4148 str(rev_to_merge_to_copy_of_D), 4149 sbox.repo_url + '/A/D', None, 4150 expected_output, 4151 expected_mergeinfo_output, 4152 expected_elision_output, 4153 expected_disk, 4154 expected_status, 4155 expected_skip, 4156 check_props=True) 4157 4158#---------------------------------------------------------------------- 4159@SkipUnless(server_has_mergeinfo) 4160@Issues(2733,2734) 4161def mergeinfo_inheritance(sbox): 4162 "target inherits mergeinfo from nearest ancestor" 4163 4164 # Test for Issues #2733 and #2734. 4165 # 4166 # When the target of a merge has no explicit mergeinfo and the merge 4167 # would result in mergeinfo being added to the target which... 4168 # 4169 # ...is a subset of the *local* mergeinfo on one of the target's 4170 # ancestors (it's nearest ancestor takes precedence), then the merge is 4171 # not repeated and no mergeinfo should be set on the target (Issue #2734). 4172 # 4173 # OR 4174 # 4175 # ...is not a subset it's nearest ancestor, the target should inherit the 4176 # non-inersecting mergeinfo (local or committed, the former takes 4177 # precedence) from it's nearest ancestor (Issue #2733). 4178 4179 sbox.build() 4180 wc_dir = sbox.wc_dir 4181 wc_disk, wc_status = set_up_branch(sbox) 4182 4183 # Some paths we'll care about 4184 A_COPY_path = sbox.ospath('A_COPY') 4185 B_COPY_path = sbox.ospath('A_COPY/B') 4186 beta_COPY_path = sbox.ospath('A_COPY/B/E/beta') 4187 E_COPY_path = sbox.ospath('A_COPY/B/E') 4188 omega_COPY_path = sbox.ospath('A_COPY/D/H/omega') 4189 D_COPY_path = sbox.ospath('A_COPY/D') 4190 G_COPY_path = sbox.ospath('A_COPY/D/G') 4191 4192 # Now start merging... 4193 4194 # Merge r4 into A_COPY/D/ 4195 expected_output = wc.State(D_COPY_path, { 4196 'G/rho' : Item(status='U '), 4197 }) 4198 expected_mergeinfo_output = wc.State(D_COPY_path, { 4199 '' : Item(status=' U'), 4200 }) 4201 expected_elision_output = wc.State(D_COPY_path, { 4202 }) 4203 expected_status = wc.State(D_COPY_path, { 4204 '' : Item(status=' M', wc_rev=2), 4205 'G' : Item(status=' ', wc_rev=2), 4206 'G/pi' : Item(status=' ', wc_rev=2), 4207 'G/rho' : Item(status='M ', wc_rev=2), 4208 'G/tau' : Item(status=' ', wc_rev=2), 4209 'H' : Item(status=' ', wc_rev=2), 4210 'H/chi' : Item(status=' ', wc_rev=2), 4211 'H/psi' : Item(status=' ', wc_rev=2), 4212 'H/omega' : Item(status=' ', wc_rev=2), 4213 'gamma' : Item(status=' ', wc_rev=2), 4214 }) 4215 # We test issue #2733 here (with a directory as the merge target). 4216 # r1 should be inherited from 'A_COPY'. 4217 expected_disk = wc.State('', { 4218 '' : Item(props={SVN_PROP_MERGEINFO : '/A/D:4'}), 4219 'G' : Item(), 4220 'G/pi' : Item("This is the file 'pi'.\n"), 4221 'G/rho' : Item("New content"), 4222 'G/tau' : Item("This is the file 'tau'.\n"), 4223 'H' : Item(), 4224 'H/chi' : Item("This is the file 'chi'.\n"), 4225 'H/psi' : Item("This is the file 'psi'.\n"), 4226 'H/omega' : Item("This is the file 'omega'.\n"), 4227 'gamma' : Item("This is the file 'gamma'.\n") 4228 }) 4229 expected_skip = wc.State(D_COPY_path, { }) 4230 svntest.actions.run_and_verify_merge(D_COPY_path, '3', '4', 4231 sbox.repo_url + '/A/D', None, 4232 expected_output, 4233 expected_mergeinfo_output, 4234 expected_elision_output, 4235 expected_disk, 4236 expected_status, 4237 expected_skip, 4238 check_props=True) 4239 4240 # Merge r4 again, this time into A_COPY/D/G. An ancestor directory 4241 # (A_COPY/D) exists with identical local mergeinfo, so the merge 4242 # should not be repeated. We test issue #2734 here with (with a 4243 # directory as the merge target). 4244 expected_output = wc.State(G_COPY_path, { }) 4245 # A_COPY/D/G gets mergeinfo set, but it immediately elides to A_COPY/D. 4246 expected_mergeinfo_output = wc.State(G_COPY_path, { 4247 '' : Item(status=' G'), 4248 }) 4249 expected_elision_output = wc.State(G_COPY_path, { 4250 '' : Item(status=' U'), 4251 }) 4252 expected_status = wc.State(G_COPY_path, { 4253 '' : Item(status=' ', wc_rev=2), 4254 'pi' : Item(status=' ', wc_rev=2), 4255 'rho' : Item(status='M ', wc_rev=2), 4256 'tau' : Item(status=' ', wc_rev=2), 4257 }) 4258 expected_disk = wc.State('', { 4259 'pi' : Item("This is the file 'pi'.\n"), 4260 'rho' : Item("New content"), 4261 'tau' : Item("This is the file 'tau'.\n"), 4262 }) 4263 expected_skip = wc.State(G_COPY_path, { }) 4264 svntest.actions.run_and_verify_merge(G_COPY_path, '3', '4', 4265 sbox.repo_url + '/A/D/G', None, 4266 expected_output, 4267 expected_mergeinfo_output, 4268 expected_elision_output, 4269 expected_disk, 4270 expected_status, 4271 expected_skip, 4272 check_props=True) 4273 4274 # Merge r5 into A_COPY/B. Again, r1 should be inherited from 4275 # A_COPY (Issue #2733) 4276 expected_output = wc.State(B_COPY_path, { 4277 'E/beta' : Item(status='U '), 4278 }) 4279 expected_mergeinfo_output = wc.State(B_COPY_path, { 4280 '' : Item(status=' U'), 4281 }) 4282 expected_elision_output = wc.State(B_COPY_path, { 4283 }) 4284 expected_status = wc.State(B_COPY_path, { 4285 '' : Item(status=' M', wc_rev=2), 4286 'E' : Item(status=' ', wc_rev=2), 4287 'E/alpha' : Item(status=' ', wc_rev=2), 4288 'E/beta' : Item(status='M ', wc_rev=2), 4289 'lambda' : Item(status=' ', wc_rev=2), 4290 'F' : Item(status=' ', wc_rev=2), 4291 }) 4292 expected_disk = wc.State('', { 4293 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B:5'}), 4294 'E' : Item(), 4295 'E/alpha' : Item("This is the file 'alpha'.\n"), 4296 'E/beta' : Item("New content"), 4297 'F' : Item(), 4298 'lambda' : Item("This is the file 'lambda'.\n") 4299 }) 4300 expected_skip = wc.State(B_COPY_path, { }) 4301 4302 svntest.actions.run_and_verify_merge(B_COPY_path, '4', '5', 4303 sbox.repo_url + '/A/B', None, 4304 expected_output, 4305 expected_mergeinfo_output, 4306 expected_elision_output, 4307 expected_disk, 4308 expected_status, 4309 expected_skip, 4310 check_props=True) 4311 4312 # Merge r5 again, this time into A_COPY/B/E/beta. An ancestor 4313 # directory (A_COPY/B) exists with identical local mergeinfo, so 4314 # the merge should not be repeated (Issue #2734 with a file as the 4315 # merge target). 4316 expected_skip = wc.State(beta_COPY_path, { }) 4317 4318 # run_and_verify_merge doesn't support merging to a file WCPATH 4319 # so use run_and_verify_svn. 4320 ### TODO: We can use run_and_verify_merge() here now. 4321 svntest.actions.run_and_verify_svn([], [], 'merge', '-c5', 4322 sbox.repo_url + '/A/B/E/beta', 4323 beta_COPY_path) 4324 4325 # The merge wasn't repeated so beta shouldn't have any mergeinfo. 4326 # We are implicitly testing that without looking at the prop value 4327 # itself, just beta's prop modification status. 4328 expected_status = wc.State(beta_COPY_path, { 4329 '' : Item(status='M ', wc_rev=2), 4330 }) 4331 svntest.actions.run_and_verify_status(beta_COPY_path, expected_status) 4332 4333 # Merge r3 into A_COPY. A_COPY's has two subtrees with mergeinfo, 4334 # A_COPY/B/E/beta and A_COPY/D. Only the latter is effected by this 4335 # merge so only its mergeinfo is updated to include r3. 4336 expected_output = wc.State(A_COPY_path, { 4337 'D/H/psi' : Item(status='U '), 4338 }) 4339 expected_mergeinfo_output = wc.State(A_COPY_path, { 4340 '' : Item(status=' U'), 4341 'D' : Item(status=' G'), 4342 }) 4343 expected_elision_output = wc.State(A_COPY_path, { 4344 }) 4345 expected_status = wc.State(A_COPY_path, { 4346 '' : Item(status=' M', wc_rev=2), 4347 'B' : Item(status=' M', wc_rev=2), 4348 'mu' : Item(status=' ', wc_rev=2), 4349 'B/E' : Item(status=' ', wc_rev=2), 4350 'B/E/alpha' : Item(status=' ', wc_rev=2), 4351 'B/E/beta' : Item(status='M ', wc_rev=2), 4352 'B/lambda' : Item(status=' ', wc_rev=2), 4353 'B/F' : Item(status=' ', wc_rev=2), 4354 'C' : Item(status=' ', wc_rev=2), 4355 'D' : Item(status=' M', wc_rev=2), 4356 'D/G' : Item(status=' ', wc_rev=2), 4357 'D/G/pi' : Item(status=' ', wc_rev=2), 4358 'D/G/rho' : Item(status='M ', wc_rev=2), 4359 'D/G/tau' : Item(status=' ', wc_rev=2), 4360 'D/gamma' : Item(status=' ', wc_rev=2), 4361 'D/H' : Item(status=' ', wc_rev=2), 4362 'D/H/chi' : Item(status=' ', wc_rev=2), 4363 'D/H/psi' : Item(status='M ', wc_rev=2), 4364 'D/H/omega' : Item(status=' ', wc_rev=2), 4365 }) 4366 expected_disk = wc.State('', { 4367 '' : Item(props={SVN_PROP_MERGEINFO : '/A:3'}), 4368 'B' : Item(props={SVN_PROP_MERGEINFO : '/A/B:5'}), 4369 'mu' : Item("This is the file 'mu'.\n"), 4370 'B/E' : Item(), 4371 'B/E/alpha' : Item("This is the file 'alpha'.\n"), 4372 'B/E/beta' : Item("New content"), 4373 'B/lambda' : Item("This is the file 'lambda'.\n"), 4374 'B/F' : Item(), 4375 'C' : Item(), 4376 'D' : Item(props={SVN_PROP_MERGEINFO : '/A/D:3-4'}), 4377 'D/G' : Item(), 4378 'D/G/pi' : Item("This is the file 'pi'.\n"), 4379 'D/G/rho' : Item("New content"), 4380 'D/G/tau' : Item("This is the file 'tau'.\n"), 4381 'D/gamma' : Item("This is the file 'gamma'.\n"), 4382 'D/H' : Item(), 4383 'D/H/chi' : Item("This is the file 'chi'.\n"), 4384 'D/H/psi' : Item("New content"), 4385 'D/H/omega' : Item("This is the file 'omega'.\n"), 4386 }) 4387 expected_skip = wc.State(A_COPY_path, { }) 4388 svntest.actions.run_and_verify_merge(A_COPY_path, '2', '3', 4389 sbox.repo_url + '/A', None, 4390 expected_output, 4391 expected_mergeinfo_output, 4392 expected_elision_output, 4393 expected_disk, 4394 expected_status, 4395 expected_skip, 4396 check_props=True) 4397 4398 # Merge r6 into A_COPY/D/H/omega, it should inherit it's nearest 4399 # ancestor's (A_COPY/D) mergeinfo (Issue #2733 with a file as the 4400 # merge target). 4401 expected_skip = wc.State(omega_COPY_path, { }) 4402 # run_and_verify_merge doesn't support merging to a file WCPATH 4403 # so use run_and_verify_svn. 4404 ### TODO: We can use run_and_verify_merge() here now. 4405 svntest.actions.run_and_verify_svn( 4406 expected_merge_output([[6]], 4407 ['U ' + omega_COPY_path + '\n', 4408 ' G ' + omega_COPY_path + '\n']), 4409 [], 'merge', '-c6', 4410 sbox.repo_url + '/A/D/H/omega', 4411 omega_COPY_path) 4412 4413 # Check that mergeinfo was properly set on A_COPY/D/H/omega 4414 svntest.actions.run_and_verify_svn(["/A/D/H/omega:3-4,6\n"], 4415 [], 4416 'propget', SVN_PROP_MERGEINFO, 4417 omega_COPY_path) 4418 4419 # Given a merge target *without* any of the following: 4420 # 4421 # 1) Explicit mergeinfo set on itself in the WC 4422 # 2) Any WC ancestor to inherit mergeinfo from 4423 # 3) Any mergeinfo for the target in the repository 4424 # 4425 # Check that the target still inherits mergeinfo from it's nearest 4426 # repository ancestor. 4427 # 4428 # Commit all the merges thus far 4429 expected_output = wc.State(wc_dir, { 4430 'A_COPY' : Item(verb='Sending'), 4431 'A_COPY/B' : Item(verb='Sending'), 4432 'A_COPY/B/E/beta' : Item(verb='Sending'), 4433 'A_COPY/D' : Item(verb='Sending'), 4434 'A_COPY/D/G/rho' : Item(verb='Sending'), 4435 'A_COPY/D/H/omega' : Item(verb='Sending'), 4436 'A_COPY/D/H/psi' : Item(verb='Sending'), 4437 }) 4438 wc_status.tweak('A_COPY', 'A_COPY/B', 'A_COPY/B/E/beta', 'A_COPY/D', 4439 'A_COPY/D/G/rho', 'A_COPY/D/H/omega', 'A_COPY/D/H/psi', 4440 wc_rev=7) 4441 svntest.actions.run_and_verify_commit(wc_dir, 4442 expected_output, 4443 wc_status) 4444 4445 # In single-db mode you can't create a disconnected working copy by just 4446 # copying a subdir 4447 4448 ## Copy the subtree A_COPY/B/E from the working copy, making the 4449 ## disconnected WC E_only. 4450 #other_wc = sbox.add_wc_path('E_only') 4451 #svntest.actions.duplicate_dir(E_COPY_path, other_wc) 4452 # 4453 ## Update the disconnected WC it so it will get the most recent mergeinfo 4454 ## from the repos when merging. 4455 #svntest.actions.run_and_verify_svn(exp_noop_up_out(7), [], 'up', 4456 # other_wc) 4457 # 4458 ## Merge r5:4 into the root of the disconnected WC. 4459 ## E_only has no explicit mergeinfo and since it's the root of the WC 4460 ## cannot inherit any mergeinfo from a working copy ancestor path. Nor 4461 ## does it have any mergeinfo explicitly set on it in the repository. 4462 ## An ancestor path on the repository side, A_COPY/B does have the merge 4463 ## info '/A/B:5' however and E_only should inherit this, resulting in 4464 ## empty mergeinfo after the removal of r5 (A_COPY has mergeinfo of 4465 ## '/A:3' so this empty mergeinfo is needed to override that. 4466 #expected_output = wc.State(other_wc, 4467 # {'beta' : Item(status='U ')}) 4468 #expected_mergeinfo_output = wc.State(other_wc, { 4469 # '' : Item(status=' G') 4470 # }) 4471 #expected_elision_output = wc.State(other_wc, { 4472 # }) 4473 #expected_status = wc.State(other_wc, { 4474 # '' : Item(status=' M', wc_rev=7), 4475 # 'alpha' : Item(status=' ', wc_rev=7), 4476 # 'beta' : Item(status='M ', wc_rev=7), 4477 # }) 4478 #expected_disk = wc.State('', { 4479 # '' : Item(props={SVN_PROP_MERGEINFO : ''}), 4480 # 'alpha' : Item("This is the file 'alpha'.\n"), 4481 # 'beta' : Item("This is the file 'beta'.\n"), 4482 # }) 4483 #expected_skip = wc.State(other_wc, { }) 4484 # 4485 #svntest.actions.run_and_verify_merge(other_wc, '5', '4', 4486 # sbox.repo_url + '/A/B/E', None, 4487 # expected_output, 4488 # expected_mergeinfo_output, 4489 # expected_elision_output, 4490 # expected_disk, 4491 # expected_status, 4492 # expected_skip, 4493 # check_props=True) 4494 4495#---------------------------------------------------------------------- 4496@SkipUnless(server_has_mergeinfo) 4497def mergeinfo_elision(sbox): 4498 "mergeinfo elides to ancestor with identical info" 4499 4500 # When a merge would result in mergeinfo on a target which is identical 4501 # to mergeinfo (local or committed) on one of the node's ancestors (the 4502 # nearest ancestor takes precedence), then the mergeinfo elides from the 4503 # target to the nearest ancestor (e.g. no mergeinfo is set on the target 4504 # or committed mergeinfo is removed). 4505 4506 sbox.build() 4507 wc_dir = sbox.wc_dir 4508 wc_disk, wc_status = set_up_branch(sbox) 4509 4510 # Some paths we'll care about 4511 A_COPY_path = sbox.ospath('A_COPY') 4512 beta_COPY_path = sbox.ospath('A_COPY/B/E/beta') 4513 G_COPY_path = sbox.ospath('A_COPY/D/G') 4514 4515 # Now start merging... 4516 4517 # Merge r5 into A_COPY/B/E/beta. 4518 expected_skip = wc.State(beta_COPY_path, { }) 4519 4520 # run_and_verify_merge doesn't support merging to a file WCPATH 4521 # so use run_and_verify_svn. 4522 ### TODO: We can use run_and_verify_merge() here now. 4523 svntest.actions.run_and_verify_svn( 4524 expected_merge_output([[5]], 4525 ['U ' + beta_COPY_path + '\n', 4526 ' U ' + beta_COPY_path + '\n']), 4527 [], 'merge', '-c5', 4528 sbox.repo_url + '/A/B/E/beta', 4529 beta_COPY_path) 4530 4531 # Check beta's status and props. 4532 expected_status = wc.State(beta_COPY_path, { 4533 '' : Item(status='MM', wc_rev=2), 4534 }) 4535 svntest.actions.run_and_verify_status(beta_COPY_path, expected_status) 4536 4537 svntest.actions.run_and_verify_svn(["/A/B/E/beta:5\n"], [], 4538 'propget', SVN_PROP_MERGEINFO, 4539 beta_COPY_path) 4540 4541 # Commit the merge 4542 expected_output = wc.State(wc_dir, { 4543 'A_COPY/B/E/beta' : Item(verb='Sending'), 4544 }) 4545 wc_status.tweak('A_COPY/B/E/beta', wc_rev=7) 4546 svntest.actions.run_and_verify_commit(wc_dir, 4547 expected_output, 4548 wc_status) 4549 4550 # Update A_COPY to get all paths to the same working revision. 4551 svntest.actions.run_and_verify_svn(exp_noop_up_out(7), [], 4552 'up', wc_dir) 4553 wc_status.tweak(wc_rev=7) 4554 4555 # Merge r4 into A_COPY/D/G. 4556 expected_output = wc.State(G_COPY_path, { 4557 'rho' : Item(status='U ') 4558 }) 4559 expected_mergeinfo_output = wc.State(G_COPY_path, { 4560 '' : Item(status=' U') 4561 }) 4562 expected_elision_output = wc.State(G_COPY_path, { 4563 }) 4564 expected_status = wc.State(G_COPY_path, { 4565 '' : Item(status=' M', wc_rev=7), 4566 'pi' : Item(status=' ', wc_rev=7), 4567 'rho' : Item(status='M ', wc_rev=7), 4568 'tau' : Item(status=' ', wc_rev=7), 4569 }) 4570 expected_disk = wc.State('', { 4571 '' : Item(props={SVN_PROP_MERGEINFO : '/A/D/G:4'}), 4572 'pi' : Item("This is the file 'pi'.\n"), 4573 'rho' : Item("New content"), 4574 'tau' : Item("This is the file 'tau'.\n"), 4575 }) 4576 expected_skip = wc.State(G_COPY_path, { }) 4577 4578 svntest.actions.run_and_verify_merge(G_COPY_path, '3', '4', 4579 sbox.repo_url + '/A/D/G', None, 4580 expected_output, 4581 expected_mergeinfo_output, 4582 expected_elision_output, 4583 expected_disk, 4584 expected_status, 4585 expected_skip, 4586 check_props=True) 4587 4588 # Merge r3:6 into A_COPY. The merge doesn't touch either of A_COPY's 4589 # subtrees with explicit mergeinfo, so those are left alone. 4590 expected_output = wc.State(A_COPY_path, { 4591 'D/H/omega' : Item(status='U ') 4592 }) 4593 expected_mergeinfo_output = wc.State(A_COPY_path, { 4594 '' : Item(status=' U') 4595 }) 4596 expected_elision_output = wc.State(A_COPY_path, { 4597 }) 4598 expected_status = wc.State(A_COPY_path, { 4599 '' : Item(status=' M', wc_rev=7), 4600 'B' : Item(status=' ', wc_rev=7), 4601 'mu' : Item(status=' ', wc_rev=7), 4602 'B/E' : Item(status=' ', wc_rev=7), 4603 'B/E/alpha' : Item(status=' ', wc_rev=7), 4604 'B/E/beta' : Item(status=' ', wc_rev=7), 4605 'B/lambda' : Item(status=' ', wc_rev=7), 4606 'B/F' : Item(status=' ', wc_rev=7), 4607 'C' : Item(status=' ', wc_rev=7), 4608 'D' : Item(status=' ', wc_rev=7), 4609 'D/G' : Item(status=' M', wc_rev=7), 4610 'D/G/pi' : Item(status=' ', wc_rev=7), 4611 'D/G/rho' : Item(status='M ', wc_rev=7), 4612 'D/G/tau' : Item(status=' ', wc_rev=7), 4613 'D/gamma' : Item(status=' ', wc_rev=7), 4614 'D/H' : Item(status=' ', wc_rev=7), 4615 'D/H/chi' : Item(status=' ', wc_rev=7), 4616 'D/H/psi' : Item(status=' ', wc_rev=7), 4617 'D/H/omega' : Item(status='M ', wc_rev=7), 4618 }) 4619 expected_disk = wc.State('', { 4620 '' : Item(props={SVN_PROP_MERGEINFO : '/A:4-6'}), 4621 'B' : Item(), 4622 'mu' : Item("This is the file 'mu'.\n"), 4623 'B/E' : Item(), 4624 'B/E/alpha' : Item("This is the file 'alpha'.\n"), 4625 'B/E/beta' : Item("New content", 4626 props={SVN_PROP_MERGEINFO : '/A/B/E/beta:5'}), 4627 'B/lambda' : Item("This is the file 'lambda'.\n"), 4628 'B/F' : Item(), 4629 'C' : Item(), 4630 'D' : Item(), 4631 'D/G' : Item(props={SVN_PROP_MERGEINFO : '/A/D/G:4'}), 4632 'D/G/pi' : Item("This is the file 'pi'.\n"), 4633 'D/G/rho' : Item("New content"), 4634 'D/G/tau' : Item("This is the file 'tau'.\n"), 4635 'D/gamma' : Item("This is the file 'gamma'.\n"), 4636 'D/H' : Item(), 4637 'D/H/chi' : Item("This is the file 'chi'.\n"), 4638 'D/H/psi' : Item("This is the file 'psi'.\n"), 4639 'D/H/omega' : Item("New content"), 4640 }) 4641 expected_skip = wc.State(A_COPY_path, { }) 4642 svntest.actions.run_and_verify_merge(A_COPY_path, '3', '6', 4643 sbox.repo_url + '/A', None, 4644 expected_output, 4645 expected_mergeinfo_output, 4646 expected_elision_output, 4647 expected_disk, 4648 expected_status, 4649 expected_skip, 4650 check_props=True) 4651 # New repeat the above merge but with the --record-only option. 4652 # This would result in identical mergeinfo 4653 # (r4-6) on A_COPY and two of its descendants, A_COPY/D/G and 4654 # A_COPY/B/E/beta, so the mergeinfo on the latter two should elide 4655 # to A_COPY. In the case of A_COPY/D/G this means its wholly uncommitted 4656 # mergeinfo is removed leaving no prop mods. In the case of 4657 # A_COPY/B/E/beta its committed mergeinfo prop is removed leaving a prop 4658 # change. 4659 4660 # to A_COPY. 4661 expected_output = wc.State(A_COPY_path, {}) 4662 expected_mergeinfo_output = wc.State(A_COPY_path, { 4663 '' : Item(status=' G'), 4664 'D/G' : Item(status=' G'), 4665 'B/E/beta' : Item(status=' U'), 4666 }) 4667 expected_elision_output = wc.State(A_COPY_path, { 4668 }) 4669 expected_elision_output = wc.State(A_COPY_path, { 4670 'B/E/beta' : Item(status=' U'), 4671 'D/G' : Item(status=' U'), 4672 }) 4673 expected_status.tweak('B/E/beta', status=' M') 4674 expected_status.tweak('D/G', status=' ') 4675 expected_disk.tweak('B/E/beta', 'D/G', props={}) 4676 svntest.actions.run_and_verify_merge(A_COPY_path, '3', '6', 4677 sbox.repo_url + '/A', None, 4678 expected_output, 4679 expected_mergeinfo_output, 4680 expected_elision_output, 4681 expected_disk, 4682 expected_status, 4683 expected_skip, 4684 [], True, True, 4685 '--record-only', 4686 A_COPY_path) 4687 4688 # Reverse merge r5 out of A_COPY/B/E/beta. The mergeinfo on 4689 # A_COPY/B/E/beta which previously elided will now return, 4690 # minus r5 of course. 4691 expected_skip = wc.State(beta_COPY_path, { }) 4692 4693 # run_and_verify_merge doesn't support merging to a file WCPATH 4694 # so use run_and_verify_svn. 4695 ### TODO: We can use run_and_verify_merge() here now. 4696 svntest.actions.run_and_verify_svn( 4697 expected_merge_output([[-5]], 4698 ['U ' + beta_COPY_path + '\n', 4699 ' G ' + beta_COPY_path + '\n']), 4700 [], 'merge', '-c-5', 4701 sbox.repo_url + '/A/B/E/beta', 4702 beta_COPY_path) 4703 4704 # Check beta's status and props. 4705 expected_status = wc.State(beta_COPY_path, { 4706 '' : Item(status='MM', wc_rev=7), 4707 }) 4708 svntest.actions.run_and_verify_status(beta_COPY_path, expected_status) 4709 4710 svntest.actions.run_and_verify_svn(["/A/B/E/beta:4,6\n"], [], 4711 'propget', SVN_PROP_MERGEINFO, 4712 beta_COPY_path) 4713 4714 # Merge r5 back into A_COPY/B/E/beta. Now the mergeinfo on the merge 4715 # target (A_COPY/B/E/beta) is identical to it's nearest ancestor with 4716 # mergeinfo (A_COPY) and so the former should elide. 4717 # run_and_verify_merge doesn't support merging to a file WCPATH 4718 # so use run_and_verify_svn. 4719 svntest.actions.run_and_verify_svn( 4720 expected_merge_output([[5]], 4721 ['G ' + beta_COPY_path + '\n', 4722 ' G ' + beta_COPY_path + '\n', # Update mergeinfo 4723 ' U ' + beta_COPY_path + '\n',], # Elide mereginfo, 4724 elides=True), 4725 [], 'merge', '-c5', 4726 sbox.repo_url + '/A/B/E/beta', 4727 beta_COPY_path) 4728 4729 # Check beta's status and props. 4730 expected_status = wc.State(beta_COPY_path, { 4731 '' : Item(status=' M', wc_rev=7), 4732 }) 4733 svntest.actions.run_and_verify_status(beta_COPY_path, expected_status) 4734 4735 # Once again A_COPY/B/E/beta has no mergeinfo. 4736 svntest.actions.run_and_verify_svn([], '.*W200017: Property.*not found', 4737 'propget', SVN_PROP_MERGEINFO, 4738 beta_COPY_path) 4739 4740#---------------------------------------------------------------------- 4741@SkipUnless(server_has_mergeinfo) 4742def mergeinfo_inheritance_and_discontinuous_ranges(sbox): 4743 "discontinuous merges produce correct mergeinfo" 4744 4745 # When a merge target has no explicit mergeinfo and is subject 4746 # to multiple merges, the resulting mergeinfo on the target 4747 # should reflect the combination of the inherited mergeinfo 4748 # with each merge performed. 4749 # 4750 # Also tests implied merge source and target when only a revision 4751 # range is specified. 4752 4753 sbox.build() 4754 wc_dir = sbox.wc_dir 4755 4756 # Some paths we'll care about 4757 A_url = sbox.repo_url + '/A' 4758 A_COPY_path = sbox.ospath('A_COPY') 4759 D_COPY_path = sbox.ospath('A_COPY/D') 4760 A_COPY_rho_path = sbox.ospath('A_COPY/D/G/rho') 4761 4762 expected_disk, expected_status = set_up_branch(sbox) 4763 4764 # Merge r4 into A_COPY 4765 saved_cwd = os.getcwd() 4766 4767 os.chdir(A_COPY_path) 4768 svntest.actions.run_and_verify_svn( 4769 expected_merge_output([[4]], 4770 ['U ' + os.path.join("D", "G", "rho") + '\n', 4771 ' U .\n']), 4772 [], 'merge', '-c4', A_url) 4773 os.chdir(saved_cwd) 4774 4775 # Check the results of the merge. 4776 expected_status.tweak("A_COPY", status=' M') 4777 expected_status.tweak("A_COPY/D/G/rho", status='M ') 4778 svntest.actions.run_and_verify_status(wc_dir, expected_status) 4779 svntest.actions.run_and_verify_svn(["/A:4\n"], [], 4780 'propget', SVN_PROP_MERGEINFO, 4781 A_COPY_path) 4782 4783 # Merge r2:6 into A_COPY/D 4784 # 4785 # A_COPY/D should inherit the mergeinfo '/A:4' from A_COPY 4786 # combine it with the discontinous merges performed directly on 4787 # it (A/D/ 2:3 and A/D 4:6) resulting in '/A/D:3-6'. 4788 expected_output = wc.State(D_COPY_path, { 4789 'H/psi' : Item(status='U '), 4790 'H/omega' : Item(status='U '), 4791 }) 4792 expected_mergeinfo_output = wc.State(D_COPY_path, { 4793 '' : Item(status=' G'), 4794 }) 4795 expected_elision_output = wc.State(D_COPY_path, { 4796 }) 4797 expected_status = wc.State(D_COPY_path, { 4798 '' : Item(status=' M', wc_rev=2), 4799 'G' : Item(status=' ', wc_rev=2), 4800 'G/pi' : Item(status=' ', wc_rev=2), 4801 'G/rho' : Item(status='M ', wc_rev=2), 4802 'G/tau' : Item(status=' ', wc_rev=2), 4803 'H' : Item(status=' ', wc_rev=2), 4804 'H/chi' : Item(status=' ', wc_rev=2), 4805 'H/psi' : Item(status='M ', wc_rev=2), 4806 'H/omega' : Item(status='M ', wc_rev=2), 4807 'gamma' : Item(status=' ', wc_rev=2), 4808 }) 4809 expected_disk = wc.State('', { 4810 '' : Item(props={SVN_PROP_MERGEINFO : '/A/D:3-6'}), 4811 'G' : Item(), 4812 'G/pi' : Item("This is the file 'pi'.\n"), 4813 'G/rho' : Item("New content"), 4814 'G/tau' : Item("This is the file 'tau'.\n"), 4815 'H' : Item(), 4816 'H/chi' : Item("This is the file 'chi'.\n"), 4817 'H/psi' : Item("New content"), 4818 'H/omega' : Item("New content"), 4819 'gamma' : Item("This is the file 'gamma'.\n") 4820 }) 4821 expected_skip = wc.State(D_COPY_path, { }) 4822 4823 svntest.actions.run_and_verify_merge(D_COPY_path, '2', '6', 4824 sbox.repo_url + '/A/D', None, 4825 expected_output, 4826 expected_mergeinfo_output, 4827 expected_elision_output, 4828 expected_disk, 4829 expected_status, 4830 expected_skip, 4831 check_props=True) 4832 4833 # Wipe the memory of a portion of the previous merge... 4834 ### It'd be nice to use 'merge --record-only' here, but we can't (yet) 4835 ### wipe all ranges for a file due to the bug pointed out in r864719. 4836 mu_copy_path = os.path.join(A_COPY_path, 'mu') 4837 svntest.actions.run_and_verify_svn(["property '" + SVN_PROP_MERGEINFO 4838 + "' set on '" + 4839 mu_copy_path + "'\n"], [], 'propset', 4840 SVN_PROP_MERGEINFO, '', mu_copy_path) 4841 # ...and confirm that we can commit the wiped mergeinfo... 4842 expected_output = wc.State(wc_dir, { 4843 'A_COPY/mu' : Item(verb='Sending'), 4844 }) 4845 svntest.actions.run_and_verify_commit(wc_dir, 4846 expected_output, 4847 None, 4848 [], 4849 mu_copy_path) 4850 # ...and that the presence of the property is retained, even when 4851 # the value has been wiped. 4852 svntest.actions.run_and_verify_svn(['\n'], [], 'propget', 4853 SVN_PROP_MERGEINFO, mu_copy_path) 4854 4855#---------------------------------------------------------------------- 4856@SkipUnless(server_has_mergeinfo) 4857@Issue(2754) 4858def merge_to_target_with_copied_children(sbox): 4859 "merge works when target has copied children" 4860 4861 # Test for Issue #2754 Can't merge to target with copied/moved children 4862 4863 sbox.build() 4864 wc_dir = sbox.wc_dir 4865 expected_disk, expected_status = set_up_branch(sbox) 4866 4867 # Some paths we'll care about 4868 D_COPY_path = sbox.ospath('A_COPY/D') 4869 G_COPY_path = sbox.ospath('A_COPY/D/G') 4870 rho_COPY_COPY_path = sbox.ospath('A_COPY/D/G/rho_copy') 4871 4872 # URL to URL copy A_COPY/D/G/rho to A_COPY/D/G/rho_copy 4873 svntest.actions.run_and_verify_svn(None, [], 'copy', 4874 sbox.repo_url + '/A_COPY/D/G/rho', 4875 sbox.repo_url + '/A_COPY/D/G/rho_copy', 4876 '-m', 'copy') 4877 4878 # Update WC. 4879 expected_output = wc.State(wc_dir, 4880 {'A_COPY/D/G/rho_copy' : Item(status='A ')}) 4881 expected_disk.add({ 4882 'A_COPY/D/G/rho_copy' : Item("This is the file 'rho'.\n", props={}) 4883 }) 4884 expected_status.tweak(wc_rev=7) 4885 expected_status.add({'A_COPY/D/G/rho_copy' : Item(status=' ', wc_rev=7)}) 4886 svntest.actions.run_and_verify_update(wc_dir, 4887 expected_output, 4888 expected_disk, 4889 expected_status, 4890 check_props=True) 4891 4892 # Merge r4 into A_COPY/D/G/rho_copy. 4893 svntest.actions.run_and_verify_svn( 4894 expected_merge_output([[4]], 4895 ['U ' + rho_COPY_COPY_path + '\n', 4896 ' U ' + rho_COPY_COPY_path + '\n']), 4897 [], 'merge', '-c4', 4898 sbox.repo_url + '/A/D/G/rho', 4899 rho_COPY_COPY_path) 4900 4901 # Merge r3:5 into A_COPY/D/G. 4902 expected_output = wc.State(G_COPY_path, { 4903 'rho' : Item(status='U ') 4904 }) 4905 expected_mergeinfo_output = wc.State(G_COPY_path, { 4906 '' : Item(status=' U'), 4907 }) 4908 expected_elision_output = wc.State(G_COPY_path, { 4909 }) 4910 expected_status = wc.State(G_COPY_path, { 4911 '' : Item(status=' M', wc_rev=7), 4912 'pi' : Item(status=' ', wc_rev=7), 4913 'rho' : Item(status='M ', wc_rev=7), 4914 'rho_copy' : Item(status='MM', wc_rev=7), 4915 'tau' : Item(status=' ', wc_rev=7), 4916 }) 4917 expected_disk = wc.State('', { 4918 '' : Item(props={SVN_PROP_MERGEINFO : '/A/D/G:4-5'}), 4919 'pi' : Item("This is the file 'pi'.\n"), 4920 'rho' : Item("New content"), 4921 'rho_copy' : Item("New content", 4922 props={SVN_PROP_MERGEINFO : '/A/D/G/rho:4'}), 4923 'tau' : Item("This is the file 'tau'.\n"), 4924 }) 4925 expected_skip = wc.State(G_COPY_path, { }) 4926 svntest.actions.run_and_verify_merge(G_COPY_path, '3', '5', 4927 sbox.repo_url + '/A/D/G', None, 4928 expected_output, 4929 expected_mergeinfo_output, 4930 expected_elision_output, 4931 expected_disk, 4932 expected_status, 4933 expected_skip, 4934 check_props=True) 4935 4936#---------------------------------------------------------------------- 4937@SkipUnless(server_has_mergeinfo) 4938@Issue(3188) 4939def merge_to_switched_path(sbox): 4940 "merge to switched path does not inherit or elide" 4941 4942 # When the target of a merge is a switched path we don't inherit WC 4943 # mergeinfo from above the target or attempt to elide the mergeinfo 4944 # set on the target as a result of the merge. 4945 4946 sbox.build() 4947 wc_dir = sbox.wc_dir 4948 wc_disk, wc_status = set_up_branch(sbox) 4949 4950 # Some paths we'll care about 4951 A_COPY_path = sbox.ospath('A_COPY') 4952 A_COPY_D_path = sbox.ospath('A_COPY/D') 4953 G_COPY_path = sbox.ospath('A/D/G_COPY') 4954 A_COPY_D_G_path = sbox.ospath('A_COPY/D/G') 4955 A_COPY_D_G_rho_path = sbox.ospath('A_COPY/D/G/rho') 4956 4957 expected = svntest.verify.UnorderedOutput( 4958 ["A " + G_COPY_path + "\n", 4959 "A " + os.path.join(G_COPY_path, "pi") + "\n", 4960 "A " + os.path.join(G_COPY_path, "rho") + "\n", 4961 "A " + os.path.join(G_COPY_path, "tau") + "\n", 4962 ]) 4963 4964 # r7 - Copy A/D/G to A/D/G_COPY and commit. 4965 svntest.actions.run_and_verify_svn(expected, [], 'copy', 4966 sbox.repo_url + "/A/D/G", 4967 G_COPY_path) 4968 4969 expected_output = wc.State(wc_dir, {'A/D/G_COPY' : Item(verb='Adding')}) 4970 wc_status.add({ 4971 "A/D/G_COPY" : Item(status=' ', wc_rev=7), 4972 "A/D/G_COPY/pi" : Item(status=' ', wc_rev=7), 4973 "A/D/G_COPY/rho" : Item(status=' ', wc_rev=7), 4974 "A/D/G_COPY/tau" : Item(status=' ', wc_rev=7), 4975 }) 4976 4977 svntest.actions.run_and_verify_commit(wc_dir, expected_output, wc_status) 4978 4979 # r8 - modify and commit A/D/G_COPY/rho 4980 svntest.main.file_write(sbox.ospath('A/D/G_COPY/rho'), 4981 "New *and* improved rho content") 4982 expected_output = wc.State(wc_dir, {'A/D/G_COPY/rho' : Item(verb='Sending')}) 4983 wc_status.tweak('A/D/G_COPY/rho', wc_rev=8) 4984 svntest.actions.run_and_verify_commit(wc_dir, expected_output, wc_status) 4985 4986 # Switch A_COPY/D/G to A/D/G. 4987 wc_disk.add({ 4988 "A" : Item(), 4989 "A/D/G_COPY" : Item(), 4990 "A/D/G_COPY/pi" : Item("This is the file 'pi'.\n"), 4991 "A/D/G_COPY/rho" : Item("New *and* improved rho content"), 4992 "A/D/G_COPY/tau" : Item("This is the file 'tau'.\n"), 4993 }) 4994 wc_disk.tweak('A_COPY/D/G/rho',contents="New content") 4995 wc_status.tweak("A_COPY/D/G", wc_rev=8, switched='S') 4996 wc_status.tweak("A_COPY/D/G/pi", wc_rev=8) 4997 wc_status.tweak("A_COPY/D/G/rho", wc_rev=8) 4998 wc_status.tweak("A_COPY/D/G/tau", wc_rev=8) 4999 expected_output = svntest.wc.State(sbox.wc_dir, { 5000 "A_COPY/D/G/rho" : Item(status='U '), 5001 }) 5002 svntest.actions.run_and_verify_switch(sbox.wc_dir, A_COPY_D_G_path, 5003 sbox.repo_url + "/A/D/G", 5004 expected_output, wc_disk, wc_status, 5005 [], 1) 5006 5007 # Update working copy to allow elision (if any). 5008 svntest.actions.run_and_verify_svn(exp_noop_up_out(8), [], 5009 'up', wc_dir) 5010 5011 # Set some mergeinfo on a working copy parent of our switched subtree 5012 # A_COPY/D/G. Because the subtree is switched it should *not* inherit 5013 # this mergeinfo. 5014 svntest.actions.run_and_verify_svn(["property '" + SVN_PROP_MERGEINFO + 5015 "' set on '" + A_COPY_path + "'" + 5016 "\n"], [], 'ps', SVN_PROP_MERGEINFO, 5017 '/A:4', A_COPY_path) 5018 5019 # Merge r8 from A/D/G_COPY into our switched target A_COPY/D/G. 5020 # A_COPY/D/G should get mergeinfo for r8 as a result of the merge, 5021 # but because it's switched should not inherit the mergeinfo from 5022 # its nearest WC ancestor with mergeinfo (A_COPY: svn:mergeinfo : /A:4) 5023 expected_output = wc.State(A_COPY_D_G_path, { 5024 'rho' : Item(status='U ') 5025 }) 5026 expected_mergeinfo_output = wc.State(A_COPY_D_G_path, { 5027 '' : Item(status=' U') 5028 }) 5029 expected_elision_output = wc.State(A_COPY_D_G_path, { 5030 }) 5031 # Note: A_COPY/D/G won't show as switched. 5032 expected_status = wc.State(A_COPY_D_G_path, { 5033 '' : Item(status=' M', wc_rev=8), 5034 'pi' : Item(status=' ', wc_rev=8), 5035 'rho' : Item(status='M ', wc_rev=8), 5036 'tau' : Item(status=' ', wc_rev=8), 5037 }) 5038 expected_status.tweak('', switched='S') 5039 expected_disk = wc.State('', { 5040 '' : Item(props={SVN_PROP_MERGEINFO : '/A/D/G_COPY:8'}), 5041 'pi' : Item("This is the file 'pi'.\n"), 5042 'rho' : Item("New *and* improved rho content"), 5043 'tau' : Item("This is the file 'tau'.\n"), 5044 }) 5045 expected_skip = wc.State(A_COPY_D_G_path, { }) 5046 5047 svntest.actions.run_and_verify_merge(A_COPY_D_G_path, '7', '8', 5048 sbox.repo_url + '/A/D/G_COPY', None, 5049 expected_output, 5050 expected_mergeinfo_output, 5051 expected_elision_output, 5052 expected_disk, 5053 expected_status, expected_skip, 5054 check_props=True) 5055 5056 # Check that the mergeinfo set on a switched target can elide to the 5057 # repository. 5058 # 5059 # Specifically this is testing the "switched target" portions of 5060 # issue #3188 'Mergeinfo on switched targets/subtrees should 5061 # elide to repos'. 5062 # 5063 # Revert the previous merge and manually set 'svn:mergeinfo : /A/D:4' 5064 # on 'merge_tests-1\A_COPY\D'. Now merge -c-4 from /A/D/G into A_COPY/D/G. 5065 # This should produce no mergeinfo on A_COPY/D/G'. If the A_COPY/D/G was 5066 # unswitched this merge would normally set empty mergeinfo on A_COPY/D/G, 5067 # but as it is switched this empty mergeinfo just elides to the 5068 # repository (empty mergeinfo on a path can elide if that path doesn't 5069 # inherit *any* mergeinfo). 5070 svntest.actions.run_and_verify_svn(["Reverted '" + A_COPY_path+ "'\n", 5071 "Reverted '" + A_COPY_D_G_path+ "'\n", 5072 "Reverted '" + A_COPY_D_G_rho_path + 5073 "'\n"], 5074 [], 'revert', '-R', wc_dir) 5075 svntest.actions.run_and_verify_svn(["property '" + SVN_PROP_MERGEINFO + 5076 "' set on '" + A_COPY_D_path+ "'" + 5077 "\n"], [], 'ps', SVN_PROP_MERGEINFO, 5078 '/A/D:4', A_COPY_D_path) 5079 svntest.actions.run_and_verify_svn( 5080 expected_merge_output([[-4]], 5081 ['U ' + A_COPY_D_G_rho_path + '\n', 5082 ' U ' + A_COPY_D_G_path + '\n'], 5083 elides=True), 5084 [], 'merge', '-c-4', 5085 sbox.repo_url + '/A/D/G_COPY', 5086 A_COPY_D_G_path) 5087 wc_status.tweak("A_COPY/D", status=' M') 5088 wc_status.tweak("A_COPY/D/G/rho", status='M ') 5089 wc_status.tweak(wc_rev=8) 5090 svntest.actions.run_and_verify_status(wc_dir, wc_status) 5091 check_mergeinfo_recursively(A_COPY_D_path, 5092 { A_COPY_D_path : '/A/D:4' }) 5093 5094#---------------------------------------------------------------------- 5095# Test for issues 5096# 5097# 2823: Account for mergeinfo differences for switched 5098# directories when gathering mergeinfo 5099# 5100# 2839: Support non-inheritable mergeinfo revision ranges 5101# 5102# 3187: Reverse merges don't work properly with 5103# non-inheritable ranges. 5104# 5105# 3188: Mergeinfo on switched targets/subtrees should 5106# elide to repos 5107@SkipUnless(server_has_mergeinfo) 5108@Issue(2823,2839,3187,3188,4056) 5109def merge_to_path_with_switched_children(sbox): 5110 "merge to path with switched children" 5111 5112 # Merging to a target with switched children requires special handling 5113 # to keep mergeinfo correct: 5114 # 5115 # 1) If the target of a merge has switched children without explicit 5116 # mergeinfo, the switched children should get mergeinfo set on 5117 # them as a result of the merge. This mergeinfo includes the 5118 # mergeinfo resulting from the merge *and* any mergeinfo inherited 5119 # from the repos for the switched path. 5120 # 5121 # 2) Mergeinfo on switched children should never elide. 5122 # 5123 # 3) The path the switched child overrides cannot be modified by the 5124 # merge (it isn't present in the WC) so should not inherit any 5125 # mergeinfo added as a result of the merge. To prevent this, the 5126 # immediate parent of any switched child should have non-inheritable 5127 # mergeinfo added/modified for the merge performed. 5128 # 5129 # 4) Because of 3, siblings of switched children will not inherit the 5130 # mergeinfo resulting from the merge, so must get their own, full set 5131 # of mergeinfo. 5132 5133 sbox.build() 5134 wc_dir = sbox.wc_dir 5135 wc_disk, wc_status = set_up_branch(sbox, False, 3) 5136 5137 # Some paths we'll care about 5138 D_path = sbox.ospath('A/D') 5139 A_COPY_path = sbox.ospath('A_COPY') 5140 A_COPY_beta_path = sbox.ospath('A_COPY/B/E/beta') 5141 A_COPY_chi_path = sbox.ospath('A_COPY/D/H/chi') 5142 A_COPY_omega_path = sbox.ospath('A_COPY/D/H/omega') 5143 A_COPY_psi_path = sbox.ospath('A_COPY/D/H/psi') 5144 A_COPY_G_path = sbox.ospath('A_COPY/D/G') 5145 A_COPY_rho_path = sbox.ospath('A_COPY/D/G/rho') 5146 A_COPY_H_path = sbox.ospath('A_COPY/D/H') 5147 A_COPY_D_path = sbox.ospath('A_COPY/D') 5148 A_COPY_gamma_path = sbox.ospath('A_COPY/D/gamma') 5149 H_COPY_2_path = sbox.ospath('A_COPY_2/D/H') 5150 5151 svntest.actions.run_and_verify_svn(exp_noop_up_out(8), [], 'up', 5152 wc_dir) 5153 wc_status.tweak(wc_rev=8) 5154 5155 # Switch a file and dir path in the branch: 5156 5157 # Switch A_COPY/D/G to A_COPY_2/D/G. 5158 wc_status.tweak("A_COPY/D/G", switched='S') 5159 expected_output = svntest.wc.State(sbox.wc_dir, {}) 5160 svntest.actions.run_and_verify_switch(sbox.wc_dir, A_COPY_G_path, 5161 sbox.repo_url + "/A_COPY_2/D/G", 5162 expected_output, wc_disk, wc_status, 5163 [], 1) 5164 5165 # Switch A_COPY/D/G/rho to A_COPY_3/D/G/rho. 5166 wc_status.tweak("A_COPY/D/G/rho", switched='S') 5167 expected_output = svntest.wc.State(sbox.wc_dir, {}) 5168 svntest.actions.run_and_verify_switch(sbox.wc_dir, A_COPY_rho_path, 5169 sbox.repo_url + "/A_COPY_3/D/G/rho", 5170 expected_output, wc_disk, wc_status, 5171 [], 1) 5172 5173 # Switch A_COPY/D/H/psi to A_COPY_2/D/H/psi. 5174 wc_status.tweak("A_COPY/D/H/psi", switched='S') 5175 expected_output = svntest.wc.State(sbox.wc_dir, {}) 5176 svntest.actions.run_and_verify_switch(sbox.wc_dir, A_COPY_psi_path, 5177 sbox.repo_url + "/A_COPY_2/D/H/psi", 5178 expected_output, wc_disk, wc_status, 5179 [], 1) 5180 5181 # Target with switched file child: 5182 # 5183 # Merge r8 from A/D/H into A_COPY/D/H. The switched child of 5184 # A_COPY/D/H, file A_COPY/D/H/psi (which has no mergeinfo prior 5185 # to the merge), is unaffected by the merge so does not get it's 5186 # own explicit mergeinfo. 5187 # 5188 # A_COPY/D/H/psi's parent A_COPY/D/H has no pre-exiting explicit 5189 # mergeinfo so should get its own mergeinfo, the non-inheritable 5190 # r8 resulting from the merge. 5191 # 5192 # A_COPY/D/H/psi's unswitched sibling, A_COPY/D/H/omega is affected 5193 # by the merge but won't inherit r8 from A_COPY/D/H, so it needs its 5194 # own mergeinfo. 5195 expected_output = wc.State(A_COPY_H_path, { 5196 'omega' : Item(status='U '), 5197 }) 5198 expected_mergeinfo_output = wc.State(A_COPY_H_path, { 5199 '' : Item(status=' U'), 5200 'omega' : Item(status=' U') 5201 }) 5202 expected_elision_output = wc.State(A_COPY_H_path, { 5203 'omega' : Item(status=' U') 5204 }) 5205 expected_status = wc.State(A_COPY_H_path, { 5206 '' : Item(status=' M', wc_rev=8), 5207 'psi' : Item(status=' ', wc_rev=8, switched='S'), 5208 'omega' : Item(status='M ', wc_rev=8), 5209 'chi' : Item(status=' ', wc_rev=8), 5210 }) 5211 expected_disk = wc.State('', { 5212 '' : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:8'}), 5213 'psi' : Item("This is the file 'psi'.\n"), 5214 'omega' : Item("New content"), 5215 'chi' : Item("This is the file 'chi'.\n"), 5216 }) 5217 expected_skip = wc.State(A_COPY_H_path, { }) 5218 5219 svntest.actions.run_and_verify_merge(A_COPY_H_path, '7', '8', 5220 sbox.repo_url + '/A/D/H', None, 5221 expected_output, 5222 expected_mergeinfo_output, 5223 expected_elision_output, 5224 expected_disk, 5225 expected_status, expected_skip, 5226 check_props=True) 5227 5228 # Target with switched dir child: 5229 # 5230 # Merge r6 from A/D into A_COPY/D. The only subtrees with explicit 5231 # mergeinfo (or switched) that are affected by the merge are A_COPY/D/G 5232 # and A_COPY/D/G/rho. Only these two subtrees, and the target itself, 5233 # should receive mergeinfo updates. 5234 expected_output = wc.State(A_COPY_D_path, { 5235 'G/rho' : Item(status='U ') 5236 }) 5237 expected_mergeinfo_output = wc.State(A_COPY_D_path, { 5238 '' : Item(status=' U'), 5239 'G' : Item(status=' U'), 5240 'G/rho' : Item(status=' U') 5241 }) 5242 expected_elision_output = wc.State(A_COPY_D_path, { 5243 }) 5244 expected_status_D = wc.State(A_COPY_D_path, { 5245 '' : Item(status=' M', wc_rev=8), 5246 'H' : Item(status=' M', wc_rev=8), 5247 'H/chi' : Item(status=' ', wc_rev=8), 5248 'H/omega' : Item(status='M ', wc_rev=8), 5249 'H/psi' : Item(status=' ', wc_rev=8, switched='S'), 5250 'G' : Item(status=' M', wc_rev=8, switched='S'), 5251 'G/pi' : Item(status=' ', wc_rev=8), 5252 'G/rho' : Item(status='MM', wc_rev=8, switched='S'), 5253 'G/tau' : Item(status=' ', wc_rev=8), 5254 'gamma' : Item(status=' ', wc_rev=8), 5255 }) 5256 expected_disk_D = wc.State('', { 5257 '' : Item(props={SVN_PROP_MERGEINFO : '/A/D:6*'}), 5258 'H' : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:8'}), 5259 'H/chi' : Item("This is the file 'chi'.\n"), 5260 'H/omega' : Item("New content"), 5261 'H/psi' : Item("This is the file 'psi'.\n",), 5262 'G' : Item(props={SVN_PROP_MERGEINFO : '/A/D/G:6*'}), 5263 'G/pi' : Item("This is the file 'pi'.\n"), 5264 'G/rho' : Item("New content", 5265 props={SVN_PROP_MERGEINFO : '/A/D/G/rho:6'}), 5266 'G/tau' : Item("This is the file 'tau'.\n"), 5267 'gamma' : Item("This is the file 'gamma'.\n"), 5268 }) 5269 expected_skip_D = wc.State(A_COPY_D_path, { }) 5270 svntest.actions.run_and_verify_merge(A_COPY_D_path, '5', '6', 5271 sbox.repo_url + '/A/D', None, 5272 expected_output, 5273 expected_mergeinfo_output, 5274 expected_elision_output, 5275 expected_disk_D, 5276 expected_status_D, expected_skip_D, 5277 check_props=True) 5278 5279 5280 # Merge r5 from A/D into A_COPY/D. This updates the mergeinfo on the 5281 # target A_COPY\D because the target is always updated. It also updates 5282 # the mergeinfo on A_COPY\D\H because that path has explicit mergeinfo 5283 # and has a subtree affected by the merge. Lastly, mergeinfo on 5284 # A_COPY/D/H/psi is added because that path is switched. 5285 expected_output = wc.State(A_COPY_D_path, { 5286 'H/psi' : Item(status='U ')}) 5287 expected_mergeinfo_output = wc.State(A_COPY_D_path, { 5288 '' : Item(status=' G'), 5289 'H' : Item(status=' G'), 5290 'H/psi' : Item(status=' U') 5291 }) 5292 expected_elision_output = wc.State(A_COPY_D_path, { 5293 }) 5294 expected_disk_D.tweak('', props={SVN_PROP_MERGEINFO : '/A/D:5,6*'}) 5295 expected_disk_D.tweak('H', props={SVN_PROP_MERGEINFO : '/A/D/H:5*,8'}) 5296 expected_disk_D.tweak('H/psi', contents="New content", 5297 props={SVN_PROP_MERGEINFO :'/A/D/H/psi:5'}) 5298 expected_status_D.tweak('H/psi', status='MM') 5299 svntest.actions.run_and_verify_merge(A_COPY_D_path, '4', '5', 5300 sbox.repo_url + '/A/D', None, 5301 expected_output, 5302 expected_mergeinfo_output, 5303 expected_elision_output, 5304 expected_disk_D, 5305 expected_status_D, expected_skip_D, 5306 check_props=True) 5307 5308 # Finally, merge r4:8 into A_COPY. A_COPY gets mergeinfo for r5-8 added but 5309 # since none of A_COPY's subtrees with mergeinfo are affected, none of them 5310 # get any mergeinfo changes. 5311 expected_output = wc.State(A_COPY_path, { 5312 'B/E/beta' : Item(status='U ') 5313 }) 5314 expected_mergeinfo_output = wc.State(A_COPY_path, { 5315 '' : Item(status=' U') 5316 }) 5317 expected_elision_output = wc.State(A_COPY_path, { 5318 }) 5319 expected_status = wc.State(A_COPY_path, { 5320 '' : Item(status=' M', wc_rev=8), 5321 'B' : Item(status=' ', wc_rev=8), 5322 'mu' : Item(status=' ', wc_rev=8), 5323 'B/E' : Item(status=' ', wc_rev=8), 5324 'B/E/alpha' : Item(status=' ', wc_rev=8), 5325 'B/E/beta' : Item(status='M ', wc_rev=8), 5326 'B/lambda' : Item(status=' ', wc_rev=8), 5327 'B/F' : Item(status=' ', wc_rev=8), 5328 'C' : Item(status=' ', wc_rev=8), 5329 'D' : Item(status=' M', wc_rev=8), 5330 'D/G' : Item(status=' M', wc_rev=8, switched='S'), 5331 'D/G/pi' : Item(status=' ', wc_rev=8), 5332 'D/G/rho' : Item(status='MM', wc_rev=8, switched='S'), 5333 'D/G/tau' : Item(status=' ', wc_rev=8), 5334 'D/gamma' : Item(status=' ', wc_rev=8), 5335 'D/H' : Item(status=' M', wc_rev=8), 5336 'D/H/chi' : Item(status=' ', wc_rev=8), 5337 'D/H/psi' : Item(status='MM', wc_rev=8, switched='S'), 5338 'D/H/omega' : Item(status='M ', wc_rev=8), 5339 }) 5340 expected_disk = wc.State('', { 5341 '' : Item(props={SVN_PROP_MERGEINFO : '/A:5-8'}), 5342 'B' : Item(), 5343 'mu' : Item("This is the file 'mu'.\n"), 5344 'B/E' : Item(), 5345 'B/E/alpha' : Item("This is the file 'alpha'.\n"), 5346 'B/E/beta' : Item("New content"), 5347 'B/lambda' : Item("This is the file 'lambda'.\n"), 5348 'B/F' : Item(), 5349 'C' : Item(), 5350 'D' : Item(props={SVN_PROP_MERGEINFO : '/A/D:5,6*'}), 5351 'D/G' : Item(props={SVN_PROP_MERGEINFO : '/A/D/G:6*'}), 5352 'D/G/pi' : Item("This is the file 'pi'.\n"), 5353 'D/G/rho' : Item("New content", 5354 props={SVN_PROP_MERGEINFO : '/A/D/G/rho:6'}), 5355 'D/G/tau' : Item("This is the file 'tau'.\n"), 5356 'D/gamma' : Item("This is the file 'gamma'.\n"), 5357 'D/H' : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:5*,8'}), 5358 'D/H/chi' : Item("This is the file 'chi'.\n"), 5359 'D/H/psi' : Item("New content", 5360 props={SVN_PROP_MERGEINFO : '/A/D/H/psi:5'}), 5361 'D/H/omega' : Item("New content"), 5362 }) 5363 expected_skip = wc.State(A_COPY_path, { }) 5364 svntest.actions.run_and_verify_merge(A_COPY_path, '4', '8', 5365 sbox.repo_url + '/A', None, 5366 expected_output, 5367 expected_mergeinfo_output, 5368 expected_elision_output, 5369 expected_disk, 5370 expected_status, expected_skip, 5371 check_props=True) 5372 # Commit changes thus far. 5373 expected_output = svntest.wc.State(wc_dir, { 5374 'A_COPY' : Item(verb='Sending'), 5375 'A_COPY/B/E/beta' : Item(verb='Sending'), 5376 'A_COPY/D' : Item(verb='Sending'), 5377 'A_COPY/D/G' : Item(verb='Sending'), 5378 'A_COPY/D/G/rho' : Item(verb='Sending'), 5379 'A_COPY/D/H' : Item(verb='Sending'), 5380 'A_COPY/D/H/omega' : Item(verb='Sending'), 5381 'A_COPY/D/H/psi' : Item(verb='Sending'), 5382 }) 5383 wc_status.tweak('A_COPY', 'A_COPY/B/E/beta', 'A_COPY/D', 'A_COPY/D/G', 5384 'A_COPY/D/G/rho', 'A_COPY/D/H', 'A_COPY/D/H/omega', 5385 'A_COPY/D/H/psi', wc_rev=9) 5386 svntest.actions.run_and_verify_commit(wc_dir, expected_output, wc_status) 5387 5388 # Unswitch A_COPY/D/H/psi. 5389 expected_output = svntest.wc.State(wc_dir, { 5390 'A_COPY/D/H/psi' : Item(status='UU')}) 5391 wc_status.tweak("A_COPY/D/H/psi", switched=None, wc_rev=9) 5392 wc_disk.tweak("A_COPY", 5393 props={SVN_PROP_MERGEINFO : '/A:5-8'}) 5394 wc_disk.tweak("A_COPY/B/E/beta", 5395 contents="New content") 5396 wc_disk.tweak("A_COPY/D", 5397 props={SVN_PROP_MERGEINFO : '/A/D:5,6*'}) 5398 wc_disk.tweak("A_COPY/D/G", 5399 props={SVN_PROP_MERGEINFO : '/A/D/G:6*'}) 5400 wc_disk.tweak("A_COPY/D/G/rho", 5401 contents="New content", 5402 props={SVN_PROP_MERGEINFO : '/A/D/G/rho:6'}) 5403 wc_disk.tweak("A_COPY/D/H", 5404 props={SVN_PROP_MERGEINFO : '/A/D/H:5*,8'}) 5405 wc_disk.tweak("A_COPY/D/H/omega", 5406 contents="New content") 5407 wc_disk.tweak("A_COPY_2", props={}) 5408 svntest.actions.run_and_verify_switch(sbox.wc_dir, A_COPY_psi_path, 5409 sbox.repo_url + "/A_COPY/D/H/psi", 5410 expected_output, wc_disk, wc_status, 5411 [], 1) 5412 5413 # Non-inheritable mergeinfo ranges on a target don't prevent repeat 5414 # merges of that range on the target's children. 5415 # 5416 # Non-inheritable mergeinfo ranges on a target are removed if the target 5417 # no longer has any switched children and a repeat merge is performed. 5418 # 5419 # Merge r4:8 from A/D/H into A_COPY/D/H. A_COPY/D/H already has mergeinfo 5420 # for r5 and r8 but it is marked as uninheritable so the repeat merge is 5421 # allowed on its children, notably the now unswitched A_COPY/D/H/psi. 5422 # Since A_COPY/D/H no longer has any switched children and the merge of 5423 # r4:8 has been repeated the previously uninheritable ranges 5* and 8* on 5424 # A_COPY/D/H are made inheritable and combined with r6-7. A_COPY/D/H/omega 5425 # has explicit mergeinfo, but is not touched by the merge, so is left as-is. 5426 expected_output = wc.State(A_COPY_H_path, { 5427 'psi' : Item(status='U ') 5428 }) 5429 expected_mergeinfo_output = wc.State(A_COPY_H_path, { 5430 '' : Item(status=' U'), 5431 'psi' : Item(status=' G') 5432 }) 5433 expected_elision_output = wc.State(A_COPY_H_path, { 5434 'psi' : Item(status=' U') 5435 }) 5436 expected_status = wc.State(A_COPY_H_path, { 5437 '' : Item(status=' M', wc_rev=9), 5438 'psi' : Item(status='M ', wc_rev=9), 5439 'omega' : Item(status=' ', wc_rev=9), 5440 'chi' : Item(status=' ', wc_rev=8), 5441 }) 5442 expected_disk = wc.State('', { 5443 '' : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:5-8'}), 5444 'psi' : Item("New content"), 5445 'omega' : Item("New content"), 5446 'chi' : Item("This is the file 'chi'.\n"), 5447 }) 5448 expected_skip = wc.State(A_COPY_H_path, { }) 5449 svntest.actions.run_and_verify_merge(A_COPY_H_path, '4', '8', 5450 sbox.repo_url + '/A/D/H', None, 5451 expected_output, 5452 expected_mergeinfo_output, 5453 expected_elision_output, 5454 expected_disk, 5455 expected_status, expected_skip, 5456 [], 5457 True, False, '--allow-mixed-revisions', 5458 A_COPY_H_path) 5459 5460 # Non-inheritable mergeinfo ranges on a target do prevent repeat 5461 # merges on the target itself. 5462 # 5463 # Add a prop A/D and commit it as r10. Merge r10 into A_COPY/D. Since 5464 # A_COPY/D has a switched child it gets r10 added as a non-inheritable 5465 # range. Repeat the same merge checking that no repeat merge is 5466 # attempted on A_COPY/D. 5467 svntest.actions.run_and_verify_svn(["property 'prop:name' set on '" + 5468 D_path + "'\n"], [], 'ps', 5469 'prop:name', 'propval', D_path) 5470 expected_output = svntest.wc.State(wc_dir, { 5471 'A/D' : Item(verb='Sending'), 5472 'A_COPY/D/H' : Item(verb='Sending'), 5473 'A_COPY/D/H/psi' : Item(verb='Sending'), 5474 }) 5475 wc_status.tweak('A_COPY/D', wc_rev=9) 5476 wc_status.tweak('A/D', 'A_COPY/D/H', 'A_COPY/D/H/psi', wc_rev=10) 5477 svntest.actions.run_and_verify_commit(wc_dir, expected_output, wc_status) 5478 expected_output = wc.State(A_COPY_D_path, { 5479 '' : Item(status=' U') 5480 }) 5481 expected_mergeinfo_output = wc.State(A_COPY_D_path, { 5482 '' : Item(status=' U') 5483 }) 5484 expected_elision_output = wc.State(A_COPY_D_path, { 5485 }) 5486 # Reuse expected status and disk from last merge to A_COPY/D 5487 expected_status_D.tweak(status=' ') 5488 expected_status_D.tweak('', status=' M', wc_rev=9) 5489 expected_status_D.tweak('H', wc_rev=10) 5490 expected_status_D.tweak('H/psi', wc_rev=10, switched=None) 5491 expected_status_D.tweak('H/omega', wc_rev=9) 5492 expected_status_D.tweak('G', 'G/rho', switched='S', wc_rev=9) 5493 expected_disk_D.tweak('', props={SVN_PROP_MERGEINFO : '/A/D:5,6*,10', 5494 "prop:name" : "propval"}) 5495 expected_disk_D.tweak('G/rho', 5496 props={SVN_PROP_MERGEINFO : '/A/D/G/rho:6'}) 5497 expected_disk_D.tweak('H', props={SVN_PROP_MERGEINFO : '/A/D/H:5-8'}) 5498 expected_disk_D.tweak('H/psi', contents="New content", props={}) 5499 svntest.actions.run_and_verify_merge(A_COPY_D_path, '9', '10', 5500 sbox.repo_url + '/A/D', None, 5501 expected_output, 5502 expected_mergeinfo_output, 5503 expected_elision_output, 5504 expected_disk_D, 5505 expected_status_D, expected_skip_D, 5506 [], 5507 True, False, '--allow-mixed-revisions', 5508 A_COPY_D_path) 5509 # Repeated merge is a no-op, though we still see the notification reporting 5510 # the mergeinfo describing the merge has been recorded, though this time it 5511 # is a ' G' notification because there is a local mergeinfo change. 5512 expected_output = wc.State(A_COPY_D_path, {}) 5513 expected_mergeinfo_output = wc.State(A_COPY_D_path, { 5514 '' : Item(status=' G') 5515 }) 5516 svntest.actions.run_and_verify_merge(A_COPY_D_path, '9', '10', 5517 sbox.repo_url + '/A/D', None, 5518 expected_output, 5519 expected_mergeinfo_output, 5520 expected_elision_output, 5521 expected_disk_D, 5522 expected_status_D, expected_skip_D, 5523 [], 5524 True, False, '--allow-mixed-revisions', 5525 A_COPY_D_path) 5526 5527 # Test issue #3187 'Reverse merges don't work properly with 5528 # non-inheritable ranges'. 5529 # 5530 # Test the "switched subtrees" portion of issue #3188 'Mergeinfo on 5531 # switched targets/subtrees should elide to repos'. 5532 # 5533 # Reverse merge r5-8, this should revert all the subtree merges done to 5534 # A_COPY thus far and remove all mergeinfo. 5535 5536 # Revert all local changes. This leaves just the mergeinfo for r5-8 5537 # on A_COPY and its various subtrees. 5538 svntest.actions.run_and_verify_svn(None, [], 'revert', '-R', wc_dir) 5539 5540 # Update merge target so working revisions are uniform and all 5541 # possible elision occurs. 5542 svntest.actions.run_and_verify_svn(exp_noop_up_out(10), [], 5543 'up', A_COPY_path) 5544 5545 # Do the reverse merge. 5546 expected_output = wc.State(A_COPY_path, { 5547 'B/E/beta' : Item(status='U '), 5548 'D/G/rho' : Item(status='U '), 5549 'D/H/omega' : Item(status='U '), 5550 'D/H/psi' : Item(status='U ') 5551 }) 5552 expected_mergeinfo_output = wc.State(A_COPY_path, { 5553 '' : Item(status=' U'), 5554 'D' : Item(status=' U'), 5555 'D/G' : Item(status=' U'), 5556 'D/G/rho' : Item(status=' U'), 5557 'D/H' : Item(status=' U'), 5558 }) 5559 expected_elision_output = wc.State(A_COPY_path, { 5560 '' : Item(status=' U'), 5561 'D' : Item(status=' U'), 5562 'D/G' : Item(status=' U'), 5563 'D/G/rho' : Item(status=' U'), 5564 'D/H' : Item(status=' U'), 5565 }) 5566 expected_status = wc.State(A_COPY_path, { 5567 '' : Item(status=' M', wc_rev=10), 5568 'B' : Item(status=' ', wc_rev=10), 5569 'mu' : Item(status=' ', wc_rev=10), 5570 'B/E' : Item(status=' ', wc_rev=10), 5571 'B/E/alpha' : Item(status=' ', wc_rev=10), 5572 'B/E/beta' : Item(status='M ', wc_rev=10), 5573 'B/lambda' : Item(status=' ', wc_rev=10), 5574 'B/F' : Item(status=' ', wc_rev=10), 5575 'C' : Item(status=' ', wc_rev=10), 5576 'D' : Item(status=' M', wc_rev=10), 5577 'D/G' : Item(status=' M', wc_rev=10, switched='S'), 5578 'D/G/pi' : Item(status=' ', wc_rev=10), 5579 'D/G/rho' : Item(status='MM', wc_rev=10, switched='S'), 5580 'D/G/tau' : Item(status=' ', wc_rev=10), 5581 'D/gamma' : Item(status=' ', wc_rev=10), 5582 'D/H' : Item(status=' M', wc_rev=10), 5583 'D/H/chi' : Item(status=' ', wc_rev=10), 5584 'D/H/psi' : Item(status='M ', wc_rev=10), 5585 'D/H/omega' : Item(status='M ', wc_rev=10), 5586 }) 5587 expected_disk = wc.State('', { 5588 'B' : Item(), 5589 'mu' : Item("This is the file 'mu'.\n"), 5590 'B/E' : Item(), 5591 'B/E/alpha' : Item("This is the file 'alpha'.\n"), 5592 'B/E/beta' : Item("This is the file 'beta'.\n"), 5593 'B/lambda' : Item("This is the file 'lambda'.\n"), 5594 'B/F' : Item(), 5595 'C' : Item(), 5596 'D' : Item(), 5597 'D/G' : Item(), 5598 'D/G/pi' : Item("This is the file 'pi'.\n"), 5599 'D/G/rho' : Item("This is the file 'rho'.\n"), 5600 'D/G/tau' : Item("This is the file 'tau'.\n"), 5601 'D/gamma' : Item("This is the file 'gamma'.\n"), 5602 'D/H' : Item(), 5603 'D/H/chi' : Item("This is the file 'chi'.\n"), 5604 'D/H/psi' : Item("This is the file 'psi'.\n"), 5605 'D/H/omega' : Item("This is the file 'omega'.\n"), 5606 }) 5607 expected_skip = wc.State(A_COPY_path, { }) 5608 svntest.actions.run_and_verify_merge(A_COPY_path, '8', '4', 5609 sbox.repo_url + '/A', None, 5610 expected_output, 5611 expected_mergeinfo_output, 5612 expected_elision_output, 5613 expected_disk, 5614 expected_status, expected_skip, 5615 check_props=True) 5616 5617#---------------------------------------------------------------------- 5618# Test for issue 2047: Merge from parent dir fails while it succeeds from 5619# the direct dir 5620@Issue(2047) 5621def merge_with_implicit_target_file(sbox): 5622 "merge a change to a file, using relative path" 5623 5624 sbox.build() 5625 wc_dir = sbox.wc_dir 5626 5627 # Make a change to A/mu, then revert it using 'svn merge -r 2:1 A/mu' 5628 5629 # change A/mu and commit 5630 A_path = sbox.ospath('A') 5631 mu_path = os.path.join(A_path, 'mu') 5632 5633 svntest.main.file_append(mu_path, "A whole new line.\n") 5634 5635 expected_output = svntest.wc.State(wc_dir, { 5636 'A/mu' : Item(verb='Sending'), 5637 }) 5638 expected_status = svntest.actions.get_virginal_state(wc_dir, 1) 5639 expected_status.tweak('A/mu', wc_rev=2) 5640 svntest.actions.run_and_verify_commit(wc_dir, expected_output, 5641 expected_status) 5642 5643 # Update to revision 2. 5644 svntest.actions.run_and_verify_svn(None, [], 'update', wc_dir) 5645 5646 # Revert the change committed in r2 5647 os.chdir(wc_dir) 5648 5649 # run_and_verify_merge doesn't accept file paths. 5650 svntest.actions.run_and_verify_svn(None, [], 'merge', '-r', '2:1', 5651 'A/mu') 5652 5653#---------------------------------------------------------------------- 5654# Test practical application of issue #2769 fix, empty rev range elision, 5655# and elision to the repos. 5656@Issue(2769) 5657@SkipUnless(server_has_mergeinfo) 5658def empty_mergeinfo(sbox): 5659 "mergeinfo can explicitly be empty" 5660 5661 # A bit o' history: The fix for issue #2769 originally permitted mergeinfo 5662 # with empty range lists and as a result we permitted partial elision and 5663 # had a whole slew of tests here for that. But the fix of issue #3029 now 5664 # prevents svn ps or svn merge from creating mergeinfo with paths mapped to 5665 # empty ranges, only empty mergeinfo is allowed. As a result this test now 5666 # covers the following areas: 5667 # 5668 # A) Merging a set of revisions into a path, then reverse merging the 5669 # same set out of a subtree of path results in empty mergeinfo 5670 # (i.e. "") on the subtree. 5671 # 5672 # B) Empty mergeinfo elides to empty mergeinfo. 5673 # 5674 # C) If a merge sets empty mergeinfo on its target and that target has 5675 # no ancestor in either the WC or the repository with explicit 5676 # mergeinfo, then the target's mergeinfo is removed (a.k.a. elides 5677 # to nothing). 5678 sbox.build() 5679 wc_dir = sbox.wc_dir 5680 wc_disk, wc_status = set_up_branch(sbox) 5681 5682 # Some paths we'll care about 5683 A_COPY_path = sbox.ospath('A_COPY') 5684 H_COPY_path = sbox.ospath('A_COPY/D/H') 5685 psi_COPY_path = sbox.ospath('A_COPY/D/H/psi') 5686 rho_COPY_path = sbox.ospath('A_COPY/D/G/rho') 5687 5688 # Test area A -- Merge r2:4 into A_COPY then reverse merge 4:2 to 5689 # A_COPY/D/G. A_COPY/D/G should end up with empty mergeinfo to 5690 # override that of A_COPY. 5691 expected_output = wc.State(A_COPY_path, { 5692 'D/H/psi' : Item(status='U '), 5693 'D/G/rho' : Item(status='U '), 5694 }) 5695 expected_mergeinfo_output = wc.State(A_COPY_path, { 5696 '' : Item(status=' U'), 5697 }) 5698 expected_elision_output = wc.State(A_COPY_path, { 5699 }) 5700 expected_status = wc.State(A_COPY_path, { 5701 '' : Item(status=' M', wc_rev=2), 5702 'B' : Item(status=' ', wc_rev=2), 5703 'mu' : Item(status=' ', wc_rev=2), 5704 'B/E' : Item(status=' ', wc_rev=2), 5705 'B/E/alpha' : Item(status=' ', wc_rev=2), 5706 'B/E/beta' : Item(status=' ', wc_rev=2), 5707 'B/lambda' : Item(status=' ', wc_rev=2), 5708 'B/F' : Item(status=' ', wc_rev=2), 5709 'C' : Item(status=' ', wc_rev=2), 5710 'D' : Item(status=' ', wc_rev=2), 5711 'D/G' : Item(status=' ', wc_rev=2), 5712 'D/G/pi' : Item(status=' ', wc_rev=2), 5713 'D/G/rho' : Item(status='M ', wc_rev=2), 5714 'D/G/tau' : Item(status=' ', wc_rev=2), 5715 'D/gamma' : Item(status=' ', wc_rev=2), 5716 'D/H' : Item(status=' ', wc_rev=2), 5717 'D/H/chi' : Item(status=' ', wc_rev=2), 5718 'D/H/psi' : Item(status='M ', wc_rev=2), 5719 'D/H/omega' : Item(status=' ', wc_rev=2), 5720 }) 5721 expected_disk = wc.State('', { 5722 '' : Item(props={SVN_PROP_MERGEINFO : '/A:3-4'}), 5723 'B' : Item(), 5724 'mu' : Item("This is the file 'mu'.\n"), 5725 'B/E' : Item(), 5726 'B/E/alpha' : Item("This is the file 'alpha'.\n"), 5727 'B/E/beta' : Item("This is the file 'beta'.\n"), 5728 'B/lambda' : Item("This is the file 'lambda'.\n"), 5729 'B/F' : Item(), 5730 'C' : Item(), 5731 'D' : Item(), 5732 'D/G' : Item(), 5733 'D/G/pi' : Item("This is the file 'pi'.\n"), 5734 'D/G/rho' : Item("New content"), 5735 'D/G/tau' : Item("This is the file 'tau'.\n"), 5736 'D/gamma' : Item("This is the file 'gamma'.\n"), 5737 'D/H' : Item(), 5738 'D/H/chi' : Item("This is the file 'chi'.\n"), 5739 'D/H/psi' : Item("New content"), 5740 'D/H/omega' : Item("This is the file 'omega'.\n"), 5741 }) 5742 expected_skip = wc.State(A_COPY_path, { }) 5743 svntest.actions.run_and_verify_merge(A_COPY_path, '2', '4', 5744 sbox.repo_url + '/A', None, 5745 expected_output, 5746 expected_mergeinfo_output, 5747 expected_elision_output, 5748 expected_disk, 5749 expected_status, 5750 expected_skip, 5751 check_props=True) 5752 # Now do the reverse merge into the subtree. 5753 expected_output = wc.State(H_COPY_path, { 5754 'psi' : Item(status='G '), 5755 }) 5756 expected_mergeinfo_output = wc.State(H_COPY_path, { 5757 '' : Item(status=' G'), 5758 }) 5759 expected_elision_output = wc.State(H_COPY_path, { 5760 }) 5761 expected_status = wc.State(H_COPY_path, { 5762 '' : Item(status=' M', wc_rev=2), 5763 'chi' : Item(status=' ', wc_rev=2), 5764 'psi' : Item(status=' ', wc_rev=2), 5765 'omega' : Item(status=' ', wc_rev=2), 5766 }) 5767 expected_disk = wc.State('', { 5768 '' : Item(props={SVN_PROP_MERGEINFO : ''}), 5769 'chi' : Item("This is the file 'chi'.\n"), 5770 'psi' : Item("This is the file 'psi'.\n"), 5771 'omega' : Item("This is the file 'omega'.\n"), 5772 }) 5773 expected_skip = wc.State(H_COPY_path, { }) 5774 svntest.actions.run_and_verify_merge(H_COPY_path, '4', '2', 5775 sbox.repo_url + '/A/D/H', None, 5776 expected_output, 5777 expected_mergeinfo_output, 5778 expected_elision_output, 5779 expected_disk, 5780 expected_status, 5781 expected_skip, 5782 check_props=True) 5783 5784 # Test areas B and C -- Reverse merge r3 into A_COPY, this would result in 5785 # empty mergeinfo on A_COPY and A_COPY/D/H, but the empty mergeinfo on the 5786 # latter elides to the former. And then the empty mergeinfo on A_COPY, 5787 # which has no parent with explicit mergeinfo to override (in either the WC 5788 # or the repos) itself elides. This leaves the WC in the same unmodified 5789 # state as after the call to set_up_branch(). 5790 expected_output = expected_merge_output( 5791 [[4,3]], ['G ' + rho_COPY_path + '\n', 5792 ' G ' + A_COPY_path + '\n', 5793 ' U ' + H_COPY_path + '\n', 5794 ' U ' + A_COPY_path + '\n',], 5795 elides=True) 5796 svntest.actions.run_and_verify_svn(expected_output, 5797 [], 'merge', '-r4:2', 5798 sbox.repo_url + '/A', 5799 A_COPY_path) 5800 svntest.actions.run_and_verify_status(wc_dir, wc_status) 5801 # Check that A_COPY's mergeinfo is gone. 5802 svntest.actions.run_and_verify_svn([], '.*W200017: Property.*not found', 5803 'pg', 'svn:mergeinfo', 5804 A_COPY_path) 5805 5806#---------------------------------------------------------------------- 5807@SkipUnless(server_has_mergeinfo) 5808@Issue(2781) 5809def prop_add_to_child_with_mergeinfo(sbox): 5810 "merge adding prop to child of merge target works" 5811 5812 # Test for Issue #2781 Prop add to child of merge target corrupts WC if 5813 # child has mergeinfo. 5814 5815 sbox.build() 5816 wc_dir = sbox.wc_dir 5817 expected_disk, expected_status = set_up_branch(sbox) 5818 5819 # Some paths we'll care about 5820 beta_path = sbox.ospath('A/B/E/beta') 5821 beta_COPY_path = sbox.ospath('A_COPY/B/E/beta') 5822 B_COPY_path = sbox.ospath('A_COPY/B') 5823 5824 # Set a non-mergeinfo prop on a file. 5825 svntest.actions.run_and_verify_svn(["property 'prop:name' set on '" + 5826 beta_path + "'\n"], [], 'ps', 5827 'prop:name', 'propval', beta_path) 5828 expected_disk.tweak('A/B/E/beta', props={'prop:name' : 'propval'}) 5829 expected_status.tweak('A/B/E/beta', wc_rev=7) 5830 expected_output = wc.State(wc_dir, 5831 {'A/B/E/beta' : Item(verb='Sending')}) 5832 svntest.actions.run_and_verify_commit(wc_dir, 5833 expected_output, 5834 expected_status) 5835 5836 # Merge r4:5 from A/B/E/beta into A_COPY/B/E/beta. 5837 svntest.actions.run_and_verify_svn( 5838 expected_merge_output([[5]], 5839 ['U ' + beta_COPY_path +'\n', 5840 ' U ' + beta_COPY_path +'\n',]), 5841 [], 'merge', '-c5', 5842 sbox.repo_url + '/A/B/E/beta', 5843 beta_COPY_path) 5844 5845 # Merge r6:7 into A_COPY/B. In issue #2781 this adds a bogus 5846 # and incomplete entry in A_COPY/B/.svn/entries for 'beta'. 5847 expected_output = wc.State(B_COPY_path, { 5848 'E/beta' : Item(status=' U'), 5849 }) 5850 expected_mergeinfo_output = wc.State(B_COPY_path, { 5851 '' : Item(status=' U'), 5852 'E/beta' : Item(status=' G'), 5853 }) 5854 expected_elision_output = wc.State(B_COPY_path, { 5855 }) 5856 expected_status = wc.State(B_COPY_path, { 5857 '' : Item(status=' M', wc_rev=2), 5858 'E' : Item(status=' ', wc_rev=2), 5859 'E/alpha' : Item(status=' ', wc_rev=2), 5860 'E/beta' : Item(status='MM', wc_rev=2), 5861 'lambda' : Item(status=' ', wc_rev=2), 5862 'F' : Item(status=' ', wc_rev=2), 5863 }) 5864 expected_disk = wc.State('', { 5865 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B:7'}), 5866 'E' : Item(), 5867 'E/alpha' : Item("This is the file 'alpha'.\n"), 5868 'E/beta' : Item(contents="New content", 5869 props={SVN_PROP_MERGEINFO : '/A/B/E/beta:5,7', 5870 'prop:name' : 'propval'}), 5871 'F' : Item(), 5872 'lambda' : Item("This is the file 'lambda'.\n") 5873 }) 5874 expected_skip = wc.State(B_COPY_path, { }) 5875 svntest.actions.run_and_verify_merge(B_COPY_path, '6', '7', 5876 sbox.repo_url + '/A/B', None, 5877 expected_output, 5878 expected_mergeinfo_output, 5879 expected_elision_output, 5880 expected_disk, 5881 expected_status, 5882 expected_skip, 5883 check_props=True) 5884 5885#---------------------------------------------------------------------- 5886@Issue(2788,3383) 5887def foreign_repos_does_not_update_mergeinfo(sbox): 5888 "set no mergeinfo when merging from foreign repos" 5889 5890 # Test for issue #2788 and issue #3383. 5891 5892 sbox.build() 5893 wc_dir = sbox.wc_dir 5894 expected_disk, expected_status = set_up_branch(sbox) 5895 5896 # Set up for test of issue #2788. 5897 5898 # Create a second repository with the same greek tree 5899 repo_dir = sbox.repo_dir 5900 other_repo_dir, other_repo_url = sbox.add_repo_path("other") 5901 other_wc_dir = sbox.add_wc_path("other") 5902 svntest.main.copy_repos(repo_dir, other_repo_dir, 6, 1) 5903 5904 # Merge r3:4 (using implied peg revisions) from 'other' repos into 5905 # A_COPY/D/G. Merge should succeed, but no mergeinfo should be set. 5906 G_COPY_path = sbox.ospath('A_COPY/D/G') 5907 svntest.actions.run_and_verify_svn(expected_merge_output([[4]], 5908 'U ' + 5909 os.path.join(G_COPY_path, 5910 "rho") + '\n', True), 5911 [], 'merge', '-c4', 5912 other_repo_url + '/A/D/G', 5913 G_COPY_path) 5914 5915 # Merge r4:5 (using explicit peg revisions) from 'other' repos into 5916 # A_COPY/B/E. Merge should succeed, but no mergeinfo should be set. 5917 E_COPY_path = sbox.ospath('A_COPY/B/E') 5918 svntest.actions.run_and_verify_svn(expected_merge_output([[5]], 5919 'U ' + 5920 os.path.join(E_COPY_path, 5921 "beta") +'\n', True), 5922 [], 'merge', 5923 other_repo_url + '/A/B/E@4', 5924 other_repo_url + '/A/B/E@5', 5925 E_COPY_path) 5926 5927 expected_status.tweak('A_COPY/D/G/rho', 'A_COPY/B/E/beta', status='M ') 5928 svntest.actions.run_and_verify_status(wc_dir, expected_status) 5929 5930 # Set up for test of issue #3383. 5931 svntest.actions.run_and_verify_svn(None, [], 'revert', '-R', wc_dir) 5932 5933 # Get a working copy for the foreign repos. 5934 svntest.actions.run_and_verify_svn(None, [], 'co', other_repo_url, 5935 other_wc_dir) 5936 5937 # Create mergeinfo on the foreign repos on an existing directory and 5938 # file and an added directory and file. Commit as r7. And no, we aren't 5939 # checking these intermediate steps very thoroughly, but we test these 5940 # simple merges to *death* elsewhere. 5941 5942 # Create mergeinfo on an existing directory. 5943 svntest.actions.run_and_verify_svn(None, [], 'merge', 5944 other_repo_url + '/A', 5945 os.path.join(other_wc_dir, 'A_COPY'), 5946 '-c5') 5947 5948 # Create mergeinfo on an existing file. 5949 svntest.actions.run_and_verify_svn(None, [], 'merge', 5950 other_repo_url + '/A/D/H/psi', 5951 os.path.join(other_wc_dir, 'A_COPY', 5952 'D', 'H', 'psi'), 5953 '-c3') 5954 5955 # Add a new directory with mergeinfo in the foreign repos. 5956 new_dir = os.path.join(other_wc_dir, 'A_COPY', 'N') 5957 svntest.actions.run_and_verify_svn(None, [], 'mkdir', new_dir) 5958 svntest.actions.run_and_verify_svn(None, [], 'ps', 5959 SVN_PROP_MERGEINFO, '', new_dir) 5960 5961 # Add a new file with mergeinfo in the foreign repos. 5962 new_file = os.path.join(other_wc_dir, 'A_COPY', 'nu') 5963 svntest.main.file_write(new_file, "This is the file 'nu'.\n") 5964 svntest.actions.run_and_verify_svn(None, [], 'add', new_file) 5965 svntest.actions.run_and_verify_svn(None, [], 'ps', 5966 SVN_PROP_MERGEINFO, '', new_file) 5967 5968 expected_output = wc.State(other_wc_dir,{ 5969 'A_COPY' : Item(verb='Sending'), # Mergeinfo created 5970 'A_COPY/B/E/beta' : Item(verb='Sending'), 5971 'A_COPY/D/H/psi' : Item(verb='Sending'), # Mergeinfo created 5972 'A_COPY/N' : Item(verb='Adding'), # Has empty mergeinfo 5973 'A_COPY/nu' : Item(verb='Adding'), # Has empty mergeinfo 5974 }) 5975 svntest.actions.run_and_verify_commit(other_wc_dir, expected_output, 5976 None, [], other_wc_dir, 5977 '-m', 5978 'create mergeinfo on foreign repos') 5979 # Now merge a diff from the foreign repos that contains the mergeinfo 5980 # addition in r7 to A_COPY. The mergeinfo diff should *not* be applied 5981 # to A_COPY since it refers to a foreign repository... 5982 svntest.actions.run_and_verify_svn(None, [], 'merge', 5983 other_repo_url + '/A@1', 5984 other_repo_url + '/A_COPY@7', 5985 sbox.ospath('A_COPY')) 5986 #...which means there should be no mergeinfo anywhere in WC_DIR, since 5987 # this test never created any. 5988 svntest.actions.run_and_verify_svn([], [], 'pg', 5989 SVN_PROP_MERGEINFO, '-vR', 5990 wc_dir) 5991 5992#---------------------------------------------------------------------- 5993# This test involves tree conflicts. 5994@XFail() 5995@Issue(2897) 5996def avoid_reflected_revs(sbox): 5997 "avoid repeated merges for cyclic merging" 5998 5999 # See <https://issues.apache.org/jira/browse/SVN-2897>. 6000 # 6001 # This test cherry-picks some changes (all of them, in fact) from the 6002 # parent branch 'A' to the child branch 'A_COPY', and then tries to 6003 # reintegrate 'A_COPY' to 'A' (explicitly specifying a revision range 6004 # on the source branch). It expects the changes that are unique to the 6005 # branch 'A_COPY' to be merged to 'A'. 6006 # 6007 # A --1----[3]---[5]----------? 6008 # \ \_____\___ / 6009 # \ \ \ / 6010 # A_COPY 2-[---4-----6--7--8]- 6011 6012 # Create a WC with a single branch 6013 sbox.build() 6014 wc_dir = sbox.wc_dir 6015 wc_disk, wc_status = set_up_branch(sbox, True, 1) 6016 6017 # Some paths we'll care about 6018 A_path = sbox.ospath('A') 6019 A_COPY_path = sbox.ospath('A_COPY') 6020 tfile1_path = sbox.ospath('A/tfile1') 6021 tfile2_path = sbox.ospath('A/tfile2') 6022 bfile1_path = os.path.join(A_COPY_path, 'bfile1') 6023 bfile2_path = os.path.join(A_COPY_path, 'bfile2') 6024 6025 # Contents to be added to files 6026 tfile1_content = "This is tfile1\n" 6027 tfile2_content = "This is tfile2\n" 6028 bfile1_content = "This is bfile1\n" 6029 bfile2_content = "This is bfile2\n" 6030 6031 # We'll consider A as the trunk and A_COPY as the feature branch 6032 # r3 - Create a tfile1 in A 6033 svntest.main.file_write(tfile1_path, tfile1_content) 6034 svntest.actions.run_and_verify_svn(None, [], 'add', tfile1_path) 6035 expected_output = wc.State(wc_dir, {'A/tfile1' : Item(verb='Adding')}) 6036 wc_status.add({'A/tfile1' : Item(status=' ', wc_rev=3)}) 6037 svntest.actions.run_and_verify_commit(wc_dir, expected_output, 6038 wc_status) 6039 6040 # r4 - Create a bfile1 in A_COPY 6041 svntest.main.file_write(bfile1_path, bfile1_content) 6042 svntest.actions.run_and_verify_svn(None, [], 'add', bfile1_path) 6043 expected_output = wc.State(wc_dir, {'A_COPY/bfile1' : Item(verb='Adding')}) 6044 wc_status.add({'A_COPY/bfile1' : Item(status=' ', wc_rev=4)}) 6045 svntest.actions.run_and_verify_commit(wc_dir, expected_output, 6046 wc_status) 6047 6048 # r5 - Create one more file in A 6049 svntest.main.file_write(tfile2_path, tfile2_content) 6050 svntest.actions.run_and_verify_svn(None, [], 'add', tfile2_path) 6051 expected_output = wc.State(wc_dir, {'A/tfile2' : Item(verb='Adding')}) 6052 wc_status.add({'A/tfile2' : Item(status=' ', wc_rev=5)}) 6053 svntest.actions.run_and_verify_commit(wc_dir, expected_output, 6054 wc_status) 6055 6056 # Merge r5 from /A to /A_COPY, creating r6 6057 expected_output = wc.State(A_COPY_path, { 6058 'tfile2' : Item(status='A '), 6059 }) 6060 expected_mergeinfo_output = wc.State(A_COPY_path, { 6061 '' : Item(status=' U'), 6062 }) 6063 expected_elision_output = wc.State(A_COPY_path, { 6064 }) 6065 expected_status = wc.State(A_COPY_path, { 6066 '' : Item(status=' M', wc_rev=2), 6067 'tfile2' : Item(status='A ', wc_rev='-', copied='+'), 6068 'bfile1' : Item(status=' ', wc_rev=4), 6069 'mu' : Item(status=' ', wc_rev=2), 6070 'C' : Item(status=' ', wc_rev=2), 6071 'D' : Item(status=' ', wc_rev=2), 6072 'B' : Item(status=' ', wc_rev=2), 6073 'B/lambda' : Item(status=' ', wc_rev=2), 6074 'B/E' : Item(status=' ', wc_rev=2), 6075 'B/E/alpha': Item(status=' ', wc_rev=2), 6076 'B/E/beta' : Item(status=' ', wc_rev=2), 6077 'B/F' : Item(status=' ', wc_rev=2), 6078 'D/gamma' : Item(status=' ', wc_rev=2), 6079 'D/G' : Item(status=' ', wc_rev=2), 6080 'D/G/pi' : Item(status=' ', wc_rev=2), 6081 'D/G/rho' : Item(status=' ', wc_rev=2), 6082 'D/G/tau' : Item(status=' ', wc_rev=2), 6083 'D/H' : Item(status=' ', wc_rev=2), 6084 'D/H/chi' : Item(status=' ', wc_rev=2), 6085 'D/H/omega': Item(status=' ', wc_rev=2), 6086 'D/H/psi' : Item(status=' ', wc_rev=2), 6087 }) 6088 expected_disk = wc.State('', { 6089 '' : Item(props={SVN_PROP_MERGEINFO : '/A:5'}), 6090 'tfile2' : Item(tfile2_content), 6091 'bfile1' : Item(bfile1_content), 6092 'mu' : Item("This is the file 'mu'.\n"), 6093 'C' : Item(), 6094 'D' : Item(), 6095 'B' : Item(), 6096 'B/lambda' : Item("This is the file 'lambda'.\n"), 6097 'B/E' : Item(), 6098 'B/E/alpha': Item("This is the file 'alpha'.\n"), 6099 'B/E/beta' : Item("This is the file 'beta'.\n"), 6100 'B/F' : Item(), 6101 'D/gamma' : Item("This is the file 'gamma'.\n"), 6102 'D/G' : Item(), 6103 'D/G/pi' : Item("This is the file 'pi'.\n"), 6104 'D/G/rho' : Item("This is the file 'rho'.\n"), 6105 'D/G/tau' : Item("This is the file 'tau'.\n"), 6106 'D/H' : Item(), 6107 'D/H/chi' : Item("This is the file 'chi'.\n"), 6108 'D/H/omega': Item("This is the file 'omega'.\n"), 6109 'D/H/psi' : Item("This is the file 'psi'.\n"), 6110 }) 6111 expected_skip = wc.State(A_COPY_path, {}) 6112 6113 svntest.actions.run_and_verify_merge(A_COPY_path, '4', '5', 6114 sbox.repo_url + '/A', None, 6115 expected_output, 6116 expected_mergeinfo_output, 6117 expected_elision_output, 6118 expected_disk, 6119 expected_status, 6120 expected_skip, 6121 [], True, False, 6122 A_COPY_path, 6123 '--allow-mixed-revisions') 6124 6125 svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir) 6126 expected_output = wc.State(wc_dir, { 6127 'A_COPY' : Item(verb='Sending'), 6128 'A_COPY/tfile2' : Item(verb='Adding'), 6129 }) 6130 svntest.actions.run_and_verify_commit(wc_dir, expected_output, 6131 None) 6132 6133 # Merge r3 from /A to /A_COPY, creating r7 6134 expected_output = wc.State(A_COPY_path, { 6135 'tfile1' : Item(status='A '), 6136 }) 6137 expected_mergeinfo_output = wc.State(A_COPY_path, { 6138 '' : Item(status=' U'), 6139 }) 6140 expected_elision_output = wc.State(A_COPY_path, { 6141 }) 6142 expected_status.tweak(wc_rev=5) 6143 expected_status.tweak('', wc_rev=6) 6144 expected_status.tweak('tfile2', status=' ', copied=None, wc_rev=6) 6145 expected_status.add({ 6146 'tfile1' : Item(status='A ', wc_rev='-', copied='+'), 6147 }) 6148 expected_disk.tweak('', props={SVN_PROP_MERGEINFO : '/A:3,5'}) 6149 expected_disk.add({ 6150 'tfile1' : Item(tfile1_content), 6151 }) 6152 6153 svntest.actions.run_and_verify_merge(A_COPY_path, '2', '3', 6154 sbox.repo_url + '/A', None, 6155 expected_output, 6156 expected_mergeinfo_output, 6157 expected_elision_output, 6158 expected_disk, 6159 expected_status, 6160 expected_skip, 6161 [], True, False, 6162 A_COPY_path, 6163 '--allow-mixed-revisions') 6164 6165 svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir) 6166 expected_output = wc.State(wc_dir, { 6167 'A_COPY' : Item(verb='Sending'), 6168 'A_COPY/tfile1' : Item(verb='Adding'), 6169 }) 6170 svntest.actions.run_and_verify_commit(wc_dir, expected_output, 6171 None) 6172 6173 # r8 - Add bfile2 to A_COPY 6174 svntest.main.file_write(bfile2_path, bfile2_content) 6175 svntest.actions.run_and_verify_svn(None, [], 'add', bfile2_path) 6176 expected_output = wc.State(wc_dir, {'A_COPY/bfile2' : Item(verb='Adding')}) 6177 wc_status.tweak(wc_rev=6) 6178 wc_status.add({ 6179 'A_COPY/bfile2' : Item(status=' ', wc_rev=8), 6180 'A_COPY' : Item(status=' ', wc_rev=7), 6181 'A_COPY/tfile2' : Item(status=' ', wc_rev=6), 6182 'A_COPY/tfile1' : Item(status=' ', wc_rev=7), 6183 }) 6184 svntest.actions.run_and_verify_commit(wc_dir, expected_output, 6185 wc_status) 6186 6187 # Merge 2:8 from A_COPY(feature branch) to A(trunk). 6188 expected_output = wc.State(A_path, { 6189 'bfile2' : Item(status='A '), 6190 'bfile1' : Item(status='A '), 6191 }) 6192 expected_mergeinfo_output = wc.State(A_path, { 6193 '' : Item(status=' U'), 6194 }) 6195 expected_elision_output = wc.State(A_path, { 6196 }) 6197 expected_status = wc.State(A_path, { 6198 '' : Item(status=' M', wc_rev=6), 6199 'bfile2' : Item(status='A ', wc_rev='-', copied='+'), 6200 'bfile1' : Item(status='A ', wc_rev='-', copied='+'), 6201 'tfile2' : Item(status=' ', wc_rev=6), 6202 'tfile1' : Item(status=' ', wc_rev=6), 6203 'mu' : Item(status=' ', wc_rev=6), 6204 'C' : Item(status=' ', wc_rev=6), 6205 'D' : Item(status=' ', wc_rev=6), 6206 'B' : Item(status=' ', wc_rev=6), 6207 'B/lambda' : Item(status=' ', wc_rev=6), 6208 'B/E' : Item(status=' ', wc_rev=6), 6209 'B/E/alpha' : Item(status=' ', wc_rev=6), 6210 'B/E/beta' : Item(status=' ', wc_rev=6), 6211 'B/F' : Item(status=' ', wc_rev=6), 6212 'D/gamma' : Item(status=' ', wc_rev=6), 6213 'D/G' : Item(status=' ', wc_rev=6), 6214 'D/G/pi' : Item(status=' ', wc_rev=6), 6215 'D/G/rho' : Item(status=' ', wc_rev=6), 6216 'D/G/tau' : Item(status=' ', wc_rev=6), 6217 'D/H' : Item(status=' ', wc_rev=6), 6218 'D/H/chi' : Item(status=' ', wc_rev=6), 6219 'D/H/omega' : Item(status=' ', wc_rev=6), 6220 'D/H/psi' : Item(status=' ', wc_rev=6), 6221 }) 6222 expected_disk = wc.State('', { 6223 '' : Item(props={SVN_PROP_MERGEINFO : '/A_COPY:3-8'}), 6224 'bfile2' : Item(bfile2_content), 6225 'bfile1' : Item(bfile1_content), 6226 'tfile2' : Item(tfile2_content), 6227 'tfile1' : Item(tfile1_content), 6228 'mu' : Item("This is the file 'mu'.\n"), 6229 'C' : Item(), 6230 'D' : Item(), 6231 'B' : Item(), 6232 'B/lambda' : Item("This is the file 'lambda'.\n"), 6233 'B/E' : Item(), 6234 'B/E/alpha' : Item("This is the file 'alpha'.\n"), 6235 'B/E/beta' : Item("This is the file 'beta'.\n"), 6236 'B/F' : Item(), 6237 'D/gamma' : Item("This is the file 'gamma'.\n"), 6238 'D/G' : Item(), 6239 'D/G/pi' : Item("This is the file 'pi'.\n"), 6240 'D/G/rho' : Item("This is the file 'rho'.\n"), 6241 'D/G/tau' : Item("This is the file 'tau'.\n"), 6242 'D/H' : Item(), 6243 'D/H/chi' : Item("This is the file 'chi'.\n"), 6244 'D/H/omega' : Item("This is the file 'omega'.\n"), 6245 'D/H/psi' : Item("This is the file 'psi'.\n"), 6246 }) 6247 6248 expected_skip = wc.State(A_path, {}) 6249 6250 svntest.actions.run_and_verify_merge(A_path, '2', '8', 6251 sbox.repo_url + '/A_COPY', None, 6252 expected_output, 6253 expected_mergeinfo_output, 6254 expected_elision_output, 6255 expected_disk, 6256 expected_status, 6257 expected_skip, 6258 check_props=True) 6259 6260#---------------------------------------------------------------------- 6261@SkipUnless(server_has_mergeinfo) 6262def update_loses_mergeinfo(sbox): 6263 "update does not merge mergeinfo" 6264 6265 """ 6266 When a working copy path receives a fresh svn:mergeinfo property due to 6267 an update, and the path has local mergeinfo changes, then the local 6268 mergeinfo should be merged with the incoming mergeinfo. 6269 """ 6270 6271 sbox.build() 6272 wc_dir = sbox.wc_dir 6273 A_C_wc_dir = sbox.ospath('A/C') 6274 A_B_url = sbox.repo_url + '/A/B' 6275 A_B_J_url = sbox.repo_url + '/A/B/J' 6276 A_B_K_url = sbox.repo_url + '/A/B/K' 6277 svntest.actions.run_and_verify_svn(['Committing transaction...\n', 6278 'Committed revision 2.\n'], 6279 [], 6280 'mkdir', '-m', 'rev 2', A_B_J_url) 6281 svntest.actions.run_and_verify_svn(['Committing transaction...\n', 6282 'Committed revision 3.\n'], 6283 [], 6284 'mkdir', '-m', 'rev 3', A_B_K_url) 6285 6286 other_wc = sbox.add_wc_path('other') 6287 svntest.actions.duplicate_dir(wc_dir, other_wc) 6288 6289 expected_output = wc.State(A_C_wc_dir, {'J' : Item(status='A ')}) 6290 expected_mergeinfo_output = wc.State(A_C_wc_dir, { 6291 '' : Item(status=' U') 6292 }) 6293 expected_elision_output = wc.State(A_C_wc_dir, { 6294 }) 6295 expected_disk = wc.State('', { 6296 'J' : Item(), 6297 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B:2'}), 6298 }) 6299 expected_status = wc.State(A_C_wc_dir, 6300 { '' : Item(wc_rev=1, status=' M'), 6301 'J' : Item(status='A ', 6302 wc_rev='-', copied='+') 6303 } 6304 ) 6305 expected_skip = wc.State('', { }) 6306 svntest.actions.run_and_verify_merge(A_C_wc_dir, '1', '2', 6307 A_B_url, None, 6308 expected_output, 6309 expected_mergeinfo_output, 6310 expected_elision_output, 6311 expected_disk, 6312 expected_status, 6313 expected_skip, 6314 check_props=1) 6315 expected_output = wc.State(A_C_wc_dir, { 6316 '' : Item(verb='Sending'), 6317 'J' : Item(verb='Adding') 6318 }) 6319 expected_status = wc.State(A_C_wc_dir, 6320 { '' : Item(status=' ', wc_rev=4), 6321 'J' : Item(status=' ', wc_rev=4) 6322 } 6323 ) 6324 svntest.actions.run_and_verify_commit(A_C_wc_dir, 6325 expected_output, 6326 expected_status) 6327 6328 other_A_C_wc_dir = os.path.join(other_wc, 'A', 'C') 6329 expected_output = wc.State(other_A_C_wc_dir, {'K' : Item(status='A ')}) 6330 expected_mergeinfo_output = wc.State(other_A_C_wc_dir, { 6331 '' : Item(status=' U') 6332 }) 6333 expected_elision_output = wc.State(other_A_C_wc_dir, { 6334 }) 6335 expected_disk = wc.State('', { 6336 'K' : Item(), 6337 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B:3'}), 6338 }) 6339 expected_status = wc.State(other_A_C_wc_dir, 6340 { '' : Item(wc_rev=1, status=' M'), 6341 'K' : Item(status='A ', 6342 wc_rev='-', copied='+') 6343 } 6344 ) 6345 expected_skip = wc.State('', { }) 6346 svntest.actions.run_and_verify_merge(other_A_C_wc_dir, '2', '3', 6347 A_B_url, None, 6348 expected_output, 6349 expected_mergeinfo_output, 6350 expected_elision_output, 6351 expected_disk, 6352 expected_status, 6353 expected_skip, 6354 check_props=1) 6355 expected_output = wc.State(other_A_C_wc_dir, 6356 {'J' : Item(status='A '), 6357 '' : Item(status=' G') 6358 } 6359 ) 6360 expected_disk = wc.State('', { 6361 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B:2-3'}), 6362 'J' : Item(), 6363 'K' : Item(), 6364 }) 6365 expected_status = wc.State(other_A_C_wc_dir, 6366 { '' : Item(wc_rev=4, status=' M'), 6367 'J' : Item(status=' ', wc_rev='4'), 6368 'K' : Item(status='A ', 6369 wc_rev='-', copied='+') 6370 } 6371 ) 6372 svntest.actions.run_and_verify_update(other_A_C_wc_dir, 6373 expected_output, 6374 expected_disk, 6375 expected_status, 6376 check_props=True) 6377 6378#---------------------------------------------------------------------- 6379# Tests part of issue# 2829. 6380@Issue(2829) 6381@SkipUnless(server_has_mergeinfo) 6382def merge_loses_mergeinfo(sbox): 6383 "merge should merge mergeinfo" 6384 6385 """ 6386 When a working copy has no mergeinfo(due to local full revert of all merges), 6387 and merge is attempted for someother revision rX, The new mergeinfo should be 6388 /merge/src: rX not all the reverted ones reappearing along with rX. 6389 """ 6390 6391 sbox.build() 6392 wc_dir = sbox.wc_dir 6393 A_C_wc_dir = sbox.ospath('A/C') 6394 A_B_url = sbox.repo_url + '/A/B' 6395 A_B_J_url = sbox.repo_url + '/A/B/J' 6396 A_B_K_url = sbox.repo_url + '/A/B/K' 6397 svntest.actions.run_and_verify_svn(['Committing transaction...\n', 6398 'Committed revision 2.\n'], 6399 [], 6400 'mkdir', '-m', 'rev 2', A_B_J_url) 6401 svntest.actions.run_and_verify_svn(['Committing transaction...\n', 6402 'Committed revision 3.\n'], 6403 [], 6404 'mkdir', '-m', 'rev 3', A_B_K_url) 6405 6406 expected_output = wc.State(A_C_wc_dir, {'J' : Item(status='A ')}) 6407 expected_mergeinfo_output = wc.State(A_C_wc_dir, { 6408 '' : Item(status=' U'), 6409 }) 6410 expected_elision_output = wc.State(A_C_wc_dir, { 6411 }) 6412 expected_disk = wc.State('', { 6413 'J' : Item(), 6414 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B:2'}), 6415 }) 6416 expected_status = wc.State(A_C_wc_dir, 6417 { '' : Item(wc_rev=1, status=' M'), 6418 'J' : Item(status='A ', 6419 wc_rev='-', copied='+') 6420 } 6421 ) 6422 expected_skip = wc.State('', { }) 6423 svntest.actions.run_and_verify_merge(A_C_wc_dir, '1', '2', 6424 A_B_url, None, 6425 expected_output, 6426 expected_mergeinfo_output, 6427 expected_elision_output, 6428 expected_disk, 6429 expected_status, 6430 expected_skip, 6431 check_props=1) 6432 expected_output = wc.State(A_C_wc_dir, { 6433 '' : Item(verb='Sending'), 6434 'J' : Item(verb='Adding') 6435 }) 6436 expected_status = wc.State(A_C_wc_dir, 6437 { '' : Item(status=' ', wc_rev=4), 6438 'J' : Item(status=' ', wc_rev=4) 6439 } 6440 ) 6441 svntest.actions.run_and_verify_commit(A_C_wc_dir, 6442 expected_output, 6443 expected_status) 6444 expected_output = wc.State(A_C_wc_dir, {'J' : Item(status='D ')}) 6445 expected_elision_output = wc.State(A_C_wc_dir, { 6446 '' : Item(status=' U'), 6447 }) 6448 expected_disk = wc.State('', {}) 6449 expected_status = wc.State(A_C_wc_dir, 6450 { '' : Item(wc_rev=4, status=' M'), 6451 'J' : Item(wc_rev=4, status='D ') 6452 } 6453 ) 6454 svntest.actions.run_and_verify_merge(A_C_wc_dir, '2', '1', 6455 A_B_url, None, 6456 expected_output, 6457 expected_mergeinfo_output, 6458 expected_elision_output, 6459 expected_disk, 6460 expected_status, 6461 expected_skip, 6462 check_props=1) 6463 6464 expected_output = wc.State(A_C_wc_dir, {'K' : Item(status='A ')}) 6465 expected_disk = wc.State('', { 6466 'K' : Item(), 6467 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B:3'}), 6468 }) 6469 expected_status = wc.State(A_C_wc_dir, 6470 { '' : Item(wc_rev=4, status=' M'), 6471 'K' : Item(status='A ', 6472 wc_rev='-', copied='+'), 6473 'J' : Item(wc_rev=4, status='D ') 6474 } 6475 ) 6476 expected_mergeinfo_output = wc.State(A_C_wc_dir, { 6477 '' : Item(status=' G'), 6478 }) 6479 expected_elision_output = wc.State(A_C_wc_dir, { 6480 }) 6481 svntest.actions.run_and_verify_merge(A_C_wc_dir, '2', '3', 6482 A_B_url, None, 6483 expected_output, 6484 expected_mergeinfo_output, 6485 expected_elision_output, 6486 expected_disk, 6487 expected_status, 6488 expected_skip, 6489 check_props=1) 6490 6491#---------------------------------------------------------------------- 6492@Issue(2853) 6493def single_file_replace_style_merge_capability(sbox): 6494 "replace-style merge capability for a single file" 6495 6496 # Test for issue #2853, do_single_file_merge() lacks "Replace-style 6497 # merge" capability 6498 6499 sbox.build() 6500 wc_dir = sbox.wc_dir 6501 iota_path = sbox.ospath('iota') 6502 mu_path = sbox.ospath('A/mu') 6503 6504 # delete mu and replace it with a copy of iota 6505 svntest.main.run_svn(None, 'rm', mu_path) 6506 svntest.main.run_svn(None, 'mv', iota_path, mu_path) 6507 6508 expected_status = svntest.actions.get_virginal_state(wc_dir, 1) 6509 expected_status.tweak('A/mu', status=' ', wc_rev=2) 6510 expected_status.remove('iota') 6511 expected_output = svntest.wc.State(wc_dir, { 6512 'iota': Item(verb='Deleting'), 6513 'A/mu': Item(verb='Replacing'), 6514 }) 6515 svntest.actions.run_and_verify_commit(wc_dir, 6516 expected_output, 6517 expected_status) 6518 6519 # Merge the file mu alone to rev1 6520 svntest.actions.run_and_verify_svn(expected_merge_output(None, 6521 ['R ' + mu_path + '\n']), 6522 [], 6523 'merge', 6524 mu_path + '@2', 6525 mu_path + '@1', 6526 mu_path) 6527 6528#---------------------------------------------------------------------- 6529# Test for issue 2786 fix. 6530@Issue(2786) 6531@SkipUnless(server_has_mergeinfo) 6532def merge_to_out_of_date_target(sbox): 6533 "merge to ood path can lead to inaccurate mergeinfo" 6534 6535 # Create a WC with a branch. 6536 sbox.build() 6537 wc_dir = sbox.wc_dir 6538 wc_disk, wc_status = set_up_branch(sbox, False, 1) 6539 6540 # Make second working copy 6541 other_wc = sbox.add_wc_path('other') 6542 svntest.actions.duplicate_dir(wc_dir, other_wc) 6543 6544 # Some paths we'll care about 6545 A_COPY_H_path = sbox.ospath('A_COPY/D/H') 6546 other_A_COPY_H_path = os.path.join(other_wc, "A_COPY", "D", "H") 6547 6548 # Merge -c3 into A_COPY/D/H of first WC. 6549 expected_output = wc.State(A_COPY_H_path, { 6550 'psi' : Item(status='U ') 6551 }) 6552 expected_mergeinfo_output = wc.State(A_COPY_H_path, { 6553 '' : Item(status=' U'), 6554 }) 6555 expected_elision_output = wc.State(A_COPY_H_path, { 6556 }) 6557 expected_status = wc.State(A_COPY_H_path, { 6558 '' : Item(status=' M', wc_rev=2), 6559 'psi' : Item(status='M ', wc_rev=2), 6560 'omega' : Item(status=' ', wc_rev=2), 6561 'chi' : Item(status=' ', wc_rev=2), 6562 }) 6563 expected_disk = wc.State('', { 6564 '' : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:3'}), 6565 'psi' : Item("New content"), 6566 'omega' : Item("This is the file 'omega'.\n"), 6567 'chi' : Item("This is the file 'chi'.\n"), 6568 }) 6569 expected_skip = wc.State(A_COPY_H_path, { }) 6570 svntest.actions.run_and_verify_merge(A_COPY_H_path, '2', '3', 6571 sbox.repo_url + '/A/D/H', None, 6572 expected_output, 6573 expected_mergeinfo_output, 6574 expected_elision_output, 6575 expected_disk, 6576 expected_status, expected_skip, 6577 check_props=True) 6578 6579 # Commit merge to first WC. 6580 wc_status.tweak('A_COPY/D/H/psi', 'A_COPY/D/H', wc_rev=7) 6581 expected_output = svntest.wc.State(wc_dir, { 6582 'A_COPY/D/H' : Item(verb='Sending'), 6583 'A_COPY/D/H/psi': Item(verb='Sending'), 6584 }) 6585 svntest.actions.run_and_verify_commit(wc_dir, 6586 expected_output, 6587 wc_status) 6588 6589 # Merge -c6 into A_COPY/D/H of other WC. 6590 expected_output = wc.State(other_A_COPY_H_path, { 6591 'omega' : Item(status='U ') 6592 }) 6593 expected_mergeinfo_output = wc.State(other_A_COPY_H_path, { 6594 '' : Item(status=' U'), 6595 }) 6596 expected_elision_output = wc.State(other_A_COPY_H_path, { 6597 }) 6598 expected_status = wc.State(other_A_COPY_H_path, { 6599 '' : Item(status=' M', wc_rev=2), 6600 'psi' : Item(status=' ', wc_rev=2), 6601 'omega' : Item(status='M ', wc_rev=2), 6602 'chi' : Item(status=' ', wc_rev=2), 6603 }) 6604 expected_disk = wc.State('', { 6605 '' : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:6'}), 6606 'psi' : Item("This is the file 'psi'.\n"), 6607 'omega' : Item("New content"), 6608 'chi' : Item("This is the file 'chi'.\n"), 6609 }) 6610 expected_skip = wc.State(other_A_COPY_H_path, { }) 6611 svntest.actions.run_and_verify_merge(other_A_COPY_H_path, '5', '6', 6612 sbox.repo_url + '/A/D/H', None, 6613 expected_output, 6614 expected_mergeinfo_output, 6615 expected_elision_output, 6616 expected_disk, 6617 expected_status, expected_skip, 6618 check_props=1) 6619 6620 # Update A_COPY/D/H in other WC. Local mergeinfo for r6 on A_COPY/D/H 6621 # should be *merged* with r3 from first WC. 6622 expected_output = svntest.wc.State(other_A_COPY_H_path, { 6623 '' : Item(status=' G'), 6624 'psi' : Item(status='U ') 6625 }) 6626 other_disk = wc.State('', { 6627 '' : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:3,6'}), 6628 'psi' : Item(contents="New content"), 6629 'chi' : Item("This is the file 'chi'.\n"), 6630 'omega' : Item(contents="New content"), 6631 }) 6632 other_status = wc.State(other_A_COPY_H_path,{ 6633 '' : Item(wc_rev=7, status=' M'), 6634 'chi' : Item(wc_rev=7, status=' '), 6635 'psi' : Item(wc_rev=7, status=' '), 6636 'omega' : Item(wc_rev=7, status='M ') 6637 }) 6638 svntest.actions.run_and_verify_update(other_A_COPY_H_path, 6639 expected_output, 6640 other_disk, 6641 other_status, 6642 check_props=True) 6643 6644#---------------------------------------------------------------------- 6645@SkipUnless(server_has_mergeinfo) 6646def merge_with_depth_files(sbox): 6647 "merge test for --depth files" 6648 6649 sbox.build() 6650 wc_dir = sbox.wc_dir 6651 6652 # Some paths we'll care about 6653 mu_path = sbox.ospath('A/mu') 6654 gamma_path = sbox.ospath('A/D/gamma') 6655 Acopy_path = sbox.ospath('A_copy') 6656 Acopy_mu_path = sbox.ospath('A_copy/mu') 6657 A_url = sbox.repo_url + '/A' 6658 Acopy_url = sbox.repo_url + '/A_copy' 6659 6660 # Copy A_url to A_copy_url 6661 svntest.actions.run_and_verify_svn(None, [], 'cp', 6662 A_url, Acopy_url, 6663 '-m', 'create a new copy of A') 6664 6665 svntest.main.file_write(mu_path, "this is file 'mu' modified.\n") 6666 svntest.main.file_write(gamma_path, "this is file 'gamma' modified.\n") 6667 6668 # Create expected output tree for commit 6669 expected_output = wc.State(wc_dir, { 6670 'A/mu' : Item(verb='Sending'), 6671 'A/D/gamma' : Item(verb='Sending'), 6672 }) 6673 6674 # Create expected status tree for commit 6675 expected_status = svntest.actions.get_virginal_state(wc_dir, 1) 6676 expected_status.add({ 6677 'A/mu' : Item(status=' ', wc_rev=3), 6678 'A/D/gamma' : Item(status=' ', wc_rev=3), 6679 }) 6680 6681 # Commit the modified contents 6682 svntest.actions.run_and_verify_commit(wc_dir, 6683 expected_output, 6684 expected_status) 6685 6686 # Update working copy 6687 svntest.actions.run_and_verify_svn(None, [], 6688 'up', Acopy_path) 6689 6690 # Merge r1:3 into A_copy with --depth files. The merge only affects 6691 # 'A_copy' and its one file child 'mu', so 'A_copy' gets non-inheritable 6692 # mergeinfo for -r1:3 and 'mu' gets its own complete set of mergeinfo: 6693 # r1 from its parent, and r1:3 from the merge itself. 6694 expected_output = wc.State(Acopy_path, { 6695 'mu' : Item(status='U '), 6696 }) 6697 expected_mergeinfo_output = wc.State(Acopy_path, { 6698 '' : Item(status=' U'), 6699 'mu' : Item(status=' U'), 6700 }) 6701 expected_elision_output = wc.State(Acopy_path, { 6702 }) 6703 expected_status = wc.State(Acopy_path, { 6704 '' : Item(status=' M'), 6705 'B' : Item(status=' '), 6706 'mu' : Item(status='MM'), 6707 'B/E' : Item(status=' '), 6708 'B/E/alpha' : Item(status=' '), 6709 'B/E/beta' : Item(status=' '), 6710 'B/lambda' : Item(status=' '), 6711 'B/F' : Item(status=' '), 6712 'C' : Item(status=' '), 6713 'D' : Item(status=' '), 6714 'D/G' : Item(status=' '), 6715 'D/G/pi' : Item(status=' '), 6716 'D/G/rho' : Item(status=' '), 6717 'D/G/tau' : Item(status=' '), 6718 'D/gamma' : Item(status=' '), 6719 'D/H' : Item(status=' '), 6720 'D/H/chi' : Item(status=' '), 6721 'D/H/psi' : Item(status=' '), 6722 'D/H/omega' : Item(status=' '), 6723 }) 6724 expected_status.tweak(wc_rev=3) 6725 expected_disk = wc.State('', { 6726 '' : Item(props={SVN_PROP_MERGEINFO : '/A:2-3*'}), 6727 'B' : Item(), 6728 'mu' : Item("this is file 'mu' modified.\n", 6729 props={SVN_PROP_MERGEINFO : '/A/mu:2-3'}), 6730 'B/E' : Item(), 6731 'B/E/alpha' : Item("This is the file 'alpha'.\n"), 6732 'B/E/beta' : Item("This is the file 'beta'.\n"), 6733 'B/lambda' : Item("This is the file 'lambda'.\n"), 6734 'B/F' : Item(), 6735 'C' : Item(), 6736 'D' : Item(), 6737 'D/G' : Item(), 6738 'D/G/pi' : Item("This is the file 'pi'.\n"), 6739 'D/G/rho' : Item("This is the file 'rho'.\n"), 6740 'D/G/tau' : Item("This is the file 'tau'.\n"), 6741 'D/gamma' : Item("This is the file 'gamma'.\n"), 6742 'D/H' : Item(), 6743 'D/H/chi' : Item("This is the file 'chi'.\n"), 6744 'D/H/psi' : Item("This is the file 'psi'.\n"), 6745 'D/H/omega' : Item("This is the file 'omega'.\n"), 6746 }) 6747 expected_skip = wc.State(Acopy_path, { }) 6748 svntest.actions.run_and_verify_merge(Acopy_path, '1', '3', 6749 sbox.repo_url + '/A', None, 6750 expected_output, 6751 expected_mergeinfo_output, 6752 expected_elision_output, 6753 expected_disk, 6754 expected_status, expected_skip, 6755 [], True, True, 6756 '--depth', 'files', Acopy_path) 6757 6758#---------------------------------------------------------------------- 6759# Test for issue #2976 Subtrees can lose non-inheritable ranges. 6760# 6761# Also test for a bug with paths added as the immediate child of the 6762# merge target when the merge target has non-inheritable mergeinfo 6763# and is also the current working directory, see 6764# http://svn.haxx.se/dev/archive-2008-12/0133.shtml. 6765# 6766# Test for issue #3392 'Parsing error with reverse merges and 6767# non-inheritable mergeinfo. 6768# 6769# Test issue #3407 'Shallow merges incorrectly set mergeinfo on children'. 6770@SkipUnless(server_has_mergeinfo) 6771@Issues(2976,3392,3407,4057) 6772def merge_away_subtrees_noninheritable_ranges(sbox): 6773 "subtrees can lose non-inheritable ranges" 6774 6775 sbox.build() 6776 wc_dir = sbox.wc_dir 6777 wc_disk, wc_status = set_up_branch(sbox, nbr_of_branches=2) 6778 6779 # Some paths we'll care about 6780 H_path = sbox.ospath('A/D/H') 6781 D_COPY_path = sbox.ospath('A_COPY/D') 6782 A_COPY_path = sbox.ospath('A_COPY') 6783 nu_path = sbox.ospath('A/nu') 6784 mu_path = sbox.ospath('A/mu') 6785 mu_2_path = sbox.ospath('A_COPY_2/mu') 6786 D_COPY_2_path = sbox.ospath('A_COPY_2/D') 6787 H_COPY_2_path = sbox.ospath('A_COPY_2/D/H') 6788 mu_COPY_path = sbox.ospath('A_COPY/mu') 6789 nu_COPY_path = sbox.ospath('A_COPY/nu') 6790 6791 # Make a change to directory A/D/H and commit as r8. 6792 svntest.actions.run_and_verify_svn(exp_noop_up_out(7), [], 6793 'update', wc_dir) 6794 6795 svntest.actions.run_and_verify_svn( 6796 ["property 'prop:name' set on '" + H_path + "'\n"], [], 6797 'ps', 'prop:name', 'propval', H_path) 6798 expected_output = svntest.wc.State(wc_dir, { 6799 'A/D/H' : Item(verb='Sending'),}) 6800 wc_status.tweak(wc_rev=7) 6801 wc_status.tweak('A/D/H', wc_rev=8) 6802 svntest.actions.run_and_verify_commit(wc_dir, expected_output, wc_status) 6803 6804 # Merge r6:8 --depth immediates to A_COPY/D. This should merge the 6805 # prop change from r8 to A_COPY/H but not the change to A_COPY/D/H/omega 6806 # from r7 since that is below the depth we are merging to. Instead, 6807 # non-inheritable mergeinfo should be set on the immediate directory 6808 # child of A_COPY/D that is affected by the merge: A_COPY/D/H. 6809 expected_output = wc.State(D_COPY_path, { 6810 'H' : Item(status=' U'), 6811 }) 6812 expected_mergeinfo_output = wc.State(D_COPY_path, { 6813 '' : Item(status=' U'), 6814 'H' : Item(status=' U'), 6815 }) 6816 expected_elision_output = wc.State(D_COPY_path, { 6817 }) 6818 expected_status = wc.State(D_COPY_path, { 6819 '' : Item(status=' M', wc_rev=7), 6820 'H' : Item(status=' M', wc_rev=7), 6821 'H/chi' : Item(status=' ', wc_rev=7), 6822 'H/omega' : Item(status=' ', wc_rev=7), 6823 'H/psi' : Item(status=' ', wc_rev=7), 6824 'G' : Item(status=' ', wc_rev=7), 6825 'G/pi' : Item(status=' ', wc_rev=7), 6826 'G/rho' : Item(status=' ', wc_rev=7), 6827 'G/tau' : Item(status=' ', wc_rev=7), 6828 'gamma' : Item(status=' ', wc_rev=7), 6829 }) 6830 expected_disk = wc.State('', { 6831 '' : Item(props={SVN_PROP_MERGEINFO : '/A/D:7-8'}), 6832 'H' : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:7-8*', 6833 'prop:name' : 'propval'}), 6834 'H/chi' : Item("This is the file 'chi'.\n"), 6835 'H/omega' : Item("This is the file 'omega'.\n"), 6836 'H/psi' : Item("This is the file 'psi'.\n"), 6837 'G' : Item(), 6838 'G/pi' : Item("This is the file 'pi'.\n"), 6839 'G/rho' : Item("This is the file 'rho'.\n"), 6840 'G/tau' : Item("This is the file 'tau'.\n"), 6841 'gamma' : Item("This is the file 'gamma'.\n"), 6842 }) 6843 expected_skip = wc.State(D_COPY_path, { }) 6844 svntest.actions.run_and_verify_merge(D_COPY_path, '6', '8', 6845 sbox.repo_url + '/A/D', None, 6846 expected_output, 6847 expected_mergeinfo_output, 6848 expected_elision_output, 6849 expected_disk, 6850 expected_status, expected_skip, 6851 [], True, True, 6852 '--depth', 'immediates', D_COPY_path) 6853 6854 # Repeat the previous merge but at default depth of infinity. The change 6855 # to A_COPY/D/H/omega should now happen and the non-inheritable ranges on 6856 # A_COPY/D/G and A_COPY/D/H be changed to inheritable and then elide to 6857 # A_COPY/D. 6858 expected_output = wc.State(D_COPY_path, { 6859 'H/omega' : Item(status='U '), 6860 }) 6861 expected_mergeinfo_output = wc.State(D_COPY_path, { 6862 '' : Item(status=' G'), 6863 'H' : Item(status=' G'), 6864 'H/omega' : Item(status=' G'), 6865 }) 6866 expected_elision_output = wc.State(D_COPY_path, { 6867 'H' : Item(status=' U'), 6868 'H/omega' : Item(status=' U'), 6869 }) 6870 expected_disk.tweak('', props={SVN_PROP_MERGEINFO : '/A/D:7-8'}) 6871 expected_disk.tweak('H', props={'prop:name' : 'propval'}) 6872 expected_disk.tweak('G', props={}) 6873 expected_disk.tweak('H/omega', contents="New content") 6874 expected_status.tweak('G', status=' ') 6875 expected_status.tweak('H/omega', status='M ') 6876 svntest.actions.run_and_verify_merge(D_COPY_path, '6', '8', 6877 sbox.repo_url + '/A/D', None, 6878 expected_output, 6879 expected_mergeinfo_output, 6880 expected_elision_output, 6881 expected_disk, 6882 expected_status, expected_skip, 6883 [], True, True) 6884 6885 # Now test the problem described in 6886 # http://svn.haxx.se/dev/archive-2008-12/0133.shtml. 6887 # 6888 # First revert all local mods. 6889 svntest.actions.run_and_verify_svn(None, [], 'revert', '-R', wc_dir) 6890 6891 # r9: Merge all available revisions from A to A_COPY at a depth of empty 6892 # this will create non-inheritable mergeinfo on A_COPY. 6893 svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir) 6894 wc_status.tweak(wc_rev=8) 6895 svntest.actions.run_and_verify_svn(None, [], 6896 'merge', '--depth', 'empty', 6897 sbox.repo_url + '/A', A_COPY_path) 6898 wc_status.tweak('A_COPY', wc_rev=9) 6899 expected_output = wc.State(wc_dir, {'A_COPY' : Item(verb='Sending')}) 6900 svntest.actions.run_and_verify_commit(wc_dir, expected_output, wc_status) 6901 6902 # r10: Add the file A/nu. 6903 svntest.main.file_write(nu_path, "This is the file 'nu'.\n") 6904 svntest.actions.run_and_verify_svn(None, [], 'add', nu_path) 6905 expected_output = wc.State(wc_dir, {'A/nu' : Item(verb='Adding')}) 6906 wc_status.add({'A/nu' : Item(status=' ', wc_rev=10)}) 6907 svntest.actions.run_and_verify_commit(wc_dir, expected_output, 6908 wc_status) 6909 6910 # Now merge -c10 from A to A_COPY. 6911 svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir) 6912 expected_output = wc.State('', { 6913 'nu': Item(status='A '), 6914 }) 6915 expected_mergeinfo_output = wc.State('', { 6916 '' : Item(status=' U'), 6917 'nu' : Item(status=' U'), 6918 }) 6919 expected_elision_output = wc.State('', { 6920 }) 6921 expected_status = wc.State('', { 6922 '' : Item(status=' M'), 6923 'nu' : Item(status='A ', copied='+'), 6924 'B' : Item(status=' '), 6925 'mu' : Item(status=' '), 6926 'B/E' : Item(status=' '), 6927 'B/E/alpha' : Item(status=' '), 6928 'B/E/beta' : Item(status=' '), 6929 'B/lambda' : Item(status=' '), 6930 'B/F' : Item(status=' '), 6931 'C' : Item(status=' '), 6932 'D' : Item(status=' '), 6933 'D/G' : Item(status=' '), 6934 'D/G/pi' : Item(status=' '), 6935 'D/G/rho' : Item(status=' '), 6936 'D/G/tau' : Item(status=' '), 6937 'D/gamma' : Item(status=' '), 6938 'D/H' : Item(status=' '), 6939 'D/H/chi' : Item(status=' '), 6940 'D/H/psi' : Item(status=' '), 6941 'D/H/omega' : Item(status=' '), 6942 }) 6943 expected_status.tweak(wc_rev=10) 6944 expected_status.tweak('nu', wc_rev='-') 6945 expected_disk = wc.State('', { 6946 '' : Item(props={SVN_PROP_MERGEINFO : '/A:2-8*,10'}), 6947 'nu' : Item("This is the file 'nu'.\n", 6948 props={SVN_PROP_MERGEINFO : '/A/nu:10'}), 6949 'B' : Item(), 6950 'mu' : Item("This is the file 'mu'.\n"), 6951 'B/E' : Item(), 6952 'B/E/alpha' : Item("This is the file 'alpha'.\n"), 6953 'B/E/beta' : Item("This is the file 'beta'.\n"), 6954 'B/lambda' : Item("This is the file 'lambda'.\n"), 6955 'B/F' : Item(), 6956 'C' : Item(), 6957 'D' : Item(), 6958 'D/G' : Item(), 6959 'D/G/pi' : Item("This is the file 'pi'.\n"), 6960 'D/G/rho' : Item("This is the file 'rho'.\n"), 6961 'D/G/tau' : Item("This is the file 'tau'.\n"), 6962 'D/gamma' : Item("This is the file 'gamma'.\n"), 6963 'D/H' : Item(), 6964 'D/H/chi' : Item("This is the file 'chi'.\n"), 6965 'D/H/psi' : Item("This is the file 'psi'.\n"), 6966 'D/H/omega' : Item("This is the file 'omega'.\n"), 6967 }) 6968 expected_skip = wc.State('.', { }) 6969 saved_cwd = os.getcwd() 6970 os.chdir(A_COPY_path) 6971 svntest.actions.run_and_verify_merge('', '9', '10', 6972 sbox.repo_url + '/A', None, 6973 expected_output, 6974 expected_mergeinfo_output, 6975 expected_elision_output, 6976 expected_disk, 6977 expected_status, 6978 expected_skip, 6979 check_props=True) 6980 os.chdir(saved_cwd) 6981 6982 # If a merge target has inheritable and non-inheritable ranges and has a 6983 # child with no explicit mergeinfo, test that a merge which brings 6984 # mergeinfo changes to that child (i.e. as part of the diff) properly 6985 # records mergeinfo on the child that includes both the incoming mergeinfo 6986 # *and* the mergeinfo inherited from it's parent. 6987 # 6988 # First revert all local changes and remove A_COPY/C/nu from disk. 6989 svntest.actions.run_and_verify_svn(None, [], 'revert', '-R', wc_dir) 6990 6991 # Make a text change to A_COPY_2/mu in r11 and then merge that 6992 # change to A/mu in r12. This will create mergeinfo of '/A_COPY_2/mu:11' 6993 # on A/mu. 6994 svntest.main.file_write(mu_2_path, 'new content') 6995 svntest.actions.run_and_verify_svn(None, [], 'ci', '-m', 'log msg', 6996 wc_dir) 6997 svntest.actions.run_and_verify_svn( 6998 expected_merge_output([[11]], 6999 ['U ' + mu_path + '\n', 7000 ' U ' + mu_path + '\n']), 7001 [], 'merge', '-c11', sbox.repo_url + '/A_COPY_2/mu', mu_path) 7002 svntest.actions.run_and_verify_svn(None, [], 'ci', '-m', 'log msg', 7003 wc_dir) 7004 7005 # Now merge r12 from A to A_COPY. A_COPY/mu should get the mergeinfo from 7006 # r12, '/A_COPY_2/mu:11' as well as mergeinfo describing the merge itself, 7007 # '/A/mu:12'. 7008 expected_output = wc.State('.', { 7009 'mu': Item(status='UG'), 7010 }) 7011 expected_mergeinfo_output = wc.State('.', { 7012 '' : Item(status=' U'), 7013 'mu' : Item(status=' G'), 7014 }) 7015 expected_elision_output = wc.State('.', { 7016 }) 7017 expected_status = wc.State('', { 7018 '' : Item(status=' M'), 7019 'B' : Item(status=' '), 7020 'mu' : Item(status='MM'), 7021 'B/E' : Item(status=' '), 7022 'B/E/alpha' : Item(status=' '), 7023 'B/E/beta' : Item(status=' '), 7024 'B/lambda' : Item(status=' '), 7025 'B/F' : Item(status=' '), 7026 'C' : Item(status=' '), 7027 'D' : Item(status=' '), 7028 'D/G' : Item(status=' '), 7029 'D/G/pi' : Item(status=' '), 7030 'D/G/rho' : Item(status=' '), 7031 'D/G/tau' : Item(status=' '), 7032 'D/gamma' : Item(status=' '), 7033 'D/H' : Item(status=' '), 7034 'D/H/chi' : Item(status=' '), 7035 'D/H/psi' : Item(status=' '), 7036 'D/H/omega' : Item(status=' '), 7037 }) 7038 expected_status.tweak(wc_rev=10) 7039 expected_disk = wc.State('', { 7040 '' : Item(props={SVN_PROP_MERGEINFO : '/A:2-8*,12'}), 7041 'B' : Item(), 7042 'mu' : Item("new content", 7043 props={SVN_PROP_MERGEINFO : '/A/mu:12\n/A_COPY_2/mu:11'}), 7044 'B/E' : Item(), 7045 'B/E/alpha' : Item("This is the file 'alpha'.\n"), 7046 'B/E/beta' : Item("This is the file 'beta'.\n"), 7047 'B/lambda' : Item("This is the file 'lambda'.\n"), 7048 'B/F' : Item(), 7049 'C' : Item(), 7050 'D' : Item(), 7051 'D/G' : Item(), 7052 'D/G/pi' : Item("This is the file 'pi'.\n"), 7053 'D/G/rho' : Item("This is the file 'rho'.\n"), 7054 'D/G/tau' : Item("This is the file 'tau'.\n"), 7055 'D/gamma' : Item("This is the file 'gamma'.\n"), 7056 'D/H' : Item(), 7057 'D/H/chi' : Item("This is the file 'chi'.\n"), 7058 'D/H/psi' : Item("This is the file 'psi'.\n"), 7059 'D/H/omega' : Item("This is the file 'omega'.\n"), 7060 }) 7061 expected_skip = wc.State('.', { }) 7062 saved_cwd = os.getcwd() 7063 os.chdir(A_COPY_path) 7064 # Don't do a dry-run, because it will differ due to the way merge 7065 # sets override mergeinfo on the children of paths with non-inheritable 7066 # ranges. 7067 svntest.actions.run_and_verify_merge('.', '11', '12', 7068 sbox.repo_url + '/A', None, 7069 expected_output, 7070 expected_mergeinfo_output, 7071 expected_elision_output, 7072 expected_disk, 7073 expected_status, 7074 expected_skip, 7075 [], True, False) 7076 os.chdir(saved_cwd) 7077 7078 # Test for issue #3392 7079 # 7080 # Revert local changes and update. 7081 svntest.actions.run_and_verify_svn(None, [], 'revert', '-R', wc_dir) 7082 svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir) 7083 7084 # Merge r8 from A/D/H to A_COPY_D/H at depth empty. Since r8 affects only 7085 # A_COPY/D/H itself, the resulting mergeinfo is inheritable. Commit this 7086 # merge as r13. 7087 expected_output = wc.State(H_COPY_2_path, { 7088 '' : Item(status=' U'), 7089 }) 7090 expected_mergeinfo_output = wc.State(H_COPY_2_path, { 7091 '' : Item(status=' U'), 7092 }) 7093 expected_elision_output = wc.State(H_COPY_2_path, { 7094 }) 7095 expected_status = wc.State(H_COPY_2_path, { 7096 '' : Item(status=' M', wc_rev=12), 7097 'psi' : Item(status=' ', wc_rev=12), 7098 'omega' : Item(status=' ', wc_rev=12), 7099 'chi' : Item(status=' ', wc_rev=12), 7100 }) 7101 expected_disk = wc.State('', { 7102 '' : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:8', 7103 "prop:name" : "propval"}), 7104 'psi' : Item("This is the file 'psi'.\n"), 7105 'omega' : Item("This is the file 'omega'.\n"), 7106 'chi' : Item("This is the file 'chi'.\n"), 7107 }) 7108 expected_skip = wc.State(H_COPY_2_path, {}) 7109 svntest.actions.run_and_verify_merge(H_COPY_2_path, '7', '8', 7110 sbox.repo_url + '/A/D/H', None, 7111 expected_output, 7112 expected_mergeinfo_output, 7113 expected_elision_output, 7114 expected_disk, 7115 expected_status, expected_skip, 7116 [], True, True, 7117 '--depth', 'empty', H_COPY_2_path) 7118 svntest.actions.run_and_verify_svn(None, [], 'commit', '-m', 7119 'log msg', wc_dir) 7120 svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir) 7121 # Now reverse the prior merge. Issue #3392 manifests itself here with 7122 # a mergeinfo parsing error: 7123 # >svn merge %url%/A/D/H merge_tests-62\A_COPY_2\D\H -c-8 7124 # --- Reverse-merging r8 into 'merge_tests-62\A_COPY_2\D\H': 7125 # U merge_tests-62\A_COPY_2\D\H 7126 # ..\..\..\subversion\libsvn_subr\mergeinfo.c:590: (apr_err=200020) 7127 # svn: Could not parse mergeinfo string '-8' 7128 # ..\..\..\subversion\libsvn_subr\kitchensink.c:52: (apr_err=200022) 7129 # svn: Negative revision number found parsing '-8' 7130 # 7131 # Status is identical but for the working revision. 7132 expected_status.tweak(wc_rev=13) 7133 # The mergeinfo and prop:name props should disappear. 7134 expected_disk.remove('') 7135 expected_elision_output = wc.State(H_COPY_2_path, { 7136 '' : Item(status=' U'), 7137 }) 7138 svntest.actions.run_and_verify_merge(H_COPY_2_path, '8', '7', 7139 sbox.repo_url + '/A/D/H', None, 7140 expected_output, 7141 expected_mergeinfo_output, 7142 expected_elision_output, 7143 expected_disk, 7144 expected_status, expected_skip, 7145 check_props=True) 7146 7147 # Test issue #3407 'Shallow merges incorrectly set mergeinfo on children'. 7148 # 7149 # Revert all local mods. 7150 svntest.actions.run_and_verify_svn(None, [], 'revert', '-R', wc_dir) 7151 7152 # Merge all available changes from A to A_COPY at --depth empty. Only the 7153 # mergeinfo on A_COPY should be affected. 7154 svntest.actions.run_and_verify_svn( 7155 expected_merge_output([[9,13]], 7156 [' U ' + A_COPY_path + '\n']), 7157 [], 'merge', '--depth', 'empty', 7158 sbox.repo_url + '/A', A_COPY_path) 7159 svntest.actions.run_and_verify_svn([A_COPY_path + ' - /A:2-13*\n'], 7160 [], 'pg', SVN_PROP_MERGEINFO, 7161 '-R', A_COPY_path) 7162 7163 # Merge all available changes from A to A_COPY at --depth files. Only the 7164 # mergeinfo on A_COPY and its file children should be affected. 7165 svntest.actions.run_and_verify_svn(None, [], 'revert', '-R', wc_dir) 7166 # Revisions 2-13 are already merged to A_COPY and now they will be merged 7167 # to A_COPY's file children. Due to the way we drive the merge editor 7168 # r2-3, which are inoperative on A_COPY's file children, do not show up 7169 # in the merge notifications, although those revs are included in the 7170 # recorded mergeinfo. 7171 expected_output = expected_merge_output([[4,13], # Merge notification 7172 [9,13], # Merge notification 7173 [2,13]], # Mergeinfo notification 7174 ['UU %s\n' % (mu_COPY_path), 7175 'A %s\n' % (nu_COPY_path), 7176 ' U %s\n' % (A_COPY_path), 7177 ' G %s\n' % (mu_COPY_path), 7178 ' U %s\n' % (nu_COPY_path),]) 7179 svntest.actions.run_and_verify_svn(expected_output, [], 7180 'merge', '--depth', 'files', 7181 sbox.repo_url + '/A', A_COPY_path) 7182 expected_output = svntest.verify.UnorderedOutput( 7183 [A_COPY_path + ' - /A:2-13*\n', 7184 mu_COPY_path + ' - /A/mu:2-13\n', 7185 nu_COPY_path + ' - /A/nu:10-13\n',]) 7186 svntest.actions.run_and_verify_svn(expected_output, 7187 [], 'pg', SVN_PROP_MERGEINFO, 7188 '-R', A_COPY_path) 7189 7190#---------------------------------------------------------------------- 7191# Test for issue #2827 7192# Handle merge info for sparsely-populated directories 7193@Issue(2827) 7194@SkipUnless(server_has_mergeinfo) 7195def merge_to_sparse_directories(sbox): 7196 "merge to sparse directories" 7197 7198 # Merges into sparse working copies should set non-inheritable mergeinfo 7199 # on the deepest directories present in the WC. 7200 7201 sbox.build() 7202 wc_dir = sbox.wc_dir 7203 wc_disk, wc_status = set_up_branch(sbox, False, 1) 7204 7205 # Some paths we'll care about 7206 A_path = sbox.ospath('A') 7207 D_path = sbox.ospath('A/D') 7208 I_path = sbox.ospath('A/C/I') 7209 G_path = sbox.ospath('A/D/G') 7210 A_COPY_path = sbox.ospath('A_COPY') 7211 7212 # Make a few more changes to the merge source... 7213 7214 # r7 - modify and commit A/mu 7215 svntest.main.file_write(sbox.ospath('A/mu'), 7216 "New content") 7217 expected_output = wc.State(wc_dir, {'A/mu' : Item(verb='Sending')}) 7218 wc_status.tweak('A/mu', wc_rev=7) 7219 svntest.actions.run_and_verify_commit(wc_dir, expected_output, 7220 wc_status) 7221 wc_disk.tweak('A/mu', contents="New content") 7222 7223 # r8 - Add a prop to A/D and commit. 7224 svntest.actions.run_and_verify_svn(exp_noop_up_out(7), [], 7225 'up', wc_dir) 7226 svntest.actions.run_and_verify_svn(["property 'prop:name' set on '" + 7227 D_path + "'\n"], [], 'ps', 7228 'prop:name', 'propval', D_path) 7229 expected_output = svntest.wc.State(wc_dir, { 7230 'A/D' : Item(verb='Sending'), 7231 }) 7232 wc_status.tweak(wc_rev=7) 7233 wc_status.tweak('A/D', wc_rev=8) 7234 svntest.actions.run_and_verify_commit(wc_dir, expected_output, wc_status) 7235 7236 # r9 - Add a prop to A and commit. 7237 svntest.actions.run_and_verify_svn(exp_noop_up_out(8), [], 7238 'up', wc_dir) 7239 svntest.actions.run_and_verify_svn(["property 'prop:name' set on '" + 7240 A_path + "'\n"], [], 'ps', 7241 'prop:name', 'propval', A_path) 7242 expected_output = svntest.wc.State(wc_dir, { 7243 'A' : Item(verb='Sending'), 7244 }) 7245 wc_status.tweak(wc_rev=8) 7246 wc_status.tweak('A', wc_rev=9) 7247 svntest.actions.run_and_verify_commit(wc_dir, expected_output, wc_status) 7248 7249 # Do an --immediates checkout of A_COPY 7250 immediates_dir = sbox.add_wc_path('immediates') 7251 expected_output = wc.State(immediates_dir, { 7252 'B' : Item(status='A '), 7253 'mu' : Item(status='A '), 7254 'C' : Item(status='A '), 7255 'D' : Item(status='A '), 7256 }) 7257 expected_disk = wc.State('', { 7258 'B' : Item(), 7259 'mu' : Item("This is the file 'mu'.\n"), 7260 'C' : Item(), 7261 'D' : Item(), 7262 }) 7263 svntest.actions.run_and_verify_checkout(sbox.repo_url + "/A_COPY", 7264 immediates_dir, 7265 expected_output, expected_disk, 7266 [], 7267 "--depth", "immediates") 7268 7269 # Merge r4:9 into the immediates WC. 7270 # The root of the immediates WC should get inheritable r4:9 as should 7271 # the one file present 'mu'. The three directory children present, 'B', 7272 # 'C', and 'D' are checked out at depth empty; the two of these affected 7273 # by the merge, 'B' and 'D', get non-inheritable mergeinfo for r4:9. 7274 # The root and 'D' do should also get the changes 7275 # that affect them directly (the prop adds from r8 and r9). 7276 # 7277 # Currently this fails due to r1424469. For a full explanation see 7278 # http://svn.haxx.se/dev/archive-2012-12/0472.shtml 7279 # and http://svn.haxx.se/dev/archive-2012-12/0475.shtml 7280 expected_output = wc.State(immediates_dir, { 7281 'D' : Item(status=' U'), 7282 'mu' : Item(status='U '), 7283 '' : Item(status=' U'), 7284 # Shadowed below skips 7285 'D/H/omega' : Item(status=' ', treeconflict='U'), 7286 'B/E/beta' : Item(status=' ', treeconflict='U'), 7287 }) 7288 expected_mergeinfo_output = wc.State(immediates_dir, { 7289 '' : Item(status=' U'), 7290 'B' : Item(status=' U'), 7291 'D' : Item(status=' U'), 7292 }) 7293 expected_elision_output = wc.State(immediates_dir, { 7294 }) 7295 expected_status = wc.State(immediates_dir, { 7296 '' : Item(status=' M', wc_rev=9), 7297 'B' : Item(status=' M', wc_rev=9), 7298 'mu' : Item(status='M ', wc_rev=9), 7299 'C' : Item(status=' ', wc_rev=9), 7300 'D' : Item(status=' M', wc_rev=9), 7301 }) 7302 expected_disk = wc.State('', { 7303 '' : Item(props={SVN_PROP_MERGEINFO : '/A:5-9', 7304 "prop:name" : "propval"}), 7305 'B' : Item(props={SVN_PROP_MERGEINFO : '/A/B:5-9*'}), 7306 'mu' : Item("New content"), 7307 'C' : Item(), 7308 'D' : Item(props={SVN_PROP_MERGEINFO : '/A/D:5-9*', 7309 "prop:name" : "propval"}), 7310 }) 7311 expected_skip = svntest.wc.State(immediates_dir, { 7312 'D/H' : Item(verb='Skipped missing target'), 7313 'B/E' : Item(verb='Skipped missing target'), 7314 }) 7315 svntest.actions.run_and_verify_merge(immediates_dir, '4', '9', 7316 sbox.repo_url + '/A', None, 7317 expected_output, 7318 expected_mergeinfo_output, 7319 expected_elision_output, 7320 expected_disk, 7321 expected_status, 7322 expected_skip, 7323 check_props=True) 7324 7325 # Do a --files checkout of A_COPY 7326 files_dir = sbox.add_wc_path('files') 7327 expected_output = wc.State(files_dir, { 7328 'mu' : Item(status='A '), 7329 }) 7330 expected_disk = wc.State('', { 7331 'mu' : Item("This is the file 'mu'.\n"), 7332 }) 7333 svntest.actions.run_and_verify_checkout(sbox.repo_url + "/A_COPY", 7334 files_dir, 7335 expected_output, expected_disk, 7336 [], 7337 "--depth", "files") 7338 7339 # Merge r4:9 into the files WC. 7340 # The root of the files WC should get non-inheritable r4:9 and its one 7341 # present child 'mu' should get the same but inheritable. The root 7342 # should also get the change that affects it directly (the prop add 7343 # from r9). 7344 expected_output = wc.State(files_dir, { 7345 'mu' : Item(status='U '), 7346 '' : Item(status=' U'), 7347 # Below the skips 7348 'D/H/omega' : Item(status=' ', treeconflict='U'), 7349 'B/E/beta' : Item(status=' ', treeconflict='U'), 7350 }) 7351 expected_mergeinfo_output = wc.State(files_dir, { 7352 '' : Item(status=' U'), 7353 'mu' : Item(status=' U'), 7354 }) 7355 expected_elision_output = wc.State(files_dir, { 7356 }) 7357 expected_status = wc.State(files_dir, { 7358 '' : Item(status=' M', wc_rev=9), 7359 'mu' : Item(status='MM', wc_rev=9), 7360 }) 7361 expected_disk = wc.State('', { 7362 '' : Item(props={SVN_PROP_MERGEINFO : '/A:5-9*', 7363 "prop:name" : "propval"}), 7364 'mu' : Item("New content", 7365 props={SVN_PROP_MERGEINFO : '/A/mu:5-9'}), 7366 }) 7367 expected_skip = svntest.wc.State(files_dir, { 7368 'D' : Item(verb='Skipped missing target'), 7369 'B' : Item(verb='Skipped missing target'), 7370 }) 7371 svntest.actions.run_and_verify_merge(files_dir, '4', '9', 7372 sbox.repo_url + '/A', None, 7373 expected_output, 7374 expected_mergeinfo_output, 7375 expected_elision_output, 7376 expected_disk, 7377 expected_status, 7378 expected_skip, 7379 check_props=True) 7380 7381 # Do an --empty checkout of A_COPY 7382 empty_dir = sbox.add_wc_path('empty') 7383 expected_output = wc.State(empty_dir, {}) 7384 expected_disk = wc.State('', {}) 7385 svntest.actions.run_and_verify_checkout(sbox.repo_url + "/A_COPY", 7386 empty_dir, 7387 expected_output, expected_disk, 7388 [], 7389 "--depth", "empty") 7390 7391 # Merge r4:9 into the empty WC. 7392 # The root of the files WC should get non-inheritable r4:9 and also get 7393 # the one change that affects it directly (the prop add from r9). 7394 expected_output = wc.State(empty_dir, { 7395 '' : Item(status=' U'), 7396 # Below the skips 7397 'B/E/beta' : Item(status=' ', treeconflict='U'), 7398 'D/H/omega' : Item(status=' ', treeconflict='U'), 7399 }) 7400 expected_mergeinfo_output = wc.State(empty_dir, { 7401 '' : Item(status=' U'), 7402 }) 7403 expected_elision_output = wc.State(empty_dir, { 7404 }) 7405 expected_status = wc.State(empty_dir, { 7406 '' : Item(status=' M', wc_rev=9), 7407 }) 7408 expected_disk = wc.State('', { 7409 '' : Item(props={SVN_PROP_MERGEINFO : '/A:5-9*', 7410 "prop:name" : "propval"}), 7411 }) 7412 expected_skip = svntest.wc.State(empty_dir, { 7413 'mu' : Item(verb='Skipped missing target'), 7414 'D' : Item(verb='Skipped missing target'), 7415 'B' : Item(verb='Skipped missing target'), 7416 }) 7417 svntest.actions.run_and_verify_merge(empty_dir, '4', '9', 7418 sbox.repo_url + '/A', None, 7419 expected_output, 7420 expected_mergeinfo_output, 7421 expected_elision_output, 7422 expected_disk, 7423 expected_status, 7424 expected_skip, 7425 check_props=True) 7426 7427 # Check that default depth for merge is infinity. 7428 # 7429 # Revert the previous changes to the immediates WC and update one 7430 # child in that WC to depth infinity. 7431 svntest.actions.run_and_verify_svn(None, [], 'revert', '-R', 7432 immediates_dir) 7433 svntest.actions.run_and_verify_svn(None, [], 'up', '--set-depth', 7434 'infinity', 7435 os.path.join(immediates_dir, 'D')) 7436 # Now merge r6 into the immediates WC, even though the root of the 7437 # is at depth immediates, the subtree rooted at child 'D' is fully 7438 # present, so a merge of r6 should affect 'D/H/omega'. 7439 expected_output = wc.State(immediates_dir, { 7440 'D/H/omega' : Item(status='U '), 7441 }) 7442 expected_mergeinfo_output = wc.State(immediates_dir, { 7443 '' : Item(status=' U'), 7444 }) 7445 expected_elision_output = wc.State(immediates_dir, { 7446 }) 7447 expected_status = wc.State(immediates_dir, { 7448 '' : Item(status=' M', wc_rev=9), 7449 'B' : Item(status=' ', wc_rev=9), 7450 'mu' : Item(status=' ', wc_rev=9), 7451 'C' : Item(status=' ', wc_rev=9), 7452 'D' : Item(status=' ', wc_rev=9), 7453 'D/gamma' : Item(status=' ', wc_rev=9), 7454 'D/G' : Item(status=' ', wc_rev=9), 7455 'D/G/pi' : Item(status=' ', wc_rev=9), 7456 'D/G/rho' : Item(status=' ', wc_rev=9), 7457 'D/G/tau' : Item(status=' ', wc_rev=9), 7458 'D/H' : Item(status=' ', wc_rev=9), 7459 'D/H/chi' : Item(status=' ', wc_rev=9), 7460 'D/H/omega' : Item(status='M ', wc_rev=9), 7461 'D/H/psi' : Item(status=' ', wc_rev=9), 7462 }) 7463 expected_disk = wc.State('', { 7464 '' : Item(props={SVN_PROP_MERGEINFO : '/A:6'}), 7465 'B' : Item(), 7466 'mu' : Item("This is the file 'mu'.\n"), 7467 'C' : Item(), 7468 'D' : Item(), 7469 'D/G' : Item(), 7470 'D/G/pi' : Item("This is the file 'pi'.\n"), 7471 'D/G/rho' : Item("This is the file 'rho'.\n"), 7472 'D/G/tau' : Item("This is the file 'tau'.\n"), 7473 'D/gamma' : Item("This is the file 'gamma'.\n"), 7474 'D/H' : Item(), 7475 'D/H/chi' : Item("This is the file 'chi'.\n"), 7476 'D/H/psi' : Item("This is the file 'psi'.\n"), 7477 'D/H/omega' : Item("New content"), 7478 }) 7479 expected_skip = wc.State(immediates_dir, {}) 7480 svntest.actions.run_and_verify_merge(immediates_dir, '5', '6', 7481 sbox.repo_url + '/A', None, 7482 expected_output, 7483 expected_mergeinfo_output, 7484 expected_elision_output, 7485 expected_disk, 7486 expected_status, 7487 expected_skip, 7488 check_props=True) 7489 7490#---------------------------------------------------------------------- 7491@SkipUnless(server_has_mergeinfo) 7492def merge_old_and_new_revs_from_renamed_dir(sbox): 7493 "merge -rold(before rename):head renamed dir" 7494 7495 # See the email on dev@ from Paul Burba, 2007-09-27, "RE: svn commit: 7496 # r26803 - [...]", <http://svn.haxx.se/dev/archive-2007-09/0706.shtml> or 7497 # <http://subversion.tigris.org/ds/viewMessage.do?dsForumId=462&dsMessageId=927127>. 7498 7499 # Create a WC with a single branch 7500 sbox.build() 7501 wc_dir = sbox.wc_dir 7502 wc_disk, wc_status = set_up_branch(sbox, True, 1) 7503 7504 # Some paths we'll care about 7505 A_url = sbox.repo_url + '/A' 7506 A_MOVED_url = sbox.repo_url + '/A_MOVED' 7507 A_COPY_path = sbox.ospath('A_COPY') 7508 mu_path = sbox.ospath('A/mu') 7509 A_MOVED_mu_path = sbox.ospath('A_MOVED/mu') 7510 7511 # Make a modification to A/mu 7512 svntest.main.file_write(mu_path, "This is the file 'mu' modified.\n") 7513 expected_output = wc.State(wc_dir, {'A/mu' : Item(verb='Sending')}) 7514 wc_status.add({'A/mu' : Item(status=' ', wc_rev=3)}) 7515 svntest.actions.run_and_verify_commit(wc_dir, expected_output, wc_status) 7516 7517 # Move A to A_MOVED 7518 svntest.actions.run_and_verify_svn(['Committing transaction...\n', 7519 'Committed revision 4.\n'], 7520 [], 'mv', '-m', 'mv A to A_MOVED', 7521 A_url, A_MOVED_url) 7522 7523 # Update the working copy to get A_MOVED 7524 svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir) 7525 7526 # Make a modification to A_MOVED/mu 7527 svntest.main.file_write(A_MOVED_mu_path, "This is 'mu' in A_MOVED.\n") 7528 expected_output = wc.State(wc_dir, {'A_MOVED/mu' : Item(verb='Sending')}) 7529 expected_status = svntest.actions.get_virginal_state(wc_dir, 4) 7530 expected_status.remove('A', 'A/mu', 'A/C', 'A/D', 'A/B', 'A/B/lambda', 7531 'A/B/E', 'A/B/E/alpha', 'A/B/E/beta', 'A/B/F', 7532 'A/D/gamma', 'A/D/G', 'A/D/G/pi', 'A/D/G/rho', 7533 'A/D/G/tau', 'A/D/H', 'A/D/H/chi', 'A/D/H/omega', 7534 'A/D/H/psi') 7535 expected_status.add({ 7536 '' : Item(status=' ', wc_rev=4), 7537 'iota' : Item(status=' ', wc_rev=4), 7538 'A_MOVED' : Item(status=' ', wc_rev=4), 7539 'A_MOVED/mu' : Item(status=' ', wc_rev=5), 7540 'A_MOVED/C' : Item(status=' ', wc_rev=4), 7541 'A_MOVED/D' : Item(status=' ', wc_rev=4), 7542 'A_MOVED/B' : Item(status=' ', wc_rev=4), 7543 'A_MOVED/B/lambda' : Item(status=' ', wc_rev=4), 7544 'A_MOVED/B/E' : Item(status=' ', wc_rev=4), 7545 'A_MOVED/B/E/alpha': Item(status=' ', wc_rev=4), 7546 'A_MOVED/B/E/beta' : Item(status=' ', wc_rev=4), 7547 'A_MOVED/B/F' : Item(status=' ', wc_rev=4), 7548 'A_MOVED/D/gamma' : Item(status=' ', wc_rev=4), 7549 'A_MOVED/D/G' : Item(status=' ', wc_rev=4), 7550 'A_MOVED/D/G/pi' : Item(status=' ', wc_rev=4), 7551 'A_MOVED/D/G/rho' : Item(status=' ', wc_rev=4), 7552 'A_MOVED/D/G/tau' : Item(status=' ', wc_rev=4), 7553 'A_MOVED/D/H' : Item(status=' ', wc_rev=4), 7554 'A_MOVED/D/H/chi' : Item(status=' ', wc_rev=4), 7555 'A_MOVED/D/H/omega': Item(status=' ', wc_rev=4), 7556 'A_MOVED/D/H/psi' : Item(status=' ', wc_rev=4), 7557 'A_COPY' : Item(status=' ', wc_rev=4), 7558 'A_COPY/mu' : Item(status=' ', wc_rev=4), 7559 'A_COPY/C' : Item(status=' ', wc_rev=4), 7560 'A_COPY/D' : Item(status=' ', wc_rev=4), 7561 'A_COPY/B' : Item(status=' ', wc_rev=4), 7562 'A_COPY/B/lambda' : Item(status=' ', wc_rev=4), 7563 'A_COPY/B/E' : Item(status=' ', wc_rev=4), 7564 'A_COPY/B/E/alpha' : Item(status=' ', wc_rev=4), 7565 'A_COPY/B/E/beta' : Item(status=' ', wc_rev=4), 7566 'A_COPY/B/F' : Item(status=' ', wc_rev=4), 7567 'A_COPY/D/gamma' : Item(status=' ', wc_rev=4), 7568 'A_COPY/D/G' : Item(status=' ', wc_rev=4), 7569 'A_COPY/D/G/pi' : Item(status=' ', wc_rev=4), 7570 'A_COPY/D/G/rho' : Item(status=' ', wc_rev=4), 7571 'A_COPY/D/G/tau' : Item(status=' ', wc_rev=4), 7572 'A_COPY/D/H' : Item(status=' ', wc_rev=4), 7573 'A_COPY/D/H/chi' : Item(status=' ', wc_rev=4), 7574 'A_COPY/D/H/omega' : Item(status=' ', wc_rev=4), 7575 'A_COPY/D/H/psi' : Item(status=' ', wc_rev=4), 7576 }) 7577 svntest.actions.run_and_verify_commit(wc_dir, expected_output, 7578 expected_status) 7579 7580 # Merge /A_MOVED to /A_COPY - this happens in multiple passes 7581 # because /A_MOVED has renames in its history between the boundaries 7582 # of the requested merge range. 7583 expected_output = wc.State(A_COPY_path, { 7584 'mu' : Item(status='G ', prev_status='U '), # mu gets touched twice 7585 }) 7586 expected_mergeinfo_output = wc.State(A_COPY_path, { 7587 '' : Item(status=' G', prev_status=' U'), 7588 }) 7589 expected_elision_output = wc.State(A_COPY_path, { 7590 }) 7591 expected_status = wc.State(A_COPY_path, { 7592 '' : Item(status=' M', wc_rev=4), 7593 'mu' : Item(status='M ', wc_rev=4), 7594 'C' : Item(status=' ', wc_rev=4), 7595 'D' : Item(status=' ', wc_rev=4), 7596 'B' : Item(status=' ', wc_rev=4), 7597 'B/lambda' : Item(status=' ', wc_rev=4), 7598 'B/E' : Item(status=' ', wc_rev=4), 7599 'B/E/alpha': Item(status=' ', wc_rev=4), 7600 'B/E/beta' : Item(status=' ', wc_rev=4), 7601 'B/F' : Item(status=' ', wc_rev=4), 7602 'D/gamma' : Item(status=' ', wc_rev=4), 7603 'D/G' : Item(status=' ', wc_rev=4), 7604 'D/G/pi' : Item(status=' ', wc_rev=4), 7605 'D/G/rho' : Item(status=' ', wc_rev=4), 7606 'D/G/tau' : Item(status=' ', wc_rev=4), 7607 'D/H' : Item(status=' ', wc_rev=4), 7608 'D/H/chi' : Item(status=' ', wc_rev=4), 7609 'D/H/omega': Item(status=' ', wc_rev=4), 7610 'D/H/psi' : Item(status=' ', wc_rev=4), 7611 }) 7612 expected_disk = wc.State('', { 7613 '' : Item(props={SVN_PROP_MERGEINFO : '/A:3\n/A_MOVED:4-5'}), 7614 'mu' : Item("This is 'mu' in A_MOVED.\n"), 7615 'C' : Item(), 7616 'D' : Item(), 7617 'B' : Item(), 7618 'B/lambda' : Item("This is the file 'lambda'.\n"), 7619 'B/E' : Item(), 7620 'B/E/alpha': Item("This is the file 'alpha'.\n"), 7621 'B/E/beta' : Item("This is the file 'beta'.\n"), 7622 'B/F' : Item(), 7623 'D/gamma' : Item("This is the file 'gamma'.\n"), 7624 'D/G' : Item(), 7625 'D/G/pi' : Item("This is the file 'pi'.\n"), 7626 'D/G/rho' : Item("This is the file 'rho'.\n"), 7627 'D/G/tau' : Item("This is the file 'tau'.\n"), 7628 'D/H' : Item(), 7629 'D/H/chi' : Item("This is the file 'chi'.\n"), 7630 'D/H/omega': Item("This is the file 'omega'.\n"), 7631 'D/H/psi' : Item("This is the file 'psi'.\n"), 7632 }) 7633 expected_skip = wc.State(A_COPY_path, {}) 7634 7635 ### Disabling dry_run mode because currently it can't handle the way 7636 ### 'mu' gets textually modified in multiple passes. 7637 svntest.actions.run_and_verify_merge(A_COPY_path, '2', '5', 7638 A_MOVED_url, None, 7639 expected_output, 7640 expected_mergeinfo_output, 7641 expected_elision_output, 7642 expected_disk, 7643 expected_status, 7644 expected_skip, 7645 [], True, False) 7646 7647#---------------------------------------------------------------------- 7648@SkipUnless(server_has_mergeinfo) 7649def merge_with_child_having_different_rev_ranges_to_merge(sbox): 7650 "child having different rev ranges to merge" 7651 #Modify A/mu to 30 lines with a content 'line1'...'line30' commit it at r2. 7652 #Create a branch A_COPY from A, commit it at r3. 7653 #Modify A/mu line number 7 to 'LINE7' modify and commit at r4. 7654 #Modify A/mu line number 17 to 'LINE17' modify, set prop 'prop1' on 'A' 7655 #with a value 'val1' and commit at r5. 7656 #Modify A/mu line number 27 to 'LINE27' modify and commit at r6. 7657 #Merge r5 to 'A/mu' as a single file merge explicitly to 'A_COPY/mu'. 7658 #Merge r3:6 from 'A' to 'A_COPY 7659 #This should merge r4 and then r5 through r6. 7660 #Revert r5 and r6 via single file merge on A_COPY/mu. 7661 #Revert r6 through r4 on A_COPY this should get back us the pristine copy. 7662 #Merge r3:6 from 'A' to 'A_COPY 7663 #Revert r5 on A_COPY/mu 7664 #Modify line number 17 with 'some other line17' of A_COPY/mu 7665 #Merge r6:3 from 'A' to 'A_COPY, This should leave line number 17 7666 #undisturbed in A_COPY/mu, rest should be reverted. 7667 7668 # Create a WC 7669 sbox.build() 7670 wc_dir = sbox.wc_dir 7671 A_path = sbox.ospath('A') 7672 mu_path = sbox.ospath('A/mu') 7673 A_url = sbox.repo_url + '/A' 7674 A_mu_url = sbox.repo_url + '/A/mu' 7675 A_COPY_url = sbox.repo_url + '/A_COPY' 7676 A_COPY_path = sbox.ospath('A_COPY') 7677 A_COPY_mu_path = sbox.ospath('A_COPY/mu') 7678 thirty_line_dummy_text = 'line1\n' 7679 for i in range(2, 31): 7680 thirty_line_dummy_text += 'line' + str(i) + '\n' 7681 7682 svntest.main.file_write(mu_path, thirty_line_dummy_text) 7683 expected_output = wc.State(wc_dir, {'A/mu' : Item(verb='Sending')}) 7684 expected_status = svntest.actions.get_virginal_state(wc_dir, 1) 7685 expected_status.tweak('A/mu', wc_rev=2) 7686 svntest.actions.run_and_verify_commit(wc_dir, expected_output, 7687 expected_status) 7688 svntest.actions.run_and_verify_svn(None, [], 7689 'cp', A_url, A_COPY_url, '-m', 'rev 3') 7690 # Update the working copy to get A_COPY 7691 svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir) 7692 expected_status.add({'A_COPY' : Item(status=' '), 7693 'A_COPY/mu' : Item(status=' '), 7694 'A_COPY/C' : Item(status=' '), 7695 'A_COPY/D' : Item(status=' '), 7696 'A_COPY/B' : Item(status=' '), 7697 'A_COPY/B/lambda' : Item(status=' '), 7698 'A_COPY/B/E' : Item(status=' '), 7699 'A_COPY/B/E/alpha' : Item(status=' '), 7700 'A_COPY/B/E/beta' : Item(status=' '), 7701 'A_COPY/B/F' : Item(status=' '), 7702 'A_COPY/D/gamma' : Item(status=' '), 7703 'A_COPY/D/G' : Item(status=' '), 7704 'A_COPY/D/G/pi' : Item(status=' '), 7705 'A_COPY/D/G/rho' : Item(status=' '), 7706 'A_COPY/D/G/tau' : Item(status=' '), 7707 'A_COPY/D/H' : Item(status=' '), 7708 'A_COPY/D/H/chi' : Item(status=' '), 7709 'A_COPY/D/H/omega' : Item(status=' '), 7710 'A_COPY/D/H/psi' : Item(status=' ')}) 7711 expected_status.tweak(wc_rev=3) 7712 tweaked_7th_line = thirty_line_dummy_text.replace('line7', 'LINE 7') 7713 svntest.main.file_write(mu_path, tweaked_7th_line) 7714 expected_status.tweak('A/mu', wc_rev=4) 7715 svntest.actions.run_and_verify_commit(wc_dir, expected_output, 7716 expected_status) 7717 svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir) 7718 expected_status.tweak(wc_rev=4) 7719 tweaked_17th_line = tweaked_7th_line.replace('line17', 'LINE 17') 7720 svntest.main.file_write(mu_path, tweaked_17th_line) 7721 svntest.main.run_svn(None, 'propset', 'prop1', 'val1', A_path) 7722 expected_output = wc.State(wc_dir, 7723 { 7724 'A' : Item(verb='Sending'), 7725 'A/mu' : Item(verb='Sending') 7726 } 7727 ) 7728 expected_status.tweak('A', wc_rev=5) 7729 expected_status.tweak('A/mu', wc_rev=5) 7730 svntest.actions.run_and_verify_commit(wc_dir, expected_output, 7731 expected_status) 7732 tweaked_27th_line = tweaked_17th_line.replace('line27', 'LINE 27') 7733 svntest.main.file_write(mu_path, tweaked_27th_line) 7734 expected_status.tweak('A/mu', wc_rev=6) 7735 expected_output = wc.State(wc_dir, {'A/mu' : Item(verb='Sending')}) 7736 svntest.actions.run_and_verify_commit(wc_dir, expected_output, 7737 expected_status) 7738 # Merge r5 to A_COPY/mu 7739 svntest.actions.run_and_verify_svn( 7740 expected_merge_output([[5]], 7741 ['U ' + A_COPY_mu_path + '\n', 7742 ' U ' + A_COPY_mu_path + '\n']), 7743 [], 'merge', '-r4:5', A_mu_url, A_COPY_mu_path) 7744 7745 expected_skip = wc.State(A_COPY_path, {}) 7746 expected_output = wc.State(A_COPY_path, { 7747 '' : Item(status=' U'), 7748 'mu' : Item(status='G ', prev_status='G '), # Updated twice 7749 }) 7750 expected_mergeinfo_output = wc.State(A_COPY_path, { 7751 '' : Item(status=' U'), 7752 'mu' : Item(status=' G'), 7753 }) 7754 expected_elision_output = wc.State(A_COPY_path, { 7755 'mu' : Item(status=' U'), 7756 }) 7757 expected_status = wc.State(A_COPY_path, { 7758 '' : Item(status=' M', wc_rev=4), 7759 'mu' : Item(status='M ', wc_rev=4), 7760 'C' : Item(status=' ', wc_rev=4), 7761 'D' : Item(status=' ', wc_rev=4), 7762 'B' : Item(status=' ', wc_rev=4), 7763 'B/lambda' : Item(status=' ', wc_rev=4), 7764 'B/E' : Item(status=' ', wc_rev=4), 7765 'B/E/alpha': Item(status=' ', wc_rev=4), 7766 'B/E/beta' : Item(status=' ', wc_rev=4), 7767 'B/F' : Item(status=' ', wc_rev=4), 7768 'D/gamma' : Item(status=' ', wc_rev=4), 7769 'D/G' : Item(status=' ', wc_rev=4), 7770 'D/G/pi' : Item(status=' ', wc_rev=4), 7771 'D/G/rho' : Item(status=' ', wc_rev=4), 7772 'D/G/tau' : Item(status=' ', wc_rev=4), 7773 'D/H' : Item(status=' ', wc_rev=4), 7774 'D/H/chi' : Item(status=' ', wc_rev=4), 7775 'D/H/omega': Item(status=' ', wc_rev=4), 7776 'D/H/psi' : Item(status=' ', wc_rev=4), 7777 }) 7778 expected_disk = wc.State('', { 7779 '' : Item(props={SVN_PROP_MERGEINFO : '/A:4-6', 7780 'prop1' : 'val1'}), 7781 'mu' : Item(tweaked_27th_line), 7782 'C' : Item(), 7783 'D' : Item(), 7784 'B' : Item(), 7785 'B/lambda' : Item("This is the file 'lambda'.\n"), 7786 'B/E' : Item(), 7787 'B/E/alpha': Item("This is the file 'alpha'.\n"), 7788 'B/E/beta' : Item("This is the file 'beta'.\n"), 7789 'B/F' : Item(), 7790 'D/gamma' : Item("This is the file 'gamma'.\n"), 7791 'D/G' : Item(), 7792 'D/G/pi' : Item("This is the file 'pi'.\n"), 7793 'D/G/rho' : Item("This is the file 'rho'.\n"), 7794 'D/G/tau' : Item("This is the file 'tau'.\n"), 7795 'D/H' : Item(), 7796 'D/H/chi' : Item("This is the file 'chi'.\n"), 7797 'D/H/omega': Item("This is the file 'omega'.\n"), 7798 'D/H/psi' : Item("This is the file 'psi'.\n"), 7799 }) 7800 svntest.actions.run_and_verify_merge(A_COPY_path, '3', '6', 7801 A_url, None, 7802 expected_output, 7803 expected_mergeinfo_output, 7804 expected_elision_output, 7805 expected_disk, 7806 expected_status, 7807 expected_skip, 7808 check_props=True) 7809 # Revert r5 and r6 on A_COPY/mu 7810 svntest.actions.run_and_verify_svn( 7811 expected_merge_output([[6,5]], 7812 ['G ' + A_COPY_mu_path + '\n', 7813 ' G ' + A_COPY_mu_path + '\n']), 7814 [], 'merge', '-r6:4', A_mu_url, A_COPY_mu_path) 7815 7816 expected_output = wc.State(A_COPY_path, { 7817 '' : Item(status=' G'), # merged removal of prop1 property 7818 'mu' : Item(status='G '), # merged reversion of text changes 7819 }) 7820 expected_mergeinfo_output = wc.State(A_COPY_path, { 7821 '' : Item(status=' G'), 7822 'mu' : Item(status=' G'), 7823 }) 7824 expected_elision_output = wc.State(A_COPY_path, { 7825 '' : Item(status=' U'), 7826 'mu' : Item(status=' U'), 7827 }) 7828 expected_status.tweak('', status=' ') 7829 expected_status.tweak('mu', status=' ') 7830 expected_disk.tweak('', props={}) 7831 expected_disk.remove('') 7832 expected_disk.tweak('mu', contents=thirty_line_dummy_text) 7833 svntest.actions.run_and_verify_merge(A_COPY_path, '6', '3', 7834 A_url, None, 7835 expected_output, 7836 expected_mergeinfo_output, 7837 expected_elision_output, 7838 expected_disk, 7839 expected_status, 7840 expected_skip, 7841 check_props=True) 7842 7843 expected_disk.add({'' : Item(props={SVN_PROP_MERGEINFO : '/A:4-6', 7844 'prop1' : 'val1'})}) 7845 expected_disk.tweak('mu', contents=tweaked_27th_line) 7846 expected_output = wc.State(A_COPY_path, { 7847 '' : Item(status=' U'), # new mergeinfo and prop1 property 7848 'mu' : Item(status='U '), # text changes 7849 }) 7850 expected_mergeinfo_output = wc.State(A_COPY_path, { 7851 '' : Item(status=' U'), 7852 }) 7853 expected_elision_output = wc.State(A_COPY_path, { 7854 }) 7855 expected_status.tweak('', status=' M') 7856 expected_status.tweak('mu', status='M ') 7857 svntest.actions.run_and_verify_merge(A_COPY_path, '3', '6', 7858 A_url, None, 7859 expected_output, 7860 expected_mergeinfo_output, 7861 expected_elision_output, 7862 expected_disk, 7863 expected_status, 7864 expected_skip, 7865 check_props=True) 7866 #Revert r5 on A_COPY/mu 7867 svntest.actions.run_and_verify_svn( 7868 expected_merge_output([[-5]], 7869 ['G ' + A_COPY_mu_path + '\n', 7870 ' G ' + A_COPY_mu_path + '\n']), 7871 [], 'merge', '-r5:4', A_mu_url, A_COPY_mu_path) 7872 tweaked_17th_line_1 = tweaked_27th_line.replace('LINE 17', 7873 'some other line17') 7874 tweaked_17th_line_2 = thirty_line_dummy_text.replace('line17', 7875 'some other line17') 7876 svntest.main.file_write(A_COPY_mu_path, tweaked_17th_line_1) 7877 expected_output = wc.State(A_COPY_path, { 7878 '' : Item(status=' G'), 7879 'mu' : Item(status='G ', prev_status='G '), 7880 }) 7881 expected_mergeinfo_output = wc.State(A_COPY_path, { 7882 '' : Item(status=' G'), 7883 'mu' : Item(status=' G'), 7884 }) 7885 expected_elision_output = wc.State(A_COPY_path, { 7886 '' : Item(status=' U'), 7887 'mu' : Item(status=' U'), 7888 }) 7889 expected_status.tweak('', status=' ') 7890 expected_status.tweak('mu', status='M ') 7891 expected_disk.remove('') 7892 expected_disk.tweak('mu', contents=tweaked_17th_line_2) 7893 svntest.actions.run_and_verify_merge(A_COPY_path, '6', '3', 7894 A_url, None, 7895 expected_output, 7896 expected_mergeinfo_output, 7897 expected_elision_output, 7898 expected_disk, 7899 expected_status, 7900 expected_skip, 7901 check_props=True) 7902 7903#---------------------------------------------------------------------- 7904@SkipUnless(server_has_mergeinfo) 7905def merge_old_and_new_revs_from_renamed_file(sbox): 7906 "merge -rold(before rename):head renamed file" 7907 7908 ## See http://svn.haxx.se/dev/archive-2007-09/0706.shtml ## 7909 7910 # Create a WC 7911 sbox.build() 7912 wc_dir = sbox.wc_dir 7913 7914 # Some paths we'll care about 7915 mu_url = sbox.repo_url + '/A/mu' 7916 mu_MOVED_url = sbox.repo_url + '/A/mu_MOVED' 7917 mu_COPY_url = sbox.repo_url + '/A/mu_COPY' 7918 mu_COPY_path = sbox.ospath('A/mu_COPY') 7919 mu_path = sbox.ospath('A/mu') 7920 mu_MOVED_path = sbox.ospath('A/mu_MOVED') 7921 7922 # Copy mu to mu_COPY 7923 svntest.actions.run_and_verify_svn(['Committing transaction...\n', 7924 'Committed revision 2.\n'], 7925 [], 'cp', '-m', 'cp mu to mu_COPY', 7926 mu_url, mu_COPY_url) 7927 7928 # Make a modification to A/mu 7929 svntest.main.file_write(mu_path, "This is the file 'mu' modified.\n") 7930 expected_output = wc.State(wc_dir, {'A/mu' : Item(verb='Sending')}) 7931 expected_status = svntest.actions.get_virginal_state(wc_dir, 1) 7932 expected_status.tweak('A/mu', wc_rev=3) 7933 svntest.actions.run_and_verify_commit(wc_dir, expected_output, 7934 expected_status) 7935 7936 # Move mu to mu_MOVED 7937 svntest.actions.run_and_verify_svn(['Committing transaction...\n', 7938 'Committed revision 4.\n'], 7939 [], 'mv', '-m', 'mv mu to mu_MOVED', 7940 mu_url, mu_MOVED_url) 7941 7942 # Update the working copy to get mu_MOVED 7943 svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir) 7944 7945 # Make a modification to mu_MOVED 7946 svntest.main.file_write(mu_MOVED_path, "This is 'mu' in mu_MOVED.\n") 7947 expected_output = wc.State(wc_dir, {'A/mu_MOVED' : Item(verb='Sending')}) 7948 expected_status = svntest.actions.get_virginal_state(wc_dir, 4) 7949 expected_status.remove('A/mu') 7950 expected_status.add({ 7951 'A/mu_MOVED' : Item(status=' ', wc_rev=5), 7952 'A/mu_COPY' : Item(status=' ', wc_rev=4), 7953 }) 7954 svntest.actions.run_and_verify_commit(wc_dir, expected_output, 7955 expected_status) 7956 7957 # Merge A/mu_MOVED to A/mu_COPY - this happens in multiple passes 7958 # because A/mu_MOVED has renames in its history between the 7959 # boundaries of the requested merge range. 7960 expected_output = expected_merge_output([[2,3],[4,5]], 7961 ['U %s\n' % (mu_COPY_path), 7962 ' U %s\n' % (mu_COPY_path), 7963 'G %s\n' % (mu_COPY_path), 7964 ' G %s\n' % (mu_COPY_path),]) 7965 svntest.actions.run_and_verify_svn(expected_output, 7966 [], 'merge', '-r', '1:5', 7967 mu_MOVED_url, 7968 mu_COPY_path) 7969 svntest.actions.run_and_verify_svn(['/A/mu:2-3\n', 7970 '/A/mu_MOVED:4-5\n'], 7971 [], 'propget', SVN_PROP_MERGEINFO, 7972 mu_COPY_path) 7973 7974#---------------------------------------------------------------------- 7975@SkipUnless(server_has_mergeinfo) 7976def merge_with_auto_rev_range_detection(sbox): 7977 "merge with auto detection of revision ranges" 7978 7979 ## See http://svn.haxx.se/dev/archive-2007-09/0735.shtml ## 7980 7981 # Create a WC 7982 sbox.build() 7983 wc_dir = sbox.wc_dir 7984 7985 # Some paths we'll care about 7986 A_url = sbox.repo_url + '/A' 7987 A_COPY_url = sbox.repo_url + '/A_COPY' 7988 B1_path = sbox.ospath('A/B1') 7989 B1_mu_path = sbox.ospath('A/B1/mu') 7990 A_COPY_path = sbox.ospath('A_COPY') 7991 7992 # Create B1 inside A 7993 svntest.actions.run_and_verify_svn(["A " + B1_path + "\n"], 7994 [], 'mkdir', 7995 B1_path) 7996 7997 # Add a file mu inside B1 7998 svntest.main.file_write(B1_mu_path, "This is the file 'mu'.\n") 7999 svntest.actions.run_and_verify_svn(["A " + B1_mu_path + "\n"], 8000 [], 'add', B1_mu_path) 8001 8002 # Commit B1 and B1/mu 8003 expected_output = wc.State(wc_dir, { 8004 'A/B1' : Item(verb='Adding'), 8005 'A/B1/mu' : Item(verb='Adding'), 8006 }) 8007 expected_status = svntest.actions.get_virginal_state(wc_dir, 1) 8008 expected_status.add({ 8009 'A/B1' : Item(status=' ', wc_rev=2), 8010 'A/B1/mu' : Item(status=' ', wc_rev=2), 8011 }) 8012 svntest.actions.run_and_verify_commit(wc_dir, expected_output, 8013 expected_status) 8014 8015 # Copy A to A_COPY 8016 svntest.actions.run_and_verify_svn(['Committing transaction...\n', 8017 'Committed revision 3.\n'], 8018 [], 'cp', '-m', 'cp A to A_COPY', 8019 A_url, A_COPY_url) 8020 8021 # Make a modification to A/B1/mu 8022 svntest.main.file_write(B1_mu_path, "This is the file 'mu' modified.\n") 8023 expected_output = wc.State(wc_dir, {'A/B1/mu' : Item(verb='Sending')}) 8024 expected_status = svntest.actions.get_virginal_state(wc_dir, 1) 8025 expected_status.add({ 8026 'A/B1' : Item(status=' ', wc_rev=2), 8027 'A/B1/mu' : Item(status=' ', wc_rev=4), 8028 }) 8029 svntest.actions.run_and_verify_commit(wc_dir, expected_output, 8030 expected_status) 8031 8032 # Update the working copy to get A_COPY 8033 svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir) 8034 8035 # Merge /A to /A_COPY 8036 expected_output = wc.State(A_COPY_path, { 8037 'B1/mu' : Item(status='U '), 8038 }) 8039 expected_mergeinfo_output = wc.State(A_COPY_path, { 8040 '' : Item(status=' U'), 8041 }) 8042 expected_elision_output = wc.State(A_COPY_path, { 8043 }) 8044 expected_status = wc.State(A_COPY_path, { 8045 '' : Item(status=' M', wc_rev=4), 8046 'mu' : Item(status=' ', wc_rev=4), 8047 'C' : Item(status=' ', wc_rev=4), 8048 'D' : Item(status=' ', wc_rev=4), 8049 'B' : Item(status=' ', wc_rev=4), 8050 'B/lambda' : Item(status=' ', wc_rev=4), 8051 'B/E' : Item(status=' ', wc_rev=4), 8052 'B/E/alpha': Item(status=' ', wc_rev=4), 8053 'B/E/beta' : Item(status=' ', wc_rev=4), 8054 'B/F' : Item(status=' ', wc_rev=4), 8055 'B1' : Item(status=' ', wc_rev=4), 8056 'B1/mu' : Item(status='M ', wc_rev=4), 8057 'D/gamma' : Item(status=' ', wc_rev=4), 8058 'D/G' : Item(status=' ', wc_rev=4), 8059 'D/G/pi' : Item(status=' ', wc_rev=4), 8060 'D/G/rho' : Item(status=' ', wc_rev=4), 8061 'D/G/tau' : Item(status=' ', wc_rev=4), 8062 'D/H' : Item(status=' ', wc_rev=4), 8063 'D/H/chi' : Item(status=' ', wc_rev=4), 8064 'D/H/omega': Item(status=' ', wc_rev=4), 8065 'D/H/psi' : Item(status=' ', wc_rev=4), 8066 }) 8067 expected_disk = wc.State('', { 8068 '' : Item(props={SVN_PROP_MERGEINFO : '/A:3-4'}), 8069 'mu' : Item("This is the file 'mu'.\n"), 8070 'C' : Item(), 8071 'D' : Item(), 8072 'B' : Item(), 8073 'B/lambda' : Item("This is the file 'lambda'.\n"), 8074 'B/E' : Item(), 8075 'B/E/alpha': Item("This is the file 'alpha'.\n"), 8076 'B/E/beta' : Item("This is the file 'beta'.\n"), 8077 'B/F' : Item(), 8078 'B1' : Item(), 8079 'B1/mu' : Item("This is the file 'mu' modified.\n"), 8080 'D/gamma' : Item("This is the file 'gamma'.\n"), 8081 'D/G' : Item(), 8082 'D/G/pi' : Item("This is the file 'pi'.\n"), 8083 'D/G/rho' : Item("This is the file 'rho'.\n"), 8084 'D/G/tau' : Item("This is the file 'tau'.\n"), 8085 'D/H' : Item(), 8086 'D/H/chi' : Item("This is the file 'chi'.\n"), 8087 'D/H/omega': Item("This is the file 'omega'.\n"), 8088 'D/H/psi' : Item("This is the file 'psi'.\n"), 8089 }) 8090 expected_skip = wc.State(A_COPY_path, {}) 8091 svntest.actions.run_and_verify_merge(A_COPY_path, None, None, 8092 A_url, None, 8093 expected_output, 8094 expected_mergeinfo_output, 8095 expected_elision_output, 8096 expected_disk, 8097 expected_status, 8098 expected_skip, 8099 [], True, True) 8100 8101#---------------------------------------------------------------------- 8102# Test for issue 2818: Provide a 'merge' API which allows for merging of 8103# arbitrary revision ranges (e.g. '-c 3,5,7') 8104@Issue(2818) 8105@SkipUnless(server_has_mergeinfo) 8106def cherry_picking(sbox): 8107 "command line supports cherry picked merge ranges" 8108 8109 sbox.build() 8110 wc_dir = sbox.wc_dir 8111 wc_disk, wc_status = set_up_branch(sbox) 8112 8113 # Some paths we'll care about 8114 H_path = sbox.ospath('A/D/H') 8115 G_path = sbox.ospath('A/D/G') 8116 A_COPY_path = sbox.ospath('A_COPY') 8117 D_COPY_path = sbox.ospath('A_COPY/D') 8118 G_COPY_path = sbox.ospath('A_COPY/D/G') 8119 H_COPY_path = sbox.ospath('A_COPY/D/H') 8120 rho_COPY_path = sbox.ospath('A_COPY/D/G/rho') 8121 omega_COPY_path = sbox.ospath('A_COPY/D/H/omega') 8122 psi_COPY_path = sbox.ospath('A_COPY/D/H/psi') 8123 8124 # Update working copy 8125 expected_output = svntest.wc.State(wc_dir, {}) 8126 wc_status.tweak(wc_rev='6') 8127 svntest.actions.run_and_verify_update(wc_dir, expected_output, 8128 wc_disk, wc_status, 8129 check_props=True) 8130 8131 # Make some prop changes to some dirs. 8132 svntest.actions.run_and_verify_svn(["property 'prop:name' set on '" + 8133 G_path + "'\n"], [], 'ps', 8134 'prop:name', 'propval', G_path) 8135 expected_output = svntest.wc.State(wc_dir, {'A/D/G': Item(verb='Sending'),}) 8136 wc_status.tweak('A/D/G', wc_rev=7) 8137 wc_disk.tweak('A/D/G', props={'prop:name' : 'propval'}) 8138 8139 svntest.actions.run_and_verify_commit(wc_dir, expected_output, wc_status) 8140 svntest.actions.run_and_verify_svn(["property 'prop:name' set on '" + 8141 H_path + "'\n"], [], 'ps', 8142 'prop:name', 'propval', H_path) 8143 expected_output = svntest.wc.State(wc_dir, {'A/D/H': Item(verb='Sending'),}) 8144 wc_status.tweak('A/D/H', wc_rev=8) 8145 wc_disk.tweak('A/D/H', props={'prop:name' : 'propval'}) 8146 svntest.actions.run_and_verify_commit(wc_dir, expected_output, wc_status) 8147 8148 # Do multiple additive merges to a file" 8149 # Merge -r2:4 -c6 into A_COPY/D/G/rho. 8150 expected_skip = wc.State(rho_COPY_path, { }) 8151 # run_and_verify_merge doesn't support merging to a file WCPATH 8152 # so use run_and_verify_svn. 8153 ### TODO: We can use run_and_verify_merge() here now. 8154 svntest.actions.run_and_verify_svn( 8155 expected_merge_output([[3,4],[6]], 8156 ['U ' + rho_COPY_path + '\n', 8157 ' U ' + rho_COPY_path + '\n', 8158 ' G ' + rho_COPY_path + '\n',]), 8159 [], 'merge', '-r2:4', '-c6', 8160 sbox.repo_url + '/A/D/G/rho', rho_COPY_path) 8161 8162 # Check rho's status and props. 8163 expected_status = wc.State(rho_COPY_path, 8164 {'' : Item(status='MM', wc_rev=6)}) 8165 svntest.actions.run_and_verify_status(rho_COPY_path, expected_status) 8166 svntest.actions.run_and_verify_svn(["/A/D/G/rho:3-4,6\n"], [], 8167 'propget', SVN_PROP_MERGEINFO, 8168 rho_COPY_path) 8169 8170 #Do multiple additive merges to a directory: 8171 # Merge -c6 -c8 into A_COPY/D/H 8172 expected_output = expected_merge_output( 8173 [[6],[8]], 8174 ['U ' + omega_COPY_path + '\n', 8175 ' U ' + H_COPY_path + '\n', 8176 ' G ' + H_COPY_path + '\n',]) 8177 svntest.actions.run_and_verify_svn(expected_output, 8178 [], 'merge', '-c6', '-c8', 8179 sbox.repo_url + '/A/D/H', 8180 H_COPY_path) 8181 8182 # Check A_COPY/D/H's status and props. 8183 expected_status = wc.State(H_COPY_path, 8184 {'' : Item(status=' M', wc_rev=6), 8185 'psi' : Item(status=' ', wc_rev=6), 8186 'chi' : Item(status=' ', wc_rev=6), 8187 'omega': Item(status='M ', wc_rev=6),}) 8188 svntest.actions.run_and_verify_status(H_COPY_path, expected_status) 8189 svntest.actions.run_and_verify_svn([H_COPY_path + " - /A/D/H:6,8\n"], 8190 [], 'propget', '-R', SVN_PROP_MERGEINFO, 8191 H_COPY_path) 8192 8193 # Do multiple reverse merges to a directory: 8194 # Merge -c-6 -c-3 into A_COPY 8195 expected_output = expected_merge_output( 8196 [[-3],[-6]], 8197 ['G ' + omega_COPY_path + '\n', 8198 ' U ' + A_COPY_path + '\n', 8199 ' U ' + H_COPY_path + '\n', 8200 ' G ' + A_COPY_path + '\n', 8201 ' G ' + H_COPY_path + '\n',], 8202 elides=True) 8203 svntest.actions.run_and_verify_svn(expected_output, 8204 [], 'merge', '-c-3', '-c-6', 8205 sbox.repo_url + '/A', 8206 A_COPY_path) 8207 expected_status = wc.State(A_COPY_path, 8208 {'' : Item(status=' ', wc_rev=6), 8209 'B' : Item(status=' ', wc_rev=6), 8210 'B/lambda' : Item(status=' ', wc_rev=6), 8211 'B/E' : Item(status=' ', wc_rev=6), 8212 'B/E/alpha' : Item(status=' ', wc_rev=6), 8213 'B/E/beta' : Item(status=' ', wc_rev=6), 8214 'B/F' : Item(status=' ', wc_rev=6), 8215 'mu' : Item(status=' ', wc_rev=6), 8216 'C' : Item(status=' ', wc_rev=6), 8217 'D' : Item(status=' ', wc_rev=6), 8218 'D/gamma' : Item(status=' ', wc_rev=6), 8219 'D/G' : Item(status=' ', wc_rev=6), 8220 'D/G/pi' : Item(status=' ', wc_rev=6), 8221 'D/G/rho' : Item(status='MM', wc_rev=6), 8222 'D/G/tau' : Item(status=' ', wc_rev=6), 8223 'D/H' : Item(status=' M', wc_rev=6), 8224 'D/H/chi' : Item(status=' ', wc_rev=6), 8225 'D/H/psi' : Item(status=' ', wc_rev=6), 8226 'D/H/omega' : Item(status=' ', wc_rev=6),}) 8227 svntest.actions.run_and_verify_status(A_COPY_path, expected_status) 8228 # A_COPY/D/G/rho is untouched by the merge so its mergeinfo 8229 # remains unchanged. 8230 expected_out = H_COPY_path + " - /A/D/H:8\n|" + \ 8231 rho_COPY_path + " - /A/D/G/rho:3-4,6\n" 8232 # Construct proper regex for '\' infested Windows paths. 8233 if sys.platform == 'win32': 8234 expected_out = expected_out.replace("\\", "\\\\") 8235 svntest.actions.run_and_verify_svn(expected_out, [], 8236 'propget', '-R', SVN_PROP_MERGEINFO, 8237 A_COPY_path) 8238 8239 # Do both additive and reverse merges to a directory: 8240 # Merge -r2:3 -c-4 -r4:7 to A_COPY/D 8241 expected_output = expected_merge_output( 8242 [[3], [-4], [6,7], [5,7]], 8243 [' U ' + G_COPY_path + '\n', 8244 'U ' + omega_COPY_path + '\n', 8245 'U ' + psi_COPY_path + '\n', 8246 ' U ' + D_COPY_path + '\n', 8247 ' G ' + D_COPY_path + '\n', 8248 ' U ' + H_COPY_path + '\n', 8249 ' G ' + H_COPY_path + '\n', 8250 'G ' + rho_COPY_path + '\n', 8251 ' U ' + rho_COPY_path + '\n', 8252 ' G ' + rho_COPY_path + '\n'], 8253 elides=True) 8254 svntest.actions.run_and_verify_svn(expected_output, [], 'merge', 8255 '-r2:3', '-c-4', '-r4:7', 8256 sbox.repo_url + '/A/D', 8257 D_COPY_path) 8258 expected_status = wc.State(D_COPY_path, 8259 {'' : Item(status=' M', wc_rev=6), 8260 'gamma' : Item(status=' ', wc_rev=6), 8261 'G' : Item(status=' M', wc_rev=6), 8262 'G/pi' : Item(status=' ', wc_rev=6), 8263 'G/rho' : Item(status=' ', wc_rev=6), 8264 'G/tau' : Item(status=' ', wc_rev=6), 8265 'H' : Item(status=' M', wc_rev=6), 8266 'H/chi' : Item(status=' ', wc_rev=6), 8267 'H/psi' : Item(status='M ', wc_rev=6), 8268 'H/omega' : Item(status='M ', wc_rev=6),}) 8269 svntest.actions.run_and_verify_status(D_COPY_path, expected_status) 8270 expected_out = D_COPY_path + " - /A/D:3,5-7\n|" + \ 8271 H_COPY_path + " - /A/D/H:3,5-8\n|" + \ 8272 rho_COPY_path + " - /A/D/G/rho:3-4,6\n" 8273 # Construct proper regex for '\' infested Windows paths. 8274 if sys.platform == 'win32': 8275 expected_out = expected_out.replace("\\", "\\\\") 8276 svntest.actions.run_and_verify_svn(expected_out, [], 8277 'propget', '-R', SVN_PROP_MERGEINFO, 8278 D_COPY_path) 8279 8280#---------------------------------------------------------------------- 8281@SkipUnless(server_has_mergeinfo) 8282@Issue(2969) 8283def propchange_of_subdir_raises_conflict(sbox): 8284 "merge of propchange on subdir raises conflict" 8285 8286 ## See https://issues.apache.org/jira/browse/SVN-2969. ## 8287 8288 # Create a WC with a single branch 8289 sbox.build() 8290 wc_dir = sbox.wc_dir 8291 wc_disk, wc_status = set_up_branch(sbox, True, 1) 8292 8293 # Some paths we'll care about 8294 B_url = sbox.repo_url + '/A/B' 8295 E_path = sbox.ospath('A/B/E') 8296 lambda_path = sbox.ospath('A/B/lambda') 8297 A_COPY_B_path = sbox.ospath('A_COPY/B') 8298 A_COPY_B_E_path = sbox.ospath('A_COPY/B/E') 8299 A_COPY_lambda_path = sbox.ospath('A_COPY/B/E/lambda') 8300 8301 # Set a property on A/B/E and Make a modification to A/B/lambda 8302 svntest.main.run_svn(None, 'propset', 'x', 'x', E_path) 8303 8304 svntest.main.file_write(lambda_path, "This is the file 'lambda' modified.\n") 8305 expected_output = wc.State(wc_dir, { 8306 'A/B/lambda' : Item(verb='Sending'), 8307 'A/B/E' : Item(verb='Sending'), 8308 }) 8309 wc_status.add({ 8310 'A/B/lambda' : Item(status=' ', wc_rev=3), 8311 'A/B/E' : Item(status=' ', wc_rev=3), 8312 }) 8313 svntest.actions.run_and_verify_commit(wc_dir, expected_output, 8314 wc_status) 8315 8316 # Merge /A/B to /A_COPY/B ie., r1 to r3 with depth files 8317 expected_output = wc.State(A_COPY_B_path, { 8318 'lambda' : Item(status='U '), 8319 }) 8320 expected_mergeinfo_output = wc.State(A_COPY_B_path, { 8321 '' : Item(status=' U'), 8322 'lambda' : Item(status=' U'), 8323 }) 8324 expected_elision_output = wc.State(A_COPY_B_path, { 8325 }) 8326 expected_disk = wc.State('', { 8327 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B:2-3*'}), 8328 'lambda' : Item(contents="This is the file 'lambda' modified.\n", 8329 props={SVN_PROP_MERGEINFO : '/A/B/lambda:2-3'}), 8330 'F' : Item(), 8331 'E' : Item(), 8332 'E/alpha' : Item(contents="This is the file 'alpha'.\n"), 8333 'E/beta' : Item(contents="This is the file 'beta'.\n"), 8334 }) 8335 expected_status = wc.State(A_COPY_B_path, { 8336 '' : Item(status=' M', wc_rev=2), 8337 'lambda' : Item(status='MM', wc_rev=2), 8338 'F' : Item(status=' ', wc_rev=2), 8339 'E' : Item(status=' ', wc_rev=2), 8340 'E/alpha' : Item(status=' ', wc_rev=2), 8341 'E/beta' : Item(status=' ', wc_rev=2), 8342 }) 8343 expected_skip = wc.State(A_COPY_B_path, {}) 8344 8345 svntest.actions.run_and_verify_merge(A_COPY_B_path, None, None, 8346 B_url, None, 8347 expected_output, 8348 expected_mergeinfo_output, 8349 expected_elision_output, 8350 expected_disk, 8351 expected_status, 8352 expected_skip, 8353 [], True, True, 8354 '--depth', 'files', 8355 A_COPY_B_path) 8356 8357 # Merge /A/B to /A_COPY/B ie., r1 to r3 with infinite depth 8358 expected_output = wc.State(A_COPY_B_path, { 8359 'E' : Item(status=' U'), 8360 }) 8361 expected_mergeinfo_output = wc.State(A_COPY_B_path, { 8362 '' : Item(status=' G'), 8363 'E' : Item(status=' G'), 8364 }) 8365 expected_elision_output = wc.State(A_COPY_B_path, { 8366 'E' : Item(status=' U'), 8367 'lambda' : Item(status=' U'), 8368 }) 8369 expected_disk = wc.State('', { 8370 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B:2-3'}), 8371 'lambda' : Item(contents="This is the file 'lambda' modified.\n"), 8372 'F' : Item(), 8373 'E' : Item(props={'x': 'x'}), 8374 'E/alpha' : Item(contents="This is the file 'alpha'.\n"), 8375 'E/beta' : Item(contents="This is the file 'beta'.\n"), 8376 }) 8377 expected_status = wc.State(A_COPY_B_path, { 8378 '' : Item(status=' M', wc_rev=2), 8379 'lambda' : Item(status='M ', wc_rev=2), 8380 'F' : Item(status=' ', wc_rev=2), 8381 'E' : Item(status=' M', wc_rev=2), 8382 'E/alpha' : Item(status=' ', wc_rev=2), 8383 'E/beta' : Item(status=' ', wc_rev=2), 8384 }) 8385 svntest.actions.run_and_verify_merge(A_COPY_B_path, None, None, 8386 B_url, None, 8387 expected_output, 8388 expected_mergeinfo_output, 8389 expected_elision_output, 8390 expected_disk, 8391 expected_status, 8392 expected_skip, 8393 [], 1, 1) 8394 8395#---------------------------------------------------------------------- 8396# Test for issue #2971: Reverse merge of prop add segfaults if 8397# merging to parent of first merge 8398@Issue(2971) 8399@SkipUnless(server_has_mergeinfo) 8400def reverse_merge_prop_add_on_child(sbox): 8401 "reverse merge of prop add on child" 8402 8403 sbox.build() 8404 wc_dir = sbox.wc_dir 8405 wc_disk, wc_status = set_up_branch(sbox, True, 1) 8406 8407 # Some paths we'll care about 8408 G_path = sbox.ospath('A/D/G') 8409 D_COPY_path = sbox.ospath('A_COPY/D') 8410 G_COPY_path = sbox.ospath('A_COPY/D/G') 8411 8412 # Make some prop changes to some dirs. 8413 svntest.actions.run_and_verify_svn(["property 'prop:name' set on '" + 8414 G_path + "'\n"], [], 'ps', 8415 'prop:name', 'propval', G_path) 8416 expected_output = svntest.wc.State(wc_dir, {'A/D/G': Item(verb='Sending'),}) 8417 wc_status.tweak('A/D/G', wc_rev=3) 8418 wc_disk.tweak('A/D/G', props={'prop:name' : 'propval'}) 8419 8420 svntest.actions.run_and_verify_commit(wc_dir, expected_output, wc_status) 8421 8422 # Merge -c3's prop add to A_COPY/D/G 8423 expected_output = wc.State(G_COPY_path, { 8424 '' : Item(status=' U') 8425 }) 8426 expected_mergeinfo_output = wc.State(G_COPY_path, { 8427 '' : Item(status=' U'), 8428 }) 8429 expected_elision_output = wc.State(G_COPY_path, { 8430 }) 8431 expected_status = wc.State(G_COPY_path, { 8432 '' : Item(status=' M', wc_rev=2), 8433 'pi' : Item(status=' ', wc_rev=2), 8434 'rho' : Item(status=' ', wc_rev=2), 8435 'tau' : Item(status=' ', wc_rev=2), 8436 }) 8437 expected_disk = wc.State('', { 8438 '' : Item(props={SVN_PROP_MERGEINFO : '/A/D/G:3', 8439 'prop:name' : 'propval'}), 8440 'pi' : Item("This is the file 'pi'.\n"), 8441 'rho' : Item("This is the file 'rho'.\n"), 8442 'tau' : Item("This is the file 'tau'.\n"), 8443 }) 8444 expected_skip = wc.State(G_COPY_path, { }) 8445 svntest.actions.run_and_verify_merge(G_COPY_path, '2', '3', 8446 sbox.repo_url + '/A/D/G', None, 8447 expected_output, 8448 expected_mergeinfo_output, 8449 expected_elision_output, 8450 expected_disk, 8451 expected_status, 8452 expected_skip, 8453 check_props=True) 8454 8455 # Now merge -c-3 but target the previous target's parent instead. 8456 expected_output = wc.State(D_COPY_path, { 8457 'G' : Item(status=' G'), 8458 }) 8459 expected_mergeinfo_output = wc.State(D_COPY_path, { 8460 '' : Item(status=' U'), 8461 'G' : Item(status=' G'), 8462 }) 8463 expected_elision_output = wc.State(D_COPY_path, { 8464 '' : Item(status=' U'), 8465 'G' : Item(status=' U'), 8466 }) 8467 expected_status = wc.State(D_COPY_path, { 8468 '' : Item(status=' ', wc_rev=2), 8469 'G' : Item(status=' ', wc_rev=2), 8470 'G/pi' : Item(status=' ', wc_rev=2), 8471 'G/rho' : Item(status=' ', wc_rev=2), 8472 'G/tau' : Item(status=' ', wc_rev=2), 8473 'H' : Item(status=' ', wc_rev=2), 8474 'H/chi' : Item(status=' ', wc_rev=2), 8475 'H/psi' : Item(status=' ', wc_rev=2), 8476 'H/omega' : Item(status=' ', wc_rev=2), 8477 'gamma' : Item(status=' ', wc_rev=2), 8478 }) 8479 expected_disk = wc.State('', { 8480 'G' : Item(), 8481 'G/pi' : Item("This is the file 'pi'.\n"), 8482 'G/rho' : Item("This is the file 'rho'.\n"), 8483 'G/tau' : Item("This is the file 'tau'.\n"), 8484 'H' : Item(), 8485 'H/chi' : Item("This is the file 'chi'.\n"), 8486 'H/psi' : Item("This is the file 'psi'.\n"), 8487 'H/omega' : Item("This is the file 'omega'.\n"), 8488 'gamma' : Item("This is the file 'gamma'.\n") 8489 }) 8490 expected_skip = wc.State(D_COPY_path, { }) 8491 svntest.actions.run_and_verify_merge(D_COPY_path, '3', '2', 8492 sbox.repo_url + '/A/D', None, 8493 expected_output, 8494 expected_mergeinfo_output, 8495 expected_elision_output, 8496 expected_disk, 8497 expected_status, 8498 expected_skip, 8499 check_props=True) 8500 8501#---------------------------------------------------------------------- 8502@XFail() 8503@Issues(2970,3642) 8504def merge_target_with_non_inheritable_mergeinfo(sbox): 8505 "merge target with non inheritable mergeinfo" 8506 8507 ## See https://issues.apache.org/jira/browse/SVN-2970. ## 8508 8509 # Create a WC with a single branch 8510 sbox.build() 8511 wc_dir = sbox.wc_dir 8512 wc_disk, wc_status = set_up_branch(sbox, True, 1) 8513 8514 # Some paths we'll care about 8515 B_url = sbox.repo_url + '/A/B' 8516 lambda_path = sbox.ospath('A/B/lambda') 8517 newfile_path = sbox.ospath('A/B/E/newfile') 8518 A_COPY_B_path = sbox.ospath('A_COPY/B') 8519 8520 # Make a modifications to A/B/lambda and add A/B/E/newfile 8521 svntest.main.file_write(lambda_path, "This is the file 'lambda' modified.\n") 8522 svntest.main.file_write(newfile_path, "This is the file 'newfile'.\n") 8523 svntest.actions.run_and_verify_svn(None, [], 'add', newfile_path) 8524 expected_output = wc.State(wc_dir, { 8525 'A/B/lambda' : Item(verb='Sending'), 8526 'A/B/E/newfile' : Item(verb='Adding'), 8527 }) 8528 wc_status.add({ 8529 'A/B/lambda' : Item(status=' ', wc_rev=3), 8530 'A/B/E/newfile' : Item(status=' ', wc_rev=3), 8531 }) 8532 svntest.actions.run_and_verify_commit(wc_dir, expected_output, 8533 wc_status) 8534 8535 # Merge /A/B to /A_COPY/B ie., r1 to r3 with depth immediates 8536 expected_output = wc.State(A_COPY_B_path, { 8537 'lambda' : Item(status='U '), 8538 }) 8539 # Issue #3642 https://issues.apache.org/jira/browse/SVN-3642 8540 # 8541 # We don't expect A_COPY/B/F to have mergeinfo recorded on it because 8542 # not only is it unaffected by the merge at depth immediates, it could 8543 # never be affected by the merge, regardless of depth. 8544 expected_mergeinfo_output = wc.State(A_COPY_B_path, { 8545 '' : Item(status=' U'), 8546 'E' : Item(status=' U'), 8547 }) 8548 expected_elision_output = wc.State(A_COPY_B_path, { 8549 }) 8550 expected_disk = wc.State('', { 8551 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B:2-3'}), 8552 'lambda' : Item(contents="This is the file 'lambda' modified.\n"), 8553 'F' : Item(), # No mergeinfo! 8554 'E' : Item(props={SVN_PROP_MERGEINFO : '/A/B/E:2-3*'}), 8555 'E/alpha' : Item(contents="This is the file 'alpha'.\n"), 8556 'E/beta' : Item(contents="This is the file 'beta'.\n"), 8557 }) 8558 expected_status = wc.State(A_COPY_B_path, { 8559 '' : Item(status=' M', wc_rev=2), 8560 'lambda' : Item(status='M ', wc_rev=2), 8561 'F' : Item(status=' ', wc_rev=2), 8562 'E' : Item(status=' M', wc_rev=2), 8563 'E/alpha' : Item(status=' ', wc_rev=2), 8564 'E/beta' : Item(status=' ', wc_rev=2), 8565 }) 8566 expected_skip = wc.State(A_COPY_B_path, {}) 8567 8568 svntest.actions.run_and_verify_merge(A_COPY_B_path, None, None, 8569 B_url, None, 8570 expected_output, 8571 expected_mergeinfo_output, 8572 expected_elision_output, 8573 expected_disk, 8574 expected_status, 8575 expected_skip, 8576 [], True, True, 8577 '--depth', 'immediates', 8578 A_COPY_B_path) 8579 8580 # Merge /A/B to /A_COPY/B ie., r1 to r3 with infinite depth 8581 expected_output = wc.State(A_COPY_B_path, { 8582 'E/newfile' : Item(status='A '), 8583 }) 8584 expected_mergeinfo_output = wc.State(A_COPY_B_path, { 8585 '' : Item(status=' G'), 8586 }) 8587 expected_elision_output = wc.State(A_COPY_B_path, { 8588 }) 8589 expected_disk = wc.State('', { 8590 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B:2-3'}), 8591 'lambda' : Item(contents="This is the file 'lambda' modified.\n"), 8592 'F' : Item(), 8593 'E' : Item(), 8594 'E/alpha' : Item(contents="This is the file 'alpha'.\n"), 8595 'E/beta' : Item(contents="This is the file 'beta'.\n"), 8596 'E/newfile' : Item(contents="This is the file 'newfile'.\n"), 8597 }) 8598 expected_status = wc.State(A_COPY_B_path, { 8599 '' : Item(status=' M', wc_rev=2), 8600 'lambda' : Item(status='M ', wc_rev=2), 8601 'F' : Item(status=' ', wc_rev=2), 8602 'E' : Item(status=' ', wc_rev=2), 8603 'E/alpha' : Item(status=' ', wc_rev=2), 8604 'E/beta' : Item(status=' ', wc_rev=2), 8605 'E/newfile' : Item(status='A ', wc_rev=2), 8606 }) 8607 8608 svntest.actions.run_and_verify_merge(A_COPY_B_path, None, None, 8609 B_url, None, 8610 expected_output, 8611 expected_mergeinfo_output, 8612 expected_elision_output, 8613 expected_disk, 8614 expected_status, 8615 expected_skip, 8616 [], True, True) 8617 8618#---------------------------------------------------------------------- 8619@SkipUnless(server_has_mergeinfo) 8620def self_reverse_merge(sbox): 8621 "revert a commit on a target" 8622 8623 sbox.build() 8624 wc_dir = sbox.wc_dir 8625 8626 # Make changes to the working copy 8627 mu_path = sbox.ospath('A/mu') 8628 svntest.main.file_append(mu_path, 'appended mu text') 8629 8630 # Created expected output tree for 'svn ci' 8631 expected_output = wc.State(wc_dir, { 8632 'A/mu' : Item(verb='Sending'), 8633 }) 8634 8635 # Create expected status tree; all local revisions should be at 1, 8636 # but mu should be at revision 2. 8637 expected_status = svntest.actions.get_virginal_state(wc_dir, 1) 8638 expected_status.tweak('A/mu', wc_rev=2) 8639 8640 svntest.actions.run_and_verify_commit(wc_dir, 8641 expected_output, 8642 expected_status) 8643 8644 # update to HEAD so that the to-be-undone revision is found in the 8645 # implicit mergeinfo (the natural history) of the target. 8646 svntest.actions.run_and_verify_svn(None, [], 'update', wc_dir) 8647 8648 expected_output = wc.State(wc_dir, { 8649 'A/mu' : Item(status='U ') 8650 }) 8651 expected_mergeinfo_output = wc.State(wc_dir, { 8652 '' : Item(status=' U'), 8653 }) 8654 expected_elision_output = wc.State(wc_dir, { 8655 '' : Item(status=' U'), 8656 }) 8657 expected_skip = wc.State(wc_dir, { }) 8658 expected_disk = svntest.main.greek_state.copy() 8659 expected_status = svntest.actions.get_virginal_state(wc_dir, 2) 8660 expected_status.tweak('A/mu', status='M ') 8661 svntest.actions.run_and_verify_merge(wc_dir, '2', '1', sbox.repo_url, 8662 None, expected_output, 8663 expected_mergeinfo_output, 8664 expected_elision_output, 8665 expected_disk, 8666 expected_status, expected_skip, 8667 [], True, True) 8668 svntest.actions.run_and_verify_svn(None, [], 'revert', '-R', wc_dir) 8669 8670 # record dummy self mergeinfo to test the fact that self-reversal should work 8671 # irrespective of mergeinfo. 8672 svntest.actions.run_and_verify_svn(None, [], 'ps', SVN_PROP_MERGEINFO, 8673 '/:1', wc_dir) 8674 8675 # Bad svntest.main.greek_state does not have '', so adding it explicitly. 8676 expected_disk.add({'' : Item(props={SVN_PROP_MERGEINFO : '/:1'})}) 8677 expected_status = svntest.actions.get_virginal_state(wc_dir, 2) 8678 expected_status.tweak('', status = ' M') 8679 expected_status.tweak('A/mu', status = 'M ') 8680 expected_mergeinfo_output = wc.State(wc_dir, { 8681 '' : Item(status=' G'), 8682 }) 8683 expected_elision_output = wc.State(wc_dir, { 8684 }) 8685 svntest.actions.run_and_verify_merge(wc_dir, '2', '1', sbox.repo_url, 8686 None, expected_output, 8687 expected_mergeinfo_output, 8688 expected_elision_output, 8689 expected_disk, 8690 expected_status, expected_skip, 8691 [], True, True) 8692 8693#---------------------------------------------------------------------- 8694@SkipUnless(server_has_mergeinfo) 8695def ignore_ancestry_and_mergeinfo(sbox): 8696 "--ignore-ancestry also ignores mergeinfo" 8697 8698 # Create a WC with a single branch 8699 sbox.build() 8700 wc_dir = sbox.wc_dir 8701 wc_disk, wc_status = set_up_branch(sbox, True, 1) 8702 8703 # Some paths we'll care about 8704 A_B_url = sbox.repo_url + '/A/B' 8705 A_COPY_B_path = sbox.ospath('A_COPY/B') 8706 lambda_path = sbox.ospath('A/B/lambda') 8707 A_COPY_lambda_path = sbox.ospath('A_COPY/B/lambda') 8708 8709 # Make modifications to A/B/lambda 8710 svntest.main.file_write(lambda_path, "This is the file 'lambda' modified.\n") 8711 expected_output = wc.State(wc_dir, { 8712 'A/B/lambda' : Item(verb='Sending'), 8713 }) 8714 wc_status.add({ 8715 'A/B/lambda' : Item(status=' ', wc_rev=3), 8716 }) 8717 svntest.actions.run_and_verify_commit(wc_dir, expected_output, 8718 wc_status) 8719 8720 svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir) 8721 8722 # Merge /A/B to /A_COPY/B ie., r1 to r3 with depth immediates 8723 expected_output = wc.State(A_COPY_B_path, { 8724 'lambda' : Item(status='U '), 8725 }) 8726 expected_mergeinfo_output = wc.State(A_COPY_B_path, { 8727 '' : Item(status=' U'), 8728 }) 8729 expected_elision_output = wc.State(A_COPY_B_path, { 8730 }) 8731 expected_disk = wc.State('', { 8732 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B:2-3'}), 8733 'lambda' : Item(contents="This is the file 'lambda' modified.\n"), 8734 'F' : Item(props={}), 8735 'E' : Item(props={}), 8736 'E/alpha' : Item(contents="This is the file 'alpha'.\n"), 8737 'E/beta' : Item(contents="This is the file 'beta'.\n"), 8738 }) 8739 expected_status = wc.State(A_COPY_B_path, { 8740 '' : Item(status=' M', wc_rev=3), 8741 'lambda' : Item(status='M ', wc_rev=3), 8742 'F' : Item(status=' ', wc_rev=3), 8743 'E' : Item(status=' ', wc_rev=3), 8744 'E/alpha' : Item(status=' ', wc_rev=3), 8745 'E/beta' : Item(status=' ', wc_rev=3), 8746 }) 8747 expected_skip = wc.State(A_COPY_B_path, {}) 8748 8749 svntest.actions.run_and_verify_merge(A_COPY_B_path, 1, 3, 8750 A_B_url, None, 8751 expected_output, 8752 expected_mergeinfo_output, 8753 expected_elision_output, 8754 expected_disk, 8755 expected_status, 8756 expected_skip, 8757 [], True, True) 8758 8759 # Now, revert lambda and repeat the merge. Nothing should happen. 8760 svntest.actions.run_and_verify_svn(None, [], 'revert', '-R', 8761 A_COPY_lambda_path) 8762 expected_output.remove('lambda') 8763 expected_disk.tweak('lambda', contents="This is the file 'lambda'.\n") 8764 expected_status.tweak('lambda', status=' ') 8765 expected_mergeinfo_output = wc.State(A_COPY_B_path, { 8766 '' : Item(status=' G'), 8767 }) 8768 svntest.actions.run_and_verify_merge(A_COPY_B_path, 1, 3, 8769 A_B_url, None, 8770 expected_output, 8771 expected_mergeinfo_output, 8772 expected_elision_output, 8773 expected_disk, 8774 expected_status, 8775 expected_skip, 8776 [], True, True) 8777 8778 # Now, try the merge again with --ignore-ancestry. We should get 8779 # lambda re-modified. */ 8780 expected_output = wc.State(A_COPY_B_path, { 8781 'lambda' : Item(status='U '), 8782 }) 8783 expected_mergeinfo_output = wc.State(A_COPY_B_path, {}) 8784 expected_elision_output = wc.State(A_COPY_B_path, { 8785 }) 8786 expected_disk.tweak('lambda', 8787 contents="This is the file 'lambda' modified.\n") 8788 expected_status.tweak('lambda', status='M ') 8789 svntest.actions.run_and_verify_merge(A_COPY_B_path, 1, 3, 8790 A_B_url, None, 8791 expected_output, 8792 expected_mergeinfo_output, 8793 expected_elision_output, 8794 expected_disk, 8795 expected_status, 8796 expected_skip, 8797 [], True, True, 8798 '--ignore-ancestry', A_COPY_B_path) 8799 8800#---------------------------------------------------------------------- 8801@SkipUnless(server_has_mergeinfo) 8802@Issue(3032) 8803def merge_from_renamed_branch_fails_while_avoiding_repeat_merge(sbox): 8804 "merge from renamed branch" 8805 #Copy A/C to A/COPY_C results in r2. 8806 #Rename A/COPY_C to A/RENAMED_C results in r3. 8807 #Add A/RENAMED_C/file1 and commit, results in r4. 8808 #Change A/RENAMED_C/file1 and commit, results in r5. 8809 #Merge r4 from A/RENAMED_C to A/C 8810 #Merge r2:5 from A/RENAMED_C to A/C <-- This fails tracked via #3032. 8811 8812 ## See https://issues.apache.org/jira/browse/SVN-3032. ## 8813 8814 # Create a WC with a single branch 8815 sbox.build() 8816 wc_dir = sbox.wc_dir 8817 # Some paths we'll care about 8818 A_C_url = sbox.repo_url + '/A/C' 8819 A_COPY_C_url = sbox.repo_url + '/A/COPY_C' 8820 A_RENAMED_C_url = sbox.repo_url + '/A/RENAMED_C' 8821 A_C_path = sbox.ospath('A/C') 8822 A_RENAMED_C_path = sbox.ospath('A/RENAMED_C') 8823 A_RENAMED_C_file1_path = sbox.ospath('A/RENAMED_C/file1') 8824 8825 svntest.main.run_svn(None, 'cp', A_C_url, A_COPY_C_url, '-m', 'copy...') 8826 svntest.main.run_svn(None, 'mv', A_COPY_C_url, A_RENAMED_C_url, '-m', 8827 'rename...') 8828 svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir) 8829 8830 svntest.main.file_write(A_RENAMED_C_file1_path, "This is the file1.\n") 8831 svntest.main.run_svn(None, 'add', A_RENAMED_C_file1_path) 8832 expected_output = wc.State(A_RENAMED_C_path, { 8833 'file1' : Item(verb='Adding'), 8834 }) 8835 expected_status = wc.State(A_RENAMED_C_path, { 8836 '' : Item(status=' ', wc_rev=3), 8837 'file1' : Item(status=' ', wc_rev=4), 8838 }) 8839 svntest.actions.run_and_verify_commit(A_RENAMED_C_path, expected_output, 8840 expected_status) 8841 svntest.main.file_write(A_RENAMED_C_file1_path, 8842 "This is the file1 modified.\n") 8843 expected_output = wc.State(A_RENAMED_C_path, { 8844 'file1' : Item(verb='Sending'), 8845 }) 8846 expected_status.tweak('file1', wc_rev=5) 8847 svntest.actions.run_and_verify_commit(A_RENAMED_C_path, expected_output, 8848 expected_status) 8849 8850 expected_skip = wc.State(A_C_path, {}) 8851 expected_output = wc.State(A_C_path, { 8852 'file1' : Item(status='A '), 8853 }) 8854 expected_mergeinfo_output = wc.State(A_C_path, { 8855 '' : Item(status=' U'), 8856 }) 8857 expected_elision_output = wc.State(A_C_path, { 8858 }) 8859 expected_disk = wc.State('', { 8860 '' : Item(props={SVN_PROP_MERGEINFO : '/A/RENAMED_C:4'}), 8861 'file1' : Item("This is the file1.\n"), 8862 }) 8863 expected_status = wc.State(A_C_path, { 8864 '' : Item(status=' M', wc_rev=3), 8865 'file1' : Item(status='A ', wc_rev='-', copied='+'), 8866 }) 8867 svntest.actions.run_and_verify_merge(A_C_path, 3, 4, 8868 A_RENAMED_C_url, None, 8869 expected_output, 8870 expected_mergeinfo_output, 8871 expected_elision_output, 8872 expected_disk, 8873 expected_status, 8874 expected_skip, 8875 [], True, True) 8876 8877 expected_output = wc.State(A_C_path, { 8878 'file1' : Item(status='U '), 8879 }) 8880 expected_mergeinfo_output = wc.State(A_C_path, { 8881 '' : Item(status=' G'), 8882 }) 8883 expected_disk = wc.State('', { 8884 '' : Item(props={SVN_PROP_MERGEINFO : '/A/RENAMED_C:3-5'}), 8885 'file1' : Item("This is the file1 modified.\n"), 8886 }) 8887 expected_status = wc.State(A_C_path, { 8888 '' : Item(status=' M', wc_rev=3), 8889 'file1' : Item(status='A ', wc_rev='-', copied='+'), 8890 }) 8891 svntest.actions.run_and_verify_merge(A_C_path, 2, 5, 8892 A_RENAMED_C_url, None, 8893 expected_output, 8894 expected_mergeinfo_output, 8895 expected_elision_output, 8896 expected_disk, 8897 expected_status, 8898 expected_skip, 8899 [], True, True) 8900 8901#---------------------------------------------------------------------- 8902# Test for part of issue #2877: 'do subtree merge only if subtree has 8903# explicit mergeinfo set and exists in the merge source' 8904@SkipUnless(server_has_mergeinfo) 8905@Issue(2877) 8906def merge_source_normalization_and_subtree_merges(sbox): 8907 "normalized mergeinfo is recorded on subtrees" 8908 8909 sbox.build() 8910 wc_dir = sbox.wc_dir 8911 8912 # Some paths we'll care about 8913 D_COPY_path = sbox.ospath('A_COPY/D') 8914 G_COPY_path = sbox.ospath('A_COPY/D/G') 8915 8916 # Use our helper to copy 'A' to 'A_COPY' then make some changes under 'A' 8917 wc_disk, wc_status = set_up_branch(sbox) 8918 8919 # r7 - Move A to A_MOVED 8920 svntest.actions.run_and_verify_svn(['Committing transaction...\n', 8921 'Committed revision 7.\n'], 8922 [], 'mv', '-m', 'mv A to A_MOVED', 8923 sbox.repo_url + '/A', 8924 sbox.repo_url + '/A_MOVED') 8925 wc_status.add({ 8926 'A_MOVED/B' : Item(), 8927 'A_MOVED/B/lambda' : Item(), 8928 'A_MOVED/B/E' : Item(), 8929 'A_MOVED/B/E/alpha' : Item(), 8930 'A_MOVED/B/E/beta' : Item(), 8931 'A_MOVED/B/F' : Item(), 8932 'A_MOVED/mu' : Item(), 8933 'A_MOVED/C' : Item(), 8934 'A_MOVED/D' : Item(), 8935 'A_MOVED/D/gamma' : Item(), 8936 'A_MOVED/D/G' : Item(), 8937 'A_MOVED/D/G/pi' : Item(), 8938 'A_MOVED/D/G/rho' : Item(), 8939 'A_MOVED/D/G/tau' : Item(), 8940 'A_MOVED/D/H' : Item(), 8941 'A_MOVED/D/H/chi' : Item(), 8942 'A_MOVED/D/H/omega' : Item(), 8943 'A_MOVED/D/H/psi' : Item(), 8944 'A_MOVED' : Item()}) 8945 wc_status.remove('A', 'A/B', 'A/B/lambda', 'A/B/E', 'A/B/E/alpha', 8946 'A/B/E/beta', 'A/B/F', 'A/mu', 'A/C', 'A/D', 8947 'A/D/gamma', 'A/D/G', 'A/D/G/pi', 'A/D/G/rho', 8948 'A/D/G/tau' , 'A/D/H', 'A/D/H/chi', 'A/D/H/omega', 8949 'A/D/H/psi') 8950 wc_status.tweak(status=' ', wc_rev=7) 8951 8952 # Update the WC 8953 svntest.actions.run_and_verify_svn(None, [], 8954 'update', wc_dir) 8955 8956 # r8 - Make a text mod to 'A_MOVED/D/G/tau' 8957 svntest.main.file_write(sbox.ospath('A_MOVED/D/G/tau'), 8958 "New content") 8959 expected_output = wc.State(wc_dir, 8960 {'A_MOVED/D/G/tau' : Item(verb='Sending')}) 8961 wc_status.tweak('A_MOVED/D/G/tau', status=' ', wc_rev=8) 8962 svntest.actions.run_and_verify_commit(wc_dir, expected_output, wc_status) 8963 8964 # Merge -c4 URL/A_MOVED/D/G A_COPY/D/G. 8965 # 8966 # A_MOVED/D/G doesn't exist at r3:4, it's still A/D/G, 8967 # so the merge source normalization logic should set 8968 # mergeinfo of '/A/D/G:4' on A_COPY/D/G, *not* 'A_MOVED/D/G:4', 8969 # see issue #2953. 8970 expected_output = wc.State(G_COPY_path, { 8971 'rho' : Item(status='U ') 8972 }) 8973 expected_mergeinfo_output = wc.State(G_COPY_path, { 8974 '' : Item(status=' U'), 8975 }) 8976 expected_elision_output = wc.State(G_COPY_path, { 8977 }) 8978 expected_status = wc.State(G_COPY_path, { 8979 '' : Item(status=' M', wc_rev=7), 8980 'pi' : Item(status=' ', wc_rev=7), 8981 'rho' : Item(status='M ', wc_rev=7), 8982 'tau' : Item(status=' ', wc_rev=7), 8983 }) 8984 expected_disk = wc.State('', { 8985 '' : Item(props={SVN_PROP_MERGEINFO : '/A/D/G:4'}), 8986 'pi' : Item("This is the file 'pi'.\n"), 8987 'rho' : Item("New content"), 8988 'tau' : Item("This is the file 'tau'.\n"), 8989 }) 8990 expected_skip = wc.State(G_COPY_path, { }) 8991 svntest.actions.run_and_verify_merge(G_COPY_path, '3', '4', 8992 sbox.repo_url + '/A_MOVED/D/G', 8993 None, 8994 expected_output, 8995 expected_mergeinfo_output, 8996 expected_elision_output, 8997 expected_disk, 8998 expected_status, 8999 expected_skip, 9000 check_props=True) 9001 9002 # Merge -c8 URL/A_MOVED/D A_COPY/D. 9003 # 9004 # The merge target A_COPY/D and the subtree at A_COPY/D/G 9005 # should both have their mergeinfo updated with r8 9006 # from A_MOVED_D, see reopened issue #2877. 9007 expected_output = wc.State(D_COPY_path, { 9008 'G/tau' : Item(status='U '), 9009 }) 9010 expected_mergeinfo_output = wc.State(D_COPY_path, { 9011 '' : Item(status=' U'), 9012 'G' : Item(status=' G'), 9013 }) 9014 expected_elision_output = wc.State(D_COPY_path, { 9015 }) 9016 expected_status = wc.State(D_COPY_path, { 9017 '' : Item(status=' M', wc_rev=7), 9018 'G' : Item(status=' M', wc_rev=7), 9019 'G/pi' : Item(status=' ', wc_rev=7), 9020 'G/rho' : Item(status='M ', wc_rev=7), 9021 'G/tau' : Item(status='M ', wc_rev=7), 9022 'H' : Item(status=' ', wc_rev=7), 9023 'H/chi' : Item(status=' ', wc_rev=7), 9024 'H/psi' : Item(status=' ', wc_rev=7), 9025 'H/omega' : Item(status=' ', wc_rev=7), 9026 'gamma' : Item(status=' ', wc_rev=7), 9027 }) 9028 expected_disk = wc.State('', { 9029 '' : Item(props={SVN_PROP_MERGEINFO : '/A_MOVED/D:8'}), 9030 'G' : Item(props={SVN_PROP_MERGEINFO : 9031 '/A/D/G:4\n/A_MOVED/D/G:8'}), 9032 'G/pi' : Item("This is the file 'pi'.\n"), 9033 'G/rho' : Item("New content"), 9034 'G/tau' : Item("New content"), 9035 'H' : Item(), 9036 'H/chi' : Item("This is the file 'chi'.\n"), 9037 'H/psi' : Item("This is the file 'psi'.\n"), 9038 'H/omega' : Item("This is the file 'omega'.\n"), 9039 'gamma' : Item("This is the file 'gamma'.\n") 9040 }) 9041 expected_skip = wc.State(D_COPY_path, { }) 9042 svntest.actions.run_and_verify_merge(D_COPY_path, '7', '8', 9043 sbox.repo_url + '/A_MOVED/D', 9044 None, 9045 expected_output, 9046 expected_mergeinfo_output, 9047 expected_elision_output, 9048 expected_disk, 9049 expected_status, 9050 expected_skip, 9051 check_props=True) 9052 9053#---------------------------------------------------------------------- 9054# Tests for issue #3067: 'subtrees with intersecting mergeinfo, that don't 9055# exist at the start of a merge range shouldn't break the merge' 9056@SkipUnless(server_has_mergeinfo) 9057@Issue(3067) 9058def new_subtrees_should_not_break_merge(sbox): 9059 "subtrees added after start of merge range are ok" 9060 9061 sbox.build() 9062 wc_dir = sbox.wc_dir 9063 wc_disk, wc_status = set_up_branch(sbox) 9064 9065 # Some paths we'll care about 9066 A_COPY_path = sbox.ospath('A_COPY') 9067 D_COPY_path = sbox.ospath('A_COPY/D') 9068 nu_path = sbox.ospath('A/D/H/nu') 9069 nu_COPY_path = sbox.ospath('A_COPY/D/H/nu') 9070 rho_COPY_path = sbox.ospath('A_COPY/D/G/rho') 9071 H_COPY_path = sbox.ospath('A_COPY/D/H') 9072 9073 # Create 'A/D/H/nu', commit it as r7, make a text mod to it in r8. 9074 svntest.main.file_write(nu_path, "This is the file 'nu'.\n") 9075 svntest.actions.run_and_verify_svn(None, [], 'add', nu_path) 9076 expected_output = wc.State(wc_dir, {'A/D/H/nu' : Item(verb='Adding')}) 9077 wc_status.add({'A/D/H/nu' : Item(status=' ', wc_rev=7)}) 9078 svntest.actions.run_and_verify_commit(wc_dir, expected_output, wc_status) 9079 svntest.main.file_write(nu_path, "New content") 9080 expected_output = wc.State(wc_dir, {'A/D/H/nu' : Item(verb='Sending')}) 9081 wc_status.tweak('A/D/H/nu', wc_rev=8) 9082 svntest.actions.run_and_verify_commit(wc_dir, expected_output, wc_status) 9083 9084 # Merge r7 to A_COPY/D/H, then, so it has it's own explicit mergeinfo, 9085 # then merge r8 to A_COPY/D/H/nu so it too has explicit mergeinfo. 9086 expected_output = wc.State(H_COPY_path, { 9087 'nu' : Item(status='A '), 9088 }) 9089 expected_mergeinfo_output = wc.State(H_COPY_path, { 9090 '' : Item(status=' U'), 9091 }) 9092 expected_elision_output = wc.State(H_COPY_path, { 9093 }) 9094 expected_status = wc.State(H_COPY_path, { 9095 '' : Item(status=' M', wc_rev=2), 9096 'psi' : Item(status=' ', wc_rev=2), 9097 'omega' : Item(status=' ', wc_rev=2), 9098 'chi' : Item(status=' ', wc_rev=2), 9099 'nu' : Item(status='A ', copied='+', wc_rev='-'), 9100 }) 9101 expected_disk = wc.State('', { 9102 '' : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:7'}), 9103 'psi' : Item("This is the file 'psi'.\n"), 9104 'omega' : Item("This is the file 'omega'.\n"), 9105 'chi' : Item("This is the file 'chi'.\n"), 9106 'nu' : Item("This is the file 'nu'.\n"), 9107 }) 9108 expected_skip = wc.State(H_COPY_path, {}) 9109 svntest.actions.run_and_verify_merge(H_COPY_path, '6', '7', 9110 sbox.repo_url + '/A/D/H', None, 9111 expected_output, 9112 expected_mergeinfo_output, 9113 expected_elision_output, 9114 expected_disk, 9115 expected_status, expected_skip, 9116 check_props=True) 9117 # run_and_verify_merge doesn't support merging to a file WCPATH 9118 # so use run_and_verify_svn. 9119 ### TODO: We can use run_and_verify_merge() here now. 9120 svntest.actions.run_and_verify_svn( 9121 expected_merge_output([[8]], 9122 ['U ' + nu_COPY_path + '\n', 9123 ' G ' + nu_COPY_path + '\n']), 9124 [], 'merge', '-c8', '--allow-mixed-revisions', 9125 sbox.repo_url + '/A/D/H/nu', nu_COPY_path) 9126 9127 # Merge -r4:6 to A_COPY, then reverse merge r6 from A_COPY/D. 9128 expected_output = wc.State(A_COPY_path, { 9129 'B/E/beta' : Item(status='U '), 9130 'D/H/omega': Item(status='U '), 9131 }) 9132 expected_mergeinfo_output = wc.State(A_COPY_path, { 9133 '' : Item(status=' U'), 9134 'D/H' : Item(status=' G'), 9135 }) 9136 expected_elision_output = wc.State(A_COPY_path, { 9137 }) 9138 expected_status = wc.State(A_COPY_path, { 9139 '' : Item(status=' M', wc_rev=2), 9140 'B' : Item(status=' ', wc_rev=2), 9141 'mu' : Item(status=' ', wc_rev=2), 9142 'B/E' : Item(status=' ', wc_rev=2), 9143 'B/E/alpha' : Item(status=' ', wc_rev=2), 9144 'B/E/beta' : Item(status='M ', wc_rev=2), 9145 'B/lambda' : Item(status=' ', wc_rev=2), 9146 'B/F' : Item(status=' ', wc_rev=2), 9147 'C' : Item(status=' ', wc_rev=2), 9148 'D' : Item(status=' ', wc_rev=2), 9149 'D/G' : Item(status=' ', wc_rev=2), 9150 'D/G/pi' : Item(status=' ', wc_rev=2), 9151 'D/G/rho' : Item(status=' ', wc_rev=2), 9152 'D/G/tau' : Item(status=' ', wc_rev=2), 9153 'D/gamma' : Item(status=' ', wc_rev=2), 9154 'D/H' : Item(status=' M', wc_rev=2), 9155 'D/H/chi' : Item(status=' ', wc_rev=2), 9156 'D/H/psi' : Item(status=' ', wc_rev=2), 9157 'D/H/omega' : Item(status='M ', wc_rev=2), 9158 'D/H/nu' : Item(status='A ', copied='+', wc_rev='-'), 9159 }) 9160 expected_disk = wc.State('', { 9161 '' : Item(props={SVN_PROP_MERGEINFO : '/A:5-6'}), 9162 'B' : Item(), 9163 'mu' : Item("This is the file 'mu'.\n"), 9164 'B/E' : Item(), 9165 'B/E/alpha' : Item("This is the file 'alpha'.\n"), 9166 'B/E/beta' : Item("New content"), 9167 'B/lambda' : Item("This is the file 'lambda'.\n"), 9168 'B/F' : Item(), 9169 'C' : Item(), 9170 'D' : Item(), 9171 'D/G' : Item(), 9172 'D/G/pi' : Item("This is the file 'pi'.\n"), 9173 'D/G/rho' : Item("This is the file 'rho'.\n"), 9174 'D/G/tau' : Item("This is the file 'tau'.\n"), 9175 'D/gamma' : Item("This is the file 'gamma'.\n"), 9176 'D/H' : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:5-7'}), 9177 'D/H/chi' : Item("This is the file 'chi'.\n"), 9178 'D/H/psi' : Item("This is the file 'psi'.\n"), 9179 'D/H/omega' : Item("New content"), 9180 'D/H/nu' : Item("New content", 9181 props={SVN_PROP_MERGEINFO : '/A/D/H/nu:7-8'}), 9182 }) 9183 expected_skip = wc.State(A_COPY_path, { }) 9184 svntest.actions.run_and_verify_merge(A_COPY_path, '4', '6', 9185 sbox.repo_url + '/A', None, 9186 expected_output, 9187 expected_mergeinfo_output, 9188 expected_elision_output, 9189 expected_disk, 9190 expected_status, 9191 expected_skip, 9192 check_props=True) 9193 expected_output = wc.State(D_COPY_path, { 9194 'H/omega': Item(status='G '), 9195 }) 9196 expected_mergeinfo_output = wc.State(D_COPY_path, { 9197 '' : Item(status=' G'), 9198 'H' : Item(status=' G'), 9199 }) 9200 expected_elision_output = wc.State(D_COPY_path, { 9201 }) 9202 expected_status = wc.State(D_COPY_path, { 9203 '' : Item(status=' M', wc_rev=2), 9204 'G' : Item(status=' ', wc_rev=2), 9205 'G/pi' : Item(status=' ', wc_rev=2), 9206 'G/rho' : Item(status=' ', wc_rev=2), 9207 'G/tau' : Item(status=' ', wc_rev=2), 9208 'gamma' : Item(status=' ', wc_rev=2), 9209 'H' : Item(status=' M', wc_rev=2), 9210 'H/chi' : Item(status=' ', wc_rev=2), 9211 'H/psi' : Item(status=' ', wc_rev=2), 9212 'H/omega' : Item(status=' ', wc_rev=2), 9213 'H/nu' : Item(status='A ', copied='+', wc_rev='-'), 9214 }) 9215 expected_disk = wc.State('', { 9216 '' : Item(props={SVN_PROP_MERGEINFO : '/A/D:5'}), 9217 'G/pi' : Item("This is the file 'pi'.\n"), 9218 'G/rho' : Item("This is the file 'rho'.\n"), 9219 'G/tau' : Item("This is the file 'tau'.\n"), 9220 'gamma' : Item("This is the file 'gamma'.\n"), 9221 'H' : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:5,7'}), 9222 'H/chi' : Item("This is the file 'chi'.\n"), 9223 'H/psi' : Item("This is the file 'psi'.\n"), 9224 'H/omega' : Item("This is the file 'omega'.\n"), 9225 'H/nu' : Item("New content", 9226 props={SVN_PROP_MERGEINFO : '/A/D/H/nu:7-8'}), 9227 }) 9228 expected_skip = wc.State(D_COPY_path, { }) 9229 svntest.actions.run_and_verify_merge(D_COPY_path, '6', '5', 9230 sbox.repo_url + '/A/D', None, 9231 expected_output, 9232 expected_mergeinfo_output, 9233 expected_elision_output, 9234 expected_disk, 9235 expected_status, 9236 expected_skip, 9237 check_props=True) 9238 # Now once again merge r6 to A_COPY. A_COPY already has r6 in its mergeinfo 9239 # so we expect only subtree merges on A_COPY/D, A_COPY_D_H, and 9240 # A_COPY/D/H/nu. The fact that A/D/H/nu doesn't exist at r6 should not cause 9241 # the merge to fail -- see 9242 # https://issues.apache.org/jira/browse/SVN-3067#desc7. 9243 expected_output = wc.State(A_COPY_path, { 9244 'D/H/omega': Item(status='U '), 9245 }) 9246 expected_mergeinfo_output = wc.State(A_COPY_path, { 9247 '' : Item(status=' G'), 9248 'D' : Item(status=' G'), 9249 'D/H' : Item(status=' G'), 9250 }) 9251 expected_elision_output = wc.State(A_COPY_path, { 9252 'D' : Item(status=' U'), 9253 }) 9254 expected_status = wc.State(A_COPY_path, { 9255 '' : Item(status=' M', wc_rev=2), 9256 'B' : Item(status=' ', wc_rev=2), 9257 'mu' : Item(status=' ', wc_rev=2), 9258 'B/E' : Item(status=' ', wc_rev=2), 9259 'B/E/alpha' : Item(status=' ', wc_rev=2), 9260 'B/E/beta' : Item(status='M ', wc_rev=2), 9261 'B/lambda' : Item(status=' ', wc_rev=2), 9262 'B/F' : Item(status=' ', wc_rev=2), 9263 'C' : Item(status=' ', wc_rev=2), 9264 'D' : Item(status=' ', wc_rev=2), 9265 'D/G' : Item(status=' ', wc_rev=2), 9266 'D/G/pi' : Item(status=' ', wc_rev=2), 9267 'D/G/rho' : Item(status=' ', wc_rev=2), 9268 'D/G/tau' : Item(status=' ', wc_rev=2), 9269 'D/gamma' : Item(status=' ', wc_rev=2), 9270 'D/H' : Item(status=' M', wc_rev=2), 9271 'D/H/chi' : Item(status=' ', wc_rev=2), 9272 'D/H/psi' : Item(status=' ', wc_rev=2), 9273 'D/H/omega' : Item(status='M ', wc_rev=2), 9274 'D/H/nu' : Item(status='A ', copied='+', wc_rev='-'), 9275 }) 9276 expected_disk_1 = wc.State('', { 9277 '' : Item(props={SVN_PROP_MERGEINFO : '/A:5-6'}), 9278 'B' : Item(), 9279 'mu' : Item("This is the file 'mu'.\n"), 9280 'B/E' : Item(), 9281 'B/E/alpha' : Item("This is the file 'alpha'.\n"), 9282 'B/E/beta' : Item("New content"), 9283 'B/lambda' : Item("This is the file 'lambda'.\n"), 9284 'B/F' : Item(), 9285 'C' : Item(), 9286 'D' : Item(), # Mergeinfo elides to 'A_COPY' 9287 'D/G' : Item(), 9288 'D/G/pi' : Item("This is the file 'pi'.\n"), 9289 'D/G/rho' : Item("This is the file 'rho'.\n"), 9290 'D/G/tau' : Item("This is the file 'tau'.\n"), 9291 'D/gamma' : Item("This is the file 'gamma'.\n"), 9292 'D/H' : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:5-7'}), 9293 'D/H/chi' : Item("This is the file 'chi'.\n"), 9294 'D/H/psi' : Item("This is the file 'psi'.\n"), 9295 'D/H/omega' : Item("New content"), 9296 'D/H/nu' : Item("New content", 9297 props={SVN_PROP_MERGEINFO : '/A/D/H/nu:7-8'}), 9298 }) 9299 expected_skip = wc.State(A_COPY_path, { }) 9300 svntest.actions.run_and_verify_merge(A_COPY_path, '5', '6', 9301 sbox.repo_url + '/A', None, 9302 expected_output, 9303 expected_mergeinfo_output, 9304 expected_elision_output, 9305 expected_disk_1, 9306 expected_status, 9307 expected_skip, 9308 check_props=True) 9309 9310 # Commit this merge as r9. 9311 # 9312 # Update the wc first to make setting the expected status a bit easier. 9313 svntest.actions.run_and_verify_svn(exp_noop_up_out(8), [], 9314 'up', wc_dir) 9315 wc_status.tweak(wc_rev=8) 9316 expected_output = wc.State(wc_dir, { 9317 'A_COPY' : Item(verb='Sending'), 9318 'A_COPY/B/E/beta' : Item(verb='Sending'), 9319 'A_COPY/D/H' : Item(verb='Sending'), 9320 'A_COPY/D/H/nu' : Item(verb='Adding'), 9321 'A_COPY/D/H/omega' : Item(verb='Sending'), 9322 }) 9323 wc_status.tweak('A_COPY', 9324 'A_COPY/B/E/beta', 9325 'A_COPY/D/H', 9326 'A_COPY/D/H/omega', 9327 wc_rev=9) 9328 wc_status.add({'A_COPY/D/H/nu' : Item(status=' ', wc_rev=9)}) 9329 svntest.actions.run_and_verify_commit(wc_dir, expected_output, wc_status) 9330 # Update the WC. 9331 svntest.actions.run_and_verify_svn(exp_noop_up_out(9), [], 9332 'up', wc_dir) 9333 wc_status.tweak(wc_rev=9) 9334 9335 # Yet another test for issue #3067. Merge -rX:Y, where X>Y (reverse merge) 9336 # and the merge target has a subtree that came into existence at some rev 9337 # N where X < N < Y. This merge should simply delete the subtree. 9338 # 9339 # For this test merge -r9:2 to A_COPY. This should revert all the merges 9340 # done thus far, leaving the tree rooted at A_COPY with no explicit 9341 # mergeinfo. 9342 expected_output = wc.State(A_COPY_path, { 9343 'B/E/beta' : Item(status='U '), 9344 'D/H/omega': Item(status='U '), 9345 'D/H/nu' : Item(status='D '), 9346 }) 9347 expected_mergeinfo_output = wc.State(A_COPY_path, { 9348 '' : Item(status=' U'), 9349 'D/H': Item(status=' U'), 9350 }) 9351 expected_elision_output = wc.State(A_COPY_path, { 9352 '' : Item(status=' U'), 9353 'D/H': Item(status=' U'), 9354 }) 9355 expected_status = wc.State(A_COPY_path, { 9356 '' : Item(status=' M', wc_rev=9), 9357 'B' : Item(status=' ', wc_rev=9), 9358 'mu' : Item(status=' ', wc_rev=9), 9359 'B/E' : Item(status=' ', wc_rev=9), 9360 'B/E/alpha' : Item(status=' ', wc_rev=9), 9361 'B/E/beta' : Item(status='M ', wc_rev=9), 9362 'B/lambda' : Item(status=' ', wc_rev=9), 9363 'B/F' : Item(status=' ', wc_rev=9), 9364 'C' : Item(status=' ', wc_rev=9), 9365 'D' : Item(status=' ', wc_rev=9), 9366 'D/G' : Item(status=' ', wc_rev=9), 9367 'D/G/pi' : Item(status=' ', wc_rev=9), 9368 'D/G/rho' : Item(status=' ', wc_rev=9), 9369 'D/G/tau' : Item(status=' ', wc_rev=9), 9370 'D/gamma' : Item(status=' ', wc_rev=9), 9371 'D/H' : Item(status=' M', wc_rev=9), 9372 'D/H/chi' : Item(status=' ', wc_rev=9), 9373 'D/H/psi' : Item(status=' ', wc_rev=9), 9374 'D/H/omega' : Item(status='M ', wc_rev=9), 9375 'D/H/nu' : Item(status='D ', wc_rev=9), 9376 }) 9377 expected_disk = wc.State('', { 9378 'B' : Item(), 9379 'mu' : Item("This is the file 'mu'.\n"), 9380 'B/E' : Item(), 9381 'B/E/alpha' : Item("This is the file 'alpha'.\n"), 9382 'B/E/beta' : Item("This is the file 'beta'.\n"), 9383 'B/lambda' : Item("This is the file 'lambda'.\n"), 9384 'B/F' : Item(), 9385 'C' : Item(), 9386 'D' : Item(), 9387 'D/G' : Item(), 9388 'D/G/pi' : Item("This is the file 'pi'.\n"), 9389 'D/G/rho' : Item("This is the file 'rho'.\n"), 9390 'D/G/tau' : Item("This is the file 'tau'.\n"), 9391 'D/gamma' : Item("This is the file 'gamma'.\n"), 9392 'D/H' : Item(), 9393 'D/H/chi' : Item("This is the file 'chi'.\n"), 9394 'D/H/psi' : Item("This is the file 'psi'.\n"), 9395 'D/H/omega' : Item("This is the file 'omega'.\n"), 9396 }) 9397 expected_skip = wc.State(A_COPY_path, { }) 9398 svntest.actions.run_and_verify_merge(A_COPY_path, '9', '2', 9399 sbox.repo_url + '/A', None, 9400 expected_output, 9401 expected_mergeinfo_output, 9402 expected_elision_output, 9403 expected_disk, 9404 expected_status, 9405 expected_skip, 9406 check_props=True) 9407 9408 # Revert the previous merge, then merge r4 to A_COPY/D/G/rho. Commit 9409 # this merge as r10. 9410 svntest.actions.run_and_verify_svn(None, [], 'revert', '-R', wc_dir) 9411 svntest.actions.run_and_verify_svn( 9412 expected_merge_output([[4]], 9413 ['U ' + rho_COPY_path + '\n', 9414 ' G ' + rho_COPY_path + '\n']), 9415 [], 'merge', '-c4', sbox.repo_url + '/A/D/G/rho', rho_COPY_path) 9416 expected_output = wc.State(wc_dir, { 9417 'A_COPY/D/G/rho' : Item(verb='Sending'),}) 9418 wc_status.tweak('A_COPY/D/G/rho', wc_rev=10) 9419 svntest.actions.run_and_verify_commit(wc_dir, expected_output, wc_status) 9420 svntest.actions.run_and_verify_svn(exp_noop_up_out(10), [], 9421 'up', wc_dir) 9422 wc_status.tweak(wc_rev=10) 9423 9424 # Yet another test for issue #3067. Merge -rX:Y, where X>Y (reverse merge) 9425 # and the merge target has a subtree that doesn't exist in the merge source 9426 # between X and Y. This merge should no effect on that subtree. 9427 # 9428 # Specifically, merge -c4 to A_COPY. This should revert the previous merge 9429 # of r4 directly to A_COPY/D/G/rho. The subtree A_COPY/D/H/nu, whose merge 9430 # source A/D/H/nu doesn't in r4:3, shouldn't be affected nor should it break 9431 # the merge editor. 9432 expected_output = wc.State(A_COPY_path, { 9433 'D/G/rho': Item(status='U '), 9434 }) 9435 expected_mergeinfo_output = wc.State(A_COPY_path, { 9436 '' : Item(status=' U'), 9437 'D/G/rho' : Item(status=' U'), 9438 }) 9439 expected_elision_output = wc.State(A_COPY_path, { 9440 'D/G/rho' : Item(status=' U'), 9441 }) 9442 expected_status = wc.State(A_COPY_path, { 9443 '' : Item(status=' ', wc_rev=10), 9444 'B' : Item(status=' ', wc_rev=10), 9445 'mu' : Item(status=' ', wc_rev=10), 9446 'B/E' : Item(status=' ', wc_rev=10), 9447 'B/E/alpha' : Item(status=' ', wc_rev=10), 9448 'B/E/beta' : Item(status=' ', wc_rev=10), 9449 'B/lambda' : Item(status=' ', wc_rev=10), 9450 'B/F' : Item(status=' ', wc_rev=10), 9451 'C' : Item(status=' ', wc_rev=10), 9452 'D' : Item(status=' ', wc_rev=10), 9453 'D/G' : Item(status=' ', wc_rev=10), 9454 'D/G/pi' : Item(status=' ', wc_rev=10), 9455 'D/G/rho' : Item(status='MM', wc_rev=10), 9456 'D/G/tau' : Item(status=' ', wc_rev=10), 9457 'D/gamma' : Item(status=' ', wc_rev=10), 9458 'D/H' : Item(status=' ', wc_rev=10), 9459 'D/H/chi' : Item(status=' ', wc_rev=10), 9460 'D/H/psi' : Item(status=' ', wc_rev=10), 9461 'D/H/omega' : Item(status=' ', wc_rev=10), 9462 'D/H/nu' : Item(status=' ', wc_rev=10), 9463 }) 9464 # Use expected_disk_1 from above since we should be 9465 # returning to that state. 9466 expected_skip = wc.State(A_COPY_path, { }) 9467 svntest.actions.run_and_verify_merge(A_COPY_path, '4', '3', 9468 sbox.repo_url + '/A', None, 9469 expected_output, 9470 expected_mergeinfo_output, 9471 expected_elision_output, 9472 expected_disk_1, 9473 expected_status, 9474 expected_skip, 9475 check_props=True) 9476 9477#---------------------------------------------------------------------- 9478@SkipUnless(server_has_mergeinfo) 9479def dont_add_mergeinfo_from_own_history(sbox): 9480 "cyclic merges don't add mergeinfo from own history" 9481 9482 sbox.build() 9483 wc_dir = sbox.wc_dir 9484 wc_disk, wc_status = set_up_branch(sbox) 9485 9486 # Some paths we'll care about 9487 A_path = sbox.ospath('A') 9488 A_MOVED_path = sbox.ospath('A_MOVED') 9489 mu_path = sbox.ospath('A/mu') 9490 mu_MOVED_path = sbox.ospath('A_MOVED/mu') 9491 A_COPY_path = sbox.ospath('A_COPY') 9492 mu_COPY_path = sbox.ospath('A_COPY/mu') 9493 9494 # Merge r3 from 'A' to 'A_COPY', make a text mod to 'A_COPY/mu' and 9495 # commit both as r7. This results in mergeinfo of '/A:3' on 'A_COPY'. 9496 # Then merge r7 from 'A_COPY' to 'A'. This attempts to add the mergeinfo 9497 # '/A:3' to 'A', but that is self-referrential and should be filtered out, 9498 # leaving only the mergeinfo '/A_COPY:7' on 'A'. 9499 expected_output = wc.State(A_COPY_path, { 9500 'D/H/psi' : Item(status='U '), 9501 }) 9502 expected_mergeinfo_output = wc.State(A_COPY_path, { 9503 '' : Item(status=' U'), 9504 }) 9505 expected_elision_output = wc.State(A_COPY_path, { 9506 }) 9507 expected_A_COPY_status = wc.State(A_COPY_path, { 9508 '' : Item(status=' M', wc_rev=2), 9509 'B' : Item(status=' ', wc_rev=2), 9510 'mu' : Item(status=' ', wc_rev=2), 9511 'B/E' : Item(status=' ', wc_rev=2), 9512 'B/E/alpha' : Item(status=' ', wc_rev=2), 9513 'B/E/beta' : Item(status=' ', wc_rev=2), 9514 'B/lambda' : Item(status=' ', wc_rev=2), 9515 'B/F' : Item(status=' ', wc_rev=2), 9516 'C' : Item(status=' ', wc_rev=2), 9517 'D' : Item(status=' ', wc_rev=2), 9518 'D/G' : Item(status=' ', wc_rev=2), 9519 'D/G/pi' : Item(status=' ', wc_rev=2), 9520 'D/G/rho' : Item(status=' ', wc_rev=2), 9521 'D/G/tau' : Item(status=' ', wc_rev=2), 9522 'D/gamma' : Item(status=' ', wc_rev=2), 9523 'D/H' : Item(status=' ', wc_rev=2), 9524 'D/H/chi' : Item(status=' ', wc_rev=2), 9525 'D/H/psi' : Item(status='M ', wc_rev=2), 9526 'D/H/omega' : Item(status=' ', wc_rev=2), 9527 }) 9528 expected_A_COPY_disk = wc.State('', { 9529 '' : Item(props={SVN_PROP_MERGEINFO : '/A:3'}), 9530 'B' : Item(), 9531 'mu' : Item("This is the file 'mu'.\n"), 9532 'B/E' : Item(), 9533 'B/E/alpha' : Item("This is the file 'alpha'.\n"), 9534 'B/E/beta' : Item("This is the file 'beta'.\n"), 9535 'B/lambda' : Item("This is the file 'lambda'.\n"), 9536 'B/F' : Item(), 9537 'C' : Item(), 9538 'D' : Item(), 9539 'D/G' : Item(), 9540 'D/G/pi' : Item("This is the file 'pi'.\n"), 9541 'D/G/rho' : Item("This is the file 'rho'.\n"), 9542 'D/G/tau' : Item("This is the file 'tau'.\n"), 9543 'D/gamma' : Item("This is the file 'gamma'.\n"), 9544 'D/H' : Item(), 9545 'D/H/chi' : Item("This is the file 'chi'.\n"), 9546 'D/H/psi' : Item("New content"), 9547 'D/H/omega' : Item("This is the file 'omega'.\n"), 9548 }) 9549 expected_A_COPY_skip = wc.State(A_COPY_path, { }) 9550 svntest.actions.run_and_verify_merge(A_COPY_path, '2', '3', 9551 sbox.repo_url + '/A', None, 9552 expected_output, 9553 expected_mergeinfo_output, 9554 expected_elision_output, 9555 expected_A_COPY_disk, 9556 expected_A_COPY_status, 9557 expected_A_COPY_skip, 9558 check_props=True) 9559 9560 # Change 'A_COPY/mu' 9561 svntest.main.file_write(mu_COPY_path, "New content") 9562 9563 # Commit r7 9564 expected_output = wc.State(wc_dir, { 9565 'A_COPY' : Item(verb='Sending'), 9566 'A_COPY/D/H/psi' : Item(verb='Sending'), 9567 'A_COPY/mu' : Item(verb='Sending'), 9568 }) 9569 wc_status.tweak('A_COPY', 'A_COPY/D/H/psi', 'A_COPY/mu', wc_rev=7) 9570 svntest.actions.run_and_verify_commit(wc_dir, 9571 expected_output, 9572 wc_status) 9573 9574 # Merge r7 back to the 'A' 9575 expected_output = wc.State(A_path, { 9576 'mu' : Item(status='U '), 9577 }) 9578 expected_mergeinfo_output = wc.State(A_path, { 9579 '' : Item(status=' U'), 9580 }) 9581 expected_elision_output = wc.State(A_path, { 9582 }) 9583 expected_A_status = wc.State(A_path, { 9584 '' : Item(status=' M', wc_rev=1), 9585 'B' : Item(status=' ', wc_rev=1), 9586 'mu' : Item(status='M ', wc_rev=1), 9587 'B/E' : Item(status=' ', wc_rev=1), 9588 'B/E/alpha' : Item(status=' ', wc_rev=1), 9589 'B/E/beta' : Item(status=' ', wc_rev=5), 9590 'B/lambda' : Item(status=' ', wc_rev=1), 9591 'B/F' : Item(status=' ', wc_rev=1), 9592 'C' : Item(status=' ', wc_rev=1), 9593 'D' : Item(status=' ', wc_rev=1), 9594 'D/G' : Item(status=' ', wc_rev=1), 9595 'D/G/pi' : Item(status=' ', wc_rev=1), 9596 'D/G/rho' : Item(status=' ', wc_rev=4), 9597 'D/G/tau' : Item(status=' ', wc_rev=1), 9598 'D/gamma' : Item(status=' ', wc_rev=1), 9599 'D/H' : Item(status=' ', wc_rev=1), 9600 'D/H/chi' : Item(status=' ', wc_rev=1), 9601 'D/H/psi' : Item(status=' ', wc_rev=3), 9602 'D/H/omega' : Item(status=' ', wc_rev=6), 9603 }) 9604 expected_A_disk = wc.State('', { 9605 '' : Item(props={SVN_PROP_MERGEINFO : '/A_COPY:7'}), 9606 'B' : Item(), 9607 'mu' : Item("New content"), 9608 'B/E' : Item(), 9609 'B/E/alpha' : Item("This is the file 'alpha'.\n"), 9610 'B/E/beta' : Item("New content"), 9611 'B/lambda' : Item("This is the file 'lambda'.\n"), 9612 'B/F' : Item(), 9613 'C' : Item(), 9614 'D' : Item(), 9615 'D/G' : Item(), 9616 'D/G/pi' : Item("This is the file 'pi'.\n"), 9617 'D/G/rho' : Item("New content"), 9618 'D/G/tau' : Item("This is the file 'tau'.\n"), 9619 'D/gamma' : Item("This is the file 'gamma'.\n"), 9620 'D/H' : Item(), 9621 'D/H/chi' : Item("This is the file 'chi'.\n"), 9622 'D/H/psi' : Item("New content"), 9623 'D/H/omega' : Item("New content"), 9624 }) 9625 expected_A_skip = wc.State(A_path, {}) 9626 svntest.actions.run_and_verify_merge(A_path, '6', '7', 9627 sbox.repo_url + '/A_COPY', None, 9628 expected_output, 9629 expected_mergeinfo_output, 9630 expected_elision_output, 9631 expected_A_disk, 9632 expected_A_status, 9633 expected_A_skip, 9634 [], True, False, 9635 '--allow-mixed-revisions', A_path) 9636 9637 # Revert all local mods 9638 svntest.actions.run_and_verify_svn(["Reverted '" + A_path + "'\n", 9639 "Reverted '" + mu_path + "'\n"], 9640 [], 'revert', '-R', wc_dir) 9641 9642 # Move 'A' to 'A_MOVED' and once again merge r7 from 'A_COPY', this time 9643 # to 'A_MOVED'. This attempts to add the mergeinfo '/A:3' to 9644 # 'A_MOVED', but 'A_MOVED@3' is 'A', so again this mergeinfo is filtered 9645 # out, leaving the only the mergeinfo created from the merge itself: 9646 # '/A_COPY:7'. 9647 svntest.actions.run_and_verify_svn(['Committing transaction...\n', 9648 'Committed revision 8.\n'], 9649 [], 'move', 9650 sbox.repo_url + '/A', 9651 sbox.repo_url + '/A_MOVED', 9652 '-m', 'Copy A to A_MOVED') 9653 wc_status.remove('A', 'A/B', 'A/B/lambda', 'A/B/E', 'A/B/E/alpha', 9654 'A/B/E/beta', 'A/B/F', 'A/mu', 'A/C', 'A/D', 'A/D/gamma', 'A/D/G', 9655 'A/D/G/pi', 'A/D/G/rho', 'A/D/G/tau', 'A/D/H', 'A/D/H/chi', 9656 'A/D/H/omega', 'A/D/H/psi') 9657 wc_status.add({ 9658 'A_MOVED' : Item(), 9659 'A_MOVED/B' : Item(), 9660 'A_MOVED/B/lambda' : Item(), 9661 'A_MOVED/B/E' : Item(), 9662 'A_MOVED/B/E/alpha' : Item(), 9663 'A_MOVED/B/E/beta' : Item(), 9664 'A_MOVED/B/F' : Item(), 9665 'A_MOVED/mu' : Item(), 9666 'A_MOVED/C' : Item(), 9667 'A_MOVED/D' : Item(), 9668 'A_MOVED/D/gamma' : Item(), 9669 'A_MOVED/D/G' : Item(), 9670 'A_MOVED/D/G/pi' : Item(), 9671 'A_MOVED/D/G/rho' : Item(), 9672 'A_MOVED/D/G/tau' : Item(), 9673 'A_MOVED/D/H' : Item(), 9674 'A_MOVED/D/H/chi' : Item(), 9675 'A_MOVED/D/H/omega' : Item(), 9676 'A_MOVED/D/H/psi' : Item(), 9677 }) 9678 wc_status.tweak(wc_rev=8, status=' ') 9679 wc_disk.remove('A', 'A/B', 'A/B/lambda', 'A/B/E', 'A/B/E/alpha', 9680 'A/B/E/beta', 'A/B/F', 'A/mu', 'A/C', 'A/D', 'A/D/gamma', 9681 'A/D/G', 'A/D/G/pi', 'A/D/G/rho', 'A/D/G/tau', 'A/D/H', 9682 'A/D/H/chi', 'A/D/H/omega', 'A/D/H/psi' ) 9683 wc_disk.add({ 9684 'A_MOVED' : Item(), 9685 'A_MOVED/B' : Item(), 9686 'A_MOVED/B/lambda' : Item("This is the file 'lambda'.\n"), 9687 'A_MOVED/B/E' : Item(), 9688 'A_MOVED/B/E/alpha' : Item("This is the file 'alpha'.\n"), 9689 'A_MOVED/B/E/beta' : Item("New content"), 9690 'A_MOVED/B/F' : Item(), 9691 'A_MOVED/mu' : Item("This is the file 'mu'.\n"), 9692 'A_MOVED/C' : Item(), 9693 'A_MOVED/D' : Item(), 9694 'A_MOVED/D/gamma' : Item("This is the file 'gamma'.\n"), 9695 'A_MOVED/D/G' : Item(), 9696 'A_MOVED/D/G/pi' : Item("This is the file 'pi'.\n"), 9697 'A_MOVED/D/G/rho' : Item("New content"), 9698 'A_MOVED/D/G/tau' : Item("This is the file 'tau'.\n"), 9699 'A_MOVED/D/H' : Item(), 9700 'A_MOVED/D/H/chi' : Item("This is the file 'chi'.\n"), 9701 'A_MOVED/D/H/omega' : Item("New content"), 9702 'A_MOVED/D/H/psi' : Item("New content"), 9703 }) 9704 wc_disk.tweak('A_COPY/D/H/psi', 'A_COPY/mu', contents='New content') 9705 wc_disk.tweak('A_COPY', props={SVN_PROP_MERGEINFO : '/A:3'}) 9706 expected_output = wc.State(wc_dir, { 9707 'A' : Item(status='D '), 9708 'A_MOVED' : Item(status='A '), 9709 'A_MOVED/B' : Item(status='A '), 9710 'A_MOVED/B/lambda' : Item(status='A '), 9711 'A_MOVED/B/E' : Item(status='A '), 9712 'A_MOVED/B/E/alpha' : Item(status='A '), 9713 'A_MOVED/B/E/beta' : Item(status='A '), 9714 'A_MOVED/B/F' : Item(status='A '), 9715 'A_MOVED/mu' : Item(status='A '), 9716 'A_MOVED/C' : Item(status='A '), 9717 'A_MOVED/D' : Item(status='A '), 9718 'A_MOVED/D/gamma' : Item(status='A '), 9719 'A_MOVED/D/G' : Item(status='A '), 9720 'A_MOVED/D/G/pi' : Item(status='A '), 9721 'A_MOVED/D/G/rho' : Item(status='A '), 9722 'A_MOVED/D/G/tau' : Item(status='A '), 9723 'A_MOVED/D/H' : Item(status='A '), 9724 'A_MOVED/D/H/chi' : Item(status='A '), 9725 'A_MOVED/D/H/omega' : Item(status='A '), 9726 'A_MOVED/D/H/psi' : Item(status='A ') 9727 }) 9728 svntest.actions.run_and_verify_update(wc_dir, 9729 expected_output, 9730 wc_disk, 9731 wc_status, 9732 check_props=True) 9733 9734 expected_output = wc.State(A_MOVED_path, { 9735 'mu' : Item(status='U '), 9736 }) 9737 expected_mergeinfo_output = wc.State(A_MOVED_path, { 9738 '' : Item(status=' U'), 9739 }) 9740 expected_elision_output = wc.State(A_MOVED_path, { 9741 }) 9742 expected_A_status = wc.State(A_MOVED_path, { 9743 '' : Item(status=' M', wc_rev=8), 9744 'B' : Item(status=' ', wc_rev=8), 9745 'mu' : Item(status='M ', wc_rev=8), 9746 'B/E' : Item(status=' ', wc_rev=8), 9747 'B/E/alpha' : Item(status=' ', wc_rev=8), 9748 'B/E/beta' : Item(status=' ', wc_rev=8), 9749 'B/lambda' : Item(status=' ', wc_rev=8), 9750 'B/F' : Item(status=' ', wc_rev=8), 9751 'C' : Item(status=' ', wc_rev=8), 9752 'D' : Item(status=' ', wc_rev=8), 9753 'D/G' : Item(status=' ', wc_rev=8), 9754 'D/G/pi' : Item(status=' ', wc_rev=8), 9755 'D/G/rho' : Item(status=' ', wc_rev=8), 9756 'D/G/tau' : Item(status=' ', wc_rev=8), 9757 'D/gamma' : Item(status=' ', wc_rev=8), 9758 'D/H' : Item(status=' ', wc_rev=8), 9759 'D/H/chi' : Item(status=' ', wc_rev=8), 9760 'D/H/psi' : Item(status=' ', wc_rev=8), 9761 'D/H/omega' : Item(status=' ', wc_rev=8), 9762 }) 9763 # We can reuse expected_A_disk from above without change. 9764 svntest.actions.run_and_verify_merge(A_MOVED_path, '6', '7', 9765 sbox.repo_url + '/A_COPY', None, 9766 expected_output, 9767 expected_mergeinfo_output, 9768 expected_elision_output, 9769 expected_A_disk, 9770 expected_A_status, 9771 expected_A_skip, 9772 check_props=True) 9773 9774 # Revert all local mods 9775 svntest.actions.run_and_verify_svn(["Reverted '" + A_MOVED_path + "'\n", 9776 "Reverted '" + mu_MOVED_path + "'\n"], 9777 [], 'revert', '-R', wc_dir) 9778 9779 # Create a new 'A' unrelated to the old 'A' which was moved. Then merge 9780 # r7 from 'A_COPY' to this new 'A'. Since the new 'A' shares no history 9781 # with the mergeinfo 'A@3', the mergeinfo '/A:3' is added and when combined 9782 # with the mergeinfo created from the merge should result in 9783 # '/A:3\n/A_COPY:7' 9784 # 9785 # Create the new 'A' by exporting the old 'A@1'. 9786 expected_output = svntest.verify.UnorderedOutput( 9787 ["A " + sbox.ospath('A') + "\n", 9788 "A " + sbox.ospath('A/B') + "\n", 9789 "A " + sbox.ospath('A/B/lambda') + "\n", 9790 "A " + sbox.ospath('A/B/E') + "\n", 9791 "A " + sbox.ospath('A/B/E/alpha') + "\n", 9792 "A " + sbox.ospath('A/B/E/beta') + "\n", 9793 "A " + sbox.ospath('A/B/F') + "\n", 9794 "A " + sbox.ospath('A/mu') + "\n", 9795 "A " + sbox.ospath('A/C') + "\n", 9796 "A " + sbox.ospath('A/D') + "\n", 9797 "A " + sbox.ospath('A/D/gamma') + "\n", 9798 "A " + sbox.ospath('A/D/G') + "\n", 9799 "A " + sbox.ospath('A/D/G/pi') + "\n", 9800 "A " + sbox.ospath('A/D/G/rho') + "\n", 9801 "A " + sbox.ospath('A/D/G/tau') + "\n", 9802 "A " + sbox.ospath('A/D/H') + "\n", 9803 "A " + sbox.ospath('A/D/H/chi') + "\n", 9804 "A " + sbox.ospath('A/D/H/omega') + "\n", 9805 "A " + sbox.ospath('A/D/H/psi') + "\n", 9806 "Exported revision 1.\n",] 9807 ) 9808 svntest.actions.run_and_verify_svn(expected_output, [], 9809 'export', sbox.repo_url + '/A@1', 9810 A_path) 9811 expected_output = svntest.verify.UnorderedOutput( 9812 ["A " + sbox.ospath('A') + "\n", 9813 "A " + sbox.ospath('A/B') + "\n", 9814 "A " + sbox.ospath('A/B/lambda') + "\n", 9815 "A " + sbox.ospath('A/B/E') + "\n", 9816 "A " + sbox.ospath('A/B/E/alpha') + "\n", 9817 "A " + sbox.ospath('A/B/E/beta') + "\n", 9818 "A " + sbox.ospath('A/B/F') + "\n", 9819 "A " + sbox.ospath('A/mu') + "\n", 9820 "A " + sbox.ospath('A/C') + "\n", 9821 "A " + sbox.ospath('A/D') + "\n", 9822 "A " + sbox.ospath('A/D/gamma') + "\n", 9823 "A " + sbox.ospath('A/D/G') + "\n", 9824 "A " + sbox.ospath('A/D/G/pi') + "\n", 9825 "A " + sbox.ospath('A/D/G/rho') + "\n", 9826 "A " + sbox.ospath('A/D/G/tau') + "\n", 9827 "A " + sbox.ospath('A/D/H') + "\n", 9828 "A " + sbox.ospath('A/D/H/chi') + "\n", 9829 "A " + sbox.ospath('A/D/H/omega') + "\n", 9830 "A " + sbox.ospath('A/D/H/psi') + "\n",] 9831 ) 9832 svntest.actions.run_and_verify_svn(expected_output, [], 9833 'add', A_path) 9834 # Commit the new 'A' as r9 9835 expected_output = wc.State(wc_dir, { 9836 'A' : Item(verb='Adding'), 9837 'A/B' : Item(verb='Adding'), 9838 'A/mu' : Item(verb='Adding'), 9839 'A/B/E' : Item(verb='Adding'), 9840 'A/B/E/alpha' : Item(verb='Adding'), 9841 'A/B/E/beta' : Item(verb='Adding'), 9842 'A/B/lambda' : Item(verb='Adding'), 9843 'A/B/F' : Item(verb='Adding'), 9844 'A/C' : Item(verb='Adding'), 9845 'A/D' : Item(verb='Adding'), 9846 'A/D/G' : Item(verb='Adding'), 9847 'A/D/G/pi' : Item(verb='Adding'), 9848 'A/D/G/rho' : Item(verb='Adding'), 9849 'A/D/G/tau' : Item(verb='Adding'), 9850 'A/D/gamma' : Item(verb='Adding'), 9851 'A/D/H' : Item(verb='Adding'), 9852 'A/D/H/chi' : Item(verb='Adding'), 9853 'A/D/H/psi' : Item(verb='Adding'), 9854 'A/D/H/omega' : Item(verb='Adding'), 9855 }) 9856 wc_status.tweak(wc_rev=8) 9857 wc_status.add({ 9858 'A' : Item(wc_rev=9), 9859 'A/B' : Item(wc_rev=9), 9860 'A/B/lambda' : Item(wc_rev=9), 9861 'A/B/E' : Item(wc_rev=9), 9862 'A/B/E/alpha' : Item(wc_rev=9), 9863 'A/B/E/beta' : Item(wc_rev=9), 9864 'A/B/F' : Item(wc_rev=9), 9865 'A/mu' : Item(wc_rev=9), 9866 'A/C' : Item(wc_rev=9), 9867 'A/D' : Item(wc_rev=9), 9868 'A/D/gamma' : Item(wc_rev=9), 9869 'A/D/G' : Item(wc_rev=9), 9870 'A/D/G/pi' : Item(wc_rev=9), 9871 'A/D/G/rho' : Item(wc_rev=9), 9872 'A/D/G/tau' : Item(wc_rev=9), 9873 'A/D/H' : Item(wc_rev=9), 9874 'A/D/H/chi' : Item(wc_rev=9), 9875 'A/D/H/omega' : Item(wc_rev=9), 9876 'A/D/H/psi' : Item(wc_rev=9), 9877 }) 9878 wc_status.tweak(status=' ') 9879 svntest.actions.run_and_verify_commit(wc_dir, 9880 expected_output, 9881 wc_status) 9882 9883 expected_output = wc.State(A_path, { 9884 'mu' : Item(status='U '), 9885 'D/H/psi' : Item(status='U '), 9886 '' : Item(status=' U'), 9887 }) 9888 expected_mergeinfo_output = wc.State(A_path, { 9889 '' : Item(status=' G'), 9890 }) 9891 expected_elision_output = wc.State(A_path, { 9892 }) 9893 expected_A_status = wc.State(A_path, { 9894 '' : Item(status=' M', wc_rev=9), 9895 'B' : Item(status=' ', wc_rev=9), 9896 'mu' : Item(status='M ', wc_rev=9), 9897 'B/E' : Item(status=' ', wc_rev=9), 9898 'B/E/alpha' : Item(status=' ', wc_rev=9), 9899 'B/E/beta' : Item(status=' ', wc_rev=9), 9900 'B/lambda' : Item(status=' ', wc_rev=9), 9901 'B/F' : Item(status=' ', wc_rev=9), 9902 'C' : Item(status=' ', wc_rev=9), 9903 'D' : Item(status=' ', wc_rev=9), 9904 'D/G' : Item(status=' ', wc_rev=9), 9905 'D/G/pi' : Item(status=' ', wc_rev=9), 9906 'D/G/rho' : Item(status=' ', wc_rev=9), 9907 'D/G/tau' : Item(status=' ', wc_rev=9), 9908 'D/gamma' : Item(status=' ', wc_rev=9), 9909 'D/H' : Item(status=' ', wc_rev=9), 9910 'D/H/chi' : Item(status=' ', wc_rev=9), 9911 'D/H/psi' : Item(status='M ', wc_rev=9), 9912 'D/H/omega' : Item(status=' ', wc_rev=9), 9913 }) 9914 expected_A_disk = wc.State('', { 9915 '' : Item(props={SVN_PROP_MERGEINFO : '/A:3\n/A_COPY:7'}), 9916 'B' : Item(), 9917 'mu' : Item("New content"), 9918 'B/E' : Item(), 9919 'B/E/alpha' : Item("This is the file 'alpha'.\n"), 9920 'B/E/beta' : Item("This is the file 'beta'.\n"), 9921 'B/lambda' : Item("This is the file 'lambda'.\n"), 9922 'B/F' : Item(), 9923 'C' : Item(), 9924 'D' : Item(), 9925 'D/G' : Item(), 9926 'D/G/pi' : Item("This is the file 'pi'.\n"), 9927 'D/G/rho' : Item("This is the file 'rho'.\n"), 9928 'D/G/tau' : Item("This is the file 'tau'.\n"), 9929 'D/gamma' : Item("This is the file 'gamma'.\n"), 9930 'D/H' : Item(), 9931 'D/H/chi' : Item("This is the file 'chi'.\n"), 9932 'D/H/psi' : Item("New content"), 9933 'D/H/omega' : Item("This is the file 'omega'.\n"), 9934 }) 9935 expected_A_skip = wc.State(A_path, {}) 9936 svntest.actions.run_and_verify_merge(A_path, '6', '7', 9937 sbox.repo_url + '/A_COPY', None, 9938 expected_output, 9939 expected_mergeinfo_output, 9940 expected_elision_output, 9941 expected_A_disk, 9942 expected_A_status, 9943 expected_A_skip, 9944 check_props=True) 9945 9946#---------------------------------------------------------------------- 9947@SkipUnless(server_has_mergeinfo) 9948@Issue(3094) 9949def merge_range_predates_history(sbox): 9950 "merge range predates history" 9951 9952 sbox.build() 9953 wc_dir = sbox.wc_dir 9954 9955 iota_path = sbox.ospath('iota') 9956 trunk_file_path = sbox.ospath('trunk/file') 9957 trunk_url = sbox.repo_url + "/trunk" 9958 branches_url = sbox.repo_url + "/branches" 9959 branch_path = sbox.ospath('branches/branch') 9960 branch_file_path = sbox.ospath('branches/branch/file') 9961 branch_url = sbox.repo_url + "/branches/branch" 9962 9963 # Tweak a file and commit. (r2) 9964 svntest.main.file_append(iota_path, "More data.\n") 9965 sbox.simple_commit(message='tweak iota') 9966 9967 # Create our trunk and branches directory, and update working copy. (r3) 9968 svntest.main.run_svn(None, 'mkdir', trunk_url, branches_url, 9969 '-m', 'add trunk and branches dirs') 9970 svntest.main.run_svn(None, 'up', wc_dir) 9971 9972 # Add a file to the trunk and commit. (r4) 9973 svntest.main.file_append(trunk_file_path, "This is the file 'file'.\n") 9974 svntest.main.run_svn(None, 'add', trunk_file_path) 9975 sbox.simple_commit(message='add trunk file') 9976 9977 # Branch trunk from r3, and update working copy. (r5) 9978 svntest.main.run_svn(None, 'cp', trunk_url, branch_url, '-r3', 9979 '-m', 'branch trunk@2') 9980 svntest.main.run_svn(None, 'up', wc_dir) 9981 9982 # Now, try to merge trunk into the branch. There should be one 9983 # outstanding change -- the addition of the file. 9984 expected_output = expected_merge_output([[4,5]], 9985 ['A ' + branch_file_path + '\n', 9986 ' U ' + branch_path + '\n']) 9987 svntest.actions.run_and_verify_svn(expected_output, [], 'merge', 9988 trunk_url, branch_path) 9989 9990#---------------------------------------------------------------------- 9991@Issue(3623) 9992def foreign_repos(sbox): 9993 "merge from a foreign repository" 9994 9995 sbox.build() 9996 wc_dir = sbox.wc_dir 9997 9998 # Make a copy of this repository and associated working copy. Both 9999 # should have nothing but a Greek tree in them, and the two 10000 # repository UUIDs should differ. 10001 sbox2 = sbox.clone_dependent(True) 10002 sbox2.build() 10003 wc_dir2 = sbox2.wc_dir 10004 10005 # Convenience variables for working copy paths. 10006 Z_path = sbox.ospath('A/D/G/Z') 10007 B_path = sbox.ospath('A/B') 10008 Q_path = sbox.ospath('Q') 10009 H_path = sbox.ospath('A/D/H') 10010 iota_path = sbox.ospath('iota') 10011 beta_path = sbox.ospath('A/B/E/beta') 10012 alpha_path = sbox.ospath('A/B/E/alpha') 10013 zeta_path = sbox.ospath('A/D/G/Z/zeta') 10014 fred_path = sbox.ospath('A/C/fred') 10015 10016 # Add new directories, with and without properties. 10017 svntest.main.run_svn(None, 'mkdir', Q_path, Z_path) 10018 svntest.main.run_svn(None, 'pset', 'foo', 'bar', Z_path) 10019 10020 # Add new files, with contents, with and without properties. 10021 zeta_contents = "This is the file 'zeta'.\n" 10022 fred_contents = "This is the file 'fred'.\n" 10023 svntest.main.file_append(zeta_path, zeta_contents) 10024 svntest.main.file_append(fred_path, fred_contents) 10025 svntest.main.run_svn(None, 'add', zeta_path, fred_path) 10026 svntest.main.run_svn(None, 'pset', 'foo', 'bar', fred_path) 10027 10028 # Modify existing files and directories. 10029 added_contents = "This is another line of text.\n" 10030 svntest.main.file_append(iota_path, added_contents) 10031 svntest.main.file_append(beta_path, added_contents) 10032 svntest.main.run_svn(None, 'pset', 'foo', 'bar', iota_path, B_path) 10033 10034 # Delete some stuff 10035 svntest.main.run_svn(None, 'delete', alpha_path, H_path) 10036 10037 # Commit up these changes. 10038 expected_output = wc.State(wc_dir, { 10039 'Q' : Item(verb='Adding'), 10040 'A/D/G/Z' : Item(verb='Adding'), 10041 'A/D/G/Z/zeta' : Item(verb='Adding'), 10042 'A/C/fred' : Item(verb='Adding'), 10043 'iota' : Item(verb='Sending'), 10044 'A/B' : Item(verb='Sending'), 10045 'A/B/E/beta' : Item(verb='Sending'), 10046 'A/B/E/alpha' : Item(verb='Deleting'), 10047 'A/D/H' : Item(verb='Deleting'), 10048 }) 10049 expected_status = svntest.actions.get_virginal_state(wc_dir, 1) 10050 expected_status.add({ 10051 'Q' : Item(status=' ', wc_rev=2), 10052 'A/D/G/Z' : Item(status=' ', wc_rev=2), 10053 'A/D/G/Z/zeta' : Item(status=' ', wc_rev=2), 10054 'A/C/fred' : Item(status=' ', wc_rev=2), 10055 }) 10056 expected_status.tweak('iota', 'A/B/E/beta', 'A/B', wc_rev=2) 10057 expected_status.remove('A/B/E/alpha', 'A/D/H', 'A/D/H/chi', 10058 'A/D/H/psi', 'A/D/H/omega') 10059 expected_disk = svntest.main.greek_state.copy() 10060 expected_disk.add({ 10061 'Q' : Item(), 10062 'A/D/G/Z' : Item(props={'foo':'bar'}), 10063 'A/D/G/Z/zeta' : Item(contents=zeta_contents), 10064 'A/C/fred' : Item(contents=fred_contents,props={'foo':'bar'}), 10065 }) 10066 expected_disk.remove('A/B/E/alpha', 'A/D/H', 'A/D/H/chi', 10067 'A/D/H/psi', 'A/D/H/omega') 10068 expected_disk.tweak('iota', 10069 contents=expected_disk.desc['iota'].contents 10070 + added_contents, 10071 props={'foo':'bar'}) 10072 expected_disk.tweak('A/B', props={'foo':'bar'}) 10073 expected_disk.tweak('A/B/E/beta', 10074 contents=expected_disk.desc['A/B/E/beta'].contents 10075 + added_contents) 10076 svntest.actions.run_and_verify_commit(wc_dir, 10077 expected_output, 10078 expected_status) 10079 svntest.actions.verify_disk(wc_dir, expected_disk, True) 10080 10081 # Now, merge our committed revision into a working copy of another 10082 # repository. Not only should the merge succeed, but the results on 10083 # disk should match those in our first working copy. 10084 10085 ### TODO: Use run_and_verify_merge() ### 10086 svntest.main.run_svn(None, 'merge', '-c2', sbox.repo_url, wc_dir2) 10087 sbox2.simple_commit(message='Merge from foreign repo') 10088 svntest.actions.verify_disk(wc_dir2, expected_disk, True) 10089 10090 # Now, let's make a third checkout -- our second from the original 10091 # repository -- and make sure that all the data there is correct. 10092 # It should look just like the original EXPECTED_DISK. 10093 # This is a regression test for issue #3623 in which wc_dir2 had the 10094 # correct state but the committed state was wrong. 10095 wc_dir3 = sbox.add_wc_path('wc3') 10096 svntest.actions.run_and_verify_svn(None, [], 'checkout', 10097 sbox2.repo_url, wc_dir3) 10098 svntest.actions.verify_disk(wc_dir3, expected_disk, True) 10099 10100#---------------------------------------------------------------------- 10101def foreign_repos_uuid(sbox): 10102 "verify uuid of items added via foreign repo merge" 10103 10104 sbox.build() 10105 wc_dir = sbox.wc_dir 10106 wc_uuid = svntest.actions.get_wc_uuid(wc_dir) 10107 10108 # Make a copy of this repository and associated working copy. Both 10109 # should have nothing but a Greek tree in them, and the two 10110 # repository UUIDs should differ. 10111 sbox2 = sbox.clone_dependent(True) 10112 sbox2.build() 10113 wc_dir2 = sbox2.wc_dir 10114 wc2_uuid = svntest.actions.get_wc_uuid(wc_dir2) 10115 10116 # Convenience variables for working copy paths. 10117 zeta_path = sbox.ospath('A/D/G/zeta') 10118 Z_path = sbox.ospath('A/Z') 10119 10120 # Add new file and directory. 10121 zeta_contents = "This is the file 'zeta'.\n" 10122 svntest.main.file_append(zeta_path, zeta_contents) 10123 os.mkdir(Z_path) 10124 svntest.main.run_svn(None, 'add', zeta_path, Z_path) 10125 10126 # Commit up these changes. 10127 expected_output = wc.State(wc_dir, { 10128 'A/D/G/zeta' : Item(verb='Adding'), 10129 'A/Z' : Item(verb='Adding'), 10130 }) 10131 expected_status = svntest.actions.get_virginal_state(wc_dir, 1) 10132 expected_status.add({ 10133 'A/D/G/zeta' : Item(status=' ', wc_rev=2), 10134 'A/Z' : Item(status=' ', wc_rev=2), 10135 }) 10136 expected_disk = svntest.main.greek_state.copy() 10137 expected_disk.add({ 10138 'A/D/G/zeta' : Item(contents=zeta_contents), 10139 'A/Z' : Item(), 10140 }) 10141 svntest.actions.run_and_verify_commit(wc_dir, 10142 expected_output, 10143 expected_status) 10144 svntest.actions.verify_disk(wc_dir, expected_disk, True) 10145 10146 svntest.main.run_svn(None, 'merge', '-c2', sbox.repo_url, wc_dir2) 10147 sbox2.simple_commit(message='Merge from foreign repos') 10148 10149 # Run info to check the copied rev to make sure it's right 10150 zeta2_path = os.path.join(wc_dir2, 'A', 'D', 'G', 'zeta') 10151 expected_info = {"Path" : re.escape(zeta2_path), # escape backslashes 10152 "URL" : sbox2.repo_url + "/A/D/G/zeta", 10153 "Repository Root" : sbox2.repo_url, 10154 "Repository UUID" : wc2_uuid, 10155 "Revision" : "2", 10156 "Node Kind" : "file", 10157 "Schedule" : "normal", 10158 } 10159 svntest.actions.run_and_verify_info([expected_info], zeta2_path) 10160 10161 # Run info to check the copied rev to make sure it's right 10162 Z2_path = os.path.join(wc_dir2, 'A', 'Z') 10163 expected_info = {"Path" : re.escape(Z2_path), # escape backslashes 10164 "URL" : sbox2.repo_url + "/A/Z", 10165 "Repository Root" : sbox2.repo_url, 10166 "Repository UUID" : wc2_uuid, 10167 "Revision" : "2", 10168 "Node Kind" : "directory", 10169 "Schedule" : "normal", 10170 } 10171 svntest.actions.run_and_verify_info([expected_info], Z2_path) 10172 10173#---------------------------------------------------------------------- 10174def foreign_repos_2_url(sbox): 10175 "2-url merge from a foreign repository" 10176 10177 sbox.build() 10178 wc_dir = sbox.wc_dir 10179 10180 # Make a copy of this repository and associated working copy. Both 10181 # should have nothing but a Greek tree in them, and the two 10182 # repository UUIDs should differ. 10183 sbox2 = sbox.clone_dependent(True) 10184 sbox2.build() 10185 wc_dir2 = sbox2.wc_dir 10186 10187 # Convenience variables for working copy paths. 10188 Z_path = sbox.ospath('A/D/G/Z') 10189 Q_path = sbox.ospath('A/Q') 10190 H_path = sbox.ospath('A/D/H') 10191 beta_path = sbox.ospath('A/B/E/beta') 10192 alpha_path = sbox.ospath('A/B/E/alpha') 10193 zeta_path = sbox.ospath('A/D/G/Z/zeta') 10194 fred_path = sbox.ospath('A/C/fred') 10195 10196 # First, "tag" the current state of the repository. 10197 svntest.main.run_svn(None, 'copy', sbox.repo_url + '/A', 10198 sbox.repo_url + '/A-tag1', '-m', 'tag1') 10199 10200 # Add new directories 10201 svntest.main.run_svn(None, 'mkdir', Q_path, Z_path) 10202 10203 # Add new files 10204 zeta_contents = "This is the file 'zeta'.\n" 10205 fred_contents = "This is the file 'fred'.\n" 10206 svntest.main.file_append(zeta_path, zeta_contents) 10207 svntest.main.file_append(fred_path, fred_contents) 10208 svntest.main.run_svn(None, 'add', zeta_path, fred_path) 10209 10210 # Modify existing files 10211 added_contents = "This is another line of text.\n" 10212 svntest.main.file_append(beta_path, added_contents) 10213 10214 # Delete some stuff 10215 svntest.main.run_svn(None, 'delete', alpha_path, H_path) 10216 10217 # Commit up these changes. 10218 expected_output = wc.State(wc_dir, { 10219 'A/Q' : Item(verb='Adding'), 10220 'A/D/G/Z' : Item(verb='Adding'), 10221 'A/D/G/Z/zeta' : Item(verb='Adding'), 10222 'A/C/fred' : Item(verb='Adding'), 10223 'A/B/E/beta' : Item(verb='Sending'), 10224 'A/B/E/alpha' : Item(verb='Deleting'), 10225 'A/D/H' : Item(verb='Deleting'), 10226 }) 10227 expected_status = svntest.actions.get_virginal_state(wc_dir, 1) 10228 expected_status.add({ 10229 'A/Q' : Item(status=' ', wc_rev=3), 10230 'A/D/G/Z' : Item(status=' ', wc_rev=3), 10231 'A/D/G/Z/zeta' : Item(status=' ', wc_rev=3), 10232 'A/C/fred' : Item(status=' ', wc_rev=3), 10233 }) 10234 expected_status.tweak('A/B/E/beta', wc_rev=3) 10235 expected_status.remove('A/B/E/alpha', 'A/D/H', 'A/D/H/chi', 10236 'A/D/H/psi', 'A/D/H/omega') 10237 expected_disk = svntest.main.greek_state.copy() 10238 expected_disk.add({ 10239 'A/Q' : Item(), 10240 'A/D/G/Z' : Item(), 10241 'A/D/G/Z/zeta' : Item(contents=zeta_contents), 10242 'A/C/fred' : Item(contents=fred_contents), 10243 }) 10244 expected_disk.remove('A/B/E/alpha', 'A/D/H', 'A/D/H/chi', 10245 'A/D/H/psi', 'A/D/H/omega') 10246 expected_disk.tweak('A/B/E/beta', 10247 contents=expected_disk.desc['A/B/E/beta'].contents 10248 + added_contents) 10249 svntest.actions.run_and_verify_commit(wc_dir, 10250 expected_output, 10251 expected_status) 10252 svntest.actions.verify_disk(wc_dir, expected_disk, True) 10253 10254 # Now, "tag" the new state of the repository. 10255 svntest.main.run_svn(None, 'copy', sbox.repo_url + '/A', 10256 sbox.repo_url + '/A-tag2', '-m', 'tag2') 10257 10258 # Now, merge across our "tags" (copies of /A) into the /A of a 10259 # working copy of another repository. Not only should the merge 10260 # succeed, but the results on disk should match those in our first 10261 # working copy. 10262 10263 ### TODO: Use run_and_verify_merge() ### 10264 svntest.main.run_svn(None, 'merge', sbox.repo_url + '/A-tag1', 10265 sbox.repo_url + '/A-tag2', 10266 os.path.join(wc_dir2, 'A')) 10267 sbox2.simple_commit(message='Merge from foreign repos') 10268 svntest.actions.verify_disk(wc_dir2, expected_disk, True) 10269 10270#---------------------------------------------------------------------- 10271@Issue(1962) 10272def merge_added_subtree(sbox): 10273 "merge added subtree" 10274 10275 # The result of a subtree added by copying 10276 # or merging an added subtree, should be the same on disk 10277 ### with the exception of mergeinfo?! 10278 10279 # test for issue 1962 10280 sbox.build() 10281 wc_dir = sbox.wc_dir 10282 url = sbox.repo_url 10283 10284 # make a branch of A 10285 # svn cp A A_COPY 10286 A_url = url + "/A" 10287 A_COPY_url = url + "/A_COPY" 10288 A_path = sbox.ospath('A') 10289 10290 svntest.actions.run_and_verify_svn(["Committing transaction...\n", 10291 "Committed revision 2.\n"], [], 10292 "cp", "-m", "", A_url, A_COPY_url) 10293 svntest.actions.run_and_verify_svn(["Committing transaction...\n", 10294 "Committed revision 3.\n"], [], 10295 "cp", "-m", "", 10296 A_COPY_url + '/D', 10297 A_COPY_url + '/D2') 10298 expected_output = wc.State(A_path, { 10299 'D2' : Item(status='A '), 10300 'D2/gamma' : Item(status='A '), 10301 'D2/H' : Item(status='A '), 10302 'D2/H/chi' : Item(status='A '), 10303 'D2/H/psi' : Item(status='A '), 10304 'D2/H/omega': Item(status='A '), 10305 'D2/G' : Item(status='A '), 10306 'D2/G/pi' : Item(status='A '), 10307 'D2/G/rho' : Item(status='A '), 10308 'D2/G/tau' : Item(status='A ') 10309 }) 10310 10311 expected_status = svntest.actions.get_virginal_state(wc_dir, 1) 10312 expected_status.add({ 10313 'A/D2' : Item(status='A ', copied='+', wc_rev='-'), 10314 'A/D2/gamma' : Item(status=' ', copied='+', wc_rev='-'), 10315 'A/D2/H' : Item(status=' ', copied='+', wc_rev='-'), 10316 'A/D2/H/chi' : Item(status=' ', copied='+', wc_rev='-'), 10317 'A/D2/H/psi' : Item(status=' ', copied='+', wc_rev='-'), 10318 'A/D2/H/omega': Item(status=' ', copied='+', wc_rev='-'), 10319 'A/D2/G' : Item(status=' ', copied='+', wc_rev='-'), 10320 'A/D2/G/pi' : Item(status=' ', copied='+', wc_rev='-'), 10321 'A/D2/G/rho' : Item(status=' ', copied='+', wc_rev='-'), 10322 'A/D2/G/tau' : Item(status=' ', copied='+', wc_rev='-') 10323 }) 10324 expected_status.remove('', 'iota') 10325 10326 expected_skip = wc.State('', {}) 10327 expected_disk = svntest.main.greek_state.subtree("A") 10328 dest_name = '' 10329 expected_disk.add({ 10330 dest_name + 'D2' : Item(), 10331 dest_name + 'D2/gamma' : Item("This is the file 'gamma'.\n"), 10332 dest_name + 'D2/G' : Item(), 10333 dest_name + 'D2/G/pi' : Item("This is the file 'pi'.\n"), 10334 dest_name + 'D2/G/rho' : Item("This is the file 'rho'.\n"), 10335 dest_name + 'D2/G/tau' : Item("This is the file 'tau'.\n"), 10336 dest_name + 'D2/H' : Item(), 10337 dest_name + 'D2/H/chi' : Item("This is the file 'chi'.\n"), 10338 dest_name + 'D2/H/omega' : Item("This is the file 'omega'.\n"), 10339 dest_name + 'D2/H/psi' : Item("This is the file 'psi'.\n") 10340 }) 10341 10342 # Using the above information, verify a REPO->WC copy 10343 svntest.actions.run_and_verify_svn(None, [], 10344 "cp", A_COPY_url + '/D2', 10345 os.path.join(A_path, "D2")) 10346 svntest.actions.verify_disk(A_path, expected_disk) 10347 svntest.actions.run_and_verify_status(A_path, expected_status) 10348 10349 # Remove the copy artifacts 10350 svntest.actions.run_and_verify_svn(None, [], 10351 "revert", "-R", A_path) 10352 svntest.main.safe_rmtree(os.path.join(A_path, "D2")) 10353 10354 # Add merge-tracking differences between copying and merging 10355 # Verify a merge using the otherwise unchanged disk and status trees 10356 expected_status.tweak('A',status=' M') 10357 expected_mergeinfo_output = wc.State(A_path, { 10358 '' : Item(status=' U'), 10359 }) 10360 expected_elision_output = wc.State(A_path, { 10361 }) 10362 svntest.actions.run_and_verify_merge(A_path, 2, 3, A_COPY_url, None, 10363 expected_output, 10364 expected_mergeinfo_output, 10365 expected_elision_output, 10366 expected_disk, 10367 expected_status, expected_skip) 10368 10369#---------------------------------------------------------------------- 10370# Issue #3138 10371@SkipUnless(server_has_mergeinfo) 10372@Issue(3138) 10373def merge_unknown_url(sbox): 10374 "merging an unknown url should return error" 10375 10376 sbox.build() 10377 wc_dir = sbox.wc_dir 10378 10379 # remove a path from the repo and commit. 10380 iota_path = sbox.ospath('iota') 10381 svntest.actions.run_and_verify_svn(None, [], 'rm', iota_path) 10382 svntest.actions.run_and_verify_svn(None, [], 10383 "ci", wc_dir, "-m", "log message") 10384 10385 10386 url = sbox.repo_url + "/iota" 10387 expected_err = ".*File not found.*iota.*|.*iota.*path not found.*" 10388 svntest.actions.run_and_verify_svn(None, expected_err, 10389 "merge", url, wc_dir) 10390 10391#---------------------------------------------------------------------- 10392@SkipUnless(server_has_mergeinfo) 10393def reverse_merge_away_all_mergeinfo(sbox): 10394 "merges that remove all mergeinfo work" 10395 10396 sbox.build() 10397 wc_dir = sbox.wc_dir 10398 wc_disk, wc_status = set_up_branch(sbox) 10399 10400 # Some paths we'll care about 10401 A_COPY_H_path = sbox.ospath('A_COPY/D/H') 10402 10403 # Merge r4:8 from A/D/H into A_COPY/D/H. 10404 expected_output = wc.State(A_COPY_H_path, { 10405 'omega' : Item(status='U '), 10406 'psi' : Item(status='U ') 10407 }) 10408 expected_mergeinfo_output = wc.State(A_COPY_H_path, { 10409 '' : Item(status=' U'), 10410 }) 10411 expected_elision_output = wc.State(A_COPY_H_path, { 10412 }) 10413 expected_status = wc.State(A_COPY_H_path, { 10414 '' : Item(status=' M', wc_rev=2), 10415 'psi' : Item(status='M ', wc_rev=2), 10416 'omega' : Item(status='M ', wc_rev=2), 10417 'chi' : Item(status=' ', wc_rev=2), 10418 }) 10419 expected_disk = wc.State('', { 10420 '' : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:3-6'}), 10421 'psi' : Item("New content"), 10422 'omega' : Item("New content"), 10423 'chi' : Item("This is the file 'chi'.\n"), 10424 }) 10425 expected_skip = wc.State(A_COPY_H_path, { }) 10426 svntest.actions.run_and_verify_merge(A_COPY_H_path, '2', '6', 10427 sbox.repo_url + '/A/D/H', None, 10428 expected_output, 10429 expected_mergeinfo_output, 10430 expected_elision_output, 10431 expected_disk, 10432 expected_status, expected_skip, 10433 check_props=True) 10434 10435 # Commit the merge as r7 10436 expected_output = wc.State(wc_dir, { 10437 'A_COPY/D/H' : Item(verb='Sending'), 10438 'A_COPY/D/H/omega' : Item(verb='Sending'), 10439 'A_COPY/D/H/psi' : Item(verb='Sending'), 10440 }) 10441 wc_status.tweak('A_COPY/D/H', 'A_COPY/D/H/omega', 'A_COPY/D/H/psi', 10442 wc_rev=7) 10443 svntest.actions.run_and_verify_commit(wc_dir, 10444 expected_output, 10445 wc_status) 10446 10447 # Now reverse merge r7 from itself, all mergeinfo should be removed. 10448 expected_output = wc.State(A_COPY_H_path, { 10449 '' : Item(status=' U'), 10450 'omega' : Item(status='U '), 10451 'psi' : Item(status='U ') 10452 }) 10453 expected_mergeinfo_output = wc.State(A_COPY_H_path, { 10454 '' : Item(status=' G'), 10455 }) 10456 expected_elision_output = wc.State(A_COPY_H_path, { 10457 '' : Item(status=' U'), 10458 }) 10459 expected_status = wc.State(A_COPY_H_path, { 10460 '' : Item(status=' M', wc_rev=7), 10461 'psi' : Item(status='M ', wc_rev=7), 10462 'omega' : Item(status='M ', wc_rev=7), 10463 'chi' : Item(status=' ', wc_rev=2), 10464 }) 10465 expected_disk = wc.State('', { 10466 'psi' : Item("This is the file 'psi'.\n"), 10467 'omega' : Item("This is the file 'omega'.\n"), 10468 'chi' : Item("This is the file 'chi'.\n"), 10469 }) 10470 expected_skip = wc.State(A_COPY_H_path, { }) 10471 svntest.actions.run_and_verify_merge(A_COPY_H_path, '7', '6', 10472 sbox.repo_url + '/A_COPY/D/H', None, 10473 expected_output, 10474 expected_mergeinfo_output, 10475 expected_elision_output, 10476 expected_disk, 10477 expected_status, expected_skip, 10478 [], 10479 True, False, '--allow-mixed-revisions', 10480 A_COPY_H_path) 10481 10482#---------------------------------------------------------------------- 10483# Issue #3138 10484# Another test for issue #3067: 'subtrees with intersecting mergeinfo, 10485# that don't exist at the start of a merge range shouldn't break the 10486# merge'. Specifically see 10487# https://issues.apache.org/jira/browse/SVN-3067#desc5 10488@SkipUnless(server_has_mergeinfo) 10489@Issues(3138,3067,4217) 10490def dont_merge_revs_into_subtree_that_predate_it(sbox): 10491 "dont merge revs into a subtree that predate it" 10492 10493 # +-> merge -c7 A/D/H/nu@7 H_COPY/nu 10494 # | +-> merge -c2 A/D/H H_COPY 10495 # | | +-> merge A/D/H H_COPY 10496 # | | | 10497 # A/D/H A---------------------- 10498 # +-psi +-M-------------M------ 10499 # +-nu A-D C---M-D 10500 # H_COPY C---------G-G 10501 # +-psi +---------+-. 10502 # +-nu +-------G---. 10503 # 1 2 3 4 5 6 7 8 9 w w w 10504 10505 # Create our good 'ole greek tree. 10506 sbox.build() 10507 wc_dir = sbox.wc_dir 10508 10509 # Some paths we'll care about 10510 psi_path = sbox.ospath('A/D/H/psi') 10511 nu_path = sbox.ospath('A/D/H/nu') 10512 H_COPY_path = sbox.ospath('H_COPY') 10513 nu_COPY_path = sbox.ospath('H_COPY/nu') 10514 10515 expected_status = svntest.actions.get_virginal_state(wc_dir, 1) 10516 expected_disk = svntest.main.greek_state.copy() 10517 10518 # Make a text mod to 'A/D/H/psi' and commit it as r2 10519 svntest.main.file_write(psi_path, "New content") 10520 expected_output = wc.State(wc_dir, {'A/D/H/psi' : Item(verb='Sending')}) 10521 expected_status.tweak('A/D/H/psi', wc_rev=2) 10522 svntest.actions.run_and_verify_commit(wc_dir, expected_output, 10523 expected_status) 10524 expected_disk.tweak('A/D/H/psi', contents="New content") 10525 10526 # Create 'A/D/H/nu' and commit it as r3. 10527 svntest.main.file_write(nu_path, "This is the file 'nu'.\n") 10528 svntest.actions.run_and_verify_svn(None, [], 'add', nu_path) 10529 expected_output = wc.State(wc_dir, {'A/D/H/nu' : Item(verb='Adding')}) 10530 expected_status.add({'A/D/H/nu' : Item(status=' ', wc_rev=3)}) 10531 svntest.actions.run_and_verify_commit(wc_dir, expected_output, 10532 expected_status) 10533 10534 # Delete 'A/D/H/nu' and commit it as r4. 10535 svntest.actions.run_and_verify_svn(None, [], 'rm', nu_path) 10536 expected_output = wc.State(wc_dir, {'A/D/H/nu' : Item(verb='Deleting')}) 10537 expected_status.remove('A/D/H/nu') 10538 svntest.actions.run_and_verify_commit(wc_dir, expected_output, 10539 expected_status) 10540 10541 # Copy 'A/D/H/nu' from r3 and commit it as r5. 10542 svntest.actions.run_and_verify_svn(None, [], 'cp', 10543 sbox.repo_url + '/A/D/H/nu@3', nu_path) 10544 expected_output = wc.State(wc_dir, {'A/D/H/nu' : Item(verb='Adding')}) 10545 expected_status.add({'A/D/H/nu' : Item(status=' ', wc_rev=5)}) 10546 svntest.actions.run_and_verify_commit(wc_dir, expected_output, 10547 expected_status) 10548 10549 # Copy 'A/D/H' to 'H_COPY' in r6. 10550 svntest.actions.run_and_verify_svn(['Committing transaction...\n', 10551 'Committed revision 6.\n'], 10552 [], 'copy', 10553 sbox.repo_url + "/A/D/H", 10554 sbox.repo_url + "/H_COPY", 10555 "-m", "Copy A/D/H to H_COPY") 10556 expected_status.add({ 10557 "H_COPY" : Item(), 10558 "H_COPY/chi" : Item(), 10559 "H_COPY/omega" : Item(), 10560 "H_COPY/psi" : Item(), 10561 "H_COPY/nu" : Item()}) 10562 10563 # Update to pull the previous copy into the WC 10564 svntest.main.run_svn(None, 'up', wc_dir) 10565 expected_status.tweak(status=' ', wc_rev=6) 10566 10567 # Make a text mod to 'A/D/H/nu' and commit it as r7. 10568 svntest.main.file_write(nu_path, "New content") 10569 expected_output = wc.State(wc_dir, {'A/D/H/nu' : Item(verb='Sending')}) 10570 expected_status.tweak('A/D/H/nu', wc_rev=7) 10571 svntest.actions.run_and_verify_commit(wc_dir, expected_output, 10572 expected_status) 10573 10574 # Remove A/D/H/nu and commit it as r8. 10575 # We do this deletion so that following cherry harvest has a *tough* 10576 # time to identify the line of history of /A/D/H/nu@HEAD. 10577 svntest.main.run_svn(None, 'rm', nu_path) 10578 expected_output = wc.State(wc_dir, {'A/D/H/nu' : Item(verb='Deleting')}) 10579 expected_status.remove('A/D/H/nu') 10580 svntest.actions.run_and_verify_commit(wc_dir, expected_output, 10581 expected_status) 10582 10583 # Make another text mod to 'A/D/H/psi' that can be merged to 'H_COPY' 10584 # during a cherry harvest and commit it as r9. 10585 svntest.main.file_write(psi_path, "Even *newer* content") 10586 expected_output = wc.State(wc_dir, {'A/D/H/psi' : Item(verb='Sending')}) 10587 expected_status.tweak('A/D/H/psi', wc_rev=9) 10588 svntest.actions.run_and_verify_commit(wc_dir, expected_output, 10589 expected_status) 10590 expected_disk.tweak('A/D/H/psi', contents="Even *newer* content") 10591 10592 # Update WC so elision occurs smoothly. 10593 svntest.main.run_svn(None, 'up', wc_dir) 10594 expected_status.tweak(status=' ', wc_rev=9) 10595 10596 # Merge r7 from 'A/D/H/nu' to 'H_COPY/nu'. 10597 svntest.actions.run_and_verify_svn( 10598 expected_merge_output([[7]], 10599 ['U ' + nu_COPY_path + '\n', 10600 ' U ' + nu_COPY_path + '\n']), 10601 [], 'merge', '-c7', sbox.repo_url + '/A/D/H/nu@7', nu_COPY_path) 10602 10603 # Cherry harvest all eligible revisions from 'A/D/H' to 'H_COPY'. 10604 # 10605 # This is where we see the problem described in 10606 # https://issues.apache.org/jira/browse/SVN-3067#desc5. 10607 # 10608 # Use run_and_verify_svn() because run_and_verify_merge*() require 10609 # explicit revision ranges. 10610 10611 expected_skip = wc.State(H_COPY_path, { }) 10612 #Cherry pick r2 prior to cherry harvest. 10613 svntest.actions.run_and_verify_svn([], [], 'merge', '-c2', 10614 sbox.repo_url + '/A/D/H', 10615 H_COPY_path) 10616 10617 # H_COPY needs r6-9 applied while H_COPY/nu needs only 6,8-9. 10618 svntest.actions.run_and_verify_svn( 10619 expected_merge_output( 10620 [[7,9], # Merge notification 10621 [6,9]], # Mergeinfo notification 10622 ['U ' + os.path.join(H_COPY_path, "psi") + '\n', 10623 'D ' + os.path.join(H_COPY_path, "nu") + '\n', 10624 ' U ' + H_COPY_path + '\n',]), 10625 [], 'merge', sbox.repo_url + '/A/D/H', H_COPY_path, '--force') 10626 10627 # Check the status after the merge. 10628 expected_status.tweak('H_COPY', status=' M') 10629 expected_status.tweak('H_COPY/psi', status='M ') 10630 expected_status.tweak('H_COPY/nu', status='D ') 10631 svntest.actions.run_and_verify_status(wc_dir, expected_status) 10632 check_mergeinfo_recursively(wc_dir, 10633 { H_COPY_path: '/A/D/H:6-9' }) 10634 10635#---------------------------------------------------------------------- 10636# Helper for merge_chokes_on_renamed_subtrees and 10637# subtrees_with_empty_mergeinfo. 10638def set_up_renamed_subtree(sbox): 10639 '''Starting with standard greek tree, make a text mod to A/D/H/psi 10640 as r2. Tweak A/D/H/omega and commit it at r3(We do this to create 10641 broken segment of history of A/D/H. 10642 *DO NOT SVN UPDATE*. 10643 Move A/D/H/psi to A/D/H/psi_moved as r4. Copy A/D/H to H_COPY 10644 as r5. Make a text mod to A/D/H/psi_moved and commit it at r6. 10645 Update the working copy and return the expected disk and status 10646 representing it''' 10647 10648 # Create our good 'ole greek tree. 10649 sbox.build() 10650 wc_dir = sbox.wc_dir 10651 10652 # Some paths we'll care about 10653 psi_path = sbox.ospath('A/D/H/psi') 10654 omega_path = sbox.ospath('A/D/H/omega') 10655 psi_moved_path = sbox.ospath('A/D/H/psi_moved') 10656 psi_COPY_moved_path = sbox.ospath('H_COPY/psi_moved') 10657 H_COPY_path = sbox.ospath('H_COPY') 10658 10659 expected_status = svntest.actions.get_virginal_state(wc_dir, 1) 10660 expected_disk = svntest.main.greek_state.copy() 10661 10662 # Make a text mod to 'A/D/H/psi' and commit it as r2 10663 svntest.main.file_write(psi_path, "New content") 10664 expected_output = wc.State(wc_dir, {'A/D/H/psi' : Item(verb='Sending')}) 10665 expected_status.tweak('A/D/H/psi', wc_rev=2) 10666 svntest.actions.run_and_verify_commit(wc_dir, expected_output, 10667 expected_status) 10668 expected_disk.tweak('A/D/H/psi', contents="New content") 10669 10670 # Make a text mod to 'A/D/H/omega' and commit it as r3 10671 svntest.main.file_write(omega_path, "New omega") 10672 expected_output = wc.State(wc_dir, {'A/D/H/omega' : Item(verb='Sending')}) 10673 expected_status.tweak('A/D/H/omega', wc_rev=3) 10674 svntest.actions.run_and_verify_commit(wc_dir, expected_output, 10675 expected_status) 10676 expected_disk.tweak('A/D/H/omega', contents="New omega") 10677 10678 # Move 'A/D/H/psi' to 'A/D/H/psi_moved' and commit it as r4. 10679 svntest.actions.run_and_verify_svn(None, [], 'move', 10680 psi_path, psi_moved_path) 10681 expected_output = wc.State(wc_dir, { 10682 'A/D/H/psi' : Item(verb='Deleting'), 10683 'A/D/H/psi_moved' : Item(verb='Adding') 10684 }) 10685 expected_status.add({'A/D/H/psi_moved' : Item(status=' ', wc_rev=4)}) 10686 expected_status.remove('A/D/H/psi') 10687 10688 # Replicate old WC-to-WC move behavior where empty mergeinfo was set on 10689 # the move destination. Pre 1.6 repositories might have mergeinfo like 10690 # this so we still want to test that the issue #3067 fixes tested by 10691 # merge_chokes_on_renamed_subtrees and subtrees_with_empty_mergeinfo 10692 # still work. 10693 svntest.actions.run_and_verify_svn(None, [], 'ps', SVN_PROP_MERGEINFO, 10694 "", psi_moved_path) 10695 10696 svntest.actions.run_and_verify_commit(wc_dir, expected_output, 10697 expected_status) 10698 10699 # Copy 'A/D/H' to 'H_COPY' in r5. 10700 svntest.actions.run_and_verify_svn(['Committing transaction...\n', 10701 'Committed revision 5.\n'], 10702 [], 'copy', 10703 sbox.repo_url + "/A/D/H", 10704 sbox.repo_url + "/H_COPY", 10705 "-m", "Copy A/D/H to H_COPY") 10706 expected_status.add({ 10707 "H_COPY" : Item(), 10708 "H_COPY/chi" : Item(), 10709 "H_COPY/omega" : Item(), 10710 "H_COPY/psi_moved" : Item()}) 10711 10712 # Update to pull the previous copy into the WC 10713 svntest.main.run_svn(None, 'up', wc_dir) 10714 expected_status.tweak(status=' ', wc_rev=5) 10715 10716 # Make a text mod to 'A/D/H/psi_moved' and commit it as r6 10717 svntest.main.file_write(psi_moved_path, "Even *Newer* content") 10718 expected_output = wc.State(wc_dir, 10719 {'A/D/H/psi_moved' : Item(verb='Sending')}) 10720 expected_status.tweak('A/D/H/psi_moved', wc_rev=6) 10721 svntest.actions.run_and_verify_commit(wc_dir, expected_output, 10722 expected_status) 10723 expected_disk.remove('A/D/H/psi') 10724 expected_disk.add({ 10725 'A/D/H/psi_moved' : Item("Even *Newer* content"), 10726 }) 10727 10728 # Update for a uniform working copy before merging. 10729 svntest.main.run_svn(None, 'up', wc_dir) 10730 expected_status.tweak(status=' ', wc_rev=6) 10731 10732 return wc_dir, expected_disk, expected_status 10733 10734#---------------------------------------------------------------------- 10735# Test for issue #3174: 'Merge algorithm chokes on subtrees needing 10736# special attention that have been renamed' 10737@SkipUnless(server_has_mergeinfo) 10738@Issue(3174) 10739def merge_chokes_on_renamed_subtrees(sbox): 10740 "merge fails with renamed subtrees with mergeinfo" 10741 10742 # Use helper to setup a renamed subtree. 10743 wc_dir, expected_disk, expected_status = set_up_renamed_subtree(sbox) 10744 10745 # Some paths we'll care about 10746 psi_COPY_moved_path = sbox.ospath('H_COPY/psi_moved') 10747 10748 10749 # Cherry harvest all available revsions from 'A/D/H/psi_moved' to 10750 # 'H_COPY/psi_moved'. 10751 # 10752 # Here is where issue #3174 appears, the merge fails with: 10753 # svn: svn: File not found: revision 3, path '/A/D/H/psi' 10754 svntest.actions.run_and_verify_svn( 10755 expected_merge_output([[5,6],[3,6]], 10756 ['U ' + psi_COPY_moved_path + '\n', 10757 ' U ' + psi_COPY_moved_path + '\n', 10758 ' G ' + psi_COPY_moved_path + '\n',], 10759 elides=True), 10760 [], 'merge', sbox.repo_url + '/A/D/H/psi_moved', 10761 psi_COPY_moved_path) 10762 10763 expected_status.tweak('H_COPY/psi_moved', status='MM') 10764 svntest.actions.run_and_verify_status(wc_dir, expected_status) 10765 10766 10767#---------------------------------------------------------------------- 10768# Issue #3157 10769@SkipUnless(server_has_mergeinfo) 10770@Issue(3157) 10771def dont_explicitly_record_implicit_mergeinfo(sbox): 10772 "don't explicitly record implicit mergeinfo" 10773 10774 sbox.build() 10775 wc_dir = sbox.wc_dir 10776 10777 A_path = sbox.ospath('A') 10778 A_copy_path = sbox.ospath('A_copy') 10779 A_copy2_path = sbox.ospath('A_copy2') 10780 A_copy_mu_path = sbox.ospath('A_copy/mu') 10781 A_copy2_mu_path = sbox.ospath('A_copy2/mu') 10782 nu_path = sbox.ospath('A/D/H/nu') 10783 nu_copy_path = sbox.ospath('A_copy/D/H/nu') 10784 10785 def _commit_and_update(rev, action): 10786 svntest.actions.run_and_verify_svn(None, [], 10787 'ci', '-m', 'r%d - %s' % (rev, action), 10788 sbox.wc_dir) 10789 svntest.main.run_svn(None, 'up', wc_dir) 10790 10791 # r2 - copy A to A_copy 10792 svntest.main.run_svn(None, 'cp', A_path, A_copy_path) 10793 _commit_and_update(2, "Copy A to A_copy.") 10794 10795 # r3 - tweak A_copy/mu 10796 svntest.main.file_append(A_copy_mu_path, "r3\n") 10797 _commit_and_update(3, "Edit A_copy/mu.") 10798 10799 # r4 - copy A_copy to A_copy2 10800 svntest.main.run_svn(None, 'cp', A_copy_path, A_copy2_path) 10801 _commit_and_update(4, "Copy A_copy to A_copy2.") 10802 10803 # r5 - tweak A_copy2/mu 10804 svntest.main.file_append(A_copy2_mu_path, "r5\n") 10805 _commit_and_update(5, "Edit A_copy2/mu.") 10806 10807 # Merge r5 from A_copy2/mu to A_copy/mu. 10808 # 10809 # run_and_verify_merge doesn't support merging to a file WCPATH 10810 # so use run_and_verify_svn. Check the resulting mergeinfo with 10811 # a propget. 10812 ### TODO: We can use run_and_verify_merge() here now. 10813 svntest.actions.run_and_verify_svn( 10814 expected_merge_output([[5]], ['U ' + A_copy_mu_path + '\n', 10815 ' U ' + A_copy_mu_path + '\n']), 10816 [], 'merge', '-c5', sbox.repo_url + '/A_copy2/mu', A_copy_mu_path) 10817 check_mergeinfo_recursively(A_copy_mu_path, 10818 { A_copy_mu_path: '/A_copy2/mu:5' }) 10819 10820 # Now, merge A_copy2 (in full) back to A_copy. This should result in 10821 # mergeinfo of '/A_copy2:4-5' on A_copy and '/A_copy2/mu:4-5' on A_copy/mu 10822 # and the latter should elide to the former. Any revisions < 4 are part of 10823 # A_copy's natural history and should not be explicitly recorded. 10824 expected_output = wc.State(A_copy_path, {}) 10825 expected_mergeinfo_output = wc.State(A_copy_path, { 10826 '' : Item(status=' U'), 10827 }) 10828 expected_elision_output = wc.State(A_copy_path, { 10829 }) 10830 expected_disk = wc.State('', { 10831 '' : Item(props={SVN_PROP_MERGEINFO : '/A_copy2:4-5'}), 10832 'mu' : Item("This is the file 'mu'.\nr3\nr5\n", 10833 props={SVN_PROP_MERGEINFO : '/A_copy2/mu:5'}), 10834 'B' : Item(), 10835 'B/lambda' : Item("This is the file 'lambda'.\n"), 10836 'B/E' : Item(), 10837 'B/E/alpha' : Item("This is the file 'alpha'.\n"), 10838 'B/E/beta' : Item("This is the file 'beta'.\n"), 10839 'B/F' : Item(), 10840 'C' : Item(), 10841 'D' : Item(), 10842 'D/gamma' : Item("This is the file 'gamma'.\n"), 10843 'D/H' : Item(), 10844 'D/H/chi' : Item("This is the file 'chi'.\n"), 10845 'D/H/psi' : Item("This is the file 'psi'.\n"), 10846 'D/H/omega' : Item("This is the file 'omega'.\n"), 10847 'D/G' : Item(), 10848 'D/G/pi' : Item("This is the file 'pi'.\n"), 10849 'D/G/rho' : Item("This is the file 'rho'.\n"), 10850 'D/G/tau' : Item("This is the file 'tau'.\n"), 10851 }) 10852 expected_status = wc.State(A_copy_path, { 10853 '' : Item(status=' M'), 10854 'mu' : Item(status='MM'), 10855 'B' : Item(status=' '), 10856 'B/lambda' : Item(status=' '), 10857 'B/E' : Item(status=' '), 10858 'B/E/alpha' : Item(status=' '), 10859 'B/E/beta' : Item(status=' '), 10860 'B/F' : Item(status=' '), 10861 'C' : Item(status=' '), 10862 'D' : Item(status=' '), 10863 'D/gamma' : Item(status=' '), 10864 'D/H' : Item(status=' '), 10865 'D/H/chi' : Item(status=' '), 10866 'D/H/psi' : Item(status=' '), 10867 'D/H/omega' : Item(status=' '), 10868 'D/G' : Item(status=' '), 10869 'D/G/pi' : Item(status=' '), 10870 'D/G/rho' : Item(status=' '), 10871 'D/G/tau' : Item(status=' '), 10872 }) 10873 expected_status.tweak(wc_rev=5) 10874 expected_skip = wc.State(A_copy_path, { }) 10875 svntest.actions.run_and_verify_merge(A_copy_path, None, None, 10876 sbox.repo_url + '/A_copy2', None, 10877 expected_output, 10878 expected_mergeinfo_output, 10879 expected_elision_output, 10880 expected_disk, 10881 expected_status, expected_skip, 10882 check_props=True) 10883 10884 # Revert the previous merges and try a cherry harvest merge where 10885 # the subtree's natural history is a proper subset of the merge. 10886 svntest.actions.run_and_verify_svn(None, [], 'revert', '-R', wc_dir) 10887 10888 svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir) 10889 wc_status = svntest.actions.get_virginal_state(wc_dir, 5) 10890 wc_status.add({ 10891 'A_copy' : Item(), 10892 'A_copy/B' : Item(), 10893 'A_copy/B/lambda' : Item(), 10894 'A_copy/B/E' : Item(), 10895 'A_copy/B/E/alpha' : Item(), 10896 'A_copy/B/E/beta' : Item(), 10897 'A_copy/B/F' : Item(), 10898 'A_copy/mu' : Item(), 10899 'A_copy/C' : Item(), 10900 'A_copy/D' : Item(), 10901 'A_copy/D/gamma' : Item(), 10902 'A_copy/D/G' : Item(), 10903 'A_copy/D/G/pi' : Item(), 10904 'A_copy/D/G/rho' : Item(), 10905 'A_copy/D/G/tau' : Item(), 10906 'A_copy/D/H' : Item(), 10907 'A_copy/D/H/chi' : Item(), 10908 'A_copy/D/H/omega' : Item(), 10909 'A_copy/D/H/psi' : Item(), 10910 'A_copy2' : Item(), 10911 'A_copy2/B' : Item(), 10912 'A_copy2/B/lambda' : Item(), 10913 'A_copy2/B/E' : Item(), 10914 'A_copy2/B/E/alpha' : Item(), 10915 'A_copy2/B/E/beta' : Item(), 10916 'A_copy2/B/F' : Item(), 10917 'A_copy2/mu' : Item(), 10918 'A_copy2/C' : Item(), 10919 'A_copy2/D' : Item(), 10920 'A_copy2/D/gamma' : Item(), 10921 'A_copy2/D/G' : Item(), 10922 'A_copy2/D/G/pi' : Item(), 10923 'A_copy2/D/G/rho' : Item(), 10924 'A_copy2/D/G/tau' : Item(), 10925 'A_copy2/D/H' : Item(), 10926 'A_copy2/D/H/chi' : Item(), 10927 'A_copy2/D/H/omega' : Item(), 10928 'A_copy2/D/H/psi' : Item(), 10929 }) 10930 wc_status.tweak(status=' ', wc_rev=5) 10931 10932 # r6 - Add the file 'A/D/H/nu'. 10933 svntest.main.file_write(nu_path, "This is the file 'nu'.\n") 10934 svntest.actions.run_and_verify_svn(None, [], 'add', nu_path) 10935 expected_output = wc.State(wc_dir, {'A/D/H/nu' : Item(verb='Adding')}) 10936 wc_status.add({'A/D/H/nu' : Item(status=' ', wc_rev=6)}) 10937 svntest.actions.run_and_verify_commit(wc_dir, expected_output, 10938 wc_status) 10939 10940 # r7 - Make a change to 'A/D/H/nu'. 10941 svntest.main.file_write(nu_path, "Nu content") 10942 expected_output = wc.State(wc_dir, {'A/D/H/nu' : Item(verb='Sending')}) 10943 wc_status.tweak('A/D/H/nu', wc_rev=7) 10944 svntest.actions.run_and_verify_commit(wc_dir, expected_output, 10945 wc_status) 10946 10947 # r8 - Merge r6 to 'A_copy'. 10948 expected_output = wc.State(A_copy_path, { 10949 'D/H/nu' : Item(status='A '), 10950 }) 10951 expected_mergeinfo_output = wc.State(A_copy_path, { 10952 '' : Item(status=' U'), 10953 }) 10954 expected_elision_output = wc.State(A_copy_path, { 10955 }) 10956 expected_A_copy_status = wc.State(A_copy_path, { 10957 '' : Item(status=' M', wc_rev=5), 10958 'B' : Item(status=' ', wc_rev=5), 10959 'mu' : Item(status=' ', wc_rev=5), 10960 'B/E' : Item(status=' ', wc_rev=5), 10961 'B/E/alpha' : Item(status=' ', wc_rev=5), 10962 'B/E/beta' : Item(status=' ', wc_rev=5), 10963 'B/lambda' : Item(status=' ', wc_rev=5), 10964 'B/F' : Item(status=' ', wc_rev=5), 10965 'C' : Item(status=' ', wc_rev=5), 10966 'D' : Item(status=' ', wc_rev=5), 10967 'D/G' : Item(status=' ', wc_rev=5), 10968 'D/G/pi' : Item(status=' ', wc_rev=5), 10969 'D/G/rho' : Item(status=' ', wc_rev=5), 10970 'D/G/tau' : Item(status=' ', wc_rev=5), 10971 'D/gamma' : Item(status=' ', wc_rev=5), 10972 'D/H' : Item(status=' ', wc_rev=5), 10973 'D/H/chi' : Item(status=' ', wc_rev=5), 10974 'D/H/psi' : Item(status=' ', wc_rev=5), 10975 'D/H/omega' : Item(status=' ', wc_rev=5), 10976 'D/H/nu' : Item(status='A ', wc_rev='-', copied='+'), 10977 }) 10978 expected_A_copy_disk = wc.State('', { 10979 '' : Item(props={SVN_PROP_MERGEINFO : '/A:6'}), 10980 'B' : Item(), 10981 'mu' : Item("This is the file 'mu'.\nr3\n"), 10982 'B/E' : Item(), 10983 'B/E/alpha' : Item("This is the file 'alpha'.\n"), 10984 'B/E/beta' : Item("This is the file 'beta'.\n"), 10985 'B/lambda' : Item("This is the file 'lambda'.\n"), 10986 'B/F' : Item(), 10987 'C' : Item(), 10988 'D' : Item(), 10989 'D/G' : Item(), 10990 'D/G/pi' : Item("This is the file 'pi'.\n"), 10991 'D/G/rho' : Item("This is the file 'rho'.\n"), 10992 'D/G/tau' : Item("This is the file 'tau'.\n"), 10993 'D/gamma' : Item("This is the file 'gamma'.\n"), 10994 'D/H' : Item(), 10995 'D/H/chi' : Item("This is the file 'chi'.\n"), 10996 'D/H/psi' : Item("This is the file 'psi'.\n"), 10997 'D/H/omega' : Item("This is the file 'omega'.\n"), 10998 'D/H/nu' : Item("This is the file 'nu'.\n"), 10999 }) 11000 expected_A_copy_skip = wc.State(A_copy_path, {}) 11001 svntest.actions.run_and_verify_merge(A_copy_path, '5', '6', 11002 sbox.repo_url + '/A', None, 11003 expected_output, 11004 expected_mergeinfo_output, 11005 expected_elision_output, 11006 expected_A_copy_disk, 11007 expected_A_copy_status, 11008 expected_A_copy_skip, 11009 check_props=True) 11010 wc_status.add({'A_copy/D/H/nu' : Item(status=' ', wc_rev=8)}) 11011 wc_status.tweak('A_copy', wc_rev=8) 11012 expected_output = wc.State(wc_dir, { 11013 'A_copy/D/H/nu' : Item(verb='Adding'), 11014 'A_copy' : Item(verb='Sending'), 11015 }) 11016 svntest.actions.run_and_verify_commit(wc_dir, expected_output, 11017 wc_status) 11018 11019 # r9 - Merge r7 to 'A_copy/D/H/nu'. 11020 expected_skip = wc.State(nu_copy_path, { }) 11021 # run_and_verify_merge doesn't support merging to a file WCPATH 11022 # so use run_and_verify_svn. 11023 ### TODO: We can use run_and_verify_merge() here now. 11024 svntest.actions.run_and_verify_svn( 11025 expected_merge_output([[7]], 11026 ['U ' + nu_copy_path + '\n', 11027 ' G ' + nu_copy_path + '\n',]), 11028 [], 'merge', '-c7', sbox.repo_url + '/A/D/H/nu', nu_copy_path) 11029 expected_output = wc.State(wc_dir, {'A_copy/D/H/nu' : Item(verb='Sending')}) 11030 wc_status.tweak('A_copy/D/H/nu', wc_rev=9) 11031 svntest.actions.run_and_verify_commit(wc_dir, expected_output, 11032 wc_status) 11033 11034 # Update WC 11035 svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir) 11036 wc_status.tweak(wc_rev=9) 11037 11038 # r10 - Make another change to 'A/D/H/nu'. 11039 svntest.main.file_write(nu_path, "Even nuer content") 11040 expected_output = wc.State(wc_dir, {'A/D/H/nu' : Item(verb='Sending')}) 11041 wc_status.tweak('A/D/H/nu', wc_rev=10) 11042 svntest.actions.run_and_verify_commit(wc_dir, expected_output, 11043 wc_status) 11044 11045 # Update WC 11046 svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir) 11047 wc_status.tweak(wc_rev=10) 11048 11049 # Now do a cherry harvest merge to 'A_copy'. 11050 expected_output = wc.State(A_copy_path, { 11051 'D/H/nu' : Item(status='U '), 11052 }) 11053 expected_mergeinfo_output = wc.State(A_copy_path, { 11054 '' : Item(status=' U'), 11055 'D/H/nu' : Item(status=' U'), 11056 }) 11057 expected_elision_output = wc.State(A_copy_path, { 11058 }) 11059 expected_A_copy_status = wc.State(A_copy_path, { 11060 '' : Item(status=' M', wc_rev=10), 11061 'B' : Item(status=' ', wc_rev=10), 11062 'mu' : Item(status=' ', wc_rev=10), 11063 'B/E' : Item(status=' ', wc_rev=10), 11064 'B/E/alpha' : Item(status=' ', wc_rev=10), 11065 'B/E/beta' : Item(status=' ', wc_rev=10), 11066 'B/lambda' : Item(status=' ', wc_rev=10), 11067 'B/F' : Item(status=' ', wc_rev=10), 11068 'C' : Item(status=' ', wc_rev=10), 11069 'D' : Item(status=' ', wc_rev=10), 11070 'D/G' : Item(status=' ', wc_rev=10), 11071 'D/G/pi' : Item(status=' ', wc_rev=10), 11072 'D/G/rho' : Item(status=' ', wc_rev=10), 11073 'D/G/tau' : Item(status=' ', wc_rev=10), 11074 'D/gamma' : Item(status=' ', wc_rev=10), 11075 'D/H' : Item(status=' ', wc_rev=10), 11076 'D/H/chi' : Item(status=' ', wc_rev=10), 11077 'D/H/psi' : Item(status=' ', wc_rev=10), 11078 'D/H/omega' : Item(status=' ', wc_rev=10), 11079 'D/H/nu' : Item(status='MM', wc_rev=10), 11080 }) 11081 expected_A_copy_disk = wc.State('', { 11082 '' : Item(props={SVN_PROP_MERGEINFO : '/A:2-10'}), 11083 'B' : Item(), 11084 'mu' : Item("This is the file 'mu'.\nr3\n"), 11085 'B/E' : Item(), 11086 'B/E/alpha' : Item("This is the file 'alpha'.\n"), 11087 'B/E/beta' : Item("This is the file 'beta'.\n"), 11088 'B/lambda' : Item("This is the file 'lambda'.\n"), 11089 'B/F' : Item(), 11090 'C' : Item(), 11091 'D' : Item(), 11092 'D/G' : Item(), 11093 'D/G/pi' : Item("This is the file 'pi'.\n"), 11094 'D/G/rho' : Item("This is the file 'rho'.\n"), 11095 'D/G/tau' : Item("This is the file 'tau'.\n"), 11096 'D/gamma' : Item("This is the file 'gamma'.\n"), 11097 'D/H' : Item(), 11098 'D/H/chi' : Item("This is the file 'chi'.\n"), 11099 'D/H/psi' : Item("This is the file 'psi'.\n"), 11100 'D/H/omega' : Item("This is the file 'omega'.\n"), 11101 'D/H/nu' : Item("Even nuer content", 11102 props={SVN_PROP_MERGEINFO : '/A/D/H/nu:6-10'}), 11103 }) 11104 expected_A_copy_skip = wc.State(A_copy_path, {}) 11105 svntest.actions.run_and_verify_merge(A_copy_path, None, None, 11106 sbox.repo_url + '/A', None, 11107 expected_output, 11108 expected_mergeinfo_output, 11109 expected_elision_output, 11110 expected_A_copy_disk, 11111 expected_A_copy_status, 11112 expected_A_copy_skip, 11113 check_props=True) 11114 11115#---------------------------------------------------------------------- 11116# Test for issue where merging a change to a broken link fails 11117@SkipUnless(svntest.main.is_posix_os) 11118def merge_broken_link(sbox): 11119 "merge with broken symlinks in target" 11120 11121 # Create our good 'ole greek tree. 11122 sbox.build() 11123 wc_dir = sbox.wc_dir 11124 src_path = sbox.ospath('A/B/E') 11125 copy_path = sbox.ospath('A/B/E_COPY') 11126 link_path = os.path.join(src_path, 'beta_link') 11127 11128 os.symlink('beta_broken', link_path) 11129 svntest.main.run_svn(None, 'add', link_path) 11130 svntest.main.run_svn(None, 'commit', '-m', 'Create a broken link', link_path) 11131 svntest.main.run_svn(None, 'copy', src_path, copy_path) 11132 svntest.main.run_svn(None, 'commit', '-m', 'Copy the tree with the broken link', 11133 copy_path) 11134 os.unlink(link_path) 11135 os.symlink('beta', link_path) 11136 svntest.main.run_svn(None, 'commit', '-m', 'Fix a broken link', link_path) 11137 svntest.actions.run_and_verify_svn( 11138 expected_merge_output([[4]], 11139 ['U ' + copy_path + '/beta_link\n', 11140 ' U ' + copy_path + '\n']), 11141 [], 'merge', '-c4', src_path, copy_path) 11142 11143#---------------------------------------------------------------------- 11144# Test for issue #3199 'Subtree merges broken when required ranges 11145# don't intersect with merge target' 11146@SkipUnless(server_has_mergeinfo) 11147@Issue(3199) 11148def subtree_merges_dont_intersect_with_targets(sbox): 11149 "subtree ranges might not intersect with target" 11150 11151 sbox.build() 11152 wc_dir = sbox.wc_dir 11153 11154 # Make two branches to merge to. 11155 wc_disk, wc_status = set_up_branch(sbox, False, 2) 11156 11157 # Some paths we'll care about. 11158 A_COPY_path = sbox.ospath('A_COPY') 11159 A_COPY_2_path = sbox.ospath('A_COPY_2') 11160 H_COPY_2_path = sbox.ospath('A_COPY_2/D/H') 11161 gamma_path = sbox.ospath('A/D/gamma') 11162 psi_path = sbox.ospath('A/D/H/psi') 11163 psi_COPY_path = sbox.ospath('A_COPY/D/H/psi') 11164 gamma_COPY_path = sbox.ospath('A_COPY/D/gamma') 11165 psi_COPY_path = sbox.ospath('A_COPY/D/H/psi') 11166 psi_COPY_2_path = sbox.ospath('A_COPY_2/D/H/psi') 11167 rho_COPY_2_path = sbox.ospath('A_COPY_2/D/G/rho') 11168 11169 # Make a tweak to A/D/gamma and A/D/H/psi in r8. 11170 svntest.main.file_write(gamma_path, "New content") 11171 svntest.main.file_write(psi_path, "Even newer content") 11172 expected_output = wc.State(wc_dir, { 11173 'A/D/gamma' : Item(verb='Sending'), 11174 'A/D/H/psi' : Item(verb='Sending'), 11175 }) 11176 wc_status.tweak('A/D/gamma', 'A/D/H/psi', wc_rev=8) 11177 svntest.actions.run_and_verify_commit(wc_dir, expected_output, 11178 wc_status) 11179 wc_disk.tweak('A/D/gamma', contents="New content") 11180 wc_disk.tweak('A/D/H/psi', contents="Even newer content") 11181 11182 # Update the WC. 11183 svntest.actions.run_and_verify_svn(exp_noop_up_out(8), [], 11184 'update', wc_dir) 11185 wc_status.tweak(wc_rev=8) 11186 11187 # Run a bunch of merges to setup the 2 branches with explicit 11188 # mergeinfo on each branch root and explicit mergeinfo on one subtree 11189 # of each root. The mergeinfo should be such that: 11190 # 11191 # 1) On one branch: The mergeinfo on the root and the subtree do 11192 # not intersect. 11193 # 11194 # 2) On the other branch: The mergeinfo on the root and subtree 11195 # are each 'missing' and eligible ranges and these missing 11196 # ranges do not intersect. 11197 # 11198 # Note: We just use run_and_verify_svn(...'merge'...) here rather than 11199 # run_and_verify_merge() because these types of simple merges are 11200 # tested to death elsewhere and this is just setup for the "real" 11201 # test. 11202 svntest.actions.run_and_verify_svn(None, [], 11203 'merge', '-c4', 11204 sbox.repo_url + '/A/D/H/psi', 11205 psi_COPY_path) 11206 svntest.actions.run_and_verify_svn(None, [], 11207 'merge', '-c8', 11208 sbox.repo_url + '/A', 11209 A_COPY_path) 11210 svntest.actions.run_and_verify_svn(None, [], 11211 'merge', '-c-8', 11212 sbox.repo_url + '/A/D/H/psi', 11213 psi_COPY_path) 11214 svntest.actions.run_and_verify_svn(None, [], 11215 'merge', 11216 sbox.repo_url + '/A', 11217 A_COPY_2_path) 11218 svntest.actions.run_and_verify_svn(None, [], 11219 'merge', '-c-5', 11220 sbox.repo_url + '/A', 11221 A_COPY_2_path) 11222 svntest.actions.run_and_verify_svn(None, [], 11223 'merge', '-c5', '-c-8', 11224 sbox.repo_url + '/A/D/H', 11225 H_COPY_2_path) 11226 11227 # Commit all the previous merges as r9. 11228 expected_output = wc.State(wc_dir, { 11229 'A_COPY' : Item(verb='Sending'), 11230 'A_COPY/D/H/psi' : Item(verb='Sending'), 11231 'A_COPY/D/gamma' : Item(verb='Sending'), 11232 'A_COPY_2' : Item(verb='Sending'), 11233 'A_COPY_2/B/E/beta' : Item(verb='Sending'), 11234 'A_COPY_2/D/H' : Item(verb='Sending'), 11235 'A_COPY_2/D/H/omega' : Item(verb='Sending'), 11236 'A_COPY_2/D/H/psi' : Item(verb='Sending'), 11237 'A_COPY_2/D/gamma' : Item(verb='Sending'), 11238 }) 11239 wc_status.tweak('A_COPY', 11240 'A_COPY/D/H/psi', 11241 'A_COPY/D/gamma', 11242 'A_COPY_2', 11243 'A_COPY_2/B/E/beta', 11244 'A_COPY_2/D/H', 11245 'A_COPY_2/D/H/omega', 11246 'A_COPY_2/D/H/psi', 11247 'A_COPY_2/D/gamma', 11248 wc_rev=9) 11249 svntest.actions.run_and_verify_commit(wc_dir, 11250 expected_output, 11251 wc_status) 11252 11253 # Update the WC. 11254 svntest.actions.run_and_verify_svn(exp_noop_up_out(9), [], 11255 'update', wc_dir) 11256 11257 # Make sure we have mergeinfo that meets the two criteria set out above. 11258 check_mergeinfo_recursively(wc_dir, 11259 { # Criterion 1 11260 A_COPY_path: '/A:8', 11261 psi_COPY_path: '/A/D/H/psi:4', 11262 # Criterion 2 11263 A_COPY_2_path : '/A:3-4,6-8', 11264 H_COPY_2_path : '/A/D/H:3-7' }) 11265 11266 # Merging to the criterion 2 branch. 11267 # 11268 # Forward merge a range to a target with a subtree where the target 11269 # and subtree need different, non-intersecting revision ranges applied: 11270 # Merge r3:9 from A into A_COPY_2. 11271 # 11272 # The subtree A_COPY_2/D/H needs r8-9 applied (affecting A_COPY_2/D/H/psi) 11273 # while the target needs r5 (affecting A_COPY_2/D/G/rho) applied. The 11274 # resulting mergeinfo on A_COPY_2 and A_COPY_2/D/H should be equivalent 11275 # and therefore elide to A_COPY_2. 11276 expected_output = wc.State(A_COPY_2_path, { 11277 'D/G/rho' : Item(status='U '), 11278 'D/H/psi' : Item(status='U '), 11279 }) 11280 expected_mergeinfo_output = wc.State(A_COPY_2_path, { 11281 '' : Item(status=' U'), 11282 'D/H' : Item(status=' U'), 11283 }) 11284 expected_elision_output = wc.State(A_COPY_2_path, { 11285 'D/H' : Item(status=' U'), 11286 }) 11287 expected_status = wc.State(A_COPY_2_path, { 11288 '' : Item(status=' M', wc_rev=9), 11289 'B' : Item(status=' ', wc_rev=9), 11290 'mu' : Item(status=' ', wc_rev=9), 11291 'B/E' : Item(status=' ', wc_rev=9), 11292 'B/E/alpha' : Item(status=' ', wc_rev=9), 11293 'B/E/beta' : Item(status=' ', wc_rev=9), 11294 'B/lambda' : Item(status=' ', wc_rev=9), 11295 'B/F' : Item(status=' ', wc_rev=9), 11296 'C' : Item(status=' ', wc_rev=9), 11297 'D' : Item(status=' ', wc_rev=9), 11298 'D/G' : Item(status=' ', wc_rev=9), 11299 'D/G/pi' : Item(status=' ', wc_rev=9), 11300 'D/G/rho' : Item(status='M ', wc_rev=9), 11301 'D/G/tau' : Item(status=' ', wc_rev=9), 11302 'D/gamma' : Item(status=' ', wc_rev=9), 11303 'D/H' : Item(status=' M', wc_rev=9), 11304 'D/H/chi' : Item(status=' ', wc_rev=9), 11305 'D/H/psi' : Item(status='M ', wc_rev=9), 11306 'D/H/omega' : Item(status=' ', wc_rev=9), 11307 }) 11308 expected_disk = wc.State('', { 11309 '' : Item(props={SVN_PROP_MERGEINFO : '/A:3-9'}), 11310 'B' : Item(), 11311 'mu' : Item("This is the file 'mu'.\n"), 11312 'B/E' : Item(), 11313 'B/E/alpha' : Item("This is the file 'alpha'.\n"), 11314 'B/E/beta' : Item("New content"), 11315 'B/lambda' : Item("This is the file 'lambda'.\n"), 11316 'B/F' : Item(), 11317 'C' : Item(), 11318 'D' : Item(), 11319 'D/G' : Item(), 11320 'D/G/pi' : Item("This is the file 'pi'.\n"), 11321 'D/G/rho' : Item("New content"), 11322 'D/G/tau' : Item("This is the file 'tau'.\n"), 11323 'D/gamma' : Item("New content"), 11324 'D/H' : Item(), 11325 'D/H/chi' : Item("This is the file 'chi'.\n"), 11326 'D/H/psi' : Item("Even newer content"), 11327 'D/H/omega' : Item("New content"), 11328 }) 11329 expected_skip = wc.State(A_COPY_2_path, {}) 11330 svntest.actions.run_and_verify_merge(A_COPY_2_path, '3', '9', 11331 sbox.repo_url + '/A', None, 11332 expected_output, 11333 expected_mergeinfo_output, 11334 expected_elision_output, 11335 expected_disk, 11336 expected_status, 11337 expected_skip, 11338 check_props=True) 11339 11340 # Merging to the criterion 1 branch. 11341 # 11342 # Reverse merge a range to a target with a subtree where the target 11343 # and subtree need different, non-intersecting revision ranges 11344 # reversed: Merge r9:3 from A into A_COPY. 11345 # 11346 # The subtree A_COPY_2/D/H/psi needs r4 reversed, while the target needs 11347 # r8 (affecting A_COPY/D/gamma) reversed. Since this reverses all merges 11348 # thus far to A_COPY, there should be *no* mergeinfo post merge. 11349 expected_output = wc.State(A_COPY_path, { 11350 'D/gamma' : Item(status='U '), 11351 'D/H/psi' : Item(status='U '), 11352 }) 11353 expected_mergeinfo_output = wc.State(A_COPY_path, { 11354 '' : Item(status=' U'), 11355 'D/H/psi' : Item(status=' U'), 11356 }) 11357 expected_elision_output = wc.State(A_COPY_path, { 11358 '' : Item(status=' U'), 11359 'D/H/psi' : Item(status=' U'), 11360 }) 11361 expected_status = wc.State(A_COPY_path, { 11362 '' : Item(status=' M', wc_rev=9), 11363 'B' : Item(status=' ', wc_rev=9), 11364 'mu' : Item(status=' ', wc_rev=9), 11365 'B/E' : Item(status=' ', wc_rev=9), 11366 'B/E/alpha' : Item(status=' ', wc_rev=9), 11367 'B/E/beta' : Item(status=' ', wc_rev=9), 11368 'B/lambda' : Item(status=' ', wc_rev=9), 11369 'B/F' : Item(status=' ', wc_rev=9), 11370 'C' : Item(status=' ', wc_rev=9), 11371 'D' : Item(status=' ', wc_rev=9), 11372 'D/G' : Item(status=' ', wc_rev=9), 11373 'D/G/pi' : Item(status=' ', wc_rev=9), 11374 'D/G/rho' : Item(status=' ', wc_rev=9), 11375 'D/G/tau' : Item(status=' ', wc_rev=9), 11376 'D/gamma' : Item(status='M ', wc_rev=9), 11377 'D/H' : Item(status=' ', wc_rev=9), 11378 'D/H/chi' : Item(status=' ', wc_rev=9), 11379 'D/H/psi' : Item(status='MM', wc_rev=9), 11380 'D/H/omega' : Item(status=' ', wc_rev=9), 11381 }) 11382 expected_disk = wc.State('', { 11383 'B' : Item(), 11384 'mu' : Item("This is the file 'mu'.\n"), 11385 'B/E' : Item(), 11386 'B/E/alpha' : Item("This is the file 'alpha'.\n"), 11387 'B/E/beta' : Item("This is the file 'beta'.\n"), 11388 'B/lambda' : Item("This is the file 'lambda'.\n"), 11389 'B/F' : Item(), 11390 'C' : Item(), 11391 'D' : Item(), 11392 'D/G' : Item(), 11393 'D/G/pi' : Item("This is the file 'pi'.\n"), 11394 'D/G/rho' : Item("This is the file 'rho'.\n"), 11395 'D/G/tau' : Item("This is the file 'tau'.\n"), 11396 'D/gamma' : Item("This is the file 'gamma'.\n"), 11397 'D/H' : Item(), 11398 'D/H/chi' : Item("This is the file 'chi'.\n"), 11399 'D/H/psi' : Item("This is the file 'psi'.\n"), 11400 'D/H/omega' : Item("This is the file 'omega'.\n"), 11401 }) 11402 expected_skip = wc.State(A_COPY_path, {}) 11403 svntest.actions.run_and_verify_merge(A_COPY_path, '9', '3', 11404 sbox.repo_url + '/A', None, 11405 expected_output, 11406 expected_mergeinfo_output, 11407 expected_elision_output, 11408 expected_disk, 11409 expected_status, 11410 expected_skip, 11411 check_props=True) 11412 11413 # Test the notification portion of issue #3199. 11414 # 11415 # run_and_verify_merge() doesn't check the notification headers 11416 # so we need to repeat the previous two merges using 11417 # run_and_verify_svn(...'merge'...) and expected_merge_output(). 11418 # 11419 ### TODO: Things are fairly ugly when it comes to testing the 11420 ### merge notification headers. run_and_verify_merge*() 11421 ### just ignores the notifications and in the few places 11422 ### we use expected_merge_output() the order of notifications 11423 ### and paths are not considered. In a perfect world we'd 11424 ### have run_and_verify_merge() that addressed these 11425 ### shortcomings (and allowed merges to file targets). 11426 # 11427 # Revert the previous merges. 11428 svntest.actions.run_and_verify_svn(None, [], 'revert', '-R', wc_dir) 11429 11430 # Repeat the forward merge 11431 expected_output = expected_merge_output( 11432 [[5],[8],[5,9]], 11433 ['U %s\n' % (rho_COPY_2_path), 11434 'U %s\n' % (psi_COPY_2_path), 11435 ' U %s\n' % (H_COPY_2_path), 11436 ' U %s\n' % (A_COPY_2_path),], 11437 elides=True) 11438 svntest.actions.run_and_verify_svn(expected_output, 11439 [], 'merge', '-r', '3:9', 11440 sbox.repo_url + '/A', 11441 A_COPY_2_path) 11442 # Repeat the reverse merge 11443 expected_output = expected_merge_output( 11444 [[-4],[-8],[8,4]], 11445 ['U %s\n' % (gamma_COPY_path), 11446 'U %s\n' % (psi_COPY_path), 11447 ' U %s\n' % (A_COPY_path), 11448 ' U %s\n' % (psi_COPY_path)], 11449 elides=True) 11450 svntest.actions.run_and_verify_svn(expected_output, 11451 [], 'merge', '-r', '9:3', 11452 sbox.repo_url + '/A', 11453 A_COPY_path) 11454 11455#---------------------------------------------------------------------- 11456# Some more tests for issue #3067 'subtrees that don't exist at the start 11457# or end of a merge range shouldn't break the merge' 11458@Issue(3067) 11459@SkipUnless(server_has_mergeinfo) 11460def subtree_source_missing_in_requested_range(sbox): 11461 "subtree merge source might not exist" 11462 11463 sbox.build() 11464 wc_dir = sbox.wc_dir 11465 11466 # Make a branch to merge to. 11467 wc_disk, wc_status = set_up_branch(sbox, False, 1) 11468 11469 # Some paths we'll care about. 11470 psi_path = sbox.ospath('A/D/H/psi') 11471 omega_path = sbox.ospath('A/D/H/omega') 11472 A_COPY_path = sbox.ospath('A_COPY') 11473 psi_COPY_path = sbox.ospath('A_COPY/D/H/psi') 11474 omega_COPY_path = sbox.ospath('A_COPY/D/H/omega') 11475 11476 # r7 Delete A/D/H/psi. 11477 svntest.actions.run_and_verify_svn(None, [], 11478 'delete', psi_path) 11479 sbox.simple_commit(message='delete psi') 11480 11481 # r8 - modify A/D/H/omega. 11482 svntest.main.file_write(os.path.join(omega_path), "Even newer content") 11483 sbox.simple_commit(message='modify omega') 11484 11485 # r9 - Merge r3 to A_COPY/D/H/psi 11486 expected_output = expected_merge_output( 11487 [[3]], ['U %s\n' % (psi_COPY_path), 11488 ' U %s\n' % (psi_COPY_path),]) 11489 svntest.actions.run_and_verify_svn(expected_output, [], 11490 'merge', '-c', '3', 11491 sbox.repo_url + '/A/D/H/psi@3', 11492 psi_COPY_path) 11493 sbox.simple_commit(message='merge r3 to A_COPY/D/H/psi') 11494 11495 # r10 - Merge r6 to A_COPY/D/H/omega. 11496 expected_output = expected_merge_output( 11497 [[6]], ['U %s\n' % (omega_COPY_path), 11498 ' U %s\n' % (omega_COPY_path),]) 11499 svntest.actions.run_and_verify_svn(expected_output, [], 11500 'merge', '-c', '6', 11501 sbox.repo_url + '/A/D/H/omega', 11502 omega_COPY_path) 11503 sbox.simple_commit(message='merge r6 to A_COPY') 11504 svntest.actions.run_and_verify_svn(exp_noop_up_out(10), [], 'up', 11505 wc_dir) 11506 11507 # r11 - Merge r8 to A_COPY. 11508 expected_output = expected_merge_output( 11509 [[8]], ['U %s\n' % (omega_COPY_path), 11510 ' U %s\n' % (omega_COPY_path), 11511 ' U %s\n' % (A_COPY_path)]) 11512 svntest.actions.run_and_verify_svn(expected_output, [], 11513 'merge', '-c', '8', 11514 sbox.repo_url + '/A', 11515 A_COPY_path) 11516 # Repeat the merge using the --record-only option so A_COPY/D/H/psi gets 11517 # mergeinfo including 'A/D/H/psi:8', which doesn't exist. Why? Because 11518 # we are trying to create mergeinfo that will provoke an invalid editor 11519 # drive. In 1.5-1.6 merge updated all subtrees, regardless of whether the 11520 # merge touched these subtrees. This --record-only merge duplicates that 11521 # behavior, allowing us to test the relevant issue #3067 fixes. 11522 expected_output = expected_merge_output( 11523 [[8]], [' G %s\n' % (omega_COPY_path), 11524 ' U %s\n' % (psi_COPY_path), 11525 ' G %s\n' % (A_COPY_path)]) 11526 svntest.actions.run_and_verify_svn(expected_output, [], 11527 'merge', '-c', '8', 11528 sbox.repo_url + '/A', 11529 A_COPY_path, '--record-only') 11530 sbox.simple_commit(message='merge r8 to A_COPY/D/H/omega') 11531 svntest.actions.run_and_verify_svn(exp_noop_up_out(11), [], 'up', 11532 wc_dir) 11533 11534 # r12 - modify A/D/H/omega yet again. 11535 svntest.main.file_write(os.path.join(omega_path), 11536 "Now with fabulous new content!") 11537 sbox.simple_commit(message='modify omega') 11538 11539 # r13 - Merge all available revs to A_COPY/D/H/omega. 11540 expected_output = expected_merge_output( 11541 [[9,12],[2,12]], ['U %s\n' % (omega_COPY_path), 11542 ' U %s\n' % (omega_COPY_path)]) 11543 svntest.actions.run_and_verify_svn(expected_output, [], 11544 'merge', 11545 sbox.repo_url + '/A/D/H/omega', 11546 omega_COPY_path) 11547 sbox.simple_commit(message='cherry harvest to A_COPY/D/H/omega') 11548 svntest.actions.run_and_verify_svn(exp_noop_up_out(13), [], 'up', 11549 wc_dir) 11550 11551 # Check that svn:mergeinfo is as expected. 11552 check_mergeinfo_recursively(wc_dir, 11553 { A_COPY_path: '/A:8', 11554 omega_COPY_path: '/A/D/H/omega:2-12', 11555 psi_COPY_path : '/A/D/H/psi:3,8' }) 11556 11557 # Now test a reverse merge where part of the requested range postdates 11558 # a subtree's existence. Merge -r12:1 to A_COPY. This should revert 11559 # all of the merges done thus far. The fact that A/D/H/psi no longer 11560 # exists after r7 shouldn't break the subtree merge into A_COPY/D/H/psi. 11561 # A_COPY/D/H/psi should simply have r3 reverse merged. No paths under 11562 # in the tree rooted at A_COPY should have any explicit mergeinfo. 11563 expected_output = wc.State(A_COPY_path, { 11564 'D/H/omega' : Item(status='U '), 11565 'D/H/psi' : Item(status='U '), 11566 'D/H/omega' : Item(status='G ', prev_status='G '), 11567 }) 11568 expected_mergeinfo_output = wc.State(A_COPY_path, { 11569 '' : Item(status=' U'), 11570 'D/H/psi' : Item(status=' U'), 11571 'D/H/omega' : Item(status=' U'), 11572 }) 11573 expected_elision_output = wc.State(A_COPY_path, { 11574 '' : Item(status=' U'), 11575 'D/H/psi' : Item(status=' U'), 11576 'D/H/omega' : Item(status=' U'), 11577 }) 11578 expected_status = wc.State(A_COPY_path, { 11579 '' : Item(status=' M', wc_rev=13), 11580 'B' : Item(status=' ', wc_rev=13), 11581 'mu' : Item(status=' ', wc_rev=13), 11582 'B/E' : Item(status=' ', wc_rev=13), 11583 'B/E/alpha' : Item(status=' ', wc_rev=13), 11584 'B/E/beta' : Item(status=' ', wc_rev=13), 11585 'B/lambda' : Item(status=' ', wc_rev=13), 11586 'B/F' : Item(status=' ', wc_rev=13), 11587 'C' : Item(status=' ', wc_rev=13), 11588 'D' : Item(status=' ', wc_rev=13), 11589 'D/G' : Item(status=' ', wc_rev=13), 11590 'D/G/pi' : Item(status=' ', wc_rev=13), 11591 'D/G/rho' : Item(status=' ', wc_rev=13), 11592 'D/G/tau' : Item(status=' ', wc_rev=13), 11593 'D/gamma' : Item(status=' ', wc_rev=13), 11594 'D/H' : Item(status=' ', wc_rev=13), 11595 'D/H/chi' : Item(status=' ', wc_rev=13), 11596 'D/H/psi' : Item(status='MM', wc_rev=13), 11597 'D/H/omega' : Item(status='MM', wc_rev=13), 11598 }) 11599 expected_disk = wc.State('', { 11600 'B' : Item(), 11601 'mu' : Item("This is the file 'mu'.\n"), 11602 'B/E' : Item(), 11603 'B/E/alpha' : Item("This is the file 'alpha'.\n"), 11604 'B/E/beta' : Item("This is the file 'beta'.\n"), 11605 'B/lambda' : Item("This is the file 'lambda'.\n"), 11606 'B/F' : Item(), 11607 'C' : Item(), 11608 'D' : Item(), 11609 'D/G' : Item(), 11610 'D/G/pi' : Item("This is the file 'pi'.\n"), 11611 'D/G/rho' : Item("This is the file 'rho'.\n"), 11612 'D/G/tau' : Item("This is the file 'tau'.\n"), 11613 'D/gamma' : Item("This is the file 'gamma'.\n"), 11614 'D/H' : Item(), 11615 'D/H/chi' : Item("This is the file 'chi'.\n"), 11616 'D/H/psi' : Item("This is the file 'psi'.\n"), 11617 'D/H/omega' : Item("This is the file 'omega'.\n"), 11618 }) 11619 expected_skip = wc.State(A_COPY_path, { }) 11620 svntest.actions.run_and_verify_merge(A_COPY_path, '12', '1', 11621 sbox.repo_url + '/A', None, 11622 expected_output, 11623 expected_mergeinfo_output, 11624 expected_elision_output, 11625 expected_disk, 11626 expected_status, 11627 expected_skip, 11628 [], True, False) 11629 11630 # Revert the previous merge. 11631 svntest.actions.run_and_verify_svn(None, [], 11632 'revert', '-R', wc_dir) 11633 # Merge r12 to A_COPY and commit as r14. 11634 expected_output = wc.State(A_COPY_path, {}) 11635 expected_mergeinfo_output = wc.State(A_COPY_path, { 11636 '' : Item(status=' U'), 11637 }) 11638 expected_elision_output = wc.State(A_COPY_path, { 11639 }) 11640 expected_status = wc.State(A_COPY_path, { 11641 '' : Item(status=' M', wc_rev=13), 11642 'B' : Item(status=' ', wc_rev=13), 11643 'mu' : Item(status=' ', wc_rev=13), 11644 'B/E' : Item(status=' ', wc_rev=13), 11645 'B/E/alpha' : Item(status=' ', wc_rev=13), 11646 'B/E/beta' : Item(status=' ', wc_rev=13), 11647 'B/lambda' : Item(status=' ', wc_rev=13), 11648 'B/F' : Item(status=' ', wc_rev=13), 11649 'C' : Item(status=' ', wc_rev=13), 11650 'D' : Item(status=' ', wc_rev=13), 11651 'D/G' : Item(status=' ', wc_rev=13), 11652 'D/G/pi' : Item(status=' ', wc_rev=13), 11653 'D/G/rho' : Item(status=' ', wc_rev=13), 11654 'D/G/tau' : Item(status=' ', wc_rev=13), 11655 'D/gamma' : Item(status=' ', wc_rev=13), 11656 'D/H' : Item(status=' ', wc_rev=13), 11657 'D/H/chi' : Item(status=' ', wc_rev=13), 11658 'D/H/psi' : Item(status=' ', wc_rev=13), 11659 'D/H/omega' : Item(status=' ', wc_rev=13), 11660 }) 11661 expected_disk = wc.State('', { 11662 '' : Item(props={SVN_PROP_MERGEINFO : '/A:8,12'}), 11663 'B' : Item(), 11664 'mu' : Item("This is the file 'mu'.\n"), 11665 'B/E' : Item(), 11666 'B/E/alpha' : Item("This is the file 'alpha'.\n"), 11667 'B/E/beta' : Item("This is the file 'beta'.\n"), 11668 'B/lambda' : Item("This is the file 'lambda'.\n"), 11669 'B/F' : Item(), 11670 'C' : Item(), 11671 'D' : Item(), 11672 'D/G' : Item(), 11673 'D/G/pi' : Item("This is the file 'pi'.\n"), 11674 'D/G/rho' : Item("This is the file 'rho'.\n"), 11675 'D/G/tau' : Item("This is the file 'tau'.\n"), 11676 'D/gamma' : Item("This is the file 'gamma'.\n"), 11677 'D/H' : Item(), 11678 'D/H/chi' : Item("This is the file 'chi'.\n"), 11679 'D/H/psi' : Item("New content", 11680 props={SVN_PROP_MERGEINFO : '/A/D/H/psi:3,8'}), 11681 'D/H/omega' : Item("Now with fabulous new content!", 11682 props={SVN_PROP_MERGEINFO : '/A/D/H/omega:2-12'}), 11683 }) 11684 expected_skip = wc.State(A_COPY_path, { }) 11685 svntest.actions.run_and_verify_merge(A_COPY_path, '11', '12', 11686 sbox.repo_url + '/A', None, 11687 expected_output, 11688 expected_mergeinfo_output, 11689 expected_elision_output, 11690 expected_disk, 11691 expected_status, 11692 expected_skip, 11693 [], True, False) 11694 # As we did earlier, repeat the merge with the --record-only option to 11695 # preserve the old behavior of recording mergeinfo on every subtree, thus 11696 # allowing this test to actually test the issue #3067 fixes. 11697 expected_output = expected_merge_output( 11698 [[12]], ['U %s\n' % (A_COPY_path), 11699 ' G %s\n' % (A_COPY_path), 11700 ' U %s\n' % (psi_COPY_path), 11701 ' U %s\n' % (omega_COPY_path),]) 11702 svntest.actions.run_and_verify_svn(expected_output, [], 11703 'merge', '-c', '12', 11704 sbox.repo_url + '/A', 11705 A_COPY_path, '--record-only') 11706 sbox.simple_commit(message='Merge r12 to A_COPY') 11707 11708 # Update A_COPY/D/H/rho back to r13 so it's mergeinfo doesn't include 11709 # r12. Then merge a range, -r6:12 which should delete a subtree 11710 # (A_COPY/D/H/psi). 11711 svntest.actions.run_and_verify_svn(exp_noop_up_out(14), [], 'up', 11712 wc_dir) 11713 expected_output = wc.State(A_COPY_path, { 11714 'D/H/psi' : Item(status='D '), 11715 }) 11716 expected_mergeinfo_output = wc.State(A_COPY_path, { 11717 '' : Item(status=' U'), 11718 }) 11719 expected_elision_output = wc.State(A_COPY_path, { 11720 }) 11721 expected_status = wc.State(A_COPY_path, { 11722 '' : Item(status=' M', wc_rev=14), 11723 'B' : Item(status=' ', wc_rev=14), 11724 'mu' : Item(status=' ', wc_rev=14), 11725 'B/E' : Item(status=' ', wc_rev=14), 11726 'B/E/alpha' : Item(status=' ', wc_rev=14), 11727 'B/E/beta' : Item(status=' ', wc_rev=14), 11728 'B/lambda' : Item(status=' ', wc_rev=14), 11729 'B/F' : Item(status=' ', wc_rev=14), 11730 'C' : Item(status=' ', wc_rev=14), 11731 'D' : Item(status=' ', wc_rev=14), 11732 'D/G' : Item(status=' ', wc_rev=14), 11733 'D/G/pi' : Item(status=' ', wc_rev=14), 11734 'D/G/rho' : Item(status=' ', wc_rev=14), 11735 'D/G/tau' : Item(status=' ', wc_rev=14), 11736 'D/gamma' : Item(status=' ', wc_rev=14), 11737 'D/H' : Item(status=' ', wc_rev=14), 11738 'D/H/chi' : Item(status=' ', wc_rev=14), 11739 'D/H/psi' : Item(status='D ', wc_rev=14), 11740 'D/H/omega' : Item(status=' ', wc_rev=14), 11741 }) 11742 expected_disk = wc.State('', { 11743 '' : Item(props={SVN_PROP_MERGEINFO : '/A:7-12'}), 11744 'B' : Item(), 11745 'mu' : Item("This is the file 'mu'.\n"), 11746 'B/E' : Item(), 11747 'B/E/alpha' : Item("This is the file 'alpha'.\n"), 11748 'B/E/beta' : Item("This is the file 'beta'.\n"), 11749 'B/lambda' : Item("This is the file 'lambda'.\n"), 11750 'B/F' : Item(), 11751 'C' : Item(), 11752 'D' : Item(), 11753 'D/G' : Item(), 11754 'D/G/pi' : Item("This is the file 'pi'.\n"), 11755 'D/G/rho' : Item("This is the file 'rho'.\n"), 11756 'D/G/tau' : Item("This is the file 'tau'.\n"), 11757 'D/gamma' : Item("This is the file 'gamma'.\n"), 11758 'D/H' : Item(), 11759 'D/H/chi' : Item("This is the file 'chi'.\n"), 11760 'D/H/omega' : Item("Now with fabulous new content!", 11761 props={SVN_PROP_MERGEINFO : '/A/D/H/omega:2-12'}), 11762 }) 11763 expected_skip = wc.State(A_COPY_path, { }) 11764 svntest.actions.run_and_verify_merge(A_COPY_path, '6', '12', 11765 sbox.repo_url + '/A', None, 11766 expected_output, 11767 expected_mergeinfo_output, 11768 expected_elision_output, 11769 expected_disk, 11770 expected_status, 11771 expected_skip, 11772 [], True, False) 11773 11774#---------------------------------------------------------------------- 11775# Another test for issue #3067: 'subtrees that don't exist at the start 11776# or end of a merge range shouldn't break the merge' 11777# 11778# See https://issues.apache.org/jira/browse/SVN-3067#desc34 11779@Issue(3067) 11780@SkipUnless(server_has_mergeinfo) 11781def subtrees_with_empty_mergeinfo(sbox): 11782 "mergeinfo not set on subtree with empty mergeinfo" 11783 11784 # Use helper to setup a renamed subtree. 11785 wc_dir, expected_disk, expected_status = set_up_renamed_subtree(sbox) 11786 11787 # Some paths we'll care about 11788 H_COPY_path = sbox.ospath('H_COPY') 11789 11790 # Cherry harvest all available revsions from 'A/D/H' to 'H_COPY'. 11791 # 11792 # This should merge r4:6 from 'A/D/H' setting mergeinfo for r5-6 11793 # on both 'H_COPY' and 'H_COPY/psi_moved'. But since the working copy 11794 # is at a uniform working revision, the latter's mergeinfo should 11795 # elide, leaving explicit mergeinfo only on the merge target. 11796 expected_output = wc.State(H_COPY_path, { 11797 'psi_moved' : Item(status='U ') 11798 }) 11799 expected_mergeinfo_output = wc.State(H_COPY_path, { 11800 '' : Item(status=' U'), 11801 'psi_moved' : Item(status=' U'), 11802 }) 11803 expected_elision_output = wc.State(H_COPY_path, { 11804 'psi_moved' : Item(status=' U'), 11805 }) 11806 expected_status = wc.State(H_COPY_path, { 11807 '' : Item(status=' M', wc_rev=6), # mergeinfo set on target 11808 'psi_moved' : Item(status='MM', wc_rev=6), # mergeinfo elides 11809 'omega' : Item(status=' ', wc_rev=6), 11810 'chi' : Item(status=' ', wc_rev=6), 11811 }) 11812 expected_disk = wc.State('', { 11813 '' : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:5-6'}), 11814 'psi_moved' : Item("Even *Newer* content"), # mergeinfo elides 11815 'omega' : Item("New omega"), 11816 'chi' : Item("This is the file 'chi'.\n"), 11817 }) 11818 expected_skip = wc.State(H_COPY_path, { }) 11819 11820 svntest.actions.run_and_verify_merge(H_COPY_path, None, None, 11821 sbox.repo_url + '/A/D/H', None, 11822 expected_output, 11823 expected_mergeinfo_output, 11824 expected_elision_output, 11825 expected_disk, 11826 expected_status, expected_skip, 11827 check_props=True) 11828 11829#---------------------------------------------------------------------- 11830# Test for issue #3240 'commits to subtrees added by merge 11831# corrupt working copy and repos'. 11832@SkipUnless(server_has_mergeinfo) 11833@Issue(3240) 11834def commit_to_subtree_added_by_merge(sbox): 11835 "commits to subtrees added by merge wreak havoc" 11836 11837 # Setup a standard greek tree in r1. 11838 sbox.build() 11839 wc_dir = sbox.wc_dir 11840 11841 # Some paths we'll care about 11842 N_path = sbox.ospath('A/D/H/N') 11843 nu_path = sbox.ospath('A/D/H/N/nu') 11844 nu_COPY_path = sbox.ospath('A_COPY/D/H/N/nu') 11845 H_COPY_path = sbox.ospath('A_COPY/D/H') 11846 11847 # Copy 'A' to 'A_COPY' in r2. 11848 wc_disk, wc_status = set_up_branch(sbox, True) 11849 11850 # Create a 'A/D/H/N' and 'A/D/H/N/nu', and commit this new 11851 # subtree as r3. 11852 os.mkdir(N_path) 11853 svntest.main.file_write(nu_path, "This is the file 'nu'.\n") 11854 svntest.actions.run_and_verify_svn(None, [], 'add', N_path) 11855 expected_output = wc.State(wc_dir, 11856 {'A/D/H/N' : Item(verb='Adding'), 11857 'A/D/H/N/nu' : Item(verb='Adding')}) 11858 wc_status.add({'A/D/H/N' : Item(status=' ', wc_rev=3), 11859 'A/D/H/N/nu' : Item(status=' ', wc_rev=3)}) 11860 svntest.actions.run_and_verify_commit(wc_dir, expected_output, 11861 wc_status) 11862 11863 # Merge r3 to 'A_COPY/D/H', creating A_COPY/D/H/N' and 'A_COPY/D/H/N/nu'. 11864 # Commit the merge as r4. 11865 expected_output = wc.State(H_COPY_path, { 11866 'N' : Item(status='A '), 11867 'N/nu' : Item(status='A '), 11868 }) 11869 expected_mergeinfo_output = wc.State(H_COPY_path, { 11870 '' : Item(status=' U'), 11871 }) 11872 expected_elision_output = wc.State(H_COPY_path, { 11873 }) 11874 expected_status = wc.State(H_COPY_path, { 11875 '' : Item(status=' M', wc_rev=2), 11876 'psi' : Item(status=' ', wc_rev=2), 11877 'omega' : Item(status=' ', wc_rev=2), 11878 'chi' : Item(status=' ', wc_rev=2), 11879 'N' : Item(status='A ', copied='+', wc_rev='-'), 11880 'N/nu' : Item(status=' ', copied='+', wc_rev='-'), 11881 }) 11882 expected_disk = wc.State('', { 11883 '' : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:2-3'}), 11884 'psi' : Item("This is the file 'psi'.\n"), 11885 'omega' : Item("This is the file 'omega'.\n"), 11886 'chi' : Item("This is the file 'chi'.\n"), 11887 'N' : Item(), 11888 'N/nu' : Item("This is the file 'nu'.\n"), 11889 }) 11890 expected_skip = wc.State(H_COPY_path, {}) 11891 svntest.actions.run_and_verify_merge(H_COPY_path, 11892 None, None, 11893 sbox.repo_url + '/A/D/H', None, 11894 expected_output, 11895 expected_mergeinfo_output, 11896 expected_elision_output, 11897 expected_disk, 11898 expected_status, expected_skip, 11899 check_props=True) 11900 expected_output = wc.State(wc_dir, { 11901 'A_COPY/D/H' : Item(verb='Sending'), 11902 'A_COPY/D/H/N' : Item(verb='Adding'), 11903 }) 11904 wc_status.add({'A_COPY/D/H/N' : Item(status=' ', wc_rev=4), 11905 'A_COPY/D/H/N/nu' : Item(status=' ', wc_rev=4)}) 11906 wc_status.tweak('A_COPY/D/H', wc_rev=4) 11907 svntest.actions.run_and_verify_commit(wc_dir, expected_output, 11908 wc_status) 11909 11910 # Make a text change to 'A_COPY/D/H/N/nu' and commit it as r5. This 11911 # is the first place issue #3240 appears over DAV layers, and the 11912 # commit fails with an error like this: 11913 # trunk>svn ci -m "" merge_tests-100 11914 # Sending merge_tests-100\A_COPY\D\H\N\nu 11915 # Transmitting file data ...\..\..\subversion\libsvn_client\commit.c:919: 11916 # (apr_err=20014) 11917 # svn: Commit failed (details follow): 11918 # ..\..\..\subversion\libsvn_ra_neon\merge.c:260: (apr_err=20014) 11919 # svn: A MERGE response for '/svn-test-work/repositories/merge_tests-100/ 11920 # A/D/H/N/nu' is not a child of the destination 11921 # ('/svn-test-work/repositories/merge_tests-100/A_COPY/D/H/N') 11922 svntest.main.file_write(nu_COPY_path, "New content") 11923 expected_output = wc.State(wc_dir, 11924 {'A_COPY/D/H/N/nu' : Item(verb='Sending')}) 11925 wc_status.tweak('A_COPY/D/H/N/nu', wc_rev=5) 11926 svntest.actions.run_and_verify_commit(wc_dir, expected_output, 11927 wc_status) 11928 # The second place issue #3240 shows up is in the fact that the commit 11929 # *did* succeed, but the wrong path ('A/D/H/nu' rather than 'A_COPY/D/H/nu') 11930 # is affected. We can see this by running an update; since we just 11931 # committed there shouldn't be any incoming changes. 11932 svntest.actions.run_and_verify_svn(exp_noop_up_out(5), [], 'up', 11933 wc_dir) 11934 11935 11936#---------------------------------------------------------------------- 11937# Tests for merging the deletion of a node, where the node to be deleted 11938# is the same as or different from the node that was deleted. 11939 11940#---------------------------------------------------------------------- 11941def del_identical_file(sbox): 11942 "merge tries to delete a file of identical content" 11943 11944 # Set up a standard greek tree in r1. 11945 sbox.build() 11946 11947 saved_cwd = os.getcwd() 11948 os.chdir(sbox.wc_dir) 11949 sbox.wc_dir = '' 11950 11951 # Set up a modification and deletion in the source branch. 11952 source = 'A/D/G' 11953 s_rev_orig = 1 11954 svn_modfile(source+"/tau") 11955 sbox.simple_commit(source) 11956 s_rev_mod = 2 11957 sbox.simple_rm(source+"/tau") 11958 sbox.simple_commit(source) 11959 s_rev_del = 3 11960 11961 # Make an identical copy, and merge a deletion to it. 11962 target = 'A/D/G2' 11963 svn_copy(s_rev_mod, source, target) 11964 sbox.simple_commit(target) 11965 # Should be deleted quietly. 11966 svn_merge(s_rev_del, source, target, 11967 ['D %s\n' % local_path('A/D/G2/tau')]) 11968 11969 # Make a differing copy, locally modify it so it's the same, 11970 # and merge a deletion to it. 11971 target = 'A/D/G3' 11972 svn_copy(s_rev_orig, source, target) 11973 sbox.simple_commit(target) 11974 svn_modfile(target+"/tau") 11975 # Should be deleted quietly. 11976 svn_merge(s_rev_del, source, target, 11977 ['D %s\n' % local_path('A/D/G3/tau')]) 11978 11979 os.chdir(saved_cwd) 11980 11981#---------------------------------------------------------------------- 11982def del_sched_add_hist_file(sbox): 11983 "merge tries to delete identical sched-add file" 11984 11985 # Setup a standard greek tree in r1. 11986 sbox.build() 11987 11988 saved_cwd = os.getcwd() 11989 os.chdir(sbox.wc_dir) 11990 sbox.wc_dir = '' 11991 11992 # Set up a creation in the source branch. 11993 source = 'A/D/G' 11994 s_rev_orig = 1 11995 svn_mkfile(source+"/file") 11996 sbox.simple_commit(source) 11997 s_rev_add = 2 11998 11999 # Merge a creation, and delete by reverse-merging into uncommitted WC. 12000 target = 'A/D/G2' 12001 svn_copy(s_rev_orig, source, target) 12002 sbox.simple_commit(target) 12003 s_rev = 3 12004 svn_merge(s_rev_add, source, target, 12005 ['A %s\n' % local_path('A/D/G2/file')]) 12006 # Should be deleted quietly. 12007 svn_merge(-s_rev_add, source, target, 12008 ['D %s\n' % local_path('A/D/G2/file')], elides=['A/D/G2']) 12009 12010 os.chdir(saved_cwd) 12011 12012#---------------------------------------------------------------------- 12013@SkipUnless(server_has_mergeinfo) 12014def subtree_merges_dont_cause_spurious_conflicts(sbox): 12015 "subtree merges dont cause spurious conflicts" 12016 12017 # Fix a merge bug where previous merges are incorrectly reversed leading 12018 # to repeat merges and spurious conflicts. These can occur when a subtree 12019 # needs a range M:N merged that is older than the ranges X:Y needed by the 12020 # merge target *and* there are changes in the merge source between N:X that 12021 # affect parts of the merge target other than the subtree. An actual case 12022 # where our own epository encountered this problem is described here: 12023 # http://subversion.tigris.org/servlets/ReadMsg?listName=dev&msgNo=141832 12024 12025 sbox.build() 12026 wc_dir = sbox.wc_dir 12027 12028 # Some paths we'll care about 12029 rho_path = sbox.ospath('A/D/G/rho') 12030 A_COPY_path = sbox.ospath('A_COPY') 12031 psi_COPY_path = sbox.ospath('A_COPY/D/H/psi') 12032 12033 # Make a branch to merge to. 12034 wc_disk, wc_status = set_up_branch(sbox, False, 1) 12035 12036 # r7 Make a text change to A/D/G/rho. 12037 svntest.main.file_write(rho_path, "Newer content") 12038 expected_output = wc.State(wc_dir, {'A/D/G/rho' : Item(verb='Sending')}) 12039 wc_status.tweak('A/D/G/rho', wc_rev=7) 12040 svntest.actions.run_and_verify_commit(wc_dir, expected_output, 12041 wc_status) 12042 wc_disk.tweak('A/D/G/rho', contents="Newer content") 12043 12044 # r8 Make another text change to A/D/G/rho. 12045 svntest.main.file_write(rho_path, "Even *newer* content") 12046 expected_output = wc.State(wc_dir, {'A/D/G/rho' : Item(verb='Sending')}) 12047 wc_status.tweak('A/D/G/rho', wc_rev=8) 12048 svntest.actions.run_and_verify_commit(wc_dir, expected_output, wc_status) 12049 wc_disk.tweak('A/D/G/rho', contents="Even *newer* content") 12050 12051 # Update the WC to allow full mergeinfo inheritance and elision. 12052 svntest.actions.run_and_verify_svn(exp_noop_up_out(8), [], 'up', 12053 wc_dir) 12054 wc_status.tweak(wc_rev=8) 12055 12056 # r9 Merge r0:7 from A to A_COPY, then create a subtree with differing 12057 # mergeinfo under A_COPY by reverse merging r3 from A_COPY/D/H/psi. 12058 expected_output = wc.State(A_COPY_path, { 12059 'B/E/beta' : Item(status='U '), 12060 'D/G/rho' : Item(status='U '), 12061 'D/H/omega' : Item(status='U '), 12062 'D/H/psi' : Item(status='U '), 12063 }) 12064 expected_mergeinfo_output = wc.State(A_COPY_path, { 12065 '' : Item(status=' U'), 12066 }) 12067 expected_elision_output = wc.State(A_COPY_path, { 12068 }) 12069 expected_status = wc.State(A_COPY_path, { 12070 '' : Item(status=' M', wc_rev=8), 12071 'B' : Item(status=' ', wc_rev=8), 12072 'mu' : Item(status=' ', wc_rev=8), 12073 'B/E' : Item(status=' ', wc_rev=8), 12074 'B/E/alpha' : Item(status=' ', wc_rev=8), 12075 'B/E/beta' : Item(status='M ', wc_rev=8), 12076 'B/lambda' : Item(status=' ', wc_rev=8), 12077 'B/F' : Item(status=' ', wc_rev=8), 12078 'C' : Item(status=' ', wc_rev=8), 12079 'D' : Item(status=' ', wc_rev=8), 12080 'D/G' : Item(status=' ', wc_rev=8), 12081 'D/G/pi' : Item(status=' ', wc_rev=8), 12082 'D/G/rho' : Item(status='M ', wc_rev=8), 12083 'D/G/tau' : Item(status=' ', wc_rev=8), 12084 'D/gamma' : Item(status=' ', wc_rev=8), 12085 'D/H' : Item(status=' ', wc_rev=8), 12086 'D/H/chi' : Item(status=' ', wc_rev=8), 12087 'D/H/psi' : Item(status='M ', wc_rev=8), 12088 'D/H/omega' : Item(status='M ', wc_rev=8), 12089 }) 12090 expected_disk = wc.State('', { 12091 '' : Item(props={SVN_PROP_MERGEINFO : '/A:2-7'}), 12092 'B' : Item(), 12093 'mu' : Item("This is the file 'mu'.\n"), 12094 'B/E' : Item(), 12095 'B/E/alpha' : Item("This is the file 'alpha'.\n"), 12096 'B/E/beta' : Item("New content"), 12097 'B/lambda' : Item("This is the file 'lambda'.\n"), 12098 'B/F' : Item(), 12099 'C' : Item(), 12100 'D' : Item(), 12101 'D/G' : Item(), 12102 'D/G/pi' : Item("This is the file 'pi'.\n"), 12103 'D/G/rho' : Item("Newer content"), 12104 'D/G/tau' : Item("This is the file 'tau'.\n"), 12105 'D/gamma' : Item("This is the file 'gamma'.\n"), 12106 'D/H' : Item(), 12107 'D/H/chi' : Item("This is the file 'chi'.\n"), 12108 'D/H/psi' : Item("New content",), 12109 'D/H/omega' : Item("New content"), 12110 }) 12111 expected_skip = wc.State(A_COPY_path, { }) 12112 svntest.actions.run_and_verify_merge(A_COPY_path, '0', '7', 12113 sbox.repo_url + '/A', None, 12114 expected_output, 12115 expected_mergeinfo_output, 12116 expected_elision_output, 12117 expected_disk, 12118 expected_status, expected_skip, 12119 check_props=True) 12120 # run_and_verify_merge doesn't support merging to a file WCPATH 12121 # so use run_and_verify_svn. 12122 ### TODO: We can use run_and_verify_merge() here now. 12123 svntest.actions.run_and_verify_svn(expected_merge_output([[-3]], 12124 ['G ' + psi_COPY_path + '\n', 12125 ' G ' + psi_COPY_path + '\n']), 12126 [], 'merge', '-c-3', 12127 sbox.repo_url + '/A/D/H/psi', 12128 psi_COPY_path) 12129 # Commit the two merges. 12130 expected_output = svntest.wc.State(wc_dir, { 12131 'A_COPY' : Item(verb='Sending'), 12132 'A_COPY/B/E/beta' : Item(verb='Sending'), 12133 'A_COPY/D/G/rho' : Item(verb='Sending'), 12134 'A_COPY/D/H/psi' : Item(verb='Sending'), 12135 'A_COPY/D/H/omega' : Item(verb='Sending'), 12136 }) 12137 wc_status.tweak('A_COPY', 12138 'A_COPY/B/E/beta', 12139 'A_COPY/D/G/rho', 12140 'A_COPY/D/H/psi', 12141 'A_COPY/D/H/omega', 12142 wc_rev=9) 12143 svntest.actions.run_and_verify_commit(wc_dir, expected_output, 12144 wc_status) 12145 12146 # Update the WC to allow full mergeinfo inheritance and elision. 12147 svntest.actions.run_and_verify_svn(exp_noop_up_out(9), [], 'up', 12148 wc_dir) 12149 wc_status.tweak(wc_rev=9) 12150 12151 # r9 Merge all available revisions from A to A_COPY. 12152 # 12153 # This is where the bug revealed itself, instead of cleanly merging 12154 # just r3 and then r8-9, the first merge editor drive of r3 set A_COPY 12155 # to the state it was in r7, effectively reverting the merge committed 12156 # in r9. So we saw unexpected merges to omega, rho, and beta, as they 12157 # are returned to their r7 state and then a conflict on rho as the editor 12158 # attempted to merge r8: 12159 # 12160 # trunk>svn merge %url%/A merge_tests-104\A_COPY 12161 # --- Merging r3 into 'merge_tests-104\A_COPY\D\H\psi': 12162 # U merge_tests-104\A_COPY\D\H\psi 12163 # --- Merging r8 through r9 into 'merge_tests-104\A_COPY': 12164 # U merge_tests-104\A_COPY\D\H\omega 12165 # U merge_tests-104\A_COPY\D\G\rho 12166 # U merge_tests-104\A_COPY\B\E\beta 12167 # Conflict discovered in 'merge_tests-104/A_COPY/D/G/rho'. 12168 # Select: (p) postpone, (df) diff-full, (e) edit, 12169 # (mc) mine-conflict, (tc) theirs-conflict, 12170 # (s) show all options: p 12171 # --- Merging r8 through r9 into 'merge_tests-104\A_COPY': 12172 # C merge_tests-104\A_COPY\D\G\rho 12173 expected_output = wc.State(A_COPY_path, { 12174 'D/G/rho' : Item(status='U '), 12175 'D/H/psi' : Item(status='U '), 12176 }) 12177 expected_mergeinfo_output = wc.State(A_COPY_path, { 12178 '' : Item(status=' U'), 12179 'D/H/psi' : Item(status=' U'), 12180 }) 12181 expected_elision_output = wc.State(A_COPY_path, { 12182 'D/H/psi' : Item(status=' U'), 12183 }) 12184 expected_status = wc.State(A_COPY_path, { 12185 '' : Item(status=' M', wc_rev=9), 12186 'B' : Item(status=' ', wc_rev=9), 12187 'mu' : Item(status=' ', wc_rev=9), 12188 'B/E' : Item(status=' ', wc_rev=9), 12189 'B/E/alpha' : Item(status=' ', wc_rev=9), 12190 'B/E/beta' : Item(status=' ', wc_rev=9), 12191 'B/lambda' : Item(status=' ', wc_rev=9), 12192 'B/F' : Item(status=' ', wc_rev=9), 12193 'C' : Item(status=' ', wc_rev=9), 12194 'D' : Item(status=' ', wc_rev=9), 12195 'D/G' : Item(status=' ', wc_rev=9), 12196 'D/G/pi' : Item(status=' ', wc_rev=9), 12197 'D/G/rho' : Item(status='M ', wc_rev=9), 12198 'D/G/tau' : Item(status=' ', wc_rev=9), 12199 'D/gamma' : Item(status=' ', wc_rev=9), 12200 'D/H' : Item(status=' ', wc_rev=9), 12201 'D/H/chi' : Item(status=' ', wc_rev=9), 12202 'D/H/psi' : Item(status='MM', wc_rev=9), 12203 'D/H/omega' : Item(status=' ', wc_rev=9), 12204 }) 12205 expected_disk = wc.State('', { 12206 '' : Item(props={SVN_PROP_MERGEINFO : '/A:2-9'}), 12207 'B' : Item(), 12208 'mu' : Item("This is the file 'mu'.\n"), 12209 'B/E' : Item(), 12210 'B/E/alpha' : Item("This is the file 'alpha'.\n"), 12211 'B/E/beta' : Item("New content"), 12212 'B/lambda' : Item("This is the file 'lambda'.\n"), 12213 'B/F' : Item(), 12214 'C' : Item(), 12215 'D' : Item(), 12216 'D/G' : Item(), 12217 'D/G/pi' : Item("This is the file 'pi'.\n"), 12218 'D/G/rho' : Item("Even *newer* content"), 12219 'D/G/tau' : Item("This is the file 'tau'.\n"), 12220 'D/gamma' : Item("This is the file 'gamma'.\n"), 12221 'D/H' : Item(), 12222 'D/H/chi' : Item("This is the file 'chi'.\n"), 12223 'D/H/psi' : Item("New content"), # Mergeinfo elides to A_COPY 12224 'D/H/omega' : Item("New content"), 12225 }) 12226 expected_skip = wc.State(A_COPY_path, { }) 12227 svntest.actions.run_and_verify_merge(A_COPY_path, None, None, 12228 sbox.repo_url + '/A', None, 12229 expected_output, 12230 expected_mergeinfo_output, 12231 expected_elision_output, 12232 expected_disk, 12233 expected_status, expected_skip, 12234 check_props=True) 12235 12236#---------------------------------------------------------------------- 12237# Test for yet another variant of issue #3067. 12238@Issue(3067) 12239@SkipUnless(server_has_mergeinfo) 12240def merge_target_and_subtrees_need_nonintersecting_ranges(sbox): 12241 "target and subtrees need nonintersecting revs" 12242 12243 sbox.build() 12244 wc_dir = sbox.wc_dir 12245 12246 # Some paths we'll care about 12247 nu_path = sbox.ospath('A/D/G/nu') 12248 A_COPY_path = sbox.ospath('A_COPY') 12249 nu_COPY_path = sbox.ospath('A_COPY/D/G/nu') 12250 omega_COPY_path = sbox.ospath('A_COPY/D/H/omega') 12251 beta_COPY_path = sbox.ospath('A_COPY/B/E/beta') 12252 rho_COPY_path = sbox.ospath('A_COPY/D/G/rho') 12253 psi_COPY_path = sbox.ospath('A_COPY/D/H/psi') 12254 12255 # Make a branch to merge to. 12256 wc_disk, wc_status = set_up_branch(sbox, False, 1) 12257 12258 # Add file A/D/G/nu in r7. 12259 svntest.main.file_write(nu_path, "This is the file 'nu'.\n") 12260 svntest.actions.run_and_verify_svn(None, [], 'add', nu_path) 12261 expected_output = wc.State(wc_dir, {'A/D/G/nu' : Item(verb='Adding')}) 12262 wc_status.add({'A/D/G/nu' : Item(status=' ', wc_rev=7)}) 12263 svntest.actions.run_and_verify_commit(wc_dir, expected_output, 12264 wc_status) 12265 12266 # Make a text mod to A/D/G/nu in r8. 12267 svntest.main.file_write(nu_path, "New content") 12268 expected_output = wc.State(wc_dir, {'A/D/G/nu' : Item(verb='Sending')}) 12269 wc_status.tweak('A/D/G/nu', wc_rev=8) 12270 svntest.actions.run_and_verify_commit(wc_dir, expected_output, 12271 wc_status) 12272 12273 # Do several merges to setup a situation where the merge 12274 # target and two of its subtrees need non-intersecting ranges 12275 # merged when doing a synch (a.k.a. cherry harvest) merge. 12276 # 12277 # 1) Merge -r0:7 from A to A_COPY. 12278 # 12279 # 2) Merge -c8 from A/D/G/nu to A_COPY/D/G/nu. 12280 # 12281 # 3) Merge -c-6 from A/D/H/omega to A_COPY/D/H/omega. 12282 # 12283 # Commit this group of merges as r9. Since we already test these type 12284 # of merges to death we don't use run_and_verify_merge() on these 12285 # intermediate merges. 12286 svntest.actions.run_and_verify_svn( 12287 expected_merge_output([[2,7]], 12288 ['U ' + beta_COPY_path + '\n', 12289 'A ' + nu_COPY_path + '\n', 12290 'U ' + rho_COPY_path + '\n', 12291 'U ' + omega_COPY_path + '\n', 12292 'U ' + psi_COPY_path + '\n', 12293 ' U ' + A_COPY_path + '\n',] 12294 ), 12295 [], 'merge', '-r0:7', sbox.repo_url + '/A', A_COPY_path) 12296 svntest.actions.run_and_verify_svn( 12297 expected_merge_output([[8]], ['U ' + nu_COPY_path + '\n', 12298 ' G ' + nu_COPY_path + '\n']), 12299 [], 'merge', '-c8', sbox.repo_url + '/A/D/G/nu', nu_COPY_path) 12300 12301 svntest.actions.run_and_verify_svn( 12302 expected_merge_output([[-6]], ['G ' + omega_COPY_path + '\n', 12303 ' G ' + omega_COPY_path + '\n']), 12304 [], 'merge', '-c-6', sbox.repo_url + '/A/D/H/omega', omega_COPY_path) 12305 wc_status.add({'A_COPY/D/G/nu' : Item(status=' ', wc_rev=9)}) 12306 wc_status.tweak('A_COPY', 12307 'A_COPY/B/E/beta', 12308 'A_COPY/D/G/rho', 12309 'A_COPY/D/H/omega', 12310 'A_COPY/D/H/psi', 12311 wc_rev=9) 12312 expected_output = wc.State(wc_dir, { 12313 'A_COPY' : Item(verb='Sending'), 12314 'A_COPY/B/E/beta' : Item(verb='Sending'), 12315 'A_COPY/D/G/rho' : Item(verb='Sending'), 12316 'A_COPY/D/G/nu' : Item(verb='Adding'), 12317 'A_COPY/D/H/omega' : Item(verb='Sending'), 12318 'A_COPY/D/H/psi' : Item(verb='Sending'), 12319 }) 12320 svntest.actions.run_and_verify_commit(wc_dir, expected_output, wc_status) 12321 12322 # Update the WC to allow full mergeinfo inheritance and elision. 12323 svntest.actions.run_and_verify_svn(exp_noop_up_out(9), [], 'up', 12324 wc_dir) 12325 12326 # Merge all available revisions from A to A_COPY, the merge logic 12327 # should handle this situation (no "svn: Working copy path 'D/G/nu' 12328 # does not exist in repository" errors!). The mergeinfo on 12329 # A_COPY/D/H/omega elides to the root, but the mergeinfo on 12330 # A_COPY/D/G/nu, untouched by the merge, does not get updated so 12331 # does not elide. 12332 expected_output = wc.State(A_COPY_path, { 12333 'D/H/omega': Item(status='U '), 12334 }) 12335 expected_mergeinfo_output = wc.State(A_COPY_path, { 12336 '' : Item(status=' U'), 12337 'D/H/omega': Item(status=' U'), 12338 }) 12339 expected_elision_output = wc.State(A_COPY_path, { 12340 'D/H/omega': Item(status=' U'), 12341 }) 12342 expected_status = wc.State(A_COPY_path, { 12343 '' : Item(status=' M', wc_rev=9), 12344 'B' : Item(status=' ', wc_rev=9), 12345 'mu' : Item(status=' ', wc_rev=9), 12346 'B/E' : Item(status=' ', wc_rev=9), 12347 'B/E/alpha' : Item(status=' ', wc_rev=9), 12348 'B/E/beta' : Item(status=' ', wc_rev=9), 12349 'B/lambda' : Item(status=' ', wc_rev=9), 12350 'B/F' : Item(status=' ', wc_rev=9), 12351 'C' : Item(status=' ', wc_rev=9), 12352 'D' : Item(status=' ', wc_rev=9), 12353 'D/G' : Item(status=' ', wc_rev=9), 12354 'D/G/pi' : Item(status=' ', wc_rev=9), 12355 'D/G/rho' : Item(status=' ', wc_rev=9), 12356 'D/G/tau' : Item(status=' ', wc_rev=9), 12357 'D/G/nu' : Item(status=' ', wc_rev=9), 12358 'D/gamma' : Item(status=' ', wc_rev=9), 12359 'D/H' : Item(status=' ', wc_rev=9), 12360 'D/H/chi' : Item(status=' ', wc_rev=9), 12361 'D/H/psi' : Item(status=' ', wc_rev=9), 12362 'D/H/omega' : Item(status='MM', wc_rev=9), 12363 }) 12364 expected_disk = wc.State('', { 12365 '' : Item(props={SVN_PROP_MERGEINFO : '/A:2-9'}), 12366 'B' : Item(), 12367 'mu' : Item("This is the file 'mu'.\n"), 12368 'B/E' : Item(), 12369 'B/E/alpha' : Item("This is the file 'alpha'.\n"), 12370 'B/E/beta' : Item("New content"), 12371 'B/lambda' : Item("This is the file 'lambda'.\n"), 12372 'B/F' : Item(), 12373 'C' : Item(), 12374 'D' : Item(), 12375 'D/G' : Item(), 12376 'D/G/pi' : Item("This is the file 'pi'.\n"), 12377 'D/G/rho' : Item("New content"), 12378 'D/G/tau' : Item("This is the file 'tau'.\n"), 12379 'D/G/nu' : Item("New content", 12380 props={SVN_PROP_MERGEINFO : '/A/D/G/nu:2-8'}), 12381 'D/gamma' : Item("This is the file 'gamma'.\n"), 12382 'D/H' : Item(), 12383 'D/H/chi' : Item("This is the file 'chi'.\n"), 12384 'D/H/psi' : Item("New content"), 12385 'D/H/omega' : Item("New content"), 12386 }) 12387 expected_skip = wc.State(A_COPY_path, { }) 12388 svntest.actions.run_and_verify_merge(A_COPY_path, None, None, 12389 sbox.repo_url + '/A', None, 12390 expected_output, 12391 expected_mergeinfo_output, 12392 expected_elision_output, 12393 expected_disk, 12394 expected_status, 12395 expected_skip, 12396 check_props=True) 12397 12398#---------------------------------------------------------------------- 12399# Part of this test is a regression test for issue #3250 "Repeated merging 12400# of conflicting properties fails". 12401@Issue(3250) 12402def merge_two_edits_to_same_prop(sbox): 12403 "merge two successive edits to the same property" 12404 12405 sbox.build() 12406 wc_dir = sbox.wc_dir 12407 12408 # Make a branch to merge to. (This is r6.) 12409 wc_disk, wc_status = set_up_branch(sbox, False, 1) 12410 initial_rev = 6 12411 12412 # Change into the WC dir for convenience 12413 was_cwd = os.getcwd() 12414 os.chdir(sbox.wc_dir) 12415 sbox.wc_dir = '' 12416 wc_disk.wc_dir = '' 12417 wc_status.wc_dir = '' 12418 12419 # Some paths we'll care about 12420 A_path = "A" 12421 A_COPY_path = "A_COPY" 12422 mu_path = os.path.join(A_path, "mu") 12423 mu_COPY_path = os.path.join(A_COPY_path, "mu") 12424 12425 # In the source, make two successive changes to the same property 12426 sbox.simple_propset('p', 'new-val-1', 'A/mu') 12427 sbox.simple_commit('A/mu') 12428 rev1 = initial_rev + 1 12429 sbox.simple_propset('p', 'new-val-2', 'A/mu') 12430 sbox.simple_commit('A/mu') 12431 rev2 = initial_rev + 2 12432 12433 # Merge the first change, then the second, to a target branch. 12434 svn_merge(rev1, A_path, A_COPY_path) 12435 svn_merge(rev2, A_path, A_COPY_path) 12436 12437 # Both changes should merge automatically: the second one should not 12438 # complain about the local mod which the first one caused. The starting 12439 # value in the target ("mine") for the second merge is exactly equal to 12440 # the merge-left source value. 12441 12442 # A merge-tracking version of this problem is when the merge-tracking 12443 # algorithm breaks a single requested merge into two phases because of 12444 # some other target within the same merge requiring only a part of the 12445 # revision range. 12446 12447 # ==================================================================== 12448 12449 # We test issue #3250 here: that is, test that we can make two successive 12450 # conflicting changes to the same property on the same node (here a file; 12451 # in #3250 it was on a dir). 12452 # 12453 # ### But we no longer support merging into a node that's already in 12454 # conflict, and the 'rev3' merge here has been tweaked to resolve 12455 # the conflict, so it no longer tests the original #3250 scenario. 12456 # 12457 # Revert changes to branch wc 12458 svntest.actions.run_and_verify_svn(None, [], 12459 'revert', '--recursive', A_COPY_path) 12460 12461 # In the branch, make two successive changes to the same property 12462 sbox.simple_propset('p', 'new-val-3', 'A_COPY/mu') 12463 sbox.simple_commit('A_COPY/mu') 12464 rev3 = initial_rev + 3 12465 sbox.simple_propset('p', 'new-val-4', 'A_COPY/mu') 12466 sbox.simple_commit('A_COPY/mu') 12467 rev4 = initial_rev + 4 12468 12469 # Merge the two changes together to trunk. 12470 svn_merge([rev3, rev4], A_COPY_path, A_path, [ 12471 " C %s\n" % mu_path, 12472 ], prop_conflicts=1, args=['--allow-mixed-revisions']) 12473 12474 # Revert changes to trunk wc, to test next scenario of #3250 12475 svntest.actions.run_and_verify_svn(None, [], 12476 'revert', '--recursive', A_path) 12477 12478 # Merge the first change, then the second, to trunk. 12479 svn_merge(rev3, A_COPY_path, A_path, [ 12480 " C %s\n" % mu_path, 12481 "Resolved .* '%s'\n" % mu_path, 12482 ], prop_resolved=1, 12483 args=['--allow-mixed-revisions', 12484 '--accept=working']) 12485 svn_merge(rev4, A_COPY_path, A_path, [ 12486 " C %s\n" % mu_path, 12487 ], prop_conflicts=1, args=['--allow-mixed-revisions']) 12488 12489 os.chdir(was_cwd) 12490 12491#---------------------------------------------------------------------- 12492def merge_an_eol_unification_and_set_svn_eol_style(sbox): 12493 "merge an EOL unification and set svn:eol-style" 12494 # In svn 1.5.2, merging the two changes between these three states: 12495 # r1. inconsistent EOLs and no svn:eol-style 12496 # r2. consistent EOLs and no svn:eol-style 12497 # r3. consistent EOLs and svn:eol-style=native 12498 # fails if attempted as a single merge (e.g. "svn merge r1:3") though it 12499 # succeeds if attempted in two phases (e.g. "svn merge -c2,3"). 12500 12501 sbox.build() 12502 wc_dir = sbox.wc_dir 12503 12504 # Make a branch to merge to. (This will be r6.) 12505 wc_disk, wc_status = set_up_branch(sbox, False, 1) 12506 initial_rev = 6 12507 12508 # Change into the WC dir for convenience 12509 was_cwd = os.getcwd() 12510 os.chdir(sbox.wc_dir) 12511 sbox.wc_dir = '' 12512 wc_disk.wc_dir = '' 12513 wc_status.wc_dir = '' 12514 12515 content1 = 'Line1\nLine2\r\n' # write as 'binary' to get these exact EOLs 12516 content2 = 'Line1\nLine2\n' # write as 'text' to get native EOLs in file 12517 12518 # In the source branch, create initial state and two successive changes. 12519 # Use binary mode to write the first file so no newline conversion occurs. 12520 svntest.main.file_write('A/mu', content1, 'wb') 12521 sbox.simple_commit('A/mu') 12522 rev1 = initial_rev + 1 12523 # Use text mode to write the second copy of the file to get native EOLs. 12524 svntest.main.file_write('A/mu', content2, 'w') 12525 sbox.simple_commit('A/mu') 12526 rev2 = initial_rev + 2 12527 sbox.simple_propset('svn:eol-style', 'native', 'A/mu') 12528 sbox.simple_commit('A/mu') 12529 rev3 = initial_rev + 3 12530 12531 # Merge the initial state (inconsistent EOLs) to the target branch. 12532 svn_merge(rev1, 'A', 'A_COPY') 12533 sbox.simple_commit('A_COPY') 12534 12535 # Merge the two changes together to the target branch. 12536 svn_merge([rev2, rev3], 'A', 'A_COPY', 12537 args=['--allow-mixed-revisions']) 12538 12539 # That merge should succeed. 12540 # Surprise: setting svn:eol-style='LF' instead of 'native' doesn't fail. 12541 # Surprise: if we don't merge the file's 'rev1' state first, it doesn't fail 12542 # nor even raise a conflict. 12543 12544#---------------------------------------------------------------------- 12545@SkipUnless(server_has_mergeinfo) 12546def merge_adds_mergeinfo_correctly(sbox): 12547 "merge adds mergeinfo to subtrees correctly" 12548 12549 # A merge may add explicit mergeinfo to the subtree of a merge target 12550 # as a result of changes in the merge source. These paths may have 12551 # inherited mergeinfo prior to the merge, if so the subtree should end up 12552 # with mergeinfo that reflects all of the following: 12553 # 12554 # A) The mergeinfo added from the merge source 12555 # 12556 # B) The mergeinfo the subtree inherited prior to the merge. 12557 # 12558 # C) Mergeinfo describing the merge performed. 12559 # 12560 # See http://subversion.tigris.org/servlets/ReadMsg?listName=dev&msgNo=142460 12561 12562 sbox.build() 12563 wc_dir = sbox.wc_dir 12564 12565 # Setup a 'trunk' and two branches. 12566 wc_disk, wc_status = set_up_branch(sbox, False, 2) 12567 12568 # Some paths we'll care about 12569 A_COPY_path = sbox.ospath('A_COPY') 12570 D_COPY_path = sbox.ospath('A_COPY/D') 12571 A_COPY_2_path = sbox.ospath('A_COPY_2') 12572 D_COPY_2_path = sbox.ospath('A_COPY_2/D') 12573 12574 # Update working copy to allow full inheritance and elision. 12575 svntest.actions.run_and_verify_svn(exp_noop_up_out(7), [], 12576 'up', wc_dir) 12577 wc_status.tweak(wc_rev=7) 12578 12579 # Merge r5 from A to A_COPY and commit as r8. 12580 # This creates explicit mergeinfo on A_COPY of '/A:5'. 12581 expected_output = wc.State(A_COPY_path, { 12582 'D/G/rho': Item(status='U '), 12583 }) 12584 expected_mergeinfo_output = wc.State(A_COPY_path, { 12585 '' : Item(status=' U'), 12586 }) 12587 expected_elision_output = wc.State(A_COPY_path, { 12588 }) 12589 expected_status = wc.State(A_COPY_path, { 12590 '' : Item(status=' M', wc_rev=7), 12591 'B' : Item(status=' ', wc_rev=7), 12592 'mu' : Item(status=' ', wc_rev=7), 12593 'B/E' : Item(status=' ', wc_rev=7), 12594 'B/E/alpha' : Item(status=' ', wc_rev=7), 12595 'B/E/beta' : Item(status=' ', wc_rev=7), 12596 'B/lambda' : Item(status=' ', wc_rev=7), 12597 'B/F' : Item(status=' ', wc_rev=7), 12598 'C' : Item(status=' ', wc_rev=7), 12599 'D' : Item(status=' ', wc_rev=7), 12600 'D/G' : Item(status=' ', wc_rev=7), 12601 'D/G/pi' : Item(status=' ', wc_rev=7), 12602 'D/G/rho' : Item(status='M ', wc_rev=7), 12603 'D/G/tau' : Item(status=' ', wc_rev=7), 12604 'D/gamma' : Item(status=' ', wc_rev=7), 12605 'D/H' : Item(status=' ', wc_rev=7), 12606 'D/H/chi' : Item(status=' ', wc_rev=7), 12607 'D/H/psi' : Item(status=' ', wc_rev=7), 12608 'D/H/omega' : Item(status=' ', wc_rev=7), 12609 }) 12610 expected_disk = wc.State('', { 12611 '' : Item(props={SVN_PROP_MERGEINFO : '/A:5'}), 12612 'B' : Item(), 12613 'mu' : Item("This is the file 'mu'.\n"), 12614 'B/E' : Item(), 12615 'B/E/alpha' : Item("This is the file 'alpha'.\n"), 12616 'B/E/beta' : Item("This is the file 'beta'.\n"), 12617 'B/lambda' : Item("This is the file 'lambda'.\n"), 12618 'B/F' : Item(), 12619 'C' : Item(), 12620 'D' : Item(), 12621 'D/G' : Item(), 12622 'D/G/pi' : Item("This is the file 'pi'.\n"), 12623 'D/G/rho' : Item("New content"), 12624 'D/G/tau' : Item("This is the file 'tau'.\n"), 12625 'D/gamma' : Item("This is the file 'gamma'.\n"), 12626 'D/H' : Item(), 12627 'D/H/chi' : Item("This is the file 'chi'.\n"), 12628 'D/H/psi' : Item("This is the file 'psi'.\n"), 12629 'D/H/omega' : Item("This is the file 'omega'.\n"), 12630 }) 12631 expected_skip = wc.State(A_COPY_path, { }) 12632 svntest.actions.run_and_verify_merge(A_COPY_path, '4', '5', 12633 sbox.repo_url + '/A', None, 12634 expected_output, 12635 expected_mergeinfo_output, 12636 expected_elision_output, 12637 expected_disk, 12638 expected_status, 12639 expected_skip, 12640 check_props=True) 12641 wc_status.tweak('A_COPY', 12642 'A_COPY/D/G/rho', 12643 wc_rev=8) 12644 expected_output = wc.State(wc_dir, { 12645 'A_COPY' : Item(verb='Sending'), 12646 'A_COPY/D/G/rho' : Item(verb='Sending'), 12647 }) 12648 svntest.actions.run_and_verify_commit(wc_dir, expected_output, wc_status) 12649 12650 # Merge r7 from A/D to A_COPY_2/D and commit as r9. 12651 # This creates explicit mergeinfo on A_COPY_2/D of '/A/D:7'. 12652 expected_output = wc.State(D_COPY_2_path, { 12653 'H/omega': Item(status='U '), 12654 }) 12655 expected_mergeinfo_output = wc.State(D_COPY_2_path, { 12656 '' : Item(status=' U'), 12657 }) 12658 expected_elision_output = wc.State(D_COPY_2_path, { 12659 }) 12660 expected_status = wc.State(D_COPY_2_path, { 12661 '' : Item(status=' M', wc_rev=7), 12662 'G' : Item(status=' ', wc_rev=7), 12663 'G/pi' : Item(status=' ', wc_rev=7), 12664 'G/rho' : Item(status=' ', wc_rev=7), 12665 'G/tau' : Item(status=' ', wc_rev=7), 12666 'gamma' : Item(status=' ', wc_rev=7), 12667 'H' : Item(status=' ', wc_rev=7), 12668 'H/chi' : Item(status=' ', wc_rev=7), 12669 'H/psi' : Item(status=' ', wc_rev=7), 12670 'H/omega' : Item(status='M ', wc_rev=7), 12671 }) 12672 expected_disk = wc.State('', { 12673 '' : Item(props={SVN_PROP_MERGEINFO : '/A/D:7'}), 12674 'G' : Item(), 12675 'G/pi' : Item("This is the file 'pi'.\n"), 12676 'G/rho' : Item("This is the file 'rho'.\n"), 12677 'G/tau' : Item("This is the file 'tau'.\n"), 12678 'gamma' : Item("This is the file 'gamma'.\n"), 12679 'H' : Item(), 12680 'H/chi' : Item("This is the file 'chi'.\n"), 12681 'H/psi' : Item("This is the file 'psi'.\n"), 12682 'H/omega' : Item("New content"), 12683 }) 12684 expected_skip = wc.State(A_COPY_path, { }) 12685 svntest.actions.run_and_verify_merge(D_COPY_2_path, '6', '7', 12686 sbox.repo_url + '/A/D', None, 12687 expected_output, 12688 expected_mergeinfo_output, 12689 expected_elision_output, 12690 expected_disk, 12691 expected_status, 12692 expected_skip, 12693 check_props=True) 12694 wc_status.tweak('A_COPY_2/D', 12695 'A_COPY_2/D/H/omega', 12696 wc_rev=9) 12697 expected_output = wc.State(wc_dir, { 12698 'A_COPY_2/D' : Item(verb='Sending'), 12699 'A_COPY_2/D/H/omega' : Item(verb='Sending'), 12700 }) 12701 svntest.actions.run_and_verify_commit(wc_dir, expected_output, wc_status) 12702 12703 # Merge r9 from A_COPY_2 to A_COPY. A_COPY/D gets the explicit mergeinfo 12704 # '/A/D/:7' added from r9. But it prior to the merge it inherited '/A/D:5' 12705 # from A_COPY, so this should be present in its explicit mergeinfo. Lastly, 12706 # the mergeinfo describing this merge '/A_COPY_2:9' should also be present 12707 # in A_COPY's explicit mergeinfo. 12708 # Update working copy to allow full inheritance and elision. 12709 svntest.actions.run_and_verify_svn(exp_noop_up_out(9), [], 12710 'up', wc_dir) 12711 expected_output = wc.State(A_COPY_path, { 12712 'D' : Item(status=' U'), 12713 'D/H/omega': Item(status='U '), 12714 }) 12715 expected_mergeinfo_output = wc.State(A_COPY_path, { 12716 '' : Item(status=' U'), 12717 'D' : Item(status=' G'), 12718 }) 12719 expected_elision_output = wc.State(A_COPY_path, { 12720 }) 12721 expected_status = wc.State(A_COPY_path, { 12722 '' : Item(status=' M', wc_rev=9), 12723 'B' : Item(status=' ', wc_rev=9), 12724 'mu' : Item(status=' ', wc_rev=9), 12725 'B/E' : Item(status=' ', wc_rev=9), 12726 'B/E/alpha' : Item(status=' ', wc_rev=9), 12727 'B/E/beta' : Item(status=' ', wc_rev=9), 12728 'B/lambda' : Item(status=' ', wc_rev=9), 12729 'B/F' : Item(status=' ', wc_rev=9), 12730 'C' : Item(status=' ', wc_rev=9), 12731 'D' : Item(status=' M', wc_rev=9), 12732 'D/G' : Item(status=' ', wc_rev=9), 12733 'D/G/pi' : Item(status=' ', wc_rev=9), 12734 'D/G/rho' : Item(status=' ', wc_rev=9), 12735 'D/G/tau' : Item(status=' ', wc_rev=9), 12736 'D/gamma' : Item(status=' ', wc_rev=9), 12737 'D/H' : Item(status=' ', wc_rev=9), 12738 'D/H/chi' : Item(status=' ', wc_rev=9), 12739 'D/H/psi' : Item(status=' ', wc_rev=9), 12740 'D/H/omega' : Item(status='M ', wc_rev=9), 12741 }) 12742 expected_disk = wc.State('', { 12743 '' : Item(props={SVN_PROP_MERGEINFO : '/A:5\n/A_COPY_2:9'}), 12744 'B' : Item(), 12745 'mu' : Item("This is the file 'mu'.\n"), 12746 'B/E' : Item(), 12747 'B/E/alpha' : Item("This is the file 'alpha'.\n"), 12748 'B/E/beta' : Item("This is the file 'beta'.\n"), 12749 'B/lambda' : Item("This is the file 'lambda'.\n"), 12750 'B/F' : Item(), 12751 'C' : Item(), 12752 'D' : Item(props={SVN_PROP_MERGEINFO : '/A/D:5,7\n/A_COPY_2/D:9'}), 12753 'D/G' : Item(), 12754 'D/G/pi' : Item("This is the file 'pi'.\n"), 12755 'D/G/rho' : Item("New content"), 12756 'D/G/tau' : Item("This is the file 'tau'.\n"), 12757 'D/gamma' : Item("This is the file 'gamma'.\n"), 12758 'D/H' : Item(), 12759 'D/H/chi' : Item("This is the file 'chi'.\n"), 12760 'D/H/psi' : Item("This is the file 'psi'.\n"), 12761 'D/H/omega' : Item("New content"), 12762 }) 12763 expected_skip = wc.State(A_COPY_path, { }) 12764 svntest.actions.run_and_verify_merge(A_COPY_path, '8', '9', 12765 sbox.repo_url + '/A_COPY_2', None, 12766 expected_output, 12767 expected_mergeinfo_output, 12768 expected_elision_output, 12769 expected_disk, 12770 expected_status, 12771 expected_skip, 12772 check_props=True) 12773 12774 # Revert and repeat the above merge, but this time create some 12775 # uncommitted mergeinfo on A_COPY/D, this should not cause a write 12776 # lock error as was seen in http://subversion.tigris.org/ 12777 # ds/viewMessage.do?dsForumId=462&dsMessageId=103945 12778 svntest.actions.run_and_verify_svn(None, [], 12779 'revert', '-R', wc_dir) 12780 svntest.actions.run_and_verify_svn(None, [], 12781 'ps', SVN_PROP_MERGEINFO, '', 12782 D_COPY_path) 12783 expected_output = wc.State(A_COPY_path, { 12784 'D' : Item(status=' G'), # Merged with local svn:mergeinfo 12785 'D/H/omega': Item(status='U '), 12786 }) 12787 expected_mergeinfo_output = wc.State(A_COPY_path, { 12788 '' : Item(status=' U'), 12789 'D' : Item(status=' G'), 12790 }) 12791 expected_elision_output = wc.State(A_COPY_path, { 12792 }) 12793 svntest.actions.run_and_verify_merge(A_COPY_path, '8', '9', 12794 sbox.repo_url + '/A_COPY_2', None, 12795 expected_output, 12796 expected_mergeinfo_output, 12797 expected_elision_output, 12798 expected_disk, 12799 expected_status, 12800 expected_skip, 12801 check_props=True) 12802 12803#---------------------------------------------------------------------- 12804@SkipUnless(server_has_mergeinfo) 12805def natural_history_filtering(sbox): 12806 "natural history filtering permits valid mergeinfo" 12807 12808 # While filtering self-referential mergeinfo (e.g. natural history) that 12809 # a merge tries to add to a target, we may encounter contiguous revision 12810 # ranges that describe *both* natural history and valid mergeinfo. The 12811 # former should be filtered, but the latter allowed and recorded on the 12812 # target. See 12813 # http://subversion.tigris.org/servlets/ReadMsg?listName=dev&msgNo=142777. 12814 # 12815 # To set up a situation where this can occur we'll do the following: 12816 # 12817 # trunk -1-----3-4-5-6-------8----------- A 12818 # \ \ \ 12819 # branch1 2-----------\-------9-------- A_COPY 12820 # \ \ 12821 # branch2 7--------10---- A_COPY_2 12822 # 12823 # 1) Create a 'trunk'. 12824 # 12825 # 2) Copy 'trunk' to 'branch1'. 12826 # 12827 # 3) Make some changes under 'trunk'. 12828 # 12829 # 4) Copy 'trunk' to 'branch2'. 12830 # 12831 # 5) Make some more changes under 'trunk'. 12832 # 12833 # 6) Merge all available revisions from 'trunk' to 'branch1' and commit. 12834 # 12835 # 7) Merge all available revisions from 'branch1' to 'branch2'. 12836 # 'branch2' should have explicit merginfo for both 'branch1' *and* for 12837 # the revisions on 'trunk' which occurred after 'branch2' was copied as 12838 # these are not part of 'branch2's natural history. 12839 12840 def path_join(head, tail): 12841 if not head: return tail 12842 if not tail: return head 12843 return head + '/' + tail 12844 12845 def greek_file_item(path): 12846 if path[-1:].islower(): 12847 basename = re.sub('.*/', '', path) 12848 return Item("This is the file '" + basename + "'.\n") 12849 return Item() 12850 12851 A_paths = [ 12852 "", 12853 "B", 12854 "B/lambda", 12855 "B/E", 12856 "B/E/alpha", 12857 "B/E/beta", 12858 "B/F", 12859 "mu", 12860 "C", 12861 "D", 12862 "D/gamma", 12863 "D/G", 12864 "D/G/pi", 12865 "D/G/rho", 12866 "D/G/tau", 12867 "D/H", 12868 "D/H/chi", 12869 "D/H/omega", 12870 "D/H/psi", 12871 ] 12872 12873 sbox.build() 12874 wc_dir = sbox.wc_dir 12875 12876 # Some paths we'll care about 12877 A_COPY_path = sbox.ospath('A_COPY') 12878 A_COPY_2_path = sbox.ospath('A_COPY_2') 12879 chi_path = sbox.ospath('A/D/H/chi') 12880 12881 # r1-r6: Setup a 'trunk' (A) and a 'branch' (A_COPY). 12882 wc_disk, wc_status = set_up_branch(sbox, False, 1) 12883 12884 # r7: Make a second 'branch': Copy A to A_COPY_2 12885 expected = svntest.verify.UnorderedOutput( 12886 [ "A " + sbox.ospath(path_join("A_COPY_2", p)) + "\n" 12887 for p in A_paths ]) 12888 wc_status.add( 12889 { path_join("A_COPY_2", p) : Item(status=' ', wc_rev=7) 12890 for p in A_paths }) 12891 wc_disk.add( 12892 { path_join("A_COPY_2", p) : 12893 Item("New content") if p in ['B/E/beta', 'D/G/rho', 'D/H/chi', 'D/H/psi'] 12894 else greek_file_item(p) 12895 for p in A_paths }) 12896 svntest.actions.run_and_verify_svn(expected, [], 'copy', 12897 sbox.repo_url + "/A", 12898 A_COPY_2_path) 12899 expected_output = wc.State(wc_dir, {"A_COPY_2" : Item(verb='Adding')}) 12900 svntest.actions.run_and_verify_commit(wc_dir, 12901 expected_output, 12902 wc_status) 12903 12904 # r8: Make a text change under A, to A/D/H/chi. 12905 svntest.main.file_write(chi_path, "New content") 12906 expected_output = wc.State(wc_dir, {'A/D/H/chi' : Item(verb='Sending')}) 12907 wc_status.tweak('A/D/H/chi', wc_rev=8) 12908 svntest.actions.run_and_verify_commit(wc_dir, expected_output, 12909 wc_status) 12910 wc_disk.tweak('A/D/H/psi', contents="New content") 12911 12912 # r9: Merge all available revisions from A to A_COPY. But first 12913 # update working copy to allow full inheritance and elision. 12914 svntest.actions.run_and_verify_svn(exp_noop_up_out(8), [], 12915 'up', wc_dir) 12916 wc_status.tweak(wc_rev=8) 12917 expected_output = wc.State(A_COPY_path, { 12918 'B/E/beta' : Item(status='U '), 12919 'D/G/rho' : Item(status='U '), 12920 'D/H/chi' : Item(status='U '), 12921 'D/H/psi' : Item(status='U '), 12922 'D/H/omega': Item(status='U '), 12923 }) 12924 expected_mergeinfo_output = wc.State(A_COPY_path, { 12925 '' : Item(status=' U'), 12926 }) 12927 expected_elision_output = wc.State(A_COPY_path, { 12928 }) 12929 expected_status = wc.State(A_COPY_path, { 12930 '' : Item(status=' M', wc_rev=8), 12931 'B' : Item(status=' ', wc_rev=8), 12932 'mu' : Item(status=' ', wc_rev=8), 12933 'B/E' : Item(status=' ', wc_rev=8), 12934 'B/E/alpha' : Item(status=' ', wc_rev=8), 12935 'B/E/beta' : Item(status='M ', wc_rev=8), 12936 'B/lambda' : Item(status=' ', wc_rev=8), 12937 'B/F' : Item(status=' ', wc_rev=8), 12938 'C' : Item(status=' ', wc_rev=8), 12939 'D' : Item(status=' ', wc_rev=8), 12940 'D/G' : Item(status=' ', wc_rev=8), 12941 'D/G/pi' : Item(status=' ', wc_rev=8), 12942 'D/G/rho' : Item(status='M ', wc_rev=8), 12943 'D/G/tau' : Item(status=' ', wc_rev=8), 12944 'D/gamma' : Item(status=' ', wc_rev=8), 12945 'D/H' : Item(status=' ', wc_rev=8), 12946 'D/H/chi' : Item(status='M ', wc_rev=8), 12947 'D/H/psi' : Item(status='M ', wc_rev=8), 12948 'D/H/omega' : Item(status='M ', wc_rev=8), 12949 }) 12950 expected_disk = wc.State('', { 12951 '' : Item(props={SVN_PROP_MERGEINFO : '/A:2-8'}), 12952 'B' : Item(), 12953 'mu' : Item("This is the file 'mu'.\n"), 12954 'B/E' : Item(), 12955 'B/E/alpha' : Item("This is the file 'alpha'.\n"), 12956 'B/E/beta' : Item("New content"), 12957 'B/lambda' : Item("This is the file 'lambda'.\n"), 12958 'B/F' : Item(), 12959 'C' : Item(), 12960 'D' : Item(), 12961 'D/G' : Item(), 12962 'D/G/pi' : Item("This is the file 'pi'.\n"), 12963 'D/G/rho' : Item("New content"), 12964 'D/G/tau' : Item("This is the file 'tau'.\n"), 12965 'D/gamma' : Item("This is the file 'gamma'.\n"), 12966 'D/H' : Item(), 12967 'D/H/chi' : Item("New content"), 12968 'D/H/psi' : Item("New content"), 12969 'D/H/omega' : Item("New content"), 12970 }) 12971 expected_skip = wc.State(A_COPY_path, { }) 12972 svntest.actions.run_and_verify_merge(A_COPY_path, None, None, 12973 sbox.repo_url + '/A', None, 12974 expected_output, 12975 expected_mergeinfo_output, 12976 expected_elision_output, 12977 expected_disk, 12978 expected_status, 12979 expected_skip, 12980 check_props=True) 12981 wc_status.tweak('A_COPY', 12982 'A_COPY/B/E/beta', 12983 'A_COPY/D/G/rho', 12984 'A_COPY/D/H/chi', 12985 'A_COPY/D/H/psi', 12986 'A_COPY/D/H/omega', 12987 wc_rev=9) 12988 expected_output = wc.State(wc_dir, { 12989 'A_COPY' : Item(verb='Sending'), 12990 'A_COPY/B/E/beta' : Item(verb='Sending'), 12991 'A_COPY/D/G/rho' : Item(verb='Sending'), 12992 'A_COPY/D/H/chi' : Item(verb='Sending'), 12993 'A_COPY/D/H/psi' : Item(verb='Sending'), 12994 'A_COPY/D/H/omega' : Item(verb='Sending'), 12995 }) 12996 svntest.actions.run_and_verify_commit(wc_dir, expected_output, wc_status) 12997 12998 # Again update the working copy to allow full inheritance and elision. 12999 svntest.actions.run_and_verify_svn(exp_noop_up_out(9), [], 13000 'up', wc_dir) 13001 wc_status.tweak(wc_rev=9) 13002 13003 # Merge all available revisions from A_COPY to A_COPY_2. The mergeinfo on 13004 # A_COPY_2 should reflect both the merge of revisions 2-9 from A_COPY *and* 13005 # revisions 7-8 from A. Reivisions 2-6 from A should not be part of the 13006 # explicit mergeinfo on A_COPY_2 as they are already part of its natural 13007 # history. 13008 expected_output = wc.State(A_COPY_2_path, { 13009 '' : Item(status=' U'), 13010 'D/H/chi' : Item(status='U '), 13011 }) 13012 expected_mergeinfo_output = wc.State(A_COPY_2_path, { 13013 '' : Item(status=' G'), 13014 }) 13015 expected_elision_output = wc.State(A_COPY_2_path, { 13016 }) 13017 expected_status = wc.State(A_COPY_2_path, { 13018 '' : Item(status=' M', wc_rev=9), 13019 'B' : Item(status=' ', wc_rev=9), 13020 'mu' : Item(status=' ', wc_rev=9), 13021 'B/E' : Item(status=' ', wc_rev=9), 13022 'B/E/alpha' : Item(status=' ', wc_rev=9), 13023 'B/E/beta' : Item(status=' ', wc_rev=9), 13024 'B/lambda' : Item(status=' ', wc_rev=9), 13025 'B/F' : Item(status=' ', wc_rev=9), 13026 'C' : Item(status=' ', wc_rev=9), 13027 'D' : Item(status=' ', wc_rev=9), 13028 'D/G' : Item(status=' ', wc_rev=9), 13029 'D/G/pi' : Item(status=' ', wc_rev=9), 13030 'D/G/rho' : Item(status=' ', wc_rev=9), 13031 'D/G/tau' : Item(status=' ', wc_rev=9), 13032 'D/gamma' : Item(status=' ', wc_rev=9), 13033 'D/H' : Item(status=' ', wc_rev=9), 13034 'D/H/chi' : Item(status='M ', wc_rev=9), 13035 'D/H/psi' : Item(status=' ', wc_rev=9), 13036 'D/H/omega' : Item(status=' ', wc_rev=9), 13037 }) 13038 expected_disk = wc.State('', { 13039 '' : Item(props={SVN_PROP_MERGEINFO : '/A:7-8\n/A_COPY:2-9'}), 13040 'B' : Item(), 13041 'mu' : Item("This is the file 'mu'.\n"), 13042 'B/E' : Item(), 13043 'B/E/alpha' : Item("This is the file 'alpha'.\n"), 13044 'B/E/beta' : Item("New content"), 13045 'B/lambda' : Item("This is the file 'lambda'.\n"), 13046 'B/F' : Item(), 13047 'C' : Item(), 13048 'D' : Item(), 13049 'D/G' : Item(), 13050 'D/G/pi' : Item("This is the file 'pi'.\n"), 13051 'D/G/rho' : Item("New content"), 13052 'D/G/tau' : Item("This is the file 'tau'.\n"), 13053 'D/gamma' : Item("This is the file 'gamma'.\n"), 13054 'D/H' : Item(), 13055 'D/H/chi' : Item("New content"), 13056 'D/H/psi' : Item("New content"), 13057 'D/H/omega' : Item("New content"), 13058 }) 13059 expected_skip = wc.State(A_COPY_path, { }) 13060 svntest.actions.run_and_verify_merge(A_COPY_2_path, None, None, 13061 sbox.repo_url + '/A_COPY', None, 13062 expected_output, 13063 expected_mergeinfo_output, 13064 expected_elision_output, 13065 expected_disk, 13066 expected_status, 13067 expected_skip, 13068 check_props=True) 13069 13070#---------------------------------------------------------------------- 13071@SkipUnless(server_has_mergeinfo) 13072@Issue(3067) 13073def subtree_gets_changes_even_if_ultimately_deleted(sbox): 13074 "subtree gets changes even if ultimately deleted" 13075 13076 # merge_tests.py 101 'merge tries to delete a file of identical content' 13077 # demonstrates how a file can be deleted by a merge if the file is identical 13078 # to the file deleted in the merge source. If the file differs then it 13079 # should be 'skipped' as a tree-conflict. But suppose the file has 13080 # mergeinfo such that the requested merge should bring the file into a state 13081 # identical to the deleted source *before* attempting to delete it. Then the 13082 # file should get those changes first and then be deleted rather than skipped. 13083 # 13084 # This problem, as discussed here, 13085 # http://subversion.tigris.org/servlets/ReadMsg?listName=dev&msgNo=141533, 13086 # is only nominally a tree conflict issue. More accurately this is yet 13087 # another issue #3067 problem, in that the merge target has a subtree which 13088 # doesn't exist in part of the requested merge range. 13089 13090 # r1: Create a greek tree. 13091 sbox.build() 13092 wc_dir = sbox.wc_dir 13093 13094 # Some paths we'll care about 13095 H_COPY_path = sbox.ospath('A_COPY/D/H') 13096 psi_path = sbox.ospath('A/D/H/psi') 13097 psi_COPY_path = sbox.ospath('A_COPY/D/H/psi') 13098 13099 # r2 - r6: Copy A to A_COPY and then make some text changes under A. 13100 set_up_branch(sbox) 13101 13102 # r7: Make an additional text mod to A/D/H/psi. 13103 svntest.main.file_write(psi_path, "Even newer content") 13104 sbox.simple_commit(message='mod psi') 13105 13106 # r8: Delete A/D/H/psi. 13107 svntest.actions.run_and_verify_svn(None, [], 13108 'delete', psi_path) 13109 sbox.simple_commit(message='delete psi') 13110 13111 # Update WC before merging so mergeinfo elision and inheritance 13112 # occur smoothly. 13113 svntest.main.run_svn(None, 'up', wc_dir) 13114 13115 # r9: Merge r3,7 from A/D/H to A_COPY/D/H, then reverse merge r7 from 13116 # A/D/H/psi to A_COPY/D/H/psi. 13117 expected_output = wc.State(H_COPY_path, { 13118 'psi' : Item(status='G ', prev_status='U '), # Touched twice 13119 }) 13120 expected_mergeinfo_output = wc.State(H_COPY_path, { 13121 '' : Item(status=' G', prev_status=' U'), 13122 }) 13123 expected_elision_output = wc.State(H_COPY_path, { 13124 }) 13125 expected_status = wc.State(H_COPY_path, { 13126 '' : Item(status=' M', wc_rev=8), 13127 'psi' : Item(status='M ', wc_rev=8), 13128 'omega' : Item(status=' ', wc_rev=8), 13129 'chi' : Item(status=' ', wc_rev=8), 13130 }) 13131 expected_disk = wc.State('', { 13132 '' : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:3,7'}), 13133 'psi' : Item("Even newer content"), 13134 'omega' : Item("This is the file 'omega'.\n"), 13135 'chi' : Item("This is the file 'chi'.\n"), 13136 }) 13137 expected_skip = wc.State(H_COPY_path, { }) 13138 13139 svntest.actions.run_and_verify_merge(H_COPY_path, None, None, 13140 sbox.repo_url + '/A/D/H', None, 13141 expected_output, 13142 expected_mergeinfo_output, 13143 expected_elision_output, 13144 expected_disk, 13145 expected_status, expected_skip, 13146 [], True, False, 13147 '-c3,7', H_COPY_path) 13148 svntest.actions.run_and_verify_svn( 13149 expected_merge_output([[-7]], 13150 ['G ' + psi_COPY_path + '\n', 13151 ' G ' + psi_COPY_path + '\n',]), 13152 [], 'merge', '-c-7', sbox.repo_url + '/A/D/H/psi@7', psi_COPY_path) 13153 sbox.simple_commit(message='merge -c3,7 from A/D/H,' \ 13154 'reverse merge -c-7 from A/D/H/psi') 13155 13156 # Merge all available revisions from A/D/H to A_COPY/D/H. This merge 13157 # ultimately tries to delete A_COPY/D/H/psi, but first it should merge 13158 # r7 to A_COPY/D/H/psi, since that is one of the available revisions. 13159 # Then when merging the deletion of A_COPY/D/H/psi in r8 the file will 13160 # be identical to the deleted source A/D/H/psi and the deletion will 13161 # succeed. 13162 # 13163 # Update WC before merging so mergeinfo elision and inheritance 13164 # occur smoothly. 13165 svntest.main.run_svn(None, 'up', wc_dir) 13166 expected_output = wc.State(H_COPY_path, { 13167 'omega' : Item(status='U '), 13168 'psi' : Item(status='D ', prev_status='U '), 13169 }) 13170 expected_mergeinfo_output = wc.State(H_COPY_path, { 13171 '' : Item(status=' U'), 13172 }) 13173 expected_elision_output = wc.State(H_COPY_path, { 13174 }) 13175 expected_status = wc.State(H_COPY_path, { 13176 '' : Item(status=' M', wc_rev=9), 13177 'psi' : Item(status='D ', wc_rev=9), 13178 'omega' : Item(status='M ', wc_rev=9), 13179 'chi' : Item(status=' ', wc_rev=9), 13180 }) 13181 expected_disk = wc.State('', { 13182 '' : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:2-9'}), 13183 'omega' : Item("New content"), 13184 'chi' : Item("This is the file 'chi'.\n"), 13185 }) 13186 expected_skip = wc.State(H_COPY_path, { }) 13187 13188 svntest.actions.run_and_verify_merge(H_COPY_path, None, None, 13189 sbox.repo_url + '/A/D/H', None, 13190 expected_output, 13191 expected_mergeinfo_output, 13192 expected_elision_output, 13193 expected_disk, 13194 expected_status, expected_skip, 13195 [], True, False) 13196 13197#---------------------------------------------------------------------- 13198@SkipUnless(server_has_mergeinfo) 13199def no_self_referential_filtering_on_added_path(sbox): 13200 "no self referential filtering on added path" 13201 13202 sbox.build() 13203 wc_dir = sbox.wc_dir 13204 13205 # Some paths we'll care about 13206 C_COPY_path = sbox.ospath('A_COPY/C') 13207 A_path = sbox.ospath('A') 13208 C_path = sbox.ospath('A/C') 13209 A_COPY_2_path = sbox.ospath('A_COPY_2') 13210 13211 # r1-r7: Setup a 'trunk' and two 'branches'. 13212 wc_disk, wc_status = set_up_branch(sbox, False, 2) 13213 13214 # r8: Make a prop change on A_COPY/C. 13215 svntest.actions.run_and_verify_svn(["property 'propname' set on '" + 13216 C_COPY_path + "'\n"], [], 13217 'ps', 'propname', 'propval', 13218 C_COPY_path) 13219 expected_output = svntest.wc.State(wc_dir, 13220 {'A_COPY/C' : Item(verb='Sending')}) 13221 wc_status.tweak('A_COPY/C', wc_rev=8) 13222 wc_disk.tweak("A_COPY/C", 13223 props={'propname' : 'propval'}) 13224 svntest.actions.run_and_verify_commit(wc_dir, expected_output, wc_status) 13225 13226 # r9: Merge r8 from A_COPY to A. 13227 # 13228 # Update first to avoid an out of date error. 13229 svntest.actions.run_and_verify_svn(exp_noop_up_out(8), [], 'up', 13230 wc_dir) 13231 wc_status.tweak(wc_rev=8) 13232 svntest.actions.run_and_verify_svn( 13233 expected_merge_output([[8]], 13234 [' U ' + C_path + '\n', 13235 ' U ' + A_path + '\n',]), 13236 [], 'merge', '-c8', sbox.repo_url + '/A_COPY', A_path) 13237 expected_output = svntest.wc.State(wc_dir, 13238 {'A' : Item(verb='Sending'), 13239 'A/C' : Item(verb='Sending')}) 13240 wc_status.tweak('A', 'A/C', wc_rev=9) 13241 svntest.actions.run_and_verify_commit(wc_dir, expected_output, wc_status) 13242 13243 wc_disk.tweak("A/C", 13244 props={'propname' : 'propval'}) 13245 wc_disk.tweak("A", 13246 props={SVN_PROP_MERGEINFO : '/A_COPY:8'}) 13247 13248 # r10: Move A/C to A/C_MOVED. 13249 svntest.actions.run_and_verify_svn(['Committing transaction...\n', 13250 'Committed revision 10.\n'], 13251 [], 'move', 13252 sbox.repo_url + '/A/C', 13253 sbox.repo_url + '/A/C_MOVED', 13254 '-m', 'Copy A/C to A/C_MOVED') 13255 svntest.actions.run_and_verify_svn(None, [], 'up', 13256 wc_dir) 13257 13258 # Now try to merge all available revisions from A to A_COPY_2. 13259 # This should try to add the directory A_COPY_2/C_MOVED which has 13260 # explicit mergeinfo. This should not break self-referential mergeinfo 13261 # filtering logic...in fact there is no reason to even attempt such 13262 # filtering since the file is *new*. 13263 13264 expected_output = wc.State(A_COPY_2_path, { 13265 '' : Item(status=' U'), 13266 'B/E/beta' : Item(status='U '), 13267 'D/G/rho' : Item(status='U '), 13268 'D/H/psi' : Item(status='U '), 13269 'D/H/omega' : Item(status='U '), 13270 'C' : Item(status='D '), 13271 'C_MOVED' : Item(status='A '), 13272 }) 13273 # Why is C_MOVED notified as ' G' rather than ' U'? C_MOVED was 13274 # added by the merge and there is only a single editor drive, so 13275 # how can any prop changes be merged to it? The answer is that 13276 # the merge code does some quiet housekeeping, merging C_MOVED's 13277 # inherited mergeinfo into its incoming mergeinfo, see 13278 # https://issues.apache.org/jira/browse/SVN-4309 13279 # This test is not covering issue #4309 so we let the current 13280 # behavior pass. 13281 expected_mergeinfo_output = wc.State(A_COPY_2_path, { 13282 '' : Item(status=' G'), 13283 'C_MOVED' : Item(status=' G'), 13284 }) 13285 expected_elision_output = wc.State(A_COPY_2_path, { 13286 }) 13287 expected_A_COPY_2_status = wc.State(A_COPY_2_path, { 13288 '' : Item(status=' M', wc_rev=10), 13289 'B' : Item(status=' ', wc_rev=10), 13290 'mu' : Item(status=' ', wc_rev=10), 13291 'B/E' : Item(status=' ', wc_rev=10), 13292 'B/E/alpha' : Item(status=' ', wc_rev=10), 13293 'B/E/beta' : Item(status='M ', wc_rev=10), 13294 'B/lambda' : Item(status=' ', wc_rev=10), 13295 'B/F' : Item(status=' ', wc_rev=10), 13296 'C' : Item(status='D ', wc_rev=10), 13297 'C_MOVED' : Item(status='A ', wc_rev='-', copied='+'), 13298 'D' : Item(status=' ', wc_rev=10), 13299 'D/G' : Item(status=' ', wc_rev=10), 13300 'D/G/pi' : Item(status=' ', wc_rev=10), 13301 'D/G/rho' : Item(status='M ', wc_rev=10), 13302 'D/G/tau' : Item(status=' ', wc_rev=10), 13303 'D/gamma' : Item(status=' ', wc_rev=10), 13304 'D/H' : Item(status=' ', wc_rev=10), 13305 'D/H/chi' : Item(status=' ', wc_rev=10), 13306 'D/H/psi' : Item(status='M ', wc_rev=10), 13307 'D/H/omega' : Item(status='M ', wc_rev=10), 13308 }) 13309 expected_A_COPY_2_disk = wc.State('', { 13310 '' : Item(props={SVN_PROP_MERGEINFO : '/A:3-10\n/A_COPY:8'}), 13311 'B' : Item(), 13312 'mu' : Item("This is the file 'mu'.\n"), 13313 'B/E' : Item(), 13314 'B/E/alpha' : Item("This is the file 'alpha'.\n"), 13315 'B/E/beta' : Item("New content"), 13316 'B/lambda' : Item("This is the file 'lambda'.\n"), 13317 'B/F' : Item(), 13318 # What's up with the mergeinfo 13319 'C_MOVED' : Item(props={SVN_PROP_MERGEINFO : '/A/C_MOVED:10\n' + 13320 '/A_COPY/C:8\n' + 13321 '/A_COPY/C_MOVED:8', 13322 'propname' : 'propval'}), 13323 'D' : Item(), 13324 'D/G' : Item(), 13325 'D/G/pi' : Item("This is the file 'pi'.\n"), 13326 'D/G/rho' : Item("New content"), 13327 'D/G/tau' : Item("This is the file 'tau'.\n"), 13328 'D/gamma' : Item("This is the file 'gamma'.\n"), 13329 'D/H' : Item(), 13330 'D/H/chi' : Item("This is the file 'chi'.\n"), 13331 'D/H/psi' : Item("New content"), 13332 'D/H/omega' : Item("New content"), 13333 }) 13334 expected_A_COPY_2_skip = wc.State(A_COPY_2_path, { }) 13335 svntest.actions.run_and_verify_merge(A_COPY_2_path, None, None, 13336 sbox.repo_url + '/A', None, 13337 expected_output, 13338 expected_mergeinfo_output, 13339 expected_elision_output, 13340 expected_A_COPY_2_disk, 13341 expected_A_COPY_2_status, 13342 expected_A_COPY_2_skip, 13343 check_props=True) 13344 13345#---------------------------------------------------------------------- 13346# Test for issue #3324 13347# https://issues.apache.org/jira/browse/SVN-3324 13348@Issue(3324) 13349@SkipUnless(server_has_mergeinfo) 13350def merge_range_prior_to_rename_source_existence(sbox): 13351 "merge prior to rename src existence still dels src" 13352 13353 # Replicate a merge bug found while synching up a feature branch on the 13354 # Subversion repository with trunk. See r874121 of 13355 # http://svn.apache.org/repos/asf/subversion/branches/ignore-mergeinfo, in which 13356 # a move was merged to the target, but the delete half of the move 13357 # didn't occur. 13358 13359 sbox.build() 13360 wc_dir = sbox.wc_dir 13361 13362 # Some paths we'll care about 13363 nu_path = sbox.ospath('A/D/H/nu') 13364 nu_moved_path = sbox.ospath('A/D/H/nu_moved') 13365 A_path = sbox.ospath('A') 13366 alpha_path = sbox.ospath('A/B/E/alpha') 13367 A_COPY_path = sbox.ospath('A_COPY') 13368 A_COPY_2_path = sbox.ospath('A_COPY_2') 13369 B_COPY_path = sbox.ospath('A_COPY/B') 13370 B_COPY_2_path = sbox.ospath('A_COPY_2/B') 13371 alpha_COPY_path = sbox.ospath('A_COPY/B/E/alpha') 13372 beta_COPY_path = sbox.ospath('A_COPY/B/E/beta') 13373 gamma_COPY_path = sbox.ospath('A_COPY/D/gamma') 13374 rho_COPY_path = sbox.ospath('A_COPY/D/G/rho') 13375 omega_COPY_path = sbox.ospath('A_COPY/D/H/omega') 13376 psi_COPY_path = sbox.ospath('A_COPY/D/H/psi') 13377 nu_COPY_path = sbox.ospath('A_COPY/D/H/nu') 13378 13379 # Setup our basic 'trunk' and 'branch': 13380 # r2 - Copy A to A_COPY 13381 # r3 - Copy A to A_COPY_2 13382 # r4 - Text change to A/D/H/psi 13383 # r5 - Text change to A/D/G/rho 13384 # r6 - Text change to A/B/E/beta 13385 # r7 - Text change to A/D/H/omega 13386 wc_disk, wc_status = set_up_branch(sbox, False, 2) 13387 13388 # r8 - Text change to A/B/E/alpha 13389 svntest.main.file_write(alpha_path, "New content") 13390 wc_status.tweak('A/B/E/alpha', wc_rev=8) 13391 svntest.actions.run_and_verify_svn(None, [], 'ci', '-m', 13392 'Text change', wc_dir) 13393 13394 # r9 - Add the file A/D/H/nu and make another change to A/B/E/alpha. 13395 svntest.main.file_write(alpha_path, "Even newer content") 13396 svntest.main.file_write(nu_path, "This is the file 'nu'.\n") 13397 svntest.actions.run_and_verify_svn(None, [], 'add', nu_path) 13398 expected_output = wc.State(wc_dir, 13399 {'A/D/H/nu' : Item(verb='Adding'), 13400 'A/B/E/alpha' : Item(verb='Sending')}) 13401 wc_status.add({'A/D/H/nu' : Item(status=' ', wc_rev=9)}) 13402 wc_status.tweak('A/B/E/alpha', wc_rev=9) 13403 svntest.actions.run_and_verify_commit(wc_dir, expected_output, 13404 wc_status) 13405 13406 # r10 - Merge all available revisions (i.e. -r1:9) from A to A_COPY. 13407 svntest.actions.run_and_verify_svn(exp_noop_up_out(9), [], 'up', 13408 wc_dir) 13409 wc_status.tweak(wc_rev=9) 13410 svntest.actions.run_and_verify_svn( 13411 expected_merge_output([[2,9]], 13412 ['A ' + nu_COPY_path + '\n', 13413 'U ' + alpha_COPY_path + '\n', 13414 'U ' + beta_COPY_path + '\n', 13415 'U ' + rho_COPY_path + '\n', 13416 'U ' + omega_COPY_path + '\n', 13417 'U ' + psi_COPY_path + '\n', 13418 ' U ' + A_COPY_path + '\n',]), 13419 [], 'merge', sbox.repo_url + '/A', A_COPY_path) 13420 expected_output = wc.State(wc_dir, 13421 {'A_COPY' : Item(verb='Sending'), 13422 'A_COPY/D/H/nu' : Item(verb='Adding'), 13423 'A_COPY/B/E/alpha' : Item(verb='Sending'), 13424 'A_COPY/B/E/beta' : Item(verb='Sending'), 13425 'A_COPY/D/G/rho' : Item(verb='Sending'), 13426 'A_COPY/D/H/omega' : Item(verb='Sending'), 13427 'A_COPY/D/H/psi' : Item(verb='Sending')}) 13428 wc_status.tweak('A_COPY', 13429 'A_COPY/B/E/alpha', 13430 'A_COPY/B/E/beta', 13431 'A_COPY/D/G/rho', 13432 'A_COPY/D/H/omega', 13433 'A_COPY/D/H/psi', 13434 wc_rev=10) 13435 wc_status.add({'A_COPY/D/H/nu' : Item(status=' ', wc_rev=10)}) 13436 svntest.actions.run_and_verify_commit(wc_dir, expected_output, 13437 wc_status) 13438 13439 # r11 - Reverse merge -r9:1 from A/B to A_COPY/B 13440 svntest.actions.run_and_verify_svn(exp_noop_up_out(10), [], 'up', 13441 wc_dir) 13442 wc_status.tweak(wc_rev=10) 13443 svntest.actions.run_and_verify_svn( 13444 expected_merge_output([[9,2]], ['U ' + alpha_COPY_path + '\n', 13445 'U ' + beta_COPY_path + '\n', 13446 ' G ' + B_COPY_path + '\n',]), 13447 [], 'merge', sbox.repo_url + '/A/B', B_COPY_path, '-r9:1') 13448 expected_output = wc.State(wc_dir, 13449 {'A_COPY/B' : Item(verb='Sending'), 13450 'A_COPY/B/E/alpha' : Item(verb='Sending'), 13451 'A_COPY/B/E/beta' : Item(verb='Sending')}) 13452 wc_status.tweak('A_COPY/B', 13453 'A_COPY/B/E/alpha', 13454 'A_COPY/B/E/beta', 13455 wc_rev=11) 13456 svntest.actions.run_and_verify_commit(wc_dir, expected_output, 13457 wc_status) 13458 13459 # r12 - Move A/D/H/nu to A/D/H/nu_moved 13460 svntest.actions.run_and_verify_svn(["Committing transaction...\n", 13461 "Committed revision 12.\n"], [], 13462 'move', sbox.repo_url + '/A/D/H/nu', 13463 sbox.repo_url + '/A/D/H/nu_moved', 13464 '-m', 'Move nu to nu_moved') 13465 expected_output = svntest.verify.UnorderedOutput( 13466 ["Updating '%s':\n" % (wc_dir), 13467 "D " + nu_path + "\n", 13468 "A " + nu_moved_path + "\n", 13469 "Updated to revision 12.\n"], 13470 ) 13471 svntest.actions.run_and_verify_svn(expected_output, 13472 [], 'up', wc_dir) 13473 13474 # Now merge -r7:12 from A to A_COPY. 13475 # A_COPY needs only -r10:12, which amounts to the rename of nu. 13476 # The subtree A_COPY/B needs the entire range -r7:12 because of 13477 # the reverse merge we performed in r11; the only operative change 13478 # here is the text mod to alpha made in r9. 13479 # 13480 # This merge previously failed because the delete half of the A_COPY/D/H/nu 13481 # to A_COPY/D/H/nu_moved move was reported in the notifications, but didn't 13482 # actually happen. 13483 expected_output = wc.State(A_COPY_path, { 13484 'B/E/alpha' : Item(status='U '), 13485 'D/H/nu' : Item(status='D '), 13486 'D/H/nu_moved' : Item(status='A '), 13487 }) 13488 expected_mergeinfo_output = wc.State(A_COPY_path, { 13489 '' : Item(status=' U'), 13490 'B' : Item(status=' U'), 13491 }) 13492 expected_elision_output = wc.State(A_COPY_path, { 13493 }) 13494 expected_status = wc.State(A_COPY_path, { 13495 '' : Item(status=' M', wc_rev=12), 13496 'B' : Item(status=' M', wc_rev=12), 13497 'mu' : Item(status=' ', wc_rev=12), 13498 'B/E' : Item(status=' ', wc_rev=12), 13499 'B/E/alpha' : Item(status='M ', wc_rev=12), 13500 'B/E/beta' : Item(status=' ', wc_rev=12), 13501 'B/lambda' : Item(status=' ', wc_rev=12), 13502 'B/F' : Item(status=' ', wc_rev=12), 13503 'C' : Item(status=' ', wc_rev=12), 13504 'D' : Item(status=' ', wc_rev=12), 13505 'D/G' : Item(status=' ', wc_rev=12), 13506 'D/G/pi' : Item(status=' ', wc_rev=12), 13507 'D/G/rho' : Item(status=' ', wc_rev=12), 13508 'D/G/tau' : Item(status=' ', wc_rev=12), 13509 'D/gamma' : Item(status=' ', wc_rev=12), 13510 'D/H' : Item(status=' ', wc_rev=12), 13511 'D/H/nu' : Item(status='D ', wc_rev=12), 13512 'D/H/nu_moved' : Item(status='A ', wc_rev='-', copied='+'), 13513 'D/H/chi' : Item(status=' ', wc_rev=12), 13514 'D/H/psi' : Item(status=' ', wc_rev=12), 13515 'D/H/omega' : Item(status=' ', wc_rev=12), 13516 }) 13517 expected_disk = wc.State('', { 13518 '' : Item(props={SVN_PROP_MERGEINFO : '/A:2-12'}), 13519 'mu' : Item("This is the file 'mu'.\n"), 13520 'B' : Item(props={SVN_PROP_MERGEINFO : '/A/B:8-12'}), 13521 'B/E' : Item(), 13522 'B/E/alpha' : Item("Even newer content"), 13523 'B/E/beta' : Item("This is the file 'beta'.\n"), 13524 'B/lambda' : Item("This is the file 'lambda'.\n"), 13525 'B/F' : Item(), 13526 'C' : Item(), 13527 'D' : Item(), 13528 'D/G' : Item(), 13529 'D/G/pi' : Item("This is the file 'pi'.\n"), 13530 'D/G/rho' : Item("New content"), 13531 'D/G/tau' : Item("This is the file 'tau'.\n"), 13532 'D/gamma' : Item("This is the file 'gamma'.\n"), 13533 'D/H' : Item(), 13534 'D/H/nu_moved' : Item("This is the file 'nu'.\n"), 13535 'D/H/chi' : Item("This is the file 'chi'.\n"), 13536 'D/H/psi' : Item("New content"), 13537 'D/H/omega' : Item("New content"), 13538 }) 13539 expected_skip = wc.State(A_COPY_path, {}) 13540 svntest.actions.run_and_verify_merge(A_COPY_path, 7, 12, 13541 sbox.repo_url + '/A', None, 13542 expected_output, 13543 expected_mergeinfo_output, 13544 expected_elision_output, 13545 expected_disk, 13546 expected_status, 13547 expected_skip, 13548 check_props=True) 13549 svntest.actions.run_and_verify_svn(None, [], 'ci', '-m', 13550 'Merge -r7:12 from A to A_COPY', wc_dir) 13551 13552 # Now run a similar scenario as above on the second branch, but with 13553 # a reverse merge this time. 13554 # 13555 # r14 - Merge all available revisions from A/B to A_COPY_B and then merge 13556 # -r2:9 from A to A_COPY_2. Among other things, this adds A_COPY_2/D/H/nu 13557 # and leaves us with mergeinfo on the A_COPY_2 branch of: 13558 # 13559 # Properties on 'A_COPY_2': 13560 # svn:mergeinfo 13561 # /A:3-9 13562 # Properties on 'A_COPY_2\B': 13563 # svn:mergeinfo 13564 # /A/B:3-13 13565 svntest.actions.run_and_verify_svn(exp_noop_up_out(13), [], 'up', 13566 wc_dir) 13567 svntest.actions.run_and_verify_svn(None, # Don't check stdout, we test this 13568 # type of merge to death elsewhere. 13569 [], 'merge', sbox.repo_url + '/A/B', 13570 B_COPY_2_path) 13571 svntest.actions.run_and_verify_svn(None,[], 'merge', '-r', '2:9', 13572 sbox.repo_url + '/A', A_COPY_2_path) 13573 svntest.actions.run_and_verify_svn( 13574 None, [], 'ci', '-m', 13575 'Merge all from A/B to A_COPY_2/B\nMerge -r2:9 from A to A_COPY_2', 13576 wc_dir) 13577 svntest.actions.run_and_verify_svn(exp_noop_up_out(14), [], 'up', 13578 wc_dir) 13579 13580 # Now reverse merge -r13:7 from A to A_COPY_2. 13581 # 13582 # Recall: 13583 # 13584 # >svn log -r8:13 ^/A -v 13585 # ------------------------------------------------------------------------ 13586 # r8 | jrandom | 2010-10-14 11:25:59 -0400 (Thu, 14 Oct 2010) | 1 line 13587 # Changed paths: 13588 # M /A/B/E/alpha 13589 # 13590 # Text change 13591 # ------------------------------------------------------------------------ 13592 # r9 | jrandom | 2010-10-14 11:25:59 -0400 (Thu, 14 Oct 2010) | 1 line 13593 # Changed paths: 13594 # M /A/B/E/alpha 13595 # A /A/D/H/nu 13596 # 13597 # log msg 13598 # ------------------------------------------------------------------------ 13599 # r12 | jrandom | 2010-10-14 11:26:01 -0400 (Thu, 14 Oct 2010) | 1 line 13600 # Changed paths: 13601 # D /A/D/H/nu 13602 # A /A/D/H/nu_moved (from /A/D/H/nu:11) 13603 # 13604 # Move nu to nu_moved 13605 # ------------------------------------------------------------------------ 13606 # 13607 # We can only reverse merge changes from the explicit mergeinfo or 13608 # natural history of a target, but since all of these changes intersect with 13609 # the target's explicit mergeinfo (including subtrees), all should be 13610 # reverse merged, including the deletion of A_COPY/D/H/nu. Like the forward 13611 # merge performed earlier, this test previously failed when A_COPY/D/H/nu 13612 # was reported as deleted, but still remained as a versioned item in the WC. 13613 expected_output = wc.State(A_COPY_2_path, { 13614 'B/E/alpha' : Item(status='U '), 13615 'D/H/nu' : Item(status='D '), 13616 }) 13617 expected_mergeinfo_output = wc.State(A_COPY_2_path, { 13618 '' : Item(status=' U'), 13619 'B' : Item(status=' U'), 13620 }) 13621 expected_elision_output = wc.State(A_COPY_2_path, { 13622 'B' : Item(status=' U'), 13623 }) 13624 expected_status = wc.State(A_COPY_2_path, { 13625 '' : Item(status=' M'), 13626 'B' : Item(status=' M'), 13627 'mu' : Item(status=' '), 13628 'B/E' : Item(status=' '), 13629 'B/E/alpha' : Item(status='M '), 13630 'B/E/beta' : Item(status=' '), 13631 'B/lambda' : Item(status=' '), 13632 'B/F' : Item(status=' '), 13633 'C' : Item(status=' '), 13634 'D' : Item(status=' '), 13635 'D/G' : Item(status=' '), 13636 'D/G/pi' : Item(status=' '), 13637 'D/G/rho' : Item(status=' '), 13638 'D/G/tau' : Item(status=' '), 13639 'D/gamma' : Item(status=' '), 13640 'D/H' : Item(status=' '), 13641 'D/H/nu' : Item(status='D '), 13642 'D/H/chi' : Item(status=' '), 13643 'D/H/psi' : Item(status=' '), 13644 'D/H/omega' : Item(status=' '), 13645 }) 13646 expected_status.tweak(wc_rev=14) 13647 expected_disk = wc.State('', { 13648 '' : Item(props={SVN_PROP_MERGEINFO : '/A:3-7'}), 13649 'mu' : Item("This is the file 'mu'.\n"), 13650 'B' : Item(), 13651 'B/E' : Item(), 13652 'B/E/alpha' : Item("This is the file 'alpha'.\n"), 13653 'B/E/beta' : Item("New content"), 13654 'B/lambda' : Item("This is the file 'lambda'.\n"), 13655 'B/F' : Item(), 13656 'C' : Item(), 13657 'D' : Item(), 13658 'D/G' : Item(), 13659 'D/G/pi' : Item("This is the file 'pi'.\n"), 13660 'D/G/rho' : Item("New content"), 13661 'D/G/tau' : Item("This is the file 'tau'.\n"), 13662 'D/gamma' : Item("This is the file 'gamma'.\n"), 13663 'D/H' : Item(), 13664 'D/H/chi' : Item("This is the file 'chi'.\n"), 13665 'D/H/psi' : Item("New content"), 13666 'D/H/omega' : Item("New content"), 13667 }) 13668 expected_skip = wc.State(A_COPY_path, {}) 13669 svntest.actions.run_and_verify_merge(A_COPY_2_path, 13, 7, 13670 sbox.repo_url + '/A', None, 13671 expected_output, 13672 expected_mergeinfo_output, 13673 expected_elision_output, 13674 expected_disk, 13675 expected_status, 13676 expected_skip, 13677 [], True, True) 13678 13679#---------------------------------------------------------------------- 13680def set_up_natural_history_gap(sbox): 13681 '''Starting with standard greek tree, do the following: 13682 r2 - A/D/H/psi 13683 r3 - A/D/G/rho 13684 r4 - A/B/E/beta 13685 r5 - A/D/H/omega 13686 r6 - Delete A 13687 r7 - "Resurrect" A, by copying A@2 to A 13688 r8 - Copy A to A_COPY 13689 r9 - Text mod to A/D/gamma 13690 Lastly it updates the WC to r9. 13691 All text mods set file contents to "New content". 13692 Return (expected_disk, expected_status).''' 13693 13694 # r1: Create a standard greek tree. 13695 sbox.build() 13696 wc_dir = sbox.wc_dir 13697 13698 # r2-5: Make some changes under 'A' (no branches yet). 13699 wc_disk, wc_status = set_up_branch(sbox, False, 0) 13700 13701 # Some paths we'll care about. 13702 A_COPY_path = sbox.ospath('A_COPY') 13703 gamma_path = sbox.ospath('A/D/gamma') 13704 13705 # r6: Delete 'A' 13706 exit_code, out, err = svntest.actions.run_and_verify_svn( 13707 ["Committing transaction...\n", 13708 "Committed revision 6.\n"], [], 13709 'delete', sbox.repo_url + '/A', '-m', 'Delete A') 13710 13711 # r7: Resurrect 'A' by copying 'A@2' to 'A'. 13712 exit_code, out, err = svntest.actions.run_and_verify_svn( 13713 ["Committing transaction...\n", 13714 "Committed revision 7.\n"], [], 13715 'copy', sbox.repo_url + '/A@2', sbox.repo_url + '/A', 13716 '-m', 'Resurrect A from A@2') 13717 13718 # r8: Branch the resurrected 'A' to 'A_COPY'. 13719 exit_code, out, err = svntest.actions.run_and_verify_svn( 13720 ["Committing transaction...\n", 13721 "Committed revision 8.\n"], [], 13722 'copy', sbox.repo_url + '/A', sbox.repo_url + '/A_COPY', 13723 '-m', 'Copy A to A_COPY') 13724 13725 # Update to bring all the repos side changes down. 13726 exit_code, out, err = svntest.actions.run_and_verify_svn(None, [], 13727 'up', wc_dir) 13728 wc_status.add({ 13729 "A_COPY/B" : Item(status=' '), 13730 "A_COPY/B/lambda" : Item(status=' '), 13731 "A_COPY/B/E" : Item(status=' '), 13732 "A_COPY/B/E/alpha" : Item(status=' '), 13733 "A_COPY/B/E/beta" : Item(status=' '), 13734 "A_COPY/B/F" : Item(status=' '), 13735 "A_COPY/mu" : Item(status=' '), 13736 "A_COPY/C" : Item(status=' '), 13737 "A_COPY/D" : Item(status=' '), 13738 "A_COPY/D/gamma" : Item(status=' '), 13739 "A_COPY/D/G" : Item(status=' '), 13740 "A_COPY/D/G/pi" : Item(status=' '), 13741 "A_COPY/D/G/rho" : Item(status=' '), 13742 "A_COPY/D/G/tau" : Item(status=' '), 13743 "A_COPY/D/H" : Item(status=' '), 13744 "A_COPY/D/H/chi" : Item(status=' '), 13745 "A_COPY/D/H/omega" : Item(status=' '), 13746 "A_COPY/D/H/psi" : Item(status=' '), 13747 "A_COPY" : Item(status=' ')}) 13748 wc_status.tweak(wc_rev=8) 13749 13750 # r9: Make a text change to 'A/D/gamma'. 13751 svntest.main.file_write(gamma_path, "New content") 13752 expected_output = wc.State(wc_dir, {'A/D/gamma' : Item(verb='Sending')}) 13753 wc_status.tweak('A/D/gamma', wc_rev=9) 13754 13755 # Update the WC to a uniform revision. 13756 svntest.actions.run_and_verify_commit(wc_dir, expected_output, 13757 wc_status) 13758 svntest.actions.run_and_verify_svn(exp_noop_up_out(9), [], 13759 'up', wc_dir) 13760 return wc_disk, wc_status 13761 13762#---------------------------------------------------------------------- 13763@SkipUnless(server_has_mergeinfo) 13764def dont_merge_gaps_in_history(sbox): 13765 "mergeinfo aware merges ignore natural history gaps" 13766 13767 ## See http://svn.haxx.se/dev/archive-2008-11/0618.shtml ## 13768 13769 wc_dir = sbox.wc_dir 13770 13771 # Create a branch with gaps in its natural history. 13772 set_up_natural_history_gap(sbox) 13773 13774 # Some paths we'll care about. 13775 A_COPY_path = sbox.ospath('A_COPY') 13776 13777 # Now merge all available changes from 'A' to 'A_COPY'. The only 13778 # available revisions are r8 and r9. Only r9 effects the source/target 13779 # so this merge should change 'A/D/gamma' from r9. The fact that 'A_COPY' 13780 # has 'broken' natural history, i.e. 13781 # 13782 # /A:2,7 <-- Recall 'A@7' was copied from 'A@2'. 13783 # /A_COPY:8-9 13784 # 13785 # should have no impact, but currently this fact is causing a failure: 13786 # 13787 # >svn merge %url127%/A merge_tests-127\A_COPY 13788 # ..\..\..\subversion\libsvn_repos\reporter.c:1162: (apr_err=160005) 13789 # svn: Target path '/A' does not exist. 13790 expected_output = wc.State(A_COPY_path, { 13791 'D/gamma' : Item(status='U '), 13792 }) 13793 expected_mergeinfo_output = wc.State(A_COPY_path, { 13794 '' : Item(status=' U'), 13795 }) 13796 expected_elision_output = wc.State(A_COPY_path, { 13797 }) 13798 expected_status = wc.State(A_COPY_path, { 13799 '' : Item(status=' M'), 13800 'B' : Item(status=' '), 13801 'mu' : Item(status=' '), 13802 'B/E' : Item(status=' '), 13803 'B/E/alpha' : Item(status=' '), 13804 'B/E/beta' : Item(status=' '), 13805 'B/lambda' : Item(status=' '), 13806 'B/F' : Item(status=' '), 13807 'C' : Item(status=' '), 13808 'D' : Item(status=' '), 13809 'D/G' : Item(status=' '), 13810 'D/G/pi' : Item(status=' '), 13811 'D/G/rho' : Item(status=' '), 13812 'D/G/tau' : Item(status=' '), 13813 'D/gamma' : Item(status='M '), 13814 'D/H' : Item(status=' '), 13815 'D/H/chi' : Item(status=' '), 13816 'D/H/psi' : Item(status=' '), 13817 'D/H/omega' : Item(status=' '), 13818 }) 13819 expected_status.tweak(wc_rev=9) 13820 expected_disk = wc.State('', { 13821 '' : Item(props={SVN_PROP_MERGEINFO : '/A:8-9'}), 13822 'B' : Item(), 13823 'mu' : Item("This is the file 'mu'.\n"), 13824 'B/E' : Item(), 13825 'B/E/alpha' : Item("This is the file 'alpha'.\n"), 13826 'B/E/beta' : Item("This is the file 'beta'.\n"), 13827 'B/lambda' : Item("This is the file 'lambda'.\n"), 13828 'B/F' : Item(), 13829 'C' : Item(), 13830 'D' : Item(), 13831 'D/G' : Item(), 13832 'D/G/pi' : Item("This is the file 'pi'.\n"), 13833 'D/G/rho' : Item("This is the file 'rho'.\n"), 13834 'D/G/tau' : Item("This is the file 'tau'.\n"), 13835 'D/gamma' : Item("New content"), 13836 'D/H' : Item(), 13837 'D/H/chi' : Item("This is the file 'chi'.\n"), 13838 'D/H/psi' : Item("New content"), 13839 'D/H/omega' : Item("This is the file 'omega'.\n"), 13840 }) 13841 expected_skip = wc.State(A_COPY_path, { }) 13842 svntest.actions.run_and_verify_merge(A_COPY_path, None, None, 13843 sbox.repo_url + '/A', None, 13844 expected_output, 13845 expected_mergeinfo_output, 13846 expected_elision_output, 13847 expected_disk, 13848 expected_status, 13849 expected_skip, 13850 check_props=True) 13851 13852#---------------------------------------------------------------------- 13853# Test for issue #3432 'Merge can record mergeinfo from natural history 13854# gaps'. See https://issues.apache.org/jira/browse/SVN-3432 13855@Issue(3432) 13856@SkipUnless(server_has_mergeinfo) 13857def handle_gaps_in_implicit_mergeinfo(sbox): 13858 "correctly consider natural history gaps" 13859 13860 wc_dir = sbox.wc_dir 13861 13862 # Create a branch with gaps in its natural history. 13863 # 13864 # r1--------r2--------r3--------r4--------r5--------r6 13865 # Add 'A' edit edit edit edit Delete A 13866 # psi rho beta omega 13867 # | 13868 # V 13869 # r7--------r9-----------------> 13870 # Rez 'A' edit 13871 # | gamma 13872 # | 13873 # V 13874 # r8---------------------------> 13875 # Copy 'A@7' to 13876 # 'A_COPY' 13877 # 13878 expected_disk, expected_status = set_up_natural_history_gap(sbox) 13879 13880 # Some paths we'll care about. 13881 A_COPY_path = sbox.ospath('A_COPY') 13882 13883 # Merge r4 to 'A_COPY' from A@4, which is *not* part of A_COPY's history. 13884 expected_output = wc.State(A_COPY_path, { 13885 'B/E/beta' : Item(status='U '), 13886 }) 13887 expected_mergeinfo_output = wc.State(A_COPY_path, { 13888 '' : Item(status=' U'), 13889 }) 13890 expected_elision_output = wc.State(A_COPY_path, { 13891 }) 13892 expected_status = wc.State(A_COPY_path, { 13893 '' : Item(status=' M'), 13894 'B' : Item(status=' '), 13895 'mu' : Item(status=' '), 13896 'B/E' : Item(status=' '), 13897 'B/E/alpha' : Item(status=' '), 13898 'B/E/beta' : Item(status='M '), 13899 'B/lambda' : Item(status=' '), 13900 'B/F' : Item(status=' '), 13901 'C' : Item(status=' '), 13902 'D' : Item(status=' '), 13903 'D/G' : Item(status=' '), 13904 'D/G/pi' : Item(status=' '), 13905 'D/G/rho' : Item(status=' '), 13906 'D/G/tau' : Item(status=' '), 13907 'D/gamma' : Item(status=' '), 13908 'D/H' : Item(status=' '), 13909 'D/H/chi' : Item(status=' '), 13910 'D/H/psi' : Item(status=' '), 13911 'D/H/omega' : Item(status=' '), 13912 }) 13913 expected_status.tweak(wc_rev=9) 13914 expected_disk = wc.State('', { 13915 '' : Item(props={SVN_PROP_MERGEINFO : '/A:4'}), 13916 'B' : Item(), 13917 'mu' : Item("This is the file 'mu'.\n"), 13918 'B/E' : Item(), 13919 'B/E/alpha' : Item("This is the file 'alpha'.\n"), 13920 'B/E/beta' : Item("New content"), # From the merge of A@4 13921 'B/lambda' : Item("This is the file 'lambda'.\n"), 13922 'B/F' : Item(), 13923 'C' : Item(), 13924 'D' : Item(), 13925 'D/G' : Item(), 13926 'D/G/pi' : Item("This is the file 'pi'.\n"), 13927 'D/G/rho' : Item("This is the file 'rho'.\n"), 13928 'D/G/tau' : Item("This is the file 'tau'.\n"), 13929 'D/gamma' : Item("This is the file 'gamma'.\n"), 13930 'D/H' : Item(), 13931 'D/H/chi' : Item("This is the file 'chi'.\n"), 13932 'D/H/psi' : Item("New content"), # From A@2 13933 'D/H/omega' : Item("This is the file 'omega'.\n"), 13934 }) 13935 expected_skip = wc.State(A_COPY_path, { }) 13936 svntest.actions.run_and_verify_merge(A_COPY_path, 3, 4, 13937 sbox.repo_url + '/A@4', None, 13938 expected_output, 13939 expected_mergeinfo_output, 13940 expected_elision_output, 13941 expected_disk, 13942 expected_status, 13943 expected_skip, 13944 check_props=True) 13945 13946 # Now reverse merge -r9:2 from 'A@HEAD' to 'A_COPY'. This should be 13947 # a no-op since the only operative change made on 'A@HEAD' between r2:9 13948 # is the text mod to 'A/D/gamma' made in r9, but since that was after 13949 # 'A_COPY' was copied from 'A 'and that change was never merged, we don't 13950 # try to reverse merge it. 13951 # 13952 # Also, the mergeinfo recorded by the previous merge, i.e. '/A:4', should 13953 # *not* be removed! A@4 is not on the same line of history as 'A@9'. 13954 expected_output = wc.State(A_COPY_path, {}) 13955 expected_mergeinfo_output = wc.State(A_COPY_path, { 13956 '' : Item(status=' G'), 13957 }) 13958 svntest.actions.run_and_verify_merge(A_COPY_path, 9, 2, 13959 sbox.repo_url + '/A', None, 13960 expected_output, 13961 expected_mergeinfo_output, 13962 expected_elision_output, 13963 expected_disk, 13964 expected_status, 13965 expected_skip, 13966 check_props=True) 13967 13968 # Now merge all available revisions from 'A' to 'A_COPY'. 13969 # The mergeinfo '/A:4' on 'A_COPY' should have no impact on this merge 13970 # since it refers to another line of history. Since 'A_COPY' was copied 13971 # from 'A@7' the only available revisions are r8 and r9. 13972 expected_output = wc.State(A_COPY_path, { 13973 'D/gamma' : Item(status='U '), 13974 }) 13975 expected_status.tweak('D/gamma', status='M ') 13976 expected_disk.tweak('D/gamma', contents='New content') 13977 expected_disk.tweak('', props={SVN_PROP_MERGEINFO : '/A:4,8-9'}) 13978 svntest.actions.run_and_verify_merge(A_COPY_path, None, None, 13979 sbox.repo_url + '/A', None, 13980 expected_output, 13981 expected_mergeinfo_output, 13982 expected_elision_output, 13983 expected_disk, 13984 expected_status, 13985 expected_skip, 13986 check_props=True) 13987 13988#---------------------------------------------------------------------- 13989# Test for issue #3323 'Mergeinfo deleted by a merge should disappear' 13990@Issue(3323) 13991@SkipUnless(server_has_mergeinfo) 13992def mergeinfo_deleted_by_a_merge_should_disappear(sbox): 13993 "mergeinfo deleted by a merge should disappear" 13994 13995 13996 # r1: Create a greek tree. 13997 sbox.build() 13998 wc_dir = sbox.wc_dir 13999 14000 # Some paths we'll care about 14001 D_COPY_path = sbox.ospath('A_COPY/D') 14002 A_COPY_path = sbox.ospath('A_COPY') 14003 A_COPY_2_path = sbox.ospath('A_COPY_2') 14004 14005 # r2 - r6: Copy A to A_COPY and then make some text changes under A. 14006 wc_disk, wc_status = set_up_branch(sbox) 14007 14008 # r7: Merge all available revisions from A/D to A_COPY/D, this creates 14009 # mergeinfo on A_COPY/D. 14010 svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir) 14011 svntest.actions.run_and_verify_svn(None, # Don't check stdout, we test this 14012 # type of merge to death elsewhere. 14013 [], 'merge', sbox.repo_url + '/A/D', 14014 D_COPY_path) 14015 svntest.actions.run_and_verify_svn( 14016 None, [], 'ci', '-m', 14017 'Merge all available revisions from A/D to A_COPY/D', wc_dir) 14018 14019 # r8: Copy A_COPY to A_COPY_2, this carries the mergeinf on A_COPY/D 14020 # to A_COPY_2/D. 14021 svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir) 14022 svntest.actions.run_and_verify_svn(None,[], 14023 'copy', A_COPY_path, A_COPY_2_path) 14024 svntest.actions.run_and_verify_svn(None, [], 'ci', '-m', 14025 'Copy A_COPY to A_COPY_2', wc_dir) 14026 14027 # r9: Propdel the mergeinfo on A_COPY/D. 14028 svntest.actions.run_and_verify_svn(None,[], 14029 'pd', SVN_PROP_MERGEINFO, D_COPY_path) 14030 svntest.actions.run_and_verify_svn(None, [], 'ci', '-m', 14031 'Propdel the mergeinfo on A_COPY/D', 14032 wc_dir) 14033 14034 # r10: Merge r5 from A to A_COPY_2 so the latter gets some explicit 14035 # mergeinfo. 14036 svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir) 14037 svntest.actions.run_and_verify_svn(None, [], 'merge', '-c5', 14038 sbox.repo_url + '/A', A_COPY_2_path) 14039 svntest.actions.run_and_verify_svn(None, [], 'ci', '-m', 14040 'Merge r5 from A to A_COPY_2', wc_dir) 14041 14042 # Now merge r9 from A_COPY to A_COPY_2. Since the merge itself cleanly 14043 # removes all explicit mergeinfo from A_COPY_2/D, we should not set any 14044 # mergeinfo on that subtree describing the merge. 14045 svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir) 14046 expected_output = wc.State(A_COPY_2_path, { 14047 'D' : Item(status=' U'), 14048 }) 14049 expected_mergeinfo_output = wc.State(A_COPY_2_path, { 14050 '' : Item(status=' U'), 14051 }) 14052 expected_elision_output = wc.State(A_COPY_2_path, { 14053 }) 14054 expected_status = wc.State(A_COPY_2_path, { 14055 '' : Item(status=' M'), 14056 'B' : Item(status=' '), 14057 'mu' : Item(status=' '), 14058 'B/E' : Item(status=' '), 14059 'B/E/alpha' : Item(status=' '), 14060 'B/E/beta' : Item(status=' '), 14061 'B/lambda' : Item(status=' '), 14062 'B/F' : Item(status=' '), 14063 'C' : Item(status=' '), 14064 'D' : Item(status=' M'), 14065 'D/G' : Item(status=' '), 14066 'D/G/pi' : Item(status=' '), 14067 'D/G/rho' : Item(status=' '), 14068 'D/G/tau' : Item(status=' '), 14069 'D/gamma' : Item(status=' '), 14070 'D/H' : Item(status=' '), 14071 'D/H/chi' : Item(status=' '), 14072 'D/H/psi' : Item(status=' '), 14073 'D/H/omega' : Item(status=' '), 14074 }) 14075 expected_status.tweak(wc_rev=10) 14076 expected_disk = wc.State('', { 14077 '' : Item(props={SVN_PROP_MERGEINFO : '/A:5\n/A_COPY:9'}), 14078 'B' : Item(), 14079 'mu' : Item("This is the file 'mu'.\n"), 14080 'B/E' : Item(), 14081 'B/E/alpha' : Item("This is the file 'alpha'.\n"), 14082 'B/E/beta' : Item("New content"), 14083 'B/lambda' : Item("This is the file 'lambda'.\n"), 14084 'B/F' : Item(), 14085 'C' : Item(), 14086 'D' : Item(), 14087 'D/G' : Item(), 14088 'D/G/pi' : Item("This is the file 'pi'.\n"), 14089 'D/G/rho' : Item("New content"), 14090 'D/G/tau' : Item("This is the file 'tau'.\n"), 14091 'D/gamma' : Item("This is the file 'gamma'.\n"), 14092 'D/H' : Item(), 14093 'D/H/chi' : Item("This is the file 'chi'.\n"), 14094 'D/H/psi' : Item("New content"), 14095 'D/H/omega' : Item("New content"), 14096 }) 14097 expected_skip = wc.State(A_COPY_path, { }) 14098 svntest.actions.run_and_verify_merge(A_COPY_2_path, '8', '9', 14099 sbox.repo_url + '/A_COPY', None, 14100 expected_output, 14101 expected_mergeinfo_output, 14102 expected_elision_output, 14103 expected_disk, 14104 expected_status, 14105 expected_skip, 14106 check_props=True) 14107 14108#---------------------------------------------------------------------- 14109# File merge optimization caused segfault during noop file merge 14110# when multiple ranges are eligible for merge, see 14111# http://svn.haxx.se/dev/archive-2009-05/0363.shtml 14112@SkipUnless(server_has_mergeinfo) 14113def noop_file_merge(sbox): 14114 "noop file merge does not segfault" 14115 14116 # r1: Create a greek tree. 14117 sbox.build() 14118 wc_dir = sbox.wc_dir 14119 14120 # Some paths we'll care about 14121 A_COPY_path = sbox.ospath('A_COPY') 14122 beta_COPY_path = sbox.ospath('A_COPY/B/E/beta') 14123 chi_COPY_path = sbox.ospath('A_COPY/D/H/chi') 14124 14125 # r2 - r6: Copy A to A_COPY and then make some text changes under A. 14126 wc_disk, wc_status = set_up_branch(sbox) 14127 14128 # Merge r5 from A to A_COPY and commit as r7. This will split the 14129 # eligible ranges to be merged to A_COPY/D/H/chi into two discrete 14130 # sets: r1-4 and r5-HEAD 14131 svntest.actions.run_and_verify_svn( 14132 expected_merge_output([[5]], 14133 ['U ' + beta_COPY_path + '\n', 14134 ' U ' + A_COPY_path + '\n',]), 14135 [], 'merge', '-c5', sbox.repo_url + '/A', A_COPY_path) 14136 svntest.actions.run_and_verify_svn(None, [], 'commit', '-m', 14137 'Merge r5 from A to A_COPY', 14138 wc_dir) 14139 14140 # Update working copy to allow full inheritance and elision. 14141 svntest.actions.run_and_verify_svn(exp_noop_up_out(7), [], 14142 'up', wc_dir) 14143 14144 # Merge all available revisions from A/D/H/chi to A_COPY/D/H/chi. 14145 # There are no operative changes in the source, so this should 14146 # not produce any output other than mergeinfo updates on 14147 # A_COPY/D/H/chi. This is where the segfault occurred. 14148 svntest.actions.run_and_verify_svn(None, [], 'merge', 14149 sbox.repo_url + '/A/D/H/chi', 14150 chi_COPY_path) 14151 svntest.actions.run_and_verify_svn([' M ' + chi_COPY_path + '\n'], 14152 [], 'st', chi_COPY_path) 14153 svntest.actions.run_and_verify_svn(['/A/D/H/chi:2-7\n'], 14154 [], 'pg', SVN_PROP_MERGEINFO, 14155 chi_COPY_path) 14156 14157#---------------------------------------------------------------------- 14158@SkipUnless(server_has_mergeinfo) 14159@Issue(2690) 14160def copy_then_replace_via_merge(sbox): 14161 "copy then replace via merge" 14162 # Testing issue #2690 with deleted/added/replaced files and subdirs. 14163 14164 sbox.build() 14165 wc_dir = sbox.wc_dir 14166 j = os.path.join 14167 14168 A = j(wc_dir, 'A') 14169 AJ = j(wc_dir, 'A', 'J') 14170 AJK = j(AJ, 'K') 14171 AJL = j(AJ, 'L') 14172 AJM = j(AJ, 'M') 14173 AJ_sigma = j(AJ, 'sigma') 14174 AJ_theta = j(AJ, 'theta') 14175 AJ_omega = j(AJ, 'omega') 14176 AJK_zeta = j(AJK, 'zeta') 14177 AJL_zeta = j(AJL, 'zeta') 14178 AJM_zeta = j(AJM, 'zeta') 14179 branch = j(wc_dir, 'branch') 14180 branch_J = j(wc_dir, 'branch', 'J') 14181 url_A = sbox.repo_url + '/A' 14182 url_branch = sbox.repo_url + '/branch' 14183 14184 # Create a branch. 14185 main.run_svn(None, 'cp', url_A, url_branch, '-m', 'create branch') # r2 14186 14187 # Create a tree J in A. 14188 os.makedirs(AJK) 14189 os.makedirs(AJL) 14190 main.file_append(AJ_sigma, 'new text') 14191 main.file_append(AJ_theta, 'new text') 14192 main.file_append(AJK_zeta, 'new text') 14193 main.file_append(AJL_zeta, 'new text') 14194 main.run_svn(None, 'add', AJ) 14195 sbox.simple_commit(message='create tree J') # r3 14196 main.run_svn(None, 'up', wc_dir) 14197 14198 # Copy J to the branch via merge 14199 main.run_svn(None, 'merge', url_A, branch) 14200 sbox.simple_commit(message='merge to branch') # r4 14201 main.run_svn(None, 'up', wc_dir) 14202 14203 # In A, replace J with a slightly different tree 14204 main.run_svn(None, 'rm', AJ) 14205 sbox.simple_commit(message='rm AJ') # r5 14206 main.run_svn(None, 'up', wc_dir) 14207 14208 os.makedirs(AJL) 14209 os.makedirs(AJM) 14210 main.file_append(AJ_theta, 'really new text') 14211 main.file_append(AJ_omega, 'really new text') 14212 main.file_append(AJL_zeta, 'really new text') 14213 main.file_append(AJM_zeta, 'really new text') 14214 main.run_svn(None, 'add', AJ) 14215 sbox.simple_commit(message='create tree J again') # r6 14216 main.run_svn(None, 'up', wc_dir) 14217 14218 # Run merge to replace /branch/J in one swell foop. 14219 main.run_svn(None, 'merge', url_A, branch) 14220 14221 # Check status: 14222 # sigma and K are deleted (not copied!) 14223 # theta and L are replaced (deleted then copied-here) 14224 # omega and M are copied-here 14225 expected_status = wc.State(branch_J, { 14226 '' : Item(status='R ', copied='+', wc_rev='-'), 14227 'sigma' : Item(status='D ', wc_rev=6), 14228 'K' : Item(status='D ', wc_rev=6), 14229 'K/zeta' : Item(status='D ', wc_rev=6), 14230 'theta' : Item(status=' ', copied='+', wc_rev='-'), 14231 'L' : Item(status=' ', copied='+', wc_rev='-'), 14232 'L/zeta' : Item(status=' ', copied='+', wc_rev='-'), 14233 'omega' : Item(status=' ', copied='+', wc_rev='-'), 14234 'M' : Item(status=' ', copied='+', wc_rev='-'), 14235 'M/zeta' : Item(status=' ', copied='+', wc_rev='-'), 14236 }) 14237 actions.run_and_verify_status(branch_J, expected_status) 14238 14239 # Update and commit, just to make sure the WC isn't busted. 14240 main.run_svn(None, 'up', branch_J) 14241 expected_output = wc.State(branch_J, { 14242 '' : Item(verb='Replacing'), 14243 }) 14244 expected_status = wc.State(branch_J, { 14245 '' : Item(status=' ', wc_rev=7), 14246 'theta' : Item(status=' ', wc_rev=7), 14247 'L' : Item(status=' ', wc_rev=7), 14248 'L/zeta' : Item(status=' ', wc_rev=7), 14249 'omega' : Item(status=' ', wc_rev=7), 14250 'M' : Item(status=' ', wc_rev=7), 14251 'M/zeta' : Item(status=' ', wc_rev=7), 14252 }) 14253 actions.run_and_verify_commit(branch_J, 14254 expected_output, 14255 expected_status) 14256 14257#---------------------------------------------------------------------- 14258@SkipUnless(server_has_mergeinfo) 14259def record_only_merge(sbox): 14260 "record only merge applies mergeinfo diffs" 14261 14262 sbox.build() 14263 wc_dir = sbox.wc_dir 14264 wc_disk, wc_status = set_up_branch(sbox) 14265 14266 # Some paths we'll care about 14267 nu_path = sbox.ospath('A/C/nu') 14268 A_COPY_path = sbox.ospath('A_COPY') 14269 A2_path = sbox.ospath('A2') 14270 Z_path = sbox.ospath('A/B/Z') 14271 Z_COPY_path = sbox.ospath('A_COPY/B/Z') 14272 rho_COPY_path = sbox.ospath('A_COPY/D/G/rho') 14273 omega_COPY_path = sbox.ospath('A_COPY/D/H/omega') 14274 H_COPY_path = sbox.ospath('A_COPY/D/H') 14275 nu_COPY_path = sbox.ospath('A_COPY/C/nu') 14276 14277 # r7 - Copy the branch A_COPY@2 to A2 and update the WC. 14278 svntest.actions.run_and_verify_svn(None, [], 14279 'copy', A_COPY_path, A2_path) 14280 svntest.actions.run_and_verify_svn(None, [], 14281 'commit', '-m', 'Branch the branch', 14282 wc_dir) 14283 # r8 - Add A/C/nu and A/B/Z. 14284 # Add a new file with mergeinfo in the foreign repos. 14285 svntest.main.file_write(nu_path, "This is the file 'nu'.\n") 14286 svntest.actions.run_and_verify_svn(None, [], 'add', nu_path) 14287 svntest.actions.run_and_verify_svn(None, [], 'mkdir', Z_path) 14288 svntest.actions.run_and_verify_svn(None, [], 14289 'commit', '-m', 'Add subtrees', 14290 wc_dir) 14291 14292 # r9 - Edit A/C/nu and add a random property on A/B/Z. 14293 svntest.main.file_write(nu_path, "New content.\n") 14294 svntest.actions.run_and_verify_svn(None, [], 14295 'ps', 'propname', 'propval', Z_path) 14296 svntest.actions.run_and_verify_svn(None, [], 14297 'commit', '-m', 'Subtree changes', 14298 wc_dir) 14299 14300 # r10 - Merge r8 from A to A_COPY. 14301 svntest.actions.run_and_verify_svn(exp_noop_up_out(9), [], 'up', 14302 wc_dir) 14303 svntest.actions.run_and_verify_svn(expected_merge_output( 14304 [[8]], 14305 ['A ' + Z_COPY_path + '\n', 14306 'A ' + nu_COPY_path + '\n', 14307 ' U ' + A_COPY_path + '\n',]), 14308 [], 'merge', '-c8', 14309 sbox.repo_url + '/A', 14310 A_COPY_path) 14311 svntest.actions.run_and_verify_svn(None, [], 14312 'commit', '-m', 'Root merge of r8', 14313 wc_dir) 14314 14315 # r11 - Do several subtree merges: 14316 # 14317 # r4 from A/D/G/rho to A_COPY/D/G/rho 14318 # r6 from A/D/H to A_COPY/D/H 14319 # r9 from A/C/nu to A_COPY/C/nu 14320 # r9 from A/B/Z to A_COPY/B/Z 14321 svntest.actions.run_and_verify_svn(expected_merge_output( 14322 [[4]], 14323 ['U ' + rho_COPY_path + '\n', 14324 ' U ' + rho_COPY_path + '\n',]), 14325 [], 'merge', '-c4', 14326 sbox.repo_url + '/A/D/G/rho', 14327 rho_COPY_path) 14328 svntest.actions.run_and_verify_svn( 14329 expected_merge_output([[6]], 14330 ['U ' + omega_COPY_path + '\n', 14331 ' U ' + H_COPY_path + '\n',]), 14332 [], 'merge', '-c6', sbox.repo_url + '/A/D/H', H_COPY_path) 14333 svntest.actions.run_and_verify_svn(expected_merge_output( 14334 [[9]], 14335 ['U ' + nu_COPY_path + '\n', 14336 ' G ' + nu_COPY_path + '\n',]), 14337 [], 'merge', '-c9', 14338 sbox.repo_url + '/A/C/nu', 14339 nu_COPY_path) 14340 svntest.actions.run_and_verify_svn(expected_merge_output( 14341 [[9]], 14342 [' U ' + Z_COPY_path + '\n', 14343 ' G ' + Z_COPY_path + '\n']), 14344 [], 'merge', '-c9', 14345 sbox.repo_url + '/A/B/Z', 14346 Z_COPY_path) 14347 svntest.actions.run_and_verify_svn(None, [], 14348 'commit', '-m', 'Several subtree merges', 14349 wc_dir) 14350 14351 svntest.actions.run_and_verify_svn(exp_noop_up_out(11), [], 'up', 14352 wc_dir) 14353 14354 # Now do a --record-only merge of r10 and r11 from A_COPY to A2. 14355 # 14356 # We only expect svn:mergeinfo changes to be applied to existing paths: 14357 # 14358 # From r10 the mergeinfo '/A:r8' is recorded on A_COPY. 14359 # 14360 # From r11 the mergeinfo of '/A/D/G/rho:r4' is recorded on A_COPY/D/G/rho 14361 # and the mergeinfo of '/A/D/H:r6' is recorded on A_COPY/D/H. Rev 8 should 14362 # also be recorded on both subtrees because explicit mergeinfo must be 14363 # complete. 14364 # 14365 # The mergeinfo describing the merge source itself, '/A_COPY:10-11' should 14366 # also be recorded on the root and the two subtrees. 14367 # 14368 # The mergeinfo changes from r10 to A_COPY/C/nu and A_COPY/B/Z cannot be 14369 # applied because the corresponding paths don't exist under A2; this should 14370 # not cause any problems. 14371 expected_output = wc.State(A2_path, { 14372 '' : Item(status=' U'), 14373 'D/G/rho' : Item(status=' U'), 14374 'D/H' : Item(status=' U'), 14375 }) 14376 expected_mergeinfo_output = wc.State(A2_path, { 14377 '' : Item(status=' G'), 14378 'D/H' : Item(status=' G'), 14379 'D/G/rho' : Item(status=' G'), 14380 }) 14381 expected_elision_output = wc.State(A2_path, { 14382 }) 14383 expected_disk = wc.State('', { 14384 '' : Item(props={SVN_PROP_MERGEINFO : '/A:8\n/A_COPY:10-11'}), 14385 'mu' : Item("This is the file 'mu'.\n"), 14386 'B' : Item(), 14387 'B/lambda' : Item("This is the file 'lambda'.\n"), 14388 'B/E' : Item(), 14389 'B/E/alpha' : Item("This is the file 'alpha'.\n"), 14390 'B/E/beta' : Item("This is the file 'beta'.\n"), 14391 'B/F' : Item(), 14392 'C' : Item(), 14393 'D' : Item(), 14394 'D/gamma' : Item("This is the file 'gamma'.\n"), 14395 'D/H' : Item(props={SVN_PROP_MERGEINFO : 14396 '/A/D/H:6,8\n/A_COPY/D/H:10-11'}), 14397 'D/H/chi' : Item("This is the file 'chi'.\n"), 14398 'D/H/psi' : Item("This is the file 'psi'.\n"), 14399 'D/H/omega' : Item("This is the file 'omega'.\n"), 14400 'D/G' : Item(), 14401 'D/G/pi' : Item("This is the file 'pi'.\n"), 14402 'D/G/rho' : Item("This is the file 'rho'.\n", 14403 props={SVN_PROP_MERGEINFO : 14404 '/A/D/G/rho:4,8\n/A_COPY/D/G/rho:10-11'}), 14405 'D/G/tau' : Item("This is the file 'tau'.\n"), 14406 }) 14407 expected_status = wc.State(A2_path, { 14408 '' : Item(status=' M'), 14409 'mu' : Item(status=' '), 14410 'B' : Item(status=' '), 14411 'B/lambda' : Item(status=' '), 14412 'B/E' : Item(status=' '), 14413 'B/E/alpha' : Item(status=' '), 14414 'B/E/beta' : Item(status=' '), 14415 'B/F' : Item(status=' '), 14416 'C' : Item(status=' '), 14417 'D' : Item(status=' '), 14418 'D/gamma' : Item(status=' '), 14419 'D/H' : Item(status=' M'), 14420 'D/H/chi' : Item(status=' '), 14421 'D/H/psi' : Item(status=' '), 14422 'D/H/omega' : Item(status=' '), 14423 'D/G' : Item(status=' '), 14424 'D/G/pi' : Item(status=' '), 14425 'D/G/rho' : Item(status=' M'), 14426 'D/G/tau' : Item(status=' '), 14427 }) 14428 expected_status.tweak(wc_rev=11) 14429 expected_skip = wc.State('', { }) 14430 svntest.actions.run_and_verify_merge(A2_path, '9', '11', 14431 sbox.repo_url + '/A_COPY', None, 14432 expected_output, 14433 expected_mergeinfo_output, 14434 expected_elision_output, 14435 expected_disk, 14436 expected_status, 14437 expected_skip, 14438 [], True, False, 14439 '--record-only', A2_path) 14440 14441#---------------------------------------------------------------------- 14442# Test for issue #3514 'svn merge --accept [ base | theirs-full ] 14443# doesn't work' 14444@Issue(3514) 14445def merge_automatic_conflict_resolution(sbox): 14446 "automatic conflict resolutions work with merge" 14447 14448 sbox.build() 14449 wc_dir = sbox.wc_dir 14450 wc_disk, wc_status = set_up_branch(sbox) 14451 14452 14453 # Some paths we'll care about 14454 A_COPY_path = sbox.ospath('A_COPY') 14455 psi_COPY_path = sbox.ospath('A_COPY/D/H/psi') 14456 14457 # r7 - Make a change on A_COPY that will conflict with r3 on A 14458 svntest.main.file_write(psi_COPY_path, "BASE.\n") 14459 svntest.actions.run_and_verify_svn(None, [], 14460 'commit', '-m', 'log msg', wc_dir) 14461 14462 # Set up our base expectations, we'll tweak accordingly for each option. 14463 expected_status = wc.State(A_COPY_path, { 14464 '' : Item(status=' M', wc_rev=2), 14465 'B' : Item(status=' ', wc_rev=2), 14466 'mu' : Item(status=' ', wc_rev=2), 14467 'B/E' : Item(status=' ', wc_rev=2), 14468 'B/E/alpha' : Item(status=' ', wc_rev=2), 14469 'B/E/beta' : Item(status=' ', wc_rev=2), 14470 'B/lambda' : Item(status=' ', wc_rev=2), 14471 'B/F' : Item(status=' ', wc_rev=2), 14472 'C' : Item(status=' ', wc_rev=2), 14473 'D' : Item(status=' ', wc_rev=2), 14474 'D/G' : Item(status=' ', wc_rev=2), 14475 'D/G/pi' : Item(status=' ', wc_rev=2), 14476 'D/G/rho' : Item(status=' ', wc_rev=2), 14477 'D/G/tau' : Item(status=' ', wc_rev=2), 14478 'D/gamma' : Item(status=' ', wc_rev=2), 14479 'D/H' : Item(status=' ', wc_rev=2), 14480 'D/H/chi' : Item(status=' ', wc_rev=2), 14481 'D/H/psi' : Item(status=' ', wc_rev=7), 14482 'D/H/omega' : Item(status=' ', wc_rev=2), 14483 }) 14484 expected_disk = wc.State('', { 14485 '' : Item(props={SVN_PROP_MERGEINFO : '/A:3'}), 14486 'B' : Item(), 14487 'mu' : Item("This is the file 'mu'.\n"), 14488 'B/E' : Item(), 14489 'B/E/alpha' : Item("This is the file 'alpha'.\n"), 14490 'B/E/beta' : Item("This is the file 'beta'.\n"), 14491 'B/lambda' : Item("This is the file 'lambda'.\n"), 14492 'B/F' : Item(), 14493 'C' : Item(), 14494 'D' : Item(), 14495 'D/G' : Item(), 14496 'D/G/pi' : Item("This is the file 'pi'.\n"), 14497 'D/G/rho' : Item("This is the file 'rho'.\n"), 14498 'D/G/tau' : Item("This is the file 'tau'.\n"), 14499 'D/gamma' : Item("This is the file 'gamma'.\n"), 14500 'D/H' : Item(), 14501 'D/H/chi' : Item("This is the file 'chi'.\n"), 14502 'D/H/psi' : Item("This is the file 'psi'.\n"), 14503 'D/H/omega' : Item("This is the file 'omega'.\n"), 14504 }) 14505 expected_skip = wc.State(A_COPY_path, { }) 14506 14507 # Test --accept postpone 14508 expected_output = wc.State(A_COPY_path, {'D/H/psi' : Item(status='C ')}) 14509 expected_mergeinfo_output = wc.State(A_COPY_path, { 14510 '' : Item(status=' U'), 14511 }) 14512 expected_elision_output = wc.State(A_COPY_path, { 14513 }) 14514 expected_disk.tweak('D/H/psi', contents="<<<<<<< .working\n" 14515 "BASE.\n" 14516 "||||||| .merge-left.r2\n" 14517 "This is the file 'psi'.\n" 14518 "=======\n" 14519 "New content>>>>>>> .merge-right.r3\n") 14520 expected_status.tweak('D/H/psi', status='C ') 14521 psi_conflict_support_files = ["psi\.working", 14522 "psi\.merge-right\.r3", 14523 "psi\.merge-left\.r2"] 14524 svntest.actions.run_and_verify_merge(A_COPY_path, '2', '3', 14525 sbox.repo_url + '/A', None, 14526 expected_output, 14527 expected_mergeinfo_output, 14528 expected_elision_output, 14529 expected_disk, 14530 expected_status, 14531 expected_skip, 14532 [], True, True, 14533 '--accept', 'postpone', 14534 '--allow-mixed-revisions', 14535 A_COPY_path, 14536 extra_files= 14537 list(psi_conflict_support_files)) 14538 svntest.actions.run_and_verify_svn(None, [], 14539 'revert', '--recursive', wc_dir) 14540 14541 # Test --accept mine-conflict and mine-full 14542 ### TODO: Also test that the output has a 'Resolved' line for this path. 14543 expected_output = wc.State(A_COPY_path, {'D/H/psi' : Item(status='C ')}) 14544 expected_disk.tweak('D/H/psi', contents="BASE.\n") 14545 expected_status.tweak('D/H/psi', status=' ') 14546 svntest.actions.run_and_verify_merge(A_COPY_path, '2', '3', 14547 sbox.repo_url + '/A', None, 14548 expected_output, 14549 expected_mergeinfo_output, 14550 expected_elision_output, 14551 expected_disk, 14552 expected_status, 14553 expected_skip, 14554 [], True, False, 14555 '--accept', 'mine-conflict', 14556 '--allow-mixed-revisions', 14557 A_COPY_path) 14558 svntest.actions.run_and_verify_svn(None, [], 14559 'revert', '--recursive', wc_dir) 14560 svntest.actions.run_and_verify_merge(A_COPY_path, '2', '3', 14561 sbox.repo_url + '/A', None, 14562 expected_output, 14563 expected_mergeinfo_output, 14564 expected_elision_output, 14565 expected_disk, 14566 expected_status, 14567 expected_skip, 14568 [], True, False, 14569 '--accept', 'mine-full', 14570 '--allow-mixed-revisions', 14571 A_COPY_path) 14572 svntest.actions.run_and_verify_svn(None, [], 14573 'revert', '--recursive', wc_dir) 14574 14575 # Test --accept theirs-conflict and theirs-full 14576 ### TODO: Also test that the output has a 'Resolved' line for this path. 14577 expected_output = wc.State(A_COPY_path, {'D/H/psi' : Item(status='C ')}) 14578 expected_disk.tweak('D/H/psi', contents="New content") 14579 expected_status.tweak('D/H/psi', status='M ') 14580 svntest.actions.run_and_verify_merge(A_COPY_path, '2', '3', 14581 sbox.repo_url + '/A', None, 14582 expected_output, 14583 expected_mergeinfo_output, 14584 expected_elision_output, 14585 expected_disk, 14586 expected_status, 14587 expected_skip, 14588 [], True, False, 14589 '--accept', 'theirs-conflict', 14590 '--allow-mixed-revisions', 14591 A_COPY_path) 14592 svntest.actions.run_and_verify_svn(None, [], 14593 'revert', '--recursive', wc_dir) 14594 svntest.actions.run_and_verify_merge(A_COPY_path, '2', '3', 14595 sbox.repo_url + '/A', None, 14596 expected_output, 14597 expected_mergeinfo_output, 14598 expected_elision_output, 14599 expected_disk, 14600 expected_status, 14601 expected_skip, 14602 [], True, False, 14603 '--accept', 'theirs-full', 14604 '--allow-mixed-revisions', 14605 A_COPY_path) 14606 svntest.actions.run_and_verify_svn(None, [], 14607 'revert', '--recursive', wc_dir) 14608 # Test --accept base 14609 ### TODO: Also test that the output has a 'Resolved' line for this path. 14610 expected_output = wc.State(A_COPY_path, {'D/H/psi' : Item(status='C ')}) 14611 expected_elision_output = wc.State(A_COPY_path, { 14612 }) 14613 expected_disk.tweak('D/H/psi', contents="This is the file 'psi'.\n") 14614 expected_status.tweak('D/H/psi', status='M ') 14615 svntest.actions.run_and_verify_merge(A_COPY_path, '2', '3', 14616 sbox.repo_url + '/A', None, 14617 expected_output, 14618 expected_mergeinfo_output, 14619 expected_elision_output, 14620 expected_disk, 14621 expected_status, 14622 expected_skip, 14623 [], True, False, 14624 '--accept', 'base', 14625 '--allow-mixed-revisions', 14626 A_COPY_path) 14627 14628#---------------------------------------------------------------------- 14629# Test for issue #3440 'Skipped paths get incorrect override mergeinfo 14630# during merge'. 14631@SkipUnless(server_has_mergeinfo) 14632@Issue(3440) 14633def skipped_files_get_correct_mergeinfo(sbox): 14634 "skipped files get correct mergeinfo set" 14635 14636 sbox.build() 14637 wc_dir = sbox.wc_dir 14638 14639 # Some paths we'll care about 14640 A_COPY_path = sbox.ospath('A_COPY') 14641 H_COPY_path = sbox.ospath('A_COPY/D/H') 14642 psi_COPY_path = sbox.ospath('A_COPY/D/H/psi') 14643 psi_path = sbox.ospath('A/D/H/psi') 14644 14645 # Setup our basic 'trunk' and 'branch': 14646 # r2 - Copy A to A_COPY 14647 # r3 - Text change to A/D/H/psi 14648 # r4 - Text change to A/D/G/rho 14649 # r5 - Text change to A/B/E/beta 14650 # r6 - Text change to A/D/H/omega 14651 wc_disk, wc_status = set_up_branch(sbox, False, 1) 14652 14653 # r7 Make another text change to A/D/H/psi 14654 svntest.main.file_write(psi_path, "Even newer content") 14655 expected_output = wc.State(wc_dir, {'A/D/H/psi' : Item(verb='Sending')}) 14656 svntest.main.run_svn(None, 'commit', '-m', 'another change to A/D/H/psi', 14657 wc_dir) 14658 14659 # Merge r3 from A to A_COPY, this will create explicit mergeinfo of 14660 # '/A:3' on A_COPY. Commit this merge as r8. 14661 svntest.actions.run_and_verify_svn( 14662 expected_merge_output([[3]], 14663 ['U ' + psi_COPY_path + '\n', 14664 ' U ' + A_COPY_path + '\n',]), 14665 [], 'merge', '-c3', sbox.repo_url + '/A', A_COPY_path) 14666 svntest.main.run_svn(None, 'commit', '-m', 'initial merge', wc_dir) 14667 14668 # Update WC to uniform revision and then set the depth on A_COPY/D/H to 14669 # empty. Then merge all available revisions from A to A_COPY. 14670 # A_COPY/D/H/psi and A_COPY/D/H/omega are not present due to their 14671 # parent's depth and should be reported as skipped. A_COPY/D/H should 14672 # get explicit mergeinfo set on it reflecting what it previously inherited 14673 # from A_COPY after the first merge, i.e. '/A/D/H:3', plus non-inheritable 14674 # mergeinfo describing what was done during this merge, 14675 # i.e. '/A/D/H:2*,4-8*'. 14676 # 14677 # Issue #3440 occurred when empty mergeinfo was set on A_COPY/D/H, making 14678 # it appear that r3 was never merged. 14679 svntest.actions.run_and_verify_svn(exp_noop_up_out(8), [], 14680 'up', wc_dir) 14681 svntest.actions.run_and_verify_svn(None, [], 14682 'up', '--set-depth=empty', H_COPY_path) 14683 expected_status = wc.State(A_COPY_path, { 14684 '' : Item(status=' M'), 14685 'B' : Item(status=' '), 14686 'mu' : Item(status=' '), 14687 'B/E' : Item(status=' '), 14688 'B/E/alpha' : Item(status=' '), 14689 'B/E/beta' : Item(status='M '), 14690 'B/lambda' : Item(status=' '), 14691 'B/F' : Item(status=' '), 14692 'C' : Item(status=' '), 14693 'D' : Item(status=' '), 14694 'D/G' : Item(status=' '), 14695 'D/G/pi' : Item(status=' '), 14696 'D/G/rho' : Item(status='M '), 14697 'D/G/tau' : Item(status=' '), 14698 'D/gamma' : Item(status=' '), 14699 'D/H' : Item(status=' M'), 14700 }) 14701 expected_status.tweak(wc_rev=8) 14702 expected_disk = wc.State('', { 14703 '' : Item(props={SVN_PROP_MERGEINFO : '/A:2-8'}), 14704 'B' : Item(), 14705 'mu' : Item("This is the file 'mu'.\n"), 14706 'B/E' : Item(), 14707 'B/E/alpha' : Item("This is the file 'alpha'.\n"), 14708 'B/E/beta' : Item("New content"), 14709 'B/lambda' : Item("This is the file 'lambda'.\n"), 14710 'B/F' : Item(), 14711 'C' : Item(), 14712 'D' : Item(), 14713 'D/G' : Item(), 14714 'D/G/pi' : Item("This is the file 'pi'.\n"), 14715 'D/G/rho' : Item("New content"), 14716 'D/G/tau' : Item("This is the file 'tau'.\n"), 14717 'D/gamma' : Item("This is the file 'gamma'.\n"), 14718 'D/H' : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:2*,3,4-8*'}), 14719 }) 14720 expected_skip = wc.State( 14721 A_COPY_path, 14722 {'D/H/psi' : Item(verb='Skipped missing target'), 14723 'D/H/omega' : Item(verb='Skipped missing target')}) 14724 expected_output = wc.State(A_COPY_path, 14725 {'B/E/beta' : Item(status='U '), 14726 'D/G/rho' : Item(status='U ')}) 14727 expected_mergeinfo_output = wc.State(A_COPY_path, { 14728 '' : Item(status=' U'), 14729 'D/H' : Item(status=' G'), # ' G' because override mergeinfo gets set 14730 # on this, the root of a 'missing' subtree. 14731 }) 14732 expected_elision_output = wc.State(A_COPY_path, { 14733 }) 14734 svntest.actions.run_and_verify_merge(A_COPY_path, None, None, 14735 sbox.repo_url + '/A', None, 14736 expected_output, 14737 expected_mergeinfo_output, 14738 expected_elision_output, 14739 expected_disk, 14740 expected_status, 14741 expected_skip, 14742 [], True, True) 14743 14744#---------------------------------------------------------------------- 14745# Test for issue #3115 'Case only renames resulting from merges don't 14746# work or break the WC on case-insensitive file systems'. 14747@Issue(3115) 14748def committed_case_only_move_and_revert(sbox): 14749 "committed case only move causes revert to fail" 14750 14751 sbox.build() 14752 wc_dir = sbox.wc_dir 14753 wc_disk, wc_status = set_up_branch(sbox, True) 14754 14755 # Some paths we'll care about 14756 A_COPY_path = sbox.ospath('A_COPY') 14757 14758 # r3: A case-only file rename on the server 14759 svntest.actions.run_and_verify_svn(['Committing transaction...\n', 14760 'Committed revision 3.\n'], 14761 [], 'move', 14762 sbox.repo_url + '/A/mu', 14763 sbox.repo_url + '/A/MU', 14764 '-m', 'Move A/mu to A/MU') 14765 14766 # Now merge that rename into the WC 14767 expected_output = wc.State(A_COPY_path, { 14768 'mu' : Item(status='D '), 14769 'MU' : Item(status='A '), 14770 }) 14771 expected_mergeinfo_output = wc.State(A_COPY_path, { 14772 '' : Item(status=' U'), 14773 }) 14774 expected_elision_output = wc.State(A_COPY_path, { 14775 }) 14776 expected_status = wc.State(A_COPY_path, { 14777 '' : Item(status=' M', wc_rev=2), 14778 'B' : Item(status=' ', wc_rev=2), 14779 'mu' : Item(status='D ', wc_rev=2), 14780 'MU' : Item(status='A ', wc_rev='-', copied='+'), 14781 'B/E' : Item(status=' ', wc_rev=2), 14782 'B/E/alpha' : Item(status=' ', wc_rev=2), 14783 'B/E/beta' : Item(status=' ', wc_rev=2), 14784 'B/lambda' : Item(status=' ', wc_rev=2), 14785 'B/F' : Item(status=' ', wc_rev=2), 14786 'C' : Item(status=' ', wc_rev=2), 14787 'D' : Item(status=' ', wc_rev=2), 14788 'D/G' : Item(status=' ', wc_rev=2), 14789 'D/G/pi' : Item(status=' ', wc_rev=2), 14790 'D/G/rho' : Item(status=' ', wc_rev=2), 14791 'D/G/tau' : Item(status=' ', wc_rev=2), 14792 'D/gamma' : Item(status=' ', wc_rev=2), 14793 'D/H' : Item(status=' ', wc_rev=2), 14794 'D/H/chi' : Item(status=' ', wc_rev=2), 14795 'D/H/psi' : Item(status=' ', wc_rev=2), 14796 'D/H/omega' : Item(status=' ', wc_rev=2), 14797 }) 14798 expected_disk = wc.State('', { 14799 '' : Item(props={SVN_PROP_MERGEINFO : '/A:3'}), 14800 'B' : Item(), 14801 'MU' : Item("This is the file 'mu'.\n"), 14802 'B/E' : Item(), 14803 'B/E/alpha' : Item("This is the file 'alpha'.\n"), 14804 'B/E/beta' : Item("This is the file 'beta'.\n"), 14805 'B/lambda' : Item("This is the file 'lambda'.\n"), 14806 'B/F' : Item(), 14807 'C' : Item(), 14808 'D' : Item(), 14809 'D/G' : Item(), 14810 'D/G/pi' : Item("This is the file 'pi'.\n"), 14811 'D/G/rho' : Item("This is the file 'rho'.\n"), 14812 'D/G/tau' : Item("This is the file 'tau'.\n"), 14813 'D/gamma' : Item("This is the file 'gamma'.\n"), 14814 'D/H' : Item(), 14815 'D/H/chi' : Item("This is the file 'chi'.\n"), 14816 'D/H/psi' : Item("This is the file 'psi'.\n"), 14817 'D/H/omega' : Item("This is the file 'omega'.\n"), 14818 }) 14819 expected_skip = wc.State(A_COPY_path, { }) 14820 svntest.actions.run_and_verify_merge(A_COPY_path, '2', '3', 14821 sbox.repo_url + '/A', None, 14822 expected_output, 14823 expected_mergeinfo_output, 14824 expected_elision_output, 14825 expected_disk, 14826 expected_status, 14827 expected_skip, 14828 [], True, False) 14829 14830 # Commit the merge 14831 expected_output = svntest.wc.State(wc_dir, { 14832 'A_COPY' : Item(verb='Sending'), 14833 'A_COPY/mu' : Item(verb='Deleting'), 14834 'A_COPY/MU' : Item(verb='Adding'), 14835 }) 14836 wc_status.tweak('A_COPY', wc_rev=4) 14837 wc_status.remove('A_COPY/mu') 14838 wc_status.add({'A_COPY/MU': Item(status=' ', wc_rev=4)}) 14839 14840 svntest.actions.run_and_verify_commit(wc_dir, expected_output, wc_status) 14841 14842 # In issue #3115 the WC gets corrupted and any subsequent revert 14843 # attempts fail with this error: 14844 # svn.exe revert -R "svn-test-work\working_copies\merge_tests-139" 14845 # ..\..\..\subversion\svn\revert-cmd.c:81: (apr_err=2) 14846 # ..\..\..\subversion\libsvn_client\revert.c:167: (apr_err=2) 14847 # ..\..\..\subversion\libsvn_client\revert.c:103: (apr_err=2) 14848 # ..\..\..\subversion\libsvn_wc\adm_ops.c:2232: (apr_err=2) 14849 # ..\..\..\subversion\libsvn_wc\adm_ops.c:2232: (apr_err=2) 14850 # ..\..\..\subversion\libsvn_wc\adm_ops.c:2232: (apr_err=2) 14851 # ..\..\..\subversion\libsvn_wc\adm_ops.c:2176: (apr_err=2) 14852 # ..\..\..\subversion\libsvn_wc\adm_ops.c:2053: (apr_err=2) 14853 # ..\..\..\subversion\libsvn_wc\adm_ops.c:1869: (apr_err=2) 14854 # ..\..\..\subversion\libsvn_wc\workqueue.c:520: (apr_err=2) 14855 # ..\..\..\subversion\libsvn_wc\workqueue.c:490: (apr_err=2) 14856 # svn: Error restoring text for 'C:\SVN\src-trunk\Debug\subversion\tests 14857 # \cmdline\svn-test-work\working_copies\merge_tests-139\A_COPY\MU' 14858 svntest.actions.run_and_verify_svn([], [], 'revert', '-R', wc_dir) 14859 14860 # r5: A case-only directory rename on the server 14861 svntest.actions.run_and_verify_svn(['Committing transaction...\n', 14862 'Committed revision 5.\n'], 14863 [], 'move', 14864 sbox.repo_url + '/A/C', 14865 sbox.repo_url + '/A/c', 14866 '-m', 'Move A/C to A/c') 14867 expected_output = wc.State(A_COPY_path, { 14868 'C' : Item(status='D '), 14869 'c' : Item(status='A '), 14870 }) 14871 expected_elision_output = wc.State(A_COPY_path, { 14872 }) 14873 expected_disk.tweak('', props={SVN_PROP_MERGEINFO : '/A:3,5'}) 14874 expected_disk.add({'c' : Item()}) 14875 expected_disk.remove('C') 14876 expected_status.tweak('MU', status=' ', wc_rev=4, copied=None) 14877 expected_status.remove('mu') 14878 expected_status.tweak('C', status='D ') 14879 expected_status.tweak('', wc_rev=4) 14880 expected_status.add({'c' : Item(status='A ', copied='+', wc_rev='-')}) 14881 # This merge succeeds. It used to leave a strange state, added with 14882 # history but missing: 14883 # 14884 # M merge_tests-139\A_COPY 14885 # ! + merge_tests-139\A_COPY\c 14886 # R + merge_tests-139\A_COPY\C 14887 svntest.actions.run_and_verify_merge(A_COPY_path, '4', '5', 14888 sbox.repo_url + '/A', None, 14889 expected_output, 14890 expected_mergeinfo_output, 14891 expected_elision_output, 14892 expected_disk, 14893 expected_status, 14894 expected_skip, 14895 [], True, False, 14896 '--allow-mixed-revisions', A_COPY_path) 14897 14898#---------------------------------------------------------------------- 14899# This is a test for issue #3221 'Unable to merge into working copy of 14900# deleted branch'. 14901@SkipUnless(server_has_mergeinfo) 14902@Issue(3221) 14903def merge_into_wc_for_deleted_branch(sbox): 14904 "merge into WC of deleted branch should work" 14905 14906 sbox.build() 14907 wc_dir = sbox.wc_dir 14908 14909 # Copy 'A' to 'A_COPY' then make some changes under 'A' 14910 wc_disk, wc_status = set_up_branch(sbox) 14911 14912 # Some paths we'll care about 14913 A_COPY_path = sbox.ospath('A_COPY') 14914 gamma_path = sbox.ospath('A/D/gamma') 14915 14916 # r7 - Delete the branch on the repository, obviously it still 14917 # exists in our WC. 14918 svntest.actions.run_and_verify_svn(None, [], 14919 'delete', sbox.repo_url + '/A_COPY', 14920 '-m', 'Delete A_COPY directly in repos') 14921 14922 # r8 - Make another change under 'A'. 14923 svntest.main.file_write(gamma_path, "Content added after A_COPY deleted") 14924 expected_output = wc.State(wc_dir, {'A/D/gamma' : Item(verb='Sending')}) 14925 svntest.main.run_svn(None, 'commit', 14926 '-m', 'Change made on A after A_COPY was deleted', 14927 wc_dir) 14928 14929 # Now merge all available revisions from A to A_COPY: 14930 expected_output = wc.State(A_COPY_path, { 14931 'B/E/beta' : Item(status='U '), 14932 'D/G/rho' : Item(status='U '), 14933 'D/H/omega' : Item(status='U '), 14934 'D/H/psi' : Item(status='U '), 14935 'D/gamma' : Item(status='U '), 14936 }) 14937 expected_mergeinfo_output = wc.State(A_COPY_path, { 14938 '' : Item(status=' U'), 14939 }) 14940 expected_elision_output = wc.State(A_COPY_path, { 14941 }) 14942 expected_status = wc.State(A_COPY_path, { 14943 '' : Item(status=' M'), 14944 'B' : Item(status=' '), 14945 'mu' : Item(status=' '), 14946 'B/E' : Item(status=' '), 14947 'B/E/alpha' : Item(status=' '), 14948 'B/E/beta' : Item(status='M '), 14949 'B/lambda' : Item(status=' '), 14950 'B/F' : Item(status=' '), 14951 'C' : Item(status=' '), 14952 'D' : Item(status=' '), 14953 'D/G' : Item(status=' '), 14954 'D/G/pi' : Item(status=' '), 14955 'D/G/rho' : Item(status='M '), 14956 'D/G/tau' : Item(status=' '), 14957 'D/gamma' : Item(status='M '), 14958 'D/H' : Item(status=' '), 14959 'D/H/chi' : Item(status=' '), 14960 'D/H/psi' : Item(status='M '), 14961 'D/H/omega' : Item(status='M '), 14962 }) 14963 expected_status.tweak(wc_rev=2) 14964 expected_disk = wc.State('', { 14965 '' : Item(props={SVN_PROP_MERGEINFO : '/A:2-8'}), 14966 'B' : Item(), 14967 'mu' : Item("This is the file 'mu'.\n"), 14968 'B/E' : Item(), 14969 'B/E/alpha' : Item("This is the file 'alpha'.\n"), 14970 'B/E/beta' : Item("New content"), 14971 'B/lambda' : Item("This is the file 'lambda'.\n"), 14972 'B/F' : Item(), 14973 'C' : Item(), 14974 'D' : Item(), 14975 'D/G' : Item(), 14976 'D/G/pi' : Item("This is the file 'pi'.\n"), 14977 'D/G/rho' : Item("New content"), 14978 'D/G/tau' : Item("This is the file 'tau'.\n"), 14979 'D/gamma' : Item("Content added after A_COPY deleted"), 14980 'D/H' : Item(), 14981 'D/H/chi' : Item("This is the file 'chi'.\n"), 14982 'D/H/psi' : Item("New content"), 14983 'D/H/omega' : Item("New content"), 14984 }) 14985 expected_skip = wc.State(A_COPY_path, { }) 14986 # Issue #3221: Previously this merge failed with: 14987 # ..\..\..\subversion\svn\util.c:900: (apr_err=160013) 14988 # ..\..\..\subversion\libsvn_client\merge.c:9383: (apr_err=160013) 14989 # ..\..\..\subversion\libsvn_client\merge.c:8029: (apr_err=160013) 14990 # ..\..\..\subversion\libsvn_client\merge.c:7577: (apr_err=160013) 14991 # ..\..\..\subversion\libsvn_client\merge.c:4132: (apr_err=160013) 14992 # ..\..\..\subversion\libsvn_client\merge.c:3312: (apr_err=160013) 14993 # ..\..\..\subversion\libsvn_client\ra.c:659: (apr_err=160013) 14994 # ..\..\..\subversion\libsvn_repos\rev_hunt.c:696: (apr_err=160013) 14995 # ..\..\..\subversion\libsvn_repos\rev_hunt.c:539: (apr_err=160013) 14996 # ..\..\..\subversion\libsvn_fs_fs\tree.c:2818: (apr_err=160013) 14997 # svn: File not found: revision 8, path '/A_COPY' 14998 svntest.actions.run_and_verify_merge(A_COPY_path, None, None, 14999 sbox.repo_url + '/A', None, 15000 expected_output, 15001 expected_mergeinfo_output, 15002 expected_elision_output, 15003 expected_disk, 15004 expected_status, 15005 expected_skip, 15006 check_props=True) 15007 15008#---------------------------------------------------------------------- 15009def foreign_repos_del_and_props(sbox): 15010 "merge del and ps variants from a foreign repos" 15011 15012 sbox.build() 15013 wc_dir = sbox.wc_dir 15014 wc2_dir = sbox.add_wc_path('wc2') 15015 15016 (r2_path, r2_url) = sbox.add_repo_path('fgn') 15017 svntest.main.create_repos(r2_path) 15018 15019 svntest.actions.run_and_verify_svn(None, [], 'checkout', 15020 r2_url, wc2_dir) 15021 15022 svntest.actions.run_and_verify_svn(None, [], 'propset', 15023 'svn:eol-style', 'native', 15024 sbox.ospath('iota')) 15025 15026 svntest.actions.run_and_verify_svn(None, [], 'cp', 15027 sbox.ospath('A/D'), 15028 sbox.ospath('D')) 15029 15030 svntest.actions.run_and_verify_svn(None, [], 'rm', 15031 sbox.ospath('A/D'), 15032 sbox.ospath('D/G')) 15033 15034 new_file = sbox.ospath('new-file') 15035 svntest.main.file_write(new_file, 'new-file') 15036 svntest.actions.run_and_verify_svn(None, [], 'add', new_file) 15037 15038 svntest.actions.run_and_verify_svn(None, [], 'propset', 15039 'svn:eol-style', 'native', new_file) 15040 15041 svntest.actions.run_and_verify_svn(None, [], 'commit', wc_dir, 15042 '-m', 'changed') 15043 15044 svntest.actions.run_and_verify_svn(None, [], 'merge', 15045 sbox.repo_url, wc2_dir, 15046 '-r', '0:1') 15047 15048 expected_status = svntest.actions.get_virginal_state(wc2_dir, 0) 15049 expected_status.tweak(status='A ') 15050 expected_status.add( 15051 { 15052 '' : Item(status=' ', wc_rev='0'), 15053 }) 15054 svntest.actions.run_and_verify_status(wc2_dir, expected_status) 15055 15056 expected_status = svntest.actions.get_virginal_state(wc2_dir, 1) 15057 15058 svntest.actions.run_and_verify_svn(None, [], 'commit', wc2_dir, 15059 '-m', 'Merged r1') 15060 15061 svntest.actions.run_and_verify_svn(None, [], 'merge', 15062 sbox.repo_url, wc2_dir, 15063 '-r', '1:2', '--allow-mixed-revisions') 15064 15065 expected_status.tweak('A/D', 'A/D/G', 'A/D/G/rho', 'A/D/G/tau', 'A/D/G/pi', 15066 'A/D/gamma', 'A/D/H', 'A/D/H/psi', 'A/D/H/omega', 15067 'A/D/H/chi', status='D ') 15068 expected_status.tweak(wc_rev='1') 15069 expected_status.tweak('', wc_rev='0') 15070 expected_status.tweak('iota', status=' M') 15071 15072 expected_status.add( 15073 { 15074 'new-file' : Item(status='A ', wc_rev='0'), 15075 'D' : Item(status='A ', wc_rev='0'), 15076 'D/H' : Item(status='A ', wc_rev='0'), 15077 'D/H/omega' : Item(status='A ', wc_rev='0'), 15078 'D/H/psi' : Item(status='A ', wc_rev='0'), 15079 'D/H/chi' : Item(status='A ', wc_rev='0'), 15080 'D/gamma' : Item(status='A ', wc_rev='0'), 15081 }) 15082 15083 svntest.actions.run_and_verify_status(wc2_dir, expected_status) 15084 15085 expected_output = ["Properties on '%s':\n" % (os.path.join(wc2_dir, 'iota')), 15086 " svn:eol-style\n", 15087 "Properties on '%s':\n" % (os.path.join(wc2_dir, 'new-file')), 15088 " svn:eol-style\n" ] 15089 svntest.actions.run_and_verify_svn(expected_output, [], 'proplist', 15090 os.path.join(wc2_dir, 'iota'), 15091 os.path.join(wc2_dir, 'new-file')) 15092 15093#---------------------------------------------------------------------- 15094# Test for issue #3642 'immediate depth merges don't create proper subtree 15095# mergeinfo'. See https://issues.apache.org/jira/browse/SVN-3642 15096@Issue(3642) 15097def immediate_depth_merge_creates_minimal_subtree_mergeinfo(sbox): 15098 "no spurious mergeinfo from immediate depth merges" 15099 15100 sbox.build() 15101 wc_dir = sbox.wc_dir 15102 wc_disk, wc_status = set_up_branch(sbox) 15103 15104 B_path = sbox.ospath('A/B') 15105 B_COPY_path = sbox.ospath('A_COPY/B') 15106 15107 15108 svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir) 15109 15110 # Merge -c5 from A/B to A_COPY/B at --depth immediates. 15111 # This should create only the minimum subtree mergeinfo 15112 # required to describe the merge. This means that A_COPY/B/E gets 15113 # non-inheritable mergeinfo for r5, because a full depth merge would 15114 # affect that subtree. The other child of the merge target, A_COPY/B/F 15115 # would never be affected by r5, so it doesn't need any explicit 15116 # mergeinfo. 15117 expected_output = wc.State(B_COPY_path, {}) 15118 expected_mergeinfo_output = wc.State(B_COPY_path, { 15119 '' : Item(status=' U'), 15120 'E' : Item(status=' U'), # A_COPY/B/E would be affected by r5 if the 15121 # merge was at infinite depth, so it needs 15122 # non-inheritable override mergeinfo. 15123 #'F' : Item(status=' U'), No override mergeinfo, r5 is 15124 # inoperative on this child. 15125 }) 15126 expected_elision_output = wc.State(B_COPY_path, { 15127 }) 15128 expected_status = wc.State(B_COPY_path, { 15129 '' : Item(status=' M'), 15130 'F' : Item(status=' '), 15131 'E' : Item(status=' M'), 15132 'E/alpha' : Item(status=' '), 15133 'E/beta' : Item(status=' '), 15134 'lambda' : Item(status=' '), 15135 15136 }) 15137 expected_status.tweak(wc_rev=6) 15138 expected_disk = wc.State('', { 15139 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B:5'}), 15140 'E' : Item(props={SVN_PROP_MERGEINFO : '/A/B/E:5*'}), 15141 'E/alpha' : Item("This is the file 'alpha'.\n"), 15142 'E/beta' : Item("This is the file 'beta'.\n"), 15143 'F' : Item(), 15144 'lambda' : Item("This is the file 'lambda'.\n") 15145 }) 15146 expected_skip = wc.State(B_COPY_path, { }) 15147 svntest.actions.run_and_verify_merge(B_COPY_path, '4', '5', 15148 sbox.repo_url + '/A/B', None, 15149 expected_output, 15150 expected_mergeinfo_output, 15151 expected_elision_output, 15152 expected_disk, 15153 expected_status, 15154 expected_skip, 15155 [], True, True, 15156 '--depth', 'immediates', 15157 B_COPY_path) 15158 15159#---------------------------------------------------------------------- 15160# Test for issue #3646 'cyclic --record-only merges create self-referential 15161# mergeinfo' 15162@SkipUnless(server_has_mergeinfo) 15163@Issue(3646) 15164def record_only_merge_creates_self_referential_mergeinfo(sbox): 15165 "merge creates self referential mergeinfo" 15166 15167 # Given a copy of trunk@M to branch, committed in r(M+1), if we 15168 # --record-only merge the branch back to trunk with no revisions 15169 # specified, then trunk gets self-referential mergeinfo recorded 15170 # reflecting its entire natural history. 15171 15172 # Setup a standard greek tree in r1. 15173 sbox.build() 15174 wc_dir = sbox.wc_dir 15175 15176 # Some paths we'll care about 15177 mu_path = sbox.ospath('A/mu') 15178 A_path = sbox.ospath('A') 15179 A_branch_path = sbox.ospath('A-branch') 15180 15181 # Make a change to A/mu in r2. 15182 svntest.main.file_write(mu_path, "Trunk edit\n") 15183 svntest.actions.run_and_verify_svn(None, [], 'ci', '-m', 'trunk edit', 15184 wc_dir) 15185 svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir) 15186 # Copy A to A-branch in r3 15187 svntest.actions.run_and_verify_svn(None, [], 15188 'copy', A_path, A_branch_path) 15189 svntest.actions.run_and_verify_svn(None, [], 'ci', 15190 '-m', 'Branch A to A-branch', wc_dir) 15191 svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir) 15192 15193 # Merge A-branch back to A. This should record the mergeinfo '/A-branch:3' 15194 # on A. 15195 expected_output = wc.State(A_path, {}) 15196 expected_mergeinfo_output = wc.State(A_path, { 15197 '' : Item(status=' U'), 15198 }) 15199 expected_elision_output = wc.State(A_path, {}) 15200 expected_A_status = wc.State(A_path, { 15201 '' : Item(status=' M'), 15202 'B' : Item(status=' '), 15203 'mu' : Item(status=' '), 15204 'B/E' : Item(status=' '), 15205 'B/E/alpha' : Item(status=' '), 15206 'B/E/beta' : Item(status=' '), 15207 'B/lambda' : Item(status=' '), 15208 'B/F' : Item(status=' '), 15209 'C' : Item(status=' '), 15210 'D' : Item(status=' '), 15211 'D/G' : Item(status=' '), 15212 'D/G/pi' : Item(status=' '), 15213 'D/G/rho' : Item(status=' '), 15214 'D/G/tau' : Item(status=' '), 15215 'D/gamma' : Item(status=' '), 15216 'D/H' : Item(status=' '), 15217 'D/H/chi' : Item(status=' '), 15218 'D/H/psi' : Item(status=' '), 15219 'D/H/omega' : Item(status=' '), 15220 }) 15221 expected_A_status.tweak(wc_rev=3) 15222 expected_A_disk = wc.State('', { 15223 '' : Item(props={SVN_PROP_MERGEINFO : '/A-branch:3'}), 15224 'B' : Item(), 15225 'mu' : Item("Trunk edit\n"), 15226 'B/E' : Item(), 15227 'B/E/alpha' : Item("This is the file 'alpha'.\n"), 15228 'B/E/beta' : Item("This is the file 'beta'.\n"), 15229 'B/lambda' : Item("This is the file 'lambda'.\n"), 15230 'B/F' : Item(), 15231 'C' : Item(), 15232 'D' : Item(), 15233 'D/G' : Item(), 15234 'D/G/pi' : Item("This is the file 'pi'.\n"), 15235 'D/G/rho' : Item("This is the file 'rho'.\n"), 15236 'D/G/tau' : Item("This is the file 'tau'.\n"), 15237 'D/gamma' : Item("This is the file 'gamma'.\n"), 15238 'D/H' : Item(), 15239 'D/H/chi' : Item("This is the file 'chi'.\n"), 15240 'D/H/psi' : Item("This is the file 'psi'.\n"), 15241 'D/H/omega' : Item("This is the file 'omega'.\n"), 15242 }) 15243 expected_A_skip = wc.State(A_path, {}) 15244 svntest.actions.run_and_verify_merge(A_path, None, None, 15245 sbox.repo_url + '/A-branch', None, 15246 expected_output, 15247 expected_mergeinfo_output, 15248 expected_elision_output, 15249 expected_A_disk, 15250 expected_A_status, 15251 expected_A_skip, 15252 [], True, True, 15253 '--record-only', A_path) 15254 15255#---------------------------------------------------------------------- 15256# Test for issue #3657 'dav update report handler in skelta mode can cause 15257# spurious conflicts'. 15258@Issue(3657) 15259def dav_skelta_mode_causes_spurious_conflicts(sbox): 15260 "dav skelta mode can cause spurious conflicts" 15261 15262 sbox.build() 15263 wc_dir = sbox.wc_dir 15264 15265 # Some paths we'll care about 15266 mu_path = sbox.ospath('A/mu') 15267 A_path = sbox.ospath('A') 15268 C_path = sbox.ospath('A/C') 15269 A_branch_path = sbox.ospath('A-branch') 15270 C_branch_path = sbox.ospath('A-branch/C') 15271 15272 # r2 - Set some initial properties: 15273 # 15274 # 'dir-prop'='value1' on A/C. 15275 # 'svn:eol-style'='native' on A/mu. 15276 svntest.actions.run_and_verify_svn(None, [], 15277 'ps', 'dir-prop', 'initial-val', 15278 C_path) 15279 svntest.actions.run_and_verify_svn(None, [], 15280 'ps', 'svn:eol-style', 'native', 15281 mu_path) 15282 svntest.actions.run_and_verify_svn(None, [], 15283 'ci', '-m', 'Set some properties', 15284 wc_dir) 15285 15286 # r3 - Branch 'A' to 'A-branch': 15287 svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir) 15288 svntest.actions.run_and_verify_svn(None, [], 15289 'copy', A_path, A_branch_path) 15290 svntest.actions.run_and_verify_svn(None, [], 15291 'ci', '-m', 'Create a branch of A', 15292 wc_dir) 15293 15294 # r4 - Make a text mod to 'A/mu' and add new props to 'A/mu' and 'A/C': 15295 svntest.main.file_write(mu_path, "The new mu!\n") 15296 svntest.actions.run_and_verify_svn(None, [], 15297 'ps', 'prop-name', 'prop-val', mu_path) 15298 svntest.actions.run_and_verify_svn(None, [], 15299 'ps', 'another-dir-prop', 'initial-val', 15300 C_path) 15301 svntest.actions.run_and_verify_svn(None, [], 'ci', '-m', 15302 'Edit a file and make some prop changes', 15303 wc_dir) 15304 15305 # r5 - Modify the sole property on 'A-branch/C': 15306 svntest.actions.run_and_verify_svn(None, [], 15307 'ps', 'dir-prop', 'branch-val', 15308 C_branch_path) 15309 svntest.actions.run_and_verify_svn(None, [], 15310 'ci', '-m', 'prop mod on branch', wc_dir) 15311 15312 # Now merge r4 from 'A' to 'A-branch'. 15313 # 15314 # Previously this failed over ra_neon and ra_serf on Windows: 15315 # 15316 # >svn merge ^^/A A-branch -c4 15317 # Conflict discovered in 'C:/SVN/src-trunk/Debug/subversion/tests/cmdline 15318 # /svn-test-work/working_copies/merge_tests-110/A-branch/mu'. 15319 # Select: (p) postpone, (df) diff-full, (e) edit, 15320 # (mc) mine-conflict, (tc) theirs-conflict, 15321 # (s) show all options: p 15322 # --- Merging r4 into 'A-branch': 15323 # CU A-branch\mu 15324 # Conflict for property 'another-dir-prop' discovered on 'C:/SVN/src-trunk 15325 # /Debug/subversion/tests/cmdline/svn-test-work/working_copies/ 15326 # merge_tests-110/A-branch/C'. 15327 # Select: (p) postpone, 15328 # (mf) mine-full, (tf) theirs-full, 15329 # (s) show all options: p 15330 # C A-branch\C 15331 # --- Recording mergeinfo for merge of r4 into 'A-branch': 15332 # U A-branch 15333 # Summary of conflicts: 15334 # Text conflicts: 1 15335 # Property conflicts: 1 15336 svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir) 15337 expected_output = wc.State(A_branch_path, { 15338 'mu' : Item(status='UU'), 15339 'C' : Item(status=' U'), 15340 }) 15341 expected_mergeinfo_output = wc.State(A_branch_path, { 15342 '' : Item(status=' U'), 15343 }) 15344 expected_elision_output = wc.State(A_branch_path, {}) 15345 expected_status = wc.State(A_branch_path, { 15346 '' : Item(status=' M'), 15347 'B' : Item(status=' '), 15348 'mu' : Item(status='MM'), 15349 'B/E' : Item(status=' '), 15350 'B/E/alpha' : Item(status=' '), 15351 'B/E/beta' : Item(status=' '), 15352 'B/lambda' : Item(status=' '), 15353 'B/F' : Item(status=' '), 15354 'C' : Item(status=' M'), 15355 'D' : Item(status=' '), 15356 'D/G' : Item(status=' '), 15357 'D/G/pi' : Item(status=' '), 15358 'D/G/rho' : Item(status=' '), 15359 'D/G/tau' : Item(status=' '), 15360 'D/gamma' : Item(status=' '), 15361 'D/H' : Item(status=' '), 15362 'D/H/chi' : Item(status=' '), 15363 'D/H/psi' : Item(status=' '), 15364 'D/H/omega' : Item(status=' '), 15365 }) 15366 expected_status.tweak(wc_rev=5) 15367 expected_disk = wc.State('', { 15368 '' : Item(props={SVN_PROP_MERGEINFO : 15369 '/A:4'}), 15370 'B' : Item(), 15371 'mu' : Item("The new mu!\n", 15372 props={'prop-name' : 'prop-val', 15373 'svn:eol-style' : 'native'}), 15374 'B/E' : Item(), 15375 'B/E/alpha' : Item("This is the file 'alpha'.\n"), 15376 'B/E/beta' : Item("This is the file 'beta'.\n"), 15377 'B/lambda' : Item("This is the file 'lambda'.\n"), 15378 'B/F' : Item(), 15379 'C' : Item(props={'dir-prop' : 'branch-val', 15380 'another-dir-prop' : 'initial-val'}), 15381 'D' : Item(), 15382 'D/G' : Item(), 15383 'D/G/pi' : Item("This is the file 'pi'.\n"), 15384 'D/G/rho' : Item("This is the file 'rho'.\n"), 15385 'D/G/tau' : Item("This is the file 'tau'.\n"), 15386 'D/gamma' : Item("This is the file 'gamma'.\n"), 15387 'D/H' : Item(), 15388 'D/H/chi' : Item("This is the file 'chi'.\n"), 15389 'D/H/psi' : Item("This is the file 'psi'.\n"), 15390 'D/H/omega' : Item("This is the file 'omega'.\n"), 15391 }) 15392 expected_skip = wc.State(A_branch_path, {}) 15393 svntest.actions.run_and_verify_merge(A_branch_path, 3, 4, 15394 sbox.repo_url + '/A', 15395 None, 15396 expected_output, 15397 expected_mergeinfo_output, 15398 expected_elision_output, 15399 expected_disk, 15400 expected_status, 15401 expected_skip, 15402 [], True, True) 15403 15404 15405#---------------------------------------------------------------------- 15406def merge_into_locally_added_file(sbox): 15407 "merge into locally added file" 15408 15409 sbox.build() 15410 wc_dir = sbox.wc_dir 15411 15412 # Some paths we'll care about 15413 pi_path = sbox.ospath("A/D/G/pi") 15414 new_path = sbox.ospath("A/D/G/new") 15415 15416 shutil.copy(pi_path, new_path) 15417 svntest.main.file_append(pi_path, "foo\n") 15418 sbox.simple_commit() # r2 15419 15420 sbox.simple_add('A/D/G/new') 15421 15422 expected_output = wc.State(wc_dir, { 15423 'A/D/G/new' : Item(status='G '), 15424 }) 15425 expected_mergeinfo_output = wc.State(wc_dir, { 15426 'A/D/G/new' : Item(status=' U'), 15427 }) 15428 expected_elision_output = wc.State(wc_dir, {}) 15429 expected_status = svntest.actions.get_virginal_state(wc_dir, 1) 15430 expected_status.add({ 'A/D/G/new' : Item(status='A ', wc_rev=0)}) 15431 expected_status.tweak('A/D/G/pi', wc_rev=2) 15432 expected_disk = svntest.main.greek_state.copy() 15433 expected_disk.tweak('A/D/G/pi', contents="This is the file 'pi'.\nfoo\n") 15434 expected_disk.add({'A/D/G/new' : Item("This is the file 'pi'.\nfoo\n", 15435 props={SVN_PROP_MERGEINFO : '/A/D/G/pi:2'})}) 15436 expected_skip = wc.State(wc_dir, {}) 15437 15438 svntest.actions.run_and_verify_merge(wc_dir, '1', '2', 15439 sbox.repo_url + '/A/D/G/pi', None, 15440 expected_output, 15441 expected_mergeinfo_output, 15442 expected_elision_output, 15443 expected_disk, 15444 expected_status, 15445 expected_skip, 15446 [], True, True, 15447 new_path) 15448 sbox.simple_commit() 15449 15450#---------------------------------------------------------------------- 15451def merge_into_locally_added_directory(sbox): 15452 "merge into locally added directory" 15453 15454 sbox.build() 15455 wc_dir = sbox.wc_dir 15456 15457 # Some paths we'll care about 15458 G_path = sbox.ospath("A/D/G") 15459 pi_path = sbox.ospath("A/D/G/pi") 15460 new_dir_path = sbox.ospath("A/D/new_dir") 15461 15462 svntest.main.file_append_binary(pi_path, "foo\n") 15463 sbox.simple_commit() # r2 15464 15465 os.mkdir(new_dir_path) 15466 svntest.main.file_append_binary(os.path.join(new_dir_path, 'pi'), 15467 "This is the file 'pi'.\n") 15468 svntest.main.file_append_binary(os.path.join(new_dir_path, 'rho'), 15469 "This is the file 'rho'.\n") 15470 svntest.main.file_append_binary(os.path.join(new_dir_path, 'tau'), 15471 "This is the file 'tau'.\n") 15472 sbox.simple_add('A/D/new_dir') 15473 15474 expected_output = wc.State(wc_dir, { 15475 'A/D/new_dir/pi' : Item(status='G '), 15476 }) 15477 expected_mergeinfo_output = wc.State(wc_dir, { 15478 'A/D/new_dir' : Item(status=' U'), 15479 }) 15480 expected_elision_output = wc.State(wc_dir, {}) 15481 expected_status = svntest.actions.get_virginal_state(wc_dir, 1) 15482 expected_status.add({ 'A/D/new_dir' : Item(status='A ', wc_rev=0)}) 15483 expected_status.add({ 'A/D/new_dir/pi' : Item(status='A ', wc_rev=0)}) 15484 expected_status.add({ 'A/D/new_dir/rho' : Item(status='A ', wc_rev=0)}) 15485 expected_status.add({ 'A/D/new_dir/tau' : Item(status='A ', wc_rev=0)}) 15486 expected_status.tweak('A/D/G/pi', wc_rev=2) 15487 expected_disk = svntest.main.greek_state.copy() 15488 expected_disk.tweak('A/D/G/pi', contents="This is the file 'pi'.\nfoo\n") 15489 expected_disk.add({'A/D/new_dir' : 15490 Item(props={SVN_PROP_MERGEINFO : '/A/D/G:2'})}) 15491 expected_disk.add({'A/D/new_dir/pi' : 15492 Item(contents="This is the file 'pi'.\nfoo\n")}) 15493 expected_disk.add({'A/D/new_dir/rho' : 15494 Item(contents="This is the file 'rho'.\n")}) 15495 expected_disk.add({'A/D/new_dir/tau' : 15496 Item(contents="This is the file 'tau'.\n")}) 15497 expected_skip = wc.State(wc_dir, {}) 15498 15499 svntest.actions.run_and_verify_merge(wc_dir, '1', '2', 15500 sbox.repo_url + '/A/D/G', None, 15501 expected_output, 15502 expected_mergeinfo_output, 15503 expected_elision_output, 15504 expected_disk, 15505 expected_status, 15506 expected_skip, 15507 [], True, True, 15508 new_dir_path) 15509 sbox.simple_commit() 15510 15511#---------------------------------------------------------------------- 15512# Test for issue #2915 'Handle mergeinfo for subtrees missing due to removal 15513# by non-svn command' 15514@SkipUnless(server_has_mergeinfo) 15515@Issue(2915) 15516def merge_with_os_deleted_subtrees(sbox): 15517 "merge tracking fails if target missing subtrees" 15518 15519 # r1: Create a greek tree. 15520 sbox.build() 15521 wc_dir = sbox.wc_dir 15522 15523 # r2 - r6: Copy A to A_COPY and then make some text changes under A. 15524 set_up_branch(sbox) 15525 15526 # Some paths we'll care about 15527 A_COPY_path = sbox.ospath('A_COPY') 15528 C_COPY_path = sbox.ospath('A_COPY/C') 15529 psi_COPY_path = sbox.ospath('A_COPY/D/H/psi') 15530 mu_COPY_path = sbox.ospath('A_COPY/mu') 15531 G_COPY_path = sbox.ospath('A_COPY/D/G') 15532 15533 # Remove several subtrees from disk. 15534 svntest.main.safe_rmtree(C_COPY_path) 15535 svntest.main.safe_rmtree(G_COPY_path) 15536 os.remove(psi_COPY_path) 15537 os.remove(mu_COPY_path) 15538 15539 # Be sure the regex paths are properly escaped on Windows, see the 15540 # note about "The Backslash Plague" in expected_merge_output(). 15541 if sys.platform == 'win32': 15542 re_sep = '\\\\' 15543 else: 15544 re_sep = os.sep 15545 15546 # Common part of the expected error message for all cases we will test. 15547 err_re = "svn: E195016: Merge tracking not allowed with missing subtrees; " + \ 15548 "try restoring these items first:" + \ 15549 "|(\n)" + \ 15550 "|" + svntest.main.stack_trace_regexp 15551 15552 # Case 1: Infinite depth merge into infinite depth WC target. 15553 # Every missing subtree under the target should be reported as missing. 15554 missing = "|(.*A_COPY" + re_sep + "mu\n)" + \ 15555 "|(.*A_COPY" + re_sep + "D" + re_sep + "G\n)" + \ 15556 "|(.*A_COPY" + re_sep + "C\n)" + \ 15557 "|(.*A_COPY" + re_sep + "D" + re_sep + "H" + re_sep + "psi\n)" 15558 exit_code, out, err = svntest.actions.run_and_verify_svn( 15559 [], svntest.verify.AnyOutput, 15560 'merge', sbox.repo_url + '/A', A_COPY_path) 15561 svntest.verify.verify_outputs("Merge failed but not in the way expected", 15562 err, None, err_re + missing, None, 15563 True) # Match *all* lines of stderr 15564 15565 # Case 2: Immediates depth merge into infinite depth WC target. 15566 # Only the two immediate children of the merge target should be reported 15567 # as missing. 15568 missing = "|(.*A_COPY" + re_sep + "mu\n)" + \ 15569 "|(.*A_COPY" + re_sep + "C\n)" 15570 exit_code, out, err = svntest.actions.run_and_verify_svn( 15571 [], svntest.verify.AnyOutput, 15572 'merge', sbox.repo_url + '/A', A_COPY_path, '--depth=immediates') 15573 svntest.verify.verify_outputs("Merge failed but not in the way expected", 15574 err, None, err_re + missing, None, True) 15575 15576 # Case 3: Files depth merge into infinite depth WC target. 15577 # Only the single file child of the merge target should be reported 15578 # as missing. 15579 missing = "|(.*A_COPY" + re_sep + "mu\n)" 15580 exit_code, out, err = svntest.actions.run_and_verify_svn( 15581 [], svntest.verify.AnyOutput, 15582 'merge', sbox.repo_url + '/A', A_COPY_path, '--depth=files') 15583 svntest.verify.verify_outputs("Merge failed but not in the way expected", 15584 err, None, err_re + missing, None, True) 15585 15586 # Case 4: Empty depth merge into infinite depth WC target. 15587 # Only the...oh, wait, the target is present and that is as deep 15588 # as the merge goes, so this merge should succeed! 15589 svntest.actions.run_and_verify_svn( 15590 svntest.verify.AnyOutput, [], 'merge', sbox.repo_url + '/A', 15591 A_COPY_path, '--depth=empty') 15592 15593#---------------------------------------------------------------------- 15594# Test for issue #3668 'inheritance can result in self-referential 15595# mergeinfo' and issue #3669 'inheritance can result in mergeinfo 15596# describing nonexistent sources' 15597@Issue(3668,3669) 15598@XFail() 15599def no_self_referential_or_nonexistent_inherited_mergeinfo(sbox): 15600 "don't inherit bogus mergeinfo" 15601 15602 # r1: Create a greek tree. 15603 sbox.build() 15604 wc_dir = sbox.wc_dir 15605 15606 # r2 - r6: Copy A to A_COPY and then make some text changes under A. 15607 set_up_branch(sbox, nbr_of_branches=1) 15608 15609 # Some paths we'll care about 15610 nu_path = sbox.ospath('A/C/nu') 15611 nu_COPY_path = sbox.ospath('A_COPY/C/nu') 15612 J_path = sbox.ospath('A/D/J') 15613 J_COPY_path = sbox.ospath('A_COPY/D/J') 15614 zeta_path = sbox.ospath('A/D/J/zeta') 15615 A_COPY_path = sbox.ospath('A_COPY') 15616 15617 # r7 - Add the file A/C/nu 15618 svntest.main.file_write(nu_path, "This is the file 'nu'.\n") 15619 svntest.actions.run_and_verify_svn(None, [], 'add', nu_path) 15620 svntest.actions.run_and_verify_svn(None, [], 'commit', 15621 '-m', 'Add file', wc_dir) 15622 15623 # r8 - Sync merge A to A_COPY 15624 svntest.actions.run_and_verify_svn( 15625 svntest.verify.AnyOutput, [], 'merge', sbox.repo_url + '/A', 15626 A_COPY_path) 15627 svntest.actions.run_and_verify_svn(None, [], 'commit', 15628 '-m', 'Sync A_COPY with A', wc_dir) 15629 15630 # r9 - Add the subtree A/D/J 15631 # A/D/J/zeta 15632 svntest.actions.run_and_verify_svn(None, [], 'mkdir', J_path) 15633 svntest.main.file_write(zeta_path, "This is the file 'zeta'.\n") 15634 svntest.actions.run_and_verify_svn(None, [], 'add', zeta_path) 15635 svntest.actions.run_and_verify_svn(None, [], 'commit', 15636 '-m', 'Add subtree', wc_dir) 15637 15638 # Update the WC in preparation for merges. 15639 svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir) 15640 15641 # r10 - Sync merge A to A_COPY 15642 svntest.actions.run_and_verify_svn( 15643 svntest.verify.AnyOutput, [], 'merge', sbox.repo_url + '/A', 15644 A_COPY_path) 15645 svntest.actions.run_and_verify_svn(None, [], 'commit', 15646 '-m', 'Sync A_COPY with A', wc_dir) 15647 15648 # r11 - Text changes to A/C/nu and A/D/J/zeta. 15649 svntest.main.file_write(nu_path, "This is the EDITED file 'nu'.\n") 15650 svntest.main.file_write(zeta_path, "This is the EDITED file 'zeta'.\n") 15651 svntest.actions.run_and_verify_svn(None, [], 'commit', 15652 '-m', 'Edit added files', wc_dir) 15653 15654 # Update the WC in preparation for merges. 15655 svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir) 15656 15657 # This test is marked as XFail because the following two merges 15658 # create mergeinfo with both non-existent path-revs and self-referential 15659 # mergeinfo. 15660 # 15661 # Merge all available revisions from A/C/nu to A_COPY/C/nu. 15662 # The target has no explicit mergeinfo of its own but inherits mergeinfo 15663 # from A_COPY. A_COPY has the mergeinfo '/A:2-9' so the naive mergeinfo 15664 # A_COPY/C/nu inherits is '/A/C/nu:2-9'. However, '/A/C/nu:2-6' don't 15665 # actually exist (issue #3669) and '/A/C/nu:7-8' is self-referential 15666 # (issue #3668). Neither of these should be present in the resulting 15667 # mergeinfo for A_COPY/C/nu, only '/A/C/nu:8-11' 15668 expected_output = wc.State(nu_COPY_path, { 15669 '' : Item(status='U '), 15670 }) 15671 expected_mergeinfo_output = wc.State(nu_COPY_path, { 15672 '' : Item(status=' G'), 15673 }) 15674 expected_elision_output = wc.State(nu_COPY_path, { 15675 }) 15676 expected_status = wc.State(nu_COPY_path, { 15677 '' : Item(status='MM', wc_rev=11), 15678 }) 15679 expected_disk = wc.State('', { 15680 '' : Item(props={SVN_PROP_MERGEINFO : '/A/C/nu:8-11'}), 15681 }) 15682 expected_skip = wc.State(nu_COPY_path, { }) 15683 svntest.actions.run_and_verify_merge(nu_COPY_path, None, None, 15684 sbox.repo_url + '/A/C/nu', None, 15685 expected_output, 15686 expected_mergeinfo_output, 15687 expected_elision_output, 15688 expected_disk, 15689 expected_status, 15690 expected_skip, 15691 check_props=True) 15692 15693 # Merge all available revisions from A/D/J to A_COPY/D/J. Like the 15694 # previous merge, the target should not have any non-existent ('/A/D/J:2-8') 15695 # or self-referential mergeinfo ('/A/D/J:9') recorded on it post-merge. 15696 expected_output = wc.State(J_COPY_path, { 15697 'zeta' : Item(status='U '), 15698 }) 15699 expected_mergeinfo_output = wc.State(J_COPY_path, { 15700 '' : Item(status=' G'), 15701 }) 15702 expected_elision_output = wc.State(J_COPY_path, { 15703 }) 15704 expected_status = wc.State(J_COPY_path, { 15705 '' : Item(status=' M', wc_rev=11), 15706 'zeta' : Item(status='M ', wc_rev=11), 15707 }) 15708 expected_disk = wc.State('', { 15709 '' : Item(props={SVN_PROP_MERGEINFO : '/A/D/J:10-11'}), 15710 'zeta' : Item("This is the EDITED file 'zeta'.\n") 15711 }) 15712 expected_skip = wc.State(J_COPY_path, { }) 15713 svntest.actions.run_and_verify_merge(J_COPY_path, None, None, 15714 sbox.repo_url + '/A/D/J', None, 15715 expected_output, 15716 expected_mergeinfo_output, 15717 expected_elision_output, 15718 expected_disk, 15719 expected_status, 15720 expected_skip, 15721 check_props=True) 15722 15723#---------------------------------------------------------------------- 15724# Test for issue #3756 'subtree merge can inherit invalid working mergeinfo', 15725# issue #3668 'inheritance can result in self-referential mergeinfo', and 15726# issue #3669 'inheritance can result in mergeinfo describing nonexistent 15727# sources'. 15728@XFail() 15729@Issue(3756,3668,3669) 15730def subtree_merges_inherit_invalid_working_mergeinfo(sbox): 15731 "don't inherit bogus working mergeinfo" 15732 15733 # r1: Create a greek tree. 15734 sbox.build() 15735 wc_dir = sbox.wc_dir 15736 15737 # r2 - r6: Copy A to A_COPY and then make some text changes under A. 15738 set_up_branch(sbox, nbr_of_branches=1) 15739 15740 # Some paths we'll care about 15741 nu_path = sbox.ospath('A/C/nu') 15742 nu_COPY_path = sbox.ospath('A_COPY/C/nu') 15743 A_COPY_path = sbox.ospath('A_COPY') 15744 15745 # r7 - Add the file A/C/nu 15746 svntest.main.file_write(nu_path, "This is the file 'nu'.\n") 15747 svntest.actions.run_and_verify_svn(None, [], 'add', nu_path) 15748 svntest.actions.run_and_verify_svn(None, [], 'commit', 15749 '-m', 'Add file', wc_dir) 15750 15751 # r8 Merge c7 from A to A_COPY. 15752 svntest.actions.run_and_verify_svn( 15753 svntest.verify.AnyOutput, [], 'merge', sbox.repo_url + '/A', 15754 A_COPY_path, '-c7') 15755 svntest.actions.run_and_verify_svn(None, [], 'commit', 15756 '-m', 'Merge subtree file addition', 15757 wc_dir) 15758 15759 # r9 - A text change to A/C/nu. 15760 svntest.main.file_write(nu_path, "This is the EDITED file 'nu'.\n") 15761 svntest.actions.run_and_verify_svn(None, [], 'commit', 15762 '-m', 'Edit added file', wc_dir) 15763 15764 # Update the WC in preparation for merges. 15765 svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir) 15766 15767 # Now do two merges. The first, r3 to the root of the branch A_COPY. 15768 # This creates working mergeinfo '/A:3,7' on A_COPY. Then do a subtree 15769 # file merge of r9 from A/C/nu to A_COPY/C/nu. Since the target has no 15770 # explicit mergeinfo, the mergeinfo set to record the merge of r9 should 15771 # include the mergeinfo inherited from A_COPY. *But* that raw inherited 15772 # mergeinfo, '/A/C/nu:3,7' is wholly invalid: '/A/C/nu:3' simply doesn't 15773 # exist in the repository and '/A/C/nu:7' is self-referential. So the 15774 # resulting mergeinfo on 'A_COPY/C/nu' should be only '/A/C/nu:9'. 15775 # 15776 # Currently this test is marked as XFail because the resulting mergeinfo is 15777 # '/A/C/nu:3,7,9' and thus includes a non-existent path-rev. 15778 svntest.actions.run_and_verify_svn( 15779 svntest.verify.AnyOutput, [], 'merge', sbox.repo_url + '/A', 15780 A_COPY_path, '-c3') 15781 svntest.actions.run_and_verify_svn( 15782 svntest.verify.AnyOutput, [], 'merge', sbox.repo_url + '/A/C/nu', 15783 nu_COPY_path, '-c9') 15784 svntest.actions.run_and_verify_svn( 15785 '/A/C/nu:9', [], 'pg', SVN_PROP_MERGEINFO, nu_COPY_path) 15786 15787 15788#---------------------------------------------------------------------- 15789# Test for issue #3686 'executable flag not correctly set on merge' 15790# See https://issues.apache.org/jira/browse/SVN-3686 15791@Issue(3686) 15792@SkipUnless(server_has_mergeinfo) 15793@SkipUnless(svntest.main.is_posix_os) 15794def merge_change_to_file_with_executable(sbox): 15795 "executable flag is maintained during binary merge" 15796 15797 # Scenario: When merging a change to a binary file with the 'svn:executable' 15798 # property set, the file is not marked as 'executable'. After commit, the 15799 # executable bit is set correctly. 15800 sbox.build() 15801 wc_dir = sbox.wc_dir 15802 trunk_url = sbox.repo_url + '/A/B/E' 15803 15804 alpha_path = sbox.ospath('A/B/E/alpha') 15805 beta_path = sbox.ospath('A/B/E/beta') 15806 15807 # Force one of the files to be a binary type 15808 svntest.actions.run_and_verify_svn2(None, 15809 binary_mime_type_on_text_file_warning, 0, 15810 'propset', 'svn:mime-type', 15811 'application/octet-stream', 15812 alpha_path) 15813 15814 # Set the 'svn:executable' property on both files 15815 svntest.actions.run_and_verify_svn(None, [], 15816 'propset', 'svn:executable', 'ON', 15817 beta_path) 15818 15819 svntest.actions.run_and_verify_svn(None, [], 15820 'propset', 'svn:executable', 'ON', 15821 alpha_path) 15822 15823 # Verify the executable bit has been set before committing 15824 if not os.access(alpha_path, os.X_OK): 15825 raise svntest.Failure("alpha not marked as executable before commit") 15826 if not os.access(beta_path, os.X_OK): 15827 raise svntest.Failure("beta is not marked as executable before commit") 15828 15829 # Commit change (r2) 15830 sbox.simple_commit() 15831 15832 # Verify the executable bit has remained after committing 15833 if not os.access(alpha_path, os.X_OK): 15834 raise svntest.Failure("alpha not marked as executable before commit") 15835 if not os.access(beta_path, os.X_OK): 15836 raise svntest.Failure("beta is not marked as executable before commit") 15837 15838 # Create the branch 15839 svntest.actions.run_and_verify_svn(None, [], 'cp', 15840 trunk_url, 15841 sbox.repo_url + '/branch', 15842 '-m', "Creating the Branch") 15843 15844 # Modify the files + commit (r3) 15845 svntest.main.file_append(alpha_path, 'appended alpha text') 15846 svntest.main.file_append(beta_path, 'appended beta text') 15847 sbox.simple_commit() 15848 15849 # Re-root the WC at the branch 15850 svntest.main.safe_rmtree(wc_dir) 15851 svntest.actions.run_and_verify_svn(None, [], 'checkout', 15852 sbox.repo_url + '/branch', wc_dir) 15853 15854 # Recalculate the paths 15855 alpha_path = sbox.ospath('alpha') 15856 beta_path = sbox.ospath('beta') 15857 15858 expected_output = wc.State(wc_dir, { 15859 'beta' : Item(status='U '), 15860 'alpha' : Item(status='U '), 15861 }) 15862 expected_mergeinfo_output = wc.State(wc_dir, { 15863 '' : Item(status=' U') 15864 }) 15865 expected_elision_output = wc.State(wc_dir, { 15866 }) 15867 expected_disk = wc.State('', { 15868 '.' : Item(props={'svn:mergeinfo':'/A/B/E:3-4'}), 15869 'alpha' : Item(contents="This is the file 'alpha'.\nappended alpha text", 15870 props={'svn:executable':'*', 15871 'svn:mime-type':'application/octet-stream'}), 15872 'beta' : Item(contents="This is the file 'beta'.\nappended beta text", 15873 props={"svn:executable" : '*'}), 15874 }) 15875 expected_status = wc.State(wc_dir, { 15876 '' : Item(status=' M', wc_rev='4'), 15877 'alpha' : Item(status='M ', wc_rev='4'), 15878 'beta' : Item(status='M ', wc_rev='4'), 15879 }) 15880 expected_skip = wc.State(wc_dir, { }) 15881 15882 # Merge the changes across 15883 svntest.actions.run_and_verify_merge(wc_dir, None, None, 15884 trunk_url, None, 15885 expected_output, 15886 expected_mergeinfo_output, 15887 expected_elision_output, 15888 expected_disk, 15889 expected_status, 15890 expected_skip, 15891 [], True, True) 15892 15893 15894 # Verify the executable bit has been set 15895 if not os.access(alpha_path, os.X_OK): 15896 raise svntest.Failure("alpha is not marked as executable after merge") 15897 if not os.access(beta_path, os.X_OK): 15898 raise svntest.Failure("beta is not marked as executable after merge") 15899 15900 # Commit (r4) 15901 sbox.simple_commit() 15902 15903 # Verify the executable bit has been set 15904 if not os.access(alpha_path, os.X_OK): 15905 raise svntest.Failure("alpha is not marked as executable after commit") 15906 if not os.access(beta_path, os.X_OK): 15907 raise svntest.Failure("beta is not marked as executable after commit") 15908 15909def dry_run_merge_conflicting_binary(sbox): 15910 "dry run shouldn't resolve conflicts" 15911 15912 # This test-case is to showcase the regression caused by 15913 # r1075802. Here is the link to the relevant discussion: 15914 # http://svn.haxx.se/dev/archive-2011-03/0145.shtml 15915 15916 sbox.build() 15917 wc_dir = sbox.wc_dir 15918 # Add a binary file to the project 15919 theta_contents = open(os.path.join(sys.path[0], "theta.bin"), 'rb').read() 15920 # Write PNG file data into 'A/theta'. 15921 theta_path = sbox.ospath('A/theta') 15922 svntest.main.file_write(theta_path, theta_contents, 'wb') 15923 15924 svntest.main.run_svn(None, 'add', theta_path) 15925 15926 # Commit the new binary file, creating revision 2. 15927 expected_output = svntest.wc.State(wc_dir, { 15928 'A/theta' : Item(verb='Adding (bin)'), 15929 }) 15930 expected_status = svntest.actions.get_virginal_state(wc_dir, 1) 15931 expected_status.add({ 15932 'A/theta' : Item(status=' ', wc_rev=2), 15933 }) 15934 svntest.actions.run_and_verify_commit(wc_dir, expected_output, 15935 expected_status) 15936 15937 # Make the "other" working copy 15938 other_wc = sbox.add_wc_path('other') 15939 svntest.actions.duplicate_dir(wc_dir, other_wc) 15940 15941 # Change the binary file in first working copy, commit revision 3. 15942 svntest.main.file_append(theta_path, "some extra junk") 15943 expected_output = wc.State(wc_dir, { 15944 'A/theta' : Item(verb='Sending'), 15945 }) 15946 expected_status = svntest.actions.get_virginal_state(wc_dir, 1) 15947 expected_status.add({ 15948 'A/theta' : Item(status=' ', wc_rev=3), 15949 }) 15950 svntest.actions.run_and_verify_commit(wc_dir, expected_output, 15951 expected_status) 15952 15953 # In second working copy, append different content to the binary 15954 # and attempt to 'svn merge -r 2:3'. 15955 # We should see a conflict during the merge. 15956 other_theta_path = os.path.join(other_wc, 'A', 'theta') 15957 svntest.main.file_append(other_theta_path, "some other junk") 15958 expected_output = wc.State(other_wc, { 15959 'A/theta' : Item(status='C '), 15960 }) 15961 expected_mergeinfo_output = wc.State(other_wc, { 15962 '' : Item(status=' U'), 15963 }) 15964 expected_elision_output = wc.State(other_wc, { 15965 }) 15966 expected_disk = svntest.main.greek_state.copy() 15967 expected_disk.add({ 15968 '' : Item(props={SVN_PROP_MERGEINFO : '/:3'}), 15969 'A/theta' : Item(theta_contents + b"some other junk", 15970 props={'svn:mime-type' : 'application/octet-stream'}), 15971 }) 15972 15973 # verify content of base(left) file 15974 expected_disk.add({ 15975 'A/theta.merge-left.r2' : 15976 Item(contents = theta_contents ) 15977 }) 15978 # verify content of theirs(right) file 15979 expected_disk.add({ 15980 'A/theta.merge-right.r3' : 15981 Item(contents= theta_contents + b"some extra junk") 15982 }) 15983 15984 expected_status = svntest.actions.get_virginal_state(other_wc, 1) 15985 expected_status.add({ 15986 '' : Item(status=' M', wc_rev=1), 15987 'A/theta' : Item(status='C ', wc_rev=2), 15988 }) 15989 expected_skip = wc.State('', { }) 15990 15991 svntest.actions.run_and_verify_merge(other_wc, '2', '3', 15992 sbox.repo_url, None, 15993 expected_output, 15994 expected_mergeinfo_output, 15995 expected_elision_output, 15996 expected_disk, 15997 expected_status, 15998 expected_skip, 15999 [], True, True, 16000 '--allow-mixed-revisions', 16001 other_wc) 16002 16003#---------------------------------------------------------------------- 16004@Issue(3857) 16005def foreign_repos_prop_conflict(sbox): 16006 "prop conflict from foreign repos merge" 16007 16008 sbox.build() 16009 wc_dir = sbox.wc_dir 16010 16011 # Create a second repository and working copy with the original 16012 # greek tree. 16013 repo_dir = sbox.repo_dir 16014 other_repo_dir, other_repo_url = sbox.add_repo_path("other") 16015 other_wc_dir = sbox.add_wc_path("other") 16016 svntest.main.copy_repos(repo_dir, other_repo_dir, 1, 1) 16017 svntest.actions.run_and_verify_svn(None, [], 'co', other_repo_url, 16018 other_wc_dir) 16019 16020 # Add properties in the first repos and commit. 16021 sbox.simple_propset('red', 'rojo', 'A/D/G') 16022 sbox.simple_propset('yellow', 'amarillo', 'A/D/G') 16023 svntest.actions.run_and_verify_svn(None, [], 16024 'ci', '-m', 'spenglish', wc_dir) 16025 16026 # Tweak properties in the first repos and commit. 16027 sbox.simple_propset('red', 'rosso', 'A/D/G') 16028 sbox.simple_propset('yellow', 'giallo', 'A/D/G') 16029 svntest.actions.run_and_verify_svn(None, [], 16030 'ci', '-m', 'engtalian', wc_dir) 16031 16032 # Now, merge the propchange to the *second* working copy. 16033 expected_output = [' C %s\n' % (os.path.join(other_wc_dir, 16034 "A", "D", "G"))] 16035 expected_output = expected_merge_output([[3]], expected_output, True, 16036 prop_conflicts=1) 16037 svntest.actions.run_and_verify_svn(expected_output, 16038 [], 'merge', '-c3', 16039 sbox.repo_url, 16040 other_wc_dir) 16041 16042#---------------------------------------------------------------------- 16043# Test for issue #3975 'adds with explicit mergeinfo don't get mergeinfo 16044# describing merge which added them' 16045@Issue(3975) 16046@SkipUnless(server_has_mergeinfo) 16047def merge_adds_subtree_with_mergeinfo(sbox): 16048 "merge adds subtree with mergeinfo" 16049 16050 sbox.build() 16051 os.chdir(sbox.wc_dir) 16052 sbox.wc_dir = '' 16053 wc_dir = sbox.wc_dir 16054 wc_disk, wc_status = set_up_branch(sbox, False, 2) 16055 16056 A_path = sbox.ospath('A') 16057 nu_path = sbox.ospath('A/C/nu') 16058 nu_COPY_path = sbox.ospath('A_COPY/C/nu') 16059 A_COPY2_path = sbox.ospath('A_COPY_2') 16060 16061 # r8 - Add the file A_COPY/C/nu. 16062 svntest.main.file_write(nu_COPY_path, "This is the file 'nu'.\n") 16063 svntest.actions.run_and_verify_svn(None, [], 'add', nu_COPY_path) 16064 svntest.actions.run_and_verify_svn(None, [], 'ci', '-m', 16065 'Add a file on the A_COPY branch', 16066 wc_dir) 16067 16068 # r9 - Cherry pick r8 from A_COPY to A. 16069 svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir) 16070 svntest.actions.run_and_verify_svn(None, [], 'merge', 16071 sbox.repo_url + '/A_COPY', 16072 A_path, '-c8') 16073 svntest.actions.run_and_verify_svn(None, [], 'ci', '-m', 16074 'Merge r8 from A_COPY to A', wc_dir) 16075 16076 # r10 - Make a modification to A_COPY/C/nu 16077 svntest.main.file_append(nu_COPY_path, 16078 "More work on the A_COPY branch.\n") 16079 svntest.actions.run_and_verify_svn(None, [], 'ci', '-m', 16080 'Some work on the A_COPY branch', wc_dir) 16081 16082 # r9 - Cherry pick r10 from A_COPY/C/nu to A/C/nu. Make some 16083 # changes to A/C/nu before committing the merge. 16084 svntest.actions.run_and_verify_svn(None, [], 'merge', 16085 sbox.repo_url + '/A_COPY/C/nu', 16086 nu_path, '-c10') 16087 svntest.main.file_append(nu_path, "A faux conflict resolution.\n") 16088 svntest.actions.run_and_verify_svn(None, [], 'ci', '-m', 16089 'Merge r8 from A_COPY to A', wc_dir) 16090 16091 # Sync merge A to A_COPY_2 16092 svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir) 16093 expected_output = wc.State(A_COPY2_path, { 16094 'B/E/beta' : Item(status='U '), 16095 'C/nu' : Item(status='A '), 16096 'D/G/rho' : Item(status='U '), 16097 'D/H/omega' : Item(status='U '), 16098 'D/H/psi' : Item(status='U '), 16099 '' : Item(status=' U'), 16100 }) 16101 expected_mergeinfo_output = wc.State(A_COPY2_path, { 16102 '' : Item(status=' G'), 16103 'C/nu' : Item(status=' U'), 16104 }) 16105 expected_elision_output = wc.State(A_COPY2_path, { 16106 }) 16107 expected_status = wc.State(A_COPY2_path, { 16108 '' : Item(status=' M'), 16109 'B' : Item(status=' '), 16110 'mu' : Item(status=' '), 16111 'B/E' : Item(status=' '), 16112 'B/E/alpha' : Item(status=' '), 16113 'B/E/beta' : Item(status='M '), 16114 'B/lambda' : Item(status=' '), 16115 'B/F' : Item(status=' '), 16116 'C' : Item(status=' '), 16117 'C/nu' : Item(status='A ', copied='+'), 16118 'D' : Item(status=' '), 16119 'D/G' : Item(status=' '), 16120 'D/G/pi' : Item(status=' '), 16121 'D/G/rho' : Item(status='M '), 16122 'D/G/tau' : Item(status=' '), 16123 'D/gamma' : Item(status=' '), 16124 'D/H' : Item(status=' '), 16125 'D/H/chi' : Item(status=' '), 16126 'D/H/psi' : Item(status='M '), 16127 'D/H/omega' : Item(status='M '), 16128 }) 16129 expected_status.tweak(wc_rev=11) 16130 expected_status.tweak('C/nu', wc_rev='-') 16131 expected_disk = wc.State('', { 16132 '' : Item(props={SVN_PROP_MERGEINFO : '/A:3-11\n/A_COPY:8'}), 16133 'B' : Item(), 16134 'mu' : Item("This is the file 'mu'.\n"), 16135 'B/E' : Item(), 16136 'B/E/alpha' : Item("This is the file 'alpha'.\n"), 16137 'B/E/beta' : Item("New content"), 16138 'B/lambda' : Item("This is the file 'lambda'.\n"), 16139 'B/F' : Item(), 16140 'C' : Item(), 16141 # C/nu will pick up the mergeinfo A_COPY/C/nu:8 which is self-referential. 16142 # This is issue #3668 'inheritance can result in self-referential 16143 # mergeinfo', but we'll allow it in this test since issue #3668 is 16144 # tested elsewhere and is not the point of *this* test. 16145 'C/nu' : Item("This is the file 'nu'.\n" \ 16146 "More work on the A_COPY branch.\n" \ 16147 "A faux conflict resolution.\n", 16148 props={SVN_PROP_MERGEINFO : 16149 '/A/C/nu:9-11\n/A_COPY/C/nu:8,10'}), 16150 'D' : Item(), 16151 'D/G' : Item(), 16152 'D/G/pi' : Item("This is the file 'pi'.\n"), 16153 'D/G/rho' : Item("New content"), 16154 'D/G/tau' : Item("This is the file 'tau'.\n"), 16155 'D/gamma' : Item("This is the file 'gamma'.\n"), 16156 'D/H' : Item(), 16157 'D/H/chi' : Item("This is the file 'chi'.\n"), 16158 'D/H/psi' : Item("New content"), 16159 'D/H/omega' : Item("New content"), 16160 }) 16161 expected_skip = wc.State('.', { }) 16162 svntest.actions.run_and_verify_merge(A_COPY2_path, None, None, 16163 sbox.repo_url + '/A', None, 16164 expected_output, 16165 expected_mergeinfo_output, 16166 expected_elision_output, 16167 expected_disk, 16168 expected_status, 16169 expected_skip, 16170 check_props=True) 16171 16172#---------------------------------------------------------------------- 16173# A test for issue #3978 'reverse merge which adds subtree fails'. 16174@Issue(3978,4057) 16175@SkipUnless(server_has_mergeinfo) 16176def reverse_merge_adds_subtree(sbox): 16177 "reverse merge adds subtree" 16178 16179 sbox.build() 16180 os.chdir(sbox.wc_dir) 16181 sbox.wc_dir = '' 16182 wc_dir = sbox.wc_dir 16183 wc_disk, wc_status = set_up_branch(sbox) 16184 16185 A_path = sbox.ospath('A') 16186 chi_path = sbox.ospath('A/D/H/chi') 16187 A_COPY_path = sbox.ospath('A_COPY') 16188 H_COPY_path = sbox.ospath('A_COPY/D/H') 16189 16190 # r7 - Delete A\D\H\chi 16191 svntest.actions.run_and_verify_svn(None, [], 'delete', chi_path) 16192 svntest.actions.run_and_verify_svn(None, [], 'ci', '-m', 16193 'Delete a file', wc_dir) 16194 16195 # r8 - Merge r7 from A to A_COPY 16196 svntest.actions.run_and_verify_svn(None, [], 'merge', 16197 sbox.repo_url + '/A', 16198 A_COPY_path, '-c7') 16199 svntest.actions.run_and_verify_svn(None, [], 'ci', '-m', 16200 'Cherry-pick r7 from A to A_COPY', wc_dir) 16201 16202 # r9 - File depth sync merge from A/D/H to A_COPY/D/H/ 16203 # This shallow merge does not create non-inheritable mergeinfo because of 16204 # the issue #4057 fix; all subtrees affected by the diff are present, so 16205 # non-inheritable mergeinfo is not required. 16206 svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir) 16207 svntest.actions.run_and_verify_svn(None, [], 'merge', 16208 sbox.repo_url + '/A/D/H', 16209 H_COPY_path, '--depth', 'files') 16210 svntest.actions.run_and_verify_svn(None, [], 'ci', '-m', 16211 'Cherry-pick r7 from A to A_COPY', wc_dir) 16212 16213 # Reverse merge r7 from A to A_COPY 16214 # 16215 # Prior to the issue #3978 fix this merge failed with an assertion: 16216 # 16217 # >svn merge ^/A A_COPY -c-7 16218 # --- Reverse-merging r7 into 'A_COPY\D\H': 16219 # A A_COPY\D\H\chi 16220 # --- Recording mergeinfo for reverse merge of r7 into 'A_COPY': 16221 # U A_COPY 16222 # --- Recording mergeinfo for reverse merge of r7 into 'A_COPY\D\H': 16223 # U A_COPY\D\H 16224 # ..\..\..\subversion\svn\util.c:913: (apr_err=200020) 16225 # ..\..\..\subversion\libsvn_client\merge.c:10990: (apr_err=200020) 16226 # ..\..\..\subversion\libsvn_client\merge.c:10944: (apr_err=200020) 16227 # ..\..\..\subversion\libsvn_client\merge.c:10944: (apr_err=200020) 16228 # ..\..\..\subversion\libsvn_client\merge.c:10914: (apr_err=200020) 16229 # ..\..\..\subversion\libsvn_client\merge.c:8928: (apr_err=200020) 16230 # ..\..\..\subversion\libsvn_client\merge.c:7850: (apr_err=200020) 16231 # ..\..\..\subversion\libsvn_client\mergeinfo.c:120: (apr_err=200020) 16232 # ..\..\..\subversion\libsvn_wc\props.c:2472: (apr_err=200020) 16233 # ..\..\..\subversion\libsvn_wc\props.c:2247: (apr_err=200020) 16234 # ..\..\..\subversion\libsvn_wc\props.c:2576: (apr_err=200020) 16235 # ..\..\..\subversion\libsvn_subr\mergeinfo.c:705: (apr_err=200020) 16236 # svn: E200020: Could not parse mergeinfo string '-7' 16237 # ..\..\..\subversion\libsvn_subr\mergeinfo.c:688: (apr_err=200022) 16238 # ..\..\..\subversion\libsvn_subr\mergeinfo.c:607: (apr_err=200022) 16239 # ..\..\..\subversion\libsvn_subr\mergeinfo.c:504: (apr_err=200022) 16240 # ..\..\..\subversion\libsvn_subr\kitchensink.c:57: (apr_err=200022) 16241 # svn: E200022: Negative revision number found parsing '-7' 16242 svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir) 16243 expected_output = wc.State(A_COPY_path, { 16244 'D/H/chi' : Item(status='A '), 16245 }) 16246 expected_mergeinfo_output = wc.State(A_COPY_path, { 16247 '' : Item(status=' U'), 16248 'D/H' : Item(status=' U'), 16249 }) 16250 expected_elision_output = wc.State(A_COPY_path, { 16251 '' : Item(status=' U'), 16252 }) 16253 expected_status = wc.State(A_COPY_path, { 16254 '' : Item(status=' M'), 16255 'B' : Item(status=' '), 16256 'mu' : Item(status=' '), 16257 'B/E' : Item(status=' '), 16258 'B/E/alpha' : Item(status=' '), 16259 'B/E/beta' : Item(status=' '), 16260 'B/lambda' : Item(status=' '), 16261 'B/F' : Item(status=' '), 16262 'C' : Item(status=' '), 16263 'D' : Item(status=' '), 16264 'D/G' : Item(status=' '), 16265 'D/G/pi' : Item(status=' '), 16266 'D/G/rho' : Item(status=' '), 16267 'D/G/tau' : Item(status=' '), 16268 'D/gamma' : Item(status=' '), 16269 'D/H' : Item(status=' M'), 16270 'D/H/chi' : Item(status='A ', copied='+'), 16271 'D/H/psi' : Item(status=' '), 16272 'D/H/omega' : Item(status=' '), 16273 }) 16274 expected_status.tweak(wc_rev=9) 16275 expected_status.tweak('D/H/chi', wc_rev='-') 16276 expected_disk = wc.State('', { 16277 'B' : Item(), 16278 'mu' : Item("This is the file 'mu'.\n"), 16279 'B/E' : Item(), 16280 'B/E/alpha' : Item("This is the file 'alpha'.\n"), 16281 'B/E/beta' : Item("This is the file 'beta'.\n"), 16282 'B/lambda' : Item("This is the file 'lambda'.\n"), 16283 'B/F' : Item(), 16284 'C' : Item(), 16285 'D' : Item(), 16286 'D/G' : Item(), 16287 'D/G/pi' : Item("This is the file 'pi'.\n"), 16288 'D/G/rho' : Item("This is the file 'rho'.\n"), 16289 'D/G/tau' : Item("This is the file 'tau'.\n"), 16290 'D/gamma' : Item("This is the file 'gamma'.\n"), 16291 'D/H' : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:2-6,8'}), 16292 'D/H/chi' : Item("This is the file 'chi'.\n"), 16293 'D/H/psi' : Item("New content"), 16294 'D/H/omega' : Item("New content"), 16295 }) 16296 expected_skip = wc.State('.', { }) 16297 svntest.actions.run_and_verify_merge(A_COPY_path, 7, 6, 16298 sbox.repo_url + '/A', None, 16299 expected_output, 16300 expected_mergeinfo_output, 16301 expected_elision_output, 16302 expected_disk, 16303 expected_status, 16304 expected_skip, 16305 check_props=True) 16306 16307#---------------------------------------------------------------------- 16308# A test for issue #3989 'merge which deletes file with native eol-style 16309# raises spurious tree conflict'. 16310@Issue(3989) 16311@SkipUnless(server_has_mergeinfo) 16312def merged_deletion_causes_tree_conflict(sbox): 16313 "merged deletion causes spurious tree conflict" 16314 16315 sbox.build() 16316 os.chdir(sbox.wc_dir) 16317 sbox.wc_dir = '' 16318 wc_dir = sbox.wc_dir 16319 16320 A_path = sbox.ospath('A') 16321 psi_path = sbox.ospath('A/D/H/psi') 16322 H_branch_path = sbox.ospath('branch/D/H') 16323 16324 # r2 - Set svn:eol-style native on A/D/H/psi 16325 svntest.actions.run_and_verify_svn(None, [], 'ps', 'svn:eol-style', 16326 'native', psi_path) 16327 svntest.actions.run_and_verify_svn(None, [], 'ci', '-m', 16328 'Set eol-style native on a path', 16329 wc_dir) 16330 16331 # r3 - Branch ^/A to ^/branch 16332 svntest.actions.run_and_verify_svn(None, [], 'copy', 16333 sbox.repo_url + '/A', 16334 sbox.repo_url + '/branch', 16335 '-m', 'Copy ^/A to ^/branch') 16336 svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir) 16337 16338 # r4 - Delete A/D/H/psi 16339 svntest.actions.run_and_verify_svn(None, [], 'delete', psi_path) 16340 svntest.actions.run_and_verify_svn(None, [], 'ci', '-m', 16341 'Delete a a path with native eol-style', 16342 wc_dir) 16343 16344 # Sync merge ^/A/D/H to branch/D/H. 16345 # 16346 # branch/D/H/psi is, ignoring differences caused by svn:eol-style, identical 16347 # to ^/A/D/H/psi when the latter was deleted, so the deletion should merge 16348 # cleanly. 16349 svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir) 16350 expected_output = wc.State(H_branch_path, { 16351 'psi' : Item(status='D '), 16352 }) 16353 expected_mergeinfo_output = wc.State(H_branch_path, { 16354 '' : Item(status=' U'), 16355 }) 16356 expected_elision_output = wc.State(H_branch_path, {}) 16357 expected_status = wc.State(H_branch_path, { 16358 '' : Item(status=' M'), 16359 'chi' : Item(status=' '), 16360 'psi' : Item(status='D '), 16361 'omega' : Item(status=' '), 16362 }) 16363 expected_status.tweak(wc_rev=4) 16364 expected_disk = wc.State('', { 16365 '' : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:3-4'}), 16366 'chi' : Item("This is the file 'chi'.\n"), 16367 'omega' : Item("This is the file 'omega'.\n"), 16368 }) 16369 expected_skip = wc.State('.', { }) 16370 svntest.actions.run_and_verify_merge(H_branch_path, None, None, 16371 sbox.repo_url + '/A/D/H', None, 16372 expected_output, 16373 expected_mergeinfo_output, 16374 expected_elision_output, 16375 expected_disk, 16376 expected_status, 16377 expected_skip, 16378 check_props=True) 16379 16380#---------------------------------------------------------------------- 16381# A test for issue #3976 'record-only merges which add new subtree mergeinfo 16382# don't record mergeinfo describing merge'. 16383@Issue(3976) 16384@SkipUnless(server_has_mergeinfo) 16385def record_only_merge_adds_new_subtree_mergeinfo(sbox): 16386 "record only merge adds new subtree mergeinfo" 16387 16388 sbox.build() 16389 os.chdir(sbox.wc_dir) 16390 sbox.wc_dir = '' 16391 wc_dir = sbox.wc_dir 16392 wc_disk, wc_status = set_up_branch(sbox) 16393 16394 psi_path = sbox.ospath('A/D/H/psi') 16395 psi_COPY_path = sbox.ospath('A_COPY/D/H/psi') 16396 H_COPY2_path = sbox.ospath('A_COPY_2/D/H') 16397 16398 # r7 - Copy ^/A_COPY to ^/A_COPY_2 16399 svntest.actions.run_and_verify_svn(None, [], 16400 'copy', '-m', 'copy A_COPY to A_COPY_2', 16401 sbox.repo_url + '/A_COPY', 16402 sbox.repo_url + '/A_COPY_2') 16403 16404 # r8 - Set a property on A/D/H/psi. It doesn't matter what property 16405 # we use, just as long as we have a change that can be merged independently 16406 # of the text change to A/D/H/psi in r3. 16407 svntest.main.run_svn(None, 'propset', 'svn:eol-style', 'native', psi_path) 16408 svntest.main.run_svn(None, 'commit', '-m', 'set svn:eol-style', wc_dir) 16409 16410 # r9 - Merge r3 from ^/A/D/H/psi to A_COPY/D/H/psi. 16411 svntest.actions.run_and_verify_svn(None, [], 'merge', 16412 sbox.repo_url + '/A/D/H/psi', 16413 psi_COPY_path, '-c3') 16414 svntest.main.run_svn(None, 'commit', '-m', 'Subtree merge', wc_dir) 16415 16416 # r10 - Merge r8 from ^/A/D/H/psi to A_COPY/D/H/psi. 16417 svntest.actions.run_and_verify_svn(None, [], 'merge', 16418 sbox.repo_url + '/A/D/H/psi', 16419 psi_COPY_path, '-c8') 16420 svntest.main.run_svn(None, 'commit', '-m', 'Subtree merge', wc_dir) 16421 16422 # Merge r10 from ^/A_COPY/D/H to A_COPY_2/D/H. This should leave 16423 # A_COPY_2/D/H/psi with three new property additions: 16424 # 16425 # 1) The 'svn:eol-style=native' from r10 via r8. 16426 # 16427 # 2) The mergeinfo '/A/D/H/psi:8' from r10. 16428 # 16429 # 3) The mergeinfo '/A_COPY/D/H/psi:10' describing the merge itself. 16430 svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir) 16431 expected_output = wc.State(H_COPY2_path, { 16432 'psi' : Item(status=' U'), 16433 }) 16434 expected_mergeinfo_output = wc.State(H_COPY2_path, { 16435 '' : Item(status=' U'), 16436 'psi' : Item(status=' G'), 16437 }) 16438 expected_elision_output = wc.State(H_COPY2_path, {}) 16439 expected_status = wc.State(H_COPY2_path, { 16440 '' : Item(status=' M'), 16441 'chi' : Item(status=' '), 16442 'psi' : Item(status=' M'), 16443 'omega' : Item(status=' '), 16444 }) 16445 expected_status.tweak(wc_rev=10) 16446 expected_disk = wc.State('', { 16447 '' : Item(props={SVN_PROP_MERGEINFO : '/A_COPY/D/H:10'}), 16448 'psi' : Item("This is the file 'psi'.\n", 16449 props={SVN_PROP_MERGEINFO : 16450 '/A/D/H/psi:8\n/A_COPY/D/H/psi:10', 16451 'svn:eol-style' : 'native'}), 16452 'chi' : Item("This is the file 'chi'.\n"), 16453 'omega' : Item("This is the file 'omega'.\n"), 16454 }) 16455 expected_skip = wc.State('.', { }) 16456 svntest.actions.run_and_verify_merge(H_COPY2_path, 9, 10, 16457 sbox.repo_url + '/A_COPY/D/H', None, 16458 expected_output, 16459 expected_mergeinfo_output, 16460 expected_elision_output, 16461 expected_disk, 16462 expected_status, 16463 expected_skip, 16464 check_props=True) 16465 16466 16467#---------------------------------------------------------------------- 16468# Test for issue #4056 "don't record non-inheritable mergeinfo if missing 16469# subtrees are not touched by the full-depth diff". 16470@Issue(4056) 16471@SkipUnless(server_has_mergeinfo) 16472def unnecessary_noninheritable_mergeinfo_missing_subtrees(sbox): 16473 "missing subtrees untouched by infinite depth merge" 16474 16475 B_branch_path = sbox.ospath('branch/B') 16476 16477 # Setup a simple branch to which 16478 expected_output, expected_mergeinfo_output, expected_elision_output, \ 16479 expected_status, expected_disk, expected_skip = \ 16480 noninheritable_mergeinfo_test_set_up(sbox) 16481 16482 # Create a shallow merge target; set depth of branch/B to files. 16483 svntest.main.run_svn(None, 'up', '--set-depth=files', B_branch_path) 16484 expected_status.remove('E', 'E/alpha', 'E/beta', 'F') 16485 expected_disk.remove('E', 'E/alpha', 'E/beta', 'F') 16486 16487 # Merge r3 from ^/A/B to branch/B 16488 # 16489 # Merge is smart enough to realize that despite the shallow merge target, 16490 # the diff can only affect branch/B/lambda, which is still present, so there 16491 # is no need to record non-inheritable mergeinfo on the target 16492 # or any subtree mergeinfo whatsoever: 16493 # 16494 # >svn pg svn:mergeinfo -vR 16495 # Properties on 'branch\B': 16496 # svn:mergeinfo 16497 # /A/B:3 <-- Nothing was skipped, so doesn't need 16498 # to be non-inheritable. 16499 svntest.actions.run_and_verify_merge(B_branch_path, 16500 '2', '3', 16501 sbox.repo_url + '/A/B', None, 16502 expected_output, 16503 expected_mergeinfo_output, 16504 expected_elision_output, 16505 expected_disk, 16506 expected_status, 16507 expected_skip, 16508 [], True, True, 16509 B_branch_path) 16510 16511#---------------------------------------------------------------------- 16512# Test for issue #4057 "don't record non-inheritable mergeinfo in shallow 16513# merge if entire diff is within requested depth". 16514@Issue(4057) 16515@SkipUnless(server_has_mergeinfo) 16516def unnecessary_noninheritable_mergeinfo_shallow_merge(sbox): 16517 "shallow merge reaches all necessary subtrees" 16518 16519 B_branch_path = sbox.ospath('branch/B') 16520 E_path = sbox.ospath('A/B/E') 16521 16522 # Setup a simple branch to which 16523 expected_output, expected_mergeinfo_output, expected_elision_output, \ 16524 expected_status, expected_disk, expected_skip = \ 16525 noninheritable_mergeinfo_test_set_up(sbox) 16526 16527 # Merge r3 from ^/A/B to branch/B at operational depth=files 16528 # 16529 # Previously this failed because merge wasn't smart enough to 16530 # realize that despite being a shallow merge, the diff can 16531 # only affect branch/B/lambda, which is within the specified 16532 # depth, so there is no need to record non-inheritable mergeinfo 16533 # or subtree mergeinfo: 16534 # 16535 # >svn pg svn:mergeinfo -vR 16536 # Properties on 'branch\B': 16537 # svn:mergeinfo 16538 # /A/B:3* <-- Should be inheritable 16539 # Properties on 'branch\B\lambda': 16540 # svn:mergeinfo 16541 # /A/B/lambda:3 <-- Not necessary 16542 expected_skip = wc.State(B_branch_path, {}) 16543 svntest.actions.run_and_verify_merge(B_branch_path, '2', '3', 16544 sbox.repo_url + '/A/B', None, 16545 expected_output, 16546 expected_mergeinfo_output, 16547 expected_elision_output, 16548 expected_disk, 16549 expected_status, 16550 expected_skip, 16551 [], True, True, 16552 '--depth', 'files', B_branch_path) 16553 16554 # Revert the merge and then make a prop change to A/B/E in r4. 16555 svntest.actions.run_and_verify_svn(None, [], 16556 'revert', '--recursive', sbox.wc_dir) 16557 svntest.actions.run_and_verify_svn(["property 'prop:name' set on '" + 16558 E_path + "'\n"], [], 'ps', 16559 'prop:name', 'propval', E_path) 16560 svntest.actions.run_and_verify_svn(None, [], 16561 'ci', '-m', 'A new property on a dir', 16562 sbox.wc_dir) 16563 svntest.actions.run_and_verify_svn(None, [], 16564 'up', sbox.wc_dir) 16565 16566 # Merge r4 from ^/A/B to branch/B at operational depth=immediates 16567 # 16568 # Previously this failed because the mergetracking logic didn't realize 16569 # that despite being a shallow merge, the diff only affected branch/B/E, 16570 # which was within the specified depth, so there was no need to record 16571 # non-inheritable mergeinfo or subtree mergeinfo: 16572 # 16573 # >svn pg svn:mergeinfo -vR 16574 # Properties on 'branch\B': 16575 # svn:mergeinfo 16576 # /A/B:4* <-- Should be inheritable 16577 # Properties on 'branch\B\E': 16578 # svn:mergeinfo 16579 # /A/B/E:4 <-- Not necessary 16580 expected_output = wc.State(B_branch_path, { 16581 'E' : Item(status=' U'), 16582 }) 16583 expected_mergeinfo_output = wc.State(B_branch_path, { 16584 '' : Item(status=' U'), 16585 'E' : Item(status=' U'), 16586 }) 16587 expected_elision_output = wc.State(B_branch_path, { 16588 'E' : Item(status=' U'), 16589 }) 16590 expected_status = wc.State(B_branch_path, { 16591 '' : Item(status=' M'), 16592 'lambda' : Item(status=' '), 16593 'E' : Item(status=' M'), 16594 'E/alpha' : Item(status=' '), 16595 'E/beta' : Item(status=' '), 16596 'F' : Item(status=' '), 16597 }) 16598 expected_status.tweak(wc_rev='4') 16599 expected_disk = wc.State('', { 16600 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B:4'}), 16601 'lambda' : Item("This is the file 'lambda'.\n"), 16602 'E' : Item(props={'prop:name' : 'propval'}), 16603 'E/alpha' : Item("This is the file 'alpha'.\n"), 16604 'E/beta' : Item("This is the file 'beta'.\n"), 16605 'F' : Item(), 16606 }) 16607 svntest.actions.run_and_verify_merge(B_branch_path, '3', '4', 16608 sbox.repo_url + '/A/B', None, 16609 expected_output, 16610 expected_mergeinfo_output, 16611 expected_elision_output, 16612 expected_disk, 16613 expected_status, 16614 expected_skip, 16615 [], True, True, 16616 '--depth', 'immediates', B_branch_path) 16617 16618#---------------------------------------------------------------------- 16619# Test for issue #4132, "merge of replaced source asserts". 16620# The original use-case is the following merges, which both asserted: 16621# svn merge -cr1295005 ^/subversion/trunk@1295000 ../src 16622# svn merge -cr1295004 ^/subversion/trunk/@r1295004 ../src 16623@Issue(4132) 16624def svnmucc_abuse_1(sbox): 16625 "svnmucc: merge a replacement" 16626 16627 sbox.build() 16628 os.chdir(sbox.wc_dir) 16629 sbox.wc_dir = '' 16630 wc_dir = sbox.wc_dir 16631 16632 ## Using A/ as our trunk, since one cannot replace the root. 16633 16634 ## r2: open a branch 16635 sbox.simple_repo_copy('A', 'A_COPY') 16636 16637 ## r3: padding (to make the revnums-mod-10 match) 16638 sbox.simple_repo_copy('iota', 'padding') 16639 16640 ## r4: trunk: accidental change 16641 sbox.simple_append('A/mu', 'accidental change') 16642 sbox.simple_commit() 16643 16644 ## r5: fail to revert it 16645 svntest.actions.run_and_verify_svnmucc(None, [], 16646 '-m', 'r5', 16647 '-U', sbox.repo_url, 16648 'rm', 'A', 16649 'cp', 'HEAD', 'A', 'A') 16650 16651 ## r6: really revert it 16652 svntest.actions.run_and_verify_svnmucc(None, [], 16653 '-m', 'r6', 16654 '-U', sbox.repo_url, 16655 'rm', 'A', 16656 'cp', '3', 'A', 'A') 16657 16658 ## Attempt to merge that. 16659 # This used to assert: 16660 # --- Recording mergeinfo for merge of r5 into \ 16661 # 'svn-test-work/working_copies/merge_tests-125/A_COPY': 16662 # subversion/libsvn_subr/mergeinfo.c:1172: (apr_err=235000) 16663 # svn: E235000: In file 'subversion/libsvn_subr/mergeinfo.c' \ 16664 # line 1172: assertion failed (IS_VALID_FORWARD_RANGE(first)) 16665 # 16666 # Then, prior to the fix asserted this way: 16667 # 16668 # >svn merge -c5 ^/A@r5 A_COPY 16669 # subversion\libsvn_client\merge.c:4871: (apr_err=235000) 16670 # svn: E235000: In file 'subversion\libsvn_client\merge.c' 16671 # line 4871: assertion failed (*gap_start < *gap_end) 16672 sbox.simple_update() 16673 svntest.main.run_svn(None, 'merge', '-c', 'r5', '^/A@r5', 16674 sbox.ospath('A_COPY')) 16675 16676#---------------------------------------------------------------------- 16677# Test for issue #4138 'replacement in merge source not notified correctly'. 16678@SkipUnless(server_has_mergeinfo) 16679@Issue(4138) 16680def merge_source_with_replacement(sbox): 16681 "replacement in merge source not notified correctly" 16682 16683 sbox.build() 16684 os.chdir(sbox.wc_dir) 16685 sbox.wc_dir = '' 16686 wc_dir = sbox.wc_dir 16687 16688 # Some paths we'll care about. 16689 A_path = sbox.ospath('A') 16690 omega_path = sbox.ospath('A/D/H/omega') 16691 A_COPY_path = sbox.ospath('A_COPY') 16692 beta_COPY_path = sbox.ospath('A_COPY/B/E/beta') 16693 psi_COPY_path = sbox.ospath('A_COPY/D/H/psi') 16694 rho_COPY_path = sbox.ospath('A_COPY/D/G/rho') 16695 omega_COPY_path = sbox.ospath('A_COPY/D/H/omega') 16696 16697 # branch A@1 to A_COPY in r2, then make a few edits under A in r3-6: 16698 wc_disk, wc_status = set_up_branch(sbox) 16699 16700 # r7 Delete A, replace it with A@5, effectively reverting the change 16701 # made to A/D/H/omega in r6: 16702 svntest.main.run_svn(None, 'up', wc_dir) 16703 svntest.main.run_svn(None, 'del', A_path) 16704 svntest.main.run_svn(None, 'copy', sbox.repo_url + '/A@5', A_path) 16705 sbox.simple_commit(message='Replace A with older version of itself') 16706 16707 # r8: Make an edit to A/D/H/omega: 16708 svntest.main.file_write(omega_path, "New content for 'omega'.\n") 16709 sbox.simple_commit(message='file edit') 16710 16711 # Update and sync merge ^/A to A_COPY. 16712 # 16713 # text text text text text 16714 # edit edit edit edit edit 16715 # psi rho beta omega omega 16716 # A@r1---r3----r4----r5----r6---X r7---r8---------> 16717 # | | ^ | 16718 # | v | | 16719 # | +---replacement---+ | 16720 # copy | 16721 # | sync-merge 16722 # | | 16723 # v v 16724 # r2---A_COPY-----------------------------------------> 16725 svntest.main.run_svn(None, 'up', wc_dir) 16726 # This test previously failed because the merge notifications make it look 16727 # like r6 from ^/A was merged and recorded: 16728 # 16729 # >svn merge ^^/A A_COPY 16730 # --- Merging r2 through r5 into 'A_COPY': 16731 # U A_COPY\B\E\beta 16732 # U A_COPY\D\G\rho 16733 # U A_COPY\D\H\psi 16734 # --- Recording mergeinfo for merge of r2 through r5 into 'A_COPY': 16735 # U A_COPY 16736 # --- Merging r6 through r8 into 'A_COPY': 16737 # U A_COPY\D\H\omega 16738 # --- Recording mergeinfo for merge of r6 through r8 into 'A_COPY': 16739 # G A_COPY 16740 expected_output = expected_merge_output( 16741 [[2,5],[7,8]], 16742 ['U ' + beta_COPY_path + '\n', 16743 'U ' + rho_COPY_path + '\n', 16744 'U ' + omega_COPY_path + '\n', 16745 'U ' + psi_COPY_path + '\n', 16746 ' U ' + A_COPY_path + '\n', 16747 ' G ' + A_COPY_path + '\n',]) 16748 svntest.actions.run_and_verify_svn(expected_output, [], 16749 'merge', sbox.repo_url + '/A', 16750 A_COPY_path) 16751 16752 # Misleading notifications are one thing, incorrect mergeinfo is quite 16753 # another. 16754 svntest.actions.run_and_verify_svn([A_COPY_path + ' - /A:2-5,7-8\n'], 16755 [], 'pg', SVN_PROP_MERGEINFO, 16756 '-R', A_COPY_path) 16757 16758 # Commit the above merge and then reverse merge it. Again r6 is not 16759 # being merged and should not be part of the notifications. 16760 sbox.simple_commit() 16761 sbox.simple_update() 16762 expected_output = expected_merge_output( 16763 [[5,2],[8,7]], 16764 ['U ' + beta_COPY_path + '\n', 16765 'U ' + rho_COPY_path + '\n', 16766 'U ' + omega_COPY_path + '\n', 16767 'U ' + psi_COPY_path + '\n', 16768 ' U ' + A_COPY_path + '\n', 16769 ' G ' + A_COPY_path + '\n',], 16770 elides=True) 16771 svntest.actions.run_and_verify_svn(expected_output, [], 16772 'merge', sbox.repo_url + '/A', 16773 A_COPY_path, '-r8:1') 16774 16775#---------------------------------------------------------------------- 16776# Test for issue #4144 'Reverse merge with replace in source applies 16777# diffs in forward order'. 16778@SkipUnless(server_has_mergeinfo) 16779@Issue(4144) 16780def reverse_merge_with_rename(sbox): 16781 "reverse merge applies revs in reverse order" 16782 16783 sbox.build() 16784 os.chdir(sbox.wc_dir) 16785 sbox.wc_dir = '' 16786 wc_dir = sbox.wc_dir 16787 16788 # Some paths we'll care about. 16789 A_path = sbox.ospath('A') 16790 omega_path = sbox.ospath('trunk/D/H/omega') 16791 A_COPY_path = sbox.ospath('A_COPY') 16792 beta_COPY_path = sbox.ospath('A_COPY/B/E/beta') 16793 psi_COPY_path = sbox.ospath('A_COPY/D/H/psi') 16794 rho_COPY_path = sbox.ospath('A_COPY/D/G/rho') 16795 omega_COPY_path = sbox.ospath('A_COPY/D/H/omega') 16796 16797 # branch A@1 to A_COPY in r2, then make a few edits under A in r3-6: 16798 wc_disk, wc_status = set_up_branch(sbox) 16799 16800 # r7 - Rename ^/A to ^/trunk. 16801 svntest.actions.run_and_verify_svn(['Committing transaction...\n', 16802 'Committed revision 7.\n'], 16803 [], 'move', 16804 sbox.repo_url + '/A', 16805 sbox.repo_url + '/trunk', 16806 '-m', "Rename 'A' to 'trunk'") 16807 svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir) 16808 16809 # r8 - Make and edit to trunk/D/H/omega (which was also edited in r6). 16810 svntest.main.file_write(omega_path, "Edit 'omega' on trunk.\n") 16811 sbox.simple_commit(message='Another omega edit') 16812 16813 # r9 - Sync merge ^/trunk to A_COPY. 16814 svntest.actions.run_and_verify_svn(None, # Don't check stdout, we test this 16815 # type of merge to death elsewhere. 16816 [], 'merge', sbox.repo_url + '/trunk', 16817 A_COPY_path) 16818 sbox.simple_commit(message='Sync A_COPY with ^/trunk') 16819 svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir) 16820 16821 # Reverse merge -r9:1 from ^/trunk to A_COPY. This should return 16822 # A_COPY to the same state it had prior to the sync merge in r2. 16823 # 16824 # This currently fails because the Subversion tries to reverse merge 16825 # -r6:1 first, then -r8:6, causing a spurious conflict on omega: 16826 # 16827 # >svn merge ^/trunk A_COPY -r9:1 --accept=postpone 16828 # --- Reverse-merging r6 through r2 into 'A_COPY': 16829 # U A_COPY\B\E\beta 16830 # U A_COPY\D\G\rho 16831 # C A_COPY\D\H\omega 16832 # U A_COPY\D\H\psi 16833 # --- Recording mergeinfo for reverse merge of r6 through r2 into 'A_COPY': 16834 # U A_COPY 16835 # Summary of conflicts: 16836 # Text conflicts: 1 16837 # ..\..\..\subversion\svn\util.c:913: (apr_err=155015) 16838 # ..\..\..\subversion\libsvn_client\merge.c:10848: (apr_err=155015) 16839 # ..\..\..\subversion\libsvn_client\merge.c:10812: (apr_err=155015) 16840 # ..\..\..\subversion\libsvn_client\merge.c:8984: (apr_err=155015) 16841 # ..\..\..\subversion\libsvn_client\merge.c:4728: (apr_err=155015) 16842 # svn: E155015: One or more conflicts were produced while merging r6:1 16843 # into 'C:\SVN\src-trunk-4\Debug\subversion\tests\cmdline\svn-test-work 16844 # \working_copies\merge_tests-127\A_COPY' -- resolve all conflicts and 16845 # rerun the merge to apply the remaining unmerged revisions 16846 expected_output = expected_merge_output( 16847 [[8,7],[6,2]], 16848 ['U ' + beta_COPY_path + '\n', 16849 'U ' + rho_COPY_path + '\n', 16850 'U ' + omega_COPY_path + '\n', 16851 'G ' + omega_COPY_path + '\n', 16852 'U ' + psi_COPY_path + '\n', 16853 ' U ' + A_COPY_path + '\n', 16854 ' G ' + A_COPY_path + '\n',], elides=True) 16855 svntest.actions.run_and_verify_svn(expected_output, [], 16856 'merge', sbox.repo_url + '/trunk', 16857 A_COPY_path, '-r9:1') 16858 16859#---------------------------------------------------------------------- 16860# Test for issue #4166 'multiple merge editor drives which add then 16861# delete a subtree fail'. 16862@SkipUnless(server_has_mergeinfo) 16863@Issue(4166) 16864def merge_adds_then_deletes_subtree(sbox): 16865 "merge adds then deletes subtree" 16866 16867 sbox.build() 16868 os.chdir(sbox.wc_dir) 16869 sbox.wc_dir = '' 16870 wc_dir = sbox.wc_dir 16871 16872 # Some paths we'll care about. 16873 A_path = sbox.ospath('A') 16874 nu_path = sbox.ospath('A/C/nu') 16875 C_branch_path = sbox.ospath('branch/C') 16876 nu_branch_path = sbox.ospath('branch/C/nu') 16877 16878 # Make a branch. 16879 svntest.actions.run_and_verify_svn(None, [], 'copy', 16880 sbox.repo_url + '/A', 16881 sbox.repo_url + '/branch', 16882 '-m', 'Make a branch.') 16883 16884 # On the branch parent: Add a file in r3 and then delete it in r4. 16885 svntest.main.file_write(nu_path, "This is the file 'nu'.\n") 16886 svntest.actions.run_and_verify_svn(None, [], 'add', nu_path) 16887 svntest.actions.run_and_verify_svn(None, [], 'ci', wc_dir, 16888 '-m', 'Add a file') 16889 svntest.actions.run_and_verify_svn(None, [], 'delete', nu_path) 16890 svntest.actions.run_and_verify_svn(None, [], 'ci', wc_dir, 16891 '-m', 'Delete a file') 16892 16893 # Merge r3 and r4 from ^/A/C to branch/C as part of one merge 16894 # command, but as separate editor drives, i.e. 'c3,4 vs. -r2:4. 16895 # These should be equivalent but the former was failing with: 16896 # 16897 # >svn merge ^/A/C branch\C -c3,4 16898 # --- Merging r3 into 'branch\C': 16899 # A branch\C\nu 16900 # --- Recording mergeinfo for merge of r3 into 'branch\C': 16901 # U branch\C 16902 # --- Merging r4 into 'branch\C': 16903 # D branch\C\nu 16904 # --- Recording mergeinfo for merge of r4 into 'branch\C': 16905 # G branch\C 16906 # ..\..\..\subversion\svn\util.c:913: (apr_err=155010) 16907 # ..\..\..\subversion\libsvn_client\merge.c:10873: (apr_err=155010) 16908 # ..\..\..\subversion\libsvn_client\merge.c:10837: (apr_err=155010) 16909 # ..\..\..\subversion\libsvn_client\merge.c:8994: (apr_err=155010) 16910 # ..\..\..\subversion\libsvn_client\merge.c:7923: (apr_err=155010) 16911 # ..\..\..\subversion\libsvn_client\mergeinfo.c:257: (apr_err=155010) 16912 # ..\..\..\subversion\libsvn_client\mergeinfo.c:97: (apr_err=155010) 16913 # ..\..\..\subversion\libsvn_wc\props.c:2003: (apr_err=155010) 16914 # ..\..\..\subversion\libsvn_wc\props.c:2024: (apr_err=155010) 16915 # ..\..\..\subversion\libsvn_wc\wc_db.c:11473: (apr_err=155010) 16916 # ..\..\..\subversion\libsvn_wc\wc_db.c:7247: (apr_err=155010) 16917 # ..\..\..\subversion\libsvn_wc\wc_db.c:7232: (apr_err=155010) 16918 # svn: E155010: The node 'C:\SVN\src-trunk\Debug\subversion\tests 16919 # \cmdline\svn-test-work\working_copies\merge_tests-128\branch\C\nu' 16920 # was not found. 16921 svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir) 16922 svntest.actions.run_and_verify_svn( 16923 expected_merge_output([[3],[4]], 16924 ['A ' + nu_branch_path + '\n', 16925 'D ' + nu_branch_path + '\n', 16926 ' U ' + C_branch_path + '\n', 16927 ' G ' + C_branch_path + '\n',]), 16928 [], 'merge', '-c3,4', sbox.repo_url + '/A/C', C_branch_path) 16929 16930#---------------------------------------------------------------------- 16931# Test for issue #4169 'added subtrees with non-inheritable mergeinfo 16932# cause spurious subtree mergeinfo'. 16933@SkipUnless(server_has_mergeinfo) 16934@Issue(4169) 16935def merge_with_added_subtrees_with_mergeinfo(sbox): 16936 "merge with added subtrees with mergeinfo" 16937 16938 sbox.build() 16939 os.chdir(sbox.wc_dir) 16940 sbox.wc_dir = '' 16941 wc_dir = sbox.wc_dir 16942 16943 # Some paths we'll care about. 16944 A_path = sbox.ospath('A') 16945 Y_path = sbox.ospath('A/C/X/Y') 16946 Z_path = sbox.ospath('A/C/X/Y/Z') 16947 nu_path = sbox.ospath('A/C/X/Y/Z/nu') 16948 A_COPY_path = sbox.ospath('A_COPY') 16949 Y_COPY_path = sbox.ospath('A_COPY/C/X/Y') 16950 W_COPY_path = sbox.ospath('A_COPY/C/X/Y/Z/W') 16951 A_COPY2_path = sbox.ospath('A_COPY_2') 16952 16953 # Make two branches of ^/A and then make a few edits under A in r4-7: 16954 wc_disk, wc_status = set_up_branch(sbox, nbr_of_branches=2) 16955 16956 # r8 - Add a subtree under A. 16957 svntest.actions.run_and_verify_svn(None, [], 'mkdir', '--parents', 16958 Z_path) 16959 svntest.main.file_write(nu_path, "This is the file 'nu'.\n") 16960 svntest.actions.run_and_verify_svn(None, [], 'add', nu_path) 16961 svntest.actions.run_and_verify_svn(None, [], 'ci', wc_dir, 16962 '-m', 'Add a subtree on our "trunk"') 16963 16964 # r9 - Sync ^/A to the first branch A_COPY. 16965 svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir) 16966 svntest.actions.run_and_verify_svn(None, [], 'merge', 16967 sbox.repo_url + '/A', A_COPY_path) 16968 svntest.actions.run_and_verify_svn(None, [], 'ci', wc_dir, 16969 '-m', 'Sync ^/A to ^/A_COPY') 16970 16971 # r10 - Make some edits on the first branch. 16972 svntest.actions.run_and_verify_svn(None, [], 'ps', 'branch-prop-foo', 16973 'bar', Y_COPY_path) 16974 svntest.actions.run_and_verify_svn(None, [], 'mkdir', W_COPY_path) 16975 svntest.actions.run_and_verify_svn(None, [], 'ci', wc_dir, 16976 '-m', 'Make some edits on "branch 1"') 16977 16978 # r11 - Cherry-pick r10 on the first branch back to A, but 16979 # do so at depth=empty so non-inheritable mergeinfo is created. 16980 svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir) 16981 svntest.actions.run_and_verify_svn(None, [], 16982 'merge', '-c10', '--depth=empty', 16983 sbox.repo_url + '/A_COPY/C/X/Y', Y_path) 16984 svntest.actions.run_and_verify_svn( 16985 None, [], 'ci', wc_dir, 16986 '-m', 'Depth empty subtree cherry pick from "branch 1" to "trunk"') 16987 16988 # Sync ^/A to the second branch A_COPY_2. 16989 # 16990 # Previously this failed because spurious mergeinfo was created on 16991 # A_COPY_2/C/X/Y/Z: 16992 # 16993 # >svn merge ^^/A A_COPY_2 16994 # --- Merging r3 through r11 into 'A_COPY_2': 16995 # U A_COPY_2\B\E\beta 16996 # A A_COPY_2\C\X 16997 # A A_COPY_2\C\X\Y 16998 # A A_COPY_2\C\X\Y\Z 16999 # A A_COPY_2\C\X\Y\Z\nu 17000 # U A_COPY_2\D\G\rho 17001 # U A_COPY_2\D\H\omega 17002 # U A_COPY_2\D\H\psi 17003 # --- Recording mergeinfo for merge of r3 through r11 into 'A_COPY_2': 17004 # U A_COPY_2 17005 # --- Recording mergeinfo for merge of r3 through r11 into 'A_COPY_2\C\X\Y': 17006 # G A_COPY_2\C\X\Y 17007 # vvvvvvvvvvvvvvvvvvvv 17008 # U A_COPY_2\C\X\Y\Z 17009 # ^^^^^^^^^^^^^^^^^^^^ 17010 # 17011 # >svn pl -vR A_COPY_2 17012 # Properties on 'A_COPY_2': 17013 # svn:mergeinfo 17014 # /A:3-11 17015 # Properties on 'A_COPY_2\C\X\Y': 17016 # branch-prop-foo 17017 # bar 17018 # svn:mergeinfo 17019 # /A/C/X/Y:8-11 17020 # /A_COPY/C/X/Y:10* 17021 # vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv 17022 # Properties on 'A_COPY_2\C\X\Y\Z': 17023 # svn:mergeinfo 17024 # /A/C/X/Y/Z:8-11 17025 # ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 17026 svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir) 17027 expected_output = wc.State(A_COPY2_path, { 17028 'B/E/beta' : Item(status='U '), 17029 'D/G/rho' : Item(status='U '), 17030 'D/H/omega' : Item(status='U '), 17031 'D/H/psi' : Item(status='U '), 17032 'C/X' : Item(status='A '), 17033 'C/X/Y' : Item(status='A '), 17034 'C/X/Y/Z' : Item(status='A '), 17035 'C/X/Y/Z/nu' : Item(status='A '), 17036 }) 17037 expected_mergeinfo_output = wc.State(A_COPY2_path, { 17038 '' : Item(status=' U'), 17039 'C/X/Y' : Item(status=' U'), # Added with explicit mergeinfo 17040 }) 17041 expected_elision_output = wc.State(A_COPY2_path, { 17042 }) 17043 expected_status = wc.State(A_COPY2_path, { 17044 '' : Item(status=' M', wc_rev=11), 17045 'B' : Item(status=' ', wc_rev=11), 17046 'mu' : Item(status=' ', wc_rev=11), 17047 'B/E' : Item(status=' ', wc_rev=11), 17048 'B/E/alpha' : Item(status=' ', wc_rev=11), 17049 'B/E/beta' : Item(status='M ', wc_rev=11), 17050 'B/lambda' : Item(status=' ', wc_rev=11), 17051 'B/F' : Item(status=' ', wc_rev=11), 17052 'C' : Item(status=' ', wc_rev=11), 17053 'C/X' : Item(status='A ', wc_rev='-', copied='+'), 17054 'C/X/Y' : Item(status=' M', wc_rev='-', copied='+'), 17055 'C/X/Y/Z' : Item(status=' ', wc_rev='-', copied='+'), 17056 'C/X/Y/Z/nu' : Item(status=' ', wc_rev='-', copied='+'), 17057 'D' : Item(status=' ', wc_rev=11), 17058 'D/G' : Item(status=' ', wc_rev=11), 17059 'D/G/pi' : Item(status=' ', wc_rev=11), 17060 'D/G/rho' : Item(status='M ', wc_rev=11), 17061 'D/G/tau' : Item(status=' ', wc_rev=11), 17062 'D/gamma' : Item(status=' ', wc_rev=11), 17063 'D/H' : Item(status=' ', wc_rev=11), 17064 'D/H/chi' : Item(status=' ', wc_rev=11), 17065 'D/H/psi' : Item(status='M ', wc_rev=11), 17066 'D/H/omega' : Item(status='M ', wc_rev=11), 17067 }) 17068 expected_disk = wc.State('', { 17069 '' : Item(props={SVN_PROP_MERGEINFO : '/A:3-11'}), 17070 'B' : Item(), 17071 'mu' : Item("This is the file 'mu'.\n"), 17072 'B/E' : Item(), 17073 'B/E/alpha' : Item("This is the file 'alpha'.\n"), 17074 'B/E/beta' : Item("New content"), 17075 'B/lambda' : Item("This is the file 'lambda'.\n"), 17076 'B/F' : Item(), 17077 'C' : Item(), 17078 'C/X' : Item(), 17079 'C/X/Y' : Item(props={ 17080 SVN_PROP_MERGEINFO : '/A/C/X/Y:8-11\n/A_COPY/C/X/Y:10*', 17081 'branch-prop-foo' : 'bar'}), 17082 'C/X/Y/Z' : Item(), 17083 'C/X/Y/Z/nu' : Item("This is the file 'nu'.\n"), 17084 'D' : Item(), 17085 'D/G' : Item(), 17086 'D/G/pi' : Item("This is the file 'pi'.\n"), 17087 'D/G/rho' : Item("New content"), 17088 'D/G/tau' : Item("This is the file 'tau'.\n"), 17089 'D/gamma' : Item("This is the file 'gamma'.\n"), 17090 'D/H' : Item(), 17091 'D/H/chi' : Item("This is the file 'chi'.\n"), 17092 'D/H/psi' : Item("New content"), 17093 'D/H/omega' : Item("New content"), 17094 }) 17095 expected_skip = wc.State(A_COPY_path, { }) 17096 svntest.actions.run_and_verify_merge(A_COPY2_path, None, None, 17097 sbox.repo_url + '/A', None, 17098 expected_output, 17099 expected_mergeinfo_output, 17100 expected_elision_output, 17101 expected_disk, 17102 expected_status, 17103 expected_skip, 17104 check_props=True) 17105 17106#---------------------------------------------------------------------- 17107@SkipUnless(server_has_mergeinfo) 17108def merge_with_externals_with_mergeinfo(sbox): 17109 "merge with externals with mergeinfo" 17110 17111 sbox.build() 17112 os.chdir(sbox.wc_dir) 17113 sbox.wc_dir = '' 17114 wc_dir = sbox.wc_dir 17115 17116 # Some paths we'll care about. 17117 A_path = sbox.ospath('A') 17118 A_COPY_path = sbox.ospath('A_COPY') 17119 file_external_path = sbox.ospath('A/file-external') 17120 mu_COPY_path = sbox.ospath('A_COPY/mu') 17121 mu_path = sbox.ospath('A/mu') 17122 17123 # Make a branch of ^/A and then make a few edits under A in r3-6: 17124 wc_disk, wc_status = set_up_branch(sbox) 17125 17126 svntest.main.file_write(mu_COPY_path, "branch edit") 17127 svntest.actions.run_and_verify_svn(None, [], 'ci', '-m', 17128 'file edit on the branch', wc_dir) 17129 svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir) 17130 17131 # Create a file external under 'A' and set some bogus mergeinfo 17132 # on it (the fact that this mergeinfo is bogus has no bearing on 17133 # this test). 17134 svntest.actions.run_and_verify_svn(None, [], 'propset', 17135 'svn:externals', 17136 '^/iota file-external', A_path) 17137 svntest.actions.run_and_verify_svn(None, [], 'ci', '-m', 17138 'set file external', wc_dir) 17139 svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir) 17140 svntest.actions.run_and_verify_svn(None, [], 'ps', SVN_PROP_MERGEINFO, 17141 "/bogus-mergeinfo:5", file_external_path) 17142 svntest.actions.run_and_verify_svn(None, [], 'ci', '-m', 17143 'set mergeinfo on file external', 17144 file_external_path) 17145 17146 # Sync merge ^/A to A_COPY and then reintegrate A_COPY back to A. 17147 svntest.actions.run_and_verify_svn(None, [], 'merge', 17148 sbox.repo_url + '/A', A_COPY_path) 17149 svntest.actions.run_and_verify_svn(None, [], 'ci', '-m', 17150 'sync merge', wc_dir) 17151 # This was segfaulting, see 17152 # http://svn.haxx.se/dev/archive-2012-10/0364.shtml 17153 svntest.actions.run_and_verify_svn( 17154 expected_merge_output(None, 17155 ['U ' + mu_path + '\n', 17156 ' U ' + A_path + '\n'], 17157 two_url=True), 17158 [], 'merge', '--reintegrate', sbox.repo_url + '/A_COPY', 17159 A_path) 17160 17161#---------------------------------------------------------------------- 17162# Test merging 'binary' files with keyword expansion enabled. 17163# Tests issue #4221 'Trivial merge of a binary file with svn:keywords 17164# raises a conflict', among other cases. 17165@SkipUnless(server_has_mergeinfo) 17166@Issue(4221) 17167def merge_binary_file_with_keywords(sbox): 17168 "merge binary file with keywords" 17169 17170 sbox.build() 17171 os.chdir(sbox.wc_dir) 17172 sbox.wc_dir = '' 17173 17174 # Some binary files, and some binary files that will become text files. 17175 # 'mod_src' means a content change on the branch (the merge source); 17176 # 'mod_tgt' means a content change on the original (the merge target); 17177 # 'to_txt' means svn:mime-type removed on the branch (the merge source). 17178 file_mod_both = 'A/B/E/alpha' 17179 file_mod_src = 'A/D/G/pi' 17180 file_mod_tgt = 'A/D/G/rho' 17181 file_mod_none = 'A/D/G/tau' 17182 file_mod_both_to_txt = 'A/B/E/beta' 17183 file_mod_src_to_txt = 'A/D/H/chi' 17184 file_mod_tgt_to_txt = 'A/D/H/psi' 17185 file_mod_none_to_txt = 'A/D/H/omega' 17186 files_bin = [ file_mod_both, file_mod_src, file_mod_tgt, file_mod_none ] 17187 files_txt = [ file_mod_both_to_txt, file_mod_src_to_txt, 17188 file_mod_tgt_to_txt, file_mod_none_to_txt ] 17189 files = files_bin + files_txt 17190 17191 # make some 'binary' files with keyword expansion enabled 17192 for f in files: 17193 svntest.main.file_append(sbox.ospath(f), "With $Revision: $ keyword.\n") 17194 svntest.main.run_svn(binary_mime_type_on_text_file_warning, 17195 'propset', 'svn:mime-type', 17196 'application/octet-stream', sbox.ospath(f)) 17197 sbox.simple_propset('svn:keywords', 'Revision', f) 17198 sbox.simple_commit() 17199 17200 # branch the files 17201 sbox.simple_repo_copy('A', 'A2') 17202 sbox.simple_update() 17203 17204 # Modify the branched (source) and/or original (target) versions. Remove 17205 # the svn:mime-type from the 'to_txt' files on the branch. 17206 # The original bug in issue #4221 gave a conflict if we modified either 17207 # version or neither (using a single-file merge test case). 17208 for f in [ file_mod_both, file_mod_both_to_txt, 17209 file_mod_src, file_mod_src_to_txt ]: 17210 f_branch = 'A2' + f[1:] 17211 svntest.main.file_append(sbox.ospath(f_branch), "Incoming mod.\n") 17212 for f in [ file_mod_both, file_mod_both_to_txt, 17213 file_mod_tgt, file_mod_tgt_to_txt ]: 17214 svntest.main.file_append(sbox.ospath(f), "Mod on merge target.\n") 17215 for f in files_txt: 17216 f_branch = 'A2' + f[1:] 17217 sbox.simple_propdel('svn:mime-type', f_branch) 17218 sbox.simple_commit() 17219 sbox.simple_update() 17220 17221 # merge back 17222 svntest.actions.run_and_verify_svn( 17223 expected_merge_output([[3,4]], 17224 ['C ' + sbox.ospath(file_mod_both) + '\n', 17225 'U ' + sbox.ospath(file_mod_src) + '\n', 17226 #' ' + sbox.ospath(file_mod_tgt) + '\n', 17227 #' ' + sbox.ospath(file_mod_none) + '\n', 17228 'CU ' + sbox.ospath(file_mod_both_to_txt) + '\n', 17229 'UU ' + sbox.ospath(file_mod_src_to_txt) + '\n', 17230 ' U ' + sbox.ospath(file_mod_tgt_to_txt) + '\n', 17231 ' U ' + sbox.ospath(file_mod_none_to_txt) + '\n', 17232 ' U A\n'], 17233 text_conflicts=2), 17234 [], 'merge', '^/A2', 'A') 17235 17236#---------------------------------------------------------------------- 17237# Test for issue #4155 'Merge conflict text of expanded keyword incorrect 17238# when svn:keyword property value removed'. Failed in 1.7.0 through 1.7.8. 17239@SkipUnless(server_has_mergeinfo) 17240@Issue(4155) 17241def merge_conflict_when_keywords_removed(sbox): 17242 "merge conflict when keywords removed" 17243 17244 sbox.build() 17245 os.chdir(sbox.wc_dir) 17246 sbox.wc_dir = '' 17247 17248 # make a file with keyword expansion enabled 17249 svntest.main.file_write('A/keyfile', "$Date$ $Revision$\n") 17250 sbox.simple_add('A/keyfile') 17251 sbox.simple_propset('svn:keywords', 'Date Revision', 'A/keyfile') 17252 sbox.simple_commit() 17253 sbox.simple_update() 17254 17255 # branch the file 17256 sbox.simple_repo_copy('A', 'A2') 17257 sbox.simple_update() 17258 17259 # 17260 svntest.main.file_append('A/keyfile', " some changes\n") 17261 sbox.simple_commit() 17262 17263 # sync merge 17264 svntest.actions.run_and_verify_svn( 17265 expected_merge_output([[3,4]], 17266 ['U '+ sbox.ospath('A2/keyfile') + '\n', 17267 ' U A2\n']), 17268 [], 'merge', '^/A', 'A2') 17269 sbox.simple_commit() 17270 sbox.simple_update() 17271 17272 # modify the original version: disable those KW & enable 'Id' 17273 sbox.simple_propset('svn:keywords', 'Id', 'A/keyfile') 17274 svntest.main.file_append('A/keyfile', "$Id$\n") 17275 sbox.simple_commit() 17276 17277 # sync merge again 17278 svntest.actions.run_and_verify_svn( 17279 expected_merge_output([[5,6]], 17280 ['UU ' + sbox.ospath('A2/keyfile') + '\n', 17281 ' U A2\n']), 17282 [], 'merge', '--accept=postpone', '^/A', 'A2') 17283 17284@SkipUnless(server_has_mergeinfo) 17285@Issue(4139, 3274, 3503) 17286def merge_target_selection(sbox): 17287 "merge target selection handling" 17288 17289 sbox.build() 17290 os.chdir(sbox.wc_dir) 17291 sbox.wc_dir = '' 17292 17293 # r2 17294 sbox.simple_mkdir('dir') 17295 sbox.simple_add_text('\1\2\3\4\5', 'dir/binary-file') 17296 sbox.simple_add_text('abcde', 'dir/text-file') 17297 sbox.simple_commit() 17298 17299 # r3 17300 sbox.simple_copy('dir', 'branch') 17301 sbox.simple_commit() 17302 17303 # r4 17304 svntest.main.file_write(sbox.ospath('dir/binary-file'), 17305 '\9\8\7\6\5\4\3\2\1') 17306 sbox.simple_commit() 17307 17308 sbox.simple_update() 17309 17310 os.chdir(sbox.ospath('branch')) 17311 17312 # Merge the directory (no target) 17313 expected_output = [ 17314 '--- Merging r4 into \'.\':\n', 17315 'U binary-file\n', 17316 '--- Recording mergeinfo for merge of r4 into \'.\':\n', 17317 ' U .\n', 17318 ] 17319 svntest.actions.run_and_verify_svn(expected_output, [], 17320 'merge', '^/dir', '-c', '4') 17321 17322 svntest.main.run_svn(None, 'revert', '-R', '.') 17323 17324 # Merge the file (no target) 17325 expected_output = [ 17326 '--- Merging r4 into \'binary-file\':\n', 17327 'U binary-file\n', 17328 '--- Recording mergeinfo for merge of r4 into \'binary-file\':\n', 17329 ' U binary-file\n', 17330 ] 17331 svntest.actions.run_and_verify_svn(expected_output, [], 17332 'merge', '^/dir/binary-file', '-c', '4') 17333 17334 svntest.main.run_svn(None, 'revert', '-R', '.') 17335 17336 # Merge the directory (explicit target) 17337 expected_output = [ 17338 '--- Merging r4 into \'.\':\n', 17339 'U binary-file\n', 17340 '--- Recording mergeinfo for merge of r4 into \'.\':\n', 17341 ' U .\n', 17342 ] 17343 svntest.actions.run_and_verify_svn(expected_output, [], 17344 'merge', '^/dir', '-c', '4', '.') 17345 17346 svntest.main.run_svn(None, 'revert', '-R', '.') 17347 17348 # Merge the file (explicit target) 17349 expected_output = [ 17350 '--- Merging r4 into \'binary-file\':\n', 17351 'U binary-file\n', 17352 '--- Recording mergeinfo for merge of r4 into \'binary-file\':\n', 17353 ' U binary-file\n', 17354 ] 17355 svntest.actions.run_and_verify_svn(expected_output, [], 17356 'merge', '^/dir/binary-file', '-c', '4', 'binary-file') 17357 17358 svntest.main.run_svn(None, 'revert', '-R', '.') 17359 17360 # Merge the file (wrong target) 17361 expected_output = [ 17362 '--- Merging r4 into \'.\':\n', 17363 ' C .\n', 17364 '--- Recording mergeinfo for merge of r4 into \'.\':\n', 17365 ' U .\n', 17366 ] + svntest.main.summary_of_conflicts(tree_conflicts=1) 17367 svntest.actions.run_and_verify_svn(expected_output, [], 17368 'merge', '^/dir/binary-file', 17369 '-c', '4', '.', '--accept', 'postpone') 17370 17371 svntest.main.run_svn(None, 'revert', '-R', '.') 17372 17373 # Merge the dir (wrong target) 17374 expected_output = [ 17375 '--- Merging r4 into \'binary-file\':\n', 17376 ' C %s\n' % os.path.join('binary-file'), 17377 '--- Recording mergeinfo for merge of r4 into \'binary-file\':\n', 17378 ' U binary-file\n', 17379 ] + svntest.main.summary_of_conflicts(tree_conflicts=1) 17380 svntest.actions.run_and_verify_svn(expected_output, [], 17381 'merge', '^/dir', '-c', '4', 'binary-file', 17382 '--accept', 'postpone') 17383 17384@SkipUnless(server_has_mergeinfo) 17385@Issue(3405) # seems to be the wrong issue number 17386def merge_properties_on_adds(sbox): 17387 "merged directory properties are added" 17388 17389 sbox.build() 17390 os.chdir(sbox.wc_dir) 17391 sbox.wc_dir = '' 17392 17393 sbox.simple_copy('A/D/G', 'G') 17394 17395 sbox.simple_mkdir('A/D/G/M') 17396 sbox.simple_mkdir('A/D/G/M/N') 17397 sbox.simple_add_text('QQ', 'A/D/G/file', 'A/D/G/M/file') 17398 sbox.simple_propset('key', 'value', 17399 'A/D/G/M', 'A/D/G/file', 'A/D/G/M/N', 'A/D/G/M/file') 17400 sbox.simple_commit() 17401 sbox.simple_update() 17402 17403 svntest.actions.run_and_verify_svn(None, [], 17404 'merge', '^/A/D/G', sbox.ospath('G')) 17405 17406 expected_output = svntest.verify.UnorderedOutput([ 17407 'Properties on \'%s\':\n' % sbox.ospath('G'), 17408 ' svn:mergeinfo\n', 17409 'Properties on \'%s\':\n' % sbox.ospath('G/M'), 17410 ' key\n', 17411 'Properties on \'%s\':\n' % sbox.ospath('G/file'), 17412 ' key\n', 17413 'Properties on \'%s\':\n' % sbox.ospath('G/M/N'), 17414 ' key\n', 17415 'Properties on \'%s\':\n' % sbox.ospath('G/M/file'), 17416 ' key\n', 17417 ]) 17418 svntest.actions.run_and_verify_svn(expected_output, [], 17419 'proplist', '-R', sbox.ospath('G')) 17420 17421 expected_output = svntest.verify.UnorderedOutput([ 17422 'Properties on \'%s\':\n' % sbox.ospath('G/M'), 17423 ' key\n', 17424 'Properties on \'%s\':\n' % sbox.ospath('G/file'), 17425 ' key\n', 17426 'Properties on \'%s\':\n' % sbox.ospath('G/M/N'), 17427 ' key\n', 17428 'Properties on \'%s\':\n' % sbox.ospath('G/M/file'), 17429 ' key\n', 17430 ]) 17431 17432 # I merged the tree, which should include history but only the files have 17433 # the properties stored in PRISTINE. All directories have the properties 17434 # as local changes in ACTUAL. 17435 svntest.actions.run_and_verify_svn(expected_output, [], 17436 'proplist', '-R', sbox.ospath('G'), 17437 '-r', 'BASE') 17438 17439 # Note that this is not a regression. This has been the case since 1.0. 17440 # ### We just made status, update and merge handle this without users 17441 # ### knowing about this limitation. 17442 17443 # ### My guess is that the base merge support on svn_wc_merge_props() 17444 # ### was originally designed to resolve this problem, but I can't 17445 # ### find a released version where this was actually implemented. 17446 17447 # For fun, also check the status: 'svn status' suppresses the M from AM. 17448 17449 # G = sbox.ospath('G') 17450 # 17451 # expected_status = wc.State('G', { 17452 # '' : Item(status=' M', wc_rev='2'), 17453 # 'pi' : Item(status=' ', wc_rev='2'), 17454 # 'tau' : Item(status=' ', wc_rev='2'), 17455 # 'file' : Item(status='A ', copied='+', wc_rev='-'), # Copied, no changes 17456 # 'M' : Item(status='A ', copied='+', wc_rev='-'), # Copied, changes 17457 # 'M/file' : Item(status=' ', copied='+', wc_rev='-'), # Copied, no changes 17458 # 'M/N' : Item(status=' M', copied='+', wc_rev='-'), # Local changes 17459 # 'rho' : Item(status=' ', wc_rev='2'), 17460 # }) 17461 # svntest.actions.run_and_verify_status(G, expected_status) 17462 17463 17464# ====================================================================== 17465# Functions for parsing mergeinfo 17466 17467def parse_changes_list(changes_string): 17468 """Parse a string containing a list of revision numbers in the form 17469 of the '--change' command-line argument (e.g. '1,3,-5,7-10'). 17470 Return a list of elements of the form [[1], [3], [-5], [7,10]]. 17471 """ 17472 rev_ranges = [] 17473 for rr in changes_string.split(','): 17474 if '-' in rr[1:]: 17475 revs = rr.split('-') 17476 rev_ranges.append([int(revs[0]), int(revs[1])]) 17477 else: 17478 rev_ranges.append([int(rr)]) 17479 return rev_ranges 17480 17481def parse_rev_args(arg_list): 17482 """Return a list of [rX:rY] or [rZ] elements representing ARG_LIST 17483 whose elements are strings in the form '-rX:Y' or '-cZ,X-Y,...'. 17484 """ 17485 rev_ranges = [] 17486 for arg in arg_list: 17487 kind = arg[:2] 17488 val = arg[2:] 17489 if kind == '-r': 17490 if ':' in val: 17491 revs = map(int, val.split(':')) 17492 if revs[0] < revs[1]: 17493 rev_ranges.append([revs[0] + 1, revs[1]]) 17494 else: 17495 rev_ranges.append([revs[0], revs[1] + 1]) 17496 else: 17497 rev_ranges.append([int(val)]) 17498 elif kind == '-c': 17499 rev_ranges.extend(parse_changes_list(val)) 17500 else: 17501 raise ValueError("revision arg '" + arg + "' in '" + arg_list + 17502 "' does not start with -r or -c") 17503 return rev_ranges 17504 17505class RangeList(list): 17506 """Represents of a list of revision ranges, as a list of one- or 17507 two-element lists, each of the form [X] meaning "--revision (X-1):X" 17508 or [X,Y] meaning "--revision (X-1):Y". 17509 """ 17510 def __init__(self, arg): 17511 """ 17512 """ 17513 self.as_given = arg 17514 if isinstance(arg, str): 17515 list.__init__(self, parse_changes_list(arg)) 17516 elif isinstance(arg, list): 17517 list.__init__(self, parse_rev_args(arg)) 17518 else: 17519 raise ValueError("RangeList needs a string or a list, not '" + str(arg) + "'") 17520 17521def expected_merge_output2(tgt_ospath, 17522 recorded_ranges, 17523 merged_ranges=None, 17524 prop_conflicts=0, 17525 prop_resolved=0): 17526 """Return an ExpectedOutput instance corresponding to the expected 17527 output of a merge into TGT_OSPATH, with one 'recording 17528 mergeinfo...' notification per specified revision range in 17529 RECORDED_RANGES and one 'merging...' notification per revision 17530 range in MERGED_RANGES. 17531 17532 RECORDED_RANGES is a mergeinfo-string or a RangeList. 17533 17534 MERGED_RANGES is a list of mergeinfo-strings or a list of 17535 RangeLists. If None, it means [[r] for r in RECORDED_RANGES]. 17536 """ 17537 # Convert RECORDED_RANGES to a RangeList. 17538 if isinstance(recorded_ranges, str): 17539 recorded_ranges = RangeList(recorded_ranges) 17540 # Convert MERGED_RANGES to a list of RangeLists. 17541 if merged_ranges is None: 17542 merged_ranges = [[r] for r in recorded_ranges] 17543 elif len(merged_ranges) > 0 and isinstance(merged_ranges[0], str): 17544 # List of mergeinfo-strings => list of rangelists 17545 merged_ranges = [RangeList(r) for r in merged_ranges] 17546 17547 status_letters_re = (prop_conflicts or prop_resolved) and ' [UC]' or ' U' 17548 status_letters_mi = ' [UG]' 17549 lines = [] 17550 for i, rr in enumerate(recorded_ranges): 17551 # Merging ... 17552 for sr in merged_ranges[i]: 17553 revstart = sr[0] 17554 revend = len(sr) > 1 and sr[1] or None 17555 lines += [svntest.main.merge_notify_line(revstart, revend, 17556 target=tgt_ospath)] 17557 lines += [status_letters_re + ' ' + re.escape(tgt_ospath) + '\n'] 17558 # Recording mergeinfo ... 17559 revstart = rr[0] 17560 revend = len(rr) > 1 and rr[1] or None 17561 lines += [svntest.main.mergeinfo_notify_line(revstart, revend, 17562 target=tgt_ospath)] 17563 lines += [status_letters_mi + ' ' + re.escape(tgt_ospath) + '\n'] 17564 17565 # Summary of conflicts 17566 lines += svntest.main.summary_of_conflicts(prop_conflicts=prop_conflicts, 17567 prop_resolved=prop_resolved, 17568 as_regex=True) 17569 17570 # The 'match_all=False' is because we also expect some 17571 # 'Resolved conflicted state of ...' lines. 17572 return RegexListOutput(lines, match_all=False) 17573 17574def expected_out_and_err(tgt_ospath, 17575 recorded_ranges, 17576 merged_ranges=None, 17577 prop_conflicts=0, 17578 prop_resolved=0, 17579 expect_error=True): 17580 """Return a tuple (expected_out, expected_err) giving the expected 17581 output and expected error output for a merge into TGT_OSPATH. See 17582 expected_merge_output2() for details of RECORDED_RANGES and 17583 MERGED_RANGES and PROP_CONFLICTS. EXPECT_ERROR should be true iff 17584 we expect the merge to abort with an error about conflicts being 17585 raised. 17586 """ 17587 expected_out = expected_merge_output2(tgt_ospath, recorded_ranges, 17588 merged_ranges, 17589 prop_conflicts, prop_resolved) 17590 if expect_error: 17591 expected_err = RegexListOutput([ 17592 '^svn: E155015: .* conflicts were produced .* into$', 17593 "^'.*" + re.escape(tgt_ospath) + "' --$", 17594 '^resolve all conflicts .* remaining$', 17595 '^unmerged revisions$'], 17596 match_all=False) 17597 else: 17598 expected_err = [] 17599 17600 return expected_out, expected_err 17601 17602def check_mergeinfo(expected_mergeinfo, tgt_ospath): 17603 """Read the mergeinfo on TGT_OSPATH; verify that it matches 17604 EXPECTED_MERGEINFO (list of lines). 17605 """ 17606 svntest.actions.run_and_verify_svn( 17607 expected_mergeinfo, [], 'pg', SVN_PROP_MERGEINFO, tgt_ospath) 17608 17609def simple_merge(src_path, tgt_ospath, rev_args): 17610 """Merge from ^/SRC_PATH to TGT_OSPATH using revision arguments REV_ARGS 17611 (list of '-r...' or '-c...' strings); expect a single-target merge 17612 with no conflicts or errors. 17613 """ 17614 rev_ranges = RangeList(rev_args) 17615 17616 expected_out = expected_merge_output(rev_ranges, 17617 [' U ' + tgt_ospath + '\n', 17618 ' [UG] ' + tgt_ospath + '\n'], 17619 target=tgt_ospath) 17620 src_url = '^/' + src_path 17621 svntest.actions.run_and_verify_svn( 17622 expected_out, [], 17623 'merge', src_url, tgt_ospath, '--accept', 'postpone', *rev_args) 17624 17625@SkipUnless(server_has_mergeinfo) 17626@Issue(4306) 17627# Test for issue #4306 'multiple editor drive file merges record wrong 17628# mergeinfo during conflicts' 17629def conflict_aborted_mergeinfo_described_partial_merge(sbox): 17630 "conflicted split merge can be repeated" 17631 17632 sbox.build() 17633 os.chdir(sbox.wc_dir) 17634 sbox.wc_dir = '' 17635 17636 trunk = 'A' 17637 branch = 'A2' 17638 file = 'mu' 17639 dir = 'B' 17640 trunk_file = 'A/mu' 17641 trunk_dir = 'A/B' 17642 17643 # r2: initial state 17644 for rev in range(4, 11): 17645 sbox.simple_propset('prop-' + str(rev), 'Old pval ' + str(rev), 17646 trunk_file, trunk_dir) 17647 sbox.simple_commit() 17648 17649 # r3: branch 17650 sbox.simple_copy(trunk, branch) 17651 sbox.simple_commit() 17652 17653 zero_rev = 3 17654 17655 def edit_file_or_dir(path, rev, val): 17656 """Make a local edit to the file at PATH.""" 17657 sbox.simple_propset('prop-' + str(rev), val + ' pval ' + str(rev), path) 17658 17659 # r4 through r10: simple edits 17660 for rev in range(4, 11): 17661 edit_file_or_dir(trunk_file, rev, 'Edited') 17662 edit_file_or_dir(trunk_dir, rev, 'Edited') 17663 sbox.simple_commit() 17664 17665 # r14: merge some changes to the branch so that later merges will be split 17666 svntest.actions.run_and_verify_svn(None, [], 'merge', '-c5,9', 17667 '^/' + trunk, sbox.ospath(branch), 17668 '--accept', 'theirs-conflict') 17669 sbox.simple_commit() 17670 sbox.simple_update() 17671 17672 def revert_branch(): 17673 svntest.actions.run_and_verify_svn(None, [], 'revert', '-R', 17674 sbox.ospath(branch)) 17675 17676 def try_merge(relpath, conflict_rev, rev_args, 17677 expected_out_err, expected_mi): 17678 """Revert RELPATH in the branch; make a change that will conflict 17679 with CONFLICT_REV if not None; merge RELPATH in the trunk 17680 to RELPATH in the branch using revision arguments REV_ARGS (list of 17681 '-r...' or '-c...' strings). 17682 17683 EXPECTED_OUT_ERR_MI is a tuple: (expected_out, expected_err, 17684 expected_mi). EXPECTED_OUT and EXPECTED_ERR are instances of 17685 ExpectedOutput. 17686 17687 Expect to find mergeinfo EXPECTED_MI if not None. EXPECTED_MI is 17688 a single mergeinfo-string. 17689 """ 17690 src_path = trunk + '/' + relpath 17691 tgt_path = branch + '/' + relpath 17692 tgt_ospath = sbox.ospath(tgt_path) 17693 17694 expected_out, expected_err = expected_out_err 17695 17696 revert_branch() 17697 17698 # Arrange for the merge to conflict at CONFLICT_REV. 17699 if conflict_rev: 17700 edit_file_or_dir(tgt_path, conflict_rev, 'Conflict') 17701 17702 src_url = '^/' + src_path 17703 svntest.actions.run_and_verify_svn( 17704 expected_out, expected_err, 17705 'merge', src_url, tgt_ospath, '--accept', 'postpone', 17706 *rev_args) 17707 17708 if expected_mi is not None: 17709 expected_mergeinfo = ['/' + src_path + ':' + expected_mi + '\n'] 17710 check_mergeinfo(expected_mergeinfo, tgt_ospath) 17711 17712 # In a mergeinfo-aware merge, each specified revision range is split 17713 # internally into sub-ranges, to avoid any already-merged revisions. 17714 # 17715 # From white-box inspection, we see there are code paths that treat 17716 # the last specified range and the last sub-range specially. The 17717 # first specified range or sub-range is not treated specially in terms 17718 # of the code paths, although it might be in terms of data flow. 17719 # 17720 # We test merges that raise a conflict in the first and last sub-range 17721 # of the first and last specified range. 17722 17723 for target in [file, dir]: 17724 17725 tgt_ospath = sbox.ospath(branch + '/' + target) 17726 17727 # First test: Merge "everything" to the branch. 17728 # 17729 # This merge is split into three sub-ranges: r3-4, r6-8, r10-head. 17730 # We have arranged that the merge will raise a conflict in the first 17731 # sub-range. Since we are postponing conflict resolution, the merge 17732 # should stop after the first sub-range, allowing us to resolve and 17733 # repeat the merge at which point the next sub-range(s) can be merged. 17734 # The mergeinfo on the target then should only reflect that the first 17735 # sub-range (r3-4) has been merged. 17736 # 17737 # Previously the merge failed after merging only r3-4 (as it should) 17738 # but mergeinfo for the whole range was recorded, preventing subsequent 17739 # repeat merges from applying the rest of the source changes. 17740 expect = expected_out_and_err(tgt_ospath, 17741 '3-4', ['3-4'], 17742 prop_conflicts=1) 17743 try_merge(target, 4, [], expect, '3-5,9') 17744 17745 # Try a multiple-range merge that raises a conflict in the 17746 # first sub-range in the first specified range; 17747 expect = expected_out_and_err(tgt_ospath, 17748 '4', ['4'], 17749 prop_conflicts=1) 17750 try_merge(target, 4, ['-c4-6,8-10'], expect, '4-5,9') 17751 # last sub-range in the first specified range; 17752 expect = expected_out_and_err(tgt_ospath, 17753 '4-6', ['4,6'], 17754 prop_conflicts=1) 17755 try_merge(target, 6, ['-c4-6,8-10'], expect, '4-6,9') 17756 # first sub-range in the last specified range; 17757 expect = expected_out_and_err(tgt_ospath, 17758 '4-6,8', ['4,6', '8'], 17759 prop_conflicts=1) 17760 try_merge(target, 8, ['-c4-6,8-10'], expect, '4-6,8-9') 17761 # last sub-range in the last specified range. 17762 # (Expect no error, because 'svn merge' does not throw an error if 17763 # there is no more merging to do when a conflict occurs.) 17764 expect = expected_out_and_err(tgt_ospath, 17765 '4-6,8-10', ['4,6', '8,10'], 17766 prop_conflicts=1, expect_error=False) 17767 try_merge(target, 10, ['-c4-6,8-10'], expect, '4-6,8-10') 17768 17769 # Try similar merges but involving ranges in reverse order. 17770 expect = expected_out_and_err(tgt_ospath, 17771 '8', ['8'], 17772 prop_conflicts=1) 17773 try_merge(target, 8, ['-c8-10,4-6'], expect, '5,8-9') 17774 expect = expected_out_and_err(tgt_ospath, 17775 '8-10', ['8,10'], 17776 prop_conflicts=1) 17777 try_merge(target, 10, ['-c8-10,4-6'], expect, '5,8-10') 17778 expect = expected_out_and_err(tgt_ospath, 17779 '8-10,4', ['8,10', '4'], 17780 prop_conflicts=1) 17781 try_merge(target, 4, ['-c8-10,4-6'], expect, '4-5,8-10') 17782 expect = expected_out_and_err(tgt_ospath, 17783 '8-10,4-6', ['8,10', '4,6'], 17784 prop_conflicts=1, expect_error=False) 17785 try_merge(target, 6, ['-c8-10,4-6'], expect, '4-6,8-10') 17786 17787 # Try some reverse merges, with ranges in forward and reverse order. 17788 # 17789 # Reverse merges start with all source changes merged except 5 and 9. 17790 revert_branch() 17791 simple_merge(trunk + '/' + target, sbox.ospath(branch + '/' + target), 17792 ['-c-5,-9,4,6-8,10']) 17793 sbox.simple_commit() 17794 sbox.simple_update() 17795 17796 expect = expected_out_and_err(tgt_ospath, 17797 '6-4,10-8', ['-6,-4', '-10,-8'], 17798 expect_error=False) 17799 try_merge(target, None, ['-r6:3', '-r10:7'], expect, '7') 17800 expect = expected_out_and_err(tgt_ospath, 17801 '-6', ['-6'], 17802 prop_conflicts=1) 17803 try_merge(target, 6, ['-r6:3', '-r10:7'], expect, '4,7-8,10') 17804 expect = expected_out_and_err(tgt_ospath, 17805 '6-4', ['-6,-4'], 17806 prop_conflicts=1) 17807 try_merge(target, 4, ['-r6:3', '-r10:7'], expect, '7-8,10') 17808 expect = expected_out_and_err(tgt_ospath, 17809 '6-4,-10', ['-6,-4', '-10'], 17810 prop_conflicts=1) 17811 try_merge(target, 10, ['-r6:3', '-r10:7'], expect, '7-8') 17812 expect = expected_out_and_err(tgt_ospath, 17813 '6-4,10-8', ['-6,-4', '-10,-8'], 17814 prop_conflicts=1, expect_error=False) 17815 try_merge(target, 8, ['-r6:3', '-r10:7'], expect, '7') 17816 17817@SkipUnless(server_has_mergeinfo) 17818@Issue(4310) 17819# Test for issue #4310 "each editor drive gets its own notification 17820# during 'svn merge'" 17821def multiple_editor_drive_merge_notifications(sbox): 17822 "each editor drive gets its own notification" 17823 17824 sbox.build() 17825 os.chdir(sbox.wc_dir) 17826 sbox.wc_dir = '' 17827 17828 iota_branch_path = sbox.ospath('iota-copy') 17829 C_branch_path = sbox.ospath('branch') 17830 17831 # Branch a file and a directory: 17832 17833 # r2 17834 sbox.simple_copy('iota', 'iota-copy') 17835 sbox.simple_commit() 17836 17837 # r3 17838 sbox.simple_copy('A/C', 'branch') 17839 sbox.simple_commit() 17840 17841 # r4-8 - Set five non-conflicting properties on the branch parents. 17842 for i in range(0,5): 17843 sbox.simple_propset('foo' + str(i) , 'bar', 'iota') 17844 sbox.simple_propset('foo' + str(i) , 'bar', 'A/C') 17845 sbox.simple_commit() 17846 17847 # Cherry pick merge r5 and r6 to each branch and commit. 17848 svntest.actions.run_and_verify_svn(None, [], 'merge', '^/iota', 17849 '-c', '5,7', iota_branch_path) 17850 svntest.actions.run_and_verify_svn(None, [], 'merge', '^/A/C', 17851 '-c', '5,7', C_branch_path) 17852 sbox.simple_commit() 17853 17854 # Now auto merge all eligible revisions to each branch. 17855 # First the directory target: 17856 # 17857 # TODO: We don't use run_and_verify_merge here because it has limitations 17858 # re checking the merge notification headers -- which need to be improved 17859 # at some point. 17860 svntest.actions.run_and_verify_svn( 17861 ["--- Merging r2 through r4 into '" + C_branch_path + "':\n", 17862 " U " + C_branch_path + "\n", 17863 "--- Merging r6 into '" + C_branch_path + "':\n", 17864 " U " + C_branch_path + "\n", 17865 "--- Merging r8 through r9 into '" + C_branch_path + "':\n", 17866 " U " + C_branch_path + "\n", 17867 "--- Recording mergeinfo for merge of r2 through r9 into '" + 17868 C_branch_path + "':\n", 17869 " U " + C_branch_path + "\n"], 17870 [], 'merge', sbox.repo_url + '/A/C', C_branch_path) 17871 17872 # Then the file target: 17873 # Previously this failed because only the first range notification was 17874 # printed: 17875 # 17876 # >svn merge ^/iota iota-copy 17877 # --- Merging r2 through r4 into 'iota-copy': 17878 # U iota-copy 17879 # U iota-copy 17880 # U iota-copy 17881 # --- Recording mergeinfo for merge of r2 through r9 into 'iota-copy': 17882 # U iota-copy 17883 # 17884 # This is what we expect: 17885 # 17886 # --- Merging r2 through r4 into 'iota-copy': 17887 # U iota-copy 17888 # --- Merging r6 into 'iota-copy': <-- 2nd editor drive 17889 # U iota-copy 17890 # --- Merging r8 through r9 into 'iota-copy': <-- 3rd editor drive 17891 # U iota-copy 17892 # --- Recording mergeinfo for merge of r2 through r9 into 'iota-copy': 17893 # U iota-copy 17894 svntest.actions.run_and_verify_svn( 17895 ["--- Merging r2 through r4 into '" + iota_branch_path + "':\n", 17896 " U " + iota_branch_path + "\n", 17897 "--- Merging r6 into '" + iota_branch_path + "':\n", 17898 " U " + iota_branch_path + "\n", 17899 "--- Merging r8 through r9 into '" + iota_branch_path + "':\n", 17900 " U " + iota_branch_path + "\n", 17901 "--- Recording mergeinfo for merge of r2 through r9 into '" + 17902 iota_branch_path + "':\n", 17903 " U " + iota_branch_path + "\n"], 17904 [], 'merge', sbox.repo_url + '/iota', iota_branch_path) 17905 17906#---------------------------------------------------------------------- 17907@SkipUnless(server_has_mergeinfo) 17908@Issue(4317) 17909# Test for issue #4317 "redundant notifications in single editor drive merge". 17910def single_editor_drive_merge_notifications(sbox): 17911 "single editor drive merge notifications" 17912 sbox.build() 17913 os.chdir(sbox.wc_dir) 17914 sbox.wc_dir = '' 17915 wc_dir = sbox.wc_dir 17916 17917 A_copy_path = sbox.ospath('A_COPY') 17918 D_copy_path = sbox.ospath('A_COPY/D') 17919 psi_copy_path = sbox.ospath('A_COPY/D/H/psi') 17920 omega_copy_path = sbox.ospath('A_COPY/D/H/omega') 17921 beta_copy_path = sbox.ospath('A_COPY/B/E/beta') 17922 17923 # r2 - r6: Copy A to A_COPY and then make some text changes under A. 17924 set_up_branch(sbox) 17925 17926 # r7 - Subtree merge 17927 svntest.actions.run_and_verify_svn(None, [], 'merge', '^/A/D', 17928 '-c4', D_copy_path) 17929 sbox.simple_commit() 17930 sbox.simple_update() 17931 17932 # Previously this failed because of redundant merge notifications 17933 # for r4-7: 17934 # 17935 # >svn merge ^/A A_COPY 17936 # --- Merging r2 through r3 into 'A_COPY\D': 17937 # U A_COPY\D\H\psi 17938 # --- Merging r5 through r7 into 'A_COPY\D': 17939 # U A_COPY\D\H\omega 17940 # --- Merging r4 through r7 into 'A_COPY': 17941 # U A_COPY\B\E\beta 17942 # --- Recording mergeinfo for merge of r2 through r7 into 'A_COPY': 17943 # U A_COPY 17944 # --- Recording mergeinfo for merge of r2 through r7 into 'A_COPY\D': 17945 # U A_COPY\D 17946 # --- Eliding mergeinfo from 'A_COPY\D': 17947 # U A_COPY\D 17948 # 17949 # The order of 'beta' and 'omega' can vary, so use UnorderedOutput. This 17950 # raises the possibility that the test could spuriously pass if the 'U'pdate 17951 # notifications aren't grouped with the correct headers, but that's not what 17952 # is being tested here. 17953 expected_output = svntest.verify.UnorderedOutput( 17954 ["--- Merging r2 through r3 into '" + A_copy_path + "':\n", 17955 "U " + psi_copy_path + "\n", 17956 "--- Merging r4 through r7 into '" + A_copy_path + "':\n", 17957 "U " + omega_copy_path + "\n", 17958 "U " + beta_copy_path + "\n", 17959 "--- Recording mergeinfo for merge of r2 through r7 into '" + 17960 A_copy_path + "':\n", 17961 " U " + A_copy_path + "\n", 17962 "--- Recording mergeinfo for merge of r2 through r7 into '" + 17963 D_copy_path + "':\n", 17964 " U " + D_copy_path + "\n", 17965 "--- Eliding mergeinfo from '" + D_copy_path + "':\n", 17966 " U " + D_copy_path + "\n"]) 17967 svntest.actions.run_and_verify_svn(expected_output, [], 'merge', 17968 sbox.repo_url + '/A', A_copy_path) 17969 17970 # r8 and r9 - Commit and do reverse subtree merge. 17971 sbox.simple_commit() 17972 sbox.simple_update() 17973 svntest.actions.run_and_verify_svn(None, [], 'merge', '^/A/D', 17974 '-c-4', D_copy_path) 17975 sbox.simple_commit() 17976 17977 # Now try a reverse merge. There should only be one notification for 17978 # r7-5: 17979 sbox.simple_update() 17980 expected_output = svntest.verify.UnorderedOutput( 17981 ["--- Reverse-merging r7 through r5 into '" + A_copy_path + "':\n", 17982 "U " + beta_copy_path + "\n", 17983 "U " + omega_copy_path + "\n", 17984 "--- Reverse-merging r4 through r3 into '" + A_copy_path + "':\n", 17985 "U " + psi_copy_path + "\n", 17986 "--- Recording mergeinfo for reverse merge of r7 through r3 into '" + 17987 A_copy_path + "':\n", 17988 " U " + A_copy_path + "\n", 17989 "--- Recording mergeinfo for reverse merge of r7 through r3 into '" + 17990 D_copy_path + "':\n", 17991 " U " + D_copy_path + "\n", 17992 "--- Eliding mergeinfo from '" + D_copy_path + "':\n", 17993 " U " + D_copy_path + "\n"]) 17994 svntest.actions.run_and_verify_svn(expected_output, [], 'merge', 17995 '-r9:2', sbox.repo_url + '/A', 17996 A_copy_path) 17997 17998@SkipUnless(server_has_mergeinfo) 17999@Issue(4316) # 'Merge errors out after resolving conflicts' 18000# Very similar to conflict_aborted_mergeinfo_described_partial_merge() 18001# (test number 135), except here we tell the merge to resolve the 18002# conflicts that are generated part way through a multi-revision-range 18003# merge, and we expect it to continue with the rest of the merge. 18004def conflicted_split_merge_with_resolve(sbox): 18005 "conflicted split merge with resolve" 18006 18007 sbox.build() 18008 os.chdir(sbox.wc_dir) 18009 sbox.wc_dir = '' 18010 18011 trunk = 'A' 18012 branch = 'A2' 18013 file = 'mu' 18014 dir = 'B' 18015 trunk_file = 'A/mu' 18016 trunk_dir = 'A/B' 18017 18018 # r2: initial state 18019 for rev in range(4, 11): 18020 sbox.simple_propset('prop-' + str(rev), 'Old pval ' + str(rev), 18021 trunk_file, trunk_dir) 18022 sbox.simple_commit() 18023 18024 # r3: branch 18025 sbox.simple_update() 18026 sbox.simple_copy(trunk, branch) 18027 sbox.simple_commit() 18028 18029 zero_rev = 3 18030 18031 def edit_file_or_dir(path, rev, val): 18032 """Make a local edit to the file at PATH.""" 18033 sbox.simple_propset('prop-' + str(rev), val + ' pval ' + str(rev), path) 18034 18035 # r4 through r10: simple edits 18036 for rev in range(4, 11): 18037 edit_file_or_dir(trunk_file, rev, 'Edited') 18038 edit_file_or_dir(trunk_dir, rev, 'Edited') 18039 sbox.simple_commit() 18040 18041 # r14: merge some changes to the branch so that later merges will be split 18042 svntest.actions.run_and_verify_svn(None, [], 'merge', '-c5,9', 18043 '^/' + trunk, sbox.ospath(branch), 18044 '--accept', 'theirs-conflict') 18045 sbox.simple_commit() 18046 sbox.simple_update() 18047 18048 def revert_branch(): 18049 svntest.actions.run_and_verify_svn(None, [], 'revert', '-R', 18050 sbox.ospath(branch)) 18051 18052 def try_merge(relpath, conflict_rev, rev_args, 18053 expected_out_err, expected_mi): 18054 """Revert RELPATH in the branch; make a change that will conflict 18055 with CONFLICT_REV if not None; merge RELPATH in the trunk 18056 to RELPATH in the branch using revision arguments REV_ARGS (list of 18057 '-r...' or '-c...' strings). 18058 18059 EXPECTED_OUT_ERR_MI is a tuple: (expected_out, expected_err, 18060 expected_mi). EXPECTED_OUT and EXPECTED_ERR are instances of 18061 ExpectedOutput. 18062 18063 Expect to find mergeinfo EXPECTED_MI if not None. EXPECTED_MI is 18064 a single mergeinfo-string. 18065 """ 18066 src_path = trunk + '/' + relpath 18067 tgt_path = branch + '/' + relpath 18068 tgt_ospath = sbox.ospath(tgt_path) 18069 18070 expected_out, expected_err = expected_out_err 18071 18072 revert_branch() 18073 18074 # Arrange for the merge to conflict at CONFLICT_REV. 18075 if conflict_rev: 18076 edit_file_or_dir(tgt_path, conflict_rev, 'Conflict') 18077 18078 src_url = '^/' + src_path + '@11' 18079 svntest.actions.run_and_verify_svn( 18080 expected_out, expected_err, 18081 'merge', src_url, tgt_ospath, '--accept', 'mine-full', 18082 *rev_args) 18083 18084 if expected_mi is not None: 18085 expected_mergeinfo = ['/' + src_path + ':' + expected_mi + '\n'] 18086 check_mergeinfo(expected_mergeinfo, tgt_ospath) 18087 18088 # In a mergeinfo-aware merge, each specified revision range is split 18089 # internally into sub-ranges, to avoid any already-merged revisions. 18090 # 18091 # From white-box inspection, we see there are code paths that treat 18092 # the last specified range and the last sub-range specially. The 18093 # first specified range or sub-range is not treated specially in terms 18094 # of the code paths, although it might be in terms of data flow. 18095 # 18096 # We test merges that raise a conflict in the first and last sub-range 18097 # of the first and last specified range. 18098 18099 for target in [file, dir]: 18100 18101 tgt_ospath = sbox.ospath(branch + '/' + target) 18102 18103 # First test: Merge "everything" to the branch. 18104 # 18105 # This merge is split into three sub-ranges: r3-4, r6-8, r10-head. 18106 # We have arranged that the merge will raise a conflict in the first 18107 # sub-range. Since we are postponing conflict resolution, the merge 18108 # should stop after the first sub-range, allowing us to resolve and 18109 # repeat the merge at which point the next sub-range(s) can be merged. 18110 # The mergeinfo on the target then should only reflect that the first 18111 # sub-range (r3-4) has been merged. 18112 expect = expected_out_and_err(tgt_ospath, 18113 '3-4,6-11', 18114 ['3-4', '6-8,10-11'], 18115 prop_resolved=1, expect_error=False) 18116 try_merge(target, 4, [], expect, '3-11') 18117 18118 # Try a multiple-range merge that raises a conflict in the 18119 # first sub-range in the first specified range; 18120 expect = expected_out_and_err(tgt_ospath, 18121 '4,6,8-10', 18122 ['4', '6', '8,10'], 18123 prop_resolved=1, expect_error=False) 18124 try_merge(target, 4, ['-c4-6,8-10'], expect, '4-6,8-10') 18125 # last sub-range in the first specified range; 18126 expect = expected_out_and_err(tgt_ospath, 18127 '4-6,8-10', ['4,6', '8,10'], 18128 prop_resolved=1, expect_error=False) 18129 try_merge(target, 6, ['-c4-6,8-10'], expect, '4-6,8-10') 18130 # first sub-range in the last specified range; 18131 expect = expected_out_and_err(tgt_ospath, 18132 '4-6,8,10', 18133 ['4,6', '8', '10'], 18134 prop_resolved=1, expect_error=False) 18135 try_merge(target, 8, ['-c4-6,8-10'], expect, '4-6,8-10') 18136 # last sub-range in the last specified range. 18137 # (Expect no error, because 'svn merge' does not throw an error if 18138 # there is no more merging to do when a conflict occurs.) 18139 expect = expected_out_and_err(tgt_ospath, 18140 '4-6,8-10', ['4,6', '8,10'], 18141 prop_resolved=1, expect_error=False) 18142 try_merge(target, 10, ['-c4-6,8-10'], expect, '4-6,8-10') 18143 18144 # Try similar merges but involving ranges in reverse order. 18145 expect = expected_out_and_err(tgt_ospath, 18146 '8', ['8'], 18147 prop_resolved=1, expect_error=False) 18148 try_merge(target, 8, ['-c8-10,4-6'], expect, '4-6,8-10') 18149 expect = expected_out_and_err(tgt_ospath, 18150 '8-10', ['8,10'], 18151 prop_resolved=1, expect_error=False) 18152 try_merge(target, 10, ['-c8-10,4-6'], expect, '4-6,8-10') 18153 expect = expected_out_and_err(tgt_ospath, 18154 '8-10,4', ['8,10', '4'], 18155 prop_resolved=1, expect_error=False) 18156 try_merge(target, 4, ['-c8-10,4-6'], expect, '4-6,8-10') 18157 expect = expected_out_and_err(tgt_ospath, 18158 '8-10,4-6', ['8,10', '4,6'], 18159 prop_resolved=1, expect_error=False) 18160 try_merge(target, 6, ['-c8-10,4-6'], expect, '4-6,8-10') 18161 18162 # Try some reverse merges, with ranges in forward and reverse order. 18163 # 18164 # Reverse merges start with all source changes merged except 5 and 9. 18165 revert_branch() 18166 simple_merge(trunk + '/' + target, sbox.ospath(branch + '/' + target), 18167 ['-c-5,-9,4,6-8,10']) 18168 sbox.simple_commit() 18169 sbox.simple_update() 18170 18171 expect = expected_out_and_err(tgt_ospath, 18172 '6-4,10-8', ['-6,-4', '-10,-8'], 18173 expect_error=False) 18174 try_merge(target, None, ['-r6:3', '-r10:7'], expect, '7') 18175 expect = expected_out_and_err(tgt_ospath, 18176 '-6,-4,10-8', 18177 ['-6', '-4', '-10,-8'], 18178 prop_resolved=1, expect_error=False) 18179 try_merge(target, 6, ['-r6:3', '-r10:7'], expect, '7') 18180 expect = expected_out_and_err(tgt_ospath, 18181 '6-4,10-8', ['-6,-4', '-10,-8'], 18182 prop_resolved=1, expect_error=False) 18183 try_merge(target, 4, ['-r6:3', '-r10:7'], expect, '7') 18184 expect = expected_out_and_err(tgt_ospath, 18185 '6-4,-10,-8', 18186 ['-6,-4', '-10', '-8'], 18187 prop_resolved=1, expect_error=False) 18188 try_merge(target, 10, ['-r6:3', '-r10:7'], expect, '7') 18189 expect = expected_out_and_err(tgt_ospath, 18190 '6-4,10-8', ['-6,-4', '-10,-8'], 18191 prop_resolved=1, expect_error=False) 18192 try_merge(target, 8, ['-r6:3', '-r10:7'], expect, '7') 18193 18194#---------------------------------------------------------------------- 18195# Test for issue 4367 'merge to shallow WC, repeat merge to infinite 18196# depth WC is broken'. 18197@SkipUnless(server_has_mergeinfo) 18198@Issues(4367) 18199def merge_to_empty_target_merge_to_infinite_target(sbox): 18200 "repeat merge to infinite depth WC conflicts" 18201 18202 sbox.build() 18203 wc_dir = sbox.wc_dir 18204 wc_disk, wc_status = set_up_branch(sbox, branch_only=True) 18205 A_COPY_path = sbox.ospath('A_COPY') 18206 C_COPY_path = sbox.ospath('A_COPY/C') 18207 E_path = sbox.ospath('A/B/E') 18208 J_path = sbox.ospath('A/C/J') 18209 K_path = sbox.ospath('A/C/J/K') 18210 nu1_path = sbox.ospath('A/C/J/nu1') 18211 nu2_path = sbox.ospath('A/C/J/K/nu2') 18212 L_path = sbox.ospath('A/B/L') 18213 nu3_path = sbox.ospath('A/B/L/nu3') 18214 18215 B1_path = sbox.ospath('A/B/B1') 18216 B1a_path = sbox.ospath('A/B/B1/B1a') 18217 test1_path = sbox.ospath('A/B/B1/test.txt') 18218 test2_path = sbox.ospath('A/B/B1/B1a/test.txt') 18219 18220 C1_path = sbox.ospath('A/C/C1') 18221 test3_path = sbox.ospath('A/C/C1/test.txt') 18222 18223 # r3 - Add some subtrees: 18224 # A /A/B/B1 18225 # A /A/B/B1/B1a 18226 # A /A/B/B1/B1a/test.txt 18227 # A /A/B/B1/test.txt 18228 svntest.main.run_svn(None, 'mkdir', B1_path) 18229 svntest.main.run_svn(None, 'mkdir', B1a_path) 18230 svntest.main.file_append(test1_path, "New file.\n") 18231 svntest.main.file_append(test2_path, "New file.\n") 18232 svntest.main.run_svn(None, 'add', test1_path, test2_path) 18233 sbox.simple_commit() 18234 18235 # r4 - Add some another subtree. 18236 # A /A/C/C1 18237 # A /A/C/C1/test.txt 18238 svntest.main.run_svn(None, 'mkdir', C1_path) 18239 svntest.main.file_append(test3_path, "New file.\n") 18240 svntest.main.run_svn(None, 'add', test3_path) 18241 sbox.simple_commit() 18242 18243 # r5 - Delete part of the subtree added in r3. 18244 # D /A/B/B1/B1a 18245 svntest.main.run_svn(None, 'del', B1a_path) 18246 sbox.simple_commit() 18247 18248 # r6 - Set depth of A_COPY to empty, merge all available revs from ^/A. 18249 svntest.actions.run_and_verify_svn(None, [], 'up', 18250 '--set-depth=empty', A_COPY_path) 18251 svntest.actions.run_and_verify_svn(None, [], 'up', 18252 '--set-depth=infinity', C_COPY_path) 18253 svntest.actions.run_and_verify_svn(None, [], 'merge', '^/A', 18254 A_COPY_path) 18255 sbox.simple_commit() 18256 18257 # Update A_COPY back to depth infinity and retry the prior merge. 18258 svntest.actions.run_and_verify_svn(None, [], 'up', 18259 '--set-depth=infinity', A_COPY_path) 18260 18261 expected_output = wc.State(A_COPY_path, { 18262 'B/B1' : Item(status='A '), 18263 'B/B1/test.txt' : Item(status='A '), 18264 'B/B1/B1a' : Item(status='D ', prev_status='A '), 18265 'B/B1/B1a/test.txt' : Item(status='A '), 18266 }) 18267 expected_mergeinfo_output = wc.State(A_COPY_path, { 18268 '' : Item(status=' U'), 18269 'B' : Item(status=' G'), 18270 }) 18271 expected_elision_output = wc.State(A_COPY_path, { 18272 'B' : Item(status=' U'), 18273 }) 18274 expected_status = wc.State(A_COPY_path, { 18275 '' : Item(status=' M'), 18276 'B' : Item(status=' '), 18277 'mu' : Item(status=' '), 18278 'B/B1' : Item(status='A ', copied='+'), 18279 'B/B1/test.txt' : Item(status=' ', copied='+'), 18280 'B/B1/B1a' : Item(status='D ', copied='+'), 18281 'B/B1/B1a/test.txt' : Item(status='D ', copied='+'), 18282 'B/E' : Item(status=' '), 18283 'B/E/alpha' : Item(status=' '), 18284 'B/E/beta' : Item(status=' '), 18285 'B/lambda' : Item(status=' '), 18286 'B/F' : Item(status=' '), 18287 'C' : Item(status=' '), 18288 'C/C1' : Item(status=' '), 18289 'C/C1/test.txt' : Item(status=' '), 18290 'D' : Item(status=' '), 18291 'D/G' : Item(status=' '), 18292 'D/G/pi' : Item(status=' '), 18293 'D/G/rho' : Item(status=' '), 18294 'D/G/tau' : Item(status=' '), 18295 'D/gamma' : Item(status=' '), 18296 'D/H' : Item(status=' '), 18297 'D/H/chi' : Item(status=' '), 18298 'D/H/psi' : Item(status=' '), 18299 'D/H/omega' : Item(status=' '), 18300 }) 18301 expected_status.tweak(wc_rev=6) 18302 expected_status.tweak('B/B1', 'B/B1/test.txt', 'B/B1/B1a', 18303 'B/B1/B1a/test.txt', wc_rev='-') 18304 expected_disk = wc.State('', { 18305 '' : Item(props={SVN_PROP_MERGEINFO : '/A:2-6'}), 18306 'B' : Item(), 18307 'mu' : Item("This is the file 'mu'.\n"), 18308 'B/B1' : Item(), 18309 'B/B1/test.txt' : Item("New file.\n"), 18310 'B/E' : Item(), 18311 'B/E/alpha' : Item("This is the file 'alpha'.\n"), 18312 'B/E/beta' : Item("This is the file 'beta'.\n"), 18313 'B/lambda' : Item("This is the file 'lambda'.\n"), 18314 'B/F' : Item(), 18315 'C' : Item(props={SVN_PROP_MERGEINFO : '/A/C:2-5'}), 18316 'C/C1' : Item(), 18317 'C/C1/test.txt' : Item("New file.\n"), 18318 'D' : Item(), 18319 'D/G' : Item(), 18320 'D/G/pi' : Item("This is the file 'pi'.\n"), 18321 'D/G/rho' : Item("This is the file 'rho'.\n"), 18322 'D/G/tau' : Item("This is the file 'tau'.\n"), 18323 'D/gamma' : Item("This is the file 'gamma'.\n"), 18324 'D/H' : Item(), 18325 'D/H/chi' : Item("This is the file 'chi'.\n"), 18326 'D/H/psi' : Item("This is the file 'psi'.\n"), 18327 'D/H/omega' : Item("This is the file 'omega'.\n"), 18328 }) 18329 expected_skip = wc.State(A_COPY_path, { }) 18330 svntest.actions.run_and_verify_merge(A_COPY_path, None, None, 18331 sbox.repo_url + '/A', None, 18332 expected_output, 18333 expected_mergeinfo_output, 18334 expected_elision_output, 18335 expected_disk, 18336 expected_status, 18337 expected_skip, 18338 [], True, False) 18339 18340 # Commit the merge. 18341 #sbox.simple_commit() 18342 18343def conflict_naming(sbox): 18344 "verify conflict file naming" 18345 18346 sbox.build() 18347 wc_dir = sbox.wc_dir 18348 sbox.simple_append('file.txt', 'This is the initial content\n') 18349 sbox.simple_add('file.txt') 18350 sbox.simple_commit() 18351 18352 sbox.simple_append('file.txt', 'This is the new content\n', truncate=True) 18353 sbox.simple_commit() 18354 18355 sbox.simple_append('file.txt', 'This is conflicting content\n', truncate=True) 18356 18357 # Update - no preserve ext 18358 expected_status = svntest.actions.get_virginal_state(wc_dir, 2) 18359 expected_disk = svntest.main.greek_state.copy() 18360 expected_output = svntest.wc.State(wc_dir, { 18361 'file.txt' : Item(status='C ') 18362 }) 18363 expected_status.add({ 18364 'file.txt' : Item(status='C ', wc_rev='2') 18365 }) 18366 18367 expected_disk.add({ 18368 'file.txt.r3' : Item(contents="This is the new content\n"), 18369 'file.txt.r2' : Item(contents="This is the initial content\n"), 18370 'file.txt' : Item(contents="<<<<<<< .mine\n" \ 18371 "This is conflicting content\n" \ 18372 "||||||| .r3\n" \ 18373 "This is the new content\n" \ 18374 "=======\n" \ 18375 "This is the initial content\n" \ 18376 ">>>>>>> .r2\n"), 18377 'file.txt.mine' : Item(contents="This is conflicting content\n"), 18378 }) 18379 svntest.actions.run_and_verify_update(wc_dir, 18380 expected_output, expected_disk, 18381 expected_status, 18382 [], False, 18383 wc_dir, '-r', '2') 18384 18385 sbox.simple_revert('file.txt') 18386 sbox.simple_update('', revision=3) 18387 sbox.simple_append('file.txt', 'This is conflicting content\n', truncate=True) 18388 18389 # Update - preserve ext 18390 expected_status = svntest.actions.get_virginal_state(wc_dir, 2) 18391 expected_disk = svntest.main.greek_state.copy() 18392 expected_output = svntest.wc.State(wc_dir, { 18393 'file.txt' : Item(status='C ') 18394 }) 18395 expected_status.add({ 18396 'file.txt' : Item(status='C ', wc_rev='2') 18397 }) 18398 18399 expected_disk.add({ 18400 'file.txt.r3.txt' : Item(contents="This is the new content\n"), 18401 'file.txt.r2.txt' : Item(contents="This is the initial content\n"), 18402 'file.txt' : Item(contents="<<<<<<< .mine.txt\n" \ 18403 "This is conflicting content\n" \ 18404 "||||||| .r3.txt\n" \ 18405 "This is the new content\n" \ 18406 "=======\n" \ 18407 "This is the initial content\n" \ 18408 ">>>>>>> .r2.txt\n"), 18409 'file.txt.mine.txt' : Item(contents="This is conflicting content\n"), 18410 }) 18411 svntest.actions.run_and_verify_update( 18412 wc_dir, 18413 expected_output, expected_disk, expected_status, 18414 [], False, 18415 wc_dir, '-r', '2', 18416 '--config-option', 18417 'config:miscellany:preserved-conflict-file-exts=' + 18418 'c txt h') 18419 18420 sbox.simple_revert('file.txt') 18421 sbox.simple_update('', revision=3) 18422 sbox.simple_append('file.txt', 'This is conflicting content\n', truncate=True) 18423 18424 # Merge - no preserve ext 18425 expected_status = svntest.actions.get_virginal_state(wc_dir, 3) 18426 expected_disk = svntest.main.greek_state.copy() 18427 expected_status.add({ 18428 'file.txt' : Item(status='C ', wc_rev='3') 18429 }) 18430 expected_disk.add({ 18431 'file.txt.merge-left.r3' : Item(contents="This is the new content\n"), 18432 'file.txt.merge-right.r2': Item(contents="This is the initial content\n"), 18433 'file.txt' : Item(contents="<<<<<<< .working\n" \ 18434 "This is conflicting content\n" \ 18435 "||||||| .merge-left.r3\n" \ 18436 "This is the new content\n" \ 18437 "=======\n" \ 18438 "This is the initial content\n" \ 18439 ">>>>>>> .merge-right.r2\n"), 18440 'file.txt.working' : Item(contents="This is conflicting content\n"), 18441 }) 18442 18443 svntest.actions.run_and_verify_svn(None, [], 18444 'merge', '-c-3', '^/', sbox.ospath('')) 18445 svntest.actions.run_and_verify_status(wc_dir, expected_status) 18446 svntest.actions.verify_disk(wc_dir, expected_disk) 18447 18448 sbox.simple_revert('file.txt') 18449 sbox.simple_append('file.txt', 'This is conflicting content\n', truncate=True) 18450 18451 # Merge - preserve ext 18452 expected_status = svntest.actions.get_virginal_state(wc_dir, 3) 18453 expected_disk = svntest.main.greek_state.copy() 18454 expected_status.add({ 18455 'file.txt' : Item(status='C ', wc_rev='3') 18456 }) 18457 expected_disk.add({ 18458 'file.txt.merge-left.r3.txt' : Item(contents="This is the new content\n"), 18459 'file.txt.merge-right.r2.txt': Item(contents="This is the initial content\n"), 18460 'file.txt' : Item(contents="<<<<<<< .working.txt\n" \ 18461 "This is conflicting content\n" \ 18462 "||||||| .merge-left.r3.txt\n" \ 18463 "This is the new content\n" \ 18464 "=======\n" \ 18465 "This is the initial content\n" \ 18466 ">>>>>>> .merge-right.r2.txt\n"), 18467 'file.txt.working.txt' : Item(contents="This is conflicting content\n"), 18468 }) 18469 18470 svntest.actions.run_and_verify_svn( 18471 None, [], 18472 'merge', '-c-3', '^/', sbox.ospath(''), 18473 '--config-option', 18474 'config:miscellany:preserved-conflict-file-exts=' + 18475 'c txt h') 18476 svntest.actions.run_and_verify_status(wc_dir, expected_status) 18477 svntest.actions.verify_disk(wc_dir, expected_disk) 18478 18479def merge_dir_delete_force(sbox): 18480 "merge a directory delete with --force" 18481 18482 sbox.build() 18483 18484 sbox.simple_rm('A/D/G') 18485 sbox.simple_commit() # r2 18486 18487 sbox.simple_update(revision=1) 18488 18489 # Just merging r2 on r1 succeeds 18490 svntest.actions.run_and_verify_svn(None, [], 18491 'merge', '-c2', '^/', sbox.wc_dir, 18492 '--ignore-ancestry') 18493 18494 # Bring working copy to r1 again 18495 svntest.actions.run_and_verify_svn(None, [], 18496 'revert', '-R', sbox.wc_dir) 18497 18498 # But when using --force this same merge caused a segfault in 1.8.0-1.8.8 18499 svntest.actions.run_and_verify_svn(None, [], 18500 'merge', '-c2', '^/', sbox.wc_dir, 18501 '--ignore-ancestry', '--force') 18502 18503# Issue #4859: Merge removing a folder with non-inheritable mergeinfo -> 18504# E155023: can't set properties: invalid status for updating properties 18505@Issue(4859) 18506def merge_deleted_folder_with_mergeinfo(sbox): 18507 "merge deleted folder with mergeinfo" 18508 18509 sbox.build() 18510 18511 was_cwd = os.getcwd() 18512 os.chdir(sbox.wc_dir) 18513 sbox.wc_dir = '' 18514 18515 # Some non-inheritable mergeinfo 18516 sbox.simple_propset('svn:mergeinfo', '/A/C:1*', 'A/D') 18517 sbox.simple_commit() # r2 18518 18519 # Branching 18520 sbox.simple_repo_copy('A', 'branch_A') # r3 18521 sbox.simple_update() 18522 18523 # On branch, remove a folder that has non-inheritable mergeinfo 18524 sbox.simple_rm('branch_A/D') 18525 sbox.simple_commit() # r4 18526 18527 sbox.simple_update() 18528 18529 # A merge that removes that folder 18530 # (merge revision 4 only from 'branch_A' to 'A') 18531 expected_output = wc.State(sbox.ospath(''), { 18532 'A/D' : Item(status='D '), 18533 }) 18534 expected_mergeinfo_output = wc.State(sbox.ospath(''), { 18535 'A' : Item(status=' U'), 18536 }) 18537 expected_status = svntest.actions.get_virginal_state(sbox.ospath('A'), 4).subtree('A') 18538 expected_status.add({ '': Item(status=' M', wc_rev=4) }) 18539 expected_status.tweak_some( 18540 lambda path, item: [True] if path.split('/')[0] == 'D' else [], 18541 status='D ') 18542 svntest.actions.run_and_verify_merge(sbox.ospath('A'), 3, 4, 18543 '^/branch_A', None, 18544 expected_output, 18545 expected_mergeinfo_output, 18546 None, 18547 None, 18548 expected_status, 18549 wc.State('', {}), 18550 [], 18551 check_props=False, 18552 dry_run=False # as dry run is broken 18553 ) 18554 18555 os.chdir(was_cwd) 18556 18557# Issue #4859: Merge removing a folder with non-inheritable mergeinfo -> 18558# E155023: can't set properties: invalid status for updating properties 18559# 18560# In this test we split the merge into two separate operable parts, a 18561# delete followed later by an add, to check it will set the mergeinfo on the 18562# subtree paths if the deleted folder is later replaced within the same 18563# overall merge. 18564@Issue(4859) 18565def merge_deleted_folder_with_mergeinfo_2(sbox): 18566 "merge deleted folder with mergeinfo 2" 18567 18568 sbox.build() 18569 18570 was_cwd = os.getcwd() 18571 os.chdir(sbox.wc_dir) 18572 sbox.wc_dir = '' 18573 18574 # Some non-inheritable mergeinfo 18575 sbox.simple_propset('svn:mergeinfo', '/A/C:1*', 'A/D') 18576 sbox.simple_commit() # r2 18577 18578 # Branching 18579 sbox.simple_repo_copy('A', 'branch_A') # r3 18580 sbox.simple_update() 18581 18582 # On branch, remove a folder that has non-inheritable mergeinfo 18583 sbox.simple_rm('branch_A/D') 18584 sbox.simple_commit() # r4 18585 18586 # A commit that we don't want to merge from the branch, to split the merge 18587 # into two separate operable parts. 18588 sbox.simple_mkdir('branch_A/IgnoreThis') 18589 sbox.simple_commit() # r5 18590 18591 # On branch, replace the deleted folder with a new one, with mergeinfo, 18592 # to check we don't omit setting mergeinfo on this. 18593 sbox.simple_mkdir('branch_A/D') 18594 sbox.simple_propset('svn:mergeinfo', '/branch_B/C:1*', 'branch_A/D') 18595 sbox.simple_mkdir('branch_A/D/G', 'branch_A/D/G2') 18596 sbox.simple_propset('svn:mergeinfo', '/branch_B/C/G:1*', 'branch_A/D/G') 18597 sbox.simple_propset('svn:mergeinfo', '/branch_B/C/G2:1*', 'branch_A/D/G2') 18598 sbox.simple_commit() # r6 18599 18600 sbox.simple_propset('svn:mergeinfo', '/branch_A:5', 'A') 18601 sbox.simple_commit() # r7 18602 18603 sbox.simple_update() 18604 18605 # A merge that removes that folder 18606 expected_output = wc.State(sbox.ospath(''), { 18607 'A/D' : Item(status='A ', prev_status='D '), 18608 'A/D/G' : Item(status='A '), 18609 'A/D/G2' : Item(status='A '), 18610 }) 18611 # verify that mergeinfo is set/changed on A/D, A/D/G, A/D/G2. 18612 expected_mergeinfo_output = wc.State(sbox.ospath(''), { 18613 'A' : Item(status=' U'), 18614 'A/D' : Item(status=' G'), 18615 'A/D/G' : Item(status=' G'), 18616 'A/D/G2' : Item(status=' G'), 18617 }) 18618 expected_status = svntest.actions.get_virginal_state(sbox.ospath('A'), 7).subtree('A') 18619 expected_status.tweak_some( 18620 lambda path, item: [True] if path.split('/')[0] == 'D' else [], 18621 status='D ') 18622 expected_status.add({ 18623 '' : Item(status=' M', wc_rev=7), 18624 'D' : Item(status='RM', copied='+', wc_rev='-'), 18625 'D/G' : Item(status=' M', copied='+', wc_rev='-'), 18626 'D/G2' : Item(status=' M', copied='+', wc_rev='-'), 18627 }) 18628 svntest.actions.run_and_verify_merge(sbox.ospath('A'), None, None, 18629 '^/branch_A', None, 18630 expected_output, 18631 expected_mergeinfo_output, 18632 None, 18633 None, 18634 expected_status, 18635 wc.State('', {}), 18636 [], 18637 check_props=False, 18638 dry_run=False # as dry run is broken 18639 ) 18640 18641 # verify that mergeinfo is set/changed on A/D, A/D/G, A/D/G2. 18642 18643 # NOTE: When writing out multi-line prop values in svn:* props, the 18644 # client converts to local encoding and local eol style. 18645 # Therefore, the expected output must contain the right kind of eoln 18646 # strings. That's why we use os.linesep in the tests below, not just 18647 # plain '\n'. 18648 18649 expected_mergeinfo = [ 18650 ('A', ['/branch_A:3-7']), 18651 ('A/D', ['/branch_A/D:5-7'+os.linesep, '/branch_B/C:1*']), 18652 ('A/D/G', ['/branch_A/D/G:5-7'+os.linesep, '/branch_B/C/G:1*']), 18653 ('A/D/G2', ['/branch_A/D/G2:5-7'+os.linesep, '/branch_B/C/G2:1*']), 18654 ] 18655 for path, mergeinfo in expected_mergeinfo: 18656 svntest.actions.check_prop('svn:mergeinfo', sbox.ospath(path), 18657 [m.encode() for m in mergeinfo]) 18658 18659 os.chdir(was_cwd) 18660 18661######################################################################## 18662# Run the tests 18663 18664 18665# list all tests here, starting with None: 18666test_list = [ None, 18667 textual_merges_galore, 18668 add_with_history, 18669 simple_property_merges, 18670 merge_with_implicit_target_using_r, 18671 merge_with_implicit_target_using_c, 18672 merge_with_implicit_target_and_revs, 18673 merge_similar_unrelated_trees, 18674 merge_with_prev, 18675 merge_binary_file, 18676 merge_one_file_using_r, 18677 merge_one_file_using_c, 18678 merge_one_file_using_implicit_revs, 18679 merge_record_only, 18680 merge_in_new_file_and_diff, 18681 merge_skips_obstructions, 18682 merge_into_missing, 18683 dry_run_adds_file_with_prop, 18684 merge_binary_with_common_ancestry, 18685 merge_funny_chars_on_path, 18686 merge_keyword_expansions, 18687 merge_prop_change_to_deleted_target, 18688 merge_file_with_space_in_its_name, 18689 merge_dir_branches, 18690 safe_property_merge, 18691 property_merge_from_branch, 18692 property_merge_undo_redo, 18693 cherry_pick_text_conflict, 18694 merge_file_replace, 18695 merge_dir_replace, 18696 merge_dir_and_file_replace, 18697 merge_file_replace_to_mixed_rev_wc, 18698 merge_ignore_whitespace, 18699 merge_ignore_eolstyle, 18700 merge_conflict_markers_matching_eol, 18701 merge_eolstyle_handling, 18702 avoid_repeated_merge_using_inherited_merge_info, 18703 avoid_repeated_merge_on_subtree_with_merge_info, 18704 obey_reporter_api_semantics_while_doing_subtree_merges, 18705 mergeinfo_inheritance, 18706 mergeinfo_elision, 18707 mergeinfo_inheritance_and_discontinuous_ranges, 18708 merge_to_target_with_copied_children, 18709 merge_to_switched_path, 18710 merge_to_path_with_switched_children, 18711 merge_with_implicit_target_file, 18712 empty_mergeinfo, 18713 prop_add_to_child_with_mergeinfo, 18714 foreign_repos_does_not_update_mergeinfo, 18715 avoid_reflected_revs, 18716 update_loses_mergeinfo, 18717 merge_loses_mergeinfo, 18718 single_file_replace_style_merge_capability, 18719 merge_to_out_of_date_target, 18720 merge_with_depth_files, 18721 merge_away_subtrees_noninheritable_ranges, 18722 merge_to_sparse_directories, 18723 merge_old_and_new_revs_from_renamed_dir, 18724 merge_with_child_having_different_rev_ranges_to_merge, 18725 merge_old_and_new_revs_from_renamed_file, 18726 merge_with_auto_rev_range_detection, 18727 cherry_picking, 18728 propchange_of_subdir_raises_conflict, 18729 reverse_merge_prop_add_on_child, 18730 merge_target_with_non_inheritable_mergeinfo, 18731 self_reverse_merge, 18732 ignore_ancestry_and_mergeinfo, 18733 merge_from_renamed_branch_fails_while_avoiding_repeat_merge, 18734 merge_source_normalization_and_subtree_merges, 18735 new_subtrees_should_not_break_merge, 18736 dont_add_mergeinfo_from_own_history, 18737 merge_range_predates_history, 18738 foreign_repos, 18739 foreign_repos_uuid, 18740 foreign_repos_2_url, 18741 merge_added_subtree, 18742 merge_unknown_url, 18743 reverse_merge_away_all_mergeinfo, 18744 dont_merge_revs_into_subtree_that_predate_it, 18745 merge_chokes_on_renamed_subtrees, 18746 dont_explicitly_record_implicit_mergeinfo, 18747 merge_broken_link, 18748 subtree_merges_dont_intersect_with_targets, 18749 subtree_source_missing_in_requested_range, 18750 subtrees_with_empty_mergeinfo, 18751 commit_to_subtree_added_by_merge, 18752 del_identical_file, 18753 del_sched_add_hist_file, 18754 subtree_merges_dont_cause_spurious_conflicts, 18755 merge_target_and_subtrees_need_nonintersecting_ranges, 18756 merge_two_edits_to_same_prop, 18757 merge_an_eol_unification_and_set_svn_eol_style, 18758 merge_adds_mergeinfo_correctly, 18759 natural_history_filtering, 18760 subtree_gets_changes_even_if_ultimately_deleted, 18761 no_self_referential_filtering_on_added_path, 18762 merge_range_prior_to_rename_source_existence, 18763 dont_merge_gaps_in_history, 18764 mergeinfo_deleted_by_a_merge_should_disappear, 18765 noop_file_merge, 18766 handle_gaps_in_implicit_mergeinfo, 18767 copy_then_replace_via_merge, 18768 record_only_merge, 18769 merge_automatic_conflict_resolution, 18770 skipped_files_get_correct_mergeinfo, 18771 committed_case_only_move_and_revert, 18772 merge_into_wc_for_deleted_branch, 18773 foreign_repos_del_and_props, 18774 immediate_depth_merge_creates_minimal_subtree_mergeinfo, 18775 record_only_merge_creates_self_referential_mergeinfo, 18776 dav_skelta_mode_causes_spurious_conflicts, 18777 merge_into_locally_added_file, 18778 merge_into_locally_added_directory, 18779 merge_with_os_deleted_subtrees, 18780 no_self_referential_or_nonexistent_inherited_mergeinfo, 18781 subtree_merges_inherit_invalid_working_mergeinfo, 18782 merge_change_to_file_with_executable, 18783 dry_run_merge_conflicting_binary, 18784 foreign_repos_prop_conflict, 18785 merge_adds_subtree_with_mergeinfo, 18786 reverse_merge_adds_subtree, 18787 merged_deletion_causes_tree_conflict, 18788 record_only_merge_adds_new_subtree_mergeinfo, 18789 unnecessary_noninheritable_mergeinfo_missing_subtrees, 18790 unnecessary_noninheritable_mergeinfo_shallow_merge, 18791 svnmucc_abuse_1, 18792 merge_source_with_replacement, 18793 reverse_merge_with_rename, 18794 merge_adds_then_deletes_subtree, 18795 merge_with_added_subtrees_with_mergeinfo, 18796 merge_with_externals_with_mergeinfo, 18797 merge_binary_file_with_keywords, 18798 merge_conflict_when_keywords_removed, 18799 merge_target_selection, 18800 merge_properties_on_adds, 18801 conflict_aborted_mergeinfo_described_partial_merge, 18802 multiple_editor_drive_merge_notifications, 18803 single_editor_drive_merge_notifications, 18804 conflicted_split_merge_with_resolve, 18805 merge_to_empty_target_merge_to_infinite_target, 18806 conflict_naming, 18807 merge_dir_delete_force, 18808 merge_deleted_folder_with_mergeinfo, 18809 merge_deleted_folder_with_mergeinfo_2, 18810 ] 18811 18812if __name__ == '__main__': 18813 svntest.main.run_tests(test_list) 18814 # NOTREACHED 18815 18816 18817### End of file. 18818