Mark many merge tests as skip-against-old-server.
[svn.git] / subversion / tests / cmdline / merge_tests.py
blobde115a6e6766b2a8164a50cfbea4c8c5563933f1
1 #!/usr/bin/env python
3 # merge_tests.py: testing merge
5 # Subversion is a tool for revision control.
6 # See http://subversion.tigris.org for more information.
8 # ====================================================================
9 # Copyright (c) 2000-2007 CollabNet. All rights reserved.
11 # This software is licensed as described in the file COPYING, which
12 # you should have received as part of this distribution. The terms
13 # are also available at http://subversion.tigris.org/license-1.html.
14 # If newer versions of this license are posted there, you may use a
15 # newer version instead, at your option.
17 ######################################################################
19 # General modules
20 import shutil, sys, re, os
21 import time
23 # Our testing module
24 import svntest
25 from svntest import wc
27 # (abbreviation)
28 Item = wc.StateItem
29 XFail = svntest.testcase.XFail
30 Skip = svntest.testcase.Skip
31 SkipUnless = svntest.testcase.SkipUnless
33 from svntest.main import SVN_PROP_MERGEINFO
34 from svntest.main import server_has_mergeinfo
35 from svntest.actions import fill_file_with_lines
36 from svntest.actions import make_conflict_marker_text
37 from svntest.actions import inject_conflict_into_expected_state
39 def shorten_path_kludge(path):
40 '''Search for the comment entitled "The Merge Kluge" elsewhere in
41 this file, to understand why we shorten, and subsequently chdir()
42 after calling this function.'''
43 shorten_by = len(svntest.main.work_dir) + len(os.sep)
44 return path[shorten_by:]
46 def expected_merge_output(rev_ranges, additional_lines=None):
47 """Generate an (inefficient) regex representing the expected merge
48 output from REV_RANGES (a list of 'range' lists of the form [start, end] or
49 [single_rev] --> [single_rev - 1, single_rev]), and ADDITIONAL_LINES (a list
50 of strings). If REV_RANGES is None then only the standard notification for
51 a 3-way merge is expected."""
52 if rev_ranges is None:
53 lines = [svntest.main.merge_notify_line(None, None, False)]
54 else:
55 lines = []
56 for rng in rev_ranges:
57 start_rev = rng[0]
58 if len(rng) > 1:
59 end_rev = rng[1]
60 else:
61 end_rev = None
62 lines += [svntest.main.merge_notify_line(start_rev, end_rev, True)]
63 if isinstance(additional_lines, list):
64 # Address "The Backslash Plague"
66 # If ADDITIONAL_LINES are present there are possibly paths in it with
67 # multiple components and on Windows these components are separated with
68 # '\'. These need to be escaped properly in the regexp for the match to
69 # work correctly. See http://aspn.activestate.com/ASPN/docs/ActivePython
70 # /2.2/howto/regex/regex.html#SECTION000420000000000000000.
71 if sys.platform == 'win32':
72 for i in range(0, len(additional_lines)):
73 additional_lines[i] = additional_lines[i].replace("\\", "\\\\")
74 lines.extend(additional_lines)
75 else:
76 if sys.platform == 'win32' and additional_lines != None:
77 additional_lines = additional_lines.replace("\\", "\\\\")
78 lines.append(str(additional_lines))
79 return "|".join(lines)
81 ######################################################################
82 # Tests
84 # Each test must return on success or raise on failure.
87 #----------------------------------------------------------------------
89 def textual_merges_galore(sbox):
90 "performing a merge, with mixed results"
92 ## The Plan:
94 ## The goal is to test that "svn merge" does the right thing in the
95 ## following cases:
97 ## 1 : _ : Received changes already present in unmodified local file
98 ## 2 : U : No local mods, received changes folded in without trouble
99 ## 3 : G : Received changes already exist as local mods
100 ## 4 : G : Received changes do not conflict with local mods
101 ## 5 : C : Received changes conflict with local mods
103 ## So first modify these files and commit:
105 ## Revision 2:
106 ## -----------
107 ## A/mu ............... add ten or so lines
108 ## A/D/G/rho .......... add ten or so lines
110 ## Now check out an "other" working copy, from revision 2.
112 ## Next further modify and commit some files from the original
113 ## working copy:
115 ## Revision 3:
116 ## -----------
117 ## A/B/lambda ......... add ten or so lines
118 ## A/D/G/pi ........... add ten or so lines
119 ## A/D/G/tau .......... add ten or so lines
120 ## A/D/G/rho .......... add an additional ten or so lines
122 ## In the other working copy (which is at rev 2), update rho back
123 ## to revision 1, while giving other files local mods. This sets
124 ## things up so that "svn merge -r 1:3" will test all of the above
125 ## cases except case 4:
127 ## case 1: A/mu .......... do nothing, the only change was in rev 2
128 ## case 2: A/B/lambda .... do nothing, so we accept the merge easily
129 ## case 3: A/D/G/pi ...... add same ten lines as committed in rev 3
130 ## case 5: A/D/G/tau ..... add ten or so lines at the end
131 ## [none]: A/D/G/rho ..... ignore what happens to this file for now
133 ## Now run
135 ## $ cd wc.other
136 ## $ svn merge -r 1:3 url-to-repo
138 ## ...and expect the right output.
140 ## Now revert rho, then update it to revision 2, then *prepend* a
141 ## bunch of lines, which will be separated by enough distance from
142 ## the changes about to be received that the merge will be clean.
144 ## $ cd wc.other/A/D/G
145 ## $ svn merge -r 2:3 url-to-repo/A/D/G
147 ## Which tests case 4. (Ignore the changes to the other files,
148 ## we're only interested in rho here.)
150 sbox.build()
151 wc_dir = sbox.wc_dir
152 # url = os.path.join(svntest.main.test_area_url, sbox.repo_dir)
154 # Change mu and rho for revision 2
155 mu_path = os.path.join(wc_dir, 'A', 'mu')
156 rho_path = os.path.join(wc_dir, 'A', 'D', 'G', 'rho')
157 mu_text = fill_file_with_lines(mu_path, 2)
158 rho_text = fill_file_with_lines(rho_path, 2)
160 # Create expected output tree for initial commit
161 expected_output = wc.State(wc_dir, {
162 'A/mu' : Item(verb='Sending'),
163 'A/D/G/rho' : Item(verb='Sending'),
166 # Create expected status tree; all local revisions should be at 1,
167 # but mu and rho should be at revision 2.
168 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
169 expected_status.tweak('A/mu', 'A/D/G/rho', wc_rev=2)
171 # Initial commit.
172 svntest.actions.run_and_verify_commit(wc_dir,
173 expected_output,
174 expected_status,
175 None,
176 wc_dir)
178 # Make the "other" working copy
179 other_wc = sbox.add_wc_path('other')
180 svntest.actions.duplicate_dir(wc_dir, other_wc)
182 # Now commit some more mods from the original working copy, to
183 # produce revision 3.
184 lambda_path = os.path.join(wc_dir, 'A', 'B', 'lambda')
185 pi_path = os.path.join(wc_dir, 'A', 'D', 'G', 'pi')
186 tau_path = os.path.join(wc_dir, 'A', 'D', 'G', 'tau')
188 lambda_text = fill_file_with_lines(lambda_path, 2)
189 pi_text = fill_file_with_lines(pi_path, 2)
190 tau_text = fill_file_with_lines(tau_path, 2)
191 additional_rho_text = fill_file_with_lines(rho_path, 2)
193 # Created expected output tree for 'svn ci'
194 expected_output = wc.State(wc_dir, {
195 'A/B/lambda' : Item(verb='Sending'),
196 'A/D/G/pi' : Item(verb='Sending'),
197 'A/D/G/tau' : Item(verb='Sending'),
198 'A/D/G/rho' : Item(verb='Sending'),
201 # Create expected status tree.
202 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
203 expected_status.tweak('A/mu', wc_rev=2)
204 expected_status.tweak('A/B/lambda', 'A/D/G/pi', 'A/D/G/tau', 'A/D/G/rho',
205 wc_rev=3)
207 # Commit revision 3.
208 svntest.actions.run_and_verify_commit(wc_dir,
209 expected_output,
210 expected_status,
211 None,
212 wc_dir)
214 # Make local mods in wc.other
215 other_pi_path = os.path.join(other_wc, 'A', 'D', 'G', 'pi')
216 other_rho_path = os.path.join(other_wc, 'A', 'D', 'G', 'rho')
217 other_tau_path = os.path.join(other_wc, 'A', 'D', 'G', 'tau')
219 # For A/mu and A/B/lambda, we do nothing. For A/D/G/pi, we add the
220 # same ten lines as were already committed in revision 3.
221 # (Remember, wc.other is only at revision 2, so it doesn't have
222 # these changes.)
223 svntest.main.file_append(other_pi_path, pi_text)
225 # We skip A/D/G/rho in this merge; it will be tested with a separate
226 # merge command. Temporarily put it back to revision 1, so this
227 # merge succeeds cleanly.
228 svntest.actions.run_and_verify_svn(None, None, [],
229 'up', '-r', '1', other_rho_path)
231 # For A/D/G/tau, we append ten different lines, to conflict with the
232 # ten lines appended in revision 3.
233 other_tau_text = fill_file_with_lines(other_tau_path, 2,
234 line_descrip="Conflicting line")
236 # Do the first merge, revs 1:3. This tests all the cases except
237 # case 4, which we'll handle in a second pass.
238 expected_output = wc.State(other_wc, {'A/B/lambda' : Item(status='U '),
239 'A/D/G/rho' : Item(status='U '),
240 'A/D/G/tau' : Item(status='C '),
243 expected_disk = svntest.main.greek_state.copy()
244 expected_disk.tweak('A/mu',
245 contents=expected_disk.desc['A/mu'].contents
246 + mu_text)
247 expected_disk.tweak('A/B/lambda',
248 contents=expected_disk.desc['A/B/lambda'].contents
249 + lambda_text)
250 expected_disk.tweak('A/D/G/rho',
251 contents=expected_disk.desc['A/D/G/rho'].contents
252 + rho_text + additional_rho_text)
253 expected_disk.tweak('A/D/G/pi',
254 contents=expected_disk.desc['A/D/G/pi'].contents
255 + pi_text)
257 expected_status = svntest.actions.get_virginal_state(other_wc, 1)
258 expected_status.tweak('', status=' M')
259 expected_status.tweak('A/mu', wc_rev=2)
260 expected_status.tweak('A/B/lambda', status='M ')
261 expected_status.tweak('A/D/G/pi', status='M ')
262 expected_status.tweak('A/D/G/rho', status='M ')
264 inject_conflict_into_expected_state('A/D/G/tau', expected_disk,
265 expected_status, other_tau_text, tau_text,
268 expected_skip = wc.State('', { })
270 tau_conflict_support_files = ["tau\.working",
271 "tau\.merge-right\.r3",
272 "tau\.merge-left\.r1"]
274 svntest.actions.run_and_verify_merge(other_wc, '1', '3',
275 sbox.repo_url,
276 expected_output,
277 expected_disk,
278 expected_status,
279 expected_skip,
280 None,
281 svntest.tree.detect_conflict_files,
282 list(tau_conflict_support_files))
284 # Now reverse merge r3 into A/D/G/rho, give it non-conflicting local
285 # mods, then merge in the 2:3 change. ### Not bothering to do the
286 # whole expected_foo routine for these intermediate operations;
287 # they're not what we're here to test, after all, so it's enough to
288 # know that they worked. Is this a bad practice? ###
290 # run_and_verify_merge doesn't support merging to a file WCPATH
291 # so use run_and_verify_svn.
292 svntest.actions.run_and_verify_svn(None,
293 expected_merge_output([[-3]], 'G ' +
294 other_rho_path +
295 '\n'),
296 [], 'merge', '-c-3',
297 sbox.repo_url + '/A/D/G/rho',
298 other_rho_path)
300 # Now *prepend* ten or so lines to A/D/G/rho. Since rho had ten
301 # lines appended in revision 2, and then another ten in revision 3,
302 # these new local mods will be separated from the rev 3 changes by
303 # enough distance that they won't conflict, so the merge should be
304 # clean.
305 other_rho_text = ""
306 for x in range(1,10):
307 other_rho_text = other_rho_text + 'Unobtrusive line ' + `x` + ' in rho\n'
308 current_other_rho_text = svntest.main.file_read(other_rho_path)
309 svntest.main.file_write(other_rho_path,
310 other_rho_text + current_other_rho_text)
312 # We expect no merge attempt for pi and tau because they inherit
313 # mergeinfo from the WC root. There is explicit mergeinfo on rho
314 # ('/A/D/G/rho:2') so expect it to be merged (cleanly).
315 expected_output = wc.State(os.path.join(other_wc, 'A', 'D', 'G'),
316 {'rho' : Item(status='G ')})
317 expected_disk = wc.State("", {
318 'pi' : Item("This is the file 'pi'.\n"),
319 'rho' : Item("This is the file 'rho'.\n"),
320 'tau' : Item("This is the file 'tau'.\n"),
322 expected_disk.tweak('rho',
323 contents=other_rho_text
324 + expected_disk.desc['rho'].contents
325 + rho_text
326 + additional_rho_text)
327 expected_disk.tweak('pi',
328 contents=expected_disk.desc['pi'].contents
329 + pi_text)
331 expected_status = wc.State(os.path.join(other_wc, 'A', 'D', 'G'),
332 { '' : Item(wc_rev=1, status=' '),
333 'rho' : Item(wc_rev=1, status='M '),
334 'pi' : Item(wc_rev=1, status='M '),
335 'tau' : Item(wc_rev=1, status='C '),
338 inject_conflict_into_expected_state('tau', expected_disk, expected_status,
339 other_tau_text, tau_text, 3)
341 # Do the merge, but check svn:mergeinfo props separately since
342 # run_and_verify_merge would attempt to proplist tau's conflict
343 # files if we asked it to check props.
344 svntest.actions.run_and_verify_merge(
345 os.path.join(other_wc, 'A', 'D', 'G'),
346 '2', '3',
347 sbox.repo_url + '/A/D/G',
348 expected_output,
349 expected_disk,
350 expected_status,
351 expected_skip,
352 None,
353 svntest.tree.detect_conflict_files, list(tau_conflict_support_files))
356 svntest.actions.run_and_verify_svn(None, [], [],
357 'propget', SVN_PROP_MERGEINFO,
358 os.path.join(other_wc,
359 "A", "D", "G", "rho"))
362 #----------------------------------------------------------------------
364 # Merge should copy-with-history when adding files or directories
366 def add_with_history(sbox):
367 "merge and add new files/dirs with history"
369 sbox.build()
370 wc_dir = sbox.wc_dir
372 C_path = os.path.join(wc_dir, 'A', 'C')
373 F_path = os.path.join(wc_dir, 'A', 'B', 'F')
374 F_url = sbox.repo_url + '/A/B/F'
376 Q_path = os.path.join(F_path, 'Q')
377 Q2_path = os.path.join(F_path, 'Q2')
378 foo_path = os.path.join(F_path, 'foo')
379 foo2_path = os.path.join(F_path, 'foo2')
380 bar_path = os.path.join(F_path, 'Q', 'bar')
381 bar2_path = os.path.join(F_path, 'Q', 'bar2')
383 svntest.main.run_svn(None, 'mkdir', Q_path)
384 svntest.main.run_svn(None, 'mkdir', Q2_path)
385 svntest.main.file_append(foo_path, "foo")
386 svntest.main.file_append(foo2_path, "foo2")
387 svntest.main.file_append(bar_path, "bar")
388 svntest.main.file_append(bar2_path, "bar2")
389 svntest.main.run_svn(None, 'add', foo_path, foo2_path, bar_path, bar2_path)
390 svntest.main.run_svn(None, 'propset', 'x', 'x', Q2_path)
391 svntest.main.run_svn(None, 'propset', 'y', 'y', foo2_path)
392 svntest.main.run_svn(None, 'propset', 'z', 'z', bar2_path)
394 expected_output = wc.State(wc_dir, {
395 'A/B/F/Q' : Item(verb='Adding'),
396 'A/B/F/Q2' : Item(verb='Adding'),
397 'A/B/F/Q/bar' : Item(verb='Adding'),
398 'A/B/F/Q/bar2': Item(verb='Adding'),
399 'A/B/F/foo' : Item(verb='Adding'),
400 'A/B/F/foo2' : Item(verb='Adding'),
402 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
403 expected_status.add({
404 'A/B/F/Q' : Item(status=' ', wc_rev=2),
405 'A/B/F/Q2' : Item(status=' ', wc_rev=2),
406 'A/B/F/Q/bar' : Item(status=' ', wc_rev=2),
407 'A/B/F/Q/bar2': Item(status=' ', wc_rev=2),
408 'A/B/F/foo' : Item(status=' ', wc_rev=2),
409 'A/B/F/foo2' : Item(status=' ', wc_rev=2),
411 svntest.actions.run_and_verify_commit(wc_dir,
412 expected_output,
413 expected_status,
414 None,
415 wc_dir)
417 ### "The Merge Kluge"
419 ### *****************************************************
420 ### *** ***
421 ### *** Before erasing this comment, please check ***
422 ### *** for references to "The Merge Kluge" ***
423 ### *** elsewhere in this file, update_tests.py ***
424 ### *** and switch_tests.py. ***
425 ### *** ***
426 ### *****************************************************
428 ### The shortening of C_path and the chdir() below are a kluge to
429 ### work around
431 ### http://subversion.tigris.org/issues/show_bug.cgi?id=767#desc16
433 ### Note that the problem isn't simply that 'svn merge' sometimes
434 ### puts temp files in cwd. That's bad enough, but even if svn
435 ### were to choose /tmp or some other static place blessed by
436 ### apr_get_temp_dir(), we'd still experience the error
438 ### svn: Move failed
439 ### svn: Can't move 'tmp.2' to '.../.svn/tmp/text-base/file1.svn-base':
440 ### Invalid cross-device link
442 ### when running the tests on a ramdisk. After all, there's no
443 ### reason why apr_get_temp_dir() would return a path inside
444 ### svn-test-work/, which is the mount point for the ramdisk.
446 ### http://subversion.tigris.org/issues/show_bug.cgi?id=767#desc20
447 ### starts a discussion on how to solve this in Subversion itself.
448 ### However, until that's settled, we still want to be able to run
449 ### the tests in a ramdisk, hence this kluge.
451 short_C_path = shorten_path_kludge(C_path)
452 expected_output = wc.State(short_C_path, {
453 'Q' : Item(status='A '),
454 'Q2' : Item(status='A '),
455 'Q/bar' : Item(status='A '),
456 'Q/bar2' : Item(status='A '),
457 'foo' : Item(status='A '),
458 'foo2' : Item(status='A '),
460 expected_disk = wc.State('', {
461 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B/F:2'}),
462 'Q' : Item(),
463 'Q2' : Item(props={'x' : 'x'}),
464 'Q/bar' : Item("bar"),
465 'Q/bar2' : Item("bar2", props={'z' : 'z'}),
466 'foo' : Item("foo"),
467 'foo2' : Item("foo2", props={'y' : 'y'}),
469 expected_status = wc.State(short_C_path, {
470 '' : Item(status=' M', wc_rev=1),
471 'Q' : Item(status='A ', wc_rev='-', copied='+'),
472 'Q2' : Item(status='A ', wc_rev='-', copied='+'),
473 'Q/bar' : Item(status='A ', wc_rev='-', copied='+'),
474 'Q/bar2' : Item(status='A ', wc_rev='-', copied='+'),
475 'foo' : Item(status='A ', wc_rev='-', copied='+'),
476 'foo2' : Item(status='A ', wc_rev='-', copied='+'),
478 expected_skip = wc.State(short_C_path, { })
480 saved_cwd = os.getcwd()
482 os.chdir(svntest.main.work_dir)
483 svntest.actions.run_and_verify_merge(short_C_path, '1', '2', F_url,
484 expected_output,
485 expected_disk,
486 expected_status,
487 expected_skip,
488 None, None, None, None, None,
489 1) # check props
490 os.chdir(saved_cwd)
492 expected_output = svntest.wc.State(wc_dir, {
493 'A/C' : Item(verb='Sending'),
494 'A/C/Q' : Item(verb='Adding'),
495 'A/C/Q2' : Item(verb='Adding'),
496 'A/C/Q/bar' : Item(verb='Adding'),
497 'A/C/Q/bar2': Item(verb='Adding'),
498 'A/C/foo' : Item(verb='Adding'),
499 'A/C/foo2' : Item(verb='Adding'),
501 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
502 expected_status.add({
503 'A/C' : Item(status=' ', wc_rev=3),
504 'A/B/F/Q' : Item(status=' ', wc_rev=2),
505 'A/B/F/Q2' : Item(status=' ', wc_rev=2),
506 'A/B/F/Q/bar' : Item(status=' ', wc_rev=2),
507 'A/B/F/Q/bar2': Item(status=' ', wc_rev=2),
508 'A/B/F/foo' : Item(status=' ', wc_rev=2),
509 'A/B/F/foo2' : Item(status=' ', wc_rev=2),
510 'A/C/Q' : Item(status=' ', wc_rev=3),
511 'A/C/Q2' : Item(status=' ', wc_rev=3),
512 'A/C/Q/bar' : Item(status=' ', wc_rev=3),
513 'A/C/Q/bar2' : Item(status=' ', wc_rev=3),
514 'A/C/foo' : Item(status=' ', wc_rev=3),
515 'A/C/foo2' : Item(status=' ', wc_rev=3),
517 svntest.actions.run_and_verify_commit(wc_dir,
518 expected_output,
519 expected_status,
520 None,
521 wc_dir)
523 #----------------------------------------------------------------------
525 def delete_file_and_dir(sbox):
526 "merge that deletes items"
528 sbox.build()
529 wc_dir = sbox.wc_dir
531 # Rev 2 copy B to B2
532 B_path = os.path.join(wc_dir, 'A', 'B')
533 B2_path = os.path.join(wc_dir, 'A', 'B2')
534 B_url = sbox.repo_url + '/A/B'
536 svntest.actions.run_and_verify_svn(None, None, [],
537 'copy', B_path, B2_path)
539 expected_output = wc.State(wc_dir, {
540 'A/B2' : Item(verb='Adding'),
542 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
543 expected_status.add({
544 'A/B2' : Item(status=' ', wc_rev=2),
545 'A/B2/E' : Item(status=' ', wc_rev=2),
546 'A/B2/E/alpha' : Item(status=' ', wc_rev=2),
547 'A/B2/E/beta' : Item(status=' ', wc_rev=2),
548 'A/B2/F' : Item(status=' ', wc_rev=2),
549 'A/B2/lambda' : Item(status=' ', wc_rev=2),
551 svntest.actions.run_and_verify_commit(wc_dir,
552 expected_output,
553 expected_status,
554 None,
555 wc_dir)
557 # Rev 3 delete E and lambda from B
558 E_path = os.path.join(B_path, 'E')
559 lambda_path = os.path.join(B_path, 'lambda')
560 svntest.actions.run_and_verify_svn(None, None, [],
561 'delete', E_path, lambda_path)
563 expected_output = wc.State(wc_dir, {
564 'A/B/E' : Item(verb='Deleting'),
565 'A/B/lambda' : Item(verb='Deleting'),
567 expected_status.remove('A/B/E',
568 'A/B/E/alpha',
569 'A/B/E/beta',
570 'A/B/lambda')
571 svntest.actions.run_and_verify_commit(wc_dir,
572 expected_output,
573 expected_status,
574 None,
575 wc_dir)
577 def modify_B2():
578 # Local mods in B2
579 B2_E_path = os.path.join(B2_path, 'E')
580 B2_lambda_path = os.path.join(B2_path, 'lambda')
581 svntest.actions.run_and_verify_svn(None, None, [],
582 'propset', 'foo', 'foo_val',
583 B2_E_path, B2_lambda_path)
584 expected_status.tweak(
585 'A/B2/E', 'A/B2/lambda', status=' M'
587 svntest.actions.run_and_verify_status(wc_dir, expected_status)
589 modify_B2()
591 # Merge rev 3 into B2
593 # The local mods to the paths modified in r3 cause the paths to be
594 # skipped (without --force), resulting in only mergeinfo changes. The
595 # target of the merge 'B2' gets mergeinfo for r3 and B2's two skipped
596 # children, 'E' and 'lambda', get override mergeinfo reflecting their
597 # mergeinfo prior to the merge (in this case empty mergeinfo).
598 expected_output = wc.State(B2_path, { })
599 expected_disk = wc.State('', {
600 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B:3'}),
601 'E' : Item(props={SVN_PROP_MERGEINFO : '',
602 'foo' : 'foo_val'}),
603 'E/alpha' : Item("This is the file 'alpha'.\n"),
604 'E/beta' : Item("This is the file 'beta'.\n"),
605 'F' : Item(),
606 'lambda' : Item("This is the file 'lambda'.\n",
607 props={SVN_PROP_MERGEINFO : '',
608 'foo' : 'foo_val'}),
610 expected_status2 = wc.State(B2_path, {
611 '' : Item(status=' M'),
612 'E' : Item(status=' M'),
613 'E/alpha' : Item(status=' '),
614 'E/beta' : Item(status=' '),
615 'F' : Item(status=' '),
616 'lambda' : Item(status=' M'),
618 expected_status2.tweak(wc_rev=2)
619 expected_skip = wc.State(B2_path, {
620 'lambda' : Item(),
621 'E' : Item(),
623 svntest.actions.run_and_verify_merge(B2_path, '2', '3', B_url,
624 expected_output,
625 expected_disk,
626 expected_status2,
627 expected_skip,
628 None, None, None, None, None,
629 True)
631 # Revert the previous merge attempt and redo the local changes to B2.
632 # Why do we need to do this? Because 'B2' already has mergeinfo reflecting
633 # r3 has been merged. If we didn't revert we'd need to use
634 # --ignore-ancestry' to force B2's children 'E' and 'lamda' to actually be
635 # deleted. This is another facet of issue #2898.
636 svntest.actions.run_and_verify_svn(None, None, [],
637 'revert', '-R', wc_dir)
638 modify_B2()
640 expected_output = wc.State(B2_path, {
641 'E' : Item(status='D '),
642 'lambda' : Item(status='D '),
644 expected_disk.remove('E/alpha', 'E/beta', 'lambda')
645 expected_disk.tweak('E', props={'foo' : 'foo_val'})
646 expected_status2.tweak('E', 'E/alpha', 'E/beta', 'lambda', status='D ')
647 expected_status2.tweak('', status=' M')
648 expected_skip.remove('lambda', 'E')
650 ### Full-to-dry-run automatic comparison disabled because a) dry-run
651 ### doesn't descend into deleted directories, and b) the full merge
652 ### notifies deleted directories twice.
653 svntest.actions.run_and_verify_merge(B2_path, '2', '3', B_url,
654 expected_output,
655 expected_disk,
656 expected_status2,
657 expected_skip,
658 None, None, None, None, None,
659 True, 0, '--force')
663 #----------------------------------------------------------------------
665 # Issue 953
666 def simple_property_merges(sbox):
667 "some simple property merges"
669 sbox.build()
670 wc_dir = sbox.wc_dir
672 # Add a property to a file and a directory
673 alpha_path = os.path.join(wc_dir, 'A', 'B', 'E', 'alpha')
674 beta_path = os.path.join(wc_dir, 'A', 'B', 'E', 'beta')
675 E_path = os.path.join(wc_dir, 'A', 'B', 'E')
677 svntest.actions.run_and_verify_svn(None, None, [],
678 'propset', 'foo', 'foo_val',
679 alpha_path)
680 # A binary, non-UTF8 property value
681 svntest.actions.run_and_verify_svn(None, None, [],
682 'propset', 'foo', 'foo\201val',
683 beta_path)
684 svntest.actions.run_and_verify_svn(None, None, [],
685 'propset', 'foo', 'foo_val',
686 E_path)
688 # Commit change as rev 2
689 expected_output = svntest.wc.State(wc_dir, {
690 'A/B/E' : Item(verb='Sending'),
691 'A/B/E/alpha' : Item(verb='Sending'),
692 'A/B/E/beta' : Item(verb='Sending'),
694 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
695 expected_status.tweak('A/B/E', 'A/B/E/alpha', 'A/B/E/beta',
696 wc_rev=2, status=' ')
697 svntest.actions.run_and_verify_commit(wc_dir,
698 expected_output, expected_status,
699 None, wc_dir)
700 svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
702 # Copy B to B2 as rev 3
703 B_url = sbox.repo_url + '/A/B'
704 B2_url = sbox.repo_url + '/A/B2'
706 svntest.actions.run_and_verify_svn(None, None, [],
707 'copy', '-m', 'copy B to B2',
708 B_url, B2_url)
709 svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
711 # Modify a property and add a property for the file and directory
712 svntest.actions.run_and_verify_svn(None, None, [],
713 'propset', 'foo', 'mod_foo', alpha_path)
714 svntest.actions.run_and_verify_svn(None, None, [],
715 'propset', 'bar', 'bar_val', alpha_path)
716 svntest.actions.run_and_verify_svn(None, None, [],
717 'propset', 'foo', 'mod\201foo', beta_path)
718 svntest.actions.run_and_verify_svn(None, None, [],
719 'propset', 'bar', 'bar\201val', beta_path)
720 svntest.actions.run_and_verify_svn(None, None, [],
721 'propset', 'foo', 'mod_foo', E_path)
722 svntest.actions.run_and_verify_svn(None, None, [],
723 'propset', 'bar', 'bar_val', E_path)
725 # Commit change as rev 4
726 expected_status = svntest.actions.get_virginal_state(wc_dir, 3)
727 expected_status.tweak('A/B/E', 'A/B/E/alpha', 'A/B/E/beta',
728 wc_rev=4, status=' ')
729 expected_status.add({
730 'A/B2' : Item(status=' ', wc_rev=3),
731 'A/B2/E' : Item(status=' ', wc_rev=3),
732 'A/B2/E/alpha' : Item(status=' ', wc_rev=3),
733 'A/B2/E/beta' : Item(status=' ', wc_rev=3),
734 'A/B2/F' : Item(status=' ', wc_rev=3),
735 'A/B2/lambda' : Item(status=' ', wc_rev=3),
737 svntest.actions.run_and_verify_commit(wc_dir,
738 expected_output, expected_status,
739 None, wc_dir)
740 svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
742 pristine_status = expected_status
743 pristine_status.tweak(wc_rev=4)
745 # Merge B 3:4 into B2
746 B2_path = os.path.join(wc_dir, 'A', 'B2')
747 expected_output = wc.State(B2_path, {
748 'E' : Item(status=' U'),
749 'E/alpha' : Item(status=' U'),
750 'E/beta' : Item(status=' U'),
752 expected_disk = wc.State('', {
753 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B:4'}),
754 'E' : Item(),
755 'E/alpha' : Item("This is the file 'alpha'.\n"),
756 'E/beta' : Item("This is the file 'beta'.\n"),
757 'F' : Item(),
758 'lambda' : Item("This is the file 'lambda'.\n"),
760 expected_disk.tweak('E', 'E/alpha',
761 props={'foo' : 'mod_foo', 'bar' : 'bar_val'})
762 expected_disk.tweak('E/beta',
763 props={'foo' : 'mod\201foo', 'bar' : 'bar\201val'})
764 expected_status = wc.State(B2_path, {
765 '' : Item(status=' M'),
766 'E' : Item(status=' M'),
767 'E/alpha' : Item(status=' M'),
768 'E/beta' : Item(status=' M'),
769 'F' : Item(status=' '),
770 'lambda' : Item(status=' '),
772 expected_status.tweak(wc_rev=4)
773 expected_skip = wc.State('', { })
774 svntest.actions.run_and_verify_merge(B2_path, '3', '4', B_url,
775 expected_output,
776 expected_disk,
777 expected_status,
778 expected_skip,
779 None, None, None, None, None, 1)
781 # Revert merge
782 svntest.actions.run_and_verify_svn(None, None, [],
783 'revert', '--recursive', wc_dir)
784 svntest.actions.run_and_verify_status(wc_dir, pristine_status)
786 # Merge B 2:1 into B2 (B2's mergeinfo should get elided away)
787 expected_status.tweak('', status=' ')
788 expected_disk.remove('')
789 expected_disk.tweak('E', 'E/alpha', 'E/beta', props={})
790 svntest.actions.run_and_verify_merge(B2_path, '2', '1', B_url,
791 expected_output,
792 expected_disk,
793 expected_status,
794 expected_skip,
795 None, None, None, None, None, 1)
797 # Merge B 3:4 into B2 now causes a conflict
798 expected_disk.add({
799 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B:4'}),
800 'E/dir_conflicts.prej'
801 : Item("Trying to change property 'foo' from 'foo_val' to 'mod_foo',\n"
802 + "but it has been locally deleted.\n"),
803 'E/alpha.prej'
804 : Item("Trying to change property 'foo' from 'foo_val' to 'mod_foo',\n"
805 + "but it has been locally deleted.\n"),
806 'E/beta.prej'
807 : Item("Trying to change property 'foo' from 'foo?\\129val' to"
808 + " 'mod?\\129foo',\n"
809 + "but it has been locally deleted.\n"),
811 expected_disk.tweak('E', 'E/alpha', props={'bar' : 'bar_val'})
812 expected_disk.tweak('E/beta', props={'bar' : 'bar\201val'})
813 expected_status.tweak('', status=' M')
814 expected_status.tweak('E', 'E/alpha', 'E/beta', status=' C')
815 expected_output.tweak('E', 'E/alpha', 'E/beta', status=' C')
816 svntest.actions.run_and_verify_merge(B2_path, '3', '4', B_url,
817 expected_output,
818 expected_disk,
819 expected_status,
820 expected_skip,
821 None, None, None, None, None, 1)
823 # issue 1109 : single file property merge. This test performs a merge
824 # that should be a no-op (adding properties that are already present).
825 svntest.actions.run_and_verify_svn(None, None, [],
826 'revert', '--recursive', wc_dir)
827 svntest.actions.run_and_verify_status(wc_dir, pristine_status)
829 # Copy A at rev 4 to A2 to make revision 5.
830 A_url = sbox.repo_url + '/A'
831 A2_url = sbox.repo_url + '/A2'
832 svntest.actions.run_and_verify_svn(None,
833 ['\n', 'Committed revision 5.\n'], [],
834 'copy', '-m', 'copy A to A2',
835 A_url, A2_url)
837 # Re-root the WC at A2.
838 svntest.actions.run_and_verify_svn(None, None, [], 'switch', A2_url, wc_dir)
840 # Attempt to re-merge rev 4 of the original A's alpha. Mergeinfo
841 # inherited from A2 (created by its copy from A) allows us to avoid
842 # a repeated merge.
843 alpha_url = sbox.repo_url + '/A/B/E/alpha'
844 alpha_path = os.path.join(wc_dir, 'B', 'E', 'alpha')
846 # Cannot use run_and_verify_merge with a file target
847 svntest.actions.run_and_verify_svn(None, [], [], 'merge', '-r', '3:4',
848 alpha_url, alpha_path)
850 exit_code, output, err = svntest.actions.run_and_verify_svn(None, None, [],
851 'pl', alpha_path)
853 saw_foo = 0
854 saw_bar = 0
855 for line in output:
856 if re.match("\\s*foo\\s*$", line):
857 saw_foo = 1
858 if re.match("\\s*bar\\s*$", line):
859 saw_bar = 1
861 if not saw_foo or not saw_bar:
862 raise svntest.Failure("Expected properties not found")
865 #----------------------------------------------------------------------
866 # This is a regression for issue #1176.
868 def merge_catches_nonexistent_target(sbox):
869 "merge should not die if a target file is absent"
871 sbox.build()
872 wc_dir = sbox.wc_dir
874 # Copy G to a new directory, Q. Create Q/newfile. Commit a change
875 # to Q/newfile. Now merge that change... into G. Merge should not
876 # error, but should do nothing.
878 G_path = os.path.join(wc_dir, 'A', 'D', 'G')
879 Q_path = os.path.join(wc_dir, 'A', 'D', 'Q')
880 newfile_path = os.path.join(Q_path, 'newfile')
881 Q_url = sbox.repo_url + '/A/D/Q'
883 # Copy dir A/D/G to A/D/Q
884 svntest.actions.run_and_verify_svn(None, None, [], 'cp', G_path, Q_path)
886 svntest.main.file_append(newfile_path, 'This is newfile.\n')
887 svntest.actions.run_and_verify_svn(None, None, [], 'add', newfile_path)
889 # Add newfile to dir G, creating r2.
890 expected_output = wc.State(wc_dir, {
891 'A/D/Q' : Item(verb='Adding'),
892 'A/D/Q/newfile' : Item(verb='Adding'),
894 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
895 expected_status.add({
896 'A/D/Q' : Item(status=' ', wc_rev=2),
897 'A/D/Q/pi' : Item(status=' ', wc_rev=2),
898 'A/D/Q/rho' : Item(status=' ', wc_rev=2),
899 'A/D/Q/tau' : Item(status=' ', wc_rev=2),
900 'A/D/Q/newfile' : Item(status=' ', wc_rev=2),
902 svntest.actions.run_and_verify_commit(wc_dir,
903 expected_output,
904 expected_status,
905 None, wc_dir)
907 # Change newfile, creating r3.
908 svntest.main.file_append(newfile_path, 'A change to newfile.\n')
909 expected_output = wc.State(wc_dir, {
910 'A/D/Q/newfile' : Item(verb='Sending'),
912 expected_status.tweak('A/D/Q/newfile', wc_rev=3)
913 svntest.actions.run_and_verify_commit(wc_dir,
914 expected_output,
915 expected_status,
916 None, wc_dir)
918 # Merge the change to newfile (from r3) into G, where newfile
919 # doesn't exist.
920 os.chdir(G_path)
921 expected_output = wc.State('', { })
922 expected_status = wc.State('', {
923 '' : Item(status=' M' ),
924 'pi' : Item(status=' ' ),
925 'rho' : Item(status=' ' ),
926 'tau' : Item(status=' ' ),
928 expected_status.tweak(wc_rev=1)
929 expected_disk = wc.State('', {
930 '' : Item(props={SVN_PROP_MERGEINFO : '/A/D/Q:3'}),
931 'pi' : Item("This is the file 'pi'.\n"),
932 'rho' : Item("This is the file 'rho'.\n"),
933 'tau' : Item("This is the file 'tau'.\n"),
935 expected_skip = wc.State('', {
936 'newfile' :Item(),
938 svntest.actions.run_and_verify_merge('', '2', '3', Q_url,
939 expected_output,
940 expected_disk,
941 expected_status,
942 expected_skip,
943 None, None, None, None, None, True)
945 #----------------------------------------------------------------------
947 def merge_tree_deleted_in_target(sbox):
948 "merge on deleted directory in target"
950 sbox.build()
951 wc_dir = sbox.wc_dir
953 # Copy B to a new directory, I. Modify B/E/alpha, Remove I/E. Now
954 # merge that change... into I. Merge should not error
956 B_path = os.path.join(wc_dir, 'A', 'B')
957 I_path = os.path.join(wc_dir, 'A', 'I')
958 alpha_path = os.path.join(B_path, 'E', 'alpha')
959 B_url = sbox.repo_url + '/A/B'
960 I_url = sbox.repo_url + '/A/I'
963 # Copy B to I, creating r1.
964 svntest.actions.run_and_verify_svn(None, None, [],
965 'cp', B_url, I_url, '-m', 'rev 2')
967 # Change some files, creating r2.
968 svntest.main.file_append(alpha_path, 'A change to alpha.\n')
969 svntest.main.file_append(os.path.join(B_path, 'lambda'), 'change lambda.\n')
970 svntest.actions.run_and_verify_svn(None, None, [],
971 'ci', '-m', 'rev 3', B_path)
973 # Remove E, creating r3.
974 E_url = sbox.repo_url + '/A/I/E'
975 svntest.actions.run_and_verify_svn(None, None, [],
976 'rm', E_url, '-m', 'rev 4')
978 svntest.actions.run_and_verify_svn(None, None, [],
979 'up', os.path.join(wc_dir,'A'))
981 expected_output = wc.State(I_path, {
982 'lambda' : Item(status='U '),
984 expected_disk = wc.State('', {
985 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B:3'}),
986 'F' : Item(),
987 'lambda' : Item("This is the file 'lambda'.\nchange lambda.\n"),
989 expected_status = wc.State(I_path, {
990 '' : Item(status=' M'),
991 'F' : Item(status=' '),
992 'lambda' : Item(status='M '),
994 expected_status.tweak(wc_rev=4)
995 expected_skip = wc.State(I_path, {
996 'E' : Item(),
997 'E/alpha' : Item(),
999 svntest.actions.run_and_verify_merge(I_path, '2', '3', B_url,
1000 expected_output,
1001 expected_disk,
1002 expected_status,
1003 expected_skip,
1004 None, None, None, None, None,
1005 1, 0)
1007 #----------------------------------------------------------------------
1008 # Issue #2515
1010 def merge_added_dir_to_deleted_in_target(sbox):
1011 "merge an added dir on a deleted dir in target"
1013 sbox.build()
1014 wc_dir = sbox.wc_dir
1016 # copy B to a new directory, I.
1017 # delete F in I.
1018 # add J to B/F.
1019 # merge add to I.
1021 B_url = sbox.repo_url + '/A/B'
1022 I_url = sbox.repo_url + '/A/I'
1023 F_url = sbox.repo_url + '/A/I/F'
1024 J_url = sbox.repo_url + '/A/B/F/J'
1025 I_path = os.path.join(wc_dir, 'A', 'I')
1028 svntest.actions.run_and_verify_svn(None, None, [],
1029 'cp', B_url, I_url, '-m', 'rev 2')
1031 svntest.actions.run_and_verify_svn(None, None, [],
1032 'rm', F_url, '-m', 'rev 3')
1034 svntest.actions.run_and_verify_svn(None, None, [],
1035 'mkdir', '-m', 'rev 4', J_url)
1037 svntest.actions.run_and_verify_svn(None, None, [],
1038 'up', os.path.join(wc_dir,'A'))
1040 expected_output = wc.State(I_path, {})
1041 expected_disk = wc.State('', {
1042 'E' : Item(),
1043 'E/alpha' : Item("This is the file 'alpha'.\n"),
1044 'E/beta' : Item("This is the file 'beta'.\n"),
1045 'lambda' : Item("This is the file 'lambda'.\n"),
1047 expected_skip = wc.State(I_path, {
1048 'F/J' : Item(),
1049 'F' : Item(),
1052 svntest.actions.run_and_verify_merge(I_path, '2', '4', B_url,
1053 expected_output,
1054 expected_disk,
1055 None,
1056 expected_skip,
1057 None, None, None, None, None,
1058 0, 0)
1060 #----------------------------------------------------------------------
1061 # This is a regression for issue #1176.
1063 def merge_similar_unrelated_trees(sbox):
1064 "merging similar trees ancestrally unrelated"
1066 ## See http://subversion.tigris.org/issues/show_bug.cgi?id=1249. ##
1068 sbox.build()
1069 wc_dir = sbox.wc_dir
1071 # Simple test. Make three directories with the same content.
1072 # Modify some stuff in the second one. Now merge
1073 # (firstdir:seconddir->thirddir).
1075 base1_path = os.path.join(wc_dir, 'base1')
1076 base2_path = os.path.join(wc_dir, 'base2')
1077 apply_path = os.path.join(wc_dir, 'apply')
1079 base1_url = os.path.join(sbox.repo_url + '/base1')
1080 base2_url = os.path.join(sbox.repo_url + '/base2')
1082 # Make a tree of stuff ...
1083 os.mkdir(base1_path)
1084 svntest.main.file_append(os.path.join(base1_path, 'iota'),
1085 "This is the file iota\n")
1086 os.mkdir(os.path.join(base1_path, 'A'))
1087 svntest.main.file_append(os.path.join(base1_path, 'A', 'mu'),
1088 "This is the file mu\n")
1089 os.mkdir(os.path.join(base1_path, 'A', 'B'))
1090 svntest.main.file_append(os.path.join(base1_path, 'A', 'B', 'alpha'),
1091 "This is the file alpha\n")
1092 svntest.main.file_append(os.path.join(base1_path, 'A', 'B', 'beta'),
1093 "This is the file beta\n")
1095 # ... Copy it twice ...
1096 shutil.copytree(base1_path, base2_path)
1097 shutil.copytree(base1_path, apply_path)
1099 # ... Gonna see if merge is naughty or nice!
1100 svntest.main.file_append(os.path.join(base2_path, 'A', 'mu'),
1101 "A new line in mu.\n")
1102 os.rename(os.path.join(base2_path, 'A', 'B', 'beta'),
1103 os.path.join(base2_path, 'A', 'B', 'zeta'))
1105 svntest.actions.run_and_verify_svn(None, None, [],
1106 'add', base1_path, base2_path, apply_path)
1108 svntest.actions.run_and_verify_svn(None, None, [],
1109 'ci', '-m', 'rev 2', wc_dir)
1111 expected_output = wc.State(apply_path, {
1112 'A/mu' : Item(status='U '),
1113 'A/B/zeta' : Item(status='A '),
1114 'A/B/beta' : Item(status='D '),
1117 # Search for the comment entitled "The Merge Kluge" elsewhere in
1118 # this file, to understand why we shorten and chdir() below.
1119 saved_cwd = os.getcwd()
1120 os.chdir(svntest.main.work_dir)
1121 # run_and_verify_merge doesn't support 'svn merge URL URL path'
1122 svntest.actions.run_and_verify_svn(None, None, [],
1123 'merge',
1124 '--ignore-ancestry',
1125 base1_url, base2_url,
1126 shorten_path_kludge(apply_path))
1127 os.chdir(saved_cwd)
1129 expected_status = wc.State(apply_path, {
1130 '' : Item(status=' '),
1131 'A' : Item(status=' '),
1132 'A/mu' : Item(status='M '),
1133 'A/B' : Item(status=' '),
1134 'A/B/zeta' : Item(status='A ', copied='+'),
1135 'A/B/alpha' : Item(status=' '),
1136 'A/B/beta' : Item(status='D '),
1137 'iota' : Item(status=' '),
1139 expected_status.tweak(wc_rev=2)
1140 expected_status.tweak('A/B/zeta', wc_rev='-')
1141 svntest.actions.run_and_verify_status(apply_path, expected_status)
1143 #----------------------------------------------------------------------
1144 def merge_one_file_helper(sbox, arg_flav, record_only = 0):
1145 "ARG_FLAV is one of 'r' (revision range) or 'c' (single change)."
1147 if arg_flav not in ('r', 'c', '*'):
1148 raise svntest.Failure("Unrecognized flavor of merge argument")
1150 sbox.build()
1151 wc_dir = sbox.wc_dir
1153 rho_rel_path = os.path.join('A', 'D', 'G', 'rho')
1154 rho_path = os.path.join(wc_dir, rho_rel_path)
1155 G_path = os.path.join(wc_dir, 'A', 'D', 'G')
1156 rho_url = sbox.repo_url + '/A/D/G/rho'
1158 # Change rho for revision 2
1159 svntest.main.file_append(rho_path, 'A new line in rho.\n')
1161 expected_output = wc.State(wc_dir, { rho_rel_path : Item(verb='Sending'), })
1162 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
1163 expected_status.tweak('A/D/G/rho', wc_rev=2)
1164 svntest.actions.run_and_verify_commit(wc_dir,
1165 expected_output,
1166 expected_status,
1167 None,
1168 wc_dir)
1170 # Backdate rho to revision 1, so we can merge in the rev 2 changes.
1171 svntest.actions.run_and_verify_svn(None, None, [],
1172 'up', '-r', '1', rho_path)
1174 # Try one merge with an explicit target; it should succeed.
1175 # ### Yes, it would be nice to use run_and_verify_merge(), but it
1176 # appears to be impossible to get the expected_foo trees working
1177 # right. I think something is still assuming a directory target.
1178 if arg_flav == 'r':
1179 svntest.actions.run_and_verify_svn(None ,
1180 expected_merge_output([[2]], 'U ' +
1181 rho_path + '\n'),
1183 'merge', '-r', '1:2',
1184 rho_url, rho_path)
1185 elif arg_flav == 'c':
1186 svntest.actions.run_and_verify_svn(None ,
1187 expected_merge_output([[2]], 'U ' +
1188 rho_path + '\n'),
1190 'merge', '-c', '2',
1191 rho_url, rho_path)
1192 elif arg_flav == '*':
1193 svntest.actions.run_and_verify_svn(None ,
1194 expected_merge_output([[2]], 'U ' +
1195 rho_path + '\n'),
1197 'merge', rho_url, rho_path)
1199 expected_status.tweak(wc_rev=1)
1200 expected_status.tweak('A/D/G/rho', status='MM')
1201 svntest.actions.run_and_verify_status(wc_dir, expected_status)
1203 # Inspect rho, make sure it's right.
1204 rho_text = svntest.tree.get_text(rho_path)
1205 if rho_text != "This is the file 'rho'.\nA new line in rho.\n":
1206 raise svntest.Failure("Unexpected text in merged '" + rho_path + "'")
1208 # Restore rho to pristine revision 1, for another merge.
1209 svntest.actions.run_and_verify_svn(None, None, [], 'revert', rho_path)
1210 expected_status.tweak('A/D/G/rho', status=' ')
1211 svntest.actions.run_and_verify_status(wc_dir, expected_status)
1213 # Cd into the directory and run merge with no targets.
1214 # It should still merge into rho.
1215 saved_cwd = os.getcwd()
1216 os.chdir(G_path)
1218 # Cannot use run_and_verify_merge with a file target
1219 merge_cmd = ['merge']
1220 if arg_flav == 'r':
1221 merge_cmd += ['-r', '1:2']
1222 elif arg_flav == 'c':
1223 merge_cmd += ['-c', '2']
1225 if record_only:
1226 expected_output = []
1227 merge_cmd.append('--record-only')
1228 rho_expected_status = ' M'
1229 else:
1230 expected_output = expected_merge_output([[2]], 'U rho\n')
1231 rho_expected_status = 'MM'
1232 merge_cmd.append(rho_url)
1234 svntest.actions.run_and_verify_svn(None, expected_output, [], *merge_cmd)
1236 # Inspect rho, make sure it's right.
1237 rho_text = svntest.tree.get_text('rho')
1238 if record_only:
1239 expected_text = "This is the file 'rho'.\n"
1240 else:
1241 expected_text = "This is the file 'rho'.\nA new line in rho.\n"
1242 if rho_text != expected_text:
1243 print
1244 raise svntest.Failure("Unexpected text merged to 'rho' in '" +
1245 G_path + "'")
1246 os.chdir(saved_cwd)
1248 expected_status.tweak('A/D/G/rho', status=rho_expected_status)
1249 svntest.actions.run_and_verify_status(wc_dir, expected_status)
1251 def merge_one_file_using_r(sbox):
1252 "merge one file (issue #1150) using the -r option"
1253 merge_one_file_helper(sbox, 'r')
1255 def merge_one_file_using_c(sbox):
1256 "merge one file (issue #1150) using the -c option"
1257 merge_one_file_helper(sbox, 'c')
1259 def merge_one_file_using_implicit_revs(sbox):
1260 "merge one file without explicit revisions"
1261 merge_one_file_helper(sbox, '*')
1263 def merge_record_only(sbox):
1264 "mark a revision range as merged"
1265 merge_one_file_helper(sbox, 'r', 1)
1267 #----------------------------------------------------------------------
1268 # This is a regression for the enhancement added in issue #785.
1270 def merge_with_implicit_target_helper(sbox, arg_flav):
1271 "ARG_FLAV is one of 'r' (revision range) or 'c' (single change)."
1273 if arg_flav not in ('r', 'c', '*'):
1274 raise svntest.Failure("Unrecognized flavor of merge argument")
1276 sbox.build()
1277 wc_dir = sbox.wc_dir
1279 # Change mu for revision 2
1280 mu_path = os.path.join(wc_dir, 'A', 'mu')
1281 orig_mu_text = svntest.tree.get_text(mu_path)
1282 added_mu_text = ""
1283 for x in range(2,11):
1284 added_mu_text = added_mu_text + 'This is line ' + `x` + ' in mu\n'
1285 svntest.main.file_append(mu_path, added_mu_text)
1287 # Create expected output tree for initial commit
1288 expected_output = wc.State(wc_dir, {
1289 'A/mu' : Item(verb='Sending'),
1292 # Create expected status tree; all local revisions should be at 1,
1293 # but mu should be at revision 2.
1294 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
1295 expected_status.tweak('A/mu', wc_rev=2)
1297 # Initial commit.
1298 svntest.actions.run_and_verify_commit(wc_dir,
1299 expected_output,
1300 expected_status,
1301 None,
1302 wc_dir)
1304 # Make the "other" working copy, at r1
1305 other_wc = sbox.add_wc_path('other')
1306 svntest.actions.duplicate_dir(wc_dir, other_wc)
1307 svntest.main.run_svn(None, 'up', '-r', 1, other_wc)
1309 # Try the merge without an explicit target; it should succeed.
1310 # Can't use run_and_verify_merge cuz it expects a directory argument.
1311 mu_url = sbox.repo_url + '/A/mu'
1313 os.chdir(os.path.join(other_wc, 'A'))
1315 # merge using filename for sourcepath
1316 # Cannot use run_and_verify_merge with a file target
1317 if arg_flav == 'r':
1318 svntest.actions.run_and_verify_svn(None,
1319 expected_merge_output([[2]],
1320 'U mu\n'),
1322 'merge', '-r', '1:2', 'mu')
1323 elif arg_flav == 'c':
1324 svntest.actions.run_and_verify_svn(None,
1325 expected_merge_output([[2]],
1326 'U mu\n'),
1328 'merge', '-c', '2', 'mu')
1330 elif arg_flav == '*':
1331 # Without a peg revision, the default merge range of BASE:1 (which
1332 # is a no-op) will be chosen. Let's do it both ways (no-op first,
1333 # of course).
1334 svntest.actions.run_and_verify_svn(None, None, [], 'merge', 'mu')
1335 svntest.actions.run_and_verify_svn(None,
1336 expected_merge_output([[2]],
1337 'U mu\n'),
1339 'merge', 'mu@2')
1341 # sanity-check resulting file
1342 if (svntest.tree.get_text('mu') != orig_mu_text + added_mu_text):
1343 raise svntest.Failure("Unexpected text in 'mu'")
1345 # merge using URL for sourcepath
1346 if arg_flav == 'r':
1347 svntest.actions.run_and_verify_svn(None,
1348 expected_merge_output([[-2]],
1349 'G mu\n'),
1351 'merge', '-r', '2:1', mu_url)
1352 elif arg_flav == 'c':
1353 svntest.actions.run_and_verify_svn(None,
1354 expected_merge_output([[-2]],
1355 'G mu\n'),
1357 'merge', '-c', '-2', mu_url)
1358 elif arg_flav == '*':
1359 # Implicit merge source URL and revision range detection is for
1360 # forward merges only (e.g. non-reverts). Undo application of
1361 # r2 to enable continuation of the test case.
1362 svntest.actions.run_and_verify_svn(None,
1363 expected_merge_output([[-2]],
1364 'G mu\n'),
1366 'merge', '-c', '-2', mu_url)
1368 # sanity-check resulting file
1369 if (svntest.tree.get_text('mu') != orig_mu_text):
1370 raise svntest.Failure("Unexpected text '%s' in 'mu', expected '%s'" %
1371 (svntest.tree.get_text('mu'), orig_mu_text))
1375 def merge_with_implicit_target_using_r(sbox):
1376 "merging a file w/no explicit target path using -r"
1377 merge_with_implicit_target_helper(sbox, 'r')
1379 def merge_with_implicit_target_using_c(sbox):
1380 "merging a file w/no explicit target path using -c"
1381 merge_with_implicit_target_helper(sbox, 'c')
1383 def merge_with_implicit_target_and_revs(sbox):
1384 "merging a file w/no explicit target path or revs"
1385 merge_with_implicit_target_helper(sbox, '*')
1388 #----------------------------------------------------------------------
1390 def merge_with_prev (sbox):
1391 "merge operations using PREV revision"
1393 sbox.build()
1394 wc_dir = sbox.wc_dir
1396 # Change mu for revision 2
1397 mu_path = os.path.join(wc_dir, 'A', 'mu')
1398 orig_mu_text = svntest.tree.get_text(mu_path)
1399 added_mu_text = ""
1400 for x in range(2,11):
1401 added_mu_text = added_mu_text + '\nThis is line ' + `x` + ' in mu'
1402 added_mu_text += "\n"
1403 svntest.main.file_append(mu_path, added_mu_text)
1405 zot_path = os.path.join(wc_dir, 'A', 'zot')
1407 svntest.main.file_append(zot_path, "bar")
1408 svntest.main.run_svn(None, 'add', zot_path)
1410 # Create expected output tree for initial commit
1411 expected_output = wc.State(wc_dir, {
1412 'A/mu' : Item(verb='Sending'),
1413 'A/zot' : Item(verb='Adding'),
1416 # Create expected status tree; all local revisions should be at 1,
1417 # but mu should be at revision 2.
1418 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
1419 expected_status.tweak('A/mu', wc_rev=2)
1420 expected_status.add({'A/zot' : Item(status=' ', wc_rev=2)})
1422 # Initial commit.
1423 svntest.actions.run_and_verify_commit(wc_dir,
1424 expected_output,
1425 expected_status,
1426 None,
1427 wc_dir)
1429 # Make some other working copies
1430 other_wc = sbox.add_wc_path('other')
1431 svntest.actions.duplicate_dir(wc_dir, other_wc)
1433 another_wc = sbox.add_wc_path('another')
1434 svntest.actions.duplicate_dir(wc_dir, another_wc)
1436 was_cwd = os.getcwd()
1438 os.chdir(os.path.join(other_wc, 'A'))
1440 # Try to revert the last change to mu via svn merge
1441 # Cannot use run_and_verify_merge with a file target
1442 svntest.actions.run_and_verify_svn(None,
1443 expected_merge_output([[-2]],
1444 'U mu\n'),
1446 'merge', '-r', 'HEAD:PREV', 'mu')
1448 # sanity-check resulting file
1449 if (svntest.tree.get_text('mu') != orig_mu_text):
1450 raise svntest.Failure("Unexpected text in 'mu'")
1452 os.chdir(was_cwd)
1454 other_status = expected_status
1455 other_status.wc_dir = other_wc
1456 other_status.tweak('A/mu', status='M ', wc_rev=2)
1457 other_status.tweak('A/zot', wc_rev=2)
1458 svntest.actions.run_and_verify_status(other_wc, other_status)
1460 os.chdir(another_wc)
1462 # ensure 'A' will be at revision 2
1463 svntest.actions.run_and_verify_svn(None, None, [], 'up')
1465 # now try a revert on a directory, and verify that it removed the zot
1466 # file we had added previously
1467 svntest.actions.run_and_verify_svn(None, None, [],
1468 'merge', '-r', 'COMMITTED:PREV',
1469 'A', 'A')
1471 if (svntest.tree.get_text('A/zot') != None):
1472 raise svntest.Failure("Unexpected text in 'A/zot'")
1474 os.chdir(was_cwd)
1476 another_status = expected_status
1477 another_status.wc_dir = another_wc
1478 another_status.tweak(wc_rev=2)
1479 another_status.tweak('A/mu', status='M ')
1480 another_status.tweak('A/zot', status='D ')
1481 svntest.actions.run_and_verify_status(another_wc, another_status)
1483 #----------------------------------------------------------------------
1484 # Regression test for issue #1319: 'svn merge' should *not* 'C' when
1485 # merging a change into a binary file, unless it has local mods, or has
1486 # different contents from the left side of the merge.
1488 def merge_binary_file (sbox):
1489 "merge change into unchanged binary file"
1491 sbox.build()
1492 wc_dir = sbox.wc_dir
1494 # Add a binary file to the project
1495 theta_contents = svntest.main.file_read(
1496 os.path.join(sys.path[0], "theta.bin"), 'rb')
1497 # Write PNG file data into 'A/theta'.
1498 theta_path = os.path.join(wc_dir, 'A', 'theta')
1499 svntest.main.file_write(theta_path, theta_contents, 'wb')
1501 svntest.main.run_svn(None, 'add', theta_path)
1503 # Commit the new binary file, creating revision 2.
1504 expected_output = svntest.wc.State(wc_dir, {
1505 'A/theta' : Item(verb='Adding (bin)'),
1507 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
1508 expected_status.add({
1509 'A/theta' : Item(status=' ', wc_rev=2),
1511 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
1512 expected_status, None,
1513 wc_dir)
1515 # Make the "other" working copy
1516 other_wc = sbox.add_wc_path('other')
1517 svntest.actions.duplicate_dir(wc_dir, other_wc)
1519 # Change the binary file in first working copy, commit revision 3.
1520 svntest.main.file_append(theta_path, "some extra junk")
1521 expected_output = wc.State(wc_dir, {
1522 'A/theta' : Item(verb='Sending'),
1524 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
1525 expected_status.add({
1526 'A/theta' : Item(status=' ', wc_rev=3),
1528 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
1529 expected_status, None,
1530 wc_dir)
1532 # Search for the comment entitled "The Merge Kluge" elsewhere in
1533 # this file, to understand why we shorten and chdir() below.
1534 short_other_wc = shorten_path_kludge(other_wc)
1536 # In second working copy, attempt to 'svn merge -r 2:3'.
1537 # We should *not* see a conflict during the update, but a 'U'.
1538 # And after the merge, the status should be 'M'.
1539 expected_output = wc.State(short_other_wc, {
1540 'A/theta' : Item(status='U '),
1542 expected_disk = svntest.main.greek_state.copy()
1543 expected_disk.add({
1544 '' : Item(props={SVN_PROP_MERGEINFO : '/:3'}),
1545 'A/theta' : Item(theta_contents + "some extra junk",
1546 props={'svn:mime-type' : 'application/octet-stream'}),
1548 expected_status = svntest.actions.get_virginal_state(short_other_wc, 1)
1549 expected_status.add({
1550 '' : Item(status=' M', wc_rev=1),
1551 'A/theta' : Item(status='M ', wc_rev=2),
1553 expected_skip = wc.State('', { })
1555 os.chdir(svntest.main.work_dir)
1556 svntest.actions.run_and_verify_merge(short_other_wc, '2', '3',
1557 sbox.repo_url,
1558 expected_output,
1559 expected_disk,
1560 expected_status,
1561 expected_skip,
1562 None, None, None, None, None,
1565 #----------------------------------------------------------------------
1566 # Regression test for issue #2403: Incorrect 3-way merge of "added"
1567 # binary file which already exists (unmodified) in the WC
1569 def three_way_merge_add_of_existing_binary_file(sbox):
1570 "3-way merge of 'file add' into existing binary"
1572 sbox.build()
1573 wc_dir = sbox.wc_dir
1575 # Create a branch of A, creating revision 2.
1576 A_url = sbox.repo_url + "/A"
1577 branch_A_url = sbox.repo_url + "/copy-of-A"
1578 svntest.actions.run_and_verify_svn(None, None, [],
1579 "cp",
1580 A_url, branch_A_url,
1581 "-m", "Creating copy-of-A")
1583 # Add a binary file to the WC.
1584 theta_contents = svntest.main.file_read(
1585 os.path.join(sys.path[0], "theta.bin"), 'rb')
1586 # Write PNG file data into 'A/theta'.
1587 theta_path = os.path.join(wc_dir, 'A', 'theta')
1588 svntest.main.file_write(theta_path, theta_contents, 'wb')
1590 svntest.main.run_svn(None, "add", theta_path)
1592 # Commit the new binary file to the repos, creating revision 3.
1593 expected_output = svntest.wc.State(wc_dir, {
1594 "A/theta" : Item(verb="Adding (bin)"),
1596 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
1597 expected_status.add({
1598 "A/theta" : Item(status=" ", wc_rev=3),
1600 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
1601 expected_status, None,
1602 wc_dir)
1604 # Search for the comment entitled "The Merge Kluge" elsewhere in
1605 # this file, to understand why we shorten and chdir() below.
1606 short_wc = shorten_path_kludge(wc_dir)
1608 # In the working copy, attempt to 'svn merge branch_A_url@2 A_url@3 A'.
1609 # We should *not* see a conflict during the merge, but an 'A'.
1610 # And after the merge, the status should not report any differences.
1612 expected_output = wc.State(short_wc, {
1613 "A/theta" : Item(status="A "),
1616 # As greek_state is rooted at / instead of /A (our merge target), we
1617 # need a sub-tree of it rather than straight copy.
1618 expected_disk = svntest.main.greek_state.subtree("A")
1619 expected_disk.add({
1620 "" : Item(props={SVN_PROP_MERGEINFO : '/A:2-3'}),
1621 "theta" : Item(theta_contents,
1622 props={"svn:mime-type" : "application/octet-stream"}),
1624 expected_status = svntest.actions.get_virginal_state(short_wc, 1)
1625 expected_status.add({
1626 "A/theta" : Item(status=" ", wc_rev=3),
1628 expected_status.tweak("A", status=" M")
1629 expected_status.remove("") # top-level of the WC
1630 expected_status.remove("iota")
1631 expected_skip = wc.State("", { })
1633 os.chdir(svntest.main.work_dir)
1634 # If we merge into short_wc alone, theta appears at the WC root,
1635 # which is in the wrong location -- append "/A" to stay on target.
1636 svntest.actions.run_and_verify_merge2(short_wc + "/A", "2", "3",
1637 branch_A_url, A_url,
1638 expected_output,
1639 expected_disk,
1640 expected_status,
1641 expected_skip,
1642 None, None, None, None, None,
1645 #----------------------------------------------------------------------
1646 # Regression test for Issue #1297:
1647 # A merge that creates a new file followed by an immediate diff
1648 # The diff should succeed.
1650 def merge_in_new_file_and_diff(sbox):
1651 "diff after merge that creates a new file"
1653 sbox.build()
1654 wc_dir = sbox.wc_dir
1656 trunk_url = sbox.repo_url + '/A/B/E'
1658 # Create a branch
1659 svntest.actions.run_and_verify_svn(None, None, [], 'cp',
1660 trunk_url,
1661 sbox.repo_url + '/branch',
1662 '-m', "Creating the Branch")
1664 # Update to revision 2.
1665 svntest.actions.run_and_verify_svn(None, None, [],
1666 'update', wc_dir)
1668 new_file_path = os.path.join(wc_dir, 'A', 'B', 'E', 'newfile')
1669 svntest.main.file_write(new_file_path, "newfile\n")
1671 # Add the new file, and commit revision 3.
1672 svntest.actions.run_and_verify_svn(None, None, [], "add", new_file_path)
1673 svntest.actions.run_and_verify_svn(None, None, [],
1674 'ci', '-m',
1675 "Changing the trunk.", wc_dir)
1677 # Search for the comment entitled "The Merge Kluge" elsewhere in
1678 # this file, to understand why we shorten and chdir() below.
1679 branch_path = os.path.join(wc_dir, "branch")
1680 short_branch_path = shorten_path_kludge(branch_path)
1682 # Merge our addition into the branch.
1683 expected_output = svntest.wc.State(short_branch_path, {
1684 'newfile' : Item(status='A '),
1686 expected_disk = wc.State('', {
1687 'alpha' : Item("This is the file 'alpha'.\n"),
1688 'beta' : Item("This is the file 'beta'.\n"),
1689 'newfile' : Item("newfile\n"),
1691 expected_status = wc.State(short_branch_path, {
1692 '' : Item(status=' M', wc_rev=2),
1693 'alpha' : Item(status=' ', wc_rev=2),
1694 'beta' : Item(status=' ', wc_rev=2),
1695 'newfile' : Item(status='A ', wc_rev='-', copied='+')
1697 expected_skip = wc.State('', { })
1699 saved_cwd = os.getcwd()
1701 os.chdir(svntest.main.work_dir)
1702 svntest.actions.run_and_verify_merge(short_branch_path,
1703 '1', 'HEAD', trunk_url,
1704 expected_output,
1705 expected_disk,
1706 expected_status,
1707 expected_skip)
1709 os.chdir(saved_cwd)
1711 # Finally, run diff. This diff produces no output!
1712 expected_output = [
1713 "\n",
1714 "Property changes on: " + branch_path + "\n",
1715 "___________________________________________________________________\n",
1716 "Added: " + SVN_PROP_MERGEINFO + "\n",
1717 " Merged /A/B/E:r2-3\n",
1718 "\n", ]
1719 svntest.actions.run_and_verify_svn(None, expected_output, [], 'diff',
1720 branch_path)
1723 #----------------------------------------------------------------------
1725 # Issue #1425: 'svn merge' should skip over any unversioned obstructions.
1727 def merge_skips_obstructions(sbox):
1728 "merge should skip over unversioned obstructions"
1730 sbox.build()
1731 wc_dir = sbox.wc_dir
1733 C_path = os.path.join(wc_dir, 'A', 'C')
1734 F_path = os.path.join(wc_dir, 'A', 'B', 'F')
1735 F_url = sbox.repo_url + '/A/B/F'
1737 Q_path = os.path.join(F_path, 'Q')
1738 foo_path = os.path.join(F_path, 'foo')
1739 bar_path = os.path.join(F_path, 'Q', 'bar')
1741 svntest.main.run_svn(None, 'mkdir', Q_path)
1742 svntest.main.file_append(foo_path, "foo")
1743 svntest.main.file_append(bar_path, "bar")
1744 svntest.main.run_svn(None, 'add', foo_path, bar_path)
1746 expected_output = wc.State(wc_dir, {
1747 'A/B/F/Q' : Item(verb='Adding'),
1748 'A/B/F/Q/bar' : Item(verb='Adding'),
1749 'A/B/F/foo' : Item(verb='Adding'),
1751 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
1752 expected_status.add({
1753 'A/B/F/Q' : Item(status=' ', wc_rev=2),
1754 'A/B/F/Q/bar' : Item(status=' ', wc_rev=2),
1755 'A/B/F/foo' : Item(status=' ', wc_rev=2),
1757 svntest.actions.run_and_verify_commit(wc_dir,
1758 expected_output,
1759 expected_status,
1760 None,
1761 wc_dir)
1763 pre_merge_status = expected_status
1765 # Revision 2 now has A/B/F/foo, A/B/F/Q, A/B/F/Q/bar. Let's merge
1766 # those 'F' changes into empty dir 'C'. But first, create an
1767 # unversioned 'foo' within C, and make sure 'svn merge' doesn't
1768 # error when the addition of foo is obstructed.
1770 # Search for the comment entitled "The Merge Kluge" elsewhere in
1771 # this file, to understand why we shorten and chdir() below.
1772 short_C_path = shorten_path_kludge(C_path)
1774 expected_output = wc.State(short_C_path, {
1775 'Q' : Item(status='A '),
1776 'Q/bar' : Item(status='A '),
1778 expected_disk = wc.State('', {
1779 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B/F:2'}),
1780 'Q' : Item(),
1781 'Q/bar' : Item("bar"),
1782 'foo' : Item("foo"),
1784 expected_status = wc.State(short_C_path, {
1785 '' : Item(status=' M', wc_rev=1),
1786 'Q' : Item(status='A ', wc_rev='-', copied='+'),
1787 'Q/bar' : Item(status='A ', wc_rev='-', copied='+'),
1789 expected_skip = wc.State(short_C_path, {
1790 'foo' : Item(),
1792 # Unversioned:
1793 svntest.main.file_append(os.path.join(C_path, "foo"), "foo")
1795 saved_cwd = os.getcwd()
1797 os.chdir(svntest.main.work_dir)
1798 svntest.actions.run_and_verify_merge(short_C_path, '1', '2', F_url,
1799 expected_output,
1800 expected_disk,
1801 expected_status,
1802 expected_skip,
1803 None, None, None, None, None,
1804 1, 0)
1806 os.chdir(saved_cwd)
1808 # Revert the local mods, and this time make "Q" obstructed. An
1809 # unversioned file called "Q" will obstruct the adding of the
1810 # directory of the same name.
1812 svntest.actions.run_and_verify_svn(None, None, [],
1813 'revert', '-R', wc_dir)
1814 os.unlink(os.path.join(C_path, "foo"))
1815 svntest.main.safe_rmtree(os.path.join(C_path, "Q"))
1816 svntest.main.file_append(os.path.join(C_path, "Q"), "foo") # unversioned
1817 svntest.actions.run_and_verify_status(wc_dir, pre_merge_status)
1819 # Search for the comment entitled "The Merge Kluge" elsewhere in
1820 # this file, to understand why we use short_C_path and chdir() below.
1821 expected_output = wc.State(short_C_path, {
1822 'foo' : Item(status='A '),
1824 expected_disk = wc.State('', {
1825 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B/F:2'}),
1826 'Q' : Item("foo"),
1827 'foo' : Item("foo"),
1829 expected_status = wc.State(short_C_path, {
1830 '' : Item(status=' M', wc_rev=1),
1831 'foo' : Item(status='A ', wc_rev='-', copied='+'),
1833 expected_skip = wc.State(short_C_path, {
1834 'Q' : Item(),
1835 'Q/bar' : Item(),
1838 saved_cwd = os.getcwd()
1840 os.chdir(svntest.main.work_dir)
1841 svntest.actions.run_and_verify_merge(short_C_path, '1', '2', F_url,
1842 expected_output,
1843 expected_disk,
1844 expected_status,
1845 expected_skip,
1846 None, None, None, None, None,
1847 1, 0)
1849 os.chdir(saved_cwd)
1851 # Revert the local mods, and commit the deletion of iota and A/D/G. (r3)
1852 os.unlink(os.path.join(C_path, "foo"))
1853 svntest.actions.run_and_verify_svn(None, None, [], 'revert', '-R', wc_dir)
1854 svntest.actions.run_and_verify_status(wc_dir, pre_merge_status)
1856 iota_path = os.path.join(wc_dir, 'iota')
1857 G_path = os.path.join(wc_dir, 'A', 'D', 'G')
1858 svntest.actions.run_and_verify_svn(None, None, [], 'rm', iota_path, G_path)
1860 expected_output = wc.State(wc_dir, {
1861 'A/D/G' : Item(verb='Deleting'),
1862 'iota' : Item(verb='Deleting'),
1864 expected_status = pre_merge_status
1865 expected_status.remove('iota', 'A/D/G', 'A/D/G/pi', 'A/D/G/rho', 'A/D/G/tau')
1866 svntest.actions.run_and_verify_commit(wc_dir,
1867 expected_output,
1868 expected_status,
1869 None, wc_dir)
1871 # Now create unversioned iota and A/D/G, try running a merge -r2:3.
1872 # The merge process should skip over these targets, since they're
1873 # unversioned.
1875 # Search for the comment entitled "The Merge Kluge" elsewhere in
1876 # this file, to understand why we shorten and chdir() below.
1877 short_wc_dir = shorten_path_kludge(wc_dir)
1879 svntest.main.file_append(iota_path, "foo") # unversioned
1880 os.mkdir(G_path) # unversioned
1882 expected_output = wc.State(short_wc_dir, { })
1883 expected_disk = svntest.main.greek_state.copy()
1884 expected_disk.remove('A/D/G/pi', 'A/D/G/rho', 'A/D/G/tau')
1885 expected_disk.add({
1886 '' : Item(props={SVN_PROP_MERGEINFO : '/:3'}),
1887 'A/B/F/Q' : Item(),
1888 'A/B/F/Q/bar' : Item("bar"),
1889 'A/B/F/foo' : Item("foo"),
1890 'iota' : Item("foo"),
1891 'A/C/Q' : Item("foo"),
1893 # No-op merge still sets mergeinfo
1894 expected_status.tweak('', status=' M')
1895 expected_skip = wc.State(short_wc_dir, {
1896 'A/D/G' : Item(),
1897 'iota' : Item(),
1900 saved_cwd = os.getcwd()
1902 os.chdir(svntest.main.work_dir)
1903 svntest.actions.run_and_verify_merge(short_wc_dir, '2', '3',
1904 sbox.repo_url,
1905 expected_output,
1906 expected_disk,
1907 expected_status.copy(short_wc_dir),
1908 expected_skip,
1909 None, None, None, None, None,
1910 1, 0)
1912 os.chdir(saved_cwd)
1914 # Revert the local mods, and commit a change to A/B/lambda (r4), and then
1915 # commit the deletion of the same file. (r5)
1916 os.unlink(iota_path)
1917 svntest.main.safe_rmtree(G_path)
1918 svntest.actions.run_and_verify_svn(None, None, [], 'revert', '-R', wc_dir)
1919 expected_status.tweak('', status=' ')
1920 svntest.actions.run_and_verify_status(wc_dir, expected_status)
1922 lambda_path = os.path.join(wc_dir, 'A', 'B', 'lambda')
1923 svntest.main.file_append(lambda_path, "more text")
1924 expected_output = wc.State(wc_dir, {
1925 'A/B/lambda' : Item(verb='Sending'),
1927 expected_status.tweak('A/B/lambda', wc_rev=4)
1928 svntest.actions.run_and_verify_commit(wc_dir,
1929 expected_output,
1930 expected_status,
1931 None, wc_dir)
1933 svntest.actions.run_and_verify_svn(None, None, [], 'rm', lambda_path)
1935 expected_output = wc.State(wc_dir, {
1936 'A/B/lambda' : Item(verb='Deleting'),
1938 expected_status.remove('A/B/lambda')
1939 svntest.actions.run_and_verify_commit(wc_dir,
1940 expected_output,
1941 expected_status,
1942 None, wc_dir)
1944 # lambda is gone, so create an unversioned lambda in its place.
1945 # Then attempt to merge -r3:4, which is a change to lambda. The merge
1946 # should simply skip the unversioned file.
1948 svntest.main.file_append(lambda_path, "foo") # unversioned
1950 # Search for the comment entitled "The Merge Kluge" elsewhere in
1951 # this file, to understand why we use short_wc_dir and chdir() below.
1952 expected_output = wc.State(short_wc_dir, { })
1953 expected_disk.add({
1954 'A/B/lambda' : Item("foo"),
1956 expected_disk.remove('A/D/G', 'iota')
1957 expected_disk.tweak('', props={SVN_PROP_MERGEINFO : '/:4'})
1958 expected_skip = wc.State(short_wc_dir, {
1959 'A/B/lambda' : Item(),
1961 # No-op merge still sets mergeinfo.
1962 expected_status_short = expected_status.copy(short_wc_dir)
1963 expected_status_short.tweak('', status=' M')
1965 saved_cwd = os.getcwd()
1967 os.chdir(svntest.main.work_dir)
1968 svntest.actions.run_and_verify_merge(short_wc_dir, '3', '4',
1969 sbox.repo_url,
1970 expected_output,
1971 expected_disk,
1972 expected_status_short,
1973 expected_skip,
1974 None, None, None, None, None,
1975 1, 0)
1977 os.chdir(saved_cwd)
1979 # OK, so let's commit the new lambda (r6), and then delete the
1980 # working file. Then re-run the -r3:4 merge, and see how svn deals
1981 # with a file being under version control, but missing.
1983 svntest.actions.run_and_verify_svn(None, None, [], 'add', lambda_path)
1985 # Mergeinfo prop changed so update to avoid out of date error.
1986 svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
1988 expected_output = wc.State(wc_dir, {
1989 '' : Item(verb='Sending'),
1990 'A/B/lambda' : Item(verb='Adding'),
1992 expected_status.tweak(wc_rev=5)
1993 expected_status.add({
1994 'A/B/lambda' : Item(wc_rev=6, status=' '),
1996 expected_status.tweak('', status=' ', wc_rev=6)
1997 svntest.actions.run_and_verify_commit(wc_dir,
1998 expected_output,
1999 expected_status,
2000 None, wc_dir)
2001 os.unlink(lambda_path)
2003 # Search for the comment entitled "The Merge Kluge" elsewhere in
2004 # this file, to understand why we use short_wc_dir and chdir() below.
2005 expected_output = wc.State(short_wc_dir, { })
2006 expected_disk.remove('A/B/lambda')
2007 expected_status.tweak('A/B/lambda', status='! ')
2008 os.chdir(svntest.main.work_dir)
2009 expected_status.tweak('', status=' ')
2010 # Why do we need to --ignore-ancestry? Because the previous merge of r4,
2011 # despite being inoperative, set mergeinfo for r4 on the WC. With the
2012 # advent of merge tracking this repeat merge attempt would not be attempted.
2013 # By using --ignore-ancestry we disregard the mergeinfo and *really* try to
2014 # merge into a missing path. This is another facet of issue #2898.
2015 svntest.actions.run_and_verify_merge(short_wc_dir, '3', '4',
2016 sbox.repo_url,
2017 expected_output,
2018 expected_disk,
2019 expected_status.copy(short_wc_dir),
2020 expected_skip,
2021 None, None, None, None, None,
2022 1, 0, '--ignore-ancestry')
2024 #----------------------------------------------------------------------
2025 # At one time, a merge that added items with the same name as missing
2026 # items would attempt to add the items and fail, leaving the working
2027 # copy locked and broken.
2029 def merge_into_missing(sbox):
2030 "merge into missing must not break working copy"
2032 sbox.build()
2033 wc_dir = sbox.wc_dir
2035 F_path = os.path.join(wc_dir, 'A', 'B', 'F')
2036 F_url = sbox.repo_url + '/A/B/F'
2037 Q_path = os.path.join(F_path, 'Q')
2038 foo_path = os.path.join(F_path, 'foo')
2040 svntest.actions.run_and_verify_svn(None, None, [], 'mkdir', Q_path)
2041 svntest.main.file_append(foo_path, "foo")
2042 svntest.actions.run_and_verify_svn(None, None, [], 'add', foo_path)
2044 expected_output = wc.State(wc_dir, {
2045 'A/B/F/Q' : Item(verb='Adding'),
2046 'A/B/F/foo' : Item(verb='Adding'),
2048 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
2049 expected_status.add({
2050 'A/B/F/Q' : Item(status=' ', wc_rev=2),
2051 'A/B/F/foo' : Item(status=' ', wc_rev=2),
2053 svntest.actions.run_and_verify_commit(wc_dir,
2054 expected_output,
2055 expected_status,
2056 None, wc_dir)
2058 R_path = os.path.join(Q_path, 'R')
2059 bar_path = os.path.join(R_path, 'bar')
2060 baz_path = os.path.join(Q_path, 'baz')
2061 svntest.actions.run_and_verify_svn(None, None, [], 'mkdir', R_path)
2062 svntest.main.file_append(bar_path, "bar")
2063 svntest.actions.run_and_verify_svn(None, None, [], 'add', bar_path)
2064 svntest.main.file_append(baz_path, "baz")
2065 svntest.actions.run_and_verify_svn(None, None, [], 'add', baz_path)
2067 expected_output = wc.State(wc_dir, {
2068 'A/B/F/Q/R' : Item(verb='Adding'),
2069 'A/B/F/Q/R/bar' : Item(verb='Adding'),
2070 'A/B/F/Q/baz' : Item(verb='Adding'),
2072 expected_status.add({
2073 'A/B/F/Q/R' : Item(status=' ', wc_rev=3),
2074 'A/B/F/Q/R/bar' : Item(status=' ', wc_rev=3),
2075 'A/B/F/Q/baz' : Item(status=' ', wc_rev=3),
2077 svntest.actions.run_and_verify_commit(wc_dir,
2078 expected_output,
2079 expected_status,
2080 None, wc_dir)
2082 os.unlink(foo_path)
2083 svntest.main.safe_rmtree(Q_path)
2085 expected_output = wc.State(F_path, {
2087 expected_disk = wc.State('', {
2089 expected_status = wc.State(F_path, {
2090 '' : Item(status=' ', wc_rev=1),
2091 'foo' : Item(status='! ', wc_rev=2),
2092 'Q' : Item(status='! ', wc_rev='?'),
2094 expected_skip = wc.State(F_path, {
2095 'Q' : Item(),
2096 'foo' : Item(),
2099 ### Need to real and dry-run separately since real merge notifies Q
2100 ### twice!
2101 svntest.actions.run_and_verify_merge(F_path, '1', '2', F_url,
2102 expected_output,
2103 expected_disk,
2104 expected_status,
2105 expected_skip,
2106 None, None, None, None, None,
2107 0, 0, '--dry-run')
2109 expected_status.tweak('foo', status='!M')
2110 expected_status.tweak('', status=' M')
2111 svntest.actions.run_and_verify_merge(F_path, '1', '2', F_url,
2112 expected_output,
2113 expected_disk,
2114 expected_status,
2115 expected_skip,
2116 None, None, None, None, None,
2117 0, 0)
2119 # This merge fails when it attempts to descend into the missing
2120 # directory. That's OK, there is no real need to support merge into
2121 # an incomplete working copy, so long as when it fails it doesn't
2122 # break the working copy.
2123 svntest.main.run_svn('Working copy not locked',
2124 'merge', '-r1:3', '--dry-run', F_url, F_path)
2126 svntest.main.run_svn('Working copy not locked',
2127 'merge', '-r1:3', F_url, F_path)
2129 # Check working copy is not locked.
2130 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
2131 expected_status.add({
2132 'A/B/F' : Item(status=' M', wc_rev=1),
2133 'A/B/F/foo' : Item(status='!M', wc_rev=2),
2134 'A/B/F/Q' : Item(status='! ', wc_rev='?'),
2136 svntest.actions.run_and_verify_status(wc_dir, expected_status)
2138 #----------------------------------------------------------------------
2139 # A test for issue 1738
2141 def dry_run_adds_file_with_prop(sbox):
2142 "merge --dry-run adding a new file with props"
2144 sbox.build()
2145 wc_dir = sbox.wc_dir
2147 # Commit a new file which has a property.
2148 zig_path = os.path.join(wc_dir, 'A', 'B', 'E', 'zig')
2149 svntest.main.file_append(zig_path, "zig contents")
2150 svntest.actions.run_and_verify_svn(None, None, [], 'add', zig_path)
2151 svntest.actions.run_and_verify_svn(None, None, [],
2152 'propset', 'foo', 'foo_val',
2153 zig_path)
2155 expected_output = wc.State(wc_dir, {
2156 'A/B/E/zig' : Item(verb='Adding'),
2158 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
2159 expected_status.add({
2160 'A/B/E/zig' : Item(status=' ', wc_rev=2),
2162 svntest.actions.run_and_verify_commit(wc_dir,
2163 expected_output,
2164 expected_status,
2165 None, wc_dir)
2167 # Do a regular merge of that change into a different dir.
2168 F_path = os.path.join(wc_dir, 'A', 'B', 'F')
2169 E_url = sbox.repo_url + '/A/B/E'
2171 # Search for the comment entitled "The Merge Kluge" elsewhere in
2172 # this file, to understand why we shorten and chdir() below.
2173 short_F_path = shorten_path_kludge(F_path)
2175 expected_output = wc.State(short_F_path, {
2176 'zig' : Item(status='A '),
2178 expected_disk = wc.State('', {
2179 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B/E:2'}),
2180 'zig' : Item("zig contents", {'foo':'foo_val'}),
2182 expected_skip = wc.State('', { })
2183 expected_status = None # status is optional
2185 os.chdir(svntest.main.work_dir)
2186 svntest.actions.run_and_verify_merge(short_F_path, '1', '2', E_url,
2187 expected_output,
2188 expected_disk,
2189 expected_status,
2190 expected_skip,
2191 None, None, None, None, None,
2192 1, # please check props
2193 1) # and do a dry-run also)
2195 #----------------------------------------------------------------------
2197 # Regression test for issue #1673
2198 # Merge a binary file from two URL with a common ancestry
2200 def merge_binary_with_common_ancestry(sbox):
2201 "merge binary files with common ancestry"
2203 sbox.build()
2204 wc_dir = sbox.wc_dir
2206 # Create the common ancestry path
2207 I_path = os.path.join(wc_dir, 'I')
2208 svntest.main.run_svn(None, 'mkdir', I_path)
2210 # Add a binary file to the common ancestry path
2211 theta_contents = svntest.main.file_read(
2212 os.path.join(sys.path[0], "theta.bin"), 'rb')
2213 theta_I_path = os.path.join(I_path, 'theta')
2214 svntest.main.file_write(theta_I_path, theta_contents)
2215 svntest.main.run_svn(None, 'add', theta_I_path)
2216 svntest.main.run_svn(None, 'propset', 'svn:mime-type',
2217 'application/octet-stream', theta_I_path)
2219 # Commit the ancestry
2220 expected_output = wc.State(wc_dir, {
2221 'I' : Item(verb='Adding'),
2222 'I/theta' : Item(verb='Adding (bin)'),
2225 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
2226 expected_status.add({
2227 'I' : Item(status=' ', wc_rev=2),
2228 'I/theta' : Item(status=' ', wc_rev=2),
2231 svntest.actions.run_and_verify_commit(wc_dir,
2232 expected_output, expected_status,
2233 None,
2234 wc_dir)
2236 # Create the first branch
2237 J_path = os.path.join(wc_dir, 'J')
2238 svntest.main.run_svn(None, 'copy', I_path, J_path)
2240 # Commit the first branch
2241 expected_output = wc.State(wc_dir, {
2242 'J' : Item(verb='Adding'),
2245 expected_status.add({
2246 'J' : Item(status=' ', wc_rev=3),
2247 'J/theta' : Item(status=' ', wc_rev=3),
2250 svntest.actions.run_and_verify_commit(wc_dir,
2251 expected_output, expected_status,
2252 None,
2253 wc_dir)
2255 # Create the path where the files will be merged
2256 K_path = os.path.join(wc_dir, 'K')
2257 svntest.main.run_svn(None, 'mkdir', K_path)
2259 # Commit the new path
2260 expected_output = wc.State(wc_dir, {
2261 'K' : Item(verb='Adding'),
2264 expected_status.add({
2265 'K' : Item(status=' ', wc_rev=4),
2268 svntest.actions.run_and_verify_commit(wc_dir,
2269 expected_output, expected_status,
2270 None,
2271 wc_dir)
2273 # Copy 'I/theta' to 'K/'. This file will be merged later.
2274 theta_K_path = os.path.join(K_path, 'theta')
2275 svntest.main.run_svn(None, 'copy', theta_I_path, theta_K_path)
2277 # Commit the new file
2278 expected_output = wc.State(wc_dir, {
2279 'K/theta' : Item(verb='Adding (bin)'),
2282 expected_status.add({
2283 'K/theta' : Item(status=' ', wc_rev=5),
2286 svntest.actions.run_and_verify_commit(wc_dir,
2287 expected_output, expected_status,
2288 None,
2289 wc_dir)
2291 # Modify the original ancestry 'I/theta'
2292 svntest.main.file_append(theta_I_path, "some extra junk")
2294 # Commit the modification
2295 expected_output = wc.State(wc_dir, {
2296 'I/theta' : Item(verb='Sending'),
2299 expected_status.tweak('I/theta', wc_rev=6)
2301 svntest.actions.run_and_verify_commit(wc_dir,
2302 expected_output, expected_status,
2303 None,
2304 wc_dir)
2306 # Create the second branch from the modified ancestry
2307 L_path = os.path.join(wc_dir, 'L')
2308 svntest.main.run_svn(None, 'copy', I_path, L_path)
2310 # Commit the second branch
2311 expected_output = wc.State(wc_dir, {
2312 'L' : Item(verb='Adding'),
2313 'L/theta' : Item(verb='Adding (bin)'),
2316 expected_status.add({
2317 'L' : Item(status=' ', wc_rev=7),
2318 'L/theta' : Item(status=' ', wc_rev=7),
2321 svntest.actions.run_and_verify_commit(wc_dir,
2322 expected_output, expected_status,
2323 None,
2324 wc_dir)
2326 # Now merge first ('J/') and second ('L/') branches into 'K/'
2327 saved_cwd = os.getcwd()
2329 os.chdir(K_path)
2330 theta_J_url = sbox.repo_url + '/J/theta'
2331 theta_L_url = sbox.repo_url + '/L/theta'
2332 svntest.actions.run_and_verify_svn(None,
2333 expected_merge_output(None,
2334 'U theta\n'),
2336 'merge', theta_J_url, theta_L_url)
2337 os.chdir(saved_cwd)
2339 expected_status.tweak('K/theta', status='MM')
2340 svntest.actions.run_and_verify_status(wc_dir, expected_status)
2342 #----------------------------------------------------------------------
2343 # A test for issue 1905
2344 def merge_funny_chars_on_path(sbox):
2345 "merge with funny characters (issue #1905)"
2347 sbox.build()
2348 wc_dir = sbox.wc_dir
2350 # In following lists: 'd' stands for directory, 'f' for file
2351 # targets to be added by recursive add
2352 add_by_add = [
2353 ('d', 'dir_10', 'F%lename'),
2354 ('d', 'dir%20', 'F lename'),
2355 ('d', 'dir 30', 'Filename'),
2356 ('d', 'dir 40', None),
2357 ('f', 'F lename', None),
2360 # targets to be added by 'svn mkdir' + add
2361 add_by_mkdir = [
2362 ('d', 'dir_11', 'F%lename'),
2363 ('d', 'dir%21', 'Filename'),
2364 ('d', 'dir 31', 'F lename'),
2365 ('d', 'dir 41', None),
2368 for target in add_by_add:
2369 if target[0] == 'd':
2370 target_dir = os.path.join(wc_dir, 'A', 'B', 'E', target[1])
2371 os.mkdir(target_dir)
2372 if target[2]:
2373 target_path = os.path.join(wc_dir, 'A', 'B', 'E', '%s' % target[1],
2374 target[2])
2375 svntest.main.file_append(target_path, "%s/%s" % (target[1], target[2]))
2376 svntest.actions.run_and_verify_svn(None, None, [], 'add', target_dir)
2377 elif target[0] == 'f':
2378 target_path = os.path.join(wc_dir, 'A', 'B', 'E', '%s' % target[1])
2379 svntest.main.file_append(target_path, "%s" % target[1])
2380 svntest.actions.run_and_verify_svn(None, None, [], 'add', target_path)
2381 else:
2382 raise svntest.Failure
2385 for target in add_by_mkdir:
2386 if target[0] == 'd':
2387 target_dir = os.path.join(wc_dir, 'A', 'B', 'E', target[1])
2388 svntest.actions.run_and_verify_svn(None, None, [], 'mkdir', target_dir)
2389 if target[2]:
2390 target_path = os.path.join(wc_dir, 'A', 'B', 'E', '%s' % target[1],
2391 target[2])
2392 svntest.main.file_append(target_path, "%s/%s" % (target[1], target[2]))
2393 svntest.actions.run_and_verify_svn(None, None, [], 'add', target_path)
2395 expected_output_dic = {}
2396 expected_status_dic = {}
2398 for targets in add_by_add,add_by_mkdir:
2399 for target in targets:
2400 key = 'A/B/E/%s' % target[1]
2401 expected_output_dic[key] = Item(verb='Adding')
2402 expected_status_dic[key] = Item(status=' ', wc_rev=2)
2404 if target[2]:
2405 key = 'A/B/E/%s/%s' % (target[1], target[2])
2406 expected_output_dic[key] = Item(verb='Adding')
2407 expected_status_dic[key] = Item(status=' ', wc_rev=2)
2410 expected_output = wc.State(wc_dir, expected_output_dic)
2412 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
2413 expected_status.add(expected_status_dic)
2415 svntest.actions.run_and_verify_commit(wc_dir,
2416 expected_output,
2417 expected_status,
2418 None, wc_dir)
2420 # Do a regular merge of that change into a different dir.
2421 F_path = os.path.join(wc_dir, 'A', 'B', 'F')
2422 E_url = sbox.repo_url + '/A/B/E'
2424 expected_output_dic = {}
2425 expected_disk_dic = {}
2427 for targets in add_by_add,add_by_mkdir:
2428 for target in targets:
2429 key = '%s' % target[1]
2430 expected_output_dic[key] = Item(status='A ')
2431 if target[0] == 'd':
2432 expected_disk_dic[key] = Item(None, {})
2433 elif target[0] == 'f':
2434 expected_disk_dic[key] = Item("%s" % target[1], {})
2435 else:
2436 raise svntest.Failure
2437 if target[2]:
2438 key = '%s/%s' % (target[1], target[2])
2439 expected_output_dic[key] = Item(status='A ')
2440 expected_disk_dic[key] = Item('%s/%s' % (target[1], target[2]), {})
2443 # Search for the comment entitled "The Merge Kluge" elsewhere in
2444 # this file, to understand why we shorten and chdir() below.
2445 short_F_path = shorten_path_kludge(F_path)
2447 expected_output = wc.State(short_F_path, expected_output_dic)
2449 expected_disk = wc.State('', expected_disk_dic)
2450 expected_skip = wc.State('', { })
2451 expected_status = None # status is optional
2453 saved_cwd = os.getcwd()
2455 os.chdir(svntest.main.work_dir)
2456 svntest.actions.run_and_verify_merge(short_F_path, '1', '2', E_url,
2457 expected_output,
2458 expected_disk,
2459 expected_status,
2460 expected_skip,
2461 None, None, None, None, None,
2462 0, # don't check props
2463 1) # but do a dry-run
2464 os.chdir(saved_cwd)
2466 expected_output_dic = {}
2468 for targets in add_by_add,add_by_mkdir:
2469 for target in targets:
2470 key = '%s' % target[1]
2471 expected_output_dic[key] = Item(verb='Adding')
2472 if target[2]:
2473 key = '%s/%s' % (target[1], target[2])
2474 expected_output_dic[key] = Item(verb='Adding')
2476 expected_output = wc.State(F_path, expected_output_dic)
2477 expected_output.add({
2478 '' : Item(verb='Sending'),
2481 svntest.actions.run_and_verify_commit(F_path,
2482 expected_output,
2483 None,
2484 None, wc_dir)
2486 #-----------------------------------------------------------------------
2487 # Regression test for issue #2064
2489 def merge_keyword_expansions(sbox):
2490 "merge changes to keyword expansion property"
2492 sbox.build()
2494 wcpath = sbox.wc_dir
2495 tpath = os.path.join(wcpath, "t")
2496 bpath = os.path.join(wcpath, "b")
2497 t_fpath = os.path.join(tpath, 'f')
2498 b_fpath = os.path.join(bpath, 'f')
2500 os.mkdir(tpath)
2501 svntest.main.run_svn(None, "add", tpath)
2502 # Commit r2.
2503 svntest.actions.run_and_verify_svn(None, None, [],
2504 "ci", "-m", "r2", wcpath)
2506 # Copy t to b.
2507 svntest.main.run_svn(None, "cp", tpath, bpath)
2508 # Commit r3
2509 svntest.actions.run_and_verify_svn(None, None, [],
2510 "ci", "-m", "r3", wcpath)
2512 # Add a file to t.
2513 svntest.main.file_append(t_fpath, "$Revision$")
2514 svntest.actions.run_and_verify_svn(None, None, [],
2515 'add', t_fpath)
2516 # Ask for keyword expansion in the file.
2517 svntest.actions.run_and_verify_svn(None, None, [],
2518 'propset', 'svn:keywords', 'Revision',
2519 t_fpath)
2520 # Commit r4
2521 svntest.actions.run_and_verify_svn(None, None, [],
2522 'ci', '-m', 'r4', wcpath)
2524 # Update the wc before the merge.
2525 svntest.actions.run_and_verify_svn(None, None, [],
2526 'update', wcpath)
2528 expected_status = svntest.actions.get_virginal_state(wcpath, 4)
2529 expected_status.add({
2530 't' : Item(status=' ', wc_rev=4),
2531 't/f' : Item(status=' ', wc_rev=4),
2532 'b' : Item(status=' ', wc_rev=4),
2534 svntest.actions.run_and_verify_status(wcpath, expected_status)
2536 # Do the merge.
2538 # Search for the comment entitled "The Merge Kluge" elsewhere in
2539 # this file, to understand why we shorten and chdir() below.
2540 short_bpath = shorten_path_kludge(bpath)
2542 expected_output = wc.State(short_bpath, {
2543 'f' : Item(status='A '),
2545 expected_disk = wc.State('', {
2546 'f' : Item("$Revision: 4 $"),
2548 expected_status = wc.State(short_bpath, {
2549 '' : Item(status=' M', wc_rev=4),
2550 'f' : Item(status='A ', wc_rev='-', copied='+'),
2552 expected_skip = wc.State(short_bpath, { })
2554 os.chdir(svntest.main.work_dir)
2555 svntest.actions.run_and_verify_merge(short_bpath, '2', 'HEAD',
2556 sbox.repo_url + '/t',
2557 expected_output,
2558 expected_disk,
2559 expected_status,
2560 expected_skip)
2562 #----------------------------------------------------------------------
2563 def merge_prop_change_to_deleted_target(sbox):
2564 "merge prop change into deleted target"
2565 # For issue #2132.
2566 sbox.build()
2567 wc_dir = sbox.wc_dir
2569 # Add a property to alpha.
2570 alpha_path = os.path.join(wc_dir, 'A', 'B', 'E', 'alpha')
2571 svntest.actions.run_and_verify_svn(None, None, [],
2572 'propset', 'foo', 'foo_val',
2573 alpha_path)
2575 # Commit the property add as r2.
2576 expected_output = svntest.wc.State(wc_dir, {
2577 'A/B/E/alpha' : Item(verb='Sending'),
2579 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
2580 expected_status.tweak('A/B/E/alpha', wc_rev=2, status=' ')
2581 svntest.actions.run_and_verify_commit(wc_dir,
2582 expected_output, expected_status,
2583 None, wc_dir)
2584 svntest.actions.run_and_verify_svn(None, None, [],
2585 'up', wc_dir)
2587 # Remove alpha entirely.
2588 svntest.actions.run_and_verify_svn(None, None, [], 'rm', alpha_path)
2589 expected_output = wc.State(wc_dir, {
2590 'A/B/E/alpha' : Item(verb='Deleting'),
2592 expected_status.tweak(wc_rev=2)
2593 expected_status.remove('A/B/E/alpha')
2594 svntest.actions.run_and_verify_commit(wc_dir,
2595 expected_output,
2596 expected_status,
2597 None, alpha_path)
2599 # Try merging the original propset, which applies to a target that
2600 # no longer exists. The bug would only reproduce when run from
2601 # inside the wc, so we cd in there. We have to use
2602 # --ignore-ancestry here because our merge logic will otherwise
2603 # prevent a merge of changes we already have.
2604 os.chdir(wc_dir)
2605 svntest.actions.run_and_verify_svn("Merge errored unexpectedly",
2606 svntest.verify.AnyOutput, [], 'merge',
2607 '-r1:2', '--ignore-ancestry', '.')
2610 def set_up_dir_replace(sbox):
2611 """Set up the working copy for directory replace tests, creating
2612 directory 'A/B/F/foo' with files 'new file' and 'new file2' within
2613 it (r2), and merging 'foo' onto 'C' (r3), then deleting 'A/B/F/foo'
2614 (r4)."""
2616 sbox.build()
2617 wc_dir = sbox.wc_dir
2619 C_path = os.path.join(wc_dir, 'A', 'C')
2620 F_path = os.path.join(wc_dir, 'A', 'B', 'F')
2621 F_url = sbox.repo_url + '/A/B/F'
2623 foo_path = os.path.join(F_path, 'foo')
2624 new_file = os.path.join(foo_path, "new file")
2625 new_file2 = os.path.join(foo_path, "new file 2")
2627 # Make directory foo in F, and add some files within it.
2628 svntest.actions.run_and_verify_svn(None, None, [], 'mkdir', foo_path)
2629 svntest.main.file_append(new_file, "Initial text in new file.\n")
2630 svntest.main.file_append(new_file2, "Initial text in new file 2.\n")
2631 svntest.main.run_svn(None, "add", new_file)
2632 svntest.main.run_svn(None, "add", new_file2)
2634 # Commit all the new content, creating r2.
2635 expected_output = wc.State(wc_dir, {
2636 'A/B/F/foo' : Item(verb='Adding'),
2637 'A/B/F/foo/new file' : Item(verb='Adding'),
2638 'A/B/F/foo/new file 2' : Item(verb='Adding'),
2640 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
2641 expected_status.add({
2642 'A/B/F/foo' : Item(status=' ', wc_rev=2),
2643 'A/B/F/foo/new file' : Item(status=' ', wc_rev=2),
2644 'A/B/F/foo/new file 2' : Item(status=' ', wc_rev=2),
2646 svntest.actions.run_and_verify_commit(wc_dir,
2647 expected_output,
2648 expected_status,
2649 None, wc_dir)
2651 # Merge foo onto C
2652 expected_output = wc.State(C_path, {
2653 'foo' : Item(status='A '),
2654 'foo/new file' : Item(status='A '),
2655 'foo/new file 2' : Item(status='A '),
2657 expected_disk = wc.State('', {
2658 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B/F:2'}),
2659 'foo' : Item(),
2660 'foo/new file' : Item("Initial text in new file.\n"),
2661 'foo/new file 2' : Item("Initial text in new file 2.\n"),
2663 expected_status = wc.State(C_path, {
2664 '' : Item(status=' M', wc_rev=1),
2665 'foo' : Item(status='A ', wc_rev='-', copied='+'),
2666 'foo/new file' : Item(status='A ', wc_rev='-', copied='+'),
2667 'foo/new file 2' : Item(status='A ', wc_rev='-', copied='+'),
2669 expected_skip = wc.State(C_path, { })
2670 svntest.actions.run_and_verify_merge(C_path, '1', '2', F_url,
2671 expected_output,
2672 expected_disk,
2673 expected_status,
2674 expected_skip,
2675 None, None, None, None, None, 1)
2676 # Commit merge of foo onto C, creating r3.
2677 expected_output = svntest.wc.State(wc_dir, {
2678 'A/C' : Item(verb='Sending'),
2679 'A/C/foo' : Item(verb='Adding'),
2680 'A/C/foo/new file' : Item(verb='Adding'),
2681 'A/C/foo/new file 2' : Item(verb='Adding'),
2683 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
2684 expected_status.add({
2685 'A/B/F/foo' : Item(status=' ', wc_rev=2),
2686 'A/C' : Item(status=' ', wc_rev=3),
2687 'A/B/F/foo/new file' : Item(status=' ', wc_rev=2),
2688 'A/B/F/foo/new file 2' : Item(status=' ', wc_rev=2),
2689 'A/C/foo' : Item(status=' ', wc_rev=3),
2690 'A/C/foo/new file' : Item(status=' ', wc_rev=3),
2691 'A/C/foo/new file 2' : Item(status=' ', wc_rev=3),
2694 svntest.actions.run_and_verify_commit(wc_dir,
2695 expected_output,
2696 expected_status,
2697 None, wc_dir)
2699 # Delete foo on F, creating r4.
2700 svntest.actions.run_and_verify_svn(None, None, [], 'rm', foo_path)
2701 expected_output = svntest.wc.State(wc_dir, {
2702 'A/B/F/foo' : Item(verb='Deleting'),
2704 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
2705 expected_status.add({
2706 'A/C' : Item(status=' ', wc_rev=3),
2707 'A/C/foo' : Item(status=' ', wc_rev=3),
2708 'A/C/foo/new file' : Item(status=' ', wc_rev=3),
2709 'A/C/foo/new file 2' : Item(status=' ', wc_rev=3),
2711 svntest.actions.run_and_verify_commit(wc_dir,
2712 expected_output,
2713 expected_status,
2714 None, wc_dir)
2716 #----------------------------------------------------------------------
2717 # A merge that replaces a directory
2718 # Tests for Issue #2144 and Issue #2607
2720 def merge_dir_replace(sbox):
2721 "merge a replacement of a directory"
2723 set_up_dir_replace(sbox)
2724 wc_dir = sbox.wc_dir
2726 C_path = os.path.join(wc_dir, 'A', 'C')
2727 F_path = os.path.join(wc_dir, 'A', 'B', 'F')
2728 F_url = sbox.repo_url + '/A/B/F'
2729 foo_path = os.path.join(F_path, 'foo')
2730 new_file2 = os.path.join(foo_path, "new file 2")
2732 # Recreate foo in F and add a new folder and two files
2733 bar_path = os.path.join(foo_path, 'bar')
2734 foo_file = os.path.join(foo_path, "file foo")
2735 new_file3 = os.path.join(bar_path, "new file 3")
2737 # Make a couple of directories, and add some files within them.
2738 svntest.actions.run_and_verify_svn(None, None, [], 'mkdir', foo_path)
2739 svntest.actions.run_and_verify_svn(None, None, [], 'mkdir', bar_path)
2740 svntest.main.file_append(new_file3, "Initial text in new file 3.\n")
2741 svntest.main.run_svn(None, "add", new_file3)
2742 svntest.main.file_append(foo_file, "Initial text in file foo.\n")
2743 svntest.main.run_svn(None, "add", foo_file)
2745 # Commit the new content, creating r5.
2746 expected_output = wc.State(wc_dir, {
2747 'A/B/F/foo' : Item(verb='Adding'),
2748 'A/B/F/foo/file foo' : Item(verb='Adding'),
2749 'A/B/F/foo/bar' : Item(verb='Adding'),
2750 'A/B/F/foo/bar/new file 3' : Item(verb='Adding'),
2752 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
2753 expected_status.add({
2754 'A/B/F/foo' : Item(status=' ', wc_rev=5),
2755 'A/B/F/foo/file foo' : Item(status=' ', wc_rev=5),
2756 'A/B/F/foo/bar' : Item(status=' ', wc_rev=5),
2757 'A/B/F/foo/bar/new file 3' : Item(status=' ', wc_rev=5),
2758 'A/C' : Item(status=' ', wc_rev=3),
2759 'A/C/foo' : Item(status=' ', wc_rev=3),
2760 'A/C/foo/new file' : Item(status=' ', wc_rev=3),
2761 'A/C/foo/new file 2' : Item(status=' ', wc_rev=3),
2763 svntest.actions.run_and_verify_commit(wc_dir,
2764 expected_output,
2765 expected_status,
2766 None, wc_dir)
2767 # Merge replacement of foo onto C
2768 expected_output = wc.State(C_path, {
2769 'foo' : Item(status='R '),
2770 'foo/file foo' : Item(status='A '),
2771 'foo/bar' : Item(status='A '),
2772 'foo/bar/new file 3' : Item(status='A '),
2774 expected_disk = wc.State('', {
2775 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B/F:2-5'}),
2776 'foo' : Item(),
2777 'foo/file foo' : Item("Initial text in file foo.\n"),
2778 'foo/bar' : Item(),
2779 'foo/bar/new file 3' : Item("Initial text in new file 3.\n"),
2781 expected_status = wc.State(C_path, {
2782 '' : Item(status=' M', wc_rev=3),
2783 'foo' : Item(status='R ', wc_rev='-', copied='+'),
2784 'foo/new file 2' : Item(status='D ', wc_rev='-', copied='+'),
2785 'foo/file foo' : Item(status='A ', wc_rev='-', copied='+'),
2786 'foo/bar' : Item(status='A ', wc_rev='-', copied='+'),
2787 'foo/bar/new file 3' : Item(status='A ', wc_rev='-', copied='+'),
2788 'foo/new file' : Item(status='D ', wc_rev='-', copied='+'),
2790 expected_skip = wc.State(C_path, { })
2791 svntest.actions.run_and_verify_merge(C_path, '2', '5', F_url,
2792 expected_output,
2793 expected_disk,
2794 expected_status,
2795 expected_skip,
2796 None, None, None, None, None,
2798 0) # don't do a dry-run
2799 # the output differs
2801 # Commit merge of foo onto C
2802 expected_output = svntest.wc.State(wc_dir, {
2803 'A/C' : Item(verb='Sending'),
2804 'A/C/foo' : Item(verb='Replacing'),
2805 'A/C/foo/file foo' : Item(verb='Adding'),
2806 'A/C/foo/bar' : Item(verb='Adding'),
2807 'A/C/foo/bar/new file 3' : Item(verb='Adding'),
2808 'A/C/foo/new file' : Item(verb='Deleting'),
2809 'A/C/foo/new file 2' : Item(verb='Deleting'),
2811 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
2812 expected_status.add({
2813 'A/B/F/foo' : Item(status=' ', wc_rev=5),
2814 'A/B/F/foo/file foo' : Item(status=' ', wc_rev=5),
2815 'A/B/F/foo/bar' : Item(status=' ', wc_rev=5),
2816 'A/B/F/foo/bar/new file 3' : Item(status=' ', wc_rev=5),
2817 'A/C' : Item(status=' ', wc_rev=6),
2818 'A/C/foo' : Item(status=' ', wc_rev=6),
2819 'A/C/foo/file foo' : Item(status=' ', wc_rev=6),
2820 'A/C/foo/bar' : Item(status=' ', wc_rev=6),
2821 'A/C/foo/bar/new file 3' : Item(status=' ', wc_rev=6),
2823 svntest.actions.run_and_verify_commit(wc_dir,
2824 expected_output,
2825 expected_status,
2826 None, wc_dir)
2828 #----------------------------------------------------------------------
2829 # A merge that replaces a directory and one of its children
2830 # Tests for Issue #2690
2832 def merge_dir_and_file_replace(sbox):
2833 "replace both dir and one of its children"
2835 set_up_dir_replace(sbox)
2836 wc_dir = sbox.wc_dir
2838 C_path = os.path.join(wc_dir, 'A', 'C')
2839 F_path = os.path.join(wc_dir, 'A', 'B', 'F')
2840 F_url = sbox.repo_url + '/A/B/F'
2841 foo_path = os.path.join(F_path, 'foo')
2842 new_file2 = os.path.join(foo_path, "new file 2")
2844 # Recreate foo and 'new file 2' in F and add a new folder with a file
2845 bar_path = os.path.join(foo_path, 'bar')
2846 new_file3 = os.path.join(bar_path, "new file 3")
2847 svntest.actions.run_and_verify_svn(None, None, [], 'mkdir', foo_path)
2848 svntest.actions.run_and_verify_svn(None, None, [], 'mkdir', bar_path)
2849 svntest.main.file_append(new_file3, "Initial text in new file 3.\n")
2850 svntest.main.run_svn(None, "add", new_file3)
2851 svntest.main.file_append(new_file2, "New text in new file 2.\n")
2852 svntest.main.run_svn(None, "add", new_file2)
2854 expected_output = wc.State(wc_dir, {
2855 'A/B/F/foo' : Item(verb='Adding'),
2856 'A/B/F/foo/new file 2' : Item(verb='Adding'),
2857 'A/B/F/foo/bar' : Item(verb='Adding'),
2858 'A/B/F/foo/bar/new file 3' : Item(verb='Adding'),
2860 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
2861 expected_status.add({
2862 'A/B/F/foo' : Item(status=' ', wc_rev=5),
2863 'A/B/F/foo/new file 2' : Item(status=' ', wc_rev=5),
2864 'A/B/F/foo/bar' : Item(status=' ', wc_rev=5),
2865 'A/B/F/foo/bar/new file 3' : Item(status=' ', wc_rev=5),
2866 'A/C/foo' : Item(status=' ', wc_rev=3),
2867 'A/C/foo/new file' : Item(status=' ', wc_rev=3),
2868 'A/C/foo/new file 2' : Item(status=' ', wc_rev=3),
2870 svntest.actions.run_and_verify_commit(wc_dir,
2871 expected_output,
2872 expected_status,
2873 None, wc_dir)
2874 # Merge replacement of foo onto C
2875 expected_output = wc.State(C_path, {
2876 'foo' : Item(status='D '),
2877 'foo' : Item(status='A '),
2878 'foo/new file 2' : Item(status='D '),
2879 'foo/new file 2' : Item(status='A '),
2880 'foo/bar' : Item(status='A '),
2881 'foo/bar/new file 3' : Item(status='A '),
2882 'foo/new file' : Item(status='D '),
2884 expected_disk = wc.State('', {
2885 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B/F:2-5'}),
2886 'foo' : Item(),
2887 'foo/new file 2' : Item("New text in new file 2.\n"),
2888 'foo/bar' : Item(),
2889 'foo/bar/new file 3' : Item("Initial text in new file 3.\n"),
2891 expected_status = wc.State(C_path, {
2892 '' : Item(status=' ', wc_rev=1),
2893 'foo' : Item(status='R ', wc_rev='-', copied='+'),
2894 'foo/new file 2' : Item(status='R ', wc_rev='-', copied='+'),
2895 'foo/bar' : Item(status='A ', wc_rev='-', copied='+'),
2896 'foo/bar/new file 3' : Item(status='A ', wc_rev='-', copied='+'),
2897 'foo/new file' : Item(status='D ', wc_rev='-', copied='+'),
2899 expected_skip = wc.State(C_path, { })
2900 svntest.actions.run_and_verify_merge(C_path, '2', '5', F_url,
2901 expected_output,
2902 expected_disk,
2903 expected_status,
2904 expected_skip,
2905 None, None, None, None, None,
2907 0) # don't do a dry-run
2908 # the output differs
2910 # Commit merge of foo onto C
2911 expected_output = svntest.wc.State(wc_dir, {
2912 'A/C/foo' : Item(verb='Replacing'),
2913 'A/C/foo/new file 2' : Item(verb='Replacing'),
2914 'A/C/foo/new file' : Item(verb='Deleting'),
2915 'A/C/foo/bar' : Item(verb='Adding'),
2916 'A/C/foo/bar/new file 3' : Item(verb='Adding'),
2919 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
2920 expected_status.add({
2921 'A/B/F/foo' : Item(status=' ', wc_rev=5),
2922 'A/B/F/foo/new file 2' : Item(status=' ', wc_rev=5),
2923 'A/B/F/foo/bar' : Item(status=' ', wc_rev=5),
2924 'A/B/F/foo/bar/new file 3' : Item(status=' ', wc_rev=5),
2925 'A/C/foo' : Item(status=' ', wc_rev=6),
2926 'A/C/foo/new file 2' : Item(status=' ', wc_rev=6),
2927 'A/C/foo/bar' : Item(status=' ', wc_rev=6),
2928 'A/C/foo/bar/new file 3' : Item(status=' ', wc_rev=6),
2930 svntest.actions.run_and_verify_commit(wc_dir,
2931 expected_output,
2932 expected_status,
2933 None, wc_dir)
2935 #----------------------------------------------------------------------
2936 def merge_file_with_space_in_its_name(sbox):
2937 "merge a file whose name contains a space"
2938 # For issue #2144
2939 sbox.build()
2940 wc_dir = sbox.wc_dir
2941 new_file = os.path.join(wc_dir, "new file")
2943 # Make r2.
2944 svntest.main.file_append(new_file, "Initial text in the file.\n")
2945 svntest.main.run_svn(None, "add", new_file)
2946 svntest.actions.run_and_verify_svn(None, None, [],
2947 "ci", "-m", "r2", wc_dir)
2949 # Make r3.
2950 svntest.main.file_append(new_file, "Next line of text in the file.\n")
2951 svntest.actions.run_and_verify_svn(None, None, [],
2952 "ci", "-m", "r3", wc_dir)
2954 # Try to reverse merge.
2956 # The reproduction recipe requires that no explicit merge target be
2957 # passed, so we run merge from inside the wc dir where the target
2958 # file (i.e., the URL basename) lives.
2959 os.chdir(wc_dir)
2960 target_url = sbox.repo_url + '/new%20file'
2961 svntest.actions.run_and_verify_svn(None, None, [],
2962 "merge", "-r3:2", target_url)
2964 #----------------------------------------------------------------------
2965 # A merge between two branches using no revision number with the dir being
2966 # created already existing as an unversioned directory.
2967 # Tests for Issue #2222
2969 def merge_dir_branches(sbox):
2970 "merge between branches (Issue #2222)"
2972 sbox.build()
2973 wc_dir = sbox.wc_dir
2975 F_path = os.path.join(wc_dir, 'A', 'B', 'F')
2976 F_url = sbox.repo_url + '/A/B/F'
2977 C_url = sbox.repo_url + '/A/C'
2979 # Create foo in F
2980 foo_path = os.path.join(F_path, 'foo')
2981 svntest.actions.run_and_verify_svn(None, None, [], 'mkdir', foo_path)
2983 expected_output = wc.State(wc_dir, {
2984 'A/B/F/foo' : Item(verb='Adding'),
2986 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
2987 expected_status.add({
2988 'A/B/F/foo' : Item(status=' ', wc_rev=2),
2990 svntest.actions.run_and_verify_commit(wc_dir,
2991 expected_output,
2992 expected_status,
2993 None, wc_dir)
2995 # Create an unversioned foo
2996 foo_path = os.path.join(wc_dir, 'foo')
2997 os.mkdir(foo_path)
2999 # Merge from C to F onto the wc_dir
3000 # We can't use run_and_verify_merge because it doesn't support this
3001 # syntax of the merge command.
3002 ### TODO: We can use run_and_verify_merge2() here now.
3003 expected_output = expected_merge_output(None, "A " + foo_path + "\n")
3004 svntest.actions.run_and_verify_svn(None, expected_output, [],
3005 'merge', C_url, F_url, wc_dir)
3007 # Run info to check the copied rev to make sure it's right
3008 expected_output = ["Path: " + foo_path + "\n",
3009 "URL: " + sbox.repo_url + "/foo\n",
3010 "Repository Root: " + sbox.repo_url + "\n",
3011 "Revision: 2\n",
3012 "Node Kind: directory\n",
3013 "Schedule: add\n",
3014 "Copied From URL: " + F_url + "/foo\n",
3015 "Copied From Rev: 2\n", "\n"]
3016 svntest.actions.run_and_verify_svn(None, expected_output, [],
3017 'info', foo_path)
3020 #----------------------------------------------------------------------
3022 def safe_property_merge(sbox):
3023 "property merges don't overwrite existing prop-mods"
3025 sbox.build()
3026 wc_dir = sbox.wc_dir
3028 # Add a property to two files and a directory, commit as r2.
3029 alpha_path = os.path.join(wc_dir, 'A', 'B', 'E', 'alpha')
3030 beta_path = os.path.join(wc_dir, 'A', 'B', 'E', 'beta')
3031 E_path = os.path.join(wc_dir, 'A', 'B', 'E')
3033 svntest.actions.run_and_verify_svn(None, None, [],
3034 'propset', 'foo', 'foo_val',
3035 alpha_path, beta_path)
3036 svntest.actions.run_and_verify_svn(None, None, [],
3037 'propset', 'foo', 'foo_val',
3038 E_path)
3040 expected_output = svntest.wc.State(wc_dir, {
3041 'A/B/E' : Item(verb='Sending'),
3042 'A/B/E/alpha' : Item(verb='Sending'),
3043 'A/B/E/beta' : Item(verb='Sending'),
3045 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
3046 expected_status.tweak('A/B/E', 'A/B/E/alpha', 'A/B/E/beta',
3047 wc_rev=2, status=' ')
3048 svntest.actions.run_and_verify_commit(wc_dir,
3049 expected_output, expected_status,
3050 None, wc_dir)
3051 svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
3053 # Copy B to B2 as rev 3 (making a branch)
3054 B_url = sbox.repo_url + '/A/B'
3055 B2_url = sbox.repo_url + '/A/B2'
3057 svntest.actions.run_and_verify_svn(None, None, [],
3058 'copy', '-m', 'copy B to B2',
3059 B_url, B2_url)
3060 svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
3062 # Change the properties underneath B again, and commit as r4
3063 svntest.actions.run_and_verify_svn(None, None, [],
3064 'propset', 'foo', 'foo_val2',
3065 alpha_path)
3066 svntest.actions.run_and_verify_svn(None, None, [],
3067 'propdel', 'foo',
3068 beta_path)
3069 svntest.actions.run_and_verify_svn(None, None, [],
3070 'propset', 'foo', 'foo_val2',
3071 E_path)
3072 expected_output = svntest.wc.State(wc_dir, {
3073 'A/B/E' : Item(verb='Sending'),
3074 'A/B/E/alpha' : Item(verb='Sending'),
3075 'A/B/E/beta' : Item(verb='Sending'),
3077 svntest.actions.run_and_verify_commit(wc_dir,
3078 expected_output, None,
3079 None, wc_dir)
3080 svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
3082 # Make local propchanges to E, alpha and beta in the branch.
3083 alpha_path2 = os.path.join(wc_dir, 'A', 'B2', 'E', 'alpha')
3084 beta_path2 = os.path.join(wc_dir, 'A', 'B2', 'E', 'beta')
3085 E_path2 = os.path.join(wc_dir, 'A', 'B2', 'E')
3087 svntest.actions.run_and_verify_svn(None, None, [],
3088 'propset', 'foo', 'branchval',
3089 alpha_path2, beta_path2)
3090 svntest.actions.run_and_verify_svn(None, None, [],
3091 'propset', 'foo', 'branchval',
3092 E_path2)
3094 # Now merge the recent B change to the branch. Because we already
3095 # have local propmods, we should get property conflicts.
3096 B2_path = os.path.join(wc_dir, 'A', 'B2')
3098 expected_output = wc.State(B2_path, {
3099 'E' : Item(status=' C'),
3100 'E/alpha' : Item(status=' C'),
3101 'E/beta' : Item(status=' C'),
3104 expected_disk = wc.State('', {
3105 '' : Item(props={SVN_PROP_MERGEINFO : "/A/B:4"}),
3106 'E' : Item(),
3107 'E/alpha' : Item("This is the file 'alpha'.\n"),
3108 'E/beta' : Item("This is the file 'beta'.\n"),
3109 'F' : Item(),
3110 'lambda' : Item("This is the file 'lambda'.\n"),
3112 expected_disk.tweak('E', 'E/alpha', 'E/beta',
3113 props={'foo' : 'branchval'}) # local mods still present
3115 expected_status = wc.State(B2_path, {
3116 '' : Item(status=' M'),
3117 'E' : Item(status=' C'),
3118 'E/alpha' : Item(status=' C'),
3119 'E/beta' : Item(status=' C'),
3120 'F' : Item(status=' '),
3121 'lambda' : Item(status=' '),
3123 expected_status.tweak(wc_rev=4)
3125 expected_skip = wc.State('', { })
3127 # should have 3 'prej' files left behind, describing prop conflicts:
3128 extra_files = ['alpha.*\.prej', 'beta.*\.prej', 'dir_conflicts.*\.prej']
3130 svntest.actions.run_and_verify_merge(B2_path, '3', '4', B_url,
3131 expected_output,
3132 expected_disk,
3133 expected_status,
3134 expected_skip,
3135 None, # expected error string
3136 svntest.tree.detect_conflict_files,
3137 extra_files,
3138 None, None, # no B singleton handler
3139 1, # check props
3140 0) # dry_run
3142 #----------------------------------------------------------------------
3144 # Test for issue 2035, whereby 'svn merge' wouldn't always mark
3145 # property conflicts when it should.
3147 def property_merge_from_branch(sbox):
3148 "property merge conflict even without local mods"
3150 sbox.build()
3151 wc_dir = sbox.wc_dir
3153 # Add a property to a file and a directory, commit as r2.
3154 alpha_path = os.path.join(wc_dir, 'A', 'B', 'E', 'alpha')
3155 E_path = os.path.join(wc_dir, 'A', 'B', 'E')
3157 svntest.actions.run_and_verify_svn(None, None, [],
3158 'propset', 'foo', 'foo_val',
3159 alpha_path)
3160 svntest.actions.run_and_verify_svn(None, None, [],
3161 'propset', 'foo', 'foo_val',
3162 E_path)
3164 expected_output = svntest.wc.State(wc_dir, {
3165 'A/B/E' : Item(verb='Sending'),
3166 'A/B/E/alpha' : Item(verb='Sending'),
3168 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
3169 expected_status.tweak('A/B/E', 'A/B/E/alpha', wc_rev=2, status=' ')
3170 svntest.actions.run_and_verify_commit(wc_dir,
3171 expected_output, expected_status,
3172 None, wc_dir)
3173 svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
3175 # Copy B to B2 as rev 3 (making a branch)
3176 B_url = sbox.repo_url + '/A/B'
3177 B2_url = sbox.repo_url + '/A/B2'
3179 svntest.actions.run_and_verify_svn(None, None, [],
3180 'copy', '-m', 'copy B to B2',
3181 B_url, B2_url)
3182 svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
3184 # Change the properties underneath B again, and commit as r4
3185 svntest.actions.run_and_verify_svn(None, None, [],
3186 'propset', 'foo', 'foo_val2',
3187 alpha_path)
3188 svntest.actions.run_and_verify_svn(None, None, [],
3189 'propset', 'foo', 'foo_val2',
3190 E_path)
3191 expected_output = svntest.wc.State(wc_dir, {
3192 'A/B/E' : Item(verb='Sending'),
3193 'A/B/E/alpha' : Item(verb='Sending'),
3195 svntest.actions.run_and_verify_commit(wc_dir,
3196 expected_output, None,
3197 None, wc_dir)
3198 svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
3200 # Make different propchanges changes to the B2 branch and commit as r5.
3201 alpha_path2 = os.path.join(wc_dir, 'A', 'B2', 'E', 'alpha')
3202 E_path2 = os.path.join(wc_dir, 'A', 'B2', 'E')
3204 svntest.actions.run_and_verify_svn(None, None, [],
3205 'propset', 'foo', 'branchval',
3206 alpha_path2)
3207 svntest.actions.run_and_verify_svn(None, None, [],
3208 'propset', 'foo', 'branchval',
3209 E_path2)
3210 expected_output = svntest.wc.State(wc_dir, {
3211 'A/B2/E' : Item(verb='Sending'),
3212 'A/B2/E/alpha' : Item(verb='Sending'),
3214 svntest.actions.run_and_verify_commit(wc_dir,
3215 expected_output, None,
3216 None, wc_dir)
3217 svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
3219 # Now merge the recent B change to the branch. There are no local
3220 # mods anywhere, but we should still get property conflicts anyway!
3221 B2_path = os.path.join(wc_dir, 'A', 'B2')
3223 expected_output = wc.State(B2_path, {
3224 'E' : Item(status=' C'),
3225 'E/alpha' : Item(status=' C'),
3228 expected_disk = wc.State('', {
3229 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B:4'}),
3230 'E' : Item(),
3231 'E/alpha' : Item("This is the file 'alpha'.\n"),
3232 'E/beta' : Item("This is the file 'beta'.\n"),
3233 'F' : Item(),
3234 'lambda' : Item("This is the file 'lambda'.\n"),
3236 expected_disk.tweak('E', 'E/alpha',
3237 props={'foo' : 'branchval'})
3239 expected_status = wc.State(B2_path, {
3240 '' : Item(status=' M'),
3241 'E' : Item(status=' C'),
3242 'E/alpha' : Item(status=' C'),
3243 'E/beta' : Item(status=' '),
3244 'F' : Item(status=' '),
3245 'lambda' : Item(status=' '),
3247 expected_status.tweak(wc_rev=5)
3249 expected_skip = wc.State('', { })
3251 # should have 2 'prej' files left behind, describing prop conflicts:
3252 extra_files = ['alpha.*\.prej', 'dir_conflicts.*\.prej']
3254 svntest.actions.run_and_verify_merge(B2_path, '3', '4', B_url,
3255 expected_output,
3256 expected_disk,
3257 expected_status,
3258 expected_skip,
3259 None, # expected error string
3260 svntest.tree.detect_conflict_files,
3261 extra_files,
3262 None, None, # no B singleton handler
3263 1, # check props
3264 0) # dry_run
3266 #----------------------------------------------------------------------
3268 # Another test for issue 2035, whereby sometimes 'svn merge' marked
3269 # property conflicts when it shouldn't!
3271 def property_merge_undo_redo(sbox):
3272 "undo, then redo a property merge"
3274 sbox.build()
3275 wc_dir = sbox.wc_dir
3277 # Add a property to a file, commit as r2.
3278 alpha_path = os.path.join(wc_dir, 'A', 'B', 'E', 'alpha')
3279 svntest.actions.run_and_verify_svn(None, None, [],
3280 'propset', 'foo', 'foo_val',
3281 alpha_path)
3283 expected_output = svntest.wc.State(wc_dir, {
3284 'A/B/E/alpha' : Item(verb='Sending'),
3286 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
3287 expected_status.tweak('A/B/E/alpha', wc_rev=2, status=' ')
3288 svntest.actions.run_and_verify_commit(wc_dir,
3289 expected_output, expected_status,
3290 None, wc_dir)
3291 svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
3293 # Use 'svn merge' to undo the commit. ('svn merge -r2:1')
3294 # Result should be a single local-prop-mod.
3295 expected_output = wc.State(wc_dir, {'A/B/E/alpha' : Item(status=' U'), })
3297 expected_disk = svntest.main.greek_state.copy()
3299 expected_status = svntest.actions.get_virginal_state(wc_dir, 2)
3300 expected_status.tweak('A/B/E/alpha', status=' M')
3302 expected_skip = wc.State('', { })
3304 svntest.actions.run_and_verify_merge(wc_dir, '2', '1',
3305 sbox.repo_url,
3306 expected_output,
3307 expected_disk,
3308 expected_status,
3309 expected_skip,
3310 None, # expected error string
3311 None, None, # no A singleton handler
3312 None, None, # no B singleton handler
3313 1, # check props
3314 0) # dry_run
3316 # Change mind, re-apply the change ('svn merge -r1:2').
3317 # This should merge cleanly into existing prop-mod, status shows nothing.
3318 expected_output = wc.State(wc_dir, {'A/B/E/alpha' : Item(status=' C'), })
3320 expected_disk = svntest.main.greek_state.copy()
3321 expected_disk.add({'A/B/E/alpha.prej'
3322 : Item("Trying to create property 'foo' with value 'foo_val',\n"
3323 + "but it has been locally deleted.\n")})
3325 expected_status = svntest.actions.get_virginal_state(wc_dir, 2)
3326 expected_status.tweak('A/B/E/alpha', status=' C')
3328 expected_skip = wc.State('', { })
3330 # Re-merge r1. We have to use --ignore-ancestry here. Otherwise
3331 # the merge logic will claim we already have this change (because it
3332 # was unable to record the previous undoing merge).
3333 svntest.actions.run_and_verify_merge(wc_dir, '1', '2',
3334 sbox.repo_url,
3335 expected_output,
3336 expected_disk,
3337 expected_status,
3338 expected_skip,
3339 None, # expected error string
3340 None, None, # no A singleton handler
3341 None, None, # no B singleton handler
3342 1, # check props
3343 0, # dry_run
3344 '--ignore-ancestry')
3348 #----------------------------------------------------------------------
3349 def cherry_pick_text_conflict(sbox):
3350 "cherry-pick a dependent change, get conflict"
3352 sbox.build()
3353 wc_dir = sbox.wc_dir
3355 A_path = os.path.join(wc_dir, 'A')
3356 A_url = sbox.repo_url + '/A'
3357 mu_path = os.path.join(A_path, 'mu')
3358 branch_A_url = sbox.repo_url + '/copy-of-A'
3359 branch_mu_path = os.path.join(wc_dir, 'copy-of-A', 'mu')
3361 # Create a branch of A.
3362 svntest.actions.run_and_verify_svn(None, None, [], 'cp',
3363 A_url, branch_A_url,
3364 '-m', "Creating copy-of-A")
3366 # Update to get the branch.
3367 svntest.actions.run_and_verify_svn(None, None, [],
3368 'update', wc_dir)
3370 # Change mu's text on the branch, producing r3 through r6.
3371 for rev in range(3, 7):
3372 svntest.main.file_append(branch_mu_path, ("r%d\n" % rev) * 3)
3373 svntest.actions.run_and_verify_svn(None, None, [],
3374 'ci', '-m',
3375 'Add lines to mu in r%d.' % rev, wc_dir)
3377 # Mark r5 as merged into trunk, to create disparate revision ranges
3378 # which need to be merged.
3379 svntest.actions.run_and_verify_svn(None, [], [],
3380 'merge', '-c5', '--record-only',
3381 branch_A_url, A_path)
3384 # Try to merge r4:6 into trunk, without r3. It should fail.
3385 expected_output = wc.State(A_path, {
3386 'mu' : Item(status='C '),
3388 expected_disk = wc.State('', {
3389 'mu' : Item("This is the file 'mu'.\n"
3390 + make_conflict_marker_text("r3\n" * 3, "r4\n" * 3, 4)),
3391 'B' : Item(),
3392 'B/lambda' : Item("This is the file 'lambda'.\n"),
3393 'B/E' : Item(),
3394 'B/E/alpha' : Item("This is the file 'alpha'.\n"),
3395 'B/E/beta' : Item("This is the file 'beta'.\n"),
3396 'B/F' : Item(),
3397 'C' : Item(),
3398 'D' : Item(),
3399 'D/gamma' : Item("This is the file 'gamma'.\n"),
3400 'D/H' : Item(),
3401 'D/H/chi' : Item("This is the file 'chi'.\n"),
3402 'D/H/psi' : Item("This is the file 'psi'.\n"),
3403 'D/H/omega' : Item("This is the file 'omega'.\n"),
3404 'D/G' : Item(),
3405 'D/G/pi' : Item("This is the file 'pi'.\n"),
3406 'D/G/rho' : Item("This is the file 'rho'.\n"),
3407 'D/G/tau' : Item("This is the file 'tau'.\n"),
3409 expected_status = wc.State(A_path, {
3410 '' : Item(status=' M'),
3411 'mu' : Item(status='C '),
3412 'B' : Item(status=' '),
3413 'B/lambda' : Item(status=' '),
3414 'B/E' : Item(status=' '),
3415 'B/E/alpha' : Item(status=' '),
3416 'B/E/beta' : Item(status=' '),
3417 'B/F' : Item(status=' '),
3418 'C' : Item(status=' '),
3419 'D' : Item(status=' '),
3420 'D/gamma' : Item(status=' '),
3421 'D/H' : Item(status=' '),
3422 'D/H/chi' : Item(status=' '),
3423 'D/H/psi' : Item(status=' '),
3424 'D/H/omega' : Item(status=' '),
3425 'D/G' : Item(status=' '),
3426 'D/G/pi' : Item(status=' '),
3427 'D/G/rho' : Item(status=' '),
3428 'D/G/tau' : Item(status=' '),
3430 expected_status.tweak(wc_rev=2)
3431 expected_skip = wc.State('', { })
3432 expected_error = "conflicts were produced while merging r3:4"
3433 svntest.actions.run_and_verify_merge(A_path, '3', '6', branch_A_url,
3434 expected_output,
3435 expected_disk,
3436 expected_status,
3437 expected_skip,
3438 expected_error,
3439 svntest.tree.detect_conflict_files,
3440 ["mu\.working",
3441 "mu\.merge-right\.r4",
3442 "mu\.merge-left\.r3"],
3443 None, None, # no singleton handler
3444 0, # don't check props
3445 0) # not a dry_run
3449 # Test for issue 2135
3450 def merge_file_replace(sbox):
3451 "merge a replacement of a file"
3453 sbox.build()
3454 wc_dir = sbox.wc_dir
3456 # File scheduled for deletion
3457 rho_path = os.path.join(wc_dir, 'A', 'D', 'G', 'rho')
3458 svntest.actions.run_and_verify_svn(None, None, [], 'rm', rho_path)
3460 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
3461 expected_status.tweak('A/D/G/rho', status='D ')
3462 svntest.actions.run_and_verify_status(wc_dir, expected_status)
3464 expected_output = svntest.wc.State(wc_dir, {
3465 'A/D/G/rho': Item(verb='Deleting'),
3468 expected_status.remove('A/D/G/rho')
3470 # Commit rev 2
3471 svntest.actions.run_and_verify_commit(wc_dir,
3472 expected_output,
3473 expected_status,
3474 None, wc_dir)
3475 # Create and add a new file.
3476 svntest.main.file_write(rho_path, "new rho\n")
3477 svntest.actions.run_and_verify_svn(None, None, [], 'add', rho_path)
3479 # Commit revsion 3
3480 expected_status.add({
3481 'A/D/G/rho' : Item(status='A ', wc_rev='0')
3483 svntest.actions.run_and_verify_status(wc_dir, expected_status)
3484 expected_output = svntest.wc.State(wc_dir, {
3485 'A/D/G/rho': Item(verb='Adding'),
3488 svntest.actions.run_and_verify_commit(wc_dir,
3489 expected_output,
3490 None,
3491 None, wc_dir)
3493 # Update working copy
3494 expected_output = svntest.wc.State(wc_dir, {})
3495 expected_disk = svntest.main.greek_state.copy()
3496 expected_disk.tweak('A/D/G/rho', contents='new rho\n' )
3497 expected_status.tweak(wc_rev='3')
3498 expected_status.tweak('A/D/G/rho', status=' ')
3500 svntest.actions.run_and_verify_update(wc_dir,
3501 expected_output,
3502 expected_disk,
3503 expected_status)
3505 # merge changes from r3:1
3506 expected_output = svntest.wc.State(wc_dir, {
3507 'A/D/G/rho': Item(status='R ')
3509 expected_status.tweak('A/D/G/rho', status='R ', copied='+', wc_rev='-')
3510 expected_skip = wc.State(wc_dir, { })
3511 expected_disk.tweak('A/D/G/rho', contents="This is the file 'rho'.\n")
3512 svntest.actions.run_and_verify_merge(wc_dir, '3', '1',
3513 sbox.repo_url,
3514 expected_output,
3515 expected_disk,
3516 expected_status,
3517 expected_skip)
3519 # Now commit merged wc
3520 expected_output = svntest.wc.State(wc_dir, {
3521 'A/D/G/rho': Item(verb='Replacing'),
3523 expected_status.tweak('A/D/G/rho', status=' ', copied=None, wc_rev='4')
3524 svntest.actions.run_and_verify_commit(wc_dir,
3525 expected_output,
3526 expected_status,
3527 None,
3528 wc_dir)
3529 # Test for issue 2522
3530 # Same as merge_file_replace, but without update before merge.
3531 def merge_file_replace_to_mixed_rev_wc(sbox):
3532 "merge a replacement of a file to mixed rev wc"
3534 sbox.build()
3535 wc_dir = sbox.wc_dir
3537 # File scheduled for deletion
3538 rho_path = os.path.join(wc_dir, 'A', 'D', 'G', 'rho')
3539 svntest.actions.run_and_verify_svn(None, None, [], 'rm', rho_path)
3541 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
3542 expected_status.tweak('A/D/G/rho', status='D ')
3543 svntest.actions.run_and_verify_status(wc_dir, expected_status)
3545 expected_output = svntest.wc.State(wc_dir, {
3546 'A/D/G/rho': Item(verb='Deleting'),
3549 expected_status.remove('A/D/G/rho')
3551 # Commit rev 2
3552 svntest.actions.run_and_verify_commit(wc_dir,
3553 expected_output,
3554 expected_status,
3555 None, wc_dir)
3557 # Update working copy
3558 expected_disk = svntest.main.greek_state.copy()
3559 expected_disk.remove('A/D/G/rho' )
3560 expected_output = svntest.wc.State(wc_dir, {})
3561 expected_status.tweak(wc_rev='2')
3563 svntest.actions.run_and_verify_update(wc_dir,
3564 expected_output,
3565 expected_disk,
3566 expected_status)
3568 # Create and add a new file.
3569 svntest.main.file_write(rho_path, "new rho\n")
3570 svntest.actions.run_and_verify_svn(None, None, [], 'add', rho_path)
3572 # Commit revsion 3
3573 expected_status.add({
3574 'A/D/G/rho' : Item(status='A ', wc_rev='0')
3576 svntest.actions.run_and_verify_status(wc_dir, expected_status)
3577 expected_output = svntest.wc.State(wc_dir, {
3578 'A/D/G/rho': Item(verb='Adding'),
3581 expected_disk.add({'A/D/G/rho' : Item(contents='new rho\n')} )
3582 expected_status.tweak(wc_rev='2')
3583 expected_status.tweak('A/D/G/rho', status=' ', wc_rev='3')
3585 svntest.actions.run_and_verify_commit(wc_dir,
3586 expected_output,
3587 expected_status,
3588 None, wc_dir)
3591 # merge changes from r3:1
3592 expected_output = svntest.wc.State(wc_dir, {
3593 'A/D/G/rho': Item(status='R ')
3595 expected_status.tweak('A/D/G/rho', status='R ', copied='+', wc_rev='-')
3596 expected_skip = wc.State(wc_dir, { })
3597 expected_disk.tweak('A/D/G/rho', contents="This is the file 'rho'.\n")
3598 svntest.actions.run_and_verify_merge(wc_dir, '3', '1',
3599 sbox.repo_url,
3600 expected_output,
3601 expected_disk,
3602 expected_status,
3603 expected_skip)
3605 # At this point WC is broken, because file rho has invalid revision
3606 # Try to update
3607 expected_output = svntest.wc.State(wc_dir, {})
3608 expected_status.tweak(wc_rev='3')
3609 expected_status.tweak('A/D/G/rho', status='R ', copied='+', wc_rev='-')
3610 svntest.actions.run_and_verify_update(wc_dir,
3611 expected_output,
3612 expected_disk,
3613 expected_status)
3615 # Now commit merged wc
3616 expected_output = svntest.wc.State(wc_dir, {
3617 'A/D/G/rho': Item(verb='Replacing'),
3619 expected_status.tweak('A/D/G/rho', status=' ', copied=None, wc_rev='4')
3620 svntest.actions.run_and_verify_commit(wc_dir,
3621 expected_output,
3622 expected_status,
3623 None,
3624 wc_dir)
3626 # use -x -w option for ignoring whitespace during merge
3627 def merge_ignore_whitespace(sbox):
3628 "ignore whitespace when merging"
3630 sbox.build()
3631 wc_dir = sbox.wc_dir
3633 # commit base version of iota
3634 file_name = "iota"
3635 file_path = os.path.join(wc_dir, file_name)
3636 file_url = sbox.repo_url + '/iota'
3638 svntest.main.file_write(file_path,
3639 "Aa\n"
3640 "Bb\n"
3641 "Cc\n")
3642 expected_output = svntest.wc.State(wc_dir, {
3643 'iota' : Item(verb='Sending'),
3645 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
3646 None, None, wc_dir)
3648 # change the file, mostly whitespace changes + an extra line
3649 svntest.main.file_write(file_path, "A a\nBb \n Cc\nNew line in iota\n")
3650 expected_output = wc.State(wc_dir, { file_name : Item(verb='Sending'), })
3651 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
3652 expected_status.tweak(file_name, wc_rev=3)
3653 svntest.actions.run_and_verify_commit(wc_dir,
3654 expected_output,
3655 expected_status,
3656 None,
3657 wc_dir)
3659 # Backdate iota to revision 2, so we can merge in the rev 3 changes.
3660 svntest.actions.run_and_verify_svn(None, None, [],
3661 'up', '-r', '2', file_path)
3662 # Make some local whitespace changes, these should not conflict
3663 # with the remote whitespace changes as both will be ignored.
3664 svntest.main.file_write(file_path, " Aa\nB b\nC c\n")
3666 # Lines changed only by whitespaces - both in local or remote -
3667 # should be ignored
3668 expected_output = wc.State(sbox.wc_dir, { file_name : Item(status='G ') })
3669 expected_disk = svntest.main.greek_state.copy()
3670 expected_disk.tweak(file_name,
3671 contents=" Aa\n"
3672 "B b\n"
3673 "C c\n"
3674 "New line in iota\n")
3675 expected_status = svntest.actions.get_virginal_state(sbox.wc_dir, 1)
3676 expected_status.tweak('', status=' M', wc_rev=1)
3677 expected_status.tweak(file_name, status='M ', wc_rev=2)
3678 expected_skip = wc.State('', { })
3680 svntest.actions.run_and_verify_merge(sbox.wc_dir, '2', '3',
3681 sbox.repo_url,
3682 expected_output,
3683 expected_disk,
3684 expected_status,
3685 expected_skip,
3686 None, None, None, None, None,
3687 0, 0,
3688 '-x', '-w')
3690 # use -x --ignore-eol-style option for ignoring eolstyle during merge
3691 def merge_ignore_eolstyle(sbox):
3692 "ignore eolstyle when merging"
3694 sbox.build()
3695 wc_dir = sbox.wc_dir
3697 # commit base version of iota
3698 file_name = "iota"
3699 file_path = os.path.join(wc_dir, file_name)
3700 file_url = sbox.repo_url + '/iota'
3702 svntest.main.file_write(file_path,
3703 "Aa\r\n"
3704 "Bb\r\n"
3705 "Cc\r\n",
3706 "wb")
3707 expected_output = svntest.wc.State(wc_dir, {
3708 'iota' : Item(verb='Sending'),
3710 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
3711 None, None, wc_dir)
3713 # change the file, mostly eol changes + an extra line
3714 svntest.main.file_write(file_path,
3715 "Aa\r"
3716 "Bb\n"
3717 "Cc\r"
3718 "New line in iota\n",
3719 "wb")
3720 expected_output = wc.State(wc_dir, { file_name : Item(verb='Sending'), })
3721 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
3722 expected_status.tweak(file_name, wc_rev=3)
3723 svntest.actions.run_and_verify_commit(wc_dir,
3724 expected_output,
3725 expected_status,
3726 None,
3727 wc_dir)
3729 # Backdate iota to revision 2, so we can merge in the rev 3 changes.
3730 svntest.actions.run_and_verify_svn(None, None, [],
3731 'up', '-r', '2', file_path)
3732 # Make some local eol changes, these should not conflict
3733 # with the remote eol changes as both will be ignored.
3734 svntest.main.file_write(file_path,
3735 "Aa\n"
3736 "Bb\r"
3737 "Cc\n",
3738 "wb")
3740 # Lines changed only by eolstyle - both in local or remote -
3741 # should be ignored
3742 expected_output = wc.State(sbox.wc_dir, { file_name : Item(status='G ') })
3743 expected_disk = svntest.main.greek_state.copy()
3744 expected_disk.tweak(file_name,
3745 contents="Aa\n"
3746 "Bb\r"
3747 "Cc\n"
3748 "New line in iota\n")
3749 expected_status = svntest.actions.get_virginal_state(sbox.wc_dir, 1)
3750 expected_status.tweak('', status=' M')
3751 expected_status.tweak(file_name, status='M ', wc_rev=2)
3752 expected_skip = wc.State('', { })
3754 svntest.actions.run_and_verify_merge(sbox.wc_dir, '2', '3',
3755 sbox.repo_url,
3756 expected_output,
3757 expected_disk,
3758 expected_status,
3759 expected_skip,
3760 None, None, None, None, None,
3761 0, 0,
3762 '-x', '--ignore-eol-style')
3764 #----------------------------------------------------------------------
3765 # Issue 2584
3766 def merge_add_over_versioned_file_conflicts(sbox):
3767 "conflict from merge of add over versioned file"
3769 sbox.build()
3770 wc_dir = sbox.wc_dir
3772 E_path = os.path.join(wc_dir, 'A', 'B', 'E')
3773 alpha_path = os.path.join(E_path, 'alpha')
3774 new_alpha_path = os.path.join(wc_dir, 'A', 'C', 'alpha')
3776 # Create a new "alpha" file, with enough differences to cause a conflict.
3777 ### TODO: Leverage inject_conflict_into_wc() here.
3778 svntest.main.file_write(new_alpha_path, 'new alpha content\n')
3780 # Add and commit the new "alpha" file, creating revision 2.
3781 svntest.main.run_svn(None, "add", new_alpha_path)
3783 expected_output = svntest.wc.State(wc_dir, {
3784 'A/C/alpha' : Item(verb='Adding'),
3786 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
3787 expected_status.add({
3788 'A/C/alpha' : Item(status=' ', wc_rev=2),
3790 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
3791 expected_status, None,
3792 wc_dir)
3794 # Search for the comment entitled "The Merge Kluge" elsewhere in
3795 # this file, to understand why we shorten and chdir() below.
3796 short_E_path = shorten_path_kludge(E_path)
3798 # Merge changes from r1:2 into our pre-existing "alpha" file,
3799 # causing a conflict.
3800 expected_output = wc.State(short_E_path, {
3801 'alpha' : Item(status='C '),
3803 expected_disk = wc.State('', {
3804 'alpha' : Item(""), # state filled in below
3805 'beta' : Item("This is the file 'beta'.\n"),
3807 expected_status = wc.State(short_E_path, {
3808 '' : Item(status=' M', wc_rev=1),
3809 'alpha' : Item(status='C ', wc_rev=1),
3810 'beta' : Item(status=' ', wc_rev=1),
3813 inject_conflict_into_expected_state('alpha', expected_disk, expected_status,
3814 "This is the file 'alpha'.\n",
3815 "new alpha content\n", 2)
3816 expected_skip = wc.State(short_E_path, { })
3818 os.chdir(svntest.main.work_dir)
3819 svntest.actions.run_and_verify_merge(short_E_path, '1', '2',
3820 sbox.repo_url + \
3821 '/A/C',
3822 expected_output,
3823 expected_disk,
3824 expected_status,
3825 expected_skip,
3826 None,
3827 svntest.tree.detect_conflict_files,
3828 ["alpha\.working",
3829 "alpha\.merge-right\.r2",
3830 "alpha\.merge-left\.r0"])
3832 #----------------------------------------------------------------------
3833 # eol-style handling during merge with conflicts, scenario 1:
3834 # when a merge creates a conflict on a file, make sure the file and files
3835 # r<left>, r<right> and .mine are in the eol-style defined for that file.
3837 # This test for 'svn update' can be found in update_tests.py as
3838 # conflict_markers_matching_eol.
3839 def merge_conflict_markers_matching_eol(sbox):
3840 "conflict markers should match the file's eol style"
3842 sbox.build()
3843 wc_dir = sbox.wc_dir
3844 filecount = 1
3846 mu_path = os.path.join(wc_dir, 'A', 'mu')
3848 if os.name == 'nt':
3849 crlf = '\n'
3850 else:
3851 crlf = '\r\n'
3853 # Checkout a second working copy
3854 wc_backup = sbox.add_wc_path('backup')
3855 svntest.actions.run_and_verify_svn(None, None, [], 'checkout',
3856 sbox.repo_url, wc_backup)
3858 # set starting revision
3859 cur_rev = 1
3861 expected_disk = svntest.main.greek_state.copy()
3862 expected_status = svntest.actions.get_virginal_state(wc_dir, cur_rev)
3863 expected_backup_status = svntest.actions.get_virginal_state(wc_backup,
3864 cur_rev)
3866 path_backup = os.path.join(wc_backup, 'A', 'mu')
3868 # do the test for each eol-style
3869 for eol, eolchar in zip(['CRLF', 'CR', 'native', 'LF'],
3870 [crlf, '\015', '\n', '\012']):
3871 # rewrite file mu and set the eol-style property.
3872 svntest.main.file_write(mu_path, "This is the file 'mu'."+ eolchar, 'wb')
3873 svntest.main.run_svn(None, 'propset', 'svn:eol-style', eol, mu_path)
3875 expected_disk.add({
3876 'A/mu' : Item("This is the file 'mu'." + eolchar)
3878 expected_output = svntest.wc.State(wc_dir, {
3879 'A/mu' : Item(verb='Sending'),
3881 expected_status.tweak(wc_rev = cur_rev)
3882 expected_status.add({
3883 'A/mu' : Item(status=' ', wc_rev = cur_rev + 1),
3886 # Commit the original change and note the 'base' revision number
3887 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
3888 expected_status, None,
3889 wc_dir)
3890 cur_rev = cur_rev + 1
3891 base_rev = cur_rev
3893 svntest.main.run_svn(None, 'update', wc_backup)
3895 # Make a local mod to mu
3896 svntest.main.file_append(mu_path,
3897 'Original appended text for mu' + eolchar)
3899 # Commit the original change and note the 'theirs' revision number
3900 svntest.main.run_svn(None, 'commit', '-m', 'test log', wc_dir)
3901 cur_rev = cur_rev + 1
3902 theirs_rev = cur_rev
3904 # Make a local mod to mu, will conflict with the previous change
3905 svntest.main.file_append(path_backup,
3906 'Conflicting appended text for mu' + eolchar)
3908 # Create expected output tree for an update of the wc_backup.
3909 expected_backup_output = svntest.wc.State(wc_backup, {
3910 'A/mu' : Item(status='C '),
3913 # Create expected disk tree for the update.
3914 expected_backup_disk = expected_disk.copy()
3916 # verify content of resulting conflicted file
3917 expected_backup_disk.add({
3918 'A/mu' : Item(contents= "This is the file 'mu'." + eolchar +
3919 "<<<<<<< .working" + eolchar +
3920 "Conflicting appended text for mu" + eolchar +
3921 "=======" + eolchar +
3922 "Original appended text for mu" + eolchar +
3923 ">>>>>>> .merge-right.r" + str(cur_rev) + eolchar),
3925 # verify content of base(left) file
3926 expected_backup_disk.add({
3927 'A/mu.merge-left.r' + str(base_rev) :
3928 Item(contents= "This is the file 'mu'." + eolchar)
3930 # verify content of theirs(right) file
3931 expected_backup_disk.add({
3932 'A/mu.merge-right.r' + str(theirs_rev) :
3933 Item(contents= "This is the file 'mu'." + eolchar +
3934 "Original appended text for mu" + eolchar)
3936 # verify content of mine file
3937 expected_backup_disk.add({
3938 'A/mu.working' : Item(contents= "This is the file 'mu'." +
3939 eolchar +
3940 "Conflicting appended text for mu" + eolchar)
3943 # Create expected status tree for the update.
3944 expected_backup_status.add({
3945 'A/mu' : Item(status=' ', wc_rev=cur_rev),
3947 expected_backup_status.tweak('A/mu', status='C ')
3948 expected_backup_status.tweak(wc_rev = cur_rev - 1)
3949 expected_backup_status.tweak('', status= ' M')
3951 expected_backup_skip = wc.State('', { })
3953 svntest.actions.run_and_verify_merge(wc_backup, cur_rev - 1, cur_rev,
3954 sbox.repo_url,
3955 expected_backup_output,
3956 expected_backup_disk,
3957 expected_backup_status,
3958 expected_backup_skip)
3960 # cleanup for next run
3961 svntest.main.run_svn(None, 'revert', '-R', wc_backup)
3962 svntest.main.run_svn(None, 'update', wc_dir)
3964 # eol-style handling during merge, scenario 2:
3965 # if part of that merge is a propchange (add, change, delete) of
3966 # svn:eol-style, make sure the correct eol-style is applied before
3967 # calculating the merge (and conflicts if any)
3969 # This test for 'svn update' can be found in update_tests.py as
3970 # update_eolstyle_handling.
3971 def merge_eolstyle_handling(sbox):
3972 "handle eol-style propchange during merge"
3974 sbox.build()
3975 wc_dir = sbox.wc_dir
3977 mu_path = os.path.join(wc_dir, 'A', 'mu')
3979 if os.name == 'nt':
3980 crlf = '\n'
3981 else:
3982 crlf = '\r\n'
3984 # Checkout a second working copy
3985 wc_backup = sbox.add_wc_path('backup')
3986 svntest.actions.run_and_verify_svn(None, None, [], 'checkout',
3987 sbox.repo_url, wc_backup)
3988 path_backup = os.path.join(wc_backup, 'A', 'mu')
3990 # Test 1: add the eol-style property and commit, change mu in the second
3991 # working copy and merge the last revision; there should be no conflict!
3992 svntest.main.run_svn(None, 'propset', 'svn:eol-style', "CRLF", mu_path)
3993 svntest.main.run_svn(None,
3994 'commit', '-m', 'set eol-style property', wc_dir)
3996 svntest.main.file_append_binary(path_backup, 'Added new line of text.\012')
3998 expected_backup_disk = svntest.main.greek_state.copy()
3999 expected_backup_disk.tweak(
4000 'A/mu', contents= "This is the file 'mu'." + crlf +
4001 "Added new line of text." + crlf)
4002 expected_backup_output = svntest.wc.State(wc_backup, {
4003 'A/mu' : Item(status='GU'),
4005 expected_backup_status = svntest.actions.get_virginal_state(wc_backup, 1)
4006 expected_backup_status.tweak('', status=' M')
4007 expected_backup_status.tweak('A/mu', status='MM')
4009 expected_backup_skip = wc.State('', { })
4011 svntest.actions.run_and_verify_merge(wc_backup, '1', '2', sbox.repo_url,
4012 expected_backup_output,
4013 expected_backup_disk,
4014 expected_backup_status,
4015 expected_backup_skip)
4017 # Test 2: now change the eol-style property to another value and commit,
4018 # merge this revision in the still changed mu in the second working copy;
4019 # there should be a property conflict! (Since this will overwrite a
4020 # local change to a versioned resource.)
4021 svntest.main.run_svn(None, 'propset', 'svn:eol-style', "CR", mu_path)
4022 svntest.main.run_svn(None,
4023 'commit', '-m', 'set eol-style property', wc_dir)
4025 expected_backup_disk = svntest.main.greek_state.copy()
4026 expected_backup_disk.add({
4027 'A/mu' : Item(contents= "This is the file 'mu'." + crlf +
4028 "Added new line of text." + crlf)
4030 expected_backup_disk.add({
4031 'A/mu.prej' : Item("Trying to change property 'svn:eol-style' from 'CRLF'"
4032 + " to 'CR',\nbut property has been locally added with"
4033 + " value 'CRLF'\n")})
4034 expected_backup_output = svntest.wc.State(wc_backup, {
4035 'A/mu' : Item(status='GC'),
4037 expected_backup_status = svntest.actions.get_virginal_state(wc_backup, 1)
4038 expected_backup_status.tweak('', status=' M')
4039 expected_backup_status.tweak('A/mu', status='MC')
4040 svntest.actions.run_and_verify_merge(wc_backup, '2', '3', sbox.repo_url,
4041 expected_backup_output,
4042 expected_backup_disk,
4043 expected_backup_status,
4044 expected_backup_skip)
4046 # Test 3: now delete the eol-style property and commit, merge this revision
4047 # in the still changed mu in the second working copy; there should be no
4048 # conflict! (after marking mu resolved from Test 2)
4049 # EOL of mu should be unchanged (=CRLF).
4050 svntest.main.run_svn(None, 'propdel', 'svn:eol-style', mu_path)
4051 svntest.main.run_svn(None,
4052 'commit', '-m', 'del eol-style property', wc_dir)
4054 expected_backup_disk = svntest.main.greek_state.copy()
4055 expected_backup_disk.add({
4056 'A/mu' : Item(contents= "This is the file 'mu'." + crlf +
4057 "Added new line of text." + crlf)
4059 expected_backup_output = svntest.wc.State(wc_backup, {
4060 'A/mu' : Item(status=' G'),
4062 expected_backup_status = svntest.actions.get_virginal_state(wc_backup, 1)
4063 expected_backup_status.tweak('', status=' M')
4064 expected_backup_status.tweak('A/mu', status='M ')
4065 svntest.main.run_svn(None, 'resolved', path_backup)
4066 svntest.actions.run_and_verify_merge(wc_backup, '3', '4', sbox.repo_url,
4067 expected_backup_output,
4068 expected_backup_disk,
4069 expected_backup_status,
4070 expected_backup_skip)
4072 def create_deep_trees(wc_dir):
4073 """Create A/B/F/E by moving A/B/E to A/B/F/E.
4074 Copy A/B/F/E to A/B/F/E1.
4075 Copy A/B to A/copy-of-B, and return the expected status.
4076 At the end of this function WC would be at r4"""
4078 A_path = os.path.join(wc_dir, 'A')
4079 A_B_path = os.path.join(A_path, 'B')
4080 A_B_E_path = os.path.join(A_B_path, 'E')
4081 A_B_F_path = os.path.join(A_B_path, 'F')
4082 A_B_F_E_path = os.path.join(A_B_F_path, 'E')
4083 A_B_F_E1_path = os.path.join(A_B_F_path, 'E1')
4085 # Deepen the directory structure we're working with by moving E to
4086 # underneath F and committing, creating revision 2.
4087 svntest.main.run_svn(None, 'mv', A_B_E_path, A_B_F_path)
4089 # A/B/F/E now has empty mergeinfo
4091 expected_output = wc.State(wc_dir, {
4092 'A/B/E' : Item(verb='Deleting'),
4093 'A/B/F/E' : Item(verb='Adding')
4095 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
4096 expected_status.remove('A/B/E', 'A/B/E/alpha', 'A/B/E/beta')
4097 expected_status.add({
4098 'A/B/F/E' : Item(status=' ', wc_rev=2),
4099 'A/B/F/E/alpha' : Item(status=' ', wc_rev=2),
4100 'A/B/F/E/beta' : Item(status=' ', wc_rev=2),
4102 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
4103 expected_status, None,
4104 wc_dir)
4106 svntest.main.run_svn(None, 'cp', A_B_F_E_path, A_B_F_E1_path)
4108 # A/B/F/E1 now has empty mergeinfo
4110 expected_output = wc.State(wc_dir, {
4111 'A/B/F/E1' : Item(verb='Adding')
4113 expected_status.add({
4114 'A/B/F/E1' : Item(status=' ', wc_rev=3),
4115 'A/B/F/E1/alpha' : Item(status=' ', wc_rev=3),
4116 'A/B/F/E1/beta' : Item(status=' ', wc_rev=3),
4118 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
4119 expected_status, None,
4120 wc_dir)
4122 # Bring the entire WC up to date with rev 3.
4123 svntest.actions.run_and_verify_svn(None, None, [], 'update', wc_dir)
4124 expected_status.tweak(wc_rev=3)
4126 # Copy B and commit, creating revision 4.
4127 copy_of_B_path = os.path.join(A_path, 'copy-of-B')
4128 svntest.main.run_svn(None, "cp", A_B_path, copy_of_B_path)
4130 # A/copy-of-B, A/copy-of-B/F/E, and A/copy-of-B/F/E1 now have empty mergeinfo
4132 expected_output = svntest.wc.State(wc_dir, {
4133 'A/copy-of-B' : Item(verb='Adding'),
4135 expected_status.add({
4136 'A/copy-of-B' : Item(status=' ', wc_rev=4),
4137 'A/copy-of-B/F' : Item(status=' ', wc_rev=4),
4138 'A/copy-of-B/F/E' : Item(status=' ', wc_rev=4),
4139 'A/copy-of-B/F/E/alpha' : Item(status=' ', wc_rev=4),
4140 'A/copy-of-B/F/E/beta' : Item(status=' ', wc_rev=4),
4141 'A/copy-of-B/F/E1' : Item(status=' ', wc_rev=4),
4142 'A/copy-of-B/F/E1/alpha' : Item(status=' ', wc_rev=4),
4143 'A/copy-of-B/F/E1/beta' : Item(status=' ', wc_rev=4),
4144 'A/copy-of-B/lambda' : Item(status=' ', wc_rev=4),
4146 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
4147 expected_status, None,
4148 wc_dir)
4150 # pre-update, empty mergeinfo can be found on:
4152 # /A/B/F/E
4153 # /A/B/F/E1
4154 # /A/copy-of-B
4155 # /A/copy-of-B/F/E
4156 # /A/copy-of-B/F/E1
4158 expected_disk = svntest.main.greek_state.copy()
4159 expected_disk.remove('A/B/E', 'A/B/E/alpha', 'A/B/E/beta')
4160 expected_disk.add({
4161 'A/B/F/E' : Item(props={SVN_PROP_MERGEINFO : ''}),
4162 'A/B/F/E/alpha' : Item(contents="This is the file 'alpha'.\n"),
4163 'A/B/F/E/beta' : Item(contents="This is the file 'beta'.\n"),
4164 'A/B/F/E1' : Item(props={SVN_PROP_MERGEINFO : ''}),
4165 'A/B/F/E1/alpha' : Item(contents="This is the file 'alpha'.\n"),
4166 'A/B/F/E1/beta' : Item(contents="This is the file 'beta'.\n"),
4167 'A/copy-of-B' : Item(props={SVN_PROP_MERGEINFO : ''}),
4168 'A/copy-of-B/F' : Item(props={}),
4169 'A/copy-of-B/F/E' : Item(props={SVN_PROP_MERGEINFO : ''}),
4170 'A/copy-of-B/F/E/alpha' : Item(contents="This is the file 'alpha'.\n"),
4171 'A/copy-of-B/F/E/beta' : Item(contents="This is the file 'beta'.\n"),
4172 'A/copy-of-B/F/E1' : Item(props={SVN_PROP_MERGEINFO : ''}),
4173 'A/copy-of-B/F/E1/alpha' : Item(contents="This is the file 'alpha'.\n"),
4174 'A/copy-of-B/F/E1/beta' : Item(contents="This is the file 'beta'.\n"),
4175 'A/copy-of-B/lambda' : Item(contents="This is the file 'lambda'.\n"),
4177 svntest.actions.verify_disk(wc_dir, expected_disk,
4178 None, None, None, None, 1)
4180 # Bring the entire WC up to date with rev 4.
4181 svntest.actions.run_and_verify_svn(None, None, [], 'update', wc_dir)
4183 svntest.actions.verify_disk(wc_dir, expected_disk,
4184 None, None, None, None, 1)
4186 expected_status.tweak(wc_rev=4)
4187 expected_disk.tweak('A/copy-of-B/F/E', 'A/copy-of-B/F/E1', status=' M')
4188 return expected_status
4190 def avoid_repeated_merge_using_inherited_merge_info(sbox):
4191 "use inherited mergeinfo to avoid repeated merge"
4193 sbox.build()
4194 wc_dir = sbox.wc_dir
4196 A_path = os.path.join(wc_dir, 'A')
4197 A_B_path = os.path.join(A_path, 'B')
4198 A_B_E_path = os.path.join(A_B_path, 'E')
4199 A_B_F_path = os.path.join(A_B_path, 'F')
4200 copy_of_B_path = os.path.join(A_path, 'copy-of-B')
4202 # Create a deeper directory structure.
4203 expected_status = create_deep_trees(wc_dir)
4205 # Edit alpha and commit it, creating revision 5.
4206 alpha_path = os.path.join(A_B_F_path, 'E', 'alpha')
4207 new_content_for_alpha = 'new content to alpha\n'
4208 svntest.main.file_write(alpha_path, new_content_for_alpha)
4209 expected_output = svntest.wc.State(wc_dir, {
4210 'A/B/F/E/alpha' : Item(verb='Sending'),
4212 expected_status.tweak('A/B/F/E/alpha', wc_rev=5)
4213 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
4214 expected_status, None,
4215 wc_dir)
4217 # Search for the comment entitled "The Merge Kluge" elsewhere in
4218 # this file, to understand why we shorten and chdir() below.
4219 short_copy_of_B_path = shorten_path_kludge(copy_of_B_path)
4221 # Bring the entire WC up to date with rev 5.
4222 svntest.actions.run_and_verify_svn(None, None, [], 'update', wc_dir)
4224 # Merge changes from rev 5 of B (to alpha) into copy_of_B.
4225 # A_COPY/copy_of_B/F/E and A_COPY/copy_of_B/F/E1 both exist in the merge
4226 # source at r5, so their empty mergeinfo should be updted with r5, which
4227 # then should elide to A_COPY/copy_of_B leaving no mergeinfo on either.
4228 expected_output = wc.State(short_copy_of_B_path, {
4229 'F/E/alpha' : Item(status='U '),
4231 expected_status = wc.State(short_copy_of_B_path, {
4232 '' : Item(status=' M', wc_rev=5),
4233 'F/E' : Item(status=' M', wc_rev=5),
4234 'F/E/alpha' : Item(status='M ', wc_rev=5),
4235 'F/E/beta' : Item(status=' ', wc_rev=5),
4236 'F/E1' : Item(status=' M', wc_rev=5),
4237 'F/E1/alpha' : Item(status=' ', wc_rev=5),
4238 'F/E1/beta' : Item(status=' ', wc_rev=5),
4239 'lambda' : Item(status=' ', wc_rev=5),
4240 'F' : Item(status=' ', wc_rev=5),
4242 expected_disk = wc.State('', {
4243 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B:5'}),
4244 'F/E' : Item(),
4245 'F/E/alpha' : Item(new_content_for_alpha),
4246 'F/E/beta' : Item("This is the file 'beta'.\n"),
4247 'F/E1' : Item(),
4248 'F/E1/alpha' : Item("This is the file 'alpha'.\n"),
4249 'F/E1/beta' : Item("This is the file 'beta'.\n"),
4250 'F' : Item(),
4251 'lambda' : Item("This is the file 'lambda'.\n")
4253 expected_skip = wc.State(short_copy_of_B_path, { })
4254 saved_cwd = os.getcwd()
4256 os.chdir(svntest.main.work_dir)
4257 svntest.actions.run_and_verify_merge(short_copy_of_B_path, '4', '5',
4258 sbox.repo_url + \
4259 '/A/B',
4260 expected_output,
4261 expected_disk,
4262 expected_status,
4263 expected_skip,
4264 None,
4265 None,
4266 None,
4267 None,
4268 None, 1)
4269 os.chdir(saved_cwd)
4271 # Commit the result of the merge, creating revision 6.
4272 expected_output = svntest.wc.State(copy_of_B_path, {
4273 '' : Item(verb='Sending'),
4274 'F/E' : Item(verb='Sending'),
4275 'F/E/alpha' : Item(verb='Sending'),
4276 'F/E1' : Item(verb='Sending'),
4278 svntest.actions.run_and_verify_commit(short_copy_of_B_path, expected_output,
4279 None, None, wc_dir)
4281 # Update the WC to bring /A/copy_of_B/F from rev 4 to rev 6.
4282 # Without this update, a subsequent merge will not find any merge
4283 # info for /A/copy_of_B/F -- nor its parent dir in the repos -- at
4284 # rev 4. Mergeinfo wasn't introduced until rev 6.
4285 copy_of_B_F_E_path = os.path.join(copy_of_B_path, 'F', 'E')
4286 svntest.actions.run_and_verify_svn(None, None, [], 'update', wc_dir)
4288 # Search for the comment entitled "The Merge Kluge" elsewhere in
4289 # this file, to understand why we shorten and chdir() below.
4290 short_copy_of_B_F_E_path = shorten_path_kludge(copy_of_B_F_E_path)
4292 # Attempt to re-merge changes to alpha from rev 4. Use the merge
4293 # info inherited from the grandparent (copy-of-B) of our merge
4294 # target (/A/copy-of-B/F/E) to avoid a repeated merge.
4295 expected_status = wc.State(short_copy_of_B_F_E_path, {
4296 '' : Item(status=' ', wc_rev=6),
4297 'alpha' : Item(status=' ', wc_rev=6),
4298 'beta' : Item(status=' ', wc_rev=6),
4300 os.chdir(svntest.main.work_dir)
4301 svntest.actions.run_and_verify_svn(None, [], [], 'merge', '-r4:5',
4302 sbox.repo_url + '/A/B/F/E',
4303 short_copy_of_B_F_E_path)
4304 svntest.actions.run_and_verify_status(short_copy_of_B_F_E_path,
4305 expected_status)
4306 os.chdir(saved_cwd)
4308 def avoid_repeated_merge_on_subtree_with_merge_info(sbox):
4309 "use subtree's mergeinfo to avoid repeated merge"
4310 # Create deep trees A/B/F/E and A/B/F/E1 and copy A/B to A/copy-of-B
4311 # with the help of 'create_deep_trees'
4312 # As /A/copy-of-B/F/E1 is not a child of /A/copy-of-B/F/E,
4313 # set_path should not be called on /A/copy-of-B/F/E1 while
4314 # doing a implicit subtree merge on /A/copy-of-B/F/E.
4315 sbox.build()
4316 wc_dir = sbox.wc_dir
4318 A_path = os.path.join(wc_dir, 'A')
4319 A_B_path = os.path.join(A_path, 'B')
4320 A_B_E_path = os.path.join(A_B_path, 'E')
4321 A_B_F_path = os.path.join(A_B_path, 'F')
4322 A_B_F_E_path = os.path.join(A_B_F_path, 'E')
4323 copy_of_B_path = os.path.join(A_path, 'copy-of-B')
4324 copy_of_B_F_path = os.path.join(A_path, 'copy-of-B', 'F')
4325 A_copy_of_B_F_E_alpha_path = os.path.join(A_path, 'copy-of-B', 'F',
4326 'E', 'alpha')
4328 # Create a deeper directory structure.
4329 expected_status = create_deep_trees(wc_dir)
4331 # Edit alpha and commit it, creating revision 5.
4332 alpha_path = os.path.join(A_B_F_E_path, 'alpha')
4333 new_content_for_alpha1 = 'new content to alpha\n'
4334 svntest.main.file_write(alpha_path, new_content_for_alpha1)
4336 expected_output = svntest.wc.State(wc_dir, {
4337 'A/B/F/E/alpha' : Item(verb='Sending'),
4339 expected_status.tweak('A/B/F/E/alpha', wc_rev=5)
4340 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
4341 expected_status, None, wc_dir)
4343 for path_and_mergeinfo in (('E', '/A/B/F/E:5'),
4344 ('E1', '/A/B/F/E:5')):
4345 path_name = os.path.join(copy_of_B_path, 'F', path_and_mergeinfo[0])
4346 # Search for the comment entitled "The Merge Kluge" elsewhere in
4347 # this file, to understand why we shorten and chdir() below.
4348 short_path_name = shorten_path_kludge(path_name)
4350 # Merge r5 to path_name.
4351 expected_output = wc.State(short_path_name, {
4352 'alpha' : Item(status='U '),
4354 expected_status = wc.State(short_path_name, {
4355 '' : Item(status=' M', wc_rev=4),
4356 'alpha' : Item(status='M ', wc_rev=4),
4357 'beta' : Item(status=' ', wc_rev=4),
4359 expected_disk = wc.State('', {
4360 '' : Item(props={SVN_PROP_MERGEINFO : path_and_mergeinfo[1]}),
4361 'alpha' : Item(new_content_for_alpha1),
4362 'beta' : Item("This is the file 'beta'.\n"),
4364 expected_skip = wc.State(short_path_name, { })
4365 saved_cwd = os.getcwd()
4367 os.chdir(svntest.main.work_dir)
4368 svntest.actions.run_and_verify_merge(short_path_name, '4', '5',
4369 sbox.repo_url + '/A/B/F/E',
4370 expected_output,
4371 expected_disk,
4372 expected_status,
4373 expected_skip,
4374 None,
4375 None,
4376 None,
4377 None,
4378 None, 1)
4379 os.chdir(saved_cwd)
4381 # Commit the result of the merge, creating new revision.
4382 expected_output = svntest.wc.State(path_name, {
4383 '' : Item(verb='Sending'),
4384 'alpha' : Item(verb='Sending'),
4386 svntest.actions.run_and_verify_commit(short_path_name,
4387 expected_output, None, None, wc_dir)
4389 # Edit A/B/F/E/alpha and commit it, creating revision 8.
4390 new_content_for_alpha = 'new content to alpha\none more line\n'
4391 svntest.main.file_write(alpha_path, new_content_for_alpha)
4393 expected_output = svntest.wc.State(A_B_F_E_path, {
4394 'alpha' : Item(verb='Sending'),
4396 expected_status = wc.State(A_B_F_E_path, {
4397 '' : Item(status=' ', wc_rev=4),
4398 'alpha' : Item(status=' ', wc_rev=8),
4399 'beta' : Item(status=' ', wc_rev=4),
4401 svntest.actions.run_and_verify_commit(A_B_F_E_path, expected_output,
4402 expected_status, None, wc_dir)
4404 # Search for the comment entitled "The Merge Kluge" elsewhere in
4405 # this file, to understand why we shorten and chdir() below.
4406 short_copy_of_B_path = shorten_path_kludge(copy_of_B_path)
4408 # Update the WC to bring /A/copy_of_B to rev 8.
4409 # Without this update expected_status tree would be cumbersome to
4410 # understand.
4411 svntest.actions.run_and_verify_svn(None, None, [], 'update', wc_dir)
4413 # Merge changes from rev 4:8 of A/B into A/copy_of_B. A/copy_of_B/F/E1
4414 # has explicit mergeinfo and exists at r4 in the merge source, so it
4415 # should be treated as a subtree with intersecting mergeinfo and its
4416 # mergeinfo updated.
4417 expected_output = wc.State(short_copy_of_B_path, {
4418 'F/E/alpha' : Item(status='U ')
4420 expected_status = wc.State(short_copy_of_B_path, {
4421 # When we merge multiple sub-targets, we record mergeinfo on each
4422 # child.
4423 '' : Item(status=' M', wc_rev=8),
4424 'F/E' : Item(status=' M', wc_rev=8),
4425 'F/E/alpha' : Item(status='M ', wc_rev=8),
4426 'F/E/beta' : Item(status=' ', wc_rev=8),
4427 'F/E1' : Item(status=' M', wc_rev=8),
4428 'F/E1/alpha' : Item(status=' ', wc_rev=8),
4429 'F/E1/beta' : Item(status=' ', wc_rev=8),
4430 'lambda' : Item(status=' ', wc_rev=8),
4431 'F' : Item(status=' ', wc_rev=8)
4433 expected_disk = wc.State('', {
4434 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B:5-8'}),
4435 'F/E' : Item(props={}), # elision!
4436 'F/E/alpha' : Item(new_content_for_alpha),
4437 'F/E/beta' : Item("This is the file 'beta'.\n"),
4438 'F' : Item(),
4439 'F/E1' : Item(props={SVN_PROP_MERGEINFO :
4440 '/A/B/F/E:5\n/A/B/F/E1:5-8\n'}),
4441 'F/E1/alpha' : Item(new_content_for_alpha1),
4442 'F/E1/beta' : Item("This is the file 'beta'.\n"),
4443 'lambda' : Item("This is the file 'lambda'.\n")
4445 expected_skip = wc.State(short_copy_of_B_path, { })
4446 os.chdir(svntest.main.work_dir)
4447 svntest.actions.run_and_verify_merge(short_copy_of_B_path, '4', '8',
4448 sbox.repo_url + '/A/B',
4449 expected_output,
4450 expected_disk,
4451 expected_status,
4452 expected_skip,
4453 None,
4454 None,
4455 None,
4456 None,
4457 None, 1)
4458 os.chdir(saved_cwd)
4460 # Test for part of Issue #2821, see
4461 # http://subversion.tigris.org/issues/show_bug.cgi?id=2821#desc22
4463 # Revert all local changes.
4464 svntest.actions.run_and_verify_svn(None, None, [], 'revert', '-R', wc_dir)
4466 # Make a text mod to A/copy-of-B/F/E/alpha
4467 newer_content_for_alpha = "Conflicting content"
4468 svntest.main.file_write(A_copy_of_B_F_E_alpha_path,
4469 newer_content_for_alpha)
4471 # Re-merge r5 to A/copy-of-B/F, this *should* be a no-op as the mergeinfo
4472 # on A/copy-of-B/F/E should prevent any attempt to merge r5 into that
4473 # subtree. The merge will leave a few local changes as mergeinfo is set
4474 # on A/copy-of-B/F, the mergeinfo on A/copy-of-B/F/E elides to it, and
4475 # the mergeinfo on A/copy-of-B/F/E1 picks up r5 from /A/B/F/E1.
4476 short_copy_of_B_F_path = shorten_path_kludge(copy_of_B_F_path)
4477 expected_output = wc.State(short_copy_of_B_F_path, {})
4478 expected_status = wc.State(short_copy_of_B_F_path, {
4479 '' : Item(status=' M', wc_rev=8),
4480 'E' : Item(status=' M', wc_rev=8),
4481 'E/alpha' : Item(status='M ', wc_rev=8),
4482 'E/beta' : Item(status=' ', wc_rev=8),
4483 'E1' : Item(status=' M', wc_rev=8),
4484 'E1/alpha' : Item(status=' ', wc_rev=8),
4485 'E1/beta' : Item(status=' ', wc_rev=8),
4487 expected_disk = wc.State('', {
4488 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B/F:5'}),
4489 'E' : Item(props={}),
4490 'E/alpha' : Item(newer_content_for_alpha),
4491 'E/beta' : Item("This is the file 'beta'.\n"),
4492 'E1' : Item(props={SVN_PROP_MERGEINFO :
4493 '/A/B/F/E:5\n/A/B/F/E1:5\n'}),
4494 'E1/alpha' : Item(new_content_for_alpha1),
4495 'E1/beta' : Item("This is the file 'beta'.\n")
4497 expected_skip = wc.State(short_copy_of_B_F_path, { })
4498 saved_cwd = os.getcwd()
4500 os.chdir(svntest.main.work_dir)
4501 svntest.actions.run_and_verify_merge(short_copy_of_B_F_path, '4', '5',
4502 sbox.repo_url + '/A/B/F',
4503 expected_output,
4504 expected_disk,
4505 expected_status,
4506 expected_skip,
4507 None,
4508 None,
4509 None,
4510 None,
4511 None, 1)
4512 os.chdir(saved_cwd)
4514 def tweak_src_then_merge_to_dest(sbox, src_path, dst_path,
4515 canon_src_path, contents, cur_rev):
4516 """Edit src and commit it. This results in new_rev.
4517 Merge new_rev to dst_path. Return new_rev."""
4519 wc_dir = sbox.wc_dir
4520 new_rev = cur_rev + 1
4521 svntest.main.file_write(src_path, contents)
4523 expected_output = svntest.wc.State(src_path, {
4524 '': Item(verb='Sending'),
4527 expected_status = wc.State(src_path,
4528 { '': Item(wc_rev=new_rev, status=' ')})
4530 svntest.actions.run_and_verify_commit(src_path, expected_output,
4531 expected_status, None, src_path)
4533 # Update the WC to new_rev so that it would be easier to expect everyone
4534 # to be at new_rev.
4535 svntest.actions.run_and_verify_svn(None, None, [], 'update', wc_dir)
4537 # Merge new_rev of src_path to dst_path.
4539 # Search for the comment entitled "The Merge Kluge" elsewhere in
4540 # this file, to understand why we shorten and chdir() below.
4541 short_dst_path = shorten_path_kludge(dst_path)
4542 expected_status = wc.State(dst_path,
4543 { '': Item(wc_rev=new_rev, status='MM')})
4544 saved_cwd = os.getcwd()
4546 os.chdir(svntest.main.work_dir)
4548 merge_url = sbox.repo_url + '/' + canon_src_path
4549 if sys.platform == 'win32':
4550 merge_url = merge_url.replace('\\', '/')
4552 svntest.actions.run_and_verify_svn(None,
4553 expected_merge_output([[new_rev]],
4554 'U ' +
4555 short_dst_path +
4556 '\n'),
4558 'merge', '-c', str(new_rev),
4559 merge_url,
4560 short_dst_path)
4561 os.chdir(saved_cwd)
4563 svntest.actions.run_and_verify_status(dst_path, expected_status)
4565 return new_rev
4567 def obey_reporter_api_semantics_while_doing_subtree_merges(sbox):
4568 "drive reporter api in depth first order"
4570 # Copy /A/D to /A/copy-of-D it results in rONE.
4571 # Create children at different hierarchies having some merge-info
4572 # to test the set_path calls on a reporter in a depth-first order.
4573 # On all 'file' descendants of /A/copy-of-D/ we run merges.
4574 # We create /A/D/umlaut directly over URL it results in rev rTWO.
4575 # When we merge rONE+1:TWO of /A/D on /A/copy-of-D it should merge smoothly.
4577 sbox.build()
4578 wc_dir = sbox.wc_dir
4580 A_path = os.path.join(wc_dir, 'A')
4581 A_D_path = os.path.join(wc_dir, 'A', 'D')
4582 copy_of_A_D_path = os.path.join(wc_dir, 'A', 'copy-of-D')
4584 svntest.main.run_svn(None, "cp", A_D_path, copy_of_A_D_path)
4586 expected_output = svntest.wc.State(wc_dir, {
4587 'A/copy-of-D' : Item(verb='Adding'),
4589 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
4590 expected_status.add({
4591 'A/copy-of-D' : Item(status=' ', wc_rev=2),
4592 'A/copy-of-D/G' : Item(status=' ', wc_rev=2),
4593 'A/copy-of-D/G/pi' : Item(status=' ', wc_rev=2),
4594 'A/copy-of-D/G/rho' : Item(status=' ', wc_rev=2),
4595 'A/copy-of-D/G/tau' : Item(status=' ', wc_rev=2),
4596 'A/copy-of-D/H' : Item(status=' ', wc_rev=2),
4597 'A/copy-of-D/H/chi' : Item(status=' ', wc_rev=2),
4598 'A/copy-of-D/H/omega' : Item(status=' ', wc_rev=2),
4599 'A/copy-of-D/H/psi' : Item(status=' ', wc_rev=2),
4600 'A/copy-of-D/gamma' : Item(status=' ', wc_rev=2),
4602 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
4603 expected_status, None, wc_dir)
4606 cur_rev = 2
4607 for path in (["A", "D", "G", "pi"],
4608 ["A", "D", "G", "rho"],
4609 ["A", "D", "G", "tau"],
4610 ["A", "D", "H", "chi"],
4611 ["A", "D", "H", "omega"],
4612 ["A", "D", "H", "psi"],
4613 ["A", "D", "gamma"]):
4614 path_name = os.path.join(wc_dir, *path)
4615 canon_path_name = os.path.join(*path)
4616 path[1] = "copy-of-D"
4617 copy_of_path_name = os.path.join(wc_dir, *path)
4618 var_name = 'new_content_for_' + path[len(path) - 1]
4619 file_contents = "new content to " + path[len(path) - 1] + "\n"
4620 globals()[var_name] = file_contents
4621 cur_rev = tweak_src_then_merge_to_dest(sbox, path_name,
4622 copy_of_path_name, canon_path_name,
4623 file_contents, cur_rev)
4625 copy_of_A_D_wc_rev = cur_rev
4626 svntest.actions.run_and_verify_svn(None,
4627 ['\n',
4628 'Committed revision ' + str(cur_rev+1) +
4629 '.\n'],
4631 'mkdir', sbox.repo_url + '/A/D/umlaut',
4632 '-m', "log msg")
4633 rev_to_merge_to_copy_of_D = cur_rev + 1
4635 # Search for the comment entitled "The Merge Kluge" elsewhere in
4636 # this file, to understand why we shorten and chdir() below.
4637 short_copy_of_A_D_path = shorten_path_kludge(copy_of_A_D_path)
4639 # All the file descendants of /A/copy-of-D/ have already been merged
4640 # so the only notification we expect is for the added 'umlaut'.
4641 expected_output = wc.State(short_copy_of_A_D_path, {
4642 'umlaut' : Item(status='A '),
4645 # All the local svn:mergeinfo under A/copy-of-D elides
4646 # to A/copy-of-D.
4647 expected_status = wc.State(short_copy_of_A_D_path, {
4648 '' : Item(status=' M', wc_rev=copy_of_A_D_wc_rev),
4649 'G' : Item(status=' ', wc_rev=copy_of_A_D_wc_rev),
4650 'G/pi' : Item(status='M ', wc_rev=copy_of_A_D_wc_rev),
4651 'G/rho' : Item(status='M ', wc_rev=copy_of_A_D_wc_rev),
4652 'G/tau' : Item(status='M ', wc_rev=copy_of_A_D_wc_rev),
4653 'H' : Item(status=' ', wc_rev=copy_of_A_D_wc_rev),
4654 'H/chi' : Item(status='M ', wc_rev=copy_of_A_D_wc_rev),
4655 'H/omega' : Item(status='M ', wc_rev=copy_of_A_D_wc_rev),
4656 'H/psi' : Item(status='M ', wc_rev=copy_of_A_D_wc_rev),
4657 'gamma' : Item(status='M ', wc_rev=copy_of_A_D_wc_rev),
4658 'umlaut' : Item(status='A ', copied='+', wc_rev='-'),
4661 merged_rangelist = "3-%d" % rev_to_merge_to_copy_of_D
4664 expected_disk = wc.State('', {
4665 '' : Item(props={SVN_PROP_MERGEINFO : '/A/D:' + merged_rangelist}),
4666 'G' : Item(),
4667 'G/pi' : Item(new_content_for_pi),
4668 'G/rho' : Item(new_content_for_rho),
4669 'G/tau' : Item(new_content_for_tau),
4670 'H' : Item(),
4671 'H/chi' : Item(new_content_for_chi,),
4672 'H/omega' : Item(new_content_for_omega,),
4673 'H/psi' : Item(new_content_for_psi,),
4674 'gamma' : Item(new_content_for_gamma,),
4675 'umlaut' : Item(),
4677 expected_skip = wc.State(short_copy_of_A_D_path, { })
4678 os.chdir(svntest.main.work_dir)
4679 svntest.actions.run_and_verify_merge(short_copy_of_A_D_path,
4681 str(rev_to_merge_to_copy_of_D),
4682 sbox.repo_url + '/A/D',
4683 expected_output,
4684 expected_disk,
4685 expected_status,
4686 expected_skip,
4687 None,
4688 None,
4689 None,
4690 None,
4691 None, 1)
4693 def set_up_branch(sbox, branch_only = False, nbr_of_branches = 1):
4694 '''Starting with standard greek tree, copy 'A' NBR_OF_BRANCHES times
4695 to A_COPY, A_COPY_2, A_COPY_3, and so on. Then make four modifications
4696 (setting file contents to "New content") under A:
4697 r(2 + NBR_OF_BRANCHES) - A/D/H/psi
4698 r(3 + NBR_OF_BRANCHES) - A/D/G/rho
4699 r(4 + NBR_OF_BRANCHES) - A/B/E/beta
4700 r(5 + NBR_OF_BRANCHES) - A/D/H/omega'''
4702 wc_dir = sbox.wc_dir
4704 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
4705 expected_disk = svntest.main.greek_state.copy()
4707 def copy_A(dest_name, rev):
4708 expected = svntest.verify.UnorderedOutput(
4709 ["A " + os.path.join(wc_dir, dest_name, "B") + "\n",
4710 "A " + os.path.join(wc_dir, dest_name, "B", "lambda") + "\n",
4711 "A " + os.path.join(wc_dir, dest_name, "B", "E") + "\n",
4712 "A " + os.path.join(wc_dir, dest_name, "B", "E", "alpha") + "\n",
4713 "A " + os.path.join(wc_dir, dest_name, "B", "E", "beta") + "\n",
4714 "A " + os.path.join(wc_dir, dest_name, "B", "F") + "\n",
4715 "A " + os.path.join(wc_dir, dest_name, "mu") + "\n",
4716 "A " + os.path.join(wc_dir, dest_name, "C") + "\n",
4717 "A " + os.path.join(wc_dir, dest_name, "D") + "\n",
4718 "A " + os.path.join(wc_dir, dest_name, "D", "gamma") + "\n",
4719 "A " + os.path.join(wc_dir, dest_name, "D", "G") + "\n",
4720 "A " + os.path.join(wc_dir, dest_name, "D", "G", "pi") + "\n",
4721 "A " + os.path.join(wc_dir, dest_name, "D", "G", "rho") + "\n",
4722 "A " + os.path.join(wc_dir, dest_name, "D", "G", "tau") + "\n",
4723 "A " + os.path.join(wc_dir, dest_name, "D", "H") + "\n",
4724 "A " + os.path.join(wc_dir, dest_name, "D", "H", "chi") + "\n",
4725 "A " + os.path.join(wc_dir, dest_name, "D", "H", "omega") + "\n",
4726 "A " + os.path.join(wc_dir, dest_name, "D", "H", "psi") + "\n",
4727 "Checked out revision " + str(rev - 1) + ".\n",
4728 "A " + os.path.join(wc_dir, dest_name) + "\n"])
4729 expected_status.add({
4730 dest_name + "/B" : Item(status=' ', wc_rev=rev),
4731 dest_name + "/B/lambda" : Item(status=' ', wc_rev=rev),
4732 dest_name + "/B/E" : Item(status=' ', wc_rev=rev),
4733 dest_name + "/B/E/alpha" : Item(status=' ', wc_rev=rev),
4734 dest_name + "/B/E/beta" : Item(status=' ', wc_rev=rev),
4735 dest_name + "/B/F" : Item(status=' ', wc_rev=rev),
4736 dest_name + "/mu" : Item(status=' ', wc_rev=rev),
4737 dest_name + "/C" : Item(status=' ', wc_rev=rev),
4738 dest_name + "/D" : Item(status=' ', wc_rev=rev),
4739 dest_name + "/D/gamma" : Item(status=' ', wc_rev=rev),
4740 dest_name + "/D/G" : Item(status=' ', wc_rev=rev),
4741 dest_name + "/D/G/pi" : Item(status=' ', wc_rev=rev),
4742 dest_name + "/D/G/rho" : Item(status=' ', wc_rev=rev),
4743 dest_name + "/D/G/tau" : Item(status=' ', wc_rev=rev),
4744 dest_name + "/D/H" : Item(status=' ', wc_rev=rev),
4745 dest_name + "/D/H/chi" : Item(status=' ', wc_rev=rev),
4746 dest_name + "/D/H/omega" : Item(status=' ', wc_rev=rev),
4747 dest_name + "/D/H/psi" : Item(status=' ', wc_rev=rev),
4748 dest_name : Item(status=' ', wc_rev=rev)})
4749 expected_disk.add({
4750 dest_name : Item(),
4751 dest_name + '/B' : Item(),
4752 dest_name + '/B/lambda' : Item("This is the file 'lambda'.\n"),
4753 dest_name + '/B/E' : Item(),
4754 dest_name + '/B/E/alpha' : Item("This is the file 'alpha'.\n"),
4755 dest_name + '/B/E/beta' : Item("This is the file 'beta'.\n"),
4756 dest_name + '/B/F' : Item(),
4757 dest_name + '/mu' : Item("This is the file 'mu'.\n"),
4758 dest_name + '/C' : Item(),
4759 dest_name + '/D' : Item(),
4760 dest_name + '/D/gamma' : Item("This is the file 'gamma'.\n"),
4761 dest_name + '/D/G' : Item(),
4762 dest_name + '/D/G/pi' : Item("This is the file 'pi'.\n"),
4763 dest_name + '/D/G/rho' : Item("This is the file 'rho'.\n"),
4764 dest_name + '/D/G/tau' : Item("This is the file 'tau'.\n"),
4765 dest_name + '/D/H' : Item(),
4766 dest_name + '/D/H/chi' : Item("This is the file 'chi'.\n"),
4767 dest_name + '/D/H/omega' : Item("This is the file 'omega'.\n"),
4768 dest_name + '/D/H/psi' : Item("This is the file 'psi'.\n"),
4771 # Make a branch A_COPY to merge into.
4772 svntest.actions.run_and_verify_svn(None, expected, [], 'copy',
4773 sbox.repo_url + "/A",
4774 os.path.join(wc_dir,
4775 dest_name))
4777 expected_output = wc.State(wc_dir, {dest_name : Item(verb='Adding')})
4778 svntest.actions.run_and_verify_commit(wc_dir,
4779 expected_output,
4780 expected_status,
4781 None,
4782 wc_dir)
4783 for i in range(nbr_of_branches):
4784 if i == 0:
4785 copy_A('A_COPY', i + 2)
4786 else:
4787 copy_A('A_COPY_' + str(i + 1), i + 2)
4789 if (branch_only):
4790 return expected_disk, expected_status
4792 # Make some changes under A which we'll later merge under A_COPY:
4794 # r(nbr_of_branches + 2) - modify and commit A/D/H/psi
4795 svntest.main.file_write(os.path.join(wc_dir, "A", "D", "H", "psi"),
4796 "New content")
4797 expected_output = wc.State(wc_dir, {'A/D/H/psi' : Item(verb='Sending')})
4798 expected_status.tweak('A/D/H/psi', wc_rev=nbr_of_branches + 2)
4799 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
4800 expected_status, None, wc_dir)
4801 expected_disk.tweak('A/D/H/psi', contents="New content")
4803 # r(nbr_of_branches + 3) - modify and commit A/D/G/rho
4804 svntest.main.file_write(os.path.join(wc_dir, "A", "D", "G", "rho"),
4805 "New content")
4806 expected_output = wc.State(wc_dir, {'A/D/G/rho' : Item(verb='Sending')})
4807 expected_status.tweak('A/D/G/rho', wc_rev=nbr_of_branches + 3)
4808 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
4809 expected_status, None, wc_dir)
4810 expected_disk.tweak('A/D/G/rho', contents="New content")
4812 # r(nbr_of_branches + 4) - modify and commit A/B/E/beta
4813 svntest.main.file_write(os.path.join(wc_dir, "A", "B", "E", "beta"),
4814 "New content")
4815 expected_output = wc.State(wc_dir, {'A/B/E/beta' : Item(verb='Sending')})
4816 expected_status.tweak('A/B/E/beta', wc_rev=nbr_of_branches + 4)
4817 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
4818 expected_status, None, wc_dir)
4819 expected_disk.tweak('A/B/E/beta', contents="New content")
4821 # r(nbr_of_branches + 5) - modify and commit A/D/H/omega
4822 svntest.main.file_write(os.path.join(wc_dir, "A", "D", "H", "omega"),
4823 "New content")
4824 expected_output = wc.State(wc_dir, {'A/D/H/omega' : Item(verb='Sending')})
4825 expected_status.tweak('A/D/H/omega', wc_rev=nbr_of_branches + 5)
4826 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
4827 expected_status, None, wc_dir)
4828 expected_disk.tweak('A/D/H/omega', contents="New content")
4830 return expected_disk, expected_status
4833 def mergeinfo_inheritance(sbox):
4834 "target inherits mergeinfo from nearest ancestor"
4836 # Test for Issues #2733 and #2734.
4838 # When the target of a merge has no explicit mergeinfo and the merge
4839 # would result in mergeinfo being added to the target which...
4841 # ...is a subset of the *local* mergeinfo on one of the target's
4842 # ancestors (it's nearest ancestor takes precedence), then the merge is
4843 # not repeated and no mergeinfo should be set on the target (Issue #2734).
4845 # OR
4847 # ...is not a subset it's nearest ancestor, the target should inherit the
4848 # non-inersecting mergeinfo (local or committed, the former takes
4849 # precedence) from it's nearest ancestor (Issue #2733).
4851 sbox.build()
4852 wc_dir = sbox.wc_dir
4853 wc_disk, wc_status = set_up_branch(sbox)
4855 # Some paths we'll care about
4856 A_COPY_path = os.path.join(wc_dir, "A_COPY")
4857 B_COPY_path = os.path.join(wc_dir, "A_COPY", "B")
4858 beta_COPY_path = os.path.join(wc_dir, "A_COPY", "B", "E", "beta")
4859 E_COPY_path = os.path.join(wc_dir, "A_COPY", "B", "E")
4860 omega_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "H", "omega")
4861 D_COPY_path = os.path.join(wc_dir, "A_COPY", "D")
4862 G_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "G")
4864 # Now start merging...
4866 # Merge r4 into A_COPY/D/
4867 # Search for the comment entitled "The Merge Kluge" elsewhere in
4868 # this file, to understand why we shorten and chdir() below.
4869 short_D_COPY_path = shorten_path_kludge(D_COPY_path)
4870 expected_output = wc.State(short_D_COPY_path, {
4871 'G/rho' : Item(status='U '),
4873 expected_status = wc.State(short_D_COPY_path, {
4874 '' : Item(status=' M', wc_rev=2),
4875 'G' : Item(status=' ', wc_rev=2),
4876 'G/pi' : Item(status=' ', wc_rev=2),
4877 'G/rho' : Item(status='M ', wc_rev=2),
4878 'G/tau' : Item(status=' ', wc_rev=2),
4879 'H' : Item(status=' ', wc_rev=2),
4880 'H/chi' : Item(status=' ', wc_rev=2),
4881 'H/psi' : Item(status=' ', wc_rev=2),
4882 'H/omega' : Item(status=' ', wc_rev=2),
4883 'gamma' : Item(status=' ', wc_rev=2),
4885 # We test issue #2733 here (with a directory as the merge target).
4886 # r1 should be inherited from 'A_COPY'.
4887 expected_disk = wc.State('', {
4888 '' : Item(props={SVN_PROP_MERGEINFO : '/A/D:4'}),
4889 'G' : Item(),
4890 'G/pi' : Item("This is the file 'pi'.\n"),
4891 'G/rho' : Item("New content"),
4892 'G/tau' : Item("This is the file 'tau'.\n"),
4893 'H' : Item(),
4894 'H/chi' : Item("This is the file 'chi'.\n"),
4895 'H/psi' : Item("This is the file 'psi'.\n"),
4896 'H/omega' : Item("This is the file 'omega'.\n"),
4897 'gamma' : Item("This is the file 'gamma'.\n")
4899 expected_skip = wc.State(short_D_COPY_path, { })
4900 saved_cwd = os.getcwd()
4901 os.chdir(svntest.main.work_dir)
4902 svntest.actions.run_and_verify_merge(short_D_COPY_path, '3', '4',
4903 sbox.repo_url + \
4904 '/A/D',
4905 expected_output,
4906 expected_disk,
4907 expected_status,
4908 expected_skip,
4909 None, None, None, None,
4910 None, 1)
4911 os.chdir(saved_cwd)
4913 # Merge r4 again, this time into A_COPY/D/G. An ancestor directory
4914 # (A_COPY/D) exists with identical local mergeinfo, so the merge
4915 # should not be repeated. We test issue #2734 here with (with a
4916 # directory as the merge target).
4917 short_G_COPY_path = shorten_path_kludge(G_COPY_path)
4918 expected_output = wc.State(short_G_COPY_path, { })
4919 expected_status = wc.State(short_G_COPY_path, {
4920 '' : Item(status=' ', wc_rev=2),
4921 'pi' : Item(status=' ', wc_rev=2),
4922 'rho' : Item(status='M ', wc_rev=2),
4923 'tau' : Item(status=' ', wc_rev=2),
4925 expected_disk = wc.State('', {
4926 'pi' : Item("This is the file 'pi'.\n"),
4927 'rho' : Item("New content"),
4928 'tau' : Item("This is the file 'tau'.\n"),
4930 expected_skip = wc.State(short_G_COPY_path, { })
4931 os.chdir(svntest.main.work_dir)
4932 svntest.actions.run_and_verify_merge(short_G_COPY_path, '3', '4',
4933 sbox.repo_url + \
4934 '/A/D/G',
4935 expected_output,
4936 expected_disk,
4937 expected_status,
4938 expected_skip,
4939 None, None, None, None,
4940 None, 1)
4941 os.chdir(saved_cwd)
4942 # Merge r5 into A_COPY/B. Again, r1 should be inherited from
4943 # A_COPY (Issue #2733)
4944 short_B_COPY_path = shorten_path_kludge(B_COPY_path)
4945 expected_output = wc.State(short_B_COPY_path, {
4946 'E/beta' : Item(status='U '),
4948 expected_status = wc.State(short_B_COPY_path, {
4949 '' : Item(status=' M', wc_rev=2),
4950 'E' : Item(status=' ', wc_rev=2),
4951 'E/alpha' : Item(status=' ', wc_rev=2),
4952 'E/beta' : Item(status='M ', wc_rev=2),
4953 'lambda' : Item(status=' ', wc_rev=2),
4954 'F' : Item(status=' ', wc_rev=2),
4956 expected_disk = wc.State('', {
4957 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B:5'}),
4958 'E' : Item(),
4959 'E/alpha' : Item("This is the file 'alpha'.\n"),
4960 'E/beta' : Item("New content"),
4961 'F' : Item(),
4962 'lambda' : Item("This is the file 'lambda'.\n")
4964 expected_skip = wc.State(short_B_COPY_path, { })
4966 os.chdir(svntest.main.work_dir)
4967 svntest.actions.run_and_verify_merge(short_B_COPY_path, '4', '5',
4968 sbox.repo_url + \
4969 '/A/B',
4970 expected_output,
4971 expected_disk,
4972 expected_status,
4973 expected_skip,
4974 None, None, None, None,
4975 None, 1)
4976 os.chdir(saved_cwd)
4978 # Merge r5 again, this time into A_COPY/B/E/beta. An ancestor
4979 # directory (A_COPY/B) exists with identical local mergeinfo, so
4980 # the merge should not be repeated (Issue #2734 with a file as the
4981 # merge target).
4982 short_beta_COPY_path = shorten_path_kludge(beta_COPY_path)
4983 expected_skip = wc.State(short_beta_COPY_path, { })
4984 saved_cwd = os.getcwd()
4986 os.chdir(svntest.main.work_dir)
4987 # run_and_verify_merge doesn't support merging to a file WCPATH
4988 # so use run_and_verify_svn.
4989 svntest.actions.run_and_verify_svn(None, [], [], 'merge', '-c5',
4990 sbox.repo_url + '/A/B/E/beta',
4991 short_beta_COPY_path)
4992 os.chdir(saved_cwd)
4994 # The merge wasn't repeated so beta shouldn't have any mergeinfo.
4995 # We are implicitly testing that without looking at the prop value
4996 # itself, just beta's prop modification status.
4997 expected_status = wc.State(beta_COPY_path, {
4998 '' : Item(status='M ', wc_rev=2),
5000 svntest.actions.run_and_verify_status(beta_COPY_path, expected_status)
5002 # Merge r3 into A_COPY. A_COPY's two descendants with mergeinfo,
5003 # A_COPY/B/E/beta and A_COPY/D/G/rho must have complete mergeinfo
5004 # so they both should pick up r3 too.
5005 short_A_COPY_path = shorten_path_kludge(A_COPY_path)
5006 expected_output = wc.State(short_A_COPY_path, {
5007 'D/H/psi' : Item(status='U '),
5009 expected_status = wc.State(short_A_COPY_path, {
5010 '' : Item(status=' M', wc_rev=2),
5011 'B' : Item(status=' M', wc_rev=2),
5012 'mu' : Item(status=' ', wc_rev=2),
5013 'B/E' : Item(status=' ', wc_rev=2),
5014 'B/E/alpha' : Item(status=' ', wc_rev=2),
5015 'B/E/beta' : Item(status='M ', wc_rev=2),
5016 'B/lambda' : Item(status=' ', wc_rev=2),
5017 'B/F' : Item(status=' ', wc_rev=2),
5018 'C' : Item(status=' ', wc_rev=2),
5019 'D' : Item(status=' M', wc_rev=2),
5020 'D/G' : Item(status=' ', wc_rev=2),
5021 'D/G/pi' : Item(status=' ', wc_rev=2),
5022 'D/G/rho' : Item(status='M ', wc_rev=2),
5023 'D/G/tau' : Item(status=' ', wc_rev=2),
5024 'D/gamma' : Item(status=' ', wc_rev=2),
5025 'D/H' : Item(status=' ', wc_rev=2),
5026 'D/H/chi' : Item(status=' ', wc_rev=2),
5027 'D/H/psi' : Item(status='M ', wc_rev=2),
5028 'D/H/omega' : Item(status=' ', wc_rev=2),
5030 expected_disk = wc.State('', {
5031 '' : Item(props={SVN_PROP_MERGEINFO : '/A:3'}),
5032 'B' : Item(props={SVN_PROP_MERGEINFO : '/A/B:3,5'}),
5033 'mu' : Item("This is the file 'mu'.\n"),
5034 'B/E' : Item(),
5035 'B/E/alpha' : Item("This is the file 'alpha'.\n"),
5036 'B/E/beta' : Item("New content"),
5037 'B/lambda' : Item("This is the file 'lambda'.\n"),
5038 'B/F' : Item(),
5039 'C' : Item(),
5040 'D' : Item(props={SVN_PROP_MERGEINFO : '/A/D:3-4'}),
5041 'D/G' : Item(),
5042 'D/G/pi' : Item("This is the file 'pi'.\n"),
5043 'D/G/rho' : Item("New content"),
5044 'D/G/tau' : Item("This is the file 'tau'.\n"),
5045 'D/gamma' : Item("This is the file 'gamma'.\n"),
5046 'D/H' : Item(),
5047 'D/H/chi' : Item("This is the file 'chi'.\n"),
5048 'D/H/psi' : Item("New content"),
5049 'D/H/omega' : Item("This is the file 'omega'.\n"),
5051 expected_skip = wc.State(short_A_COPY_path, { })
5052 saved_cwd = os.getcwd()
5053 os.chdir(svntest.main.work_dir)
5054 svntest.actions.run_and_verify_merge(short_A_COPY_path, '2', '3',
5055 sbox.repo_url + \
5056 '/A',
5057 expected_output,
5058 expected_disk,
5059 expected_status,
5060 expected_skip,
5061 None, None, None, None,
5062 None, 1)
5063 os.chdir(saved_cwd)
5065 # Merge r6 into A_COPY/D/H/omega, it should inherit it's nearest
5066 # ancestor's (A_COPY/D) mergeinfo (Issue #2733 with a file as the
5067 # merge target).
5068 short_omega_COPY_path = shorten_path_kludge(omega_COPY_path)
5069 expected_skip = wc.State(short_omega_COPY_path, { })
5070 saved_cwd = os.getcwd()
5071 os.chdir(svntest.main.work_dir)
5072 # run_and_verify_merge doesn't support merging to a file WCPATH
5073 # so use run_and_verify_svn.
5074 svntest.actions.run_and_verify_svn(None,
5075 expected_merge_output([[6]],
5076 'U ' + short_omega_COPY_path + '\n'),
5077 [], 'merge', '-c6',
5078 sbox.repo_url + '/A/D/H/omega',
5079 short_omega_COPY_path)
5080 os.chdir(saved_cwd)
5082 # Check that mergeinfo was properly set on A_COPY/D/H/omega
5083 svntest.actions.run_and_verify_svn(None,
5084 ["/A/D/H/omega:3-4,6\n"],
5086 'propget', SVN_PROP_MERGEINFO,
5087 omega_COPY_path)
5089 # Given a merge target *without* any of the following:
5091 # 1) Explicit mergeinfo set on itself in the WC
5092 # 2) Any WC ancestor to inherit mergeinfo from
5093 # 3) Any mergeinfo for the target in the repository
5095 # Check that the target still inherits mergeinfo from it's nearest
5096 # repository ancestor.
5098 # Commit all the merges thus far
5099 expected_output = wc.State(wc_dir, {
5100 'A_COPY' : Item(verb='Sending'),
5101 'A_COPY/B' : Item(verb='Sending'),
5102 'A_COPY/B/E/beta' : Item(verb='Sending'),
5103 'A_COPY/D' : Item(verb='Sending'),
5104 'A_COPY/D/G/rho' : Item(verb='Sending'),
5105 'A_COPY/D/H/omega' : Item(verb='Sending'),
5106 'A_COPY/D/H/psi' : Item(verb='Sending'),
5108 wc_status.tweak('A_COPY', 'A_COPY/B', 'A_COPY/B/E/beta', 'A_COPY/D',
5109 'A_COPY/D/G/rho', 'A_COPY/D/H/omega', 'A_COPY/D/H/psi',
5110 wc_rev=7)
5111 svntest.actions.run_and_verify_commit(wc_dir,
5112 expected_output,
5113 wc_status,
5114 None,
5115 wc_dir)
5117 # Copy the subtree A_COPY/B/E from the working copy, making the
5118 # disconnected WC E_only.
5119 other_wc = sbox.add_wc_path('E_only')
5120 svntest.actions.duplicate_dir(E_COPY_path, other_wc)
5122 # Update the disconnected WC it so it will get the most recent mergeinfo
5123 # from the repos when merging.
5124 svntest.actions.run_and_verify_svn(None, ["At revision 7.\n"], [], 'up',
5125 other_wc)
5127 # Merge r5:4 into the root of the disconnected WC.
5128 # E_only has no explicit mergeinfo and since it's the root of the WC
5129 # cannot inherit and mergeinfo from a working copy ancestor path. Nor
5130 # does it have any mergeinfo explicitly set on it in the repository.
5131 # An ancestor path on the repository side, A_COPY/B does have the merge
5132 # info '/A/B:1,3,5' however and E_only should inherit this, resulting in
5133 # mergeinfo of 'A/B/E:1,3' after the removal of r5.
5134 short_other_wc_path = shorten_path_kludge(other_wc)
5135 expected_output = wc.State(short_other_wc_path,
5136 {'beta' : Item(status='U ')})
5137 expected_status = wc.State(short_other_wc_path, {
5138 '' : Item(status=' M', wc_rev=7),
5139 'alpha' : Item(status=' ', wc_rev=7),
5140 'beta' : Item(status='M ', wc_rev=7),
5142 expected_disk = wc.State('', {
5143 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B/E:3'}),
5144 'alpha' : Item("This is the file 'alpha'.\n"),
5145 'beta' : Item("This is the file 'beta'.\n"),
5147 expected_skip = wc.State(short_other_wc_path, { })
5149 os.chdir(svntest.main.work_dir)
5150 svntest.actions.run_and_verify_merge(short_other_wc_path, '5', '4',
5151 sbox.repo_url + \
5152 '/A/B/E',
5153 expected_output,
5154 expected_disk,
5155 expected_status,
5156 expected_skip,
5157 None, None, None, None,
5158 None, 1)
5160 def mergeinfo_elision(sbox):
5161 "mergeinfo elides to ancestor with identical info"
5163 # When a merge would result in mergeinfo on a target which is identical
5164 # to mergeinfo (local or committed) on one of the node's ancestors (the
5165 # nearest ancestor takes precedence), then the mergeinfo elides from the
5166 # target to the nearest ancestor (e.g. no mergeinfo is set on the target
5167 # or committed mergeinfo is removed).
5169 sbox.build()
5170 wc_dir = sbox.wc_dir
5171 wc_disk, wc_status = set_up_branch(sbox)
5173 # Some paths we'll care about
5174 A_COPY_path = os.path.join(wc_dir, "A_COPY")
5175 beta_COPY_path = os.path.join(wc_dir, "A_COPY", "B", "E", "beta")
5176 G_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "G")
5178 # Now start merging...
5180 # Merge r5 into A_COPY/B/E/beta.
5181 short_beta_COPY_path = shorten_path_kludge(beta_COPY_path)
5182 expected_skip = wc.State(short_beta_COPY_path, { })
5183 saved_cwd = os.getcwd()
5185 os.chdir(svntest.main.work_dir)
5186 # run_and_verify_merge doesn't support merging to a file WCPATH
5187 # so use run_and_verify_svn.
5188 svntest.actions.run_and_verify_svn(None,
5189 expected_merge_output([[5]],
5190 'U ' + short_beta_COPY_path + '\n'),
5191 [], 'merge', '-c5',
5192 sbox.repo_url + '/A/B/E/beta',
5193 short_beta_COPY_path)
5194 os.chdir(saved_cwd)
5196 # Check beta's status and props.
5197 expected_status = wc.State(beta_COPY_path, {
5198 '' : Item(status='MM', wc_rev=2),
5200 svntest.actions.run_and_verify_status(beta_COPY_path, expected_status)
5202 svntest.actions.run_and_verify_svn(None, ["/A/B/E/beta:5\n"], [],
5203 'propget', SVN_PROP_MERGEINFO,
5204 beta_COPY_path)
5206 # Commit the merge
5207 expected_output = wc.State(wc_dir, {
5208 'A_COPY/B/E/beta' : Item(verb='Sending'),
5210 wc_status.tweak('A_COPY/B/E/beta', wc_rev=7)
5211 svntest.actions.run_and_verify_commit(wc_dir,
5212 expected_output,
5213 wc_status,
5214 None,
5215 wc_dir)
5217 # Update A_COPY to get all paths to the same working revision.
5218 svntest.actions.run_and_verify_svn(None, ["At revision 7.\n"], [],
5219 'up', wc_dir)
5220 wc_status.tweak(wc_rev=7)
5222 # Merge r4 into A_COPY/D/G.
5223 # Search for the comment entitled "The Merge Kluge" elsewhere in
5224 # this file, to understand why we shorten and chdir() below.
5225 short_G_COPY_path = shorten_path_kludge(G_COPY_path)
5226 expected_output = wc.State(short_G_COPY_path, {
5227 'rho' : Item(status='U ')
5229 expected_status = wc.State(short_G_COPY_path, {
5230 '' : Item(status=' M', wc_rev=7),
5231 'pi' : Item(status=' ', wc_rev=7),
5232 'rho' : Item(status='M ', wc_rev=7),
5233 'tau' : Item(status=' ', wc_rev=7),
5235 expected_disk = wc.State('', {
5236 '' : Item(props={SVN_PROP_MERGEINFO : '/A/D/G:4'}),
5237 'pi' : Item("This is the file 'pi'.\n"),
5238 'rho' : Item("New content"),
5239 'tau' : Item("This is the file 'tau'.\n"),
5241 expected_skip = wc.State(short_G_COPY_path, { })
5243 os.chdir(svntest.main.work_dir)
5244 svntest.actions.run_and_verify_merge(short_G_COPY_path, '3', '4',
5245 sbox.repo_url + \
5246 '/A/D/G',
5247 expected_output,
5248 expected_disk,
5249 expected_status,
5250 expected_skip,
5251 None, None, None, None,
5252 None, 1)
5253 os.chdir(saved_cwd)
5255 # Merge r3:6 into A_COPY. This would result in identical mergeinfo
5256 # (r4-6) on A_COPY and two of it's descendants, A_COPY/D/G and
5257 # A_COPY/B/E/beta, so the mergeinfo on the latter two should elide
5258 # to A_COPY. In the case of A_COPY/D/G this means its wholly uncommitted
5259 # mergeinfo is removed leaving no prop mods. In the case of
5260 # A_COPY/B/E/beta its committed mergeinfo prop is removed leaving a prop
5261 # change.
5262 short_A_COPY_path = shorten_path_kludge(A_COPY_path)
5263 expected_output = wc.State(short_A_COPY_path, {
5264 'D/H/omega' : Item(status='U ')
5266 expected_status = wc.State(short_A_COPY_path, {
5267 '' : Item(status=' M', wc_rev=7),
5268 'B' : Item(status=' ', wc_rev=7),
5269 'mu' : Item(status=' ', wc_rev=7),
5270 'B/E' : Item(status=' ', wc_rev=7),
5271 'B/E/alpha' : Item(status=' ', wc_rev=7),
5272 'B/E/beta' : Item(status=' M', wc_rev=7),
5273 'B/lambda' : Item(status=' ', wc_rev=7),
5274 'B/F' : Item(status=' ', wc_rev=7),
5275 'C' : Item(status=' ', wc_rev=7),
5276 'D' : Item(status=' ', wc_rev=7),
5277 'D/G' : Item(status=' ', wc_rev=7),
5278 'D/G/pi' : Item(status=' ', wc_rev=7),
5279 'D/G/rho' : Item(status='M ', wc_rev=7),
5280 'D/G/tau' : Item(status=' ', wc_rev=7),
5281 'D/gamma' : Item(status=' ', wc_rev=7),
5282 'D/H' : Item(status=' ', wc_rev=7),
5283 'D/H/chi' : Item(status=' ', wc_rev=7),
5284 'D/H/psi' : Item(status=' ', wc_rev=7),
5285 'D/H/omega' : Item(status='M ', wc_rev=7),
5287 expected_disk = wc.State('', {
5288 '' : Item(props={SVN_PROP_MERGEINFO : '/A:4-6'}),
5289 'B' : Item(),
5290 'mu' : Item("This is the file 'mu'.\n"),
5291 'B/E' : Item(),
5292 'B/E/alpha' : Item("This is the file 'alpha'.\n"),
5293 'B/E/beta' : Item("New content"),
5294 'B/lambda' : Item("This is the file 'lambda'.\n"),
5295 'B/F' : Item(),
5296 'C' : Item(),
5297 'D' : Item(),
5298 'D/G' : Item(),
5299 'D/G/pi' : Item("This is the file 'pi'.\n"),
5300 'D/G/rho' : Item("New content"),
5301 'D/G/tau' : Item("This is the file 'tau'.\n"),
5302 'D/gamma' : Item("This is the file 'gamma'.\n"),
5303 'D/H' : Item(),
5304 'D/H/chi' : Item("This is the file 'chi'.\n"),
5305 'D/H/psi' : Item("This is the file 'psi'.\n"),
5306 'D/H/omega' : Item("New content"),
5308 expected_skip = wc.State(short_A_COPY_path, { })
5310 os.chdir(svntest.main.work_dir)
5311 svntest.actions.run_and_verify_merge(short_A_COPY_path, '3', '6',
5312 sbox.repo_url + \
5313 '/A',
5314 expected_output,
5315 expected_disk,
5316 expected_status,
5317 expected_skip,
5318 None, None, None, None,
5319 None, 1)
5320 os.chdir(saved_cwd)
5322 # Reverse merge r5 out of A_COPY/B/E/beta. The mergeinfo on
5323 # A_COPY/B/E/beta which previously elided will now return,
5324 # minus r5 of course.
5325 expected_skip = wc.State(short_beta_COPY_path, { })
5327 os.chdir(svntest.main.work_dir)
5328 # run_and_verify_merge doesn't support merging to a file WCPATH
5329 # so use run_and_verify_svn.
5330 svntest.actions.run_and_verify_svn(None,
5331 expected_merge_output([[-5]],
5332 'U ' + short_beta_COPY_path + '\n'),
5333 [], 'merge', '-c-5',
5334 sbox.repo_url + '/A/B/E/beta',
5335 short_beta_COPY_path)
5336 os.chdir(saved_cwd)
5338 # Check beta's status and props.
5339 expected_status = wc.State(beta_COPY_path, {
5340 '' : Item(status='MM', wc_rev=7),
5342 svntest.actions.run_and_verify_status(beta_COPY_path, expected_status)
5344 svntest.actions.run_and_verify_svn(None, ["/A/B/E/beta:4,6\n"], [],
5345 'propget', SVN_PROP_MERGEINFO,
5346 beta_COPY_path)
5348 # Merge r5 back into A_COPY/B/E/beta. Now the mergeinfo on the merge
5349 # target (A_COPY/B/E/beta) is identical to it's nearest ancestor with
5350 # mergeinfo (A_COPY) and so the former should elide.
5351 os.chdir(svntest.main.work_dir)
5352 # run_and_verify_merge doesn't support merging to a file WCPATH
5353 # so use run_and_verify_svn.
5354 svntest.actions.run_and_verify_svn(None,
5355 expected_merge_output([[5]],
5356 'G ' + short_beta_COPY_path + '\n'),
5357 [], 'merge', '-c5',
5358 sbox.repo_url + '/A/B/E/beta',
5359 short_beta_COPY_path)
5360 os.chdir(saved_cwd)
5362 # Check beta's status and props.
5363 expected_status = wc.State(beta_COPY_path, {
5364 '' : Item(status=' M', wc_rev=7),
5366 svntest.actions.run_and_verify_status(beta_COPY_path, expected_status)
5368 # Once again A_COPY/B/E/beta has no mergeinfo.
5369 svntest.actions.run_and_verify_svn(None, [], [],
5370 'propget', SVN_PROP_MERGEINFO,
5371 beta_COPY_path)
5373 def mergeinfo_inheritance_and_discontinuous_ranges(sbox):
5374 "discontinuous merges produce correct mergeinfo"
5376 # When a merge target has no explicit mergeinfo and is subject
5377 # to multiple merges, the resulting mergeinfo on the target
5378 # should reflect the combination of the inherited mergeinfo
5379 # with each merge performed.
5381 # Also tests implied merge source and target when only a revision
5382 # range is specified.
5384 sbox.build()
5385 wc_dir = sbox.wc_dir
5387 # Some paths we'll care about
5388 A_url = sbox.repo_url + '/A'
5389 A_COPY_path = os.path.join(wc_dir, "A_COPY")
5390 D_COPY_path = os.path.join(wc_dir, "A_COPY", "D")
5391 A_COPY_rho_path = os.path.join(wc_dir, "A_COPY", "D", "G", "rho")
5393 expected_disk, expected_status = set_up_branch(sbox)
5395 # Merge r4 into A_COPY
5396 saved_cwd = os.getcwd()
5398 os.chdir(A_COPY_path)
5399 svntest.actions.run_and_verify_svn(None,
5400 expected_merge_output([[4]], 'U ' +
5401 os.path.join("D", "G", "rho") + '\n'),
5402 [], 'merge', '-c4', A_url)
5403 os.chdir(saved_cwd)
5405 # Check the results of the merge.
5406 expected_status.tweak("A_COPY", status=' M')
5407 expected_status.tweak("A_COPY/D/G/rho", status='M ')
5408 svntest.actions.run_and_verify_status(wc_dir, expected_status)
5409 svntest.actions.run_and_verify_svn(None, ["/A:4\n"], [],
5410 'propget', SVN_PROP_MERGEINFO,
5411 A_COPY_path)
5413 # Merge r2:6 into A_COPY/D
5415 # Search for the comment entitled "The Merge Kluge" elsewhere in
5416 # this file, to understand why we shorten and chdir() below.
5418 # A_COPY/D should inherit the mergeinfo '/A:4' from A_COPY
5419 # combine it with the discontinous merges performed directly on
5420 # it (A/D/ 2:3 and A/D 4:6) resulting in '/A/D:3-6'.
5421 short_D_COPY_path = shorten_path_kludge(D_COPY_path)
5422 expected_output = wc.State(short_D_COPY_path, {
5423 'H/psi' : Item(status='U '),
5424 'H/omega' : Item(status='U '),
5426 expected_status = wc.State(short_D_COPY_path, {
5427 '' : Item(status=' M', wc_rev=2),
5428 'G' : Item(status=' ', wc_rev=2),
5429 'G/pi' : Item(status=' ', wc_rev=2),
5430 'G/rho' : Item(status='M ', wc_rev=2),
5431 'G/tau' : Item(status=' ', wc_rev=2),
5432 'H' : Item(status=' ', wc_rev=2),
5433 'H/chi' : Item(status=' ', wc_rev=2),
5434 'H/psi' : Item(status='M ', wc_rev=2),
5435 'H/omega' : Item(status='M ', wc_rev=2),
5436 'gamma' : Item(status=' ', wc_rev=2),
5438 expected_disk = wc.State('', {
5439 '' : Item(props={SVN_PROP_MERGEINFO : '/A/D:3-6'}),
5440 'G' : Item(),
5441 'G/pi' : Item("This is the file 'pi'.\n"),
5442 'G/rho' : Item("New content"),
5443 'G/tau' : Item("This is the file 'tau'.\n"),
5444 'H' : Item(),
5445 'H/chi' : Item("This is the file 'chi'.\n"),
5446 'H/psi' : Item("New content"),
5447 'H/omega' : Item("New content"),
5448 'gamma' : Item("This is the file 'gamma'.\n")
5450 expected_skip = wc.State(short_D_COPY_path, { })
5452 os.chdir(svntest.main.work_dir)
5453 svntest.actions.run_and_verify_merge(short_D_COPY_path, '2', '6',
5454 sbox.repo_url + '/A/D',
5455 expected_output,
5456 expected_disk,
5457 expected_status,
5458 expected_skip,
5459 None, None, None, None,
5460 None, 1)
5461 os.chdir(saved_cwd)
5463 # Wipe the memory of a portion of the previous merge...
5464 ### It'd be nice to use 'merge --record-only' here, but we can't (yet)
5465 ### wipe all ranges for a file due to the bug pointed out in r24645.
5466 mu_copy_path = os.path.join(A_COPY_path, 'mu')
5467 svntest.actions.run_and_verify_svn(None,
5468 ["property '" + SVN_PROP_MERGEINFO
5469 + "' set on '" +
5470 mu_copy_path + "'\n"], [], 'propset',
5471 SVN_PROP_MERGEINFO, '', mu_copy_path)
5472 # ...and confirm that we can commit the wiped mergeinfo...
5473 expected_output = wc.State(wc_dir, {
5474 'A_COPY/mu' : Item(verb='Sending'),
5476 svntest.actions.run_and_verify_commit(wc_dir,
5477 expected_output,
5478 None,
5479 None,
5480 mu_copy_path)
5481 # ...and that the presence of the property is retained, even when
5482 # the value has been wiped.
5483 svntest.actions.run_and_verify_svn(None, ['\n'], [], 'propget',
5484 SVN_PROP_MERGEINFO, mu_copy_path)
5486 def merge_to_target_with_copied_children(sbox):
5487 "merge works when target has copied children"
5489 # Test for Issue #2754 Can't merge to target with copied/moved children
5491 sbox.build()
5492 wc_dir = sbox.wc_dir
5493 expected_disk, expected_status = set_up_branch(sbox)
5495 # Some paths we'll care about
5496 D_COPY_path = os.path.join(wc_dir, "A_COPY", "D")
5497 G_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "G")
5498 rho_COPY_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "G", "rho_copy")
5500 # URL to URL copy A_COPY/D/G/rho to A_COPY/D/G/rho_copy
5501 svntest.actions.run_and_verify_svn(None, None, [], 'copy',
5502 sbox.repo_url + '/A_COPY/D/G/rho',
5503 sbox.repo_url + '/A_COPY/D/G/rho_copy',
5504 '-m', 'copy')
5506 # Update WC.
5507 expected_output = wc.State(wc_dir,
5508 {'A_COPY/D/G/rho_copy' : Item(status='A ')})
5509 expected_disk.add({
5510 'A_COPY/D/G/rho_copy' : Item("This is the file 'rho'.\n", props={})
5512 expected_status.tweak(wc_rev=7)
5513 expected_status.add({'A_COPY/D/G/rho_copy' : Item(status=' ', wc_rev=7)})
5514 svntest.actions.run_and_verify_update(wc_dir,
5515 expected_output,
5516 expected_disk,
5517 expected_status,
5518 None, None, None,
5519 None, None, 1)
5521 # Merge r4 into A_COPY/D/G/rho_copy.
5523 # Search for the comment entitled "The Merge Kluge" elsewhere in
5524 # this file, to understand why we shorten and chdir() below.
5525 os.chdir(svntest.main.work_dir)
5526 short_rho_COPY_COPY_path = shorten_path_kludge(rho_COPY_COPY_path)
5527 svntest.actions.run_and_verify_svn(None,
5528 expected_merge_output([[4]],
5529 'U ' + short_rho_COPY_COPY_path +
5530 '\n'),
5531 [], 'merge', '-c4',
5532 sbox.repo_url + '/A/D/G/rho',
5533 short_rho_COPY_COPY_path)
5535 # Merge r3:5 into A_COPY/D/G.
5536 short_G_COPY_path = shorten_path_kludge(G_COPY_path)
5537 expected_output = wc.State(short_G_COPY_path, {
5538 'rho' : Item(status='U ')
5540 expected_status = wc.State(short_G_COPY_path, {
5541 '' : Item(status=' M', wc_rev=7),
5542 'pi' : Item(status=' ', wc_rev=7),
5543 'rho' : Item(status='M ', wc_rev=7),
5544 'rho_copy' : Item(status='MM', wc_rev=7),
5545 'tau' : Item(status=' ', wc_rev=7),
5547 expected_disk = wc.State('', {
5548 '' : Item(props={SVN_PROP_MERGEINFO : '/A/D/G:4-5'}),
5549 'pi' : Item("This is the file 'pi'.\n"),
5550 'rho' : Item("New content"),
5551 'rho_copy' : Item("New content",
5552 props={SVN_PROP_MERGEINFO : '/A/D/G/rho:4'}),
5553 'tau' : Item("This is the file 'tau'.\n"),
5555 expected_skip = wc.State(short_G_COPY_path, { })
5556 svntest.actions.run_and_verify_merge(short_G_COPY_path, '3', '5',
5557 sbox.repo_url + \
5558 '/A/D/G',
5559 expected_output,
5560 expected_disk,
5561 expected_status,
5562 expected_skip,
5563 None, None, None, None,
5564 None, 1)
5566 def merge_to_switched_path(sbox):
5567 "merge to switched path does not inherit or elide"
5569 # When the target of a merge is a switched path we don't inherit WC
5570 # mergeinfo from above the target or attempt to elide the mergeinfo
5571 # set on the target as a result of the merge.
5573 sbox.build()
5574 wc_dir = sbox.wc_dir
5575 wc_disk, wc_status = set_up_branch(sbox)
5577 # Some paths we'll care about
5578 A_COPY_D_path = os.path.join(wc_dir, "A_COPY", "D")
5579 G_COPY_path = os.path.join(wc_dir, "A", "D", "G_COPY")
5580 A_COPY_D_G_path = os.path.join(wc_dir, "A_COPY", "D", "G")
5581 A_COPY_D_G_rho_path = os.path.join(wc_dir, "A_COPY", "D", "G", "rho")
5583 expected = svntest.verify.UnorderedOutput(
5584 ["A " + os.path.join(G_COPY_path, "pi") + "\n",
5585 "A " + os.path.join(G_COPY_path, "rho") + "\n",
5586 "A " + os.path.join(G_COPY_path, "tau") + "\n",
5587 "Checked out revision 6.\n",
5588 "A " + G_COPY_path + "\n"])
5590 # r7 - Copy A/D/G to A/D/G_COPY and commit.
5591 svntest.actions.run_and_verify_svn(None, expected, [], 'copy',
5592 sbox.repo_url + "/A/D/G",
5593 G_COPY_path)
5595 expected_output = wc.State(wc_dir, {'A/D/G_COPY' : Item(verb='Adding')})
5596 wc_status.add({
5597 "A/D/G_COPY" : Item(status=' ', wc_rev=7),
5598 "A/D/G_COPY/pi" : Item(status=' ', wc_rev=7),
5599 "A/D/G_COPY/rho" : Item(status=' ', wc_rev=7),
5600 "A/D/G_COPY/tau" : Item(status=' ', wc_rev=7),
5603 svntest.actions.run_and_verify_commit(wc_dir, expected_output, wc_status,
5604 None, wc_dir)
5606 # r8 - modify and commit A/D/G_COPY/rho
5607 svntest.main.file_write(os.path.join(wc_dir, "A", "D", "G_COPY", "rho"),
5608 "New *and* improved rho content")
5609 expected_output = wc.State(wc_dir, {'A/D/G_COPY/rho' : Item(verb='Sending')})
5610 wc_status.tweak('A/D/G_COPY/rho', wc_rev=8)
5611 svntest.actions.run_and_verify_commit(wc_dir, expected_output, wc_status,
5612 None, wc_dir)
5614 # Switch A_COPY/D/G to A/D/G.
5615 wc_disk.add({
5616 "A" : Item(),
5617 "A/D/G_COPY" : Item(),
5618 "A/D/G_COPY/pi" : Item("This is the file 'pi'.\n"),
5619 "A/D/G_COPY/rho" : Item("New *and* improved rho content"),
5620 "A/D/G_COPY/tau" : Item("This is the file 'tau'.\n"),
5622 wc_disk.tweak('A_COPY/D/G/rho',contents="New content")
5623 wc_status.tweak("A_COPY/D/G", wc_rev=8, switched='S')
5624 wc_status.tweak("A_COPY/D/G/pi", wc_rev=8)
5625 wc_status.tweak("A_COPY/D/G/rho", wc_rev=8)
5626 wc_status.tweak("A_COPY/D/G/tau", wc_rev=8)
5627 expected_output = svntest.wc.State(sbox.wc_dir, {
5628 "A_COPY/D/G/rho" : Item(status='U '),
5630 svntest.actions.run_and_verify_switch(sbox.wc_dir, A_COPY_D_G_path,
5631 sbox.repo_url + "/A/D/G",
5632 expected_output, wc_disk, wc_status,
5633 None, None, None, None, None, 1)
5635 # Merge r8 from A/D/G_COPY into our switched target A_COPY/D/G.
5636 # A_COPY/D/G should get mergeinfo for r8 as a result of the merge,
5637 # but because it's switched should not inherit the mergeinfo from
5638 # its nearest WC ancestor with mergeinfo (A_COPY: svn:mergeinfo : /A:1)
5640 # Search for the comment entitled "The Merge Kluge" elsewhere in
5641 # this file, to understand why we shorten and chdir() below.
5642 short_G_COPY_path = shorten_path_kludge(A_COPY_D_G_path)
5643 expected_output = wc.State(short_G_COPY_path, {
5644 'rho' : Item(status='U ')
5646 # Note: A_COPY/D/G won't show as switched because of the Merge Kluge.
5647 expected_status = wc.State(short_G_COPY_path, {
5648 '' : Item(status=' M', wc_rev=8),
5649 'pi' : Item(status=' ', wc_rev=8),
5650 'rho' : Item(status='M ', wc_rev=8),
5651 'tau' : Item(status=' ', wc_rev=8),
5653 expected_disk = wc.State('', {
5654 '' : Item(props={SVN_PROP_MERGEINFO : '/A/D/G_COPY:8'}),
5655 'pi' : Item("This is the file 'pi'.\n"),
5656 'rho' : Item("New *and* improved rho content"),
5657 'tau' : Item("This is the file 'tau'.\n"),
5659 expected_skip = wc.State(short_G_COPY_path, { })
5660 saved_cwd = os.getcwd()
5662 os.chdir(svntest.main.work_dir)
5663 svntest.actions.run_and_verify_merge(short_G_COPY_path, '7', '8',
5664 sbox.repo_url + '/A/D/G_COPY',
5665 expected_output, expected_disk,
5666 expected_status, expected_skip,
5667 None, None, None, None, None, 1)
5668 os.chdir(saved_cwd)
5670 # Check that the mergeinfo set on a target doesn't elide when that
5671 # target is switched.
5673 # Revert the previous merge and manually set 'svn:mergeinfo : '
5674 # on 'merge_tests-1\A_COPY\D'. Now merge -c-4 from /A/D/G into A_COPY/D/G.
5675 # This should still set 'svn:mergeinfo : ' on
5676 # 'merge_tests-1\A_COPY\D\G'. This would normally elide to A_COPY/D,
5677 # but since A_COPY/D/G is switched it should not.
5678 svntest.actions.run_and_verify_svn(None,
5679 ["Reverted '" + A_COPY_D_G_path+ "'\n",
5680 "Reverted '" + A_COPY_D_G_rho_path +
5681 "'\n"],
5682 [], 'revert', '-R', wc_dir)
5683 svntest.actions.run_and_verify_svn(None,
5684 ["property '" + SVN_PROP_MERGEINFO +
5685 "' set on '" + A_COPY_D_path+ "'" +
5686 "\n"], [], 'ps', SVN_PROP_MERGEINFO,
5687 '', A_COPY_D_path)
5688 svntest.actions.run_and_verify_svn(None,
5689 expected_merge_output([[-4]],
5690 'U ' + A_COPY_D_G_rho_path + '\n'),
5691 [], 'merge', '-c-4',
5692 sbox.repo_url + '/A/D/G_COPY',
5693 A_COPY_D_G_path)
5694 wc_status.tweak("A_COPY/D", status=' M')
5695 wc_status.tweak("A_COPY/D/G", status=' M')
5696 wc_status.tweak("A_COPY/D/G/rho", status='M ')
5697 svntest.actions.run_and_verify_status(wc_dir, wc_status)
5698 expected = svntest.verify.UnorderedOutput(
5699 ["A " + os.path.join(G_COPY_path, "pi") + "\n",
5700 "A " + os.path.join(G_COPY_path, "rho") + "\n",
5701 "A " + os.path.join(G_COPY_path, "tau") + "\n",
5702 "Checked out revision 6.\n",
5703 "A " + G_COPY_path + "\n"])
5704 expected = svntest.verify.UnorderedOutput(
5705 ["Properties on '" + A_COPY_D_path + "':\n",
5706 " " + SVN_PROP_MERGEINFO + " : \n",
5707 "Properties on '" + A_COPY_D_G_path + "':\n",
5708 " " + SVN_PROP_MERGEINFO +" : \n"])
5709 svntest.actions.run_and_verify_svn(None,
5710 expected, [],
5711 'pl', '-vR', A_COPY_D_path)
5713 # Test for issues
5715 # 2823: Account for mergeinfo differences for switched
5716 # directories when gathering mergeinfo
5718 # 2839: Support non-inheritable mergeinfo revision ranges
5719 def merge_to_path_with_switched_children(sbox):
5720 "merge to path with switched children"
5722 # Merging to a target with switched children requires special handling
5723 # to keep mergeinfo correct:
5725 # 1) If the target of a merge has switched children without explicit
5726 # mergeinfo, the switched children should get mergeinfo set on
5727 # them as a result of the merge. This mergeinfo includes the
5728 # mergeinfo resulting from the merge *and* any mergeinfo inherited
5729 # from the repos for the switched path.
5731 # 2) Mergeinfo on switched children should never elide.
5733 # 3) The path the switched child overrides cannot be modified by the
5734 # merge (it isn't present in the WC) so should not inherit any
5735 # mergeinfo added as a result of the merge. To prevent this, the
5736 # immediate parent of any switched child should have non-inheritable
5737 # mergeinfo added/modified for the merge performed.
5739 # 4) Because of 3, siblings of switched children will not inherit the
5740 # mergeinfo resulting from the merge, so must get their own, full set
5741 # of mergeinfo.
5743 sbox.build()
5744 wc_dir = sbox.wc_dir
5745 wc_disk, wc_status = set_up_branch(sbox, False, 3)
5747 # Some paths we'll care about
5748 D_path = os.path.join(wc_dir, "A", "D")
5749 A_COPY_path = os.path.join(wc_dir, "A_COPY")
5750 A_COPY_beta_path = os.path.join(wc_dir, "A_COPY", "B", "E", "beta")
5751 A_COPY_chi_path = os.path.join(wc_dir, "A_COPY", "D", "H", "chi")
5752 A_COPY_omega_path = os.path.join(wc_dir, "A_COPY", "D", "H", "omega")
5753 A_COPY_psi_path = os.path.join(wc_dir, "A_COPY", "D", "H", "psi")
5754 A_COPY_G_path = os.path.join(wc_dir, "A_COPY", "D", "G")
5755 A_COPY_rho_path = os.path.join(wc_dir, "A_COPY", "D", "G", "rho")
5756 A_COPY_H_path = os.path.join(wc_dir, "A_COPY", "D", "H")
5757 A_COPY_D_path = os.path.join(wc_dir, "A_COPY", "D")
5758 A_COPY_gamma_path = os.path.join(wc_dir, "A_COPY", "D", "gamma")
5759 H_COPY_2_path = os.path.join(wc_dir, "A_COPY_2", "D", "H")
5761 svntest.actions.run_and_verify_svn(None, ["At revision 8.\n"], [], 'up',
5762 wc_dir)
5763 wc_status.tweak(wc_rev=8)
5765 # Switch a file and dir path in the branch:
5767 # Switch A_COPY/D/G to A_COPY_2/D/G.
5768 wc_status.tweak("A_COPY/D/G", switched='S')
5769 expected_output = svntest.wc.State(sbox.wc_dir, {})
5770 svntest.actions.run_and_verify_switch(sbox.wc_dir, A_COPY_G_path,
5771 sbox.repo_url + "/A_COPY_2/D/G",
5772 expected_output, wc_disk, wc_status,
5773 None, None, None, None, None, 1)
5775 # Switch A_COPY/D/G/rho to A_COPY_3/D/G/rho.
5776 wc_status.tweak("A_COPY/D/G/rho", switched='S')
5777 expected_output = svntest.wc.State(sbox.wc_dir, {})
5778 svntest.actions.run_and_verify_switch(sbox.wc_dir, A_COPY_rho_path,
5779 sbox.repo_url + "/A_COPY_3/D/G/rho",
5780 expected_output, wc_disk, wc_status,
5781 None, None, None, None, None, 1)
5783 # Switch A_COPY/D/H/psi to A_COPY_2/D/H/psi.
5784 wc_status.tweak("A_COPY/D/H/psi", switched='S')
5785 expected_output = svntest.wc.State(sbox.wc_dir, {})
5786 svntest.actions.run_and_verify_switch(sbox.wc_dir, A_COPY_psi_path,
5787 sbox.repo_url + "/A_COPY_2/D/H/psi",
5788 expected_output, wc_disk, wc_status,
5789 None, None, None, None, None, 1)
5791 # Target with switched file child:
5793 # Merge r8 from A/D/H into A_COPY/D/H. The switched child of
5794 # A_COPY/D/H, file A_COPY/D/H/psi (which has no mergeinfo prior
5795 # to the merge), should get their own mergeinfo, both r8 from the
5796 # merge itself, and r1-2 inherited from A_COPY_2 in the repository.
5798 # A_COPY/D/H/psi's parent A_COPY/D/H has no pre-exiting explicit
5799 # mergeinfo so should get its own mergeinfo, made up of r1 inherited
5800 # from A_COPY and the non-inheritable r8 resulting from the merge.
5802 # A_COPY/D/H/psi's two unswitched siblings, A_COPY/D/H/chi and
5803 # A_COPY/D/H/omega won't inherit r8 from A_COPY/D/H, so need their
5804 # own mergeinfo.
5806 # Search for the comment entitled "The Merge Kluge" elsewhere in
5807 # this file, to understand why we shorten and chdir() below.
5808 short_H_COPY_path = shorten_path_kludge(A_COPY_H_path)
5809 expected_output = wc.State(short_H_COPY_path, {
5810 'omega' : Item(status='U ')
5812 expected_status = wc.State(short_H_COPY_path, {
5813 '' : Item(status=' M', wc_rev=8),
5814 'psi' : Item(status=' M', wc_rev=8, switched='S'),
5815 'omega' : Item(status='MM', wc_rev=8),
5816 'chi' : Item(status=' M', wc_rev=8),
5818 expected_disk = wc.State('', {
5819 '' : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:8*'}),
5820 'psi' : Item("This is the file 'psi'.\n",
5821 props={SVN_PROP_MERGEINFO : '/A/D/H/psi:8'}),
5822 'omega' : Item("New content",
5823 props={SVN_PROP_MERGEINFO : '/A/D/H/omega:8'}),
5824 'chi' : Item("This is the file 'chi'.\n",
5825 props={SVN_PROP_MERGEINFO : '/A/D/H/chi:8'}),
5827 expected_skip = wc.State(short_H_COPY_path, { })
5828 saved_cwd = os.getcwd()
5830 os.chdir(svntest.main.work_dir)
5832 svntest.actions.run_and_verify_merge(short_H_COPY_path, '7', '8',
5833 sbox.repo_url + '/A/D/H',
5834 expected_output, expected_disk,
5835 expected_status, expected_skip,
5836 None, None, None, None, None, 1)
5837 os.chdir(saved_cwd)
5839 # Target with switched dir child:
5841 # Merge r6 from A/D into A_COPY/D. The switched child of A_COPY/D,
5842 # directory A_COPY/D/G (which has no mergeinfo prior to the merge),
5843 # should get its own mergeinfo, both for r6 from the merge itself and
5844 # r1-2 from the repository.
5846 # A_COPY/D/G's parent A_COPY/D has no pre-exiting explicit
5847 # mergeinfo so should get its own mergeinfo, made up of r1 inherited
5848 # from A_COPY and the non-inheritable r5 resulting from the merge.
5850 # A_COPY/D/G's two unswitched siblings, A_COPY/D/gamma and A_COPY/D/H
5851 # won't inherit r5 from A_COPY/D, so need their own mergeinfo
5852 # added/updated respectively. A_COPY/D/H itself has a switched child
5853 # so r5 is set as non-inheritable on it. All of A_COPY/D/H's children,
5854 # which have explict mergeinfo from the previous merge, get r5 in their
5855 # mergeinfo.
5856 short_D_COPY_path = shorten_path_kludge(A_COPY_D_path)
5857 expected_output = wc.State(short_D_COPY_path, {
5858 'G/rho' : Item(status='U ')
5860 expected_status_D = wc.State(short_D_COPY_path, {
5861 '' : Item(status=' M', wc_rev=8),
5862 'H' : Item(status=' M', wc_rev=8),
5863 'H/chi' : Item(status=' M', wc_rev=8),
5864 'H/omega' : Item(status='MM', wc_rev=8),
5865 'H/psi' : Item(status=' M', wc_rev=8, switched='S'),
5866 'G' : Item(status=' M', wc_rev=8, switched='S'),
5867 'G/pi' : Item(status=' M', wc_rev=8),
5868 'G/rho' : Item(status='MM', wc_rev=8, switched='S'),
5869 'G/tau' : Item(status=' M', wc_rev=8),
5870 'gamma' : Item(status=' M', wc_rev=8),
5872 expected_disk_D = wc.State('', {
5873 '' : Item(props={SVN_PROP_MERGEINFO : '/A/D:6*'}),
5874 'H' : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:6*,8*'}),
5875 'H/chi' : Item("This is the file 'chi'.\n",
5876 props={SVN_PROP_MERGEINFO : '/A/D/H/chi:6,8'}),
5877 'H/omega' : Item("New content",
5878 props={SVN_PROP_MERGEINFO : '/A/D/H/omega:6,8'}),
5879 'H/psi' : Item("This is the file 'psi'.\n",
5880 props={SVN_PROP_MERGEINFO : '/A/D/H/psi:6,8'}),
5881 'G' : Item(props={SVN_PROP_MERGEINFO : '/A/D/G:6*'}),
5882 'G/pi' : Item("This is the file 'pi'.\n",
5883 props={SVN_PROP_MERGEINFO : '/A/D/G/pi:6'}),
5884 'G/rho' : Item("New content",
5885 props={SVN_PROP_MERGEINFO : '/A/D/G/rho:6'}),
5886 'G/tau' : Item("This is the file 'tau'.\n",
5887 props={SVN_PROP_MERGEINFO : '/A/D/G/tau:6'}),
5888 'gamma' : Item("This is the file 'gamma'.\n",
5889 props={SVN_PROP_MERGEINFO : '/A/D/gamma:6'}),
5891 expected_skip_D = wc.State(short_D_COPY_path, { })
5892 os.chdir(svntest.main.work_dir)
5893 svntest.actions.run_and_verify_merge(short_D_COPY_path, '5', '6',
5894 sbox.repo_url + '/A/D',
5895 expected_output, expected_disk_D,
5896 expected_status_D, expected_skip_D,
5897 None, None, None, None, None, 1)
5899 # Merge r5 from A/D into A_COPY/D. A_COPY/D's switched child A_COPY/D/G
5900 # gets r5 as does A_COPY/D/G's unswitched sibling A_COPY/D/gamma. Since
5901 # A_COPY/D has a switched child, it gets an uninheritable r5. Every other
5902 # path with existing mergeinfo already has r8 so are unchanged.
5904 # A_COPY/D/H/chi and A_COPY/D/H/omega both have mergeinfo for r1,6,8 but
5905 # won't elide to A_COPY/D/H becuase the latter's mergeinfo, while
5906 # encompassing the same ranges, r1,6*,8*, has some non-inheritable ranges.
5907 # The same is true of A_COPY/D/gamma and A_COPY/D.
5908 expected_output = wc.State(short_D_COPY_path, {
5909 'H/psi' : Item(status='U ')})
5910 expected_disk_D.tweak('', props={SVN_PROP_MERGEINFO : '/A/D:5-6*'})
5911 expected_disk_D.tweak('G', props={SVN_PROP_MERGEINFO : '/A/D/G:5-6*'})
5912 expected_disk_D.tweak('G/pi',
5913 props={SVN_PROP_MERGEINFO : '/A/D/G/pi:5-6'})
5914 expected_disk_D.tweak('G/rho',
5915 props={SVN_PROP_MERGEINFO : '/A/D/G/rho:5-6'})
5916 expected_disk_D.tweak('G/tau',
5917 props={SVN_PROP_MERGEINFO : '/A/D/G/tau:5-6'})
5918 expected_disk_D.tweak('H', props={SVN_PROP_MERGEINFO : '/A/D/H:5-6*,8*'})
5919 expected_disk_D.tweak('gamma',
5920 props={SVN_PROP_MERGEINFO : '/A/D/gamma:5-6'})
5921 expected_disk_D.tweak('H/chi',
5922 props={SVN_PROP_MERGEINFO :'/A/D/H/chi:5-6,8'})
5923 expected_disk_D.tweak('H/psi', contents="New content",
5924 props={SVN_PROP_MERGEINFO :'/A/D/H/psi:5-6,8'})
5925 expected_disk_D.tweak('H/omega',
5926 props={SVN_PROP_MERGEINFO :'/A/D/H/omega:5-6,8'})
5927 expected_status_D.tweak('H/psi', status='MM')
5928 svntest.actions.run_and_verify_merge(short_D_COPY_path, '4', '5',
5929 sbox.repo_url + '/A/D',
5930 expected_output, expected_disk_D,
5931 expected_status_D, expected_skip_D,
5932 None, None, None, None, None, 1)
5933 os.chdir(saved_cwd)
5935 # Finally, merge r4:8 into A_COPY. A_COPY gets r5-8 added and every path
5936 # under it with with explicit mergeinfo gets r5,7 added (they all have r6,8
5937 # already). Again there is no elision, since the only possibilities, e.g.
5938 # A_COPY/D/H's '/A/D/H:1,5-8*' to A_COPY/D's '/A/D:1,5-8*', involve
5939 # non-inheritable mergeinfo one ond side or the other.
5940 short_A_COPY_path = shorten_path_kludge(A_COPY_path)
5941 expected_output = wc.State(short_A_COPY_path, {
5942 'B/E/beta' : Item(status='U ')
5944 expected_status = wc.State(short_A_COPY_path, {
5945 '' : Item(status=' M', wc_rev=8),
5946 'B' : Item(status=' ', wc_rev=8),
5947 'mu' : Item(status=' ', wc_rev=8),
5948 'B/E' : Item(status=' ', wc_rev=8),
5949 'B/E/alpha' : Item(status=' ', wc_rev=8),
5950 'B/E/beta' : Item(status='M ', wc_rev=8),
5951 'B/lambda' : Item(status=' ', wc_rev=8),
5952 'B/F' : Item(status=' ', wc_rev=8),
5953 'C' : Item(status=' ', wc_rev=8),
5954 'D' : Item(status=' M', wc_rev=8),
5955 'D/G' : Item(status=' M', wc_rev=8, switched='S'),
5956 'D/G/pi' : Item(status=' M', wc_rev=8),
5957 'D/G/rho' : Item(status='MM', wc_rev=8, switched='S'),
5958 'D/G/tau' : Item(status=' M', wc_rev=8),
5959 'D/gamma' : Item(status=' M', wc_rev=8),
5960 'D/H' : Item(status=' M', wc_rev=8),
5961 'D/H/chi' : Item(status=' M', wc_rev=8),
5962 'D/H/psi' : Item(status='MM', wc_rev=8, switched='S'),
5963 'D/H/omega' : Item(status='MM', wc_rev=8),
5965 expected_disk = wc.State('', {
5966 '' : Item(props={SVN_PROP_MERGEINFO : '/A:5-8'}),
5967 'B' : Item(),
5968 'mu' : Item("This is the file 'mu'.\n"),
5969 'B/E' : Item(),
5970 'B/E/alpha' : Item("This is the file 'alpha'.\n"),
5971 'B/E/beta' : Item("New content"),
5972 'B/lambda' : Item("This is the file 'lambda'.\n"),
5973 'B/F' : Item(),
5974 'C' : Item(),
5975 'D' : Item(props={SVN_PROP_MERGEINFO : '/A/D:5-8*'}),
5976 'D/G' : Item(props={SVN_PROP_MERGEINFO : '/A/D/G:5-8*'}),
5977 'D/G/pi' : Item("This is the file 'pi'.\n",
5978 props={SVN_PROP_MERGEINFO : '/A/D/G/pi:5-8'}),
5979 'D/G/rho' : Item("New content",
5980 props={SVN_PROP_MERGEINFO : '/A/D/G/rho:5-8'}),
5981 'D/G/tau' : Item("This is the file 'tau'.\n",
5982 props={SVN_PROP_MERGEINFO : '/A/D/G/tau:5-8'}),
5983 'D/gamma' : Item("This is the file 'gamma'.\n",
5984 props={SVN_PROP_MERGEINFO : '/A/D/gamma:5-8'}),
5985 'D/H' : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:5-8*'}),
5986 'D/H/chi' : Item("This is the file 'chi'.\n",
5987 props={SVN_PROP_MERGEINFO : '/A/D/H/chi:5-8'}),
5988 'D/H/psi' : Item("New content",
5989 props={SVN_PROP_MERGEINFO : '/A/D/H/psi:5-8'}),
5990 'D/H/omega' : Item("New content",
5991 props={SVN_PROP_MERGEINFO : '/A/D/H/omega:5-8'}),
5993 expected_skip = wc.State(short_A_COPY_path, { })
5994 os.chdir(svntest.main.work_dir)
5995 svntest.actions.run_and_verify_merge(short_A_COPY_path, '4', '8',
5996 sbox.repo_url + '/A',
5997 expected_output, expected_disk,
5998 expected_status, expected_skip,
5999 None, None, None, None, None, 1)
6001 os.chdir(saved_cwd)
6003 # Commit changes thus far.
6004 expected_output = svntest.wc.State(wc_dir, {
6005 'A_COPY' : Item(verb='Sending'),
6006 'A_COPY/B/E/beta' : Item(verb='Sending'),
6007 'A_COPY/D' : Item(verb='Sending'),
6008 'A_COPY/D/gamma' : Item(verb='Sending'),
6009 'A_COPY/D/G' : Item(verb='Sending'),
6010 'A_COPY/D/G/pi' : Item(verb='Sending'),
6011 'A_COPY/D/G/rho' : Item(verb='Sending'),
6012 'A_COPY/D/G/tau' : Item(verb='Sending'),
6013 'A_COPY/D/H' : Item(verb='Sending'),
6014 'A_COPY/D/H/chi' : Item(verb='Sending'),
6015 'A_COPY/D/H/omega' : Item(verb='Sending'),
6016 'A_COPY/D/H/psi' : Item(verb='Sending'),
6018 wc_status.tweak('A_COPY', 'A_COPY/B/E/beta', 'A_COPY/D', 'A_COPY/D/gamma',
6019 'A_COPY/D/G', 'A_COPY/D/G/pi', 'A_COPY/D/G/rho',
6020 'A_COPY/D/G/tau','A_COPY/D/H', 'A_COPY/D/H/chi',
6021 'A_COPY/D/H/omega', 'A_COPY/D/H/psi',
6022 wc_rev=9)
6023 svntest.actions.run_and_verify_commit(wc_dir, expected_output, wc_status,
6024 None, wc_dir)
6026 # Unswitch A_COPY/D/H/psi.
6027 expected_output = svntest.wc.State(wc_dir, {
6028 'A_COPY/D/H/psi' : Item(status='UU')})
6029 wc_status.tweak("A_COPY/D/H/psi", switched=None, wc_rev=9)
6030 wc_disk.tweak("A_COPY",
6031 props={SVN_PROP_MERGEINFO : '/A:5-8'})
6032 wc_disk.tweak("A_COPY/B/E/beta",
6033 contents="New content")
6034 wc_disk.tweak("A_COPY/D",
6035 props={SVN_PROP_MERGEINFO : '/A/D:5-8*'})
6036 wc_disk.tweak("A_COPY/D/gamma",
6037 props={SVN_PROP_MERGEINFO : '/A/D/gamma:5-8'})
6038 wc_disk.tweak("A_COPY/D/G",
6039 props={SVN_PROP_MERGEINFO : '/A/D/G:5-8*'})
6040 wc_disk.tweak("A_COPY/D/G/pi",
6041 props={SVN_PROP_MERGEINFO : '/A/D/G/pi:5-8'})
6042 wc_disk.tweak("A_COPY/D/G/rho",
6043 contents="New content",
6044 props={SVN_PROP_MERGEINFO : '/A/D/G/rho:5-8'})
6045 wc_disk.tweak("A_COPY/D/G/tau",
6046 props={SVN_PROP_MERGEINFO : '/A/D/G/tau:5-8'})
6047 wc_disk.tweak("A_COPY/D/H",
6048 props={SVN_PROP_MERGEINFO : '/A/D/H:5-8*'})
6049 wc_disk.tweak("A_COPY/D/H/chi",
6050 props={SVN_PROP_MERGEINFO : '/A/D/H/chi:5-8'})
6051 wc_disk.tweak("A_COPY/D/H/omega",
6052 contents="New content",
6053 props={SVN_PROP_MERGEINFO : '/A/D/H/omega:5-8'})
6054 wc_disk.tweak("A_COPY_2", props={})
6055 svntest.actions.run_and_verify_switch(sbox.wc_dir, A_COPY_psi_path,
6056 sbox.repo_url + "/A_COPY/D/H/psi",
6057 expected_output, wc_disk, wc_status,
6058 None, None, None, None, None, 1)
6060 # Non-inheritable mergeinfo ranges on a target don't prevent repeat
6061 # merges of that range on the target's children.
6063 # Non-inheritable mergeinfo ranges on a target are removed if the target
6064 # no longer has any switched children and a repeat merge is performed.
6066 # Merge r4:8 from A/D/H into A_COPY/D/H. A_COPY/D/H already has mergeinfo
6067 # for r4:8 but it is marked as uninheritable so the repeat merge is
6068 # allowed on its children, notably the now unswitched A_COPY/D/H/psi.
6069 # Since A_COPY/D/H no longer has any switched children and the merge of
6070 # r4:8 has been repeated the previously uninheritable ranges 5-8* on
6071 # A_COPY/D/H are made inheritable. This also means that the mergeinfo on
6072 # A_COPY/D/H's children will elide to it.
6073 short_H_COPY_path = shorten_path_kludge(A_COPY_H_path)
6074 expected_output = wc.State(short_H_COPY_path, {
6075 'psi' : Item(status='U ')
6077 expected_status = wc.State(short_H_COPY_path, {
6078 '' : Item(status=' M', wc_rev=9),
6079 'psi' : Item(status='M ', wc_rev=9),
6080 'omega' : Item(status=' M', wc_rev=9),
6081 'chi' : Item(status=' M', wc_rev=9),
6083 expected_disk = wc.State('', {
6084 '' : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:5-8'}),
6085 'psi' : Item("New content"),
6086 'omega' : Item("New content"),
6087 'chi' : Item("This is the file 'chi'.\n"),
6089 expected_skip = wc.State(short_H_COPY_path, { })
6090 os.chdir(svntest.main.work_dir)
6091 svntest.actions.run_and_verify_merge(short_H_COPY_path, '4', '8',
6092 sbox.repo_url + '/A/D/H',
6093 expected_output, expected_disk,
6094 expected_status, expected_skip,
6095 None, None, None, None, None, 1)
6096 os.chdir(saved_cwd)
6098 # Non-inheritable mergeinfo ranges on a target do prevent repeat
6099 # merges on the target itself.
6101 # Add a prop A/D and commit it as r10. Merge r10 into A_COPY/D. Since
6102 # A_COPY/D has a switched child it gets r10 added as a non-inheritable
6103 # range. Repeat the same merge checking that no repeat merge is
6104 # attempted on A_COPY/D.
6105 svntest.actions.run_and_verify_svn(None,
6106 ["property 'prop:name' set on '" +
6107 D_path + "'\n"], [], 'ps',
6108 'prop:name', 'propval', D_path)
6109 expected_output = svntest.wc.State(wc_dir, {
6110 'A/D' : Item(verb='Sending'),
6111 'A_COPY/D/H' : Item(verb='Sending'),
6112 'A_COPY/D/H/chi' : Item(verb='Sending'),
6113 'A_COPY/D/H/psi' : Item(verb='Sending'),
6114 'A_COPY/D/H/omega' : Item(verb='Sending'),
6116 wc_status.tweak('A_COPY/D', wc_rev=9)
6117 wc_status.tweak('A/D', 'A_COPY/D/H', 'A_COPY/D/H/chi', 'A_COPY/D/H/psi',
6118 'A_COPY/D/H/omega', wc_rev=10)
6119 svntest.actions.run_and_verify_commit(wc_dir, expected_output, wc_status,
6120 None, wc_dir)
6121 expected_output = wc.State(short_D_COPY_path, {
6122 '' : Item(status=' U')
6124 # Reuse expected status and disk from last merge to A_COPY/D
6125 expected_status_D.tweak('', 'gamma', status=' M', wc_rev=9)
6126 expected_status_D.tweak('H', status=' M', wc_rev=10)
6127 expected_status_D.tweak('H/psi', 'H/chi', 'H/omega', status=' ', wc_rev=10,
6128 switched=None)
6129 expected_status_D.tweak('G', switched='S', status=' M', wc_rev=9)
6130 expected_status_D.tweak('G/tau', 'G/pi', 'G/rho', status=' M', wc_rev=9)
6131 expected_disk_D.tweak('', props={SVN_PROP_MERGEINFO : '/A/D:5-8*,10*',
6132 "prop:name" : "propval"})
6133 expected_disk_D.tweak('G',
6134 props={SVN_PROP_MERGEINFO : '/A/D/G:5-8*,10*'})
6135 expected_disk_D.tweak('G/pi',
6136 props={SVN_PROP_MERGEINFO : '/A/D/G/pi:5-8,10'})
6137 expected_disk_D.tweak('G/rho',
6138 props={SVN_PROP_MERGEINFO : '/A/D/G/rho:5-8,10'})
6139 expected_disk_D.tweak('G/tau',
6140 props={SVN_PROP_MERGEINFO : '/A/D/G/tau:5-8,10'})
6141 expected_disk_D.tweak('H', props={SVN_PROP_MERGEINFO : '/A/D/H:5-8,10'})
6142 expected_disk_D.tweak('gamma',
6143 props={SVN_PROP_MERGEINFO : '/A/D/gamma:5-8,10'})
6144 expected_disk_D.tweak('H/chi', 'H/omega', props={})
6145 expected_disk_D.tweak('H/psi', contents="New content", props={})
6146 os.chdir(svntest.main.work_dir)
6147 svntest.actions.run_and_verify_merge(short_D_COPY_path, '9', '10',
6148 sbox.repo_url + '/A/D',
6149 expected_output, expected_disk_D,
6150 expected_status_D, expected_skip_D,
6151 None, None, None, None, None, 1)
6152 # Repeated merge is a no-op.
6153 expected_output = wc.State(short_D_COPY_path, {})
6154 svntest.actions.run_and_verify_merge(short_D_COPY_path, '9', '10',
6155 sbox.repo_url + '/A/D',
6156 expected_output, expected_disk_D,
6157 expected_status_D, expected_skip_D,
6158 None, None, None, None, None, 1)
6159 os.chdir(saved_cwd)
6162 # Test for issue 2047: Merge from parent dir fails while it succeeds from
6163 # the direct dir
6164 def merge_with_implicit_target_file(sbox):
6165 "merge a change to a file, using relative path"
6167 sbox.build()
6168 wc_dir = sbox.wc_dir
6170 # Make a change to A/mu, then revert it using 'svn merge -r 2:1 A/mu'
6172 # change A/mu and commit
6173 A_path = os.path.join(wc_dir, 'A')
6174 mu_path = os.path.join(A_path, 'mu')
6176 svntest.main.file_append(mu_path, "A whole new line.\n")
6178 expected_output = svntest.wc.State(wc_dir, {
6179 'A/mu' : Item(verb='Sending'),
6181 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
6182 expected_status.tweak('A/mu', wc_rev=2)
6183 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
6184 expected_status, None, wc_dir)
6186 # Update to revision 2.
6187 svntest.actions.run_and_verify_svn(None, None, [], 'update', wc_dir)
6189 # Revert the change committed in r2
6190 os.chdir(wc_dir)
6192 # run_and_verify_merge doesn't accept file paths.
6193 svntest.actions.run_and_verify_svn(None, None, [], 'merge', '-r', '2:1',
6194 'A/mu')
6196 # Test practical application of issue #2769 fix, empty rev range elision,
6197 # and elision to the repos.
6198 def empty_mergeinfo(sbox):
6199 "mergeinfo can explicitly be empty"
6201 # A bit o' history: The fix for issue #2769 originally permitted mergeinfo
6202 # with empty range lists and as a result we permitted partial elision and
6203 # had a whole slew of tests here for that. But the fix of issue #3029 now
6204 # prevents svn ps or svn merge from creating mergeinfo with paths mapped to
6205 # empty ranges, only empty mergeinfo is allowed. As a result this test now
6206 # covers the following areas:
6208 # A) Merging a set of revisions into a path, then reverse merging the
6209 # same set out of a subtree of path results in empty mergeinfo
6210 # (i.e. "") on the subtree.
6212 # B) Empty mergeinfo elides to empty mergeinfo.
6214 # C) If a merge sets empty mergeinfo on its target and that target has
6215 # no ancestor in either the WC or the repository with explict
6216 # mergeinfo, then the target's mergeinfo is removed (a.k.a. elides
6217 # to nothing).
6218 sbox.build()
6219 wc_dir = sbox.wc_dir
6220 wc_disk, wc_status = set_up_branch(sbox)
6222 # Some paths we'll care about
6223 A_COPY_path = os.path.join(wc_dir, "A_COPY")
6224 H_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "H")
6225 psi_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "H", "psi")
6226 rho_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "G", "rho")
6228 # Test area A -- Merge r2:4 into A_COPY then reverse merge 4:2 to
6229 # A_COPY/D/G. A_COPY/D/G should end up with empty mergeinfo to
6230 # override that of A_COPY.
6232 # Search for the comment entitled "The Merge Kluge" elsewhere in
6233 # this file, to understand why we shorten and chdir() below.
6234 short_A_COPY_path = shorten_path_kludge(A_COPY_path)
6235 expected_output = wc.State(short_A_COPY_path, {
6236 'D/H/psi' : Item(status='U '),
6237 'D/G/rho' : Item(status='U '),
6239 expected_status = wc.State(short_A_COPY_path, {
6240 '' : Item(status=' M', wc_rev=2),
6241 'B' : Item(status=' ', wc_rev=2),
6242 'mu' : Item(status=' ', wc_rev=2),
6243 'B/E' : Item(status=' ', wc_rev=2),
6244 'B/E/alpha' : Item(status=' ', wc_rev=2),
6245 'B/E/beta' : Item(status=' ', wc_rev=2),
6246 'B/lambda' : Item(status=' ', wc_rev=2),
6247 'B/F' : Item(status=' ', wc_rev=2),
6248 'C' : Item(status=' ', wc_rev=2),
6249 'D' : Item(status=' ', wc_rev=2),
6250 'D/G' : Item(status=' ', wc_rev=2),
6251 'D/G/pi' : Item(status=' ', wc_rev=2),
6252 'D/G/rho' : Item(status='M ', wc_rev=2),
6253 'D/G/tau' : Item(status=' ', wc_rev=2),
6254 'D/gamma' : Item(status=' ', wc_rev=2),
6255 'D/H' : Item(status=' ', wc_rev=2),
6256 'D/H/chi' : Item(status=' ', wc_rev=2),
6257 'D/H/psi' : Item(status='M ', wc_rev=2),
6258 'D/H/omega' : Item(status=' ', wc_rev=2),
6260 expected_disk = wc.State('', {
6261 '' : Item(props={SVN_PROP_MERGEINFO : '/A:3-4'}),
6262 'B' : Item(),
6263 'mu' : Item("This is the file 'mu'.\n"),
6264 'B/E' : Item(),
6265 'B/E/alpha' : Item("This is the file 'alpha'.\n"),
6266 'B/E/beta' : Item("This is the file 'beta'.\n"),
6267 'B/lambda' : Item("This is the file 'lambda'.\n"),
6268 'B/F' : Item(),
6269 'C' : Item(),
6270 'D' : Item(),
6271 'D/G' : Item(),
6272 'D/G/pi' : Item("This is the file 'pi'.\n"),
6273 'D/G/rho' : Item("New content"),
6274 'D/G/tau' : Item("This is the file 'tau'.\n"),
6275 'D/gamma' : Item("This is the file 'gamma'.\n"),
6276 'D/H' : Item(),
6277 'D/H/chi' : Item("This is the file 'chi'.\n"),
6278 'D/H/psi' : Item("New content"),
6279 'D/H/omega' : Item("This is the file 'omega'.\n"),
6281 expected_skip = wc.State(short_A_COPY_path, { })
6282 saved_cwd = os.getcwd()
6283 os.chdir(svntest.main.work_dir)
6284 svntest.actions.run_and_verify_merge(short_A_COPY_path, '2', '4',
6285 sbox.repo_url + \
6286 '/A',
6287 expected_output,
6288 expected_disk,
6289 expected_status,
6290 expected_skip,
6291 None, None, None, None,
6292 None, 1)
6293 # Now do the reverse merge into the subtree.
6294 short_H_COPY_path = shorten_path_kludge(H_COPY_path)
6295 expected_output = wc.State(short_H_COPY_path, {
6296 'psi' : Item(status='G '),
6298 expected_status = wc.State(short_H_COPY_path, {
6299 '' : Item(status=' M', wc_rev=2),
6300 'chi' : Item(status=' ', wc_rev=2),
6301 'psi' : Item(status=' ', wc_rev=2),
6302 'omega' : Item(status=' ', wc_rev=2),
6304 expected_disk = wc.State('', {
6305 '' : Item(props={SVN_PROP_MERGEINFO : ''}),
6306 'chi' : Item("This is the file 'chi'.\n"),
6307 'psi' : Item("This is the file 'psi'.\n"),
6308 'omega' : Item("This is the file 'omega'.\n"),
6310 expected_skip = wc.State(short_H_COPY_path, { })
6311 svntest.actions.run_and_verify_merge(short_H_COPY_path, '4', '2',
6312 sbox.repo_url + \
6313 '/A/D/H',
6314 expected_output,
6315 expected_disk,
6316 expected_status,
6317 expected_skip,
6318 None, None, None, None,
6319 None, 1)
6321 # Test areas B and C -- Reverse merge r3 into A_COPY, this would result in
6322 # empty mergeinfo on A_COPY and A_COPY/D/H, but the empty mergeinfo on the
6323 # latter elides to the former. And then the empty mergeinfo on A_COPY,
6324 # which has no parent with explicit mergeinfo to override (in either the WC
6325 # or the repos) itself elides. This leaves the WC in the same unmodified
6326 # state as after the call to set_up_branch().
6327 short_rho_COPY_path = shorten_path_kludge(rho_COPY_path)
6328 expected_output = expected_merge_output(
6329 [[4,3]], 'G ' + short_rho_COPY_path + '\n')
6330 svntest.actions.run_and_verify_svn(None, expected_output,
6331 [], 'merge', '-r4:2',
6332 sbox.repo_url + '/A',
6333 short_A_COPY_path)
6334 os.chdir(saved_cwd)
6335 svntest.actions.run_and_verify_status(wc_dir, wc_status)
6336 # Check that A_COPY's mergeinfo is gone.
6337 svntest.actions.run_and_verify_svn(None, [], [], 'pg', 'svn:mergeinfo',
6338 A_COPY_path)
6340 def prop_add_to_child_with_mergeinfo(sbox):
6341 "merge adding prop to child of merge target works"
6343 # Test for Issue #2781 Prop add to child of merge target corrupts WC if
6344 # child has mergeinfo.
6346 sbox.build()
6347 wc_dir = sbox.wc_dir
6348 expected_disk, expected_status = set_up_branch(sbox)
6350 # Some paths we'll care about
6351 beta_path = os.path.join(wc_dir, "A", "B", "E", "beta")
6352 beta_COPY_path = os.path.join(wc_dir, "A_COPY", "B", "E", "beta")
6353 B_COPY_path = os.path.join(wc_dir, "A_COPY", "B")
6355 # Set a non-mergeinfo prop on a file.
6356 svntest.actions.run_and_verify_svn(None,
6357 ["property 'prop:name' set on '" +
6358 beta_path + "'\n"], [], 'ps',
6359 'prop:name', 'propval', beta_path)
6360 expected_disk.tweak('A/B/E/beta', props={'prop:name' : 'propval'})
6361 expected_status.tweak('A/B/E/beta', wc_rev=7)
6362 expected_output = wc.State(wc_dir,
6363 {'A/B/E/beta' : Item(verb='Sending')})
6364 svntest.actions.run_and_verify_commit(wc_dir,
6365 expected_output,
6366 expected_status,
6367 None,
6368 wc_dir)
6370 # Merge r4:5 from A/B/E/beta into A_COPY/B/E/beta.
6371 short_beta_COPY_path = shorten_path_kludge(beta_COPY_path)
6372 saved_cwd = os.getcwd()
6373 os.chdir(svntest.main.work_dir)
6374 svntest.actions.run_and_verify_svn(None,
6375 expected_merge_output([[5]],
6376 'U ' + short_beta_COPY_path +'\n'),
6377 [], 'merge', '-c5',
6378 sbox.repo_url + '/A/B/E/beta',
6379 short_beta_COPY_path)
6380 os.chdir(saved_cwd)
6382 # Merge r6:7 into A_COPY/B. In issue #2781 this adds a bogus
6383 # and incomplete entry in A_COPY/B/.svn/entries for 'beta'.
6384 short_B_COPY_path = shorten_path_kludge(B_COPY_path)
6385 expected_output = wc.State(short_B_COPY_path, {
6386 'E/beta' : Item(status=' U'),
6388 expected_status = wc.State(short_B_COPY_path, {
6389 '' : Item(status=' M', wc_rev=2),
6390 'E' : Item(status=' ', wc_rev=2),
6391 'E/alpha' : Item(status=' ', wc_rev=2),
6392 'E/beta' : Item(status='MM', wc_rev=2),
6393 'lambda' : Item(status=' ', wc_rev=2),
6394 'F' : Item(status=' ', wc_rev=2),
6396 expected_disk = wc.State('', {
6397 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B:7'}),
6398 'E' : Item(),
6399 'E/alpha' : Item("This is the file 'alpha'.\n"),
6400 'E/beta' : Item(contents="New content",
6401 props={SVN_PROP_MERGEINFO : '/A/B/E/beta:5,7',
6402 'prop:name' : 'propval'}),
6403 'F' : Item(),
6404 'lambda' : Item("This is the file 'lambda'.\n")
6406 expected_skip = wc.State(short_B_COPY_path, { })
6407 os.chdir(svntest.main.work_dir)
6408 svntest.actions.run_and_verify_merge(short_B_COPY_path, '6', '7',
6409 sbox.repo_url + \
6410 '/A/B',
6411 expected_output,
6412 expected_disk,
6413 expected_status,
6414 expected_skip,
6415 None, None, None, None,
6416 None, 1)
6418 def diff_repos_does_not_update_mergeinfo(sbox):
6419 "don't set mergeinfo when merging from another repo"
6421 # Test for issue #2788.
6423 sbox.build()
6424 wc_dir = sbox.wc_dir
6425 expected_disk, expected_status = set_up_branch(sbox)
6427 # Create a second repository with the same greek tree
6428 repo_dir = sbox.repo_dir
6429 other_repo_dir, other_repo_url = sbox.add_repo_path("other")
6430 svntest.main.copy_repos(repo_dir, other_repo_dir, 6, 1)
6432 # Merge r3:4 (using implied peg revisions) from 'other' repos into
6433 # A_COPY/D/G. Merge should succeed, but no mergeinfo should be set.
6435 # Search for the comment entitled "The Merge Kluge" elsewhere in
6436 # this file, to understand why we shorten and chdir() below.
6437 short_G_COPY_path = shorten_path_kludge(os.path.join(wc_dir,
6438 "A_COPY", "D", "G"))
6439 saved_cwd = os.getcwd()
6441 os.chdir(svntest.main.work_dir)
6442 svntest.actions.run_and_verify_svn(None,
6443 expected_merge_output([[4]],
6444 'U ' +
6445 os.path.join(short_G_COPY_path,
6446 "rho") + '\n'),
6447 [], 'merge', '-c4',
6448 other_repo_url + '/A/D/G',
6449 short_G_COPY_path)
6450 os.chdir(saved_cwd)
6452 # Merge r4:5 (using explicit peg revisions) from 'other' repos into
6453 # A_COPY/B/E. Merge should succeed, but no mergeinfo should be set.
6454 short_E_COPY_path = shorten_path_kludge(os.path.join(wc_dir,
6455 "A_COPY", "B", "E"))
6457 os.chdir(svntest.main.work_dir)
6458 svntest.actions.run_and_verify_svn(None,
6459 expected_merge_output([[5]],
6460 'U ' +
6461 os.path.join(short_E_COPY_path,
6462 "beta") +'\n'),
6463 [], 'merge',
6464 other_repo_url + '/A/B/E@4',
6465 other_repo_url + '/A/B/E@5',
6466 short_E_COPY_path)
6467 os.chdir(saved_cwd)
6469 expected_status.tweak('A_COPY/D/G/rho', 'A_COPY/B/E/beta', status='M ')
6470 svntest.actions.run_and_verify_status(wc_dir, expected_status)
6472 def avoid_reflected_revs(sbox):
6473 "avoid repeated merges for cyclic merging"
6475 ## See http://subversion.tigris.org/issues/show_bug.cgi?id=2897. ##
6477 # Create a WC with a single branch
6478 sbox.build()
6479 wc_dir = sbox.wc_dir
6480 wc_disk, wc_status = set_up_branch(sbox, True, 1)
6482 # Some paths we'll care about
6483 A_path = os.path.join(wc_dir, 'A')
6484 A_COPY_path = os.path.join(wc_dir, 'A_COPY')
6485 tfile1_path = os.path.join(wc_dir, 'A', 'tfile1')
6486 tfile2_path = os.path.join(wc_dir, 'A', 'tfile2')
6487 bfile1_path = os.path.join(A_COPY_path, 'bfile1')
6488 bfile2_path = os.path.join(A_COPY_path, 'bfile2')
6490 # Contents to be added to files
6491 tfile1_content = "This is tfile1\n"
6492 tfile2_content = "This is tfile2\n"
6493 bfile1_content = "This is bfile1\n"
6494 bfile2_content = "This is bfile2\n"
6496 # We'll consider A as the trunk and A_COPY as the feature branch
6497 # Create a tfile1 in A
6498 svntest.main.file_write(tfile1_path, tfile1_content)
6499 svntest.actions.run_and_verify_svn(None, None, [], 'add', tfile1_path)
6500 expected_output = wc.State(wc_dir, {'A/tfile1' : Item(verb='Adding')})
6501 wc_status.add({'A/tfile1' : Item(status=' ', wc_rev=3)})
6502 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
6503 wc_status, None, wc_dir)
6505 # Create a bfile1 in A_COPY
6506 svntest.main.file_write(bfile1_path, bfile1_content)
6507 svntest.actions.run_and_verify_svn(None, None, [], 'add', bfile1_path)
6508 expected_output = wc.State(wc_dir, {'A_COPY/bfile1' : Item(verb='Adding')})
6509 wc_status.add({'A_COPY/bfile1' : Item(status=' ', wc_rev=4)})
6510 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
6511 wc_status, None, wc_dir)
6513 # Create one more file in A
6514 svntest.main.file_write(tfile2_path, tfile2_content)
6515 svntest.actions.run_and_verify_svn(None, None, [], 'add', tfile2_path)
6516 expected_output = wc.State(wc_dir, {'A/tfile2' : Item(verb='Adding')})
6517 wc_status.add({'A/tfile2' : Item(status=' ', wc_rev=5)})
6518 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
6519 wc_status, None, wc_dir)
6521 short_A_COPY = shorten_path_kludge(A_COPY_path)
6522 saved_cwd = os.getcwd()
6523 os.chdir(svntest.main.work_dir)
6525 # Merge r5 from /A to /A_COPY, creating r6
6526 expected_output = wc.State(short_A_COPY, {
6527 'tfile2' : Item(status='A '),
6529 expected_status = wc.State(short_A_COPY, {
6530 '' : Item(status=' M', wc_rev=2),
6531 'tfile2' : Item(status='A ', wc_rev='-', copied='+'),
6532 'bfile1' : Item(status=' ', wc_rev=4),
6533 'mu' : Item(status=' ', wc_rev=2),
6534 'C' : Item(status=' ', wc_rev=2),
6535 'D' : Item(status=' ', wc_rev=2),
6536 'B' : Item(status=' ', wc_rev=2),
6537 'B/lambda' : Item(status=' ', wc_rev=2),
6538 'B/E' : Item(status=' ', wc_rev=2),
6539 'B/E/alpha': Item(status=' ', wc_rev=2),
6540 'B/E/beta' : Item(status=' ', wc_rev=2),
6541 'B/F' : Item(status=' ', wc_rev=2),
6542 'D/gamma' : Item(status=' ', wc_rev=2),
6543 'D/G' : Item(status=' ', wc_rev=2),
6544 'D/G/pi' : Item(status=' ', wc_rev=2),
6545 'D/G/rho' : Item(status=' ', wc_rev=2),
6546 'D/G/tau' : Item(status=' ', wc_rev=2),
6547 'D/H' : Item(status=' ', wc_rev=2),
6548 'D/H/chi' : Item(status=' ', wc_rev=2),
6549 'D/H/omega': Item(status=' ', wc_rev=2),
6550 'D/H/psi' : Item(status=' ', wc_rev=2),
6552 expected_disk = wc.State('', {
6553 '' : Item(props={SVN_PROP_MERGEINFO : '/A:5'}),
6554 'tfile2' : Item(tfile2_content),
6555 'bfile1' : Item(bfile1_content),
6556 'mu' : Item("This is the file 'mu'.\n"),
6557 'C' : Item(),
6558 'D' : Item(),
6559 'B' : Item(),
6560 'B/lambda' : Item("This is the file 'lambda'.\n"),
6561 'B/E' : Item(),
6562 'B/E/alpha': Item("This is the file 'alpha'.\n"),
6563 'B/E/beta' : Item("This is the file 'beta'.\n"),
6564 'B/F' : Item(),
6565 'D/gamma' : Item("This is the file 'gamma'.\n"),
6566 'D/G' : Item(),
6567 'D/G/pi' : Item("This is the file 'pi'.\n"),
6568 'D/G/rho' : Item("This is the file 'rho'.\n"),
6569 'D/G/tau' : Item("This is the file 'tau'.\n"),
6570 'D/H' : Item(),
6571 'D/H/chi' : Item("This is the file 'chi'.\n"),
6572 'D/H/omega': Item("This is the file 'omega'.\n"),
6573 'D/H/psi' : Item("This is the file 'psi'.\n"),
6575 expected_skip = wc.State(short_A_COPY, {})
6577 svntest.actions.run_and_verify_merge(short_A_COPY, '4', '5',
6578 sbox.repo_url + '/A',
6579 expected_output,
6580 expected_disk,
6581 expected_status,
6582 expected_skip,
6583 None, None, None, None, None, 1)
6584 os.chdir(saved_cwd)
6586 # Sync up with the trunk ie., A
6587 svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
6588 expected_output = wc.State(wc_dir, {
6589 'A_COPY' : Item(verb='Sending'),
6590 'A_COPY/tfile2' : Item(verb='Adding'),
6592 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
6593 None, None, wc_dir)
6594 os.chdir(svntest.main.work_dir)
6596 # Merge r3 from /A to /A_COPY, creating r7
6597 expected_output = wc.State(short_A_COPY, {
6598 'tfile1' : Item(status='A '),
6600 expected_status.tweak(wc_rev=5)
6601 expected_status.tweak('', wc_rev=6)
6602 expected_status.tweak('tfile2', status=' ', copied=None, wc_rev=6)
6603 expected_status.add({
6604 'tfile1' : Item(status='A ', wc_rev='-', copied='+'),
6606 expected_disk.tweak('', props={SVN_PROP_MERGEINFO : '/A:3,5'})
6607 expected_disk.add({
6608 'tfile1' : Item(tfile1_content),
6611 svntest.actions.run_and_verify_merge(short_A_COPY, '2', '3',
6612 sbox.repo_url + '/A',
6613 expected_output,
6614 expected_disk,
6615 expected_status,
6616 expected_skip,
6617 None, None, None, None, None, 1)
6619 os.chdir(saved_cwd)
6621 svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
6622 expected_output = wc.State(wc_dir, {
6623 'A_COPY' : Item(verb='Sending'),
6624 'A_COPY/tfile1' : Item(verb='Adding'),
6626 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
6627 None, None, wc_dir)
6629 # Add bfile2 to A_COPY
6630 svntest.main.file_write(bfile2_path, bfile2_content)
6631 svntest.actions.run_and_verify_svn(None, None, [], 'add', bfile2_path)
6632 expected_output = wc.State(wc_dir, {'A_COPY/bfile2' : Item(verb='Adding')})
6633 wc_status.tweak(wc_rev=6)
6634 wc_status.add({
6635 'A_COPY/bfile2' : Item(status=' ', wc_rev=8),
6636 'A_COPY' : Item(status=' ', wc_rev=7),
6637 'A_COPY/tfile2' : Item(status=' ', wc_rev=6),
6638 'A_COPY/tfile1' : Item(status=' ', wc_rev=7),
6640 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
6641 wc_status, None, wc_dir)
6643 # Merge 2:8 from A_COPY(feature branch) to A(trunk).
6644 short_A_path = shorten_path_kludge(A_path)
6645 expected_output = wc.State(short_A_path, {
6646 'bfile2' : Item(status='A '),
6647 'bfile1' : Item(status='A '),
6649 expected_status = wc.State(short_A_path, {
6650 '' : Item(status=' M', wc_rev=6),
6651 'bfile2' : Item(status='A ', wc_rev='-', copied='+'),
6652 'bfile1' : Item(status='A ', wc_rev='-', copied='+'),
6653 'tfile2' : Item(status=' ', wc_rev=6),
6654 'tfile1' : Item(status=' ', wc_rev=6),
6655 'mu' : Item(status=' ', wc_rev=6),
6656 'C' : Item(status=' ', wc_rev=6),
6657 'D' : Item(status=' ', wc_rev=6),
6658 'B' : Item(status=' ', wc_rev=6),
6659 'B/lambda' : Item(status=' ', wc_rev=6),
6660 'B/E' : Item(status=' ', wc_rev=6),
6661 'B/E/alpha' : Item(status=' ', wc_rev=6),
6662 'B/E/beta' : Item(status=' ', wc_rev=6),
6663 'B/F' : Item(status=' ', wc_rev=6),
6664 'D/gamma' : Item(status=' ', wc_rev=6),
6665 'D/G' : Item(status=' ', wc_rev=6),
6666 'D/G/pi' : Item(status=' ', wc_rev=6),
6667 'D/G/rho' : Item(status=' ', wc_rev=6),
6668 'D/G/tau' : Item(status=' ', wc_rev=6),
6669 'D/H' : Item(status=' ', wc_rev=6),
6670 'D/H/chi' : Item(status=' ', wc_rev=6),
6671 'D/H/omega' : Item(status=' ', wc_rev=6),
6672 'D/H/psi' : Item(status=' ', wc_rev=6),
6674 expected_disk = wc.State('', {
6675 '' : Item(props={SVN_PROP_MERGEINFO : '/A_COPY:3-8'}),
6676 'bfile2' : Item(bfile2_content),
6677 'bfile1' : Item(bfile1_content),
6678 'tfile2' : Item(tfile2_content),
6679 'tfile1' : Item(tfile1_content),
6680 'mu' : Item("This is the file 'mu'.\n"),
6681 'C' : Item(),
6682 'D' : Item(),
6683 'B' : Item(),
6684 'B/lambda' : Item("This is the file 'lambda'.\n"),
6685 'B/E' : Item(),
6686 'B/E/alpha' : Item("This is the file 'alpha'.\n"),
6687 'B/E/beta' : Item("This is the file 'beta'.\n"),
6688 'B/F' : Item(),
6689 'D/gamma' : Item("This is the file 'gamma'.\n"),
6690 'D/G' : Item(),
6691 'D/G/pi' : Item("This is the file 'pi'.\n"),
6692 'D/G/rho' : Item("This is the file 'rho'.\n"),
6693 'D/G/tau' : Item("This is the file 'tau'.\n"),
6694 'D/H' : Item(),
6695 'D/H/chi' : Item("This is the file 'chi'.\n"),
6696 'D/H/omega' : Item("This is the file 'omega'.\n"),
6697 'D/H/psi' : Item("This is the file 'psi'.\n"),
6700 expected_skip = wc.State(short_A_path, {})
6701 saved_cwd = os.getcwd()
6702 os.chdir(svntest.main.work_dir)
6704 svntest.actions.run_and_verify_merge(short_A_path, '2', '8',
6705 sbox.repo_url + '/A_COPY',
6706 expected_output,
6707 expected_disk,
6708 expected_status,
6709 expected_skip,
6710 None, None, None, None, None, 1)
6711 os.chdir(saved_cwd)
6714 def update_loses_mergeinfo(sbox):
6715 "update does not merge mergeinfo"
6718 When a working copy receives a fresh svn:mergeinfo property due to update,
6719 and working copy has some local mergeinfo(yet to be added), local mergeinfo
6720 is left unchanged.
6723 sbox.build()
6724 wc_dir = sbox.wc_dir
6725 A_C_wc_dir = wc_dir + '/A/C'
6726 A_B_url = sbox.repo_url + '/A/B'
6727 A_B_J_url = sbox.repo_url + '/A/B/J'
6728 A_B_K_url = sbox.repo_url + '/A/B/K'
6729 svntest.actions.run_and_verify_svn(None, ['\n', 'Committed revision 2.\n'],
6731 'mkdir', '-m', 'rev 2', A_B_J_url)
6732 svntest.actions.run_and_verify_svn(None, ['\n', 'Committed revision 3.\n'],
6734 'mkdir', '-m', 'rev 3', A_B_K_url)
6736 other_wc = sbox.add_wc_path('other')
6737 svntest.actions.duplicate_dir(wc_dir, other_wc)
6739 short_A_C_wc_dir = shorten_path_kludge(A_C_wc_dir)
6740 expected_output = wc.State(short_A_C_wc_dir, {'J' : Item(status='A ')})
6741 expected_disk = wc.State('', {
6742 'J' : Item(),
6743 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B:2'}),
6745 expected_status = wc.State(short_A_C_wc_dir,
6746 { '' : Item(wc_rev=1, status=' M'),
6747 'J' : Item(status='A ',
6748 wc_rev='-', copied='+')
6751 expected_skip = wc.State('', { })
6752 saved_cwd = os.getcwd()
6753 os.chdir(svntest.main.work_dir)
6754 svntest.actions.run_and_verify_merge(short_A_C_wc_dir, '1', '2',
6755 A_B_url,
6756 expected_output,
6757 expected_disk,
6758 expected_status,
6759 expected_skip,
6760 check_props=1)
6761 os.chdir(saved_cwd)
6762 expected_output = wc.State(A_C_wc_dir, {
6763 '' : Item(verb='Sending'),
6764 'J' : Item(verb='Adding')
6766 expected_status = wc.State(A_C_wc_dir,
6767 { '' : Item(status=' ', wc_rev=4),
6768 'J' : Item(status=' ', wc_rev=4)
6771 svntest.actions.run_and_verify_commit(A_C_wc_dir,
6772 expected_output,
6773 expected_status,
6774 None,
6775 A_C_wc_dir)
6777 other_A_C_wc_dir = other_wc + '/A/C'
6778 short_other_A_C_wc_dir = shorten_path_kludge(other_A_C_wc_dir)
6779 expected_output = wc.State(short_other_A_C_wc_dir, {'K' : Item(status='A ')})
6780 expected_disk = wc.State('', {
6781 'K' : Item(),
6782 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B:3'}),
6784 expected_status = wc.State(short_other_A_C_wc_dir,
6785 { '' : Item(wc_rev=1, status=' M'),
6786 'K' : Item(status='A ',
6787 wc_rev='-', copied='+')
6790 expected_skip = wc.State('', { })
6791 saved_cwd = os.getcwd()
6792 os.chdir(svntest.main.work_dir)
6793 svntest.actions.run_and_verify_merge(short_other_A_C_wc_dir, '2', '3',
6794 A_B_url,
6795 expected_output,
6796 expected_disk,
6797 expected_status,
6798 expected_skip,
6799 check_props=1)
6800 os.chdir(saved_cwd)
6801 expected_output = wc.State(other_A_C_wc_dir,
6802 {'J' : Item(status='A '),
6803 '' : Item(status=' G')
6806 expected_disk = wc.State('', {
6807 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B:2-3'}),
6808 'J' : Item(),
6809 'K' : Item(),
6811 expected_status = wc.State(other_A_C_wc_dir,
6812 { '' : Item(wc_rev=4, status=' M'),
6813 'J' : Item(status=' ', wc_rev='4'),
6814 'K' : Item(status='A ',
6815 wc_rev='-', copied='+')
6818 svntest.actions.run_and_verify_update(other_A_C_wc_dir,
6819 expected_output,
6820 expected_disk,
6821 expected_status,
6822 check_props=1)
6824 # Tests part of issue# 2829, marked as XFail until that issue is fixed.
6825 def merge_loses_mergeinfo(sbox):
6826 "merge should merge mergeinfo"
6829 When a working copy has no mergeinfo(due to local full revert of all merges),
6830 and merge is attempted for someother revision rX, The new mergeinfo should be
6831 /merge/src: rX not all the reverted ones reappearing along with rX.
6834 sbox.build()
6835 wc_dir = sbox.wc_dir
6836 A_C_wc_dir = wc_dir + '/A/C'
6837 A_B_url = sbox.repo_url + '/A/B'
6838 A_B_J_url = sbox.repo_url + '/A/B/J'
6839 A_B_K_url = sbox.repo_url + '/A/B/K'
6840 svntest.actions.run_and_verify_svn(None, ['\n', 'Committed revision 2.\n'],
6842 'mkdir', '-m', 'rev 2', A_B_J_url)
6843 svntest.actions.run_and_verify_svn(None, ['\n', 'Committed revision 3.\n'],
6845 'mkdir', '-m', 'rev 3', A_B_K_url)
6847 short_A_C_wc_dir = shorten_path_kludge(A_C_wc_dir)
6848 expected_output = wc.State(short_A_C_wc_dir, {'J' : Item(status='A ')})
6849 expected_disk = wc.State('', {
6850 'J' : Item(),
6851 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B:2'}),
6853 expected_status = wc.State(short_A_C_wc_dir,
6854 { '' : Item(wc_rev=1, status=' M'),
6855 'J' : Item(status='A ',
6856 wc_rev='-', copied='+')
6859 expected_skip = wc.State('', { })
6860 saved_cwd = os.getcwd()
6861 os.chdir(svntest.main.work_dir)
6862 svntest.actions.run_and_verify_merge(short_A_C_wc_dir, '1', '2',
6863 A_B_url,
6864 expected_output,
6865 expected_disk,
6866 expected_status,
6867 expected_skip,
6868 check_props=1)
6869 os.chdir(saved_cwd)
6870 expected_output = wc.State(A_C_wc_dir, {
6871 '' : Item(verb='Sending'),
6872 'J' : Item(verb='Adding')
6874 expected_status = wc.State(A_C_wc_dir,
6875 { '' : Item(status=' ', wc_rev=4),
6876 'J' : Item(status=' ', wc_rev=4)
6879 svntest.actions.run_and_verify_commit(A_C_wc_dir,
6880 expected_output,
6881 expected_status,
6882 None,
6883 A_C_wc_dir)
6884 expected_output = wc.State(short_A_C_wc_dir, {'J' : Item(status='D ')})
6885 expected_disk = wc.State('', {'J': Item()})
6886 expected_status = wc.State(short_A_C_wc_dir,
6887 { '' : Item(wc_rev=4, status=' M'),
6888 'J' : Item(wc_rev=4, status='D ')
6891 saved_cwd = os.getcwd()
6892 os.chdir(svntest.main.work_dir)
6893 svntest.actions.run_and_verify_merge(short_A_C_wc_dir, '2', '1',
6894 A_B_url,
6895 expected_output,
6896 expected_disk,
6897 expected_status,
6898 expected_skip,
6899 check_props=1)
6900 os.chdir(saved_cwd)
6902 expected_output = wc.State(short_A_C_wc_dir, {'K' : Item(status='A ')})
6903 expected_disk = wc.State('', {
6904 'K' : Item(),
6905 'J' : Item(),
6906 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B:3'}),
6908 expected_status = wc.State(short_A_C_wc_dir,
6909 { '' : Item(wc_rev=4, status=' M'),
6910 'K' : Item(status='A ',
6911 wc_rev='-', copied='+'),
6912 'J' : Item(wc_rev=4, status='D ')
6915 saved_cwd = os.getcwd()
6916 os.chdir(svntest.main.work_dir)
6917 svntest.actions.run_and_verify_merge(short_A_C_wc_dir, '2', '3',
6918 A_B_url,
6919 expected_output,
6920 expected_disk,
6921 expected_status,
6922 expected_skip,
6923 check_props=1)
6925 def single_file_replace_style_merge_capability(sbox):
6926 "replace-style merge capability for a single file"
6928 # Test for issue #2853, do_single_file_merge() lacks "Replace-style
6929 # merge" capability
6931 sbox.build()
6932 wc_dir = sbox.wc_dir
6933 iota_path = os.path.join(wc_dir, 'iota')
6934 mu_path = os.path.join(wc_dir, 'A', 'mu')
6936 # delete mu and replace it with a copy of iota
6937 svntest.main.run_svn(None, 'rm', mu_path)
6938 svntest.main.run_svn(None, 'mv', iota_path, mu_path)
6940 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
6941 expected_status.tweak('A/mu', status=' ', wc_rev=2)
6942 expected_status.remove('iota')
6943 expected_output = svntest.wc.State(wc_dir, {
6944 'iota': Item(verb='Deleting'),
6945 'A/mu': Item(verb='Replacing'),
6947 svntest.actions.run_and_verify_commit(wc_dir,
6948 expected_output,
6949 expected_status,
6950 None, wc_dir)
6952 # Merge the file mu alone to rev1
6953 svntest.actions.run_and_verify_svn(None,
6954 expected_merge_output(None,
6955 ['D ' + mu_path + '\n',
6956 'A ' + mu_path + '\n']),
6958 'merge',
6959 mu_path + '@2',
6960 mu_path + '@1',
6961 mu_path)
6963 # Test for issue 2786 fix.
6964 def merge_to_out_of_date_target(sbox):
6965 "merge to ood path can lead to inaccurate mergeinfo"
6967 # Create a WC with a branch.
6968 sbox.build()
6969 wc_dir = sbox.wc_dir
6970 wc_disk, wc_status = set_up_branch(sbox, False, 1)
6972 # Make second working copy
6973 other_wc = sbox.add_wc_path('other')
6974 svntest.actions.duplicate_dir(wc_dir, other_wc)
6976 # Some paths we'll care about
6977 A_COPY_H_path = os.path.join(wc_dir, "A_COPY", "D", "H")
6978 other_A_COPY_H_path = os.path.join(other_wc, "A_COPY", "D", "H")
6980 # Merge -c3 into A_COPY/D/H of first WC.
6982 # Search for the comment entitled "The Merge Kluge" elsewhere in
6983 # this file, to understand why we shorten and chdir() below.
6984 short_H_COPY_path = shorten_path_kludge(A_COPY_H_path)
6985 expected_output = wc.State(short_H_COPY_path, {
6986 'psi' : Item(status='U ')
6988 expected_status = wc.State(short_H_COPY_path, {
6989 '' : Item(status=' M', wc_rev=2),
6990 'psi' : Item(status='M ', wc_rev=2),
6991 'omega' : Item(status=' ', wc_rev=2),
6992 'chi' : Item(status=' ', wc_rev=2),
6994 expected_disk = wc.State('', {
6995 '' : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:3'}),
6996 'psi' : Item("New content"),
6997 'omega' : Item("This is the file 'omega'.\n"),
6998 'chi' : Item("This is the file 'chi'.\n"),
7000 expected_skip = wc.State(short_H_COPY_path, { })
7001 saved_cwd = os.getcwd()
7002 os.chdir(svntest.main.work_dir)
7003 svntest.actions.run_and_verify_merge(short_H_COPY_path, '2', '3',
7004 sbox.repo_url + '/A/D/H',
7005 expected_output, expected_disk,
7006 expected_status, expected_skip,
7007 None, None, None, None, None, 1)
7008 os.chdir(saved_cwd)
7010 # Commit merge to first WC.
7011 wc_status.tweak('A_COPY/D/H/psi', 'A_COPY/D/H', wc_rev=7)
7012 expected_output = svntest.wc.State(wc_dir, {
7013 'A_COPY/D/H' : Item(verb='Sending'),
7014 'A_COPY/D/H/psi': Item(verb='Sending'),
7016 svntest.actions.run_and_verify_commit(wc_dir,
7017 expected_output,
7018 wc_status,
7019 None, wc_dir)
7021 # Merge -c6 into A_COPY/D/H of other WC.
7022 short_H_COPY_path = shorten_path_kludge(other_A_COPY_H_path)
7023 expected_output = wc.State(short_H_COPY_path, {
7024 'omega' : Item(status='U ')
7026 expected_status = wc.State(short_H_COPY_path, {
7027 '' : Item(status=' M', wc_rev=2),
7028 'psi' : Item(status=' ', wc_rev=2),
7029 'omega' : Item(status='M ', wc_rev=2),
7030 'chi' : Item(status=' ', wc_rev=2),
7032 expected_disk = wc.State('', {
7033 '' : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:6'}),
7034 'psi' : Item("This is the file 'psi'.\n"),
7035 'omega' : Item("New content"),
7036 'chi' : Item("This is the file 'chi'.\n"),
7038 expected_skip = wc.State(short_H_COPY_path, { })
7039 os.chdir(svntest.main.work_dir)
7040 svntest.actions.run_and_verify_merge(short_H_COPY_path, '5', '6',
7041 sbox.repo_url + '/A/D/H',
7042 expected_output, expected_disk,
7043 expected_status, expected_skip,
7044 None, None, None, None, None, 1)
7045 os.chdir(saved_cwd)
7047 # Update A_COPY/D/H in other WC. Local mergeinfo for r6 on A_COPY/D/H
7048 # should be *merged* with r3 from first WC.
7049 expected_output = svntest.wc.State(other_A_COPY_H_path, {
7050 '' : Item(status=' G'),
7051 'psi' : Item(status='U ')
7053 other_disk = wc.State('', {
7054 '' : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:3,6'}),
7055 'psi' : Item(contents="New content"),
7056 'chi' : Item("This is the file 'chi'.\n"),
7057 'omega' : Item(contents="New content"),
7059 other_status = wc.State(other_A_COPY_H_path,{
7060 '' : Item(wc_rev=7, status=' M'),
7061 'chi' : Item(wc_rev=7, status=' '),
7062 'psi' : Item(wc_rev=7, status=' '),
7063 'omega' : Item(wc_rev=7, status='M ')
7065 svntest.actions.run_and_verify_update(other_A_COPY_H_path,
7066 expected_output,
7067 other_disk,
7068 other_status,
7069 check_props=1)
7071 def merge_with_depth_files(sbox):
7072 "merge test for --depth files"
7074 sbox.build()
7075 wc_dir = sbox.wc_dir
7077 # Some paths we'll care about
7078 mu_path = os.path.join(wc_dir, 'A', 'mu')
7079 gamma_path = os.path.join(wc_dir, 'A', 'D', 'gamma')
7080 Acopy_path = os.path.join(wc_dir, 'A_copy')
7081 Acopy_mu_path = os.path.join(wc_dir, 'A_copy', 'mu')
7082 A_url = sbox.repo_url + '/A'
7083 Acopy_url = sbox.repo_url + '/A_copy'
7085 # Copy A_url to A_copy_url
7086 svntest.actions.run_and_verify_svn(None, None, [], 'cp',
7087 A_url, Acopy_url,
7088 '-m', 'create a new copy of A')
7090 svntest.main.file_write(mu_path, "this is file 'mu' modified.\n")
7091 svntest.main.file_write(gamma_path, "this is file 'gamma' modified.\n")
7093 # Create expected output tree for commit
7094 expected_output = wc.State(wc_dir, {
7095 'A/mu' : Item(verb='Sending'),
7096 'A/D/gamma' : Item(verb='Sending'),
7099 # Create expected status tree for commit
7100 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
7101 expected_status.add({
7102 'A/mu' : Item(status=' ', wc_rev=3),
7103 'A/D/gamma' : Item(status=' ', wc_rev=3),
7106 # Commit the modified contents
7107 svntest.actions.run_and_verify_commit(wc_dir,
7108 expected_output,
7109 expected_status,
7110 None,
7111 wc_dir)
7113 # Update working copy
7114 svntest.actions.run_and_verify_svn(None, None, [],
7115 'up', Acopy_path)
7117 # Merge r1:3 into A_copy with --depth files. The merge only affects
7118 # 'A_copy' and its one file child 'mu', so 'A_copy' gets non-inheritable
7119 # mergeinfo for -r1:3 and 'mu' gets its own complete set of mergeinfo:
7120 # r1 from its parent, and r1:3 from the merge itself.
7122 # Search for the comment entitled "The Merge Kluge" elsewhere in
7123 # this file, to understand why we shorten and chdir() below.
7124 short_A_COPY_path = shorten_path_kludge(Acopy_path)
7125 expected_output = wc.State(short_A_COPY_path, {
7126 'mu' : Item(status='U '),
7128 expected_status = wc.State(short_A_COPY_path, {
7129 '' : Item(status=' M'),
7130 'B' : Item(status=' '),
7131 'mu' : Item(status='MM'),
7132 'B/E' : Item(status=' '),
7133 'B/E/alpha' : Item(status=' '),
7134 'B/E/beta' : Item(status=' '),
7135 'B/lambda' : Item(status=' '),
7136 'B/F' : Item(status=' '),
7137 'C' : Item(status=' '),
7138 'D' : Item(status=' '),
7139 'D/G' : Item(status=' '),
7140 'D/G/pi' : Item(status=' '),
7141 'D/G/rho' : Item(status=' '),
7142 'D/G/tau' : Item(status=' '),
7143 'D/gamma' : Item(status=' '),
7144 'D/H' : Item(status=' '),
7145 'D/H/chi' : Item(status=' '),
7146 'D/H/psi' : Item(status=' '),
7147 'D/H/omega' : Item(status=' '),
7149 expected_status.tweak(wc_rev=3)
7150 expected_disk = wc.State('', {
7151 '' : Item(props={SVN_PROP_MERGEINFO : '/A:2-3*'}),
7152 'B' : Item(),
7153 'mu' : Item("this is file 'mu' modified.\n",
7154 props={SVN_PROP_MERGEINFO : '/A/mu:2-3'}),
7155 'B/E' : Item(),
7156 'B/E/alpha' : Item("This is the file 'alpha'.\n"),
7157 'B/E/beta' : Item("This is the file 'beta'.\n"),
7158 'B/lambda' : Item("This is the file 'lambda'.\n"),
7159 'B/F' : Item(),
7160 'C' : Item(),
7161 'D' : Item(),
7162 'D/G' : Item(),
7163 'D/G/pi' : Item("This is the file 'pi'.\n"),
7164 'D/G/rho' : Item("This is the file 'rho'.\n"),
7165 'D/G/tau' : Item("This is the file 'tau'.\n"),
7166 'D/gamma' : Item("This is the file 'gamma'.\n"),
7167 'D/H' : Item(),
7168 'D/H/chi' : Item("This is the file 'chi'.\n"),
7169 'D/H/psi' : Item("This is the file 'psi'.\n"),
7170 'D/H/omega' : Item("This is the file 'omega'.\n"),
7172 expected_skip = wc.State(short_A_COPY_path, { })
7173 saved_cwd = os.getcwd()
7174 os.chdir(svntest.main.work_dir)
7175 svntest.actions.run_and_verify_merge(short_A_COPY_path, '1', '3',
7176 sbox.repo_url + '/A',
7177 expected_output, expected_disk,
7178 expected_status, expected_skip,
7179 None, None, None, None, None, 1, 1,
7180 '--depth', 'files')
7181 os.chdir(saved_cwd)
7183 def merge_fails_if_subtree_is_deleted_on_src(sbox):
7184 "merge fails if subtree is deleted on src"
7186 ## See http://subversion.tigris.org/issues/show_bug.cgi?id=2876. ##
7188 # Create a WC
7189 sbox.build()
7190 wc_dir = sbox.wc_dir
7192 # Some paths we'll care about
7193 Acopy_path = os.path.join(wc_dir, 'A_copy')
7194 gamma_path = os.path.join(wc_dir, 'A', 'D', 'gamma')
7195 Acopy_gamma_path = os.path.join(wc_dir, 'A_copy', 'D', 'gamma')
7196 A_url = sbox.repo_url + '/A'
7197 Acopy_url = sbox.repo_url + '/A_copy'
7199 # Contents to be added to 'gamma'
7200 new_content = "line1\nline2\nline3\nline4\nline5\n"
7202 svntest.main.file_write(gamma_path, new_content)
7204 # Create expected output tree for commit
7205 expected_output = wc.State(wc_dir, {
7206 'A/D/gamma' : Item(verb='Sending'),
7209 # Create expected status tree for commit
7210 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
7211 expected_status.tweak('A/D/gamma', wc_rev=2)
7213 # Commit the new content
7214 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
7215 expected_status, None, wc_dir)
7217 svntest.actions.run_and_verify_svn(None, None, [], 'cp', A_url, Acopy_url,
7218 '-m', 'create a new copy of A')
7220 # Update working copy
7221 svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
7223 svntest.main.file_substitute(gamma_path, "line1", "this is line1")
7224 # Create expected output tree for commit
7225 expected_output = wc.State(wc_dir, {
7226 'A/D/gamma' : Item(verb='Sending'),
7229 # Create expected status tree for commit
7230 expected_status.tweak(wc_rev=3)
7231 expected_status.tweak('A/D/gamma', wc_rev=4)
7232 expected_status.add({
7233 'A_copy' : Item(status=' ', wc_rev=3),
7234 'A_copy/B' : Item(status=' ', wc_rev=3),
7235 'A_copy/B/lambda' : Item(status=' ', wc_rev=3),
7236 'A_copy/B/E' : Item(status=' ', wc_rev=3),
7237 'A_copy/B/E/alpha': Item(status=' ', wc_rev=3),
7238 'A_copy/B/E/beta' : Item(status=' ', wc_rev=3),
7239 'A_copy/B/F' : Item(status=' ', wc_rev=3),
7240 'A_copy/mu' : Item(status=' ', wc_rev=3),
7241 'A_copy/C' : Item(status=' ', wc_rev=3),
7242 'A_copy/D' : Item(status=' ', wc_rev=3),
7243 'A_copy/D/gamma' : Item(status=' ', wc_rev=3),
7244 'A_copy/D/G' : Item(status=' ', wc_rev=3),
7245 'A_copy/D/G/pi' : Item(status=' ', wc_rev=3),
7246 'A_copy/D/G/rho' : Item(status=' ', wc_rev=3),
7247 'A_copy/D/G/tau' : Item(status=' ', wc_rev=3),
7248 'A_copy/D/H' : Item(status=' ', wc_rev=3),
7249 'A_copy/D/H/chi' : Item(status=' ', wc_rev=3),
7250 'A_copy/D/H/omega': Item(status=' ', wc_rev=3),
7251 'A_copy/D/H/psi' : Item(status=' ', wc_rev=3),
7254 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
7255 expected_status, None, wc_dir)
7257 # Delete A/D/gamma from working copy
7258 svntest.actions.run_and_verify_svn(None, None, [], 'delete', gamma_path)
7259 # Create expected output tree for commit
7260 expected_output = wc.State(wc_dir, {
7261 'A/D/gamma' : Item(verb='Deleting'),
7264 expected_status.remove('A/D/gamma')
7266 svntest.actions.run_and_verify_commit(wc_dir,
7267 expected_output,
7268 expected_status,
7269 None,
7270 wc_dir, wc_dir)
7272 svntest.actions.run_and_verify_svn(None, expected_merge_output([[3,4]],
7273 'U ' + Acopy_gamma_path + '\n'),
7274 [], 'merge', '-r1:4',
7275 A_url + '/D/gamma' + '@4',
7276 Acopy_gamma_path)
7278 svntest.actions.run_and_verify_svn(None, expected_merge_output([[5]],
7279 'D ' + Acopy_gamma_path + '\n'),
7280 [], 'merge', '-r1:5', '--force',
7281 A_url, Acopy_path)
7283 # Test for issue #2976 Subtrees can lose non-inheritable ranges
7284 def merge_away_subtrees_noninheritable_ranges(sbox):
7285 "subtrees can lose non-inheritable ranges"
7287 sbox.build()
7288 wc_dir = sbox.wc_dir
7289 wc_disk, wc_status = set_up_branch(sbox)
7291 # Some paths we'll care about
7292 H_path = os.path.join(wc_dir, "A", "D", "H")
7293 D_COPY_path = os.path.join(wc_dir, "A_COPY", "D")
7295 # Make a change to directory A/D/H and commit as r7.
7296 svntest.actions.run_and_verify_svn(None, ['At revision 6.\n'], [],
7297 'update', wc_dir)
7299 svntest.actions.run_and_verify_svn(
7300 None, ["property 'prop:name' set on '" + H_path + "'\n"], [],
7301 'ps', 'prop:name', 'propval', H_path)
7302 expected_output = svntest.wc.State(wc_dir, {
7303 'A/D/H' : Item(verb='Sending'),})
7304 wc_status.tweak(wc_rev=6)
7305 wc_status.tweak('A/D/H', wc_rev=7)
7306 svntest.actions.run_and_verify_commit(wc_dir, expected_output, wc_status,
7307 None, wc_dir)
7309 # Merge r5:7 --depth immediates to A_COPY/D. This should merge the
7310 # prop change from r7 to A_COPY/H but not the change to A_COPY/D/H/omega
7311 # from r6 since that is below the depth we are merging to. Instead,
7312 # non-inheritable mergeinfo should be set on the immediate directory
7313 # children of A_COPY/D: A_COPY/D/G and A_COPY/D/H.
7315 # Search for the comment entitled "The Merge Kluge" elsewhere in
7316 # this file, to understand why we shorten and chdir() below.
7317 short_D_COPY_path = shorten_path_kludge(D_COPY_path)
7318 expected_output = wc.State(short_D_COPY_path, {
7319 'H' : Item(status=' U'),
7321 expected_status = wc.State(short_D_COPY_path, {
7322 '' : Item(status=' M', wc_rev=6),
7323 'H' : Item(status=' M', wc_rev=6),
7324 'H/chi' : Item(status=' ', wc_rev=6),
7325 'H/omega' : Item(status=' ', wc_rev=6),
7326 'H/psi' : Item(status=' ', wc_rev=6),
7327 'G' : Item(status=' M', wc_rev=6),
7328 'G/pi' : Item(status=' ', wc_rev=6),
7329 'G/rho' : Item(status=' ', wc_rev=6),
7330 'G/tau' : Item(status=' ', wc_rev=6),
7331 'gamma' : Item(status=' ', wc_rev=6),
7333 expected_disk = wc.State('', {
7334 '' : Item(props={SVN_PROP_MERGEINFO : '/A/D:6-7'}),
7335 'H' : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:6-7*',
7336 'prop:name' : 'propval'}),
7337 'H/chi' : Item("This is the file 'chi'.\n"),
7338 'H/omega' : Item("This is the file 'omega'.\n"),
7339 'H/psi' : Item("This is the file 'psi'.\n"),
7340 'G' : Item(props={SVN_PROP_MERGEINFO : '/A/D/G:6-7*'}),
7341 'G/pi' : Item("This is the file 'pi'.\n"),
7342 'G/rho' : Item("This is the file 'rho'.\n"),
7343 'G/tau' : Item("This is the file 'tau'.\n"),
7344 'gamma' : Item("This is the file 'gamma'.\n"),
7346 expected_skip = wc.State(short_D_COPY_path, { })
7347 saved_cwd = os.getcwd()
7348 os.chdir(svntest.main.work_dir)
7349 svntest.actions.run_and_verify_merge(short_D_COPY_path, '5', '7',
7350 sbox.repo_url + '/A/D',
7351 expected_output, expected_disk,
7352 expected_status, expected_skip,
7353 None, None, None, None, None, 1, 1,
7354 '--depth', 'immediates')
7355 os.chdir(saved_cwd)
7357 # Repeat the previous merge but at default depth of infinity. The change
7358 # to A_COPY/D/H/omega should now happen and the non-inheritable ranges on
7359 # A_COPY/D/G and A_COPY/D/H be changed to inheritable and then elide to
7360 # A_COPY/D.
7361 expected_output = wc.State(short_D_COPY_path, {
7362 'H/omega' : Item(status='U '),
7364 expected_disk.tweak('', props={SVN_PROP_MERGEINFO : '/A/D:6-7'})
7365 expected_disk.tweak('H', props={'prop:name' : 'propval'})
7366 expected_disk.tweak('G', props={})
7367 expected_disk.tweak('H/omega', contents="New content")
7368 expected_status.tweak('G', status=' ')
7369 expected_status.tweak('H/omega', status='M ')
7370 os.chdir(svntest.main.work_dir)
7371 svntest.actions.run_and_verify_merge(short_D_COPY_path, '5', '7',
7372 sbox.repo_url + '/A/D',
7373 expected_output, expected_disk,
7374 expected_status, expected_skip,
7375 None, None, None, None, None, 1, 1)
7376 os.chdir(saved_cwd)
7378 # Test for issue #2827
7379 # Handle merge info for sparsely-populated directories
7380 def merge_to_sparse_directories(sbox):
7381 "merge to sparse directories"
7383 # Merges into sparse working copies should set non-inheritable mergeinfo
7384 # on the deepest directories present in the WC.
7386 sbox.build()
7387 wc_dir = sbox.wc_dir
7388 wc_disk, wc_status = set_up_branch(sbox, False, 1)
7390 # Some paths we'll care about
7391 A_path = os.path.join(wc_dir, "A")
7392 D_path = os.path.join(wc_dir, "A", "D")
7393 I_path = os.path.join(wc_dir, "A", "C", "I")
7394 G_path = os.path.join(wc_dir, "A", "D", "G")
7395 A_COPY_path = os.path.join(wc_dir, "A_COPY")
7397 # Make a few more changes to the merge source...
7399 # r7 - modify and commit A/mu
7400 svntest.main.file_write(os.path.join(wc_dir, "A", "mu"),
7401 "New content")
7402 expected_output = wc.State(wc_dir, {'A/mu' : Item(verb='Sending')})
7403 wc_status.tweak('A/mu', wc_rev=7)
7404 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
7405 wc_status, None, wc_dir)
7406 wc_disk.tweak('A/mu', contents="New content")
7408 # r8 - Add a prop to A/D and commit.
7409 svntest.actions.run_and_verify_svn(None, ["At revision 7.\n"], [],
7410 'up', wc_dir)
7411 svntest.actions.run_and_verify_svn(None,
7412 ["property 'prop:name' set on '" +
7413 D_path + "'\n"], [], 'ps',
7414 'prop:name', 'propval', D_path)
7415 expected_output = svntest.wc.State(wc_dir, {
7416 'A/D' : Item(verb='Sending'),
7418 wc_status.tweak(wc_rev=7)
7419 wc_status.tweak('A/D', wc_rev=8)
7420 svntest.actions.run_and_verify_commit(wc_dir, expected_output, wc_status,
7421 None, wc_dir)
7423 # r9 - Add a prop to A and commit.
7424 svntest.actions.run_and_verify_svn(None, ["At revision 8.\n"], [],
7425 'up', wc_dir)
7426 svntest.actions.run_and_verify_svn(None,
7427 ["property 'prop:name' set on '" +
7428 A_path + "'\n"], [], 'ps',
7429 'prop:name', 'propval', A_path)
7430 expected_output = svntest.wc.State(wc_dir, {
7431 'A' : Item(verb='Sending'),
7433 wc_status.tweak(wc_rev=8)
7434 wc_status.tweak('A', wc_rev=9)
7435 svntest.actions.run_and_verify_commit(wc_dir, expected_output, wc_status,
7436 None, wc_dir)
7438 # Do an --immediates checkout of A_COPY
7439 immediates_dir = sbox.add_wc_path('immediates')
7440 expected_output = wc.State(immediates_dir, {
7441 'B' : Item(status='A '),
7442 'mu' : Item(status='A '),
7443 'C' : Item(status='A '),
7444 'D' : Item(status='A '),
7446 expected_disk = wc.State('', {
7447 'B' : Item(),
7448 'mu' : Item("This is the file 'mu'.\n"),
7449 'C' : Item(),
7450 'D' : Item(),
7452 svntest.actions.run_and_verify_checkout(sbox.repo_url + "/A_COPY",
7453 immediates_dir,
7454 expected_output, expected_disk,
7455 None, None, None, None,
7456 "--depth", "immediates")
7458 # Merge r4:9 into the immediates WC.
7459 # The root of the immediates WC should get inheritable r4:9 as should
7460 # the one file present 'mu'. The three directory children present, 'B',
7461 # 'C', and 'D' are checked out at depth empty, so get non-inheritable
7462 # mergeinfo for r4:9. The root and 'D' do should also get the changes
7463 # that affect them directly (the prop adds from r8 and r9). Any changes
7464 # deeper than the immediates should be skipped.
7466 # Search for the comment entitled "The Merge Kluge" elsewhere in
7467 # this file, to understand why we shorten and chdir() below.
7468 short_immediates_dir = shorten_path_kludge(immediates_dir)
7469 expected_output = wc.State(short_immediates_dir, {
7470 'D' : Item(status=' U'),
7471 'mu' : Item(status='U '),
7472 '' : Item(status=' U'),
7474 expected_status = wc.State(short_immediates_dir, {
7475 '' : Item(status=' M', wc_rev=9),
7476 'B' : Item(status=' M', wc_rev=9),
7477 'mu' : Item(status='M ', wc_rev=9),
7478 'C' : Item(status=' M', wc_rev=9),
7479 'D' : Item(status=' M', wc_rev=9),
7481 expected_disk = wc.State('', {
7482 '' : Item(props={SVN_PROP_MERGEINFO : '/A:5-9',
7483 "prop:name" : "propval"}),
7484 'B' : Item(props={SVN_PROP_MERGEINFO : '/A/B:5-9*'}),
7485 'mu' : Item("New content"),
7486 'C' : Item(props={SVN_PROP_MERGEINFO : '/A/C:5-9*'}),
7487 'D' : Item(props={SVN_PROP_MERGEINFO : '/A/D:5-9*',
7488 "prop:name" : "propval"}),
7490 expected_skip = wc.State(short_immediates_dir, {})
7491 saved_cwd = os.getcwd()
7492 os.chdir(svntest.main.work_dir)
7493 svntest.actions.run_and_verify_merge(short_immediates_dir, '4', '9',
7494 sbox.repo_url + \
7495 '/A',
7496 expected_output,
7497 expected_disk,
7498 expected_status,
7499 expected_skip,
7500 None, None, None, None,
7501 None, 1)
7502 os.chdir(saved_cwd)
7504 # Do a --files checkout of A_COPY
7505 files_dir = sbox.add_wc_path('files')
7506 expected_output = wc.State(files_dir, {
7507 'mu' : Item(status='A '),
7509 expected_disk = wc.State('', {
7510 'mu' : Item("This is the file 'mu'.\n"),
7512 svntest.actions.run_and_verify_checkout(sbox.repo_url + "/A_COPY",
7513 files_dir,
7514 expected_output, expected_disk,
7515 None, None, None, None,
7516 "--depth", "files")
7518 # Merge r4:9 into the files WC.
7519 # The root of the files WC should get non-inheritable r4:9 and its one
7520 # present child 'mu' should get the same but inheritable. The root
7521 # should also get the change that affects it directly (the prop add
7522 # from r9).
7523 short_files_dir = shorten_path_kludge(files_dir)
7524 expected_output = wc.State(short_files_dir, {
7525 'mu' : Item(status='U '),
7526 '' : Item(status=' U'),
7528 expected_status = wc.State(short_files_dir, {
7529 '' : Item(status=' M', wc_rev=9),
7530 'mu' : Item(status='MM', wc_rev=9),
7532 expected_disk = wc.State('', {
7533 '' : Item(props={SVN_PROP_MERGEINFO : '/A:5-9*',
7534 "prop:name" : "propval"}),
7535 'mu' : Item("New content",
7536 props={SVN_PROP_MERGEINFO : '/A/mu:5-9'}),
7538 expected_skip = wc.State(short_files_dir, {})
7539 os.chdir(svntest.main.work_dir)
7540 svntest.actions.run_and_verify_merge(short_files_dir, '4', '9',
7541 sbox.repo_url + \
7542 '/A',
7543 expected_output,
7544 expected_disk,
7545 expected_status,
7546 expected_skip,
7547 None, None, None, None,
7548 None, 1)
7549 os.chdir(saved_cwd)
7551 # Do an --empty checkout of A_COPY
7552 empty_dir = sbox.add_wc_path('empty')
7553 expected_output = wc.State(empty_dir, {})
7554 expected_disk = wc.State('', {})
7555 svntest.actions.run_and_verify_checkout(sbox.repo_url + "/A_COPY",
7556 empty_dir,
7557 expected_output, expected_disk,
7558 None, None, None, None,
7559 "--depth", "empty")
7561 # Merge r4:9 into the empty WC.
7562 # The root of the files WC should get non-inheritable r4:9 and also get
7563 # the one change that affects it directly (the prop add from r9).
7564 short_empty_dir = shorten_path_kludge(empty_dir)
7565 expected_output = wc.State(short_empty_dir, {
7566 '' : Item(status=' U'),
7568 expected_status = wc.State(short_empty_dir, {
7569 '' : Item(status=' M', wc_rev=9),
7571 expected_disk = wc.State('', {
7572 '' : Item(props={SVN_PROP_MERGEINFO : '/A:5-9*',
7573 "prop:name" : "propval"}),
7575 expected_skip = wc.State(short_empty_dir, {})
7576 os.chdir(svntest.main.work_dir)
7577 svntest.actions.run_and_verify_merge(short_empty_dir, '4', '9',
7578 sbox.repo_url + \
7579 '/A',
7580 expected_output,
7581 expected_disk,
7582 expected_status,
7583 expected_skip,
7584 None, None, None, None,
7585 None, 1)
7586 os.chdir(saved_cwd)
7588 def merge_old_and_new_revs_from_renamed_dir(sbox):
7589 "merge -rold(before rename):head renamed dir"
7591 ## See http://svn.haxx.se/dev/archive-2007-09/0706.shtml ##
7593 # Create a WC with a single branch
7594 sbox.build()
7595 wc_dir = sbox.wc_dir
7596 wc_disk, wc_status = set_up_branch(sbox, True, 1)
7598 # Some paths we'll care about
7599 A_url = sbox.repo_url + '/A'
7600 A_MOVED_url = sbox.repo_url + '/A_MOVED'
7601 A_COPY_path = os.path.join(wc_dir, 'A_COPY')
7602 mu_path = os.path.join(wc_dir, 'A', 'mu')
7603 A_MOVED_mu_path = os.path.join(wc_dir, 'A_MOVED', 'mu')
7605 # Make a modification to A/mu
7606 svntest.main.file_write(mu_path, "This is the file 'mu' modified.\n")
7607 expected_output = wc.State(wc_dir, {'A/mu' : Item(verb='Sending')})
7608 wc_status.add({'A/mu' : Item(status=' ', wc_rev=3)})
7609 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
7610 wc_status, None, wc_dir)
7612 # Move A to A_MOVED
7613 svntest.actions.run_and_verify_svn(None, ['\n', 'Committed revision 4.\n'],
7614 [], 'mv', '-m', 'mv A to A_MOVED',
7615 A_url, A_MOVED_url)
7617 # Update the working copy to get A_MOVED
7618 svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
7620 # Make a modification to A_MOVED/mu
7621 svntest.main.file_write(A_MOVED_mu_path, "This is 'mu' in A_MOVED.\n")
7622 expected_output = wc.State(wc_dir, {'A_MOVED/mu' : Item(verb='Sending')})
7623 expected_status = svntest.actions.get_virginal_state(wc_dir, 4)
7624 expected_status.remove('A', 'A/mu', 'A/C', 'A/D', 'A/B', 'A/B/lambda',
7625 'A/B/E', 'A/B/E/alpha', 'A/B/E/beta', 'A/B/F',
7626 'A/D/gamma', 'A/D/G', 'A/D/G/pi', 'A/D/G/rho',
7627 'A/D/G/tau', 'A/D/H', 'A/D/H/chi', 'A/D/H/omega',
7628 'A/D/H/psi')
7629 expected_status.add({
7630 '' : Item(status=' ', wc_rev=4),
7631 'iota' : Item(status=' ', wc_rev=4),
7632 'A_MOVED' : Item(status=' ', wc_rev=4),
7633 'A_MOVED/mu' : Item(status=' ', wc_rev=5),
7634 'A_MOVED/C' : Item(status=' ', wc_rev=4),
7635 'A_MOVED/D' : Item(status=' ', wc_rev=4),
7636 'A_MOVED/B' : Item(status=' ', wc_rev=4),
7637 'A_MOVED/B/lambda' : Item(status=' ', wc_rev=4),
7638 'A_MOVED/B/E' : Item(status=' ', wc_rev=4),
7639 'A_MOVED/B/E/alpha': Item(status=' ', wc_rev=4),
7640 'A_MOVED/B/E/beta' : Item(status=' ', wc_rev=4),
7641 'A_MOVED/B/F' : Item(status=' ', wc_rev=4),
7642 'A_MOVED/D/gamma' : Item(status=' ', wc_rev=4),
7643 'A_MOVED/D/G' : Item(status=' ', wc_rev=4),
7644 'A_MOVED/D/G/pi' : Item(status=' ', wc_rev=4),
7645 'A_MOVED/D/G/rho' : Item(status=' ', wc_rev=4),
7646 'A_MOVED/D/G/tau' : Item(status=' ', wc_rev=4),
7647 'A_MOVED/D/H' : Item(status=' ', wc_rev=4),
7648 'A_MOVED/D/H/chi' : Item(status=' ', wc_rev=4),
7649 'A_MOVED/D/H/omega': Item(status=' ', wc_rev=4),
7650 'A_MOVED/D/H/psi' : Item(status=' ', wc_rev=4),
7651 'A_COPY' : Item(status=' ', wc_rev=4),
7652 'A_COPY/mu' : Item(status=' ', wc_rev=4),
7653 'A_COPY/C' : Item(status=' ', wc_rev=4),
7654 'A_COPY/D' : Item(status=' ', wc_rev=4),
7655 'A_COPY/B' : Item(status=' ', wc_rev=4),
7656 'A_COPY/B/lambda' : Item(status=' ', wc_rev=4),
7657 'A_COPY/B/E' : Item(status=' ', wc_rev=4),
7658 'A_COPY/B/E/alpha' : Item(status=' ', wc_rev=4),
7659 'A_COPY/B/E/beta' : Item(status=' ', wc_rev=4),
7660 'A_COPY/B/F' : Item(status=' ', wc_rev=4),
7661 'A_COPY/D/gamma' : Item(status=' ', wc_rev=4),
7662 'A_COPY/D/G' : Item(status=' ', wc_rev=4),
7663 'A_COPY/D/G/pi' : Item(status=' ', wc_rev=4),
7664 'A_COPY/D/G/rho' : Item(status=' ', wc_rev=4),
7665 'A_COPY/D/G/tau' : Item(status=' ', wc_rev=4),
7666 'A_COPY/D/H' : Item(status=' ', wc_rev=4),
7667 'A_COPY/D/H/chi' : Item(status=' ', wc_rev=4),
7668 'A_COPY/D/H/omega' : Item(status=' ', wc_rev=4),
7669 'A_COPY/D/H/psi' : Item(status=' ', wc_rev=4),
7671 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
7672 expected_status, None, wc_dir)
7674 short_A_COPY = shorten_path_kludge(A_COPY_path)
7675 saved_cwd = os.getcwd()
7676 os.chdir(svntest.main.work_dir)
7678 # Merge /A_MOVED to /A_COPY - this happens in multiple passes
7679 # because /A_MOVED has renames in its history between the boundaries
7680 # of the requested merge range.
7681 expected_output = wc.State(short_A_COPY, {
7682 'mu' : Item(status='G '), # mu gets touched twice
7684 expected_status = wc.State(short_A_COPY, {
7685 '' : Item(status=' M', wc_rev=4),
7686 'mu' : Item(status='M ', wc_rev=4),
7687 'C' : Item(status=' ', wc_rev=4),
7688 'D' : Item(status=' ', wc_rev=4),
7689 'B' : Item(status=' ', wc_rev=4),
7690 'B/lambda' : Item(status=' ', wc_rev=4),
7691 'B/E' : Item(status=' ', wc_rev=4),
7692 'B/E/alpha': Item(status=' ', wc_rev=4),
7693 'B/E/beta' : Item(status=' ', wc_rev=4),
7694 'B/F' : Item(status=' ', wc_rev=4),
7695 'D/gamma' : Item(status=' ', wc_rev=4),
7696 'D/G' : Item(status=' ', wc_rev=4),
7697 'D/G/pi' : Item(status=' ', wc_rev=4),
7698 'D/G/rho' : Item(status=' ', wc_rev=4),
7699 'D/G/tau' : Item(status=' ', wc_rev=4),
7700 'D/H' : Item(status=' ', wc_rev=4),
7701 'D/H/chi' : Item(status=' ', wc_rev=4),
7702 'D/H/omega': Item(status=' ', wc_rev=4),
7703 'D/H/psi' : Item(status=' ', wc_rev=4),
7705 expected_disk = wc.State('', {
7706 '' : Item(props={SVN_PROP_MERGEINFO : '/A:3\n/A_MOVED:4-5\n'}),
7707 'mu' : Item("This is 'mu' in A_MOVED.\n"),
7708 'C' : Item(),
7709 'D' : Item(),
7710 'B' : Item(),
7711 'B/lambda' : Item("This is the file 'lambda'.\n"),
7712 'B/E' : Item(),
7713 'B/E/alpha': Item("This is the file 'alpha'.\n"),
7714 'B/E/beta' : Item("This is the file 'beta'.\n"),
7715 'B/F' : Item(),
7716 'D/gamma' : Item("This is the file 'gamma'.\n"),
7717 'D/G' : Item(),
7718 'D/G/pi' : Item("This is the file 'pi'.\n"),
7719 'D/G/rho' : Item("This is the file 'rho'.\n"),
7720 'D/G/tau' : Item("This is the file 'tau'.\n"),
7721 'D/H' : Item(),
7722 'D/H/chi' : Item("This is the file 'chi'.\n"),
7723 'D/H/omega': Item("This is the file 'omega'.\n"),
7724 'D/H/psi' : Item("This is the file 'psi'.\n"),
7726 expected_skip = wc.State(short_A_COPY, {})
7728 ### Disabling dry_run mode because currently it can't handle the way
7729 ### 'mu' gets textually modified in multiple passes.
7730 svntest.actions.run_and_verify_merge(short_A_COPY, '2', '5',
7731 A_MOVED_url,
7732 expected_output,
7733 expected_disk,
7734 expected_status,
7735 expected_skip,
7736 None, None, None, None, None,
7737 True, False)
7738 os.chdir(saved_cwd)
7740 def merge_with_child_having_different_rev_ranges_to_merge(sbox):
7741 "child having different rev ranges to merge"
7742 #Modify A/mu to 30 lines with a content 'line1'...'line30' commit it at r2.
7743 #Create a branch A_COPY from A, commit it at r3.
7744 #Modify A/mu line number 7 to 'LINE7' modify and commit at r4.
7745 #Modify A/mu line number 17 to 'LINE17' modify, set prop 'prop1' on 'A'
7746 #with a value 'val1' and commit at r5.
7747 #Modify A/mu line number 27 to 'LINE27' modify and commit at r6.
7748 #Merge r5 to 'A/mu' as a single file merge explicitly to 'A_COPY/mu'.
7749 #Merge r3:6 from 'A' to 'A_COPY
7750 #This should merge r4 and then r5 through r6.
7751 #Revert r5 and r6 via single file merge on A_COPY/mu.
7752 #Revert r6 through r4 on A_COPY this should get back us the pristine copy.
7753 #Merge r3:6 from 'A' to 'A_COPY
7754 #Revert r5 on A_COPY/mu
7755 #Modify line number 17 with 'some other line17' of A_COPY/mu
7756 #Merge r6:3 from 'A' to 'A_COPY, This should leave line number 17
7757 #undisturbed in A_COPY/mu, rest should be reverted.
7759 # Create a WC
7760 sbox.build()
7761 wc_dir = sbox.wc_dir
7762 A_path = os.path.join(wc_dir, 'A')
7763 mu_path = os.path.join(wc_dir, 'A', 'mu')
7764 A_url = sbox.repo_url + '/A'
7765 A_mu_url = sbox.repo_url + '/A/mu'
7766 A_COPY_url = sbox.repo_url + '/A_COPY'
7767 A_COPY_path = os.path.join(wc_dir, 'A_COPY')
7768 A_COPY_mu_path = os.path.join(wc_dir, 'A_COPY', 'mu')
7769 thirty_line_dummy_text = 'line1\n'
7770 for i in range(2, 31):
7771 thirty_line_dummy_text += 'line' + str(i) + '\n'
7773 svntest.main.file_write(mu_path, thirty_line_dummy_text)
7774 expected_output = wc.State(wc_dir, {'A/mu' : Item(verb='Sending')})
7775 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
7776 expected_status.tweak('A/mu', wc_rev=2)
7777 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
7778 expected_status, None, wc_dir)
7779 svntest.actions.run_and_verify_svn(None, None, [],
7780 'cp', A_url, A_COPY_url, '-m', 'rev 3')
7781 # Update the working copy to get A_COPY
7782 svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
7783 expected_status.add({'A_COPY' : Item(status=' '),
7784 'A_COPY/mu' : Item(status=' '),
7785 'A_COPY/C' : Item(status=' '),
7786 'A_COPY/D' : Item(status=' '),
7787 'A_COPY/B' : Item(status=' '),
7788 'A_COPY/B/lambda' : Item(status=' '),
7789 'A_COPY/B/E' : Item(status=' '),
7790 'A_COPY/B/E/alpha' : Item(status=' '),
7791 'A_COPY/B/E/beta' : Item(status=' '),
7792 'A_COPY/B/F' : Item(status=' '),
7793 'A_COPY/D/gamma' : Item(status=' '),
7794 'A_COPY/D/G' : Item(status=' '),
7795 'A_COPY/D/G/pi' : Item(status=' '),
7796 'A_COPY/D/G/rho' : Item(status=' '),
7797 'A_COPY/D/G/tau' : Item(status=' '),
7798 'A_COPY/D/H' : Item(status=' '),
7799 'A_COPY/D/H/chi' : Item(status=' '),
7800 'A_COPY/D/H/omega' : Item(status=' '),
7801 'A_COPY/D/H/psi' : Item(status=' ')})
7802 expected_status.tweak(wc_rev=3)
7803 tweaked_7th_line = thirty_line_dummy_text.replace('line7', 'LINE 7')
7804 svntest.main.file_write(mu_path, tweaked_7th_line)
7805 expected_status.tweak('A/mu', wc_rev=4)
7806 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
7807 expected_status, None, wc_dir)
7808 svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
7809 expected_status.tweak(wc_rev=4)
7810 tweaked_17th_line = tweaked_7th_line.replace('line17', 'LINE 17')
7811 svntest.main.file_write(mu_path, tweaked_17th_line)
7812 svntest.main.run_svn(None, 'propset', 'prop1', 'val1', A_path)
7813 expected_output = wc.State(wc_dir,
7815 'A' : Item(verb='Sending'),
7816 'A/mu' : Item(verb='Sending')
7819 expected_status.tweak('A', wc_rev=5)
7820 expected_status.tweak('A/mu', wc_rev=5)
7821 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
7822 expected_status, None, wc_dir)
7823 tweaked_27th_line = tweaked_17th_line.replace('line27', 'LINE 27')
7824 svntest.main.file_write(mu_path, tweaked_27th_line)
7825 expected_status.tweak('A/mu', wc_rev=6)
7826 expected_output = wc.State(wc_dir, {'A/mu' : Item(verb='Sending')})
7827 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
7828 expected_status, None, wc_dir)
7829 # Merge r5 to A_COPY/mu
7830 svntest.actions.run_and_verify_svn(None,
7831 expected_merge_output([[5]],
7832 ['U ' + A_COPY_mu_path + '\n']),
7834 'merge', '-r4:5',
7835 A_mu_url,
7836 A_COPY_mu_path)
7838 short_A_COPY = shorten_path_kludge(A_COPY_path)
7839 saved_cwd = os.getcwd()
7840 os.chdir(svntest.main.work_dir)
7841 expected_skip = wc.State(short_A_COPY, {})
7842 expected_output = wc.State(short_A_COPY, {
7843 '' : Item(status=' U'),
7844 'mu' : Item(status='G '),
7846 expected_status = wc.State(short_A_COPY, {
7847 '' : Item(status=' M', wc_rev=4),
7848 'mu' : Item(status='M ', wc_rev=4),
7849 'C' : Item(status=' ', wc_rev=4),
7850 'D' : Item(status=' ', wc_rev=4),
7851 'B' : Item(status=' ', wc_rev=4),
7852 'B/lambda' : Item(status=' ', wc_rev=4),
7853 'B/E' : Item(status=' ', wc_rev=4),
7854 'B/E/alpha': Item(status=' ', wc_rev=4),
7855 'B/E/beta' : Item(status=' ', wc_rev=4),
7856 'B/F' : Item(status=' ', wc_rev=4),
7857 'D/gamma' : Item(status=' ', wc_rev=4),
7858 'D/G' : Item(status=' ', wc_rev=4),
7859 'D/G/pi' : Item(status=' ', wc_rev=4),
7860 'D/G/rho' : Item(status=' ', wc_rev=4),
7861 'D/G/tau' : Item(status=' ', wc_rev=4),
7862 'D/H' : Item(status=' ', wc_rev=4),
7863 'D/H/chi' : Item(status=' ', wc_rev=4),
7864 'D/H/omega': Item(status=' ', wc_rev=4),
7865 'D/H/psi' : Item(status=' ', wc_rev=4),
7867 expected_disk = wc.State('', {
7868 '' : Item(props={SVN_PROP_MERGEINFO : '/A:4-6',
7869 'prop1' : 'val1'}),
7870 'mu' : Item(tweaked_27th_line),
7871 'C' : Item(),
7872 'D' : Item(),
7873 'B' : Item(),
7874 'B/lambda' : Item("This is the file 'lambda'.\n"),
7875 'B/E' : Item(),
7876 'B/E/alpha': Item("This is the file 'alpha'.\n"),
7877 'B/E/beta' : Item("This is the file 'beta'.\n"),
7878 'B/F' : Item(),
7879 'D/gamma' : Item("This is the file 'gamma'.\n"),
7880 'D/G' : Item(),
7881 'D/G/pi' : Item("This is the file 'pi'.\n"),
7882 'D/G/rho' : Item("This is the file 'rho'.\n"),
7883 'D/G/tau' : Item("This is the file 'tau'.\n"),
7884 'D/H' : Item(),
7885 'D/H/chi' : Item("This is the file 'chi'.\n"),
7886 'D/H/omega': Item("This is the file 'omega'.\n"),
7887 'D/H/psi' : Item("This is the file 'psi'.\n"),
7889 svntest.actions.run_and_verify_merge(short_A_COPY, '3', '6',
7890 A_url,
7891 expected_output,
7892 expected_disk,
7893 expected_status,
7894 expected_skip,
7895 None, None, None, None, None, 1)
7896 os.chdir(saved_cwd)
7897 # Revert r5 and r6 on A_COPY/mu
7898 svntest.actions.run_and_verify_svn(None,
7899 expected_merge_output([[6,5]],
7900 ['G ' + A_COPY_mu_path + '\n']),
7902 'merge', '-r6:4',
7903 A_mu_url,
7904 A_COPY_mu_path)
7906 expected_output = wc.State(short_A_COPY, {
7907 '' : Item(status=' G'), # merged removal of prop1 property
7908 'mu' : Item(status='G '), # merged reversion of text changes
7911 expected_status.tweak('', status=' ')
7912 expected_status.tweak('mu', status=' ')
7913 expected_disk.tweak('', props={})
7914 expected_disk.remove('')
7915 expected_disk.tweak('mu', contents=thirty_line_dummy_text)
7916 os.chdir(svntest.main.work_dir)
7917 svntest.actions.run_and_verify_merge(short_A_COPY, '6', '3',
7918 A_url,
7919 expected_output,
7920 expected_disk,
7921 expected_status,
7922 expected_skip,
7923 None, None, None, None, None, 1)
7924 os.chdir(saved_cwd)
7926 expected_disk.add({'' : Item(props={SVN_PROP_MERGEINFO : '/A:4-6',
7927 'prop1' : 'val1'})})
7928 expected_disk.tweak('mu', contents=tweaked_27th_line)
7929 expected_output = wc.State(short_A_COPY, {
7930 '' : Item(status=' U'), # new mergeinfo and prop1 property
7931 'mu' : Item(status='U '), # text changes
7933 expected_status.tweak('', status=' M')
7934 expected_status.tweak('mu', status='M ')
7935 os.chdir(svntest.main.work_dir)
7936 svntest.actions.run_and_verify_merge(short_A_COPY, '3', '6',
7937 A_url,
7938 expected_output,
7939 expected_disk,
7940 expected_status,
7941 expected_skip,
7942 None, None, None, None, None, 1)
7943 os.chdir(saved_cwd)
7944 #Revert r5 on A_COPY/mu
7945 svntest.actions.run_and_verify_svn(None,
7946 expected_merge_output([[-5]],
7947 ['G ' + A_COPY_mu_path + '\n']),
7949 'merge', '-r5:4',
7950 A_mu_url,
7951 A_COPY_mu_path)
7952 tweaked_17th_line_1 = tweaked_27th_line.replace('LINE 17',
7953 'some other line17')
7954 tweaked_17th_line_2 = thirty_line_dummy_text.replace('line17',
7955 'some other line17')
7956 svntest.main.file_write(A_COPY_mu_path, tweaked_17th_line_1)
7957 expected_output = wc.State(short_A_COPY, {
7958 '' : Item(status=' G'),
7959 'mu' : Item(status='G '),
7961 expected_status.tweak('', status=' ')
7962 expected_status.tweak('mu', status='M ')
7963 expected_disk.remove('')
7964 expected_disk.tweak('mu', contents=tweaked_17th_line_2)
7965 os.chdir(svntest.main.work_dir)
7966 svntest.actions.run_and_verify_merge(short_A_COPY, '6', '3',
7967 A_url,
7968 expected_output,
7969 expected_disk,
7970 expected_status,
7971 expected_skip,
7972 None, None, None, None, None, 1)
7973 os.chdir(saved_cwd)
7975 def merge_old_and_new_revs_from_renamed_file(sbox):
7976 "merge -rold(before rename):head renamed file"
7978 ## See http://svn.haxx.se/dev/archive-2007-09/0706.shtml ##
7980 # Create a WC
7981 sbox.build()
7982 wc_dir = sbox.wc_dir
7984 # Some paths we'll care about
7985 mu_url = sbox.repo_url + '/A/mu'
7986 mu_MOVED_url = sbox.repo_url + '/A/mu_MOVED'
7987 mu_COPY_url = sbox.repo_url + '/A/mu_COPY'
7988 mu_COPY_path = os.path.join(wc_dir, 'A', 'mu_COPY')
7989 mu_path = os.path.join(wc_dir, 'A', 'mu')
7990 mu_MOVED_path = os.path.join(wc_dir, 'A', 'mu_MOVED')
7992 # Copy mu to mu_COPY
7993 svntest.actions.run_and_verify_svn(None, ['\n', 'Committed revision 2.\n'],
7994 [], 'cp', '-m', 'cp mu to mu_COPY',
7995 mu_url, mu_COPY_url)
7997 # Make a modification to A/mu
7998 svntest.main.file_write(mu_path, "This is the file 'mu' modified.\n")
7999 expected_output = wc.State(wc_dir, {'A/mu' : Item(verb='Sending')})
8000 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
8001 expected_status.tweak('A/mu', wc_rev=3)
8002 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
8003 expected_status, None, wc_dir)
8005 # Move mu to mu_MOVED
8006 svntest.actions.run_and_verify_svn(None, ['\n', 'Committed revision 4.\n'],
8007 [], 'mv', '-m', 'mv mu to mu_MOVED',
8008 mu_url, mu_MOVED_url)
8010 # Update the working copy to get mu_MOVED
8011 svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
8013 # Make a modification to mu_MOVED
8014 svntest.main.file_write(mu_MOVED_path, "This is 'mu' in mu_MOVED.\n")
8015 expected_output = wc.State(wc_dir, {'A/mu_MOVED' : Item(verb='Sending')})
8016 expected_status = svntest.actions.get_virginal_state(wc_dir, 4)
8017 expected_status.remove('A/mu')
8018 expected_status.add({
8019 'A/mu_MOVED' : Item(status=' ', wc_rev=5),
8020 'A/mu_COPY' : Item(status=' ', wc_rev=4),
8022 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
8023 expected_status, None, wc_dir)
8025 # Merge A/mu_MOVED to A/mu_COPY - this happens in multiple passes
8026 # because A/mu_MOVED has renames in its history between the
8027 # boundaries of the requested merge range.
8028 expected_output = expected_merge_output([[2,3],[4,5]],
8029 ['U %s\n' % (mu_COPY_path),
8030 'G %s\n' % (mu_COPY_path)])
8031 svntest.actions.run_and_verify_svn(None, expected_output,
8032 [], 'merge', '-r', '1:5',
8033 mu_MOVED_url,
8034 mu_COPY_path)
8035 svntest.actions.run_and_verify_svn(None, ['/A/mu:2-3\n',
8036 '/A/mu_MOVED:4-5\n'],
8037 [], 'propget', SVN_PROP_MERGEINFO,
8038 mu_COPY_path)
8041 def merge_with_auto_rev_range_detection(sbox):
8042 "merge with auto detection of revision ranges"
8044 ## See http://svn.haxx.se/dev/archive-2007-09/0735.shtml ##
8046 # Create a WC
8047 sbox.build()
8048 wc_dir = sbox.wc_dir
8050 # Some paths we'll care about
8051 A_url = sbox.repo_url + '/A'
8052 A_COPY_url = sbox.repo_url + '/A_COPY'
8053 B1_path = os.path.join(wc_dir, 'A', 'B1')
8054 B1_mu_path = os.path.join(wc_dir, 'A', 'B1', 'mu')
8055 A_COPY_path = os.path.join(wc_dir, 'A_COPY')
8057 # Create B1 inside A
8058 svntest.actions.run_and_verify_svn(None, ["A " + B1_path + "\n"],
8059 [], 'mkdir',
8060 B1_path)
8062 # Add a file mu inside B1
8063 svntest.main.file_write(B1_mu_path, "This is the file 'mu'.\n")
8064 svntest.actions.run_and_verify_svn(None, ["A " + B1_mu_path + "\n"],
8065 [], 'add', B1_mu_path)
8067 # Commit B1 and B1/mu
8068 expected_output = wc.State(wc_dir, {
8069 'A/B1' : Item(verb='Adding'),
8070 'A/B1/mu' : Item(verb='Adding'),
8072 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
8073 expected_status.add({
8074 'A/B1' : Item(status=' ', wc_rev=2),
8075 'A/B1/mu' : Item(status=' ', wc_rev=2),
8077 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
8078 expected_status, None, wc_dir)
8080 # Copy A to A_COPY
8081 svntest.actions.run_and_verify_svn(None, ['\n', 'Committed revision 3.\n'],
8082 [], 'cp', '-m', 'cp A to A_COPY',
8083 A_url, A_COPY_url)
8085 # Make a modification to A/B1/mu
8086 svntest.main.file_write(B1_mu_path, "This is the file 'mu' modified.\n")
8087 expected_output = wc.State(wc_dir, {'A/B1/mu' : Item(verb='Sending')})
8088 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
8089 expected_status.add({
8090 'A/B1' : Item(status=' ', wc_rev=2),
8091 'A/B1/mu' : Item(status=' ', wc_rev=4),
8093 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
8094 expected_status, None, wc_dir)
8096 # Update the working copy to get A_COPY
8097 svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
8099 short_A_COPY = shorten_path_kludge(A_COPY_path)
8100 saved_cwd = os.getcwd()
8101 os.chdir(svntest.main.work_dir)
8103 # Merge /A to /A_COPY
8104 expected_output = wc.State(short_A_COPY, {
8105 'B1/mu' : Item(status='U '),
8107 expected_status = wc.State(short_A_COPY, {
8108 '' : Item(status=' M', wc_rev=4),
8109 'mu' : Item(status=' ', wc_rev=4),
8110 'C' : Item(status=' ', wc_rev=4),
8111 'D' : Item(status=' ', wc_rev=4),
8112 'B' : Item(status=' ', wc_rev=4),
8113 'B/lambda' : Item(status=' ', wc_rev=4),
8114 'B/E' : Item(status=' ', wc_rev=4),
8115 'B/E/alpha': Item(status=' ', wc_rev=4),
8116 'B/E/beta' : Item(status=' ', wc_rev=4),
8117 'B/F' : Item(status=' ', wc_rev=4),
8118 'B1' : Item(status=' ', wc_rev=4),
8119 'B1/mu' : Item(status='M ', wc_rev=4),
8120 'D/gamma' : Item(status=' ', wc_rev=4),
8121 'D/G' : Item(status=' ', wc_rev=4),
8122 'D/G/pi' : Item(status=' ', wc_rev=4),
8123 'D/G/rho' : Item(status=' ', wc_rev=4),
8124 'D/G/tau' : Item(status=' ', wc_rev=4),
8125 'D/H' : Item(status=' ', wc_rev=4),
8126 'D/H/chi' : Item(status=' ', wc_rev=4),
8127 'D/H/omega': Item(status=' ', wc_rev=4),
8128 'D/H/psi' : Item(status=' ', wc_rev=4),
8130 expected_disk = wc.State('', {
8131 '' : Item(props={SVN_PROP_MERGEINFO : '/A:3-4'}),
8132 'mu' : Item("This is the file 'mu'.\n"),
8133 'C' : Item(),
8134 'D' : Item(),
8135 'B' : Item(),
8136 'B/lambda' : Item("This is the file 'lambda'.\n"),
8137 'B/E' : Item(),
8138 'B/E/alpha': Item("This is the file 'alpha'.\n"),
8139 'B/E/beta' : Item("This is the file 'beta'.\n"),
8140 'B/F' : Item(),
8141 'B1' : Item(),
8142 'B1/mu' : Item("This is the file 'mu' modified.\n"),
8143 'D/gamma' : Item("This is the file 'gamma'.\n"),
8144 'D/G' : Item(),
8145 'D/G/pi' : Item("This is the file 'pi'.\n"),
8146 'D/G/rho' : Item("This is the file 'rho'.\n"),
8147 'D/G/tau' : Item("This is the file 'tau'.\n"),
8148 'D/H' : Item(),
8149 'D/H/chi' : Item("This is the file 'chi'.\n"),
8150 'D/H/omega': Item("This is the file 'omega'.\n"),
8151 'D/H/psi' : Item("This is the file 'psi'.\n"),
8153 expected_skip = wc.State(short_A_COPY, {})
8154 svntest.actions.run_and_verify_merge(short_A_COPY, None, None,
8155 A_url,
8156 expected_output,
8157 expected_disk,
8158 expected_status,
8159 expected_skip,
8160 None, None, None, None, None,
8161 1, 1)
8162 os.chdir(saved_cwd)
8164 def mergeinfo_recording_in_skipped_merge(sbox):
8165 "mergeinfo recording in skipped merge"
8167 ## See http://subversion.tigris.org/issues/show_bug.cgi?id=2829. ##
8169 # Create a WC with a single branch
8170 sbox.build()
8171 wc_dir = sbox.wc_dir
8172 wc_disk, wc_status = set_up_branch(sbox, True, 1)
8174 # Some paths we'll care about
8175 A_url = sbox.repo_url + '/A'
8176 A_COPY_path = os.path.join(wc_dir, 'A_COPY')
8177 mu_path = os.path.join(wc_dir, 'A', 'mu')
8178 alpha_path = os.path.join(wc_dir, 'A', 'B', 'E', 'alpha')
8179 A_COPY_B_E_path = os.path.join(wc_dir, 'A_COPY', 'B', 'E')
8180 A_COPY_alpha_path = os.path.join(wc_dir, 'A_COPY', 'B', 'E', 'alpha')
8181 A_COPY_beta_path = os.path.join(wc_dir, 'A_COPY', 'B', 'E', 'beta')
8183 # Make a modification to A/mu
8184 svntest.main.file_write(mu_path, "This is the file 'mu' modified.\n")
8185 expected_output = wc.State(wc_dir, {'A/mu' : Item(verb='Sending')})
8186 wc_status.add({'A/mu' : Item(status=' ', wc_rev=3)})
8187 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
8188 wc_status, None, wc_dir)
8190 # Make a modification to A/B/E/alpha
8191 svntest.main.file_write(alpha_path, "This is the file 'alpha' modified.\n")
8192 expected_output = wc.State(wc_dir, {'A/B/E/alpha' : Item(verb='Sending')})
8193 wc_status.add({'A/B/E/alpha' : Item(status=' ', wc_rev=4)})
8194 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
8195 wc_status, None, wc_dir)
8197 # Delete A_COPY/B/E
8198 svntest.actions.run_and_verify_svn(None, None, [], 'rm',
8199 A_COPY_B_E_path)
8201 short_A_COPY = shorten_path_kludge(A_COPY_path)
8202 saved_cwd = os.getcwd()
8203 os.chdir(svntest.main.work_dir)
8205 # Merge /A to /A_COPY ie., r1 to r4
8206 expected_output = wc.State(short_A_COPY, {
8207 'mu' : Item(status='U '),
8209 expected_status = wc.State(short_A_COPY, {
8210 '' : Item(status=' M', wc_rev=2),
8211 'mu' : Item(status='M ', wc_rev=2),
8212 'B' : Item(status=' ', wc_rev=2),
8213 'B/lambda' : Item(status=' ', wc_rev=2),
8214 'B/F' : Item(status=' ', wc_rev=2),
8215 'B/E' : Item(status='D ', wc_rev=2),
8216 'B/E/alpha': Item(status='D ', wc_rev=2),
8217 'B/E/beta' : Item(status='D ', wc_rev=2),
8218 'C' : Item(status=' ', wc_rev=2),
8219 'D' : Item(status=' ', wc_rev=2),
8220 'D/gamma' : Item(status=' ', wc_rev=2),
8221 'D/G' : Item(status=' ', wc_rev=2),
8222 'D/G/pi' : Item(status=' ', wc_rev=2),
8223 'D/G/rho' : Item(status=' ', wc_rev=2),
8224 'D/G/tau' : Item(status=' ', wc_rev=2),
8225 'D/H' : Item(status=' ', wc_rev=2),
8226 'D/H/chi' : Item(status=' ', wc_rev=2),
8227 'D/H/omega': Item(status=' ', wc_rev=2),
8228 'D/H/psi' : Item(status=' ', wc_rev=2),
8230 expected_disk = wc.State('', {
8231 '' : Item(props={SVN_PROP_MERGEINFO : '/A:2-4'}),
8232 'mu' : Item("This is the file 'mu' modified.\n"),
8233 'C' : Item(),
8234 'D' : Item(),
8235 'B' : Item(),
8236 'B/lambda' : Item(contents="This is the file 'lambda'.\n"),
8237 'B/F' : Item(),
8238 'B/E' : Item(),
8239 'D/gamma' : Item("This is the file 'gamma'.\n"),
8240 'D/G' : Item(),
8241 'D/G/pi' : Item("This is the file 'pi'.\n"),
8242 'D/G/rho' : Item("This is the file 'rho'.\n"),
8243 'D/G/tau' : Item("This is the file 'tau'.\n"),
8244 'D/H' : Item(),
8245 'D/H/chi' : Item("This is the file 'chi'.\n"),
8246 'D/H/omega': Item("This is the file 'omega'.\n"),
8247 'D/H/psi' : Item("This is the file 'psi'.\n"),
8249 expected_skip = wc.State(short_A_COPY, {
8250 'B/E/alpha' : Item(),
8252 svntest.actions.run_and_verify_merge(short_A_COPY, None, None,
8253 A_url,
8254 expected_output,
8255 expected_disk,
8256 expected_status,
8257 expected_skip,
8258 None, None, None, None, None,
8259 1, 1)
8261 os.chdir(saved_cwd)
8263 # Test for issue 2818: Provide a 'merge' API which allows for merging of
8264 # arbitrary revision ranges (e.g. '-c 3,5,7')
8265 def cherry_picking(sbox):
8266 "command line supports cherry picked merge ranges"
8268 sbox.build()
8269 wc_dir = sbox.wc_dir
8270 wc_disk, wc_status = set_up_branch(sbox)
8272 # Some paths we'll care about
8273 H_path = os.path.join(wc_dir, "A", "D", "H")
8274 G_path = os.path.join(wc_dir, "A", "D", "G")
8275 A_COPY_path = os.path.join(wc_dir, "A_COPY")
8276 D_COPY_path = os.path.join(wc_dir, "A_COPY", "D")
8277 G_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "G")
8278 H_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "H")
8279 rho_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "G", "rho")
8280 omega_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "H", "omega")
8281 psi_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "H", "psi")
8283 # Update working copy
8284 expected_output = svntest.wc.State(wc_dir, {})
8285 wc_status.tweak(wc_rev='6')
8286 svntest.actions.run_and_verify_update(wc_dir, expected_output,
8287 wc_disk, wc_status,
8288 None, None, None, None, None, True)
8290 # Make some prop changes to some dirs.
8291 svntest.actions.run_and_verify_svn(None,
8292 ["property 'prop:name' set on '" +
8293 G_path + "'\n"], [], 'ps',
8294 'prop:name', 'propval', G_path)
8295 expected_output = svntest.wc.State(wc_dir, {'A/D/G': Item(verb='Sending'),})
8296 wc_status.tweak('A/D/G', wc_rev=7)
8297 wc_disk.tweak('A/D/G', props={'prop:name' : 'propval'})
8299 svntest.actions.run_and_verify_commit(wc_dir, expected_output, wc_status,
8300 None, wc_dir)
8301 svntest.actions.run_and_verify_svn(None,
8302 ["property 'prop:name' set on '" +
8303 H_path + "'\n"], [], 'ps',
8304 'prop:name', 'propval', H_path)
8305 expected_output = svntest.wc.State(wc_dir, {'A/D/H': Item(verb='Sending'),})
8306 wc_status.tweak('A/D/H', wc_rev=8)
8307 wc_disk.tweak('A/D/H', props={'prop:name' : 'propval'})
8308 svntest.actions.run_and_verify_commit(wc_dir, expected_output, wc_status,
8309 None, wc_dir)
8311 # Do multiple additive merges to a file"
8312 # Merge -r2:4 -c6 into A_COPY/D/G/rho.
8313 short_rho_COPY_path = shorten_path_kludge(rho_COPY_path)
8314 expected_skip = wc.State(short_rho_COPY_path, { })
8315 saved_cwd = os.getcwd()
8316 os.chdir(svntest.main.work_dir)
8317 # run_and_verify_merge doesn't support merging to a file WCPATH
8318 # so use run_and_verify_svn.
8319 svntest.actions.run_and_verify_svn(None,
8320 expected_merge_output(
8321 [[3,4],[6]],
8322 'U ' + short_rho_COPY_path + '\n'),
8323 [], 'merge', '-r2:4', '-c6',
8324 sbox.repo_url + '/A/D/G/rho',
8325 short_rho_COPY_path)
8326 os.chdir(saved_cwd)
8328 # Check rho's status and props.
8329 expected_status = wc.State(rho_COPY_path,
8330 {'' : Item(status='MM', wc_rev=6)})
8331 svntest.actions.run_and_verify_status(rho_COPY_path, expected_status)
8332 svntest.actions.run_and_verify_svn(None, ["/A/D/G/rho:3-4,6\n"], [],
8333 'propget', SVN_PROP_MERGEINFO,
8334 rho_COPY_path)
8336 #Do multiple additive merges to a directory:
8337 # Merge -c6 -c8 into A_COPY/D/H
8338 short_H_COPY_path = shorten_path_kludge(H_COPY_path)
8339 short_omega_COPY_path = shorten_path_kludge(omega_COPY_path)
8340 expected_output = expected_merge_output(
8341 [[6],[8]],
8342 ['U ' + short_omega_COPY_path + '\n',
8343 ' U ' + short_H_COPY_path + '\n'])
8344 os.chdir(svntest.main.work_dir)
8345 svntest.actions.run_and_verify_svn(None, expected_output,
8346 [], 'merge', '-c6', '-c8',
8347 sbox.repo_url + '/A/D/H',
8348 short_H_COPY_path)
8349 os.chdir(saved_cwd)
8351 # Check A_COPY/D/H's status and props.
8352 expected_status = wc.State(H_COPY_path,
8353 {'' : Item(status=' M', wc_rev=6),
8354 'psi' : Item(status=' ', wc_rev=6),
8355 'chi' : Item(status=' ', wc_rev=6),
8356 'omega': Item(status='M ', wc_rev=6),})
8357 svntest.actions.run_and_verify_status(H_COPY_path, expected_status)
8358 svntest.actions.run_and_verify_svn(None,
8359 [H_COPY_path + " - /A/D/H:6,8\n"],
8360 [], 'propget', '-R', SVN_PROP_MERGEINFO,
8361 H_COPY_path)
8363 # Do multiple reverse merges to a directory:
8364 # Merge -c-6 -c-3 into A_COPY
8365 short_A_COPY_path = shorten_path_kludge(A_COPY_path)
8366 short_omega_COPY_path = shorten_path_kludge(omega_COPY_path)
8367 expected_output = expected_merge_output(
8368 [[-3],[-6]], 'G ' + short_omega_COPY_path + '\n')
8369 os.chdir(svntest.main.work_dir)
8370 svntest.actions.run_and_verify_svn(None, expected_output,
8371 [], 'merge', '-c-3', '-c-6',
8372 sbox.repo_url + '/A',
8373 short_A_COPY_path)
8374 os.chdir(saved_cwd)
8375 expected_status = wc.State(A_COPY_path,
8376 {'' : Item(status=' ', wc_rev=6),
8377 'B' : Item(status=' ', wc_rev=6),
8378 'B/lambda' : Item(status=' ', wc_rev=6),
8379 'B/E' : Item(status=' ', wc_rev=6),
8380 'B/E/alpha' : Item(status=' ', wc_rev=6),
8381 'B/E/beta' : Item(status=' ', wc_rev=6),
8382 'B/F' : Item(status=' ', wc_rev=6),
8383 'mu' : Item(status=' ', wc_rev=6),
8384 'C' : Item(status=' ', wc_rev=6),
8385 'D' : Item(status=' ', wc_rev=6),
8386 'D/gamma' : Item(status=' ', wc_rev=6),
8387 'D/G' : Item(status=' ', wc_rev=6),
8388 'D/G/pi' : Item(status=' ', wc_rev=6),
8389 'D/G/rho' : Item(status='MM', wc_rev=6),
8390 'D/G/tau' : Item(status=' ', wc_rev=6),
8391 'D/H' : Item(status=' M', wc_rev=6),
8392 'D/H/chi' : Item(status=' ', wc_rev=6),
8393 'D/H/psi' : Item(status=' ', wc_rev=6),
8394 'D/H/omega' : Item(status=' ', wc_rev=6),})
8395 svntest.actions.run_and_verify_status(A_COPY_path, expected_status)
8396 expected_out = H_COPY_path + " - /A/D/H:8\n|" + \
8397 rho_COPY_path + " - /A/D/G/rho:4\n"
8398 # Construct proper regex for '\' infested Windows paths.
8399 if sys.platform == 'win32':
8400 expected_out = expected_out.replace("\\", "\\\\")
8401 svntest.actions.run_and_verify_svn(None, expected_out, [],
8402 'propget', '-R', SVN_PROP_MERGEINFO,
8403 A_COPY_path)
8405 # Do both additive and reverse merges to a directory:
8406 # Merge -r2:3 -c-4 -r4:7 to A_COPY/D
8407 short_D_COPY_path = shorten_path_kludge(D_COPY_path)
8408 short_psi_COPY_path = shorten_path_kludge(psi_COPY_path)
8409 short_G_COPY_path = shorten_path_kludge(G_COPY_path)
8410 expected_output = expected_merge_output(
8411 [[3],[-4], [5,7]],
8412 [' U ' + short_G_COPY_path + '\n',
8413 'U ' + short_omega_COPY_path + '\n',
8414 'U ' + short_psi_COPY_path + '\n',
8415 'G ' + short_rho_COPY_path + '\n'])
8417 os.chdir(svntest.main.work_dir)
8418 svntest.actions.run_and_verify_svn(None, expected_output, [], 'merge',
8419 '-r2:3', '-c-4', '-r4:7',
8420 sbox.repo_url + '/A/D',
8421 short_D_COPY_path)
8422 os.chdir(saved_cwd)
8423 expected_status = wc.State(D_COPY_path,
8424 {'' : Item(status=' M', wc_rev=6),
8425 'gamma' : Item(status=' ', wc_rev=6),
8426 'G' : Item(status=' M', wc_rev=6),
8427 'G/pi' : Item(status=' ', wc_rev=6),
8428 'G/rho' : Item(status=' ', wc_rev=6),
8429 'G/tau' : Item(status=' ', wc_rev=6),
8430 'H' : Item(status=' M', wc_rev=6),
8431 'H/chi' : Item(status=' ', wc_rev=6),
8432 'H/psi' : Item(status='M ', wc_rev=6),
8433 'H/omega' : Item(status='M ', wc_rev=6),})
8434 svntest.actions.run_and_verify_status(D_COPY_path, expected_status)
8435 expected_out = D_COPY_path + " - /A/D:3,5-7\n|" + \
8436 H_COPY_path + " - /A/D/H:3,5-8\n"
8437 # Construct proper regex for '\' infested Windows paths.
8438 if sys.platform == 'win32':
8439 expected_out = expected_out.replace("\\", "\\\\")
8440 svntest.actions.run_and_verify_svn(None, expected_out, [],
8441 'propget', '-R', SVN_PROP_MERGEINFO,
8442 D_COPY_path)
8444 def propchange_of_subdir_raises_conflict(sbox):
8445 "merge of propchange on subdir raises conflict"
8447 ## See http://subversion.tigris.org/issues/show_bug.cgi?id=2969. ##
8449 # Create a WC with a single branch
8450 sbox.build()
8451 wc_dir = sbox.wc_dir
8452 wc_disk, wc_status = set_up_branch(sbox, True, 1)
8454 # Some paths we'll care about
8455 B_url = sbox.repo_url + '/A/B'
8456 E_path = os.path.join(wc_dir, 'A', 'B', 'E')
8457 lambda_path = os.path.join(wc_dir, 'A', 'B', 'lambda')
8458 A_COPY_B_path = os.path.join(wc_dir, 'A_COPY', 'B')
8459 A_COPY_B_E_path = os.path.join(wc_dir, 'A_COPY', 'B', 'E')
8460 A_COPY_lambda_path = os.path.join(wc_dir, 'A_COPY', 'B', 'E', 'lambda')
8462 # Set a property on A/B/E and Make a modification to A/B/lambda
8463 svntest.main.run_svn(None, 'propset', 'x', 'x', E_path)
8465 svntest.main.file_write(lambda_path, "This is the file 'lambda' modified.\n")
8466 expected_output = wc.State(wc_dir, {
8467 'A/B/lambda' : Item(verb='Sending'),
8468 'A/B/E' : Item(verb='Sending'),
8470 wc_status.add({
8471 'A/B/lambda' : Item(status=' ', wc_rev=3),
8472 'A/B/E' : Item(status=' ', wc_rev=3),
8474 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
8475 wc_status, None, wc_dir)
8477 short_A_COPY_B = shorten_path_kludge(A_COPY_B_path)
8478 saved_cwd = os.getcwd()
8479 os.chdir(svntest.main.work_dir)
8481 # Merge /A/B to /A_COPY/B ie., r1 to r3 with depth files
8482 expected_output = wc.State(short_A_COPY_B, {
8483 'lambda' : Item(status='U '),
8485 expected_disk = wc.State('', {
8486 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B:2-3*'}),
8487 'lambda' : Item(contents="This is the file 'lambda' modified.\n",
8488 props={SVN_PROP_MERGEINFO : '/A/B/lambda:2-3'}),
8489 'F' : Item(),
8490 'E' : Item(),
8491 'E/alpha' : Item(contents="This is the file 'alpha'.\n"),
8492 'E/beta' : Item(contents="This is the file 'beta'.\n"),
8494 expected_status = wc.State(short_A_COPY_B, {
8495 '' : Item(status=' M', wc_rev=2),
8496 'lambda' : Item(status='MM', wc_rev=2),
8497 'F' : Item(status=' ', wc_rev=2),
8498 'E' : Item(status=' ', wc_rev=2),
8499 'E/alpha' : Item(status=' ', wc_rev=2),
8500 'E/beta' : Item(status=' ', wc_rev=2),
8502 expected_skip = wc.State(short_A_COPY_B, {})
8504 svntest.actions.run_and_verify_merge(short_A_COPY_B, None, None,
8505 B_url,
8506 expected_output,
8507 expected_disk,
8508 expected_status,
8509 expected_skip,
8510 None, None, None, None, None,
8511 1, 1, '--depth', 'files')
8513 # Merge /A/B to /A_COPY/B ie., r1 to r3 with infinite depth
8514 expected_output = wc.State(short_A_COPY_B, {
8515 'E' : Item(status=' U'),
8517 expected_disk = wc.State('', {
8518 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B:2-3'}),
8519 'lambda' : Item(contents="This is the file 'lambda' modified.\n"),
8520 'F' : Item(),
8521 'E' : Item(props={'x': 'x'}),
8522 'E/alpha' : Item(contents="This is the file 'alpha'.\n"),
8523 'E/beta' : Item(contents="This is the file 'beta'.\n"),
8525 expected_status = wc.State(short_A_COPY_B, {
8526 '' : Item(status=' M', wc_rev=2),
8527 'lambda' : Item(status='M ', wc_rev=2),
8528 'F' : Item(status=' ', wc_rev=2),
8529 'E' : Item(status=' M', wc_rev=2),
8530 'E/alpha' : Item(status=' ', wc_rev=2),
8531 'E/beta' : Item(status=' ', wc_rev=2),
8534 svntest.actions.run_and_verify_merge(short_A_COPY_B, None, None,
8535 B_url,
8536 expected_output,
8537 expected_disk,
8538 expected_status,
8539 expected_skip,
8540 None, None, None, None, None,
8541 1, 1)
8543 os.chdir(saved_cwd)
8545 # Test for issue #2971: Reverse merge of prop add segfaults if
8546 # merging to parent of first merge
8547 def reverse_merge_prop_add_on_child(sbox):
8548 "reverse merge of prop add on child"
8550 sbox.build()
8551 wc_dir = sbox.wc_dir
8552 wc_disk, wc_status = set_up_branch(sbox, True, 1)
8554 # Some paths we'll care about
8555 G_path = os.path.join(wc_dir, "A", "D", "G")
8556 D_COPY_path = os.path.join(wc_dir, "A_COPY", "D")
8557 G_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "G")
8559 # Make some prop changes to some dirs.
8560 svntest.actions.run_and_verify_svn(None,
8561 ["property 'prop:name' set on '" +
8562 G_path + "'\n"], [], 'ps',
8563 'prop:name', 'propval', G_path)
8564 expected_output = svntest.wc.State(wc_dir, {'A/D/G': Item(verb='Sending'),})
8565 wc_status.tweak('A/D/G', wc_rev=3)
8566 wc_disk.tweak('A/D/G', props={'prop:name' : 'propval'})
8568 svntest.actions.run_and_verify_commit(wc_dir, expected_output, wc_status,
8569 None, wc_dir)
8571 # Merge -c3's prop add to A_COPY/D/G
8572 saved_cwd = os.getcwd()
8573 short_G_COPY_path = shorten_path_kludge(G_COPY_path)
8574 expected_output = wc.State(short_G_COPY_path, {
8575 '' : Item(status=' U')
8577 expected_status = wc.State(short_G_COPY_path, {
8578 '' : Item(status=' M', wc_rev=2),
8579 'pi' : Item(status=' ', wc_rev=2),
8580 'rho' : Item(status=' ', wc_rev=2),
8581 'tau' : Item(status=' ', wc_rev=2),
8583 expected_disk = wc.State('', {
8584 '' : Item(props={SVN_PROP_MERGEINFO : '/A/D/G:3',
8585 'prop:name' : 'propval'}),
8586 'pi' : Item("This is the file 'pi'.\n"),
8587 'rho' : Item("This is the file 'rho'.\n"),
8588 'tau' : Item("This is the file 'tau'.\n"),
8590 expected_skip = wc.State(short_G_COPY_path, { })
8591 os.chdir(svntest.main.work_dir)
8592 svntest.actions.run_and_verify_merge(short_G_COPY_path, '2', '3',
8593 sbox.repo_url + \
8594 '/A/D/G',
8595 expected_output,
8596 expected_disk,
8597 expected_status,
8598 expected_skip,
8599 None, None, None, None,
8600 None, 1)
8601 os.chdir(saved_cwd)
8603 # Now merge -c-3 but target the previous target's parent instead.
8604 short_D_COPY_path = shorten_path_kludge(D_COPY_path)
8605 expected_output = wc.State(short_D_COPY_path, {
8606 'G' : Item(status=' G'),
8608 expected_status = wc.State(short_D_COPY_path, {
8609 '' : Item(status=' ', wc_rev=2),
8610 'G' : Item(status=' ', wc_rev=2),
8611 'G/pi' : Item(status=' ', wc_rev=2),
8612 'G/rho' : Item(status=' ', wc_rev=2),
8613 'G/tau' : Item(status=' ', wc_rev=2),
8614 'H' : Item(status=' ', wc_rev=2),
8615 'H/chi' : Item(status=' ', wc_rev=2),
8616 'H/psi' : Item(status=' ', wc_rev=2),
8617 'H/omega' : Item(status=' ', wc_rev=2),
8618 'gamma' : Item(status=' ', wc_rev=2),
8620 expected_disk = wc.State('', {
8621 'G' : Item(),
8622 'G/pi' : Item("This is the file 'pi'.\n"),
8623 'G/rho' : Item("This is the file 'rho'.\n"),
8624 'G/tau' : Item("This is the file 'tau'.\n"),
8625 'H' : Item(),
8626 'H/chi' : Item("This is the file 'chi'.\n"),
8627 'H/psi' : Item("This is the file 'psi'.\n"),
8628 'H/omega' : Item("This is the file 'omega'.\n"),
8629 'gamma' : Item("This is the file 'gamma'.\n")
8631 expected_skip = wc.State(short_D_COPY_path, { })
8632 os.chdir(svntest.main.work_dir)
8633 svntest.actions.run_and_verify_merge(short_D_COPY_path, '3', '2',
8634 sbox.repo_url + \
8635 '/A/D',
8636 expected_output,
8637 expected_disk,
8638 expected_status,
8639 expected_skip,
8640 None, None, None, None,
8641 None, 1)
8642 os.chdir(saved_cwd)
8644 def merge_target_with_non_inheritable_mergeinfo(sbox):
8645 "merge target with non inheritable mergeinfo"
8647 ## See http://subversion.tigris.org/issues/show_bug.cgi?id=2970. ##
8649 # Create a WC with a single branch
8650 sbox.build()
8651 wc_dir = sbox.wc_dir
8652 wc_disk, wc_status = set_up_branch(sbox, True, 1)
8654 # Some paths we'll care about
8655 B_url = sbox.repo_url + '/A/B'
8656 lambda_path = os.path.join(wc_dir, 'A', 'B', 'lambda')
8657 newfile_path = os.path.join(wc_dir, 'A', 'B', 'E', 'newfile')
8658 A_COPY_B_path = os.path.join(wc_dir, 'A_COPY', 'B')
8660 # Make a modifications to A/B/lambda and add A/B/E/newfile
8661 svntest.main.file_write(lambda_path, "This is the file 'lambda' modified.\n")
8662 svntest.main.file_write(newfile_path, "This is the file 'newfile'.\n")
8663 svntest.actions.run_and_verify_svn(None, None, [], 'add', newfile_path)
8664 expected_output = wc.State(wc_dir, {
8665 'A/B/lambda' : Item(verb='Sending'),
8666 'A/B/E/newfile' : Item(verb='Adding'),
8668 wc_status.add({
8669 'A/B/lambda' : Item(status=' ', wc_rev=3),
8670 'A/B/E/newfile' : Item(status=' ', wc_rev=3),
8672 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
8673 wc_status, None, wc_dir)
8675 short_A_COPY_B = shorten_path_kludge(A_COPY_B_path)
8676 saved_cwd = os.getcwd()
8677 os.chdir(svntest.main.work_dir)
8679 # Merge /A/B to /A_COPY/B ie., r1 to r3 with depth immediates
8680 expected_output = wc.State(short_A_COPY_B, {
8681 'lambda' : Item(status='U '),
8683 expected_disk = wc.State('', {
8684 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B:1-3'}),
8685 'lambda' : Item(contents="This is the file 'lambda' modified.\n"),
8686 'F' : Item(props={SVN_PROP_MERGEINFO : '/A/B/F:1,2-3*'}),
8687 'E' : Item(props={SVN_PROP_MERGEINFO : '/A/B/E:1,2-3*'}),
8688 'E/alpha' : Item(contents="This is the file 'alpha'.\n"),
8689 'E/beta' : Item(contents="This is the file 'beta'.\n"),
8691 expected_status = wc.State(short_A_COPY_B, {
8692 '' : Item(status=' M', wc_rev=2),
8693 'lambda' : Item(status='M ', wc_rev=2),
8694 'F' : Item(status=' M', wc_rev=2),
8695 'E' : Item(status=' M', wc_rev=2),
8696 'E/alpha' : Item(status=' ', wc_rev=2),
8697 'E/beta' : Item(status=' ', wc_rev=2),
8699 expected_skip = wc.State(short_A_COPY_B, {})
8701 svntest.actions.run_and_verify_merge(short_A_COPY_B, None, None,
8702 B_url,
8703 expected_output,
8704 expected_disk,
8705 expected_status,
8706 expected_skip,
8707 None, None, None, None, None,
8708 1, 1, '--depth', 'immediates')
8710 # Merge /A/B to /A_COPY/B ie., r1 to r3 with infinite depth
8711 expected_output = wc.State(short_A_COPY_B, {
8712 'E/newfile' : Item(status='A '),
8714 expected_disk = wc.State('', {
8715 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B:1-3'}),
8716 'lambda' : Item(contents="This is the file 'lambda' modified.\n"),
8717 'F' : Item(),
8718 'E' : Item(),
8719 'E/alpha' : Item(contents="This is the file 'alpha'.\n"),
8720 'E/beta' : Item(contents="This is the file 'beta'.\n"),
8721 'E/newfile' : Item(contents="This is the file 'newfile'.\n"),
8723 expected_status = wc.State(short_A_COPY_B, {
8724 '' : Item(status=' M', wc_rev=2),
8725 'lambda' : Item(status='M ', wc_rev=2),
8726 'F' : Item(status=' ', wc_rev=2),
8727 'E' : Item(status=' ', wc_rev=2),
8728 'E/alpha' : Item(status=' ', wc_rev=2),
8729 'E/beta' : Item(status=' ', wc_rev=2),
8730 'E/newfile' : Item(status='A ', wc_rev=2),
8733 svntest.actions.run_and_verify_merge(short_A_COPY_B, None, None,
8734 B_url,
8735 expected_output,
8736 expected_disk,
8737 expected_status,
8738 expected_skip,
8739 None, None, None, None, None,
8740 1, 1)
8742 os.chdir(saved_cwd)
8744 def self_reverse_merge(sbox):
8745 "revert a commit on a target"
8747 sbox.build()
8748 wc_dir = sbox.wc_dir
8750 # Make changes to the working copy
8751 mu_path = os.path.join(wc_dir, 'A', 'mu')
8752 svntest.main.file_append(mu_path, 'appended mu text')
8754 # Created expected output tree for 'svn ci'
8755 expected_output = wc.State(wc_dir, {
8756 'A/mu' : Item(verb='Sending'),
8759 # Create expected status tree; all local revisions should be at 1,
8760 # but mu should be at revision 2.
8761 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
8762 expected_status.tweak('A/mu', wc_rev=2)
8764 svntest.actions.run_and_verify_commit(wc_dir,
8765 expected_output,
8766 expected_status,
8767 None,
8768 wc_dir)
8770 # update to HEAD so that the to-be-undone revision is found in the
8771 # implicit mergeinfo (the natural history) of the target.
8772 svntest.actions.run_and_verify_svn(None, None, [], 'update', wc_dir)
8774 expected_output = wc.State(wc_dir, {
8775 'A/mu' : Item(status='U ')
8777 expected_skip = wc.State(wc_dir, { })
8778 expected_disk = svntest.main.greek_state.copy()
8779 expected_status = svntest.actions.get_virginal_state(wc_dir, 2)
8780 expected_status.tweak('A/mu', status='M ')
8781 svntest.actions.run_and_verify_merge(wc_dir, '2', '1', sbox.repo_url,
8782 expected_output, expected_disk,
8783 expected_status, expected_skip,
8784 None, None, None, None, None, 1, 1)
8785 svntest.actions.run_and_verify_svn(None, None, [], 'revert', '-R', wc_dir)
8787 # record dummy self mergeinfo to test the fact that self-reversal should work
8788 # irrespective of mergeinfo.
8789 svntest.actions.run_and_verify_svn(None, None, [], 'merge', '-c', '1',
8790 '--record-only', sbox.repo_url, wc_dir)
8792 # Bad svntest.main.greek_state does not have '', so adding it explicitly.
8793 expected_disk.add({'' : Item(props={SVN_PROP_MERGEINFO : '/:1'})})
8794 expected_status = svntest.actions.get_virginal_state(wc_dir, 2)
8795 expected_status.tweak('', status = ' M')
8796 expected_status.tweak('A/mu', status = 'M ')
8797 svntest.actions.run_and_verify_merge(wc_dir, '2', '1', sbox.repo_url,
8798 expected_output, expected_disk,
8799 expected_status, expected_skip,
8800 None, None, None, None, None, 1, 1)
8802 def ignore_ancestry_and_mergeinfo(sbox):
8803 "--ignore-ancestry also ignores mergeinfo"
8805 # Create a WC with a single branch
8806 sbox.build()
8807 wc_dir = sbox.wc_dir
8808 wc_disk, wc_status = set_up_branch(sbox, True, 1)
8810 # Some paths we'll care about
8811 A_B_url = sbox.repo_url + '/A/B'
8812 A_COPY_B_path = os.path.join(wc_dir, 'A_COPY', 'B')
8813 lambda_path = os.path.join(wc_dir, 'A', 'B', 'lambda')
8814 A_COPY_lambda_path = os.path.join(wc_dir, 'A_COPY', 'B', 'lambda')
8816 # Make modifications to A/B/lambda
8817 svntest.main.file_write(lambda_path, "This is the file 'lambda' modified.\n")
8818 expected_output = wc.State(wc_dir, {
8819 'A/B/lambda' : Item(verb='Sending'),
8821 wc_status.add({
8822 'A/B/lambda' : Item(status=' ', wc_rev=3),
8824 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
8825 wc_status, None, wc_dir)
8827 svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
8829 short_A_COPY_B = shorten_path_kludge(A_COPY_B_path)
8830 short_A_COPY_lambda = shorten_path_kludge(A_COPY_lambda_path)
8831 saved_cwd = os.getcwd()
8832 os.chdir(svntest.main.work_dir)
8834 # Merge /A/B to /A_COPY/B ie., r1 to r3 with depth immediates
8835 expected_output = wc.State(short_A_COPY_B, {
8836 'lambda' : Item(status='U '),
8838 expected_disk = wc.State('', {
8839 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B:2-3'}),
8840 'lambda' : Item(contents="This is the file 'lambda' modified.\n"),
8841 'F' : Item(props={}),
8842 'E' : Item(props={}),
8843 'E/alpha' : Item(contents="This is the file 'alpha'.\n"),
8844 'E/beta' : Item(contents="This is the file 'beta'.\n"),
8846 expected_status = wc.State(short_A_COPY_B, {
8847 '' : Item(status=' M', wc_rev=3),
8848 'lambda' : Item(status='M ', wc_rev=3),
8849 'F' : Item(status=' ', wc_rev=3),
8850 'E' : Item(status=' ', wc_rev=3),
8851 'E/alpha' : Item(status=' ', wc_rev=3),
8852 'E/beta' : Item(status=' ', wc_rev=3),
8854 expected_skip = wc.State(short_A_COPY_B, {})
8856 svntest.actions.run_and_verify_merge(short_A_COPY_B, 1, 3,
8857 A_B_url,
8858 expected_output,
8859 expected_disk,
8860 expected_status,
8861 expected_skip,
8862 None, None, None, None, None, 1, 1)
8864 # Now, revert lambda and repeat the merge. Nothing should happen.
8865 svntest.actions.run_and_verify_svn(None, None, [], 'revert', '-R',
8866 short_A_COPY_lambda)
8867 expected_output.remove('lambda')
8868 expected_disk.tweak('lambda', contents="This is the file 'lambda'.\n")
8869 expected_status.tweak('lambda', status=' ')
8870 svntest.actions.run_and_verify_merge(short_A_COPY_B, 1, 3,
8871 A_B_url,
8872 expected_output,
8873 expected_disk,
8874 expected_status,
8875 expected_skip,
8876 None, None, None, None, None, 1, 1)
8878 # Now, try the merge again with --ignore-ancestry. We should get
8879 # lambda re-modified. */
8880 expected_output = wc.State(short_A_COPY_B, {
8881 'lambda' : Item(status='U '),
8883 expected_disk.tweak('lambda',
8884 contents="This is the file 'lambda' modified.\n")
8885 expected_status.tweak('lambda', status='M ')
8886 svntest.actions.run_and_verify_merge(short_A_COPY_B, 1, 3,
8887 A_B_url,
8888 expected_output,
8889 expected_disk,
8890 expected_status,
8891 expected_skip,
8892 None, None, None, None, None, 1, 1,
8893 '--ignore-ancestry')
8895 os.chdir(saved_cwd)
8897 def merge_from_renamed_branch_fails_while_avoiding_repeat_merge(sbox):
8898 "merge from renamed branch"
8899 #Copy A/C to A/COPY_C results in r2.
8900 #Rename A/COPY_C to A/RENAMED_C results in r3.
8901 #Add A/RENAMED_C/file1 and commit, results in r4.
8902 #Change A/RENAMED_C/file1 and commit, results in r5.
8903 #Merge r4 from A/RENAMED_C to A/C
8904 #Merge r2:5 from A/RENAMED_C to A/C <-- This fails tracked via #3032.
8906 ## See http://subversion.tigris.org/issues/show_bug.cgi?id=3032. ##
8908 # Create a WC with a single branch
8909 sbox.build()
8910 wc_dir = sbox.wc_dir
8911 # Some paths we'll care about
8912 A_C_url = sbox.repo_url + '/A/C'
8913 A_COPY_C_url = sbox.repo_url + '/A/COPY_C'
8914 A_RENAMED_C_url = sbox.repo_url + '/A/RENAMED_C'
8915 A_C_path = os.path.join(wc_dir, 'A', 'C')
8916 A_RENAMED_C_path = os.path.join(wc_dir, 'A', 'RENAMED_C')
8917 A_RENAMED_C_file1_path = os.path.join(wc_dir, 'A', 'RENAMED_C', 'file1')
8919 svntest.main.run_svn(None, 'cp', A_C_url, A_COPY_C_url, '-m', 'copy...')
8920 svntest.main.run_svn(None, 'mv', A_COPY_C_url, A_RENAMED_C_url, '-m',
8921 'rename...')
8922 svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
8924 svntest.main.file_write(A_RENAMED_C_file1_path, "This is the file1.\n")
8925 svntest.main.run_svn(None, 'add', A_RENAMED_C_file1_path)
8926 expected_output = wc.State(A_RENAMED_C_path, {
8927 'file1' : Item(verb='Adding'),
8929 expected_status = wc.State(A_RENAMED_C_path, {
8930 '' : Item(status=' ', wc_rev=3),
8931 'file1' : Item(status=' ', wc_rev=4),
8933 svntest.actions.run_and_verify_commit(A_RENAMED_C_path, expected_output,
8934 expected_status, None,
8935 A_RENAMED_C_path)
8936 svntest.main.file_write(A_RENAMED_C_file1_path,
8937 "This is the file1 modified.\n")
8938 expected_output = wc.State(A_RENAMED_C_path, {
8939 'file1' : Item(verb='Sending'),
8941 expected_status.tweak('file1', wc_rev=5)
8942 svntest.actions.run_and_verify_commit(A_RENAMED_C_path, expected_output,
8943 expected_status, None,
8944 A_RENAMED_C_path)
8946 short_A_C = shorten_path_kludge(A_C_path)
8947 expected_skip = wc.State(short_A_C, {})
8948 expected_output = wc.State(short_A_C, {
8949 'file1' : Item(status='A '),
8951 expected_disk = wc.State('', {
8952 '' : Item(props={SVN_PROP_MERGEINFO : '/A/RENAMED_C:4'}),
8953 'file1' : Item("This is the file1.\n"),
8955 expected_status = wc.State(short_A_C, {
8956 '' : Item(status=' M', wc_rev=3),
8957 'file1' : Item(status='A ', wc_rev='-', copied='+'),
8959 saved_cwd = os.getcwd()
8960 os.chdir(svntest.main.work_dir)
8961 svntest.actions.run_and_verify_merge(short_A_C, 3, 4,
8962 A_RENAMED_C_url,
8963 expected_output,
8964 expected_disk,
8965 expected_status,
8966 expected_skip,
8967 None, None, None, None, None, 1, 1)
8969 expected_output = wc.State(short_A_C, {
8970 'file1' : Item(status='U '),
8972 expected_disk = wc.State('', {
8973 '' : Item(props={SVN_PROP_MERGEINFO : '/A/RENAMED_C:3-5'}),
8974 'file1' : Item("This is the file1 modified.\n"),
8976 expected_status = wc.State(short_A_C, {
8977 '' : Item(status=' M', wc_rev=3),
8978 'file1' : Item(status='A ', wc_rev='-', copied='+'),
8980 svntest.actions.run_and_verify_merge(short_A_C, 2, 5,
8981 A_RENAMED_C_url,
8982 expected_output,
8983 expected_disk,
8984 expected_status,
8985 expected_skip,
8986 None, None, None, None, None, 1, 1)
8987 os.chdir(saved_cwd)
8989 # Test for part of issue #2877: 'do subtree merge only if subtree has
8990 # explicit mergeinfo set and exists in the merge source'
8991 def merge_source_normalization_and_subtree_merges(sbox):
8992 "normalized mergeinfo is recorded on subtrees"
8994 sbox.build()
8995 wc_dir = sbox.wc_dir
8997 # Some paths we'll care about
8998 D_COPY_path = os.path.join(wc_dir, "A_COPY", "D")
8999 G_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "G")
9001 # Use our helper to copy 'A' to 'A_COPY' then make some changes under 'A'
9002 wc_disk, wc_status = set_up_branch(sbox)
9004 # r7 - Move A to A_MOVED
9005 svntest.actions.run_and_verify_svn(None, ['\n', 'Committed revision 7.\n'],
9006 [], 'mv', '-m', 'mv A to A_MOVED',
9007 sbox.repo_url + '/A',
9008 sbox.repo_url + '/A_MOVED')
9009 wc_status.add({
9010 'A_MOVED/B' : Item(),
9011 'A_MOVED/B/lambda' : Item(),
9012 'A_MOVED/B/E' : Item(),
9013 'A_MOVED/B/E/alpha' : Item(),
9014 'A_MOVED/B/E/beta' : Item(),
9015 'A_MOVED/B/F' : Item(),
9016 'A_MOVED/mu' : Item(),
9017 'A_MOVED/C' : Item(),
9018 'A_MOVED/D' : Item(),
9019 'A_MOVED/D/gamma' : Item(),
9020 'A_MOVED/D/G' : Item(),
9021 'A_MOVED/D/G/pi' : Item(),
9022 'A_MOVED/D/G/rho' : Item(),
9023 'A_MOVED/D/G/tau' : Item(),
9024 'A_MOVED/D/H' : Item(),
9025 'A_MOVED/D/H/chi' : Item(),
9026 'A_MOVED/D/H/omega' : Item(),
9027 'A_MOVED/D/H/psi' : Item(),
9028 'A_MOVED' : Item()})
9029 wc_status.remove('A', 'A/B', 'A/B/lambda', 'A/B/E', 'A/B/E/alpha',
9030 'A/B/E/beta', 'A/B/F', 'A/mu', 'A/C', 'A/D',
9031 'A/D/gamma', 'A/D/G', 'A/D/G/pi', 'A/D/G/rho',
9032 'A/D/G/tau' , 'A/D/H', 'A/D/H/chi', 'A/D/H/omega',
9033 'A/D/H/psi')
9034 wc_status.tweak(status=' ', wc_rev=7)
9036 # Update the WC
9037 svntest.actions.run_and_verify_svn(None, None, [],
9038 'update', wc_dir)
9040 # r8 - Make a text mod to 'A_MOVED/D/G/tau'
9041 svntest.main.file_write(os.path.join(wc_dir, "A_MOVED", "D", "G", "tau"),
9042 "New content")
9043 expected_output = wc.State(wc_dir,
9044 {'A_MOVED/D/G/tau' : Item(verb='Sending')})
9045 wc_status.tweak('A_MOVED/D/G/tau', status=' ', wc_rev=8)
9046 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
9047 wc_status, None, wc_dir)
9049 # Merge -c4 URL/A_MOVED/D/G A_COPY/D/G.
9051 # Search for the comment entitled "The Merge Kluge" elsewhere in
9052 # this file, to understand why we shorten and chdir() below.
9054 # A_MOVED/D/G doesn't exist at r3:4, it's still A/D/G,
9055 # so the merge source normalization logic should set
9056 # mergeinfo of '/A/D/G:4' on A_COPY/D/G, *not* 'A_MOVED/D/G:4',
9057 # see issue #2953.
9058 short_G_COPY_path = shorten_path_kludge(G_COPY_path)
9059 expected_output = wc.State(short_G_COPY_path, {
9060 'rho' : Item(status='U ')
9062 expected_status = wc.State(short_G_COPY_path, {
9063 '' : Item(status=' M', wc_rev=7),
9064 'pi' : Item(status=' ', wc_rev=7),
9065 'rho' : Item(status='M ', wc_rev=7),
9066 'tau' : Item(status=' ', wc_rev=7),
9068 expected_disk = wc.State('', {
9069 '' : Item(props={SVN_PROP_MERGEINFO : '/A/D/G:4'}),
9070 'pi' : Item("This is the file 'pi'.\n"),
9071 'rho' : Item("New content"),
9072 'tau' : Item("This is the file 'tau'.\n"),
9074 expected_skip = wc.State(short_G_COPY_path, { })
9075 saved_cwd = os.getcwd()
9076 os.chdir(svntest.main.work_dir)
9077 svntest.actions.run_and_verify_merge(short_G_COPY_path, '3', '4',
9078 sbox.repo_url + '/A_MOVED/D/G',
9079 expected_output,
9080 expected_disk,
9081 expected_status,
9082 expected_skip,
9083 None, None, None, None,
9084 None, 1)
9085 os.chdir(saved_cwd)
9087 # Merge -c8 URL/A_MOVED/D A_COPY/D.
9089 # The merge target A_COPY/D and the subtree at A_COPY/D/G
9090 # should both have their mergeinfo updated with r8
9091 # from A_MOVED_D, see reopened issue #2877.
9092 short_D_COPY_path = shorten_path_kludge(D_COPY_path)
9093 expected_output = wc.State(short_D_COPY_path, {
9094 'G/tau' : Item(status='U '),
9096 expected_status = wc.State(short_D_COPY_path, {
9097 '' : Item(status=' M', wc_rev=7),
9098 'G' : Item(status=' M', wc_rev=7),
9099 'G/pi' : Item(status=' ', wc_rev=7),
9100 'G/rho' : Item(status='M ', wc_rev=7),
9101 'G/tau' : Item(status='M ', wc_rev=7),
9102 'H' : Item(status=' ', wc_rev=7),
9103 'H/chi' : Item(status=' ', wc_rev=7),
9104 'H/psi' : Item(status=' ', wc_rev=7),
9105 'H/omega' : Item(status=' ', wc_rev=7),
9106 'gamma' : Item(status=' ', wc_rev=7),
9108 expected_disk = wc.State('', {
9109 '' : Item(props={SVN_PROP_MERGEINFO : '/A_MOVED/D:8'}),
9110 'G' : Item(props={SVN_PROP_MERGEINFO :
9111 '/A/D/G:4\n/A_MOVED/D/G:8\n'}),
9112 'G/pi' : Item("This is the file 'pi'.\n"),
9113 'G/rho' : Item("New content"),
9114 'G/tau' : Item("New content"),
9115 'H' : Item(),
9116 'H/chi' : Item("This is the file 'chi'.\n"),
9117 'H/psi' : Item("This is the file 'psi'.\n"),
9118 'H/omega' : Item("This is the file 'omega'.\n"),
9119 'gamma' : Item("This is the file 'gamma'.\n")
9121 expected_skip = wc.State(short_D_COPY_path, { })
9122 os.chdir(svntest.main.work_dir)
9123 svntest.actions.run_and_verify_merge(short_D_COPY_path, '7', '8',
9124 sbox.repo_url + \
9125 '/A_MOVED/D',
9126 expected_output,
9127 expected_disk,
9128 expected_status,
9129 expected_skip,
9130 None, None, None, None,
9131 None, 1)
9132 os.chdir(saved_cwd)
9134 # Test for issue #3067: 'subtrees with intersecting mergeinfo, that don't
9135 # exist at the start of a merge range shouldn't break the merge'
9136 def new_subtrees_should_not_break_merge(sbox):
9137 "subtrees added after start of merge range are ok"
9139 sbox.build()
9140 wc_dir = sbox.wc_dir
9141 wc_disk, wc_status = set_up_branch(sbox)
9143 # Some paths we'll care about
9144 A_COPY_path = os.path.join(wc_dir, "A_COPY")
9145 D_COPY_path = os.path.join(wc_dir, "A_COPY", "D")
9146 nu_path = os.path.join(wc_dir, "A", "D", "H", "nu")
9147 nu_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "H", "nu")
9148 H_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "H")
9150 # Create 'A/D/H/nu', commit it as r7, make a text mod to it in r8.
9151 svntest.main.file_write(nu_path, "This is the file 'nu'.\n")
9152 svntest.actions.run_and_verify_svn(None, None, [], 'add', nu_path)
9153 expected_output = wc.State(wc_dir, {'A/D/H/nu' : Item(verb='Adding')})
9154 wc_status.add({'A/D/H/nu' : Item(status=' ', wc_rev=7)})
9155 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
9156 wc_status, None, wc_dir)
9157 svntest.main.file_write(nu_path, "New content")
9158 expected_output = wc.State(wc_dir, {'A/D/H/nu' : Item(verb='Sending')})
9159 wc_status.tweak('A/D/H/nu', wc_rev=8)
9160 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
9161 wc_status, None, wc_dir)
9163 # Merge r7 to A_COPY/D/H, then, so it has it's own explicit mergeinfo,
9164 # then merge r8 to A_COPY/D/H/nu so it too has explicit mergeinfo.
9165 short_H_COPY_path = shorten_path_kludge(H_COPY_path)
9166 expected_output = wc.State(short_H_COPY_path, {
9167 'nu' : Item(status='A '),
9169 expected_status = wc.State(short_H_COPY_path, {
9170 '' : Item(status=' M', wc_rev=2),
9171 'psi' : Item(status=' ', wc_rev=2),
9172 'omega' : Item(status=' ', wc_rev=2),
9173 'chi' : Item(status=' ', wc_rev=2),
9174 'nu' : Item(status='A ', copied='+', wc_rev='-'),
9176 expected_disk = wc.State('', {
9177 '' : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:7'}),
9178 'psi' : Item("This is the file 'psi'.\n"),
9179 'omega' : Item("This is the file 'omega'.\n"),
9180 'chi' : Item("This is the file 'chi'.\n"),
9181 'nu' : Item("This is the file 'nu'.\n"),
9183 expected_skip = wc.State(short_H_COPY_path, {})
9184 saved_cwd = os.getcwd()
9185 os.chdir(svntest.main.work_dir)
9186 svntest.actions.run_and_verify_merge(short_H_COPY_path, '6', '7',
9187 sbox.repo_url + '/A/D/H',
9188 expected_output, expected_disk,
9189 expected_status, expected_skip,
9190 None, None, None, None, None, 1)
9191 # run_and_verify_merge doesn't support merging to a file WCPATH
9192 # so use run_and_verify_svn.
9193 short_nu_COPY_path = shorten_path_kludge(nu_COPY_path)
9194 svntest.actions.run_and_verify_svn(None,
9195 expected_merge_output([[8]],
9196 'U ' + short_nu_COPY_path + '\n'),
9197 [], 'merge', '-c8',
9198 sbox.repo_url + '/A/D/H/nu',
9199 short_nu_COPY_path)
9200 # Merge -r4:6 to A_COPY, then reverse merge r6 from A_COPY/D.
9201 short_A_COPY_path = shorten_path_kludge(A_COPY_path)
9202 expected_output = wc.State(short_A_COPY_path, {
9203 'B/E/beta' : Item(status='U '),
9204 'D/H/omega': Item(status='U '),
9206 expected_status = wc.State(short_A_COPY_path, {
9207 '' : Item(status=' M', wc_rev=2),
9208 'B' : Item(status=' ', wc_rev=2),
9209 'mu' : Item(status=' ', wc_rev=2),
9210 'B/E' : Item(status=' ', wc_rev=2),
9211 'B/E/alpha' : Item(status=' ', wc_rev=2),
9212 'B/E/beta' : Item(status='M ', wc_rev=2),
9213 'B/lambda' : Item(status=' ', wc_rev=2),
9214 'B/F' : Item(status=' ', wc_rev=2),
9215 'C' : Item(status=' ', wc_rev=2),
9216 'D' : Item(status=' ', wc_rev=2),
9217 'D/G' : Item(status=' ', wc_rev=2),
9218 'D/G/pi' : Item(status=' ', wc_rev=2),
9219 'D/G/rho' : Item(status=' ', wc_rev=2),
9220 'D/G/tau' : Item(status=' ', wc_rev=2),
9221 'D/gamma' : Item(status=' ', wc_rev=2),
9222 'D/H' : Item(status=' M', wc_rev=2),
9223 'D/H/chi' : Item(status=' ', wc_rev=2),
9224 'D/H/psi' : Item(status=' ', wc_rev=2),
9225 'D/H/omega' : Item(status='M ', wc_rev=2),
9226 'D/H/nu' : Item(status='A ', copied='+', wc_rev='-'),
9228 expected_disk = wc.State('', {
9229 '' : Item(props={SVN_PROP_MERGEINFO : '/A:5-6'}),
9230 'B' : Item(),
9231 'mu' : Item("This is the file 'mu'.\n"),
9232 'B/E' : Item(),
9233 'B/E/alpha' : Item("This is the file 'alpha'.\n"),
9234 'B/E/beta' : Item("New content"),
9235 'B/lambda' : Item("This is the file 'lambda'.\n"),
9236 'B/F' : Item(),
9237 'C' : Item(),
9238 'D' : Item(),
9239 'D/G' : Item(),
9240 'D/G/pi' : Item("This is the file 'pi'.\n"),
9241 'D/G/rho' : Item("This is the file 'rho'.\n"),
9242 'D/G/tau' : Item("This is the file 'tau'.\n"),
9243 'D/gamma' : Item("This is the file 'gamma'.\n"),
9244 'D/H' : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:5-7'}),
9245 'D/H/chi' : Item("This is the file 'chi'.\n"),
9246 'D/H/psi' : Item("This is the file 'psi'.\n"),
9247 'D/H/omega' : Item("New content"),
9248 'D/H/nu' : Item("New content",
9249 props={SVN_PROP_MERGEINFO : '/A/D/H/nu:5-8'}),
9251 expected_skip = wc.State(short_A_COPY_path, { })
9252 svntest.actions.run_and_verify_merge(short_A_COPY_path, '4', '6',
9253 sbox.repo_url + \
9254 '/A',
9255 expected_output,
9256 expected_disk,
9257 expected_status,
9258 expected_skip,
9259 None, None, None, None,
9260 None, 1)
9261 short_D_COPY_path = shorten_path_kludge(D_COPY_path)
9262 expected_output = wc.State(short_D_COPY_path, {
9263 'H/omega': Item(status='G '),
9265 expected_status = wc.State(short_D_COPY_path, {
9266 '' : Item(status=' M', wc_rev=2),
9267 'G' : Item(status=' ', wc_rev=2),
9268 'G/pi' : Item(status=' ', wc_rev=2),
9269 'G/rho' : Item(status=' ', wc_rev=2),
9270 'G/tau' : Item(status=' ', wc_rev=2),
9271 'gamma' : Item(status=' ', wc_rev=2),
9272 'H' : Item(status=' M', wc_rev=2),
9273 'H/chi' : Item(status=' ', wc_rev=2),
9274 'H/psi' : Item(status=' ', wc_rev=2),
9275 'H/omega' : Item(status=' ', wc_rev=2),
9276 'H/nu' : Item(status='A ', copied='+', wc_rev='-'),
9278 expected_disk = wc.State('', {
9279 '' : Item(props={SVN_PROP_MERGEINFO : '/A/D:5'}),
9280 'G/pi' : Item("This is the file 'pi'.\n"),
9281 'G/rho' : Item("This is the file 'rho'.\n"),
9282 'G/tau' : Item("This is the file 'tau'.\n"),
9283 'gamma' : Item("This is the file 'gamma'.\n"),
9284 'H' : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:5,7'}),
9285 'H/chi' : Item("This is the file 'chi'.\n"),
9286 'H/psi' : Item("This is the file 'psi'.\n"),
9287 'H/omega' : Item("This is the file 'omega'.\n"),
9288 'H/nu' : Item("New content",
9289 props={SVN_PROP_MERGEINFO : '/A/D/H/nu:5,7-8'}),
9291 expected_skip = wc.State(short_D_COPY_path, { })
9292 svntest.actions.run_and_verify_merge(short_D_COPY_path, '6', '5',
9293 sbox.repo_url + \
9294 '/A/D',
9295 expected_output,
9296 expected_disk,
9297 expected_status,
9298 expected_skip,
9299 None, None, None, None,
9300 None, 1)
9301 # Now once again merge r6 to A_COPY. A_COPY already has r6 in its mergeinfo
9302 # so we expect only subtree merges on A_COPY/D, A_COPY_D_H, and
9303 # A_COPY/D/H/nu. The fact that A/D/H/nu doesn't exist at r6 should not cause
9304 # the merge to fail.
9305 short_A_COPY_path = shorten_path_kludge(A_COPY_path)
9306 expected_output = wc.State(short_A_COPY_path, {
9307 'D/H/omega': Item(status='U '),
9309 expected_status = wc.State(short_A_COPY_path, {
9310 '' : Item(status=' M', wc_rev=2),
9311 'B' : Item(status=' ', wc_rev=2),
9312 'mu' : Item(status=' ', wc_rev=2),
9313 'B/E' : Item(status=' ', wc_rev=2),
9314 'B/E/alpha' : Item(status=' ', wc_rev=2),
9315 'B/E/beta' : Item(status='M ', wc_rev=2),
9316 'B/lambda' : Item(status=' ', wc_rev=2),
9317 'B/F' : Item(status=' ', wc_rev=2),
9318 'C' : Item(status=' ', wc_rev=2),
9319 'D' : Item(status=' ', wc_rev=2),
9320 'D/G' : Item(status=' ', wc_rev=2),
9321 'D/G/pi' : Item(status=' ', wc_rev=2),
9322 'D/G/rho' : Item(status=' ', wc_rev=2),
9323 'D/G/tau' : Item(status=' ', wc_rev=2),
9324 'D/gamma' : Item(status=' ', wc_rev=2),
9325 'D/H' : Item(status=' M', wc_rev=2),
9326 'D/H/chi' : Item(status=' ', wc_rev=2),
9327 'D/H/psi' : Item(status=' ', wc_rev=2),
9328 'D/H/omega' : Item(status='M ', wc_rev=2),
9329 'D/H/nu' : Item(status='A ', copied='+', wc_rev='-'),
9331 expected_disk = wc.State('', {
9332 '' : Item(props={SVN_PROP_MERGEINFO : '/A:5-6'}),
9333 'B' : Item(),
9334 'mu' : Item("This is the file 'mu'.\n"),
9335 'B/E' : Item(),
9336 'B/E/alpha' : Item("This is the file 'alpha'.\n"),
9337 'B/E/beta' : Item("New content"),
9338 'B/lambda' : Item("This is the file 'lambda'.\n"),
9339 'B/F' : Item(),
9340 'C' : Item(),
9341 'D' : Item(), # Mergeinfo elides to 'A_COPY'
9342 'D/G' : Item(),
9343 'D/G/pi' : Item("This is the file 'pi'.\n"),
9344 'D/G/rho' : Item("This is the file 'rho'.\n"),
9345 'D/G/tau' : Item("This is the file 'tau'.\n"),
9346 'D/gamma' : Item("This is the file 'gamma'.\n"),
9347 'D/H' : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:5-7'}),
9348 'D/H/chi' : Item("This is the file 'chi'.\n"),
9349 'D/H/psi' : Item("This is the file 'psi'.\n"),
9350 'D/H/omega' : Item("New content"),
9351 'D/H/nu' : Item("New content",
9352 props={SVN_PROP_MERGEINFO : '/A/D/H/nu:5-8'}),
9354 expected_skip = wc.State(short_A_COPY_path, { })
9355 svntest.actions.run_and_verify_merge(short_A_COPY_path, '5', '6',
9356 sbox.repo_url + \
9357 '/A',
9358 expected_output,
9359 expected_disk,
9360 expected_status,
9361 expected_skip,
9362 None, None, None, None,
9363 None, 1)
9364 os.chdir(saved_cwd)
9366 def basic_reintegrate(sbox):
9367 "basic merge --reintegrate support"
9369 # Make A_COPY branch in r2, and do a few more commits to A in r3-6.
9370 sbox.build()
9371 wc_dir = sbox.wc_dir
9372 expected_disk, expected_status = set_up_branch(sbox)
9374 # Make a change on the branch, to A/mu. Commit in r7.
9375 svntest.main.file_write(os.path.join(wc_dir, "A_COPY", "mu"),
9376 "Changed on the branch.")
9377 expected_output = wc.State(wc_dir, {'A_COPY/mu' : Item(verb='Sending')})
9378 expected_status.tweak('A_COPY/mu', wc_rev=7)
9379 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
9380 expected_status, None, wc_dir)
9381 expected_disk.tweak('A_COPY/mu', contents='Changed on the branch.')
9383 # Update the wcs.
9384 expected_output = wc.State(wc_dir, {})
9385 expected_status.tweak(wc_rev='7')
9386 svntest.actions.run_and_verify_update(wc_dir, expected_output,
9387 expected_disk, expected_status,
9388 None, None, None, None, None, True)
9390 # Merge from trunk to branch (ie, r3-6), using normal cherry-harvest.
9391 A_COPY_path = os.path.join(wc_dir, "A_COPY")
9392 short_A_COPY_path = shorten_path_kludge(A_COPY_path)
9393 expected_output = wc.State(short_A_COPY_path, {
9394 'D/H/psi' : Item(status='U '),
9395 'D/G/rho' : Item(status='U '),
9396 'B/E/beta' : Item(status='U '),
9397 'D/H/omega' : Item(status='U '),
9399 k_expected_status = wc.State(short_A_COPY_path, {
9400 "B" : Item(status=' ', wc_rev=7),
9401 "B/lambda" : Item(status=' ', wc_rev=7),
9402 "B/E" : Item(status=' ', wc_rev=7),
9403 "B/E/alpha" : Item(status=' ', wc_rev=7),
9404 "B/E/beta" : Item(status='M ', wc_rev=7),
9405 "B/F" : Item(status=' ', wc_rev=7),
9406 "mu" : Item(status=' ', wc_rev=7),
9407 "C" : Item(status=' ', wc_rev=7),
9408 "D" : Item(status=' ', wc_rev=7),
9409 "D/gamma" : Item(status=' ', wc_rev=7),
9410 "D/G" : Item(status=' ', wc_rev=7),
9411 "D/G/pi" : Item(status=' ', wc_rev=7),
9412 "D/G/rho" : Item(status='M ', wc_rev=7),
9413 "D/G/tau" : Item(status=' ', wc_rev=7),
9414 "D/H" : Item(status=' ', wc_rev=7),
9415 "D/H/chi" : Item(status=' ', wc_rev=7),
9416 "D/H/omega" : Item(status='M ', wc_rev=7),
9417 "D/H/psi" : Item(status='M ', wc_rev=7),
9418 "" : Item(status=' M', wc_rev=7),
9420 k_expected_disk = wc.State('', {
9421 '' : Item(props={SVN_PROP_MERGEINFO : '/A:2-7'}),
9422 'B' : Item(),
9423 'B/lambda' : Item("This is the file 'lambda'.\n"),
9424 'B/E' : Item(),
9425 'B/E/alpha' : Item("This is the file 'alpha'.\n"),
9426 'B/E/beta' : Item("New content"),
9427 'B/F' : Item(),
9428 'mu' : Item("Changed on the branch."),
9429 'C' : Item(),
9430 'D' : Item(),
9431 'D/gamma' : Item("This is the file 'gamma'.\n"),
9432 'D/G' : Item(),
9433 'D/G/pi' : Item("This is the file 'pi'.\n"),
9434 'D/G/rho' : Item("New content"),
9435 'D/G/tau' : Item("This is the file 'tau'.\n"),
9436 'D/H' : Item(),
9437 'D/H/chi' : Item("This is the file 'chi'.\n"),
9438 'D/H/omega' : Item("New content"),
9439 'D/H/psi' : Item("New content"),
9441 expected_skip = wc.State(short_A_COPY_path, {})
9442 saved_cwd = os.getcwd()
9443 os.chdir(svntest.main.work_dir)
9444 svntest.actions.run_and_verify_merge(short_A_COPY_path, None, None,
9445 sbox.repo_url + '/A',
9446 expected_output,
9447 k_expected_disk,
9448 k_expected_status,
9449 expected_skip,
9450 None, None, None, None,
9451 None, True)
9452 os.chdir(saved_cwd)
9453 expected_disk.tweak('A_COPY', props={SVN_PROP_MERGEINFO: '/A:2-7'})
9454 expected_disk.tweak('A_COPY/B/E/beta', contents="New content")
9455 expected_disk.tweak('A_COPY/D/G/rho', contents="New content")
9456 expected_disk.tweak('A_COPY/D/H/omega', contents="New content")
9457 expected_disk.tweak('A_COPY/D/H/psi', contents="New content")
9459 # Commit the merge to branch (r8).
9460 expected_output = wc.State(wc_dir, {
9461 'A_COPY/D/H/psi' : Item(verb='Sending'),
9462 'A_COPY/D/G/rho' : Item(verb='Sending'),
9463 'A_COPY/B/E/beta' : Item(verb='Sending'),
9464 'A_COPY/D/H/omega' : Item(verb='Sending'),
9465 'A_COPY' : Item(verb='Sending'),
9467 expected_status.tweak('A_COPY', 'A_COPY/D/H/psi', 'A_COPY/D/G/rho',
9468 'A_COPY/B/E/beta', 'A_COPY/D/H/omega', wc_rev=8)
9469 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
9470 expected_status, None, wc_dir)
9472 # Update the wcs again.
9473 expected_output = wc.State(wc_dir, {})
9474 expected_status.tweak(wc_rev='8')
9475 svntest.actions.run_and_verify_update(wc_dir, expected_output,
9476 expected_disk, expected_status,
9477 None, None, None, None, None, True)
9480 # *finally*, actually run merge --reintegrate in trunk with the
9481 # branch URL. This should bring in the mu change and the tauprime
9482 # change.
9483 A_path = os.path.join(wc_dir, "A")
9484 short_A_path = shorten_path_kludge(A_path)
9485 expected_output = wc.State(short_A_path, {
9486 '' : Item(status=' U'),
9487 'mu' : Item(status='U '),
9489 k_expected_status = wc.State(short_A_path, {
9490 "B" : Item(status=' ', wc_rev=8),
9491 "B/lambda" : Item(status=' ', wc_rev=8),
9492 "B/E" : Item(status=' ', wc_rev=8),
9493 "B/E/alpha" : Item(status=' ', wc_rev=8),
9494 "B/E/beta" : Item(status=' ', wc_rev=8),
9495 "B/F" : Item(status=' ', wc_rev=8),
9496 "mu" : Item(status='M ', wc_rev=8),
9497 "C" : Item(status=' ', wc_rev=8),
9498 "D" : Item(status=' ', wc_rev=8),
9499 "D/gamma" : Item(status=' ', wc_rev=8),
9500 "D/G" : Item(status=' ', wc_rev=8),
9501 "D/G/pi" : Item(status=' ', wc_rev=8),
9502 "D/G/rho" : Item(status=' ', wc_rev=8),
9503 "D/G/tau" : Item(status=' ', wc_rev=8),
9504 "D/H" : Item(status=' ', wc_rev=8),
9505 "D/H/chi" : Item(status=' ', wc_rev=8),
9506 "D/H/omega" : Item(status=' ', wc_rev=8),
9507 "D/H/psi" : Item(status=' ', wc_rev=8),
9508 "" : Item(status=' M', wc_rev=8),
9510 k_expected_disk.tweak('', props={SVN_PROP_MERGEINFO : '/A_COPY:2-8'})
9511 expected_skip = wc.State(short_A_path, {})
9512 saved_cwd = os.getcwd()
9513 os.chdir(svntest.main.work_dir)
9514 svntest.actions.run_and_verify_merge(short_A_path, None, None,
9515 sbox.repo_url + '/A_COPY',
9516 expected_output,
9517 k_expected_disk,
9518 k_expected_status,
9519 expected_skip,
9520 None, None, None, None,
9521 None, True, True,
9522 '--reintegrate')
9523 os.chdir(saved_cwd)
9525 # Finally, commit the result of the merge (r9).
9526 expected_output = wc.State(wc_dir, {
9527 'A/mu' : Item(verb='Sending'),
9528 'A' : Item(verb='Sending'),
9530 expected_status.tweak('A', 'A/mu', wc_rev=9)
9531 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
9532 expected_status, None, wc_dir)
9534 def reintegrate_with_rename(sbox):
9535 "merge --reintegrate with renamed file on branch"
9537 # Make A_COPY branch in r2, and do a few more commits to A in r3-6.
9538 sbox.build()
9539 wc_dir = sbox.wc_dir
9540 expected_disk, expected_status = set_up_branch(sbox)
9542 # Make a change on the branch, to A/mu. Commit in r7.
9543 svntest.main.file_write(os.path.join(wc_dir, "A_COPY", "mu"),
9544 "Changed on the branch.")
9545 expected_output = wc.State(wc_dir, {'A_COPY/mu' : Item(verb='Sending')})
9546 expected_status.tweak('A_COPY/mu', wc_rev=7)
9547 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
9548 expected_status, None, wc_dir)
9549 expected_disk.tweak('A_COPY/mu', contents='Changed on the branch.')
9551 # Update the wcs.
9552 expected_output = wc.State(wc_dir, {})
9553 expected_status.tweak(wc_rev='7')
9554 svntest.actions.run_and_verify_update(wc_dir, expected_output,
9555 expected_disk, expected_status,
9556 None, None, None, None, None, True)
9558 # Merge from trunk to branch (ie, r3-6), using normal cherry-harvest.
9559 A_COPY_path = os.path.join(wc_dir, "A_COPY")
9560 short_A_COPY_path = shorten_path_kludge(A_COPY_path)
9561 expected_output = wc.State(short_A_COPY_path, {
9562 'D/H/psi' : Item(status='U '),
9563 'D/G/rho' : Item(status='U '),
9564 'B/E/beta' : Item(status='U '),
9565 'D/H/omega' : Item(status='U '),
9567 k_expected_status = wc.State(short_A_COPY_path, {
9568 "B" : Item(status=' ', wc_rev=7),
9569 "B/lambda" : Item(status=' ', wc_rev=7),
9570 "B/E" : Item(status=' ', wc_rev=7),
9571 "B/E/alpha" : Item(status=' ', wc_rev=7),
9572 "B/E/beta" : Item(status='M ', wc_rev=7),
9573 "B/F" : Item(status=' ', wc_rev=7),
9574 "mu" : Item(status=' ', wc_rev=7),
9575 "C" : Item(status=' ', wc_rev=7),
9576 "D" : Item(status=' ', wc_rev=7),
9577 "D/gamma" : Item(status=' ', wc_rev=7),
9578 "D/G" : Item(status=' ', wc_rev=7),
9579 "D/G/pi" : Item(status=' ', wc_rev=7),
9580 "D/G/rho" : Item(status='M ', wc_rev=7),
9581 "D/G/tau" : Item(status=' ', wc_rev=7),
9582 "D/H" : Item(status=' ', wc_rev=7),
9583 "D/H/chi" : Item(status=' ', wc_rev=7),
9584 "D/H/omega" : Item(status='M ', wc_rev=7),
9585 "D/H/psi" : Item(status='M ', wc_rev=7),
9586 "" : Item(status=' M', wc_rev=7),
9588 k_expected_disk = wc.State('', {
9589 '' : Item(props={SVN_PROP_MERGEINFO : '/A:2-7'}),
9590 'B' : Item(),
9591 'B/lambda' : Item("This is the file 'lambda'.\n"),
9592 'B/E' : Item(),
9593 'B/E/alpha' : Item("This is the file 'alpha'.\n"),
9594 'B/E/beta' : Item("New content"),
9595 'B/F' : Item(),
9596 'mu' : Item("Changed on the branch."),
9597 'C' : Item(),
9598 'D' : Item(),
9599 'D/gamma' : Item("This is the file 'gamma'.\n"),
9600 'D/G' : Item(),
9601 'D/G/pi' : Item("This is the file 'pi'.\n"),
9602 'D/G/rho' : Item("New content"),
9603 'D/G/tau' : Item("This is the file 'tau'.\n"),
9604 'D/H' : Item(),
9605 'D/H/chi' : Item("This is the file 'chi'.\n"),
9606 'D/H/omega' : Item("New content"),
9607 'D/H/psi' : Item("New content"),
9609 expected_skip = wc.State(short_A_COPY_path, {})
9610 saved_cwd = os.getcwd()
9611 os.chdir(svntest.main.work_dir)
9612 svntest.actions.run_and_verify_merge(short_A_COPY_path, None, None,
9613 sbox.repo_url + '/A',
9614 expected_output,
9615 k_expected_disk,
9616 k_expected_status,
9617 expected_skip,
9618 None, None, None, None,
9619 None, True)
9620 os.chdir(saved_cwd)
9621 expected_disk.tweak('A_COPY', props={SVN_PROP_MERGEINFO: '/A:2-7'})
9622 expected_disk.tweak('A_COPY/B/E/beta', contents="New content")
9623 expected_disk.tweak('A_COPY/D/G/rho', contents="New content")
9624 expected_disk.tweak('A_COPY/D/H/omega', contents="New content")
9625 expected_disk.tweak('A_COPY/D/H/psi', contents="New content")
9627 # Commit the merge to branch (r8).
9628 expected_output = wc.State(wc_dir, {
9629 'A_COPY/D/H/psi' : Item(verb='Sending'),
9630 'A_COPY/D/G/rho' : Item(verb='Sending'),
9631 'A_COPY/B/E/beta' : Item(verb='Sending'),
9632 'A_COPY/D/H/omega' : Item(verb='Sending'),
9633 'A_COPY' : Item(verb='Sending'),
9635 expected_status.tweak('A_COPY', 'A_COPY/D/H/psi', 'A_COPY/D/G/rho',
9636 'A_COPY/B/E/beta', 'A_COPY/D/H/omega', wc_rev=8)
9637 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
9638 expected_status, None, wc_dir)
9641 # Update the wcs again.
9643 # Note: this update had to be added because of r28942 (which was
9644 # merged into the reintegrate branch in r28947). Without this
9645 # update, the mergeinfo will not be inherited properly as part of
9646 # the 'svn cp tau tauprime' step, and later (during the post-commit
9647 # update, with the new expected_disk) we'll get an error like this:
9649 # =============================================================
9650 # Expected 'tauprime' and actual 'tauprime' in disk tree are different!
9651 # =============================================================
9652 # EXPECTED NODE TO BE:
9653 # =============================================================
9654 # * Node name: tauprime
9655 # Path: A_COPY/D/G/tauprime
9656 # Contents: This is the file 'tau'.
9658 # Properties: {'svn:mergeinfo': '/A/D/G/tau:2-7'}
9659 # Attributes: {}
9660 # Children: N/A (node is a file)
9661 # =============================================================
9662 # ACTUAL NODE FOUND:
9663 # =============================================================
9664 # * Node name: tauprime
9665 # Path: G/tauprime
9666 # Contents: This is the file 'tau'.
9668 # Properties: {'svn:mergeinfo': ''}
9669 # Attributes: {}
9670 # Children: N/A (node is a file)
9672 expected_output = wc.State(wc_dir, {})
9673 expected_status.tweak(wc_rev='8')
9674 svntest.actions.run_and_verify_update(wc_dir, expected_output,
9675 expected_disk, expected_status,
9676 None, None, None, None, None, True)
9678 # Make another change on the branch: copy tau to tauprime. Commit
9679 # in r9.
9680 svntest.actions.run_and_verify_svn(None, None, [], 'cp',
9681 os.path.join(wc_dir, 'A_COPY', 'D', 'G',
9682 'tau'),
9683 os.path.join(wc_dir, 'A_COPY', 'D', 'G',
9684 'tauprime'))
9686 expected_output = wc.State(wc_dir, {
9687 'A_COPY/D/G/tauprime' : Item(verb='Adding')
9689 expected_status.add({'A_COPY/D/G/tauprime': Item(status=' ', wc_rev=9)})
9690 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
9691 expected_status, None, wc_dir)
9693 expected_disk.add({
9694 'A_COPY/D/G/tauprime' : Item(props={SVN_PROP_MERGEINFO: '/A/D/G/tau:2-7'},
9695 contents="This is the file 'tau'.\n")
9698 # Update the trunk (well, the whole wc) (since reintegrate really
9699 # wants a clean wc).
9700 expected_output = wc.State(wc_dir, {})
9701 expected_status.tweak(wc_rev='9')
9702 svntest.actions.run_and_verify_update(wc_dir, expected_output,
9703 expected_disk, expected_status,
9704 None, None, None, None, None, True)
9706 # *finally*, actually run merge --reintegrate in trunk with the
9707 # branch URL. This should bring in the mu change and the tauprime
9708 # change.
9709 A_path = os.path.join(wc_dir, "A")
9710 short_A_path = shorten_path_kludge(A_path)
9711 expected_output = wc.State(short_A_path, {
9712 '' : Item(status=' U'),
9713 'mu' : Item(status='U '),
9714 'D/G/tauprime' : Item(status='A '),
9716 k_expected_status = wc.State(short_A_path, {
9717 "B" : Item(status=' ', wc_rev=9),
9718 "B/lambda" : Item(status=' ', wc_rev=9),
9719 "B/E" : Item(status=' ', wc_rev=9),
9720 "B/E/alpha" : Item(status=' ', wc_rev=9),
9721 "B/E/beta" : Item(status=' ', wc_rev=9),
9722 "B/F" : Item(status=' ', wc_rev=9),
9723 "mu" : Item(status='M ', wc_rev=9),
9724 "C" : Item(status=' ', wc_rev=9),
9725 "D" : Item(status=' ', wc_rev=9),
9726 "D/gamma" : Item(status=' ', wc_rev=9),
9727 "D/G" : Item(status=' ', wc_rev=9),
9728 "D/G/pi" : Item(status=' ', wc_rev=9),
9729 "D/G/rho" : Item(status=' ', wc_rev=9),
9730 "D/G/tau" : Item(status=' ', wc_rev=9),
9731 "D/G/tauprime" : Item(status='A ', wc_rev='-', copied='+'),
9732 "D/H" : Item(status=' ', wc_rev=9),
9733 "D/H/chi" : Item(status=' ', wc_rev=9),
9734 "D/H/omega" : Item(status=' ', wc_rev=9),
9735 "D/H/psi" : Item(status=' ', wc_rev=9),
9736 "" : Item(status=' M', wc_rev=9),
9738 k_expected_disk.tweak('', props={SVN_PROP_MERGEINFO : '/A_COPY:2-9'})
9739 k_expected_disk.add({
9740 'D/G/tauprime' : Item(props={SVN_PROP_MERGEINFO: '/A/D/G/tau:2-7'},
9741 contents="This is the file 'tau'.\n")
9743 expected_skip = wc.State(short_A_path, {})
9744 saved_cwd = os.getcwd()
9745 os.chdir(svntest.main.work_dir)
9746 svntest.actions.run_and_verify_merge(short_A_path, None, None,
9747 sbox.repo_url + '/A_COPY',
9748 expected_output,
9749 k_expected_disk,
9750 k_expected_status,
9751 expected_skip,
9752 None, None, None, None,
9753 None, True, True,
9754 '--reintegrate')
9755 os.chdir(saved_cwd)
9757 # Finally, commit the result of the merge (r10).
9758 expected_output = wc.State(wc_dir, {
9759 'A/D/G/tauprime' : Item(verb='Adding'),
9760 'A/mu' : Item(verb='Sending'),
9761 'A' : Item(verb='Sending'),
9763 expected_status.add({
9764 'A/D/G/tauprime' : Item(status=' ', wc_rev=10),
9766 expected_status.tweak('A', 'A/mu', wc_rev=10)
9767 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
9768 expected_status, None, wc_dir)
9770 def reintegrate_branch_never_merged_to(sbox):
9771 "merge --reintegrate on a never-updated branch"
9773 # Make A_COPY branch in r2, and do a few more commits to A in r3-6.
9774 sbox.build()
9775 wc_dir = sbox.wc_dir
9776 expected_disk, expected_status = set_up_branch(sbox)
9778 # Make a change on the branch, to A/mu. Commit in r7.
9779 svntest.main.file_write(os.path.join(wc_dir, "A_COPY", "mu"),
9780 "Changed on the branch.")
9781 expected_output = wc.State(wc_dir, {'A_COPY/mu' : Item(verb='Sending')})
9782 expected_status.tweak('A_COPY/mu', wc_rev=7)
9783 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
9784 expected_status, None, wc_dir)
9785 expected_disk.tweak('A_COPY/mu', contents='Changed on the branch.')
9787 # Update the wcs.
9788 expected_output = wc.State(wc_dir, {})
9789 expected_status.tweak(wc_rev='7')
9790 svntest.actions.run_and_verify_update(wc_dir, expected_output,
9791 expected_disk, expected_status,
9792 None, None, None, None, None, True)
9794 # Make another change on the branch: copy tau to tauprime. Commit
9795 # in r8.
9796 svntest.actions.run_and_verify_svn(None, None, [], 'cp',
9797 os.path.join(wc_dir, 'A_COPY', 'D', 'G',
9798 'tau'),
9799 os.path.join(wc_dir, 'A_COPY', 'D', 'G',
9800 'tauprime'))
9801 expected_output = wc.State(wc_dir, {
9802 'A_COPY/D/G/tauprime' : Item(verb='Adding')
9804 expected_status.add({'A_COPY/D/G/tauprime': Item(status=' ', wc_rev=8)})
9805 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
9806 expected_status, None, wc_dir)
9807 expected_disk.add({
9808 'A_COPY/D/G/tauprime' : Item(props={SVN_PROP_MERGEINFO: ''},
9809 ### TODO(reint): why empty?
9810 contents="This is the file 'tau'.\n")
9813 # Update the trunk (well, the whole wc) (since reintegrate really
9814 # wants a clean wc).
9815 expected_output = wc.State(wc_dir, {})
9816 expected_status.tweak(wc_rev='8')
9817 svntest.actions.run_and_verify_update(wc_dir, expected_output,
9818 expected_disk, expected_status,
9819 None, None, None, None, None, True)
9821 # *finally*, actually run merge --reintegrate in trunk with the
9822 # branch URL. This should bring in the mu change and the tauprime
9823 # change.
9824 A_path = os.path.join(wc_dir, "A")
9825 short_A_path = shorten_path_kludge(A_path)
9826 expected_output = wc.State(short_A_path, {
9827 'mu' : Item(status='U '),
9828 'D/G/tauprime' : Item(status='A '),
9830 k_expected_status = wc.State(short_A_path, {
9831 "B" : Item(status=' ', wc_rev=8),
9832 "B/lambda" : Item(status=' ', wc_rev=8),
9833 "B/E" : Item(status=' ', wc_rev=8),
9834 "B/E/alpha" : Item(status=' ', wc_rev=8),
9835 "B/E/beta" : Item(status=' ', wc_rev=8),
9836 "B/F" : Item(status=' ', wc_rev=8),
9837 "mu" : Item(status='M ', wc_rev=8),
9838 "C" : Item(status=' ', wc_rev=8),
9839 "D" : Item(status=' ', wc_rev=8),
9840 "D/gamma" : Item(status=' ', wc_rev=8),
9841 "D/G" : Item(status=' ', wc_rev=8),
9842 "D/G/pi" : Item(status=' ', wc_rev=8),
9843 "D/G/rho" : Item(status=' ', wc_rev=8),
9844 "D/G/tau" : Item(status=' ', wc_rev=8),
9845 "D/G/tauprime" : Item(status='A ', wc_rev='-', copied='+'),
9846 "D/H" : Item(status=' ', wc_rev=8),
9847 "D/H/chi" : Item(status=' ', wc_rev=8),
9848 "D/H/omega" : Item(status=' ', wc_rev=8),
9849 "D/H/psi" : Item(status=' ', wc_rev=8),
9850 "" : Item(status=' M', wc_rev=8),
9852 k_expected_disk = wc.State('', {
9853 '' : Item(props={SVN_PROP_MERGEINFO : '/A_COPY:2-8'}),
9854 'B' : Item(),
9855 'B/lambda' : Item("This is the file 'lambda'.\n"),
9856 'B/E' : Item(),
9857 'B/E/alpha' : Item("This is the file 'alpha'.\n"),
9858 'B/E/beta' : Item("New content"),
9859 'B/F' : Item(),
9860 'mu' : Item("Changed on the branch."),
9861 'C' : Item(),
9862 'D' : Item(),
9863 'D/gamma' : Item("This is the file 'gamma'.\n"),
9864 'D/G' : Item(),
9865 'D/G/pi' : Item("This is the file 'pi'.\n"),
9866 'D/G/rho' : Item("New content"),
9867 'D/G/tau' : Item("This is the file 'tau'.\n"),
9868 'D/G/tauprime' : Item("This is the file 'tau'.\n",
9869 ### TODO(reint): why empty?
9870 props={SVN_PROP_MERGEINFO: ''}),
9871 'D/H' : Item(),
9872 'D/H/chi' : Item("This is the file 'chi'.\n"),
9873 'D/H/omega' : Item("New content"),
9874 'D/H/psi' : Item("New content"),
9876 expected_skip = wc.State(short_A_path, {})
9877 saved_cwd = os.getcwd()
9878 os.chdir(svntest.main.work_dir)
9879 svntest.actions.run_and_verify_merge(short_A_path, None, None,
9880 sbox.repo_url + '/A_COPY',
9881 expected_output,
9882 k_expected_disk,
9883 k_expected_status,
9884 expected_skip,
9885 None, None, None, None,
9886 None, True, True,
9887 '--reintegrate')
9888 os.chdir(saved_cwd)
9890 # Finally, commit the result of the merge (r9).
9891 expected_output = wc.State(wc_dir, {
9892 'A/D/G/tauprime' : Item(verb='Adding'),
9893 'A/mu' : Item(verb='Sending'),
9894 'A' : Item(verb='Sending'),
9896 expected_status.add({
9897 'A/D/G/tauprime' : Item(status=' ', wc_rev=9),
9899 expected_status.tweak('A', 'A/mu', wc_rev=9)
9900 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
9901 expected_status, None, wc_dir)
9903 def reintegrate_fail_on_modified_wc(sbox):
9904 "merge --reintegrate should fail in modified wc"
9905 sbox.build()
9906 wc_dir = sbox.wc_dir
9907 A_path = os.path.join(wc_dir, "A")
9908 mu_path = os.path.join(A_path, "mu")
9909 ignored_expected_disk, ignored_expected_status = set_up_branch(sbox)
9910 svntest.main.file_write(mu_path, "Changed on 'trunk' (the merge target).")
9911 svntest.actions.run_and_verify_merge(
9912 A_path, None, None, sbox.repo_url + '/A_COPY', None, None, None, None,
9913 ".*Cannot reintegrate into a working copy that has local modifications.*",
9914 None, None, None, None, True, False, '--reintegrate')
9916 def reintegrate_fail_on_mixed_rev_wc(sbox):
9917 "merge --reintegrate should fail in mixed-rev wc"
9918 sbox.build()
9919 wc_dir = sbox.wc_dir
9920 A_path = os.path.join(wc_dir, "A")
9921 mu_path = os.path.join(A_path, "mu")
9922 ignored_expected_disk, expected_status = set_up_branch(sbox)
9923 # Make and commit a change, in order to get a mixed-rev wc.
9924 svntest.main.file_write(mu_path, "Changed on 'trunk' (the merge target).")
9925 expected_output = wc.State(wc_dir, {
9926 'A/mu' : Item(verb='Sending'),
9928 expected_status.tweak('A/mu', wc_rev=7)
9929 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
9930 expected_status, None, wc_dir)
9931 # Try merging into that same wc, expecting failure.
9932 svntest.actions.run_and_verify_merge(
9933 A_path, None, None, sbox.repo_url + '/A_COPY', None, None, None, None,
9934 ".*Cannot reintegrate into mixed-revision working copy.*",
9935 None, None, None, None, True, False, '--reintegrate')
9937 def reintegrate_fail_on_switched_wc(sbox):
9938 "merge --reintegrate should fail in switched wc"
9939 sbox.build()
9940 wc_dir = sbox.wc_dir
9941 A_path = os.path.join(wc_dir, "A")
9942 G_path = os.path.join(A_path, "D", "G")
9943 switch_url = sbox.repo_url + "/A/D/H"
9944 expected_disk, expected_status = set_up_branch(sbox)
9946 # Switch a subdir of the target.
9947 expected_output = svntest.wc.State(wc_dir, {
9948 'A/D/G/pi' : Item(status='D '),
9949 'A/D/G/rho' : Item(status='D '),
9950 'A/D/G/tau' : Item(status='D '),
9951 'A/D/G/chi' : Item(status='A '),
9952 'A/D/G/psi' : Item(status='A '),
9953 'A/D/G/omega' : Item(status='A '),
9955 expected_disk.remove('A/D/G/pi', 'A/D/G/rho', 'A/D/G/tau')
9956 expected_disk.add({
9957 'A/D/G/chi' : Item(contents="This is the file 'chi'.\n"),
9958 'A/D/G/psi' : Item(contents="New content"),
9959 'A/D/G/omega' : Item(contents="New content"),
9961 expected_status.remove('A/D/G/pi', 'A/D/G/rho', 'A/D/G/tau')
9962 expected_status.add({
9963 'A/D/G' : Item(status=' ', wc_rev=6, switched='S'),
9964 'A/D/G/chi' : Item(status=' ', wc_rev=6),
9965 'A/D/G/psi' : Item(status=' ', wc_rev=6),
9966 'A/D/G/omega' : Item(status=' ', wc_rev=6),
9968 svntest.actions.run_and_verify_switch(wc_dir,
9969 G_path,
9970 switch_url,
9971 expected_output,
9972 expected_disk,
9973 expected_status,
9974 None, None, None, None, False);
9975 svntest.actions.run_and_verify_merge(
9976 A_path, None, None, sbox.repo_url + '/A_COPY', None, None, None, None,
9977 ".*Cannot reintegrate into a working copy with a switched subtree.*",
9978 None, None, None, None, True, False, '--reintegrate')
9980 def reintegrate_fail_on_shallow_wc(sbox):
9981 "merge --reintegrate should fail in shallow wc"
9982 sbox.build()
9983 wc_dir = sbox.wc_dir
9984 expected_disk, expected_status = set_up_branch(sbox)
9985 A_path = os.path.join(wc_dir, "A")
9986 G_path = os.path.join(A_path, "D", "G")
9987 # Our default checkout doesn't have any subdirs at non-infinite
9988 # depth, so we'll have to create one the old-fashioned way: remove a
9989 # tree, then "update" it back into existence at a shallower depth.
9990 svntest.main.safe_rmtree(G_path)
9991 svntest.actions.run_and_verify_svn(None, None, [], 'update', G_path,
9992 '--depth=files')
9993 # Even though everything is actually present (as G has no subdirs
9994 # anyway), the reintegration should fail, because G's depth is other
9995 # than infinity.
9996 svntest.actions.run_and_verify_merge(
9997 A_path, None, None, sbox.repo_url + '/A_COPY', None, None, None, None,
9998 ".*Cannot reintegrate into a working copy not.*at infinite depth.*",
9999 None, None, None, None, True, False, '--reintegrate')
10001 def reintegrate_fail_on_stale_source(sbox):
10002 "merge --reintegrate should fail on stale source"
10003 sbox.build()
10004 wc_dir = sbox.wc_dir
10005 expected_disk, expected_status = set_up_branch(sbox)
10006 A_path = os.path.join(wc_dir, "A")
10007 mu_path = os.path.join(A_path, "mu")
10008 svntest.main.file_append(mu_path, 'some text appended to mu\n')
10009 svntest.actions.run_and_verify_svn(None, None, [], 'commit',
10010 '-m', 'a change to mu', mu_path);
10011 # Unmix the revisions in the working copy.
10012 svntest.actions.run_and_verify_svn(None, None, [], 'update', wc_dir);
10013 # The merge --reintegrate should fail because target has changes not
10014 # present in source.
10015 svntest.actions.run_and_verify_merge(
10016 A_path, None, None, sbox.repo_url + '/A_COPY', None, None, None, None,
10017 ".*", ###TODO(reint): need a more specific check here
10018 None, None, None, None, True, False, '--reintegrate')
10020 def dont_add_mergeinfo_from_own_history(sbox):
10021 "cyclic merges don't add mergeinfo from own history"
10023 sbox.build()
10024 wc_dir = sbox.wc_dir
10025 wc_disk, wc_status = set_up_branch(sbox)
10027 # Some paths we'll care about
10028 A_path = os.path.join(wc_dir, "A")
10029 A_MOVED_path = os.path.join(wc_dir, "A_MOVED")
10030 mu_path = os.path.join(wc_dir, "A", "mu")
10031 mu_MOVED_path = os.path.join(wc_dir, "A_MOVED", "mu")
10032 A_COPY_path = os.path.join(wc_dir, "A_COPY")
10033 mu_COPY_path = os.path.join(wc_dir, "A_COPY", "mu")
10035 # Merge r3 from 'A' to 'A_COPY', make a text mod to 'A_COPY/mu' and
10036 # commit both as r7. This results in mergeinfo of '/A:3' on 'A_COPY'.
10037 # Then merge r7 from 'A_COPY' to 'A'. This attempts to add the mergeinfo
10038 # '/A:3' to 'A', but that is self-referrential and should be filtered out,
10039 # leaving only the mergeinfo '/A_COPY:7' on 'A'.
10041 # Search for the comment entitled "The Merge Kluge" elsewhere in
10042 # this file, to understand why we shorten and chdir() below.
10043 short_A_COPY_path = shorten_path_kludge(A_COPY_path)
10044 expected_output = wc.State(short_A_COPY_path, {
10045 'D/H/psi' : Item(status='U '),
10047 expected_A_COPY_status = wc.State(short_A_COPY_path, {
10048 '' : Item(status=' M', wc_rev=2),
10049 'B' : Item(status=' ', wc_rev=2),
10050 'mu' : Item(status=' ', wc_rev=2),
10051 'B/E' : Item(status=' ', wc_rev=2),
10052 'B/E/alpha' : Item(status=' ', wc_rev=2),
10053 'B/E/beta' : Item(status=' ', wc_rev=2),
10054 'B/lambda' : Item(status=' ', wc_rev=2),
10055 'B/F' : Item(status=' ', wc_rev=2),
10056 'C' : Item(status=' ', wc_rev=2),
10057 'D' : Item(status=' ', wc_rev=2),
10058 'D/G' : Item(status=' ', wc_rev=2),
10059 'D/G/pi' : Item(status=' ', wc_rev=2),
10060 'D/G/rho' : Item(status=' ', wc_rev=2),
10061 'D/G/tau' : Item(status=' ', wc_rev=2),
10062 'D/gamma' : Item(status=' ', wc_rev=2),
10063 'D/H' : Item(status=' ', wc_rev=2),
10064 'D/H/chi' : Item(status=' ', wc_rev=2),
10065 'D/H/psi' : Item(status='M ', wc_rev=2),
10066 'D/H/omega' : Item(status=' ', wc_rev=2),
10068 expected_A_COPY_disk = wc.State('', {
10069 '' : Item(props={SVN_PROP_MERGEINFO : '/A:3'}),
10070 'B' : Item(),
10071 'mu' : Item("This is the file 'mu'.\n"),
10072 'B/E' : Item(),
10073 'B/E/alpha' : Item("This is the file 'alpha'.\n"),
10074 'B/E/beta' : Item("This is the file 'beta'.\n"),
10075 'B/lambda' : Item("This is the file 'lambda'.\n"),
10076 'B/F' : Item(),
10077 'C' : Item(),
10078 'D' : Item(),
10079 'D/G' : Item(),
10080 'D/G/pi' : Item("This is the file 'pi'.\n"),
10081 'D/G/rho' : Item("This is the file 'rho'.\n"),
10082 'D/G/tau' : Item("This is the file 'tau'.\n"),
10083 'D/gamma' : Item("This is the file 'gamma'.\n"),
10084 'D/H' : Item(),
10085 'D/H/chi' : Item("This is the file 'chi'.\n"),
10086 'D/H/psi' : Item("New content"),
10087 'D/H/omega' : Item("This is the file 'omega'.\n"),
10089 expected_A_COPY_skip = wc.State(short_A_COPY_path, { })
10090 saved_cwd = os.getcwd()
10091 os.chdir(svntest.main.work_dir)
10092 svntest.actions.run_and_verify_merge(short_A_COPY_path, '2', '3',
10093 sbox.repo_url + \
10094 '/A',
10095 expected_output,
10096 expected_A_COPY_disk,
10097 expected_A_COPY_status,
10098 expected_A_COPY_skip,
10099 None, None, None, None,
10100 None, 1)
10101 os.chdir(saved_cwd)
10103 # Change 'A_COPY/mu'
10104 svntest.main.file_write(mu_COPY_path, "New content")
10106 # Commit r7
10107 expected_output = wc.State(wc_dir, {
10108 'A_COPY' : Item(verb='Sending'),
10109 'A_COPY/D/H/psi' : Item(verb='Sending'),
10110 'A_COPY/mu' : Item(verb='Sending'),
10112 wc_status.tweak('A_COPY', 'A_COPY/D/H/psi', 'A_COPY/mu', wc_rev=7)
10113 svntest.actions.run_and_verify_commit(wc_dir,
10114 expected_output,
10115 wc_status,
10116 None,
10117 wc_dir)
10119 # Merge r7 back to the 'A'
10120 short_A_path = shorten_path_kludge(A_path)
10121 expected_output = wc.State(short_A_path, {
10122 'mu' : Item(status='U '),
10124 expected_A_status = wc.State(short_A_path, {
10125 '' : Item(status=' M', wc_rev=1),
10126 'B' : Item(status=' ', wc_rev=1),
10127 'mu' : Item(status='M ', wc_rev=1),
10128 'B/E' : Item(status=' ', wc_rev=1),
10129 'B/E/alpha' : Item(status=' ', wc_rev=1),
10130 'B/E/beta' : Item(status=' ', wc_rev=5),
10131 'B/lambda' : Item(status=' ', wc_rev=1),
10132 'B/F' : Item(status=' ', wc_rev=1),
10133 'C' : Item(status=' ', wc_rev=1),
10134 'D' : Item(status=' ', wc_rev=1),
10135 'D/G' : Item(status=' ', wc_rev=1),
10136 'D/G/pi' : Item(status=' ', wc_rev=1),
10137 'D/G/rho' : Item(status=' ', wc_rev=4),
10138 'D/G/tau' : Item(status=' ', wc_rev=1),
10139 'D/gamma' : Item(status=' ', wc_rev=1),
10140 'D/H' : Item(status=' ', wc_rev=1),
10141 'D/H/chi' : Item(status=' ', wc_rev=1),
10142 'D/H/psi' : Item(status=' ', wc_rev=3),
10143 'D/H/omega' : Item(status=' ', wc_rev=6),
10145 expected_A_disk = wc.State('', {
10146 '' : Item(props={SVN_PROP_MERGEINFO : '/A_COPY:7'}),
10147 'B' : Item(),
10148 'mu' : Item("New content"),
10149 'B/E' : Item(),
10150 'B/E/alpha' : Item("This is the file 'alpha'.\n"),
10151 'B/E/beta' : Item("New content"),
10152 'B/lambda' : Item("This is the file 'lambda'.\n"),
10153 'B/F' : Item(),
10154 'C' : Item(),
10155 'D' : Item(),
10156 'D/G' : Item(),
10157 'D/G/pi' : Item("This is the file 'pi'.\n"),
10158 'D/G/rho' : Item("New content"),
10159 'D/G/tau' : Item("This is the file 'tau'.\n"),
10160 'D/gamma' : Item("This is the file 'gamma'.\n"),
10161 'D/H' : Item(),
10162 'D/H/chi' : Item("This is the file 'chi'.\n"),
10163 'D/H/psi' : Item("New content"),
10164 'D/H/omega' : Item("New content"),
10166 expected_A_skip = wc.State(short_A_path, {})
10167 os.chdir(svntest.main.work_dir)
10168 svntest.actions.run_and_verify_merge(short_A_path, '6', '7',
10169 sbox.repo_url + \
10170 '/A_COPY',
10171 expected_output,
10172 expected_A_disk,
10173 expected_A_status,
10174 expected_A_skip,
10175 None, None, None, None,
10176 None, 1)
10177 os.chdir(saved_cwd)
10179 # Revert all local mods
10180 svntest.actions.run_and_verify_svn(None,
10181 ["Reverted '" + A_path + "'\n",
10182 "Reverted '" + mu_path + "'\n"],
10183 [], 'revert', '-R', wc_dir)
10185 # Move 'A' to 'A_MOVED' and once again merge r7 from 'A_COPY', this time
10186 # to 'A_MOVED'. This attempts to add the mergeinfo '/A:3' to
10187 # 'A_MOVED', but 'A_MOVED@3' is 'A', so again this mergeinfo is filtered
10188 # out, leaving the only the mergeinfo created from the merge itself:
10189 # '/A_COPY:7'.
10190 svntest.actions.run_and_verify_svn(None,
10191 ['\n', 'Committed revision 8.\n'],
10192 [], 'move',
10193 sbox.repo_url + '/A',
10194 sbox.repo_url + '/A_MOVED',
10195 '-m', 'Copy A to A_MOVED')
10196 wc_status.remove('A', 'A/B', 'A/B/lambda', 'A/B/E', 'A/B/E/alpha',
10197 'A/B/E/beta', 'A/B/F', 'A/mu', 'A/C', 'A/D', 'A/D/gamma', 'A/D/G',
10198 'A/D/G/pi', 'A/D/G/rho', 'A/D/G/tau', 'A/D/H', 'A/D/H/chi',
10199 'A/D/H/omega', 'A/D/H/psi')
10200 wc_status.add({
10201 'A_MOVED' : Item(),
10202 'A_MOVED/B' : Item(),
10203 'A_MOVED/B/lambda' : Item(),
10204 'A_MOVED/B/E' : Item(),
10205 'A_MOVED/B/E/alpha' : Item(),
10206 'A_MOVED/B/E/beta' : Item(),
10207 'A_MOVED/B/F' : Item(),
10208 'A_MOVED/mu' : Item(),
10209 'A_MOVED/C' : Item(),
10210 'A_MOVED/D' : Item(),
10211 'A_MOVED/D/gamma' : Item(),
10212 'A_MOVED/D/G' : Item(),
10213 'A_MOVED/D/G/pi' : Item(),
10214 'A_MOVED/D/G/rho' : Item(),
10215 'A_MOVED/D/G/tau' : Item(),
10216 'A_MOVED/D/H' : Item(),
10217 'A_MOVED/D/H/chi' : Item(),
10218 'A_MOVED/D/H/omega' : Item(),
10219 'A_MOVED/D/H/psi' : Item(),
10221 wc_status.tweak(wc_rev=8, status=' ')
10222 wc_disk.remove('A', 'A/B', 'A/B/lambda', 'A/B/E', 'A/B/E/alpha',
10223 'A/B/E/beta', 'A/B/F', 'A/mu', 'A/C', 'A/D', 'A/D/gamma',
10224 'A/D/G', 'A/D/G/pi', 'A/D/G/rho', 'A/D/G/tau', 'A/D/H',
10225 'A/D/H/chi', 'A/D/H/omega', 'A/D/H/psi' )
10226 wc_disk.add({
10227 'A_MOVED' : Item(),
10228 'A_MOVED/B' : Item(),
10229 'A_MOVED/B/lambda' : Item("This is the file 'lambda'.\n"),
10230 'A_MOVED/B/E' : Item(),
10231 'A_MOVED/B/E/alpha' : Item("This is the file 'alpha'.\n"),
10232 'A_MOVED/B/E/beta' : Item("New content"),
10233 'A_MOVED/B/F' : Item(),
10234 'A_MOVED/mu' : Item("This is the file 'mu'.\n"),
10235 'A_MOVED/C' : Item(),
10236 'A_MOVED/D' : Item(),
10237 'A_MOVED/D/gamma' : Item("This is the file 'gamma'.\n"),
10238 'A_MOVED/D/G' : Item(),
10239 'A_MOVED/D/G/pi' : Item("This is the file 'pi'.\n"),
10240 'A_MOVED/D/G/rho' : Item("New content"),
10241 'A_MOVED/D/G/tau' : Item("This is the file 'tau'.\n"),
10242 'A_MOVED/D/H' : Item(),
10243 'A_MOVED/D/H/chi' : Item("This is the file 'chi'.\n"),
10244 'A_MOVED/D/H/omega' : Item("New content"),
10245 'A_MOVED/D/H/psi' : Item("New content"),
10247 wc_disk.tweak('A_COPY/D/H/psi', 'A_COPY/mu', contents='New content')
10248 wc_disk.tweak('A_COPY', props={SVN_PROP_MERGEINFO : '/A:3'})
10249 expected_output = wc.State(wc_dir, {
10250 'A' : Item(status='D '),
10251 'A_MOVED' : Item(status='A '),
10252 'A_MOVED/B' : Item(status='A '),
10253 'A_MOVED/B/lambda' : Item(status='A '),
10254 'A_MOVED/B/E' : Item(status='A '),
10255 'A_MOVED/B/E/alpha' : Item(status='A '),
10256 'A_MOVED/B/E/beta' : Item(status='A '),
10257 'A_MOVED/B/F' : Item(status='A '),
10258 'A_MOVED/mu' : Item(status='A '),
10259 'A_MOVED/C' : Item(status='A '),
10260 'A_MOVED/D' : Item(status='A '),
10261 'A_MOVED/D/gamma' : Item(status='A '),
10262 'A_MOVED/D/G' : Item(status='A '),
10263 'A_MOVED/D/G/pi' : Item(status='A '),
10264 'A_MOVED/D/G/rho' : Item(status='A '),
10265 'A_MOVED/D/G/tau' : Item(status='A '),
10266 'A_MOVED/D/H' : Item(status='A '),
10267 'A_MOVED/D/H/chi' : Item(status='A '),
10268 'A_MOVED/D/H/omega' : Item(status='A '),
10269 'A_MOVED/D/H/psi' : Item(status='A ')
10271 svntest.actions.run_and_verify_update(wc_dir,
10272 expected_output,
10273 wc_disk,
10274 wc_status,
10275 None, None, None, None, None,
10276 True)
10278 short_A_MOVED_path = shorten_path_kludge(A_MOVED_path)
10279 expected_output = wc.State(short_A_MOVED_path, {
10280 'mu' : Item(status='U '),
10282 expected_A_status = wc.State(short_A_MOVED_path, {
10283 '' : Item(status=' M', wc_rev=8),
10284 'B' : Item(status=' ', wc_rev=8),
10285 'mu' : Item(status='M ', wc_rev=8),
10286 'B/E' : Item(status=' ', wc_rev=8),
10287 'B/E/alpha' : Item(status=' ', wc_rev=8),
10288 'B/E/beta' : Item(status=' ', wc_rev=8),
10289 'B/lambda' : Item(status=' ', wc_rev=8),
10290 'B/F' : Item(status=' ', wc_rev=8),
10291 'C' : Item(status=' ', wc_rev=8),
10292 'D' : Item(status=' ', wc_rev=8),
10293 'D/G' : Item(status=' ', wc_rev=8),
10294 'D/G/pi' : Item(status=' ', wc_rev=8),
10295 'D/G/rho' : Item(status=' ', wc_rev=8),
10296 'D/G/tau' : Item(status=' ', wc_rev=8),
10297 'D/gamma' : Item(status=' ', wc_rev=8),
10298 'D/H' : Item(status=' ', wc_rev=8),
10299 'D/H/chi' : Item(status=' ', wc_rev=8),
10300 'D/H/psi' : Item(status=' ', wc_rev=8),
10301 'D/H/omega' : Item(status=' ', wc_rev=8),
10303 # We can reuse expected_A_disk from above without change.
10304 os.chdir(svntest.main.work_dir)
10305 svntest.actions.run_and_verify_merge(short_A_MOVED_path, '6', '7',
10306 sbox.repo_url + \
10307 '/A_COPY',
10308 expected_output,
10309 expected_A_disk,
10310 expected_A_status,
10311 expected_A_skip,
10312 None, None, None, None,
10313 None, 1)
10314 os.chdir(saved_cwd)
10316 # Revert all local mods
10317 svntest.actions.run_and_verify_svn(None,
10318 ["Reverted '" + A_MOVED_path + "'\n",
10319 "Reverted '" + mu_MOVED_path + "'\n"],
10320 [], 'revert', '-R', wc_dir)
10322 # Create a new 'A' unrelated to the old 'A' which was moved. Then merge
10323 # r7 from 'A_COPY' to this new 'A'. Since the new 'A' shares no history
10324 # with the mergeinfo 'A@3', the mergeinfo '/A:3' is added and when combined
10325 # with the mergeinfo created from the merge should result in
10326 # '/A:3\n/A_COPY:7'
10328 # Create the new 'A' by exporting the old 'A@1'.
10329 expected_output = svntest.verify.UnorderedOutput(
10330 ["A " + os.path.join(wc_dir, "A") + "\n",
10331 "A " + os.path.join(wc_dir, "A", "B") + "\n",
10332 "A " + os.path.join(wc_dir, "A", "B", "lambda") + "\n",
10333 "A " + os.path.join(wc_dir, "A", "B", "E") + "\n",
10334 "A " + os.path.join(wc_dir, "A", "B", "E", "alpha") + "\n",
10335 "A " + os.path.join(wc_dir, "A", "B", "E", "beta") + "\n",
10336 "A " + os.path.join(wc_dir, "A", "B", "F") + "\n",
10337 "A " + os.path.join(wc_dir, "A", "mu") + "\n",
10338 "A " + os.path.join(wc_dir, "A", "C") + "\n",
10339 "A " + os.path.join(wc_dir, "A", "D") + "\n",
10340 "A " + os.path.join(wc_dir, "A", "D", "gamma") + "\n",
10341 "A " + os.path.join(wc_dir, "A", "D", "G") + "\n",
10342 "A " + os.path.join(wc_dir, "A", "D", "G", "pi") + "\n",
10343 "A " + os.path.join(wc_dir, "A", "D", "G", "rho") + "\n",
10344 "A " + os.path.join(wc_dir, "A", "D", "G", "tau") + "\n",
10345 "A " + os.path.join(wc_dir, "A", "D", "H") + "\n",
10346 "A " + os.path.join(wc_dir, "A", "D", "H", "chi") + "\n",
10347 "A " + os.path.join(wc_dir, "A", "D", "H", "omega") + "\n",
10348 "A " + os.path.join(wc_dir, "A", "D", "H", "psi") + "\n",
10349 "Exported revision 1.\n",]
10351 svntest.actions.run_and_verify_svn(None, expected_output, [],
10352 'export', sbox.repo_url + '/A@1',
10353 A_path)
10354 expected_output = svntest.verify.UnorderedOutput(
10355 ["A " + os.path.join(wc_dir, "A") + "\n",
10356 "A " + os.path.join(wc_dir, "A", "B") + "\n",
10357 "A " + os.path.join(wc_dir, "A", "B", "lambda") + "\n",
10358 "A " + os.path.join(wc_dir, "A", "B", "E") + "\n",
10359 "A " + os.path.join(wc_dir, "A", "B", "E", "alpha") + "\n",
10360 "A " + os.path.join(wc_dir, "A", "B", "E", "beta") + "\n",
10361 "A " + os.path.join(wc_dir, "A", "B", "F") + "\n",
10362 "A " + os.path.join(wc_dir, "A", "mu") + "\n",
10363 "A " + os.path.join(wc_dir, "A", "C") + "\n",
10364 "A " + os.path.join(wc_dir, "A", "D") + "\n",
10365 "A " + os.path.join(wc_dir, "A", "D", "gamma") + "\n",
10366 "A " + os.path.join(wc_dir, "A", "D", "G") + "\n",
10367 "A " + os.path.join(wc_dir, "A", "D", "G", "pi") + "\n",
10368 "A " + os.path.join(wc_dir, "A", "D", "G", "rho") + "\n",
10369 "A " + os.path.join(wc_dir, "A", "D", "G", "tau") + "\n",
10370 "A " + os.path.join(wc_dir, "A", "D", "H") + "\n",
10371 "A " + os.path.join(wc_dir, "A", "D", "H", "chi") + "\n",
10372 "A " + os.path.join(wc_dir, "A", "D", "H", "omega") + "\n",
10373 "A " + os.path.join(wc_dir, "A", "D", "H", "psi") + "\n",]
10375 svntest.actions.run_and_verify_svn(None, expected_output, [],
10376 'add', A_path)
10377 # Commit the new 'A' as r9
10378 expected_output = wc.State(wc_dir, {
10379 'A' : Item(verb='Adding'),
10380 'A/B' : Item(verb='Adding'),
10381 'A/mu' : Item(verb='Adding'),
10382 'A/B/E' : Item(verb='Adding'),
10383 'A/B/E/alpha' : Item(verb='Adding'),
10384 'A/B/E/beta' : Item(verb='Adding'),
10385 'A/B/lambda' : Item(verb='Adding'),
10386 'A/B/F' : Item(verb='Adding'),
10387 'A/C' : Item(verb='Adding'),
10388 'A/D' : Item(verb='Adding'),
10389 'A/D/G' : Item(verb='Adding'),
10390 'A/D/G/pi' : Item(verb='Adding'),
10391 'A/D/G/rho' : Item(verb='Adding'),
10392 'A/D/G/tau' : Item(verb='Adding'),
10393 'A/D/gamma' : Item(verb='Adding'),
10394 'A/D/H' : Item(verb='Adding'),
10395 'A/D/H/chi' : Item(verb='Adding'),
10396 'A/D/H/psi' : Item(verb='Adding'),
10397 'A/D/H/omega' : Item(verb='Adding'),
10399 wc_status.tweak(wc_rev=8)
10400 wc_status.add({
10401 'A' : Item(wc_rev=9),
10402 'A/B' : Item(wc_rev=9),
10403 'A/B/lambda' : Item(wc_rev=9),
10404 'A/B/E' : Item(wc_rev=9),
10405 'A/B/E/alpha' : Item(wc_rev=9),
10406 'A/B/E/beta' : Item(wc_rev=9),
10407 'A/B/F' : Item(wc_rev=9),
10408 'A/mu' : Item(wc_rev=9),
10409 'A/C' : Item(wc_rev=9),
10410 'A/D' : Item(wc_rev=9),
10411 'A/D/gamma' : Item(wc_rev=9),
10412 'A/D/G' : Item(wc_rev=9),
10413 'A/D/G/pi' : Item(wc_rev=9),
10414 'A/D/G/rho' : Item(wc_rev=9),
10415 'A/D/G/tau' : Item(wc_rev=9),
10416 'A/D/H' : Item(wc_rev=9),
10417 'A/D/H/chi' : Item(wc_rev=9),
10418 'A/D/H/omega' : Item(wc_rev=9),
10419 'A/D/H/psi' : Item(wc_rev=9),
10421 wc_status.tweak(status=' ')
10422 svntest.actions.run_and_verify_commit(wc_dir,
10423 expected_output,
10424 wc_status,
10425 None,
10426 wc_dir)
10428 expected_output = wc.State(short_A_path, {
10429 'mu' : Item(status='U '),
10430 'D/H/psi' : Item(status='U '),
10431 '' : Item(status=' U'),
10433 expected_A_status = wc.State(short_A_path, {
10434 '' : Item(status=' M', wc_rev=9),
10435 'B' : Item(status=' ', wc_rev=9),
10436 'mu' : Item(status='M ', wc_rev=9),
10437 'B/E' : Item(status=' ', wc_rev=9),
10438 'B/E/alpha' : Item(status=' ', wc_rev=9),
10439 'B/E/beta' : Item(status=' ', wc_rev=9),
10440 'B/lambda' : Item(status=' ', wc_rev=9),
10441 'B/F' : Item(status=' ', wc_rev=9),
10442 'C' : Item(status=' ', wc_rev=9),
10443 'D' : Item(status=' ', wc_rev=9),
10444 'D/G' : Item(status=' ', wc_rev=9),
10445 'D/G/pi' : Item(status=' ', wc_rev=9),
10446 'D/G/rho' : Item(status=' ', wc_rev=9),
10447 'D/G/tau' : Item(status=' ', wc_rev=9),
10448 'D/gamma' : Item(status=' ', wc_rev=9),
10449 'D/H' : Item(status=' ', wc_rev=9),
10450 'D/H/chi' : Item(status=' ', wc_rev=9),
10451 'D/H/psi' : Item(status='M ', wc_rev=9),
10452 'D/H/omega' : Item(status=' ', wc_rev=9),
10454 expected_A_disk = wc.State('', {
10455 '' : Item(props={SVN_PROP_MERGEINFO : '/A:3\n/A_COPY:7\n'}),
10456 'B' : Item(),
10457 'mu' : Item("New content"),
10458 'B/E' : Item(),
10459 'B/E/alpha' : Item("This is the file 'alpha'.\n"),
10460 'B/E/beta' : Item("This is the file 'beta'.\n"),
10461 'B/lambda' : Item("This is the file 'lambda'.\n"),
10462 'B/F' : Item(),
10463 'C' : Item(),
10464 'D' : Item(),
10465 'D/G' : Item(),
10466 'D/G/pi' : Item("This is the file 'pi'.\n"),
10467 'D/G/rho' : Item("This is the file 'rho'.\n"),
10468 'D/G/tau' : Item("This is the file 'tau'.\n"),
10469 'D/gamma' : Item("This is the file 'gamma'.\n"),
10470 'D/H' : Item(),
10471 'D/H/chi' : Item("This is the file 'chi'.\n"),
10472 'D/H/psi' : Item("New content"),
10473 'D/H/omega' : Item("This is the file 'omega'.\n"),
10475 expected_A_skip = wc.State(short_A_path, {})
10476 os.chdir(svntest.main.work_dir)
10477 svntest.actions.run_and_verify_merge(short_A_path, '6', '7',
10478 sbox.repo_url + \
10479 '/A_COPY',
10480 expected_output,
10481 expected_A_disk,
10482 expected_A_status,
10483 expected_A_skip,
10484 None, None, None, None,
10485 None, 1)
10487 def merge_range_predates_history(sbox):
10488 "merge range predates history (issue #3094)"
10490 sbox.build()
10491 wc_dir = sbox.wc_dir
10493 iota_path = os.path.join(wc_dir, "iota")
10494 trunk_file_path = os.path.join(wc_dir, "trunk", "file")
10495 trunk_url = sbox.repo_url + "/trunk"
10496 branches_url = sbox.repo_url + "/branches"
10497 branch_path = os.path.join(wc_dir, "branches", "branch")
10498 branch_file_path = os.path.join(wc_dir, "branches", "branch", "file")
10499 branch_url = sbox.repo_url + "/branches/branch"
10501 # Tweak a file and commit. (r2)
10502 svntest.main.file_append(iota_path, "More data.\n")
10503 svntest.main.run_svn(None, 'ci', '-m', 'tweak iota', wc_dir)
10505 # Create our trunk and branches directory, and update working copy. (r3)
10506 svntest.main.run_svn(None, 'mkdir', trunk_url, branches_url,
10507 '-m', 'add trunk and branches dirs')
10508 svntest.main.run_svn(None, 'up', wc_dir)
10510 # Add a file to the trunk and commit. (r4)
10511 svntest.main.file_append(trunk_file_path, "This is the file 'file'.\n")
10512 svntest.main.run_svn(None, 'add', trunk_file_path)
10513 svntest.main.run_svn(None, 'ci', '-m', 'add trunk file', wc_dir)
10515 # Branch trunk from r3, and update working copy. (r5)
10516 svntest.main.run_svn(None, 'cp', trunk_url, branch_url, '-r3',
10517 '-m', 'branch trunk@2')
10518 svntest.main.run_svn(None, 'up', wc_dir)
10520 # Now, try to merge trunk into the branch. There should be one
10521 # outstanding change -- the addition of the file.
10522 expected_output = expected_merge_output([[4,5]],
10523 'A ' + branch_file_path + '\n')
10524 svntest.actions.run_and_verify_svn(None, expected_output, [], 'merge',
10525 trunk_url, branch_path)
10528 def foreign_repos(sbox):
10529 "merge from a foreign repository"
10531 sbox.build()
10532 wc_dir = sbox.wc_dir
10534 # Make a copy of this repository and associated working copy. Both
10535 # should have nothing but a Greek tree in them, and the two
10536 # repository UUIDs should differ.
10537 sbox2 = sbox.clone_dependent(True)
10538 sbox2.build()
10539 wc_dir2 = sbox2.wc_dir
10541 # Convenience variables for working copy paths.
10542 Z_path = os.path.join(wc_dir, 'A', 'D', 'G', 'Z')
10543 Q_path = os.path.join(wc_dir, 'Q')
10544 H_path = os.path.join(wc_dir, 'A', 'D', 'H')
10545 iota_path = os.path.join(wc_dir, 'iota')
10546 beta_path = os.path.join(wc_dir, 'A', 'B', 'E', 'beta')
10547 alpha_path = os.path.join(wc_dir, 'A', 'B', 'E', 'alpha')
10548 zeta_path = os.path.join(wc_dir, 'A', 'D', 'G', 'Z', 'zeta')
10549 fred_path = os.path.join(wc_dir, 'A', 'C', 'fred')
10551 # Add new directories
10552 svntest.main.run_svn(None, 'mkdir', Q_path, Z_path)
10554 # Add new files
10555 zeta_contents = "This is the file 'zeta'.\n"
10556 fred_contents = "This is the file 'fred'.\n"
10557 svntest.main.file_append(zeta_path, zeta_contents)
10558 svntest.main.file_append(fred_path, fred_contents)
10559 svntest.main.run_svn(None, 'add', zeta_path, fred_path)
10561 # Modify existing files
10562 added_contents = "This is another line of text.\n"
10563 svntest.main.file_append(iota_path, added_contents)
10564 svntest.main.file_append(beta_path, added_contents)
10566 # Delete some stuff
10567 svntest.main.run_svn(None, 'delete', alpha_path, H_path)
10569 # Commit up these changes.
10570 expected_output = wc.State(wc_dir, {
10571 'Q' : Item(verb='Adding'),
10572 'A/D/G/Z' : Item(verb='Adding'),
10573 'A/D/G/Z/zeta' : Item(verb='Adding'),
10574 'A/C/fred' : Item(verb='Adding'),
10575 'iota' : Item(verb='Sending'),
10576 'A/B/E/beta' : Item(verb='Sending'),
10577 'A/B/E/alpha' : Item(verb='Deleting'),
10578 'A/D/H' : Item(verb='Deleting'),
10580 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
10581 expected_status.add({
10582 'Q' : Item(status=' ', wc_rev=2),
10583 'A/D/G/Z' : Item(status=' ', wc_rev=2),
10584 'A/D/G/Z/zeta' : Item(status=' ', wc_rev=2),
10585 'A/C/fred' : Item(status=' ', wc_rev=2),
10587 expected_status.tweak('iota', 'A/B/E/beta', wc_rev=2)
10588 expected_status.remove('A/B/E/alpha', 'A/D/H', 'A/D/H/chi',
10589 'A/D/H/psi', 'A/D/H/omega')
10590 expected_disk = svntest.main.greek_state.copy()
10591 expected_disk.add({
10592 'Q' : Item(),
10593 'A/D/G/Z' : Item(),
10594 'A/D/G/Z/zeta' : Item(contents=zeta_contents),
10595 'A/C/fred' : Item(contents=fred_contents),
10597 expected_disk.remove('A/B/E/alpha', 'A/D/H', 'A/D/H/chi',
10598 'A/D/H/psi', 'A/D/H/omega')
10599 expected_disk.tweak('iota',
10600 contents=expected_disk.desc['iota'].contents
10601 + added_contents)
10602 expected_disk.tweak('A/B/E/beta',
10603 contents=expected_disk.desc['A/B/E/beta'].contents
10604 + added_contents)
10605 svntest.actions.run_and_verify_commit(wc_dir,
10606 expected_output,
10607 expected_status,
10608 None,
10609 wc_dir)
10610 svntest.actions.verify_disk(wc_dir, expected_disk,
10611 None, None, None, None, 1)
10613 # Now, merge our committed revision into a working copy of another
10614 # repository. Not only should the merge succeed, but the results on
10615 # disk should match those in our first working copy.
10617 ### TODO: Use run_and_verify_merge() ###
10618 svntest.main.run_svn(None, 'merge', '-c2', sbox.repo_url, wc_dir2)
10619 svntest.main.run_svn(None, 'ci', '-m', 'Merge from foreign repos', wc_dir2)
10620 svntest.actions.verify_disk(wc_dir2, expected_disk,
10621 None, None, None, None, 1)
10624 def foreign_repos_2_url(sbox):
10625 "2-url merge from a foreign repository"
10627 sbox.build()
10628 wc_dir = sbox.wc_dir
10630 # Make a copy of this repository and associated working copy. Both
10631 # should have nothing but a Greek tree in them, and the two
10632 # repository UUIDs should differ.
10633 sbox2 = sbox.clone_dependent(True)
10634 sbox2.build()
10635 wc_dir2 = sbox2.wc_dir
10637 # Convenience variables for working copy paths.
10638 Z_path = os.path.join(wc_dir, 'A', 'D', 'G', 'Z')
10639 Q_path = os.path.join(wc_dir, 'A', 'Q')
10640 H_path = os.path.join(wc_dir, 'A', 'D', 'H')
10641 beta_path = os.path.join(wc_dir, 'A', 'B', 'E', 'beta')
10642 alpha_path = os.path.join(wc_dir, 'A', 'B', 'E', 'alpha')
10643 zeta_path = os.path.join(wc_dir, 'A', 'D', 'G', 'Z', 'zeta')
10644 fred_path = os.path.join(wc_dir, 'A', 'C', 'fred')
10646 # First, "tag" the current state of the repository.
10647 svntest.main.run_svn(None, 'copy', sbox.repo_url + '/A',
10648 sbox.repo_url + '/A-tag1', '-m', 'tag1')
10650 # Add new directories
10651 svntest.main.run_svn(None, 'mkdir', Q_path, Z_path)
10653 # Add new files
10654 zeta_contents = "This is the file 'zeta'.\n"
10655 fred_contents = "This is the file 'fred'.\n"
10656 svntest.main.file_append(zeta_path, zeta_contents)
10657 svntest.main.file_append(fred_path, fred_contents)
10658 svntest.main.run_svn(None, 'add', zeta_path, fred_path)
10660 # Modify existing files
10661 added_contents = "This is another line of text.\n"
10662 svntest.main.file_append(beta_path, added_contents)
10664 # Delete some stuff
10665 svntest.main.run_svn(None, 'delete', alpha_path, H_path)
10667 # Commit up these changes.
10668 expected_output = wc.State(wc_dir, {
10669 'A/Q' : Item(verb='Adding'),
10670 'A/D/G/Z' : Item(verb='Adding'),
10671 'A/D/G/Z/zeta' : Item(verb='Adding'),
10672 'A/C/fred' : Item(verb='Adding'),
10673 'A/B/E/beta' : Item(verb='Sending'),
10674 'A/B/E/alpha' : Item(verb='Deleting'),
10675 'A/D/H' : Item(verb='Deleting'),
10677 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
10678 expected_status.add({
10679 'A/Q' : Item(status=' ', wc_rev=3),
10680 'A/D/G/Z' : Item(status=' ', wc_rev=3),
10681 'A/D/G/Z/zeta' : Item(status=' ', wc_rev=3),
10682 'A/C/fred' : Item(status=' ', wc_rev=3),
10684 expected_status.tweak('A/B/E/beta', wc_rev=3)
10685 expected_status.remove('A/B/E/alpha', 'A/D/H', 'A/D/H/chi',
10686 'A/D/H/psi', 'A/D/H/omega')
10687 expected_disk = svntest.main.greek_state.copy()
10688 expected_disk.add({
10689 'A/Q' : Item(),
10690 'A/D/G/Z' : Item(),
10691 'A/D/G/Z/zeta' : Item(contents=zeta_contents),
10692 'A/C/fred' : Item(contents=fred_contents),
10694 expected_disk.remove('A/B/E/alpha', 'A/D/H', 'A/D/H/chi',
10695 'A/D/H/psi', 'A/D/H/omega')
10696 expected_disk.tweak('A/B/E/beta',
10697 contents=expected_disk.desc['A/B/E/beta'].contents
10698 + added_contents)
10699 svntest.actions.run_and_verify_commit(wc_dir,
10700 expected_output,
10701 expected_status,
10702 None,
10703 wc_dir)
10704 svntest.actions.verify_disk(wc_dir, expected_disk,
10705 None, None, None, None, 1)
10707 # Now, "tag" the new state of the repository.
10708 svntest.main.run_svn(None, 'copy', sbox.repo_url + '/A',
10709 sbox.repo_url + '/A-tag2', '-m', 'tag2')
10711 # Now, merge across our "tags" (copies of /A) into the /A of a
10712 # working copy of another repository. Not only should the merge
10713 # succeed, but the results on disk should match those in our first
10714 # working copy.
10716 ### TODO: Use run_and_verify_merge() ###
10717 svntest.main.run_svn(None, 'merge', sbox.repo_url + '/A-tag1',
10718 sbox.repo_url + '/A-tag2',
10719 os.path.join(wc_dir2, 'A'))
10720 svntest.main.run_svn(None, 'ci', '-m', 'Merge from foreign repos', wc_dir2)
10721 svntest.actions.verify_disk(wc_dir2, expected_disk,
10722 None, None, None, None, 1)
10725 def merge_added_subtree(sbox):
10726 "merge added subtree"
10728 # The result of a subtree added by copying
10729 # or merging an added subtree, should be the same on disk
10730 ### with the exception of mergeinfo?!
10732 # test for issue 1962
10733 sbox.build()
10734 wc_dir = sbox.wc_dir
10735 url = sbox.repo_url
10737 # make a branch of A
10738 # svn cp A A_COPY
10739 A_url = url + "/A"
10740 A_COPY_url = url + "/A_COPY"
10741 A_path = os.path.join(wc_dir, "A")
10743 svntest.actions.run_and_verify_svn("",["\n", "Committed revision 2.\n"], [],
10744 "cp", "-m", "", A_url, A_COPY_url)
10745 svntest.actions.run_and_verify_svn("",["\n", "Committed revision 3.\n"], [],
10746 "cp", "-m", "",
10747 A_COPY_url + '/D',
10748 A_COPY_url + '/D2')
10749 expected_output = wc.State(A_path, {
10750 'D2' : Item(status='A '),
10751 'D2/gamma' : Item(status='A '),
10752 'D2/H/' : Item(status='A '),
10753 'D2/H/chi' : Item(status='A '),
10754 'D2/H/psi' : Item(status='A '),
10755 'D2/H/omega': Item(status='A '),
10756 'D2/G/' : Item(status='A '),
10757 'D2/G/pi' : Item(status='A '),
10758 'D2/G/rho' : Item(status='A '),
10759 'D2/G/tau' : Item(status='A ')
10762 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
10763 expected_status.add({
10764 'A/D2' : Item(status='A ', copied='+', wc_rev='-'),
10765 'A/D2/gamma' : Item(status=' ', copied='+', wc_rev='-'),
10766 'A/D2/H/' : Item(status=' ', copied='+', wc_rev='-'),
10767 'A/D2/H/chi' : Item(status=' ', copied='+', wc_rev='-'),
10768 'A/D2/H/psi' : Item(status=' ', copied='+', wc_rev='-'),
10769 'A/D2/H/omega': Item(status=' ', copied='+', wc_rev='-'),
10770 'A/D2/G/' : Item(status=' ', copied='+', wc_rev='-'),
10771 'A/D2/G/pi' : Item(status=' ', copied='+', wc_rev='-'),
10772 'A/D2/G/rho' : Item(status=' ', copied='+', wc_rev='-'),
10773 'A/D2/G/tau' : Item(status=' ', copied='+', wc_rev='-')
10775 expected_status.remove('', 'iota')
10777 expected_skip = wc.State('', {})
10778 expected_disk = svntest.main.greek_state.subtree("A")
10779 dest_name = ''
10780 expected_disk.add({
10781 dest_name + 'D2' : Item(),
10782 dest_name + 'D2/gamma' : Item("This is the file 'gamma'.\n"),
10783 dest_name + 'D2/G' : Item(),
10784 dest_name + 'D2/G/pi' : Item("This is the file 'pi'.\n"),
10785 dest_name + 'D2/G/rho' : Item("This is the file 'rho'.\n"),
10786 dest_name + 'D2/G/tau' : Item("This is the file 'tau'.\n"),
10787 dest_name + 'D2/H' : Item(),
10788 dest_name + 'D2/H/chi' : Item("This is the file 'chi'.\n"),
10789 dest_name + 'D2/H/omega' : Item("This is the file 'omega'.\n"),
10790 dest_name + 'D2/H/psi' : Item("This is the file 'psi'.\n")
10793 # Using the above information, verify a REPO->WC copy
10794 svntest.actions.run_and_verify_svn("", None, [],
10795 "cp", A_COPY_url + '/D2',
10796 os.path.join(A_path, "D2"))
10797 actual_tree = svntest.tree.build_tree_from_wc (A_path, 0)
10798 svntest.tree.compare_trees (actual_tree, expected_disk.old_tree(),
10799 None, None, None, None)
10800 svntest.actions.run_and_verify_status(A_path, expected_status)
10802 # Remove the copy artifacts
10803 svntest.actions.run_and_verify_svn("", None, [],
10804 "revert", "-R", A_path)
10805 svntest.main.safe_rmtree(os.path.join(A_path, "D2"))
10807 # Add merge-tracking differences between copying and merging
10808 # Verify a merge using the otherwise unchanged disk and status trees
10809 expected_status.tweak('A',status=' M')
10810 svntest.actions.run_and_verify_merge(A_path, 2, 3, A_COPY_url,
10811 expected_output, expected_disk,
10812 expected_status, expected_skip)
10814 #----------------------------------------------------------------------
10815 # Issue #3138
10816 def merge_unknown_url(sbox):
10817 "merging an unknown url should return error"
10819 sbox.build()
10820 wc_dir = sbox.wc_dir
10822 # remove a path from the repo and commit.
10823 iota_path = os.path.join(wc_dir, 'iota')
10824 svntest.actions.run_and_verify_svn(None, None, [], 'rm', iota_path)
10825 svntest.actions.run_and_verify_svn("", None, [],
10826 "ci", wc_dir, "-m", "log message")
10829 url = sbox.repo_url + "/iota"
10830 expected_err = ".*File not found.*iota.*|.*iota.*path not found.*"
10831 svntest.actions.run_and_verify_svn("", None, expected_err,
10832 "merge", url, wc_dir)
10834 ########################################################################
10835 # Run the tests
10838 # list all tests here, starting with None:
10839 test_list = [ None,
10840 SkipUnless(textual_merges_galore,
10841 server_has_mergeinfo),
10842 SkipUnless(add_with_history,
10843 server_has_mergeinfo),
10844 SkipUnless(delete_file_and_dir,
10845 server_has_mergeinfo),
10846 SkipUnless(simple_property_merges,
10847 server_has_mergeinfo),
10848 merge_with_implicit_target_using_r,
10849 merge_with_implicit_target_using_c,
10850 merge_with_implicit_target_and_revs,
10851 SkipUnless(merge_catches_nonexistent_target,
10852 server_has_mergeinfo),
10853 SkipUnless(merge_tree_deleted_in_target,
10854 server_has_mergeinfo),
10855 merge_similar_unrelated_trees,
10856 merge_with_prev,
10857 SkipUnless(merge_binary_file,
10858 server_has_mergeinfo),
10859 three_way_merge_add_of_existing_binary_file,
10860 SkipUnless(merge_one_file_using_r,
10861 server_has_mergeinfo),
10862 SkipUnless(merge_one_file_using_c,
10863 server_has_mergeinfo),
10864 SkipUnless(merge_one_file_using_implicit_revs,
10865 server_has_mergeinfo),
10866 SkipUnless(merge_record_only,
10867 server_has_mergeinfo),
10868 SkipUnless(merge_in_new_file_and_diff,
10869 server_has_mergeinfo),
10870 SkipUnless(merge_skips_obstructions,
10871 server_has_mergeinfo),
10872 SkipUnless(merge_into_missing,
10873 server_has_mergeinfo),
10874 SkipUnless(dry_run_adds_file_with_prop,
10875 server_has_mergeinfo),
10876 merge_binary_with_common_ancestry,
10877 SkipUnless(merge_funny_chars_on_path,
10878 server_has_mergeinfo),
10879 merge_keyword_expansions,
10880 merge_prop_change_to_deleted_target,
10881 merge_file_with_space_in_its_name,
10882 merge_dir_branches,
10883 SkipUnless(safe_property_merge,
10884 server_has_mergeinfo),
10885 SkipUnless(property_merge_from_branch,
10886 server_has_mergeinfo),
10887 property_merge_undo_redo,
10888 SkipUnless(cherry_pick_text_conflict,
10889 server_has_mergeinfo),
10890 merge_file_replace,
10891 SkipUnless(merge_dir_replace,
10892 server_has_mergeinfo),
10893 XFail(merge_dir_and_file_replace),
10894 merge_file_replace_to_mixed_rev_wc,
10895 merge_added_dir_to_deleted_in_target,
10896 SkipUnless(merge_ignore_whitespace,
10897 server_has_mergeinfo),
10898 SkipUnless(merge_ignore_eolstyle,
10899 server_has_mergeinfo),
10900 SkipUnless(merge_add_over_versioned_file_conflicts,
10901 server_has_mergeinfo),
10902 SkipUnless(merge_conflict_markers_matching_eol,
10903 server_has_mergeinfo),
10904 SkipUnless(merge_eolstyle_handling,
10905 server_has_mergeinfo),
10906 SkipUnless(avoid_repeated_merge_using_inherited_merge_info,
10907 server_has_mergeinfo),
10908 SkipUnless(avoid_repeated_merge_on_subtree_with_merge_info,
10909 server_has_mergeinfo),
10910 SkipUnless(obey_reporter_api_semantics_while_doing_subtree_merges,
10911 server_has_mergeinfo),
10912 SkipUnless(mergeinfo_inheritance,
10913 server_has_mergeinfo),
10914 SkipUnless(mergeinfo_elision,
10915 server_has_mergeinfo),
10916 SkipUnless(mergeinfo_inheritance_and_discontinuous_ranges,
10917 server_has_mergeinfo),
10918 SkipUnless(merge_to_target_with_copied_children,
10919 server_has_mergeinfo),
10920 SkipUnless(merge_to_switched_path,
10921 server_has_mergeinfo),
10922 SkipUnless(merge_to_path_with_switched_children,
10923 server_has_mergeinfo),
10924 merge_with_implicit_target_file,
10925 SkipUnless(empty_mergeinfo,
10926 server_has_mergeinfo),
10927 SkipUnless(prop_add_to_child_with_mergeinfo,
10928 server_has_mergeinfo),
10929 diff_repos_does_not_update_mergeinfo,
10930 XFail(avoid_reflected_revs),
10931 SkipUnless(update_loses_mergeinfo,
10932 server_has_mergeinfo),
10933 SkipUnless(merge_loses_mergeinfo,
10934 server_has_mergeinfo),
10935 single_file_replace_style_merge_capability,
10936 SkipUnless(merge_to_out_of_date_target,
10937 server_has_mergeinfo),
10938 SkipUnless(merge_with_depth_files,
10939 server_has_mergeinfo),
10940 SkipUnless(merge_fails_if_subtree_is_deleted_on_src,
10941 server_has_mergeinfo),
10942 SkipUnless(merge_away_subtrees_noninheritable_ranges,
10943 server_has_mergeinfo),
10944 SkipUnless(merge_to_sparse_directories,
10945 server_has_mergeinfo),
10946 SkipUnless(merge_old_and_new_revs_from_renamed_dir,
10947 server_has_mergeinfo),
10948 SkipUnless(merge_with_child_having_different_rev_ranges_to_merge,
10949 server_has_mergeinfo),
10950 SkipUnless(merge_old_and_new_revs_from_renamed_file,
10951 server_has_mergeinfo),
10952 SkipUnless(merge_with_auto_rev_range_detection,
10953 server_has_mergeinfo),
10954 SkipUnless(mergeinfo_recording_in_skipped_merge,
10955 server_has_mergeinfo),
10956 SkipUnless(cherry_picking,
10957 server_has_mergeinfo),
10958 SkipUnless(propchange_of_subdir_raises_conflict,
10959 server_has_mergeinfo),
10960 SkipUnless(reverse_merge_prop_add_on_child,
10961 server_has_mergeinfo),
10962 XFail(merge_target_with_non_inheritable_mergeinfo),
10963 self_reverse_merge,
10964 SkipUnless(ignore_ancestry_and_mergeinfo,
10965 server_has_mergeinfo),
10966 SkipUnless(merge_from_renamed_branch_fails_while_avoiding_repeat_merge,
10967 server_has_mergeinfo),
10968 SkipUnless(merge_source_normalization_and_subtree_merges,
10969 server_has_mergeinfo),
10970 SkipUnless(new_subtrees_should_not_break_merge,
10971 server_has_mergeinfo),
10972 SkipUnless(basic_reintegrate,
10973 server_has_mergeinfo),
10974 XFail(reintegrate_with_rename),
10975 XFail(reintegrate_branch_never_merged_to),
10976 reintegrate_fail_on_modified_wc,
10977 reintegrate_fail_on_mixed_rev_wc,
10978 reintegrate_fail_on_switched_wc,
10979 reintegrate_fail_on_shallow_wc,
10980 SkipUnless(XFail(reintegrate_fail_on_stale_source),
10981 server_has_mergeinfo),
10982 SkipUnless(dont_add_mergeinfo_from_own_history,
10983 server_has_mergeinfo),
10984 merge_range_predates_history,
10985 foreign_repos,
10986 foreign_repos_2_url,
10987 XFail(merge_added_subtree),
10988 SkipUnless(merge_unknown_url,
10989 server_has_mergeinfo),
10992 if __name__ == '__main__':
10993 svntest.main.run_tests(test_list)
10994 # NOTREACHED
10997 ### End of file.