1#!/usr/bin/env python 2# 3# mergeinfo_tests.py: testing Merge Tracking reporting 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 29 30# Our testing module 31import svntest 32from svntest import wc 33 34# (abbreviation) 35Item = wc.StateItem 36Skip = svntest.testcase.Skip_deco 37SkipUnless = svntest.testcase.SkipUnless_deco 38XFail = svntest.testcase.XFail_deco 39Issues = svntest.testcase.Issues_deco 40Issue = svntest.testcase.Issue_deco 41Wimp = svntest.testcase.Wimp_deco 42exp_noop_up_out = svntest.actions.expected_noop_update_output 43 44from svntest.main import SVN_PROP_MERGEINFO 45from svntest.main import server_has_mergeinfo 46 47# Get a couple merge helpers 48from svntest.mergetrees import set_up_branch 49from svntest.mergetrees import expected_merge_output 50 51def adjust_error_for_server_version(expected_err): 52 "Return the expected error regexp appropriate for the server version." 53 if server_has_mergeinfo(): 54 return expected_err 55 else: 56 return ".*Retrieval of mergeinfo unsupported by '.+'" 57 58###################################################################### 59# Tests 60# 61# Each test must return on success or raise on failure. 62 63 64#---------------------------------------------------------------------- 65 66def no_mergeinfo(sbox): 67 "'mergeinfo' on a URL that lacks mergeinfo" 68 69 sbox.build(create_wc=False) 70 sbox.simple_repo_copy('A', 'A2') 71 svntest.actions.run_and_verify_mergeinfo(adjust_error_for_server_version(""), 72 [], 73 sbox.repo_url + '/A', 74 sbox.repo_url + '/A2', 75 "--show-revs=merged") 76 77@SkipUnless(server_has_mergeinfo) 78def mergeinfo(sbox): 79 "'mergeinfo' on a path with mergeinfo" 80 81 sbox.build() 82 wc_dir = sbox.wc_dir 83 84 # make a branch 'A2' 85 sbox.simple_repo_copy('A', 'A2') # r2 86 # make a change in branch 'A' 87 sbox.simple_mkdir('A/newdir') 88 sbox.simple_commit() # r3 89 sbox.simple_update() 90 91 # Dummy up some mergeinfo. 92 svntest.actions.run_and_verify_svn(None, [], 93 'ps', SVN_PROP_MERGEINFO, '/A:3', 94 sbox.ospath('A2')) 95 svntest.actions.run_and_verify_mergeinfo(adjust_error_for_server_version(""), 96 ['3'], 97 sbox.repo_url + '/A', 98 sbox.ospath('A2'), 99 "--show-revs=merged") 100 101@SkipUnless(server_has_mergeinfo) 102def explicit_mergeinfo_source(sbox): 103 "'mergeinfo' with source selection" 104 105 # The idea is the target has mergeinfo pertaining to two or more different 106 # source branches and we're asking about just one of them. 107 108 sbox.build() 109 110 def url(relpath): 111 return sbox.repo_url + '/' + relpath 112 def path(relpath): 113 return sbox.ospath(relpath) 114 115 B = 'A/B' 116 117 # make some branches 118 B2 = 'A/B2' 119 B3 = 'A/B3' 120 sbox.simple_repo_copy(B, B2) # r2 121 sbox.simple_repo_copy(B, B3) # r3 122 sbox.simple_update() 123 124 # make changes in the branches 125 sbox.simple_mkdir('A/B2/newdir') 126 sbox.simple_commit() # r4 127 sbox.simple_mkdir('A/B3/newdir') 128 sbox.simple_commit() # r5 129 130 # Put dummy mergeinfo on branch root 131 mergeinfo = '/A/B2:2-5\n/A/B3:2-5\n' 132 sbox.simple_propset(SVN_PROP_MERGEINFO, mergeinfo, B) 133 sbox.simple_commit() 134 135 # Check using each of our recorded merge sources (as paths and URLs). 136 svntest.actions.run_and_verify_mergeinfo(adjust_error_for_server_version(""), 137 ['2', '4'], url(B2), path(B), 138 "--show-revs=merged") 139 svntest.actions.run_and_verify_mergeinfo(adjust_error_for_server_version(""), 140 ['2', '4'], path(B2), path(B), 141 "--show-revs=merged") 142 svntest.actions.run_and_verify_mergeinfo(adjust_error_for_server_version(""), 143 ['3', '5'], url(B3), path(B), 144 "--show-revs=merged") 145 svntest.actions.run_and_verify_mergeinfo(adjust_error_for_server_version(""), 146 ['3', '5'], path(B3), path(B), 147 "--show-revs=merged") 148 149@SkipUnless(server_has_mergeinfo) 150def mergeinfo_non_source(sbox): 151 "'mergeinfo' with uninteresting source selection" 152 153 sbox.build() 154 wc_dir = sbox.wc_dir 155 H_path = os.path.join(wc_dir, 'A', 'D', 'H') 156 H2_path = os.path.join(wc_dir, 'A', 'D', 'H2') 157 B_url = sbox.repo_url + '/A/B' 158 B_path = os.path.join(wc_dir, 'A', 'B') 159 G_url = sbox.repo_url + '/A/D/G' 160 G_path = os.path.join(wc_dir, 'A', 'D', 'G') 161 H2_url = sbox.repo_url + '/A/D/H2' 162 163 # Make a copy, and dummy up some mergeinfo. 164 mergeinfo = '/A/B:1\n/A/D/G:1\n' 165 svntest.actions.set_prop(SVN_PROP_MERGEINFO, mergeinfo, H_path) 166 svntest.main.run_svn(None, "cp", H_path, H2_path) 167 svntest.main.run_svn(None, "ci", "-m", "r2", wc_dir) 168 169 # Check on a source we haven't "merged" from. 170 svntest.actions.run_and_verify_mergeinfo(adjust_error_for_server_version(""), 171 [], H2_url, H_path, 172 "--show-revs=merged") 173 174#---------------------------------------------------------------------- 175# Issue #3138 176@SkipUnless(server_has_mergeinfo) 177@Issue(3138) 178def mergeinfo_on_unknown_url(sbox): 179 "mergeinfo of an unknown url should return error" 180 181 sbox.build() 182 wc_dir = sbox.wc_dir 183 184 # remove a path from the repo and commit. 185 iota_path = os.path.join(wc_dir, 'iota') 186 svntest.actions.run_and_verify_svn(None, [], 'rm', iota_path) 187 svntest.actions.run_and_verify_svn(None, [], 188 "ci", wc_dir, "-m", "log message") 189 190 url = sbox.repo_url + "/iota" 191 expected_err = adjust_error_for_server_version(".*File not found.*iota.*|" 192 ".*iota.*path not found.*") 193 svntest.actions.run_and_verify_svn(None, expected_err, 194 "mergeinfo", "--show-revs", "eligible", 195 url, wc_dir) 196 197# Test for issue #3126 'svn mergeinfo shows too few or too many 198# eligible revisions'. Specifically 199# https://issues.apache.org/jira/browse/SVN-3126#desc5. 200@SkipUnless(server_has_mergeinfo) 201@Issue(3126) 202def non_inheritable_mergeinfo(sbox): 203 "non-inheritable mergeinfo shows as merged" 204 205 sbox.build() 206 wc_dir = sbox.wc_dir 207 expected_disk, expected_status = set_up_branch(sbox) 208 209 # Some paths we'll care about 210 A_COPY_path = os.path.join(wc_dir, "A_COPY") 211 D_COPY_path = os.path.join(wc_dir, "A_COPY", "D") 212 rho_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "G", "rho") 213 214 # Update the WC, then merge r4 from A to A_COPY and r6 from A to A_COPY 215 # at --depth empty and commit the merges as r7. 216 svntest.actions.run_and_verify_svn(exp_noop_up_out(6), [], 'up', 217 wc_dir) 218 expected_status.tweak(wc_rev=6) 219 svntest.actions.run_and_verify_svn( 220 expected_merge_output([[4]], 221 ['U ' + rho_COPY_path + '\n', 222 ' U ' + A_COPY_path + '\n',]), 223 [], 'merge', '-c4', 224 sbox.repo_url + '/A', 225 A_COPY_path) 226 svntest.actions.run_and_verify_svn( 227 expected_merge_output([[6]], ' G ' + A_COPY_path + '\n'), 228 [], 'merge', '-c6', 229 sbox.repo_url + '/A', 230 A_COPY_path, '--depth', 'empty') 231 expected_output = wc.State(wc_dir, { 232 'A_COPY' : Item(verb='Sending'), 233 'A_COPY/D/G/rho' : Item(verb='Sending'), 234 }) 235 expected_status.tweak('A_COPY', 'A_COPY/D/G/rho', wc_rev=7) 236 svntest.actions.run_and_verify_commit(wc_dir, expected_output, 237 expected_status) 238 239 # Update the WC a last time to ensure full inheritance. 240 svntest.actions.run_and_verify_svn(exp_noop_up_out(7), [], 'up', 241 wc_dir) 242 243 # Despite being non-inheritable, r6 should still show as merged to A_COPY 244 # and not eligible for merging. 245 svntest.actions.run_and_verify_mergeinfo(adjust_error_for_server_version(""), 246 ['4','6*'], 247 sbox.repo_url + '/A', 248 A_COPY_path, 249 '--show-revs', 'merged') 250 svntest.actions.run_and_verify_mergeinfo(adjust_error_for_server_version(""), 251 ['3','5','6*'], 252 sbox.repo_url + '/A', 253 A_COPY_path, 254 '--show-revs', 'eligible') 255 # But if we drop down to A_COPY/D, r6 should show as eligible because it 256 # was only merged into A_COPY, no deeper. 257 svntest.actions.run_and_verify_mergeinfo(adjust_error_for_server_version(""), 258 ['4'], 259 sbox.repo_url + '/A/D', 260 D_COPY_path, 261 '--show-revs', 'merged') 262 svntest.actions.run_and_verify_mergeinfo(adjust_error_for_server_version(""), 263 ['3','6'], 264 sbox.repo_url + '/A/D', 265 D_COPY_path, 266 '--show-revs', 'eligible') 267 268# Test for -R option with svn mergeinfo subcommand. 269# 270# Test for issue #3242 'Subversion demands unnecessary access to parent 271# directories of operations' 272@Issue(3242) 273@SkipUnless(server_has_mergeinfo) 274def recursive_mergeinfo(sbox): 275 "test svn mergeinfo -R" 276 277 sbox.build() 278 wc_dir = sbox.wc_dir 279 expected_disk, expected_status = set_up_branch(sbox) 280 281 # Some paths we'll care about 282 A_path = os.path.join(wc_dir, "A") 283 A_COPY_path = os.path.join(wc_dir, "A_COPY") 284 B_COPY_path = os.path.join(wc_dir, "A_COPY", "B") 285 C_COPY_path = os.path.join(wc_dir, "A_COPY", "C") 286 rho_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "G", "rho") 287 H_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "H") 288 F_COPY_path = os.path.join(wc_dir, "A_COPY", "B", "F") 289 omega_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "H", "omega") 290 beta_COPY_path = os.path.join(wc_dir, "A_COPY", "B", "E", "beta") 291 A2_path = os.path.join(wc_dir, "A2") 292 nu_path = os.path.join(wc_dir, "A2", "B", "F", "nu") 293 nu_COPY_path = os.path.join(wc_dir, "A_COPY", "B", "F", "nu") 294 nu2_path = os.path.join(wc_dir, "A2", "C", "nu2") 295 296 # Rename A to A2 in r7. 297 svntest.actions.run_and_verify_svn(exp_noop_up_out(6), [], 'up', wc_dir) 298 svntest.actions.run_and_verify_svn(None, [], 299 'ren', A_path, A2_path) 300 svntest.actions.run_and_verify_svn(None, [], 301 'ci', wc_dir, '-m', 'rename A to A2') 302 303 # Add the files A/B/F/nu and A/C/nu2 and commit them as r8. 304 svntest.main.file_write(nu_path, "A new file.\n") 305 svntest.main.file_write(nu2_path, "Another new file.\n") 306 svntest.main.run_svn(None, "add", nu_path, nu2_path) 307 svntest.actions.run_and_verify_svn(None, [], 308 'ci', wc_dir, '-m', 'Add 2 new files') 309 310 # Do several merges to create varied subtree mergeinfo 311 312 # Merge r4 from A2 to A_COPY at depth empty 313 svntest.actions.run_and_verify_svn(exp_noop_up_out(8), [], 'up', 314 wc_dir) 315 svntest.actions.run_and_verify_svn( 316 expected_merge_output([[4]], ' U ' + A_COPY_path + '\n'), 317 [], 'merge', '-c4', '--depth', 'empty', 318 sbox.repo_url + '/A2', 319 A_COPY_path) 320 321 # Merge r6 from A2/D/H to A_COPY/D/H 322 svntest.actions.run_and_verify_svn( 323 expected_merge_output([[6]], 324 ['U ' + omega_COPY_path + '\n', 325 ' G ' + H_COPY_path + '\n']), 326 [], 'merge', '-c6', 327 sbox.repo_url + '/A2/D/H', 328 H_COPY_path) 329 330 # Merge r5 from A2 to A_COPY 331 svntest.actions.run_and_verify_svn( 332 expected_merge_output([[5]], 333 ['U ' + beta_COPY_path + '\n', 334 ' G ' + A_COPY_path + '\n', 335 ' G ' + B_COPY_path + '\n', 336 ' U ' + B_COPY_path + '\n',], # Elision 337 elides=True), 338 [], 'merge', '-c5', 339 sbox.repo_url + '/A2', 340 A_COPY_path) 341 342 # Reverse merge -r5 from A2/C to A_COPY/C leaving empty mergeinfo on 343 # A_COPY/C. 344 svntest.actions.run_and_verify_svn( 345 expected_merge_output([[-5]], 346 ' G ' + C_COPY_path + '\n'), 347 [], 'merge', '-c-5', 348 sbox.repo_url + '/A2/C', C_COPY_path) 349 350 # Merge r8 from A2/B/F to A_COPY/B/F 351 svntest.actions.run_and_verify_svn( 352 expected_merge_output([[8]], 353 ['A ' + nu_COPY_path + '\n', 354 ' G ' + F_COPY_path + '\n']), 355 [], 'merge', '-c8', 356 sbox.repo_url + '/A2/B/F', 357 F_COPY_path) 358 359 # Commit everything this far as r9 360 svntest.actions.run_and_verify_svn(None, [], 361 'ci', wc_dir, '-m', 'Many merges') 362 svntest.actions.run_and_verify_svn(exp_noop_up_out(9), [], 'up', 363 wc_dir) 364 365 # Test svn mergeinfo -R / --depth infinity. 366 367 # Asking for eligible revisions from A2 to A_COPY should show: 368 # 369 # r3 - Was never merged. 370 # 371 # r4 - Was merged at depth empty, so while there is mergeinfo for the 372 # revision, the actual text change to A_COPY/D/G/rho hasn't yet 373 # happened. 374 # 375 # r8* - Was only partially merged to the subtree at A_COPY/B/F. The 376 # addition of A_COPY/C/nu2 is still outstanding. 377 svntest.actions.run_and_verify_mergeinfo(adjust_error_for_server_version(""), 378 ['3', '4*', '8*'], 379 sbox.repo_url + '/A2', 380 sbox.repo_url + '/A_COPY', 381 '--show-revs', 'eligible', '-R') 382 # Do the same as above, but test that we can request the revisions 383 # in reverse order. 384 svntest.actions.run_and_verify_mergeinfo(adjust_error_for_server_version(""), 385 ['8*', '4*', '3'], 386 sbox.repo_url + '/A2', 387 sbox.repo_url + '/A_COPY', 388 '--show-revs', 'eligible', '-R', 389 '-r', '9:0') 390 391 # Asking for merged revisions from A2 to A_COPY should show: 392 # 393 # r4* - Was merged at depth empty, so while there is mergeinfo for the 394 # revision, the actual text change to A_COPY/D/G/rho hasn't yet 395 # happened. 396 # 397 # r5 - Was merged at depth infinity to the root of the 'branch', so it 398 # should show as fully merged. 399 # 400 # r6 - This was a subtree merge, but since the subtree A_COPY/D/H was 401 # the ancestor of the only change made in r6 it is considered 402 # fully merged. 403 # 404 # r8* - Was only partially merged to the subtree at A_COPY/B/F. The 405 # addition of A_COPY/C/nu2 is still outstanding. 406 svntest.actions.run_and_verify_mergeinfo(adjust_error_for_server_version(""), 407 ['4*', '5', '6', '8*'], 408 A2_path, 409 A_COPY_path, 410 '--show-revs', 'merged', 411 '--depth', 'infinity') 412 # Do the same as above, but test that we can request the revisions 413 # in reverse order. 414 svntest.actions.run_and_verify_mergeinfo(adjust_error_for_server_version(""), 415 ['8*', '6', '5', '4*'], 416 A2_path, 417 A_COPY_path, 418 '--show-revs', 'merged', 419 '--depth', 'infinity', 420 '-r', '9:0') 421 422 # A couple tests of problems found with initial issue #3242 fixes. 423 # We should be able to check for the merged revs from a URL to a URL 424 # when the latter has explicit mergeinfo... 425 svntest.actions.run_and_verify_mergeinfo( 426 adjust_error_for_server_version(''), ['6'], 427 sbox.repo_url + '/A2/D/H', 428 sbox.repo_url + '/A_COPY/D/H', 429 '--show-revs', 'merged') 430 # ...and when the latter has inherited mergeinfo. 431 svntest.actions.run_and_verify_mergeinfo( 432 adjust_error_for_server_version(''), ['6'], 433 sbox.repo_url + '/A2/D/H/omega', 434 sbox.repo_url + '/A_COPY/D/H/omega', 435 '--show-revs', 'merged') 436 437# Test for issue #3180 'svn mergeinfo ignores peg rev for WC target'. 438@SkipUnless(server_has_mergeinfo) 439def mergeinfo_on_pegged_wc_path(sbox): 440 "svn mergeinfo on pegged working copy target" 441 442 sbox.build() 443 wc_dir = sbox.wc_dir 444 expected_disk, expected_status = set_up_branch(sbox) 445 446 # Some paths we'll care about 447 A_path = os.path.join(wc_dir, "A") 448 A_COPY_path = os.path.join(wc_dir, "A_COPY") 449 psi_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "H", "psi") 450 omega_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "H", "omega") 451 beta_COPY_path = os.path.join(wc_dir, "A_COPY", "B", "E", "beta") 452 453 # Do a couple merges 454 # 455 # r7 - Merge -c3,6 from A to A_COPY. 456 svntest.actions.run_and_verify_svn( 457 expected_merge_output([[3],[6]], 458 ['U ' + psi_COPY_path + '\n', 459 'U ' + omega_COPY_path + '\n', 460 ' U ' + A_COPY_path + '\n', 461 ' G ' + A_COPY_path + '\n',]), 462 [], 'merge', '-c3,6', sbox.repo_url + '/A', A_COPY_path) 463 svntest.actions.run_and_verify_svn(None, [], 464 'ci', wc_dir, 465 '-m', 'Merge r3 and r6') 466 467 # r8 - Merge -c5 from A to A_COPY. 468 svntest.actions.run_and_verify_svn( 469 expected_merge_output([[5]], 470 ['U ' + beta_COPY_path + '\n', 471 ' U ' + A_COPY_path + '\n']), 472 [], 'merge', '-c5', '--allow-mixed-revisions', 473 sbox.repo_url + '/A', A_COPY_path) 474 svntest.actions.run_and_verify_svn(None, [], 475 'ci', wc_dir, 476 '-m', 'Merge r5') 477 478 # Ask for merged and eligible revisions to A_COPY pegged at various values. 479 # Prior to issue #3180 fix the peg revision was ignored. 480 # 481 # A_COPY pegged to non-existent revision 482 svntest.actions.run_and_verify_mergeinfo( 483 adjust_error_for_server_version('.*No such revision 99'), 484 [], A_path, A_COPY_path + '@99', '--show-revs', 'merged') 485 486 # A_COPY@BASE 487 svntest.actions.run_and_verify_mergeinfo( 488 adjust_error_for_server_version(''), 489 ['3','5','6'], A_path, A_COPY_path + '@BASE', '--show-revs', 'merged') 490 491 # A_COPY@HEAD 492 svntest.actions.run_and_verify_mergeinfo( 493 adjust_error_for_server_version(''), 494 ['3','5','6'], A_path, A_COPY_path + '@HEAD', '--show-revs', 'merged') 495 496 # A_COPY@4 (Prior to any merges) 497 svntest.actions.run_and_verify_mergeinfo( 498 adjust_error_for_server_version(''), 499 [], A_path, A_COPY_path + '@4', '--show-revs', 'merged') 500 501 # A_COPY@COMMITTED (r8) 502 svntest.actions.run_and_verify_mergeinfo( 503 adjust_error_for_server_version(''), 504 ['3','5','6'], A_path, A_COPY_path + '@COMMITTED', '--show-revs', 505 'merged') 506 507 # A_COPY@PREV (r7) 508 svntest.actions.run_and_verify_mergeinfo( 509 adjust_error_for_server_version(''), 510 ['3', '6'], A_path, A_COPY_path + '@PREV', '--show-revs', 'merged') 511 512 # A_COPY@BASE 513 svntest.actions.run_and_verify_mergeinfo( 514 adjust_error_for_server_version(''), 515 ['4'], A_path, A_COPY_path + '@BASE', '--show-revs', 'eligible') 516 517 # A_COPY@HEAD 518 svntest.actions.run_and_verify_mergeinfo( 519 adjust_error_for_server_version(''), 520 ['4'], A_path, A_COPY_path + '@HEAD', '--show-revs', 'eligible') 521 522 # A_COPY@4 (Prior to any merges) 523 svntest.actions.run_and_verify_mergeinfo( 524 adjust_error_for_server_version(''), 525 ['3', '4', '5', '6'], A_path, A_COPY_path + '@4', '--show-revs', 'eligible') 526 527 # A_COPY@COMMITTED (r8) 528 svntest.actions.run_and_verify_mergeinfo( 529 adjust_error_for_server_version(''), 530 ['4'], A_path, A_COPY_path + '@COMMITTED', '--show-revs', 531 'eligible') 532 533 # A_COPY@PREV (r7) 534 svntest.actions.run_and_verify_mergeinfo( 535 adjust_error_for_server_version(''), 536 ['4', '5'], A_path, A_COPY_path + '@PREV', '--show-revs', 'eligible') 537 538#---------------------------------------------------------------------- 539# A test for issue 3986 'svn_client_mergeinfo_log API is broken'. 540@Issue(3986) 541@SkipUnless(server_has_mergeinfo) 542def wc_target_inherits_mergeinfo_from_repos(sbox): 543 "wc target inherits mergeinfo from repos" 544 545 sbox.build() 546 wc_dir = sbox.wc_dir 547 wc_disk, wc_status = set_up_branch(sbox, nbr_of_branches=2) 548 549 A_COPY_path = os.path.join(wc_dir, 'A_COPY') 550 rho_COPY_path = os.path.join(wc_dir, 'A_COPY', 'D', 'G', 'rho') 551 gamma_2_path = os.path.join(wc_dir, 'A_COPY_2', 'D', 'gamma') 552 tau_path = os.path.join(wc_dir, 'A', 'D', 'G', 'tau') 553 D_COPY_path = os.path.join(wc_dir, 'A_COPY', 'D') 554 555 # Merge -c5 ^/A/D/G/rho A_COPY\D\G\rho 556 # Merge -c7 ^/A A_COPY 557 # Commit as r8 558 # 559 # This gives us some explicit mergeinfo on the "branch" root and 560 # one of its subtrees: 561 # 562 # Properties on 'A_COPY\D\G\rho': 563 # svn:mergeinfo 564 # /A/D/G/rho:5 565 # Properties on 'A_COPY': 566 # svn:mergeinfo 567 # /A:7 568 svntest.actions.run_and_verify_svn(None, [], 'merge', 569 sbox.repo_url + '/A/D/G/rho', 570 rho_COPY_path, '-c5') 571 svntest.actions.run_and_verify_svn(None, [], 'merge', 572 sbox.repo_url + '/A', 573 A_COPY_path, '-c7') 574 svntest.actions.run_and_verify_svn(None, [], 'ci', '-m', 575 'Cherrypicks to branch subtree and root', 576 wc_dir) 577 578 # Checkout a new wc rooted at ^/A_COPY/D. 579 subtree_wc = sbox.add_wc_path('D_COPY') 580 svntest.actions.run_and_verify_svn(None, [], 'co', 581 sbox.repo_url + '/A_COPY/D', 582 subtree_wc) 583 584 # Check the merged and eligible revisions both recursively and 585 # non-recursively. 586 587 # Eligible : Non-recursive 588 svntest.actions.run_and_verify_mergeinfo( 589 adjust_error_for_server_version(''), 590 ['4','5'], sbox.repo_url + '/A/D', subtree_wc, 591 '--show-revs', 'eligible') 592 593 # Eligible : Recursive 594 svntest.actions.run_and_verify_mergeinfo( 595 adjust_error_for_server_version(''), 596 ['4'], sbox.repo_url + '/A/D', subtree_wc, 597 '--show-revs', 'eligible', '-R') 598 599 # Merged : Non-recursive 600 svntest.actions.run_and_verify_mergeinfo( 601 adjust_error_for_server_version(''), 602 ['7'], sbox.repo_url + '/A/D', subtree_wc, 603 '--show-revs', 'merged') 604 605 # Merged : Recursive 606 svntest.actions.run_and_verify_mergeinfo( 607 adjust_error_for_server_version(''), 608 ['5','7'], sbox.repo_url + '/A/D', subtree_wc, 609 '--show-revs', 'merged', '-R') 610 611 # Test that intersecting revisions in the 'svn mergeinfo' target 612 # from one source don't show up as merged when asking about a different 613 # source. 614 # 615 # In r9 make a change that effects two branches: 616 svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir) 617 svntest.main.file_write(gamma_2_path, "New content.\n") 618 svntest.main.file_write(tau_path, "New content.\n") 619 svntest.actions.run_and_verify_svn(None, [], 'ci', '-m', 620 'Make changes under both A and A_COPY_2', 621 wc_dir) 622 623 # In r10 merge r9 from A_COPY_2 to A_COPY. 624 # 625 # This gives us this mergeinfo: 626 # 627 # Properties on 'A_COPY': 628 # svn:mergeinfo 629 # /A:7 630 # /A_COPY_2:9 631 # Properties on 'A_COPY\D\G\rho': 632 # svn:mergeinfo 633 # /A/D/G/rho:5 634 svntest.actions.run_and_verify_svn(None, [], 'merge', 635 sbox.repo_url + '/A_COPY_2', 636 A_COPY_path, '-c9') 637 svntest.actions.run_and_verify_svn(None, [], 'ci', '-m', 638 'Merge r8 from A_COPY_2 to A_COPY', 639 wc_dir) 640 641 def test_svn_mergeinfo_4_way(wc_target): 642 # Eligible : Non-recursive 643 svntest.actions.run_and_verify_mergeinfo( 644 adjust_error_for_server_version(''), 645 ['4','5','9'], sbox.repo_url + '/A/D', wc_target, 646 '--show-revs', 'eligible') 647 648 # Eligible : Recursive 649 svntest.actions.run_and_verify_mergeinfo( 650 adjust_error_for_server_version(''), 651 ['4','9'], sbox.repo_url + '/A/D', wc_target, 652 '--show-revs', 'eligible', '-R') 653 654 # Merged : Non-recursive 655 svntest.actions.run_and_verify_mergeinfo( 656 adjust_error_for_server_version(''), 657 ['7'], sbox.repo_url + '/A/D', wc_target, 658 '--show-revs', 'merged') 659 660 # Merged : Recursive 661 svntest.actions.run_and_verify_mergeinfo( 662 adjust_error_for_server_version(''), 663 ['5','7'], sbox.repo_url + '/A/D', wc_target, 664 '--show-revs', 'merged', '-R') 665 666 # Test while the target is the full WC and then with the subtree WC: 667 svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir) 668 svntest.actions.run_and_verify_svn(None, [], 'up', subtree_wc) 669 670 test_svn_mergeinfo_4_way(D_COPY_path) 671 test_svn_mergeinfo_4_way(subtree_wc) 672 673#---------------------------------------------------------------------- 674# A test for issue 3791 'svn mergeinfo shows natural history of added 675# subtrees as eligible'. 676@Issue(3791) 677@SkipUnless(server_has_mergeinfo) 678def natural_history_is_not_eligible_nor_merged(sbox): 679 "natural history is not eligible nor merged" 680 681 sbox.build() 682 wc_dir = sbox.wc_dir 683 wc_disk, wc_status = set_up_branch(sbox) 684 685 nu_path = os.path.join(wc_dir, 'A', 'C', 'nu') 686 A_COPY_path = os.path.join(wc_dir, 'A_COPY') 687 nu_COPY_path = os.path.join(wc_dir, 'A_COPY', 'C', 'nu') 688 689 # r7 - Add a new file A/C/nu 690 svntest.main.file_write(nu_path, "This is the file 'nu'.\n") 691 svntest.actions.run_and_verify_svn(None, [], 'add', nu_path) 692 svntest.actions.run_and_verify_svn(None, [], 'ci', 693 '-m', 'Add a file', wc_dir) 694 695 # r8 - Sync merge ^/A to A_COPY 696 svntest.actions.run_and_verify_svn(None, [], 'merge', 697 sbox.repo_url + '/A', A_COPY_path) 698 svntest.actions.run_and_verify_svn(None, [], 'ci', 699 '-m', 'Add a file', wc_dir) 700 701 # r9 - Modify the file added in r7 702 svntest.main.file_write(nu_path, "Modification to file 'nu'.\n") 703 svntest.actions.run_and_verify_svn(None, [], 'ci', 704 '-m', 'Modify added file', wc_dir) 705 706 # r10 - Merge ^/A/C/nu to A_COPY/C/nu, creating subtree mergeinfo. 707 svntest.actions.run_and_verify_svn(None, [], 'merge', 708 sbox.repo_url + '/A/C/nu', nu_COPY_path) 709 svntest.actions.run_and_verify_svn(None, [], 'ci', 710 '-m', 'Add a file', wc_dir) 711 svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir) 712 713 # We've effectively merged everything from ^/A to A_COPY, check 714 # that svn mergeinfo -R agrees. 715 # 716 # First check if there are eligible revisions, there should be none. 717 svntest.actions.run_and_verify_mergeinfo( 718 adjust_error_for_server_version(''), 719 [], sbox.repo_url + '/A', 720 A_COPY_path, '--show-revs', 'eligible', '-R') 721 722 # Now check that all operative revisions show as merged. 723 svntest.actions.run_and_verify_mergeinfo( 724 adjust_error_for_server_version(''), 725 ['3','4','5','6','7','9'], sbox.repo_url + '/A', 726 A_COPY_path, '--show-revs', 'merged', '-R') 727 728#---------------------------------------------------------------------- 729# A test for issue 4050 "'svn mergeinfo' always considers non-inheritable 730# ranges as partially merged". 731@Issue(4050) 732@SkipUnless(server_has_mergeinfo) 733def noninheritable_mergeinfo_not_always_eligible(sbox): 734 "noninheritable mergeinfo not always eligible" 735 736 sbox.build() 737 wc_dir = sbox.wc_dir 738 739 A_path = os.path.join(wc_dir, 'A') 740 branch_path = os.path.join(wc_dir, 'branch') 741 742 # r2 - Branch ^/A to ^/branch. 743 svntest.main.run_svn(None, 'copy', sbox.repo_url + '/A', 744 sbox.repo_url + '/branch', '-m', 'make a branch') 745 746 # r3 - Make prop edit to A. 747 svntest.main.run_svn(None, 'ps', 'prop', 'val', A_path) 748 svntest.main.run_svn(None, 'commit', '-m', 'file edit', wc_dir) 749 svntest.main.run_svn(None, 'up', wc_dir) 750 751 # r4 - Merge r3 from ^/A to branch at depth=empty. 752 svntest.actions.run_and_verify_svn(None, [], 'merge', 753 sbox.repo_url + '/A', branch_path, 754 '-c3', '--depth=empty') 755 # Forcibly set non-inheritable mergeinfo to replicate the pre-1.8 behavior, 756 # where prior to the fix for issue #4057, non-inheritable mergeinfo was 757 # unconditionally set for merges with shallow operational depths. 758 svntest.actions.run_and_verify_svn(None, [], 759 'propset', SVN_PROP_MERGEINFO, 760 '/A:3*\n', branch_path) 761 svntest.main.run_svn(None, 'commit', '-m', 'shallow merge', wc_dir) 762 763 # Now check that r3 is reported as fully merged from ^/A to ^/branch 764 # and does not show up all when asking for eligible revs. 765 svntest.actions.run_and_verify_mergeinfo( 766 adjust_error_for_server_version(''), 767 ['3'], sbox.repo_url + '/A', sbox.repo_url + '/branch', 768 '--show-revs', 'merged', '-R') 769 # Likewise r3 shows up as partially eligible when asking about 770 # for --show-revs=eligible. 771 svntest.actions.run_and_verify_mergeinfo( 772 adjust_error_for_server_version(''), 773 [], sbox.repo_url + '/A', sbox.repo_url + '/branch', 774 '--show-revs', 'eligible', '-R') 775 776@SkipUnless(server_has_mergeinfo) 777@Issue(4301) 778def mergeinfo_local_move(sbox): 779 "'mergeinfo' on a locally moved path" 780 781 sbox.build() 782 wc_dir = sbox.wc_dir 783 784 sbox.simple_move('A', 'A2') 785 svntest.actions.run_and_verify_svn(None, [], 786 'mergeinfo', sbox.repo_url + '/A', 787 sbox.ospath('A2')) 788 789@SkipUnless(server_has_mergeinfo) 790@Issue(4582) 791def no_mergeinfo_on_tree_conflict_victim(sbox): 792 "do not record mergeinfo on tree conflict victims" 793 sbox.build() 794 795 # Create a branch of A called A_copy 796 sbox.simple_copy('A', 'A_copy') 797 sbox.simple_commit() 798 799 # Add a new directory and file on both branches 800 sbox.simple_mkdir('A/dir') 801 sbox.simple_add_text('new file', 'A/dir/f') 802 sbox.simple_commit() 803 804 sbox.simple_mkdir('A_copy/dir') 805 sbox.simple_add_text('new file', 'A_copy/dir/f') 806 sbox.simple_commit() 807 808 # Run a merge from A to A_copy 809 expected_output = wc.State(sbox.ospath('A_copy'), { 810 'dir' : Item(status=' ', treeconflict='C'), 811 'dir/f' : Item(status=' ', treeconflict='A'), 812 }) 813 expected_mergeinfo_output = wc.State(sbox.ospath('A_copy'), { 814 '' : Item(status=' U'), 815 }) 816 expected_elision_output = wc.State(sbox.ospath('A_copy'), { 817 }) 818 819 expected_disk = svntest.wc.State('', { 820 'C' : Item(), 821 'B/E/beta' : Item(contents="This is the file 'beta'.\n"), 822 'B/E/alpha' : Item(contents="This is the file 'alpha'.\n"), 823 'B/lambda' : Item(contents="This is the file 'lambda'.\n"), 824 'B/F' : Item(), 825 'D/H/omega' : Item(contents="This is the file 'omega'.\n"), 826 'D/H/psi' : Item(contents="This is the file 'psi'.\n"), 827 'D/H/chi' : Item(contents="This is the file 'chi'.\n"), 828 'D/G/tau' : Item(contents="This is the file 'tau'.\n"), 829 'D/G/pi' : Item(contents="This is the file 'pi'.\n"), 830 'D/G/rho' : Item(contents="This is the file 'rho'.\n"), 831 'D/gamma' : Item(contents="This is the file 'gamma'.\n"), 832 'dir/f' : Item(contents="new file"), 833 'mu' : Item(contents="This is the file 'mu'.\n"), 834 }) 835 836 # The merge will create an add vs add tree conflict on A_copy/dir 837 expected_status = svntest.wc.State(sbox.ospath('A_copy'), { 838 '' : Item(status=' M', wc_rev='4'), 839 'D' : Item(status=' ', wc_rev='4'), 840 'D/G' : Item(status=' ', wc_rev='4'), 841 'D/G/pi' : Item(status=' ', wc_rev='4'), 842 'D/G/rho' : Item(status=' ', wc_rev='4'), 843 'D/G/tau' : Item(status=' ', wc_rev='4'), 844 'D/H' : Item(status=' ', wc_rev='4'), 845 'D/H/psi' : Item(status=' ', wc_rev='4'), 846 'D/H/omega' : Item(status=' ', wc_rev='4'), 847 'D/H/chi' : Item(status=' ', wc_rev='4'), 848 'D/gamma' : Item(status=' ', wc_rev='4'), 849 'B' : Item(status=' ', wc_rev='4'), 850 'B/F' : Item(status=' ', wc_rev='4'), 851 'B/E' : Item(status=' ', wc_rev='4'), 852 'B/E/alpha' : Item(status=' ', wc_rev='4'), 853 'B/E/beta' : Item(status=' ', wc_rev='4'), 854 'B/lambda' : Item(status=' ', wc_rev='4'), 855 'C' : Item(status=' ', wc_rev='4'), 856 'dir' : Item(status=' ', treeconflict='C', wc_rev='4'), 857 'dir/f' : Item(status=' ', wc_rev='4'), 858 'mu' : Item(status=' ', wc_rev='4'), 859 }) 860 861 expected_skip = wc.State('', { }) 862 863 sbox.simple_update('A_copy') 864 svntest.actions.run_and_verify_merge(sbox.ospath('A_copy'), 865 None, None, # rev1, rev2 866 '^/A', 867 None, # URL2 868 expected_output, 869 expected_mergeinfo_output, 870 expected_elision_output, 871 expected_disk, 872 expected_status, 873 expected_skip) 874 875 # Resolve the tree conflict by accepting the working copy state left 876 # behind by the merge. This preserves the line of history of A_copy/dir, 877 # which originated on the branch 'A_copy', rather than replacing it with 878 # Jthe line f history of A/dir which originated on branch 'A' 879 svntest.actions.run_and_verify_resolve([sbox.ospath('A_copy/dir')], 880 '--accept', 'working', 881 sbox.ospath('A_copy/dir')) 882 sbox.simple_commit('A_copy') 883 884 # Now try to merge the 'A_copy' branch back to 'A" 885 expected_output = wc.State(sbox.ospath('A'), { 886 'dir' : Item(status='R '), # changes line of history of A/dir 887 'dir/f' : Item(status='A '), 888 }) 889 expected_mergeinfo_output = wc.State(sbox.ospath('A'), { 890 '' : Item(status=' U'), 891 }) 892 expected_elision_output = wc.State(sbox.ospath('A'), { 893 }) 894 895 expected_disk = svntest.wc.State('', { 896 'C' : Item(), 897 'B/E/beta' : Item(contents="This is the file 'beta'.\n"), 898 'B/E/alpha' : Item(contents="This is the file 'alpha'.\n"), 899 'B/F' : Item(), 900 'B/lambda' : Item(contents="This is the file 'lambda'.\n"), 901 'D/H/omega' : Item(contents="This is the file 'omega'.\n"), 902 'D/H/psi' : Item(contents="This is the file 'psi'.\n"), 903 'D/H/chi' : Item(contents="This is the file 'chi'.\n"), 904 'D/G/tau' : Item(contents="This is the file 'tau'.\n"), 905 'D/G/pi' : Item(contents="This is the file 'pi'.\n"), 906 'D/G/rho' : Item(contents="This is the file 'rho'.\n"), 907 'D/gamma' : Item(contents="This is the file 'gamma'.\n"), 908 'dir/f' : Item(contents="new file"), 909 'mu' : Item(contents="This is the file 'mu'.\n"), 910 }) 911 912 expected_status = svntest.wc.State(sbox.ospath('A'), { 913 '' : Item(status=' M', wc_rev='5'), 914 'dir' : Item(status='R ', copied='+', wc_rev='-'), 915 'dir/f' : Item(status=' ', copied='+', wc_rev='-'), 916 'D' : Item(status=' ', wc_rev='5'), 917 'D/H' : Item(status=' ', wc_rev='5'), 918 'D/H/chi' : Item(status=' ', wc_rev='5'), 919 'D/H/omega' : Item(status=' ', wc_rev='5'), 920 'D/H/psi' : Item(status=' ', wc_rev='5'), 921 'D/G' : Item(status=' ', wc_rev='5'), 922 'D/G/pi' : Item(status=' ', wc_rev='5'), 923 'D/G/rho' : Item(status=' ', wc_rev='5'), 924 'D/G/tau' : Item(status=' ', wc_rev='5'), 925 'D/gamma' : Item(status=' ', wc_rev='5'), 926 'B' : Item(status=' ', wc_rev='5'), 927 'B/E' : Item(status=' ', wc_rev='5'), 928 'B/E/beta' : Item(status=' ', wc_rev='5'), 929 'B/E/alpha' : Item(status=' ', wc_rev='5'), 930 'B/lambda' : Item(status=' ', wc_rev='5'), 931 'B/F' : Item(status=' ', wc_rev='5'), 932 'mu' : Item(status=' ', wc_rev='5'), 933 'C' : Item(status=' ', wc_rev='5'), 934 }) 935 936 expected_skip = wc.State('', { }) 937 sbox.simple_update('A') 938 svntest.actions.run_and_verify_merge(sbox.ospath('A'), 939 None, None, # rev1, rev2 940 '^/A_copy', 941 None, # URL2 942 expected_output, 943 expected_mergeinfo_output, 944 expected_elision_output, 945 expected_disk, 946 expected_status, 947 expected_skip) 948 sbox.simple_commit('A') 949 950######################################################################## 951# Run the tests 952 953# Note that mergeinfo --log is tested in log_tests.py 954 955# list all tests here, starting with None: 956test_list = [ None, 957 no_mergeinfo, 958 mergeinfo, 959 explicit_mergeinfo_source, 960 mergeinfo_non_source, 961 mergeinfo_on_unknown_url, 962 non_inheritable_mergeinfo, 963 recursive_mergeinfo, 964 mergeinfo_on_pegged_wc_path, 965 wc_target_inherits_mergeinfo_from_repos, 966 natural_history_is_not_eligible_nor_merged, 967 noninheritable_mergeinfo_not_always_eligible, 968 mergeinfo_local_move, 969 no_mergeinfo_on_tree_conflict_victim, 970 ] 971 972if __name__ == '__main__': 973 svntest.main.run_tests(test_list) 974 # NOTREACHED 975