Minor tweak to XFail merge test for issue #3067.
[svn.git] / subversion / tests / cmdline / merge_tests.py
blobe4db737eec0e10f2d3c0254c705f18801cfc4637
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, foreign=False):
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, foreign)]
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,
63 True, foreign)]
64 if isinstance(additional_lines, list):
65 # Address "The Backslash Plague"
67 # If ADDITIONAL_LINES are present there are possibly paths in it with
68 # multiple components and on Windows these components are separated with
69 # '\'. These need to be escaped properly in the regexp for the match to
70 # work correctly. See http://aspn.activestate.com/ASPN/docs/ActivePython
71 # /2.2/howto/regex/regex.html#SECTION000420000000000000000.
72 if sys.platform == 'win32':
73 for i in range(0, len(additional_lines)):
74 additional_lines[i] = additional_lines[i].replace("\\", "\\\\")
75 lines.extend(additional_lines)
76 else:
77 if sys.platform == 'win32' and additional_lines != None:
78 additional_lines = additional_lines.replace("\\", "\\\\")
79 lines.append(str(additional_lines))
80 return "|".join(lines)
82 ######################################################################
83 # Tests
85 # Each test must return on success or raise on failure.
88 #----------------------------------------------------------------------
90 def textual_merges_galore(sbox):
91 "performing a merge, with mixed results"
93 ## The Plan:
95 ## The goal is to test that "svn merge" does the right thing in the
96 ## following cases:
98 ## 1 : _ : Received changes already present in unmodified local file
99 ## 2 : U : No local mods, received changes folded in without trouble
100 ## 3 : G : Received changes already exist as local mods
101 ## 4 : G : Received changes do not conflict with local mods
102 ## 5 : C : Received changes conflict with local mods
104 ## So first modify these files and commit:
106 ## Revision 2:
107 ## -----------
108 ## A/mu ............... add ten or so lines
109 ## A/D/G/rho .......... add ten or so lines
111 ## Now check out an "other" working copy, from revision 2.
113 ## Next further modify and commit some files from the original
114 ## working copy:
116 ## Revision 3:
117 ## -----------
118 ## A/B/lambda ......... add ten or so lines
119 ## A/D/G/pi ........... add ten or so lines
120 ## A/D/G/tau .......... add ten or so lines
121 ## A/D/G/rho .......... add an additional ten or so lines
123 ## In the other working copy (which is at rev 2), update rho back
124 ## to revision 1, while giving other files local mods. This sets
125 ## things up so that "svn merge -r 1:3" will test all of the above
126 ## cases except case 4:
128 ## case 1: A/mu .......... do nothing, the only change was in rev 2
129 ## case 2: A/B/lambda .... do nothing, so we accept the merge easily
130 ## case 3: A/D/G/pi ...... add same ten lines as committed in rev 3
131 ## case 5: A/D/G/tau ..... add ten or so lines at the end
132 ## [none]: A/D/G/rho ..... ignore what happens to this file for now
134 ## Now run
136 ## $ cd wc.other
137 ## $ svn merge -r 1:3 url-to-repo
139 ## ...and expect the right output.
141 ## Now revert rho, then update it to revision 2, then *prepend* a
142 ## bunch of lines, which will be separated by enough distance from
143 ## the changes about to be received that the merge will be clean.
145 ## $ cd wc.other/A/D/G
146 ## $ svn merge -r 2:3 url-to-repo/A/D/G
148 ## Which tests case 4. (Ignore the changes to the other files,
149 ## we're only interested in rho here.)
151 sbox.build()
152 wc_dir = sbox.wc_dir
153 # url = os.path.join(svntest.main.test_area_url, sbox.repo_dir)
155 # Change mu and rho for revision 2
156 mu_path = os.path.join(wc_dir, 'A', 'mu')
157 rho_path = os.path.join(wc_dir, 'A', 'D', 'G', 'rho')
158 mu_text = fill_file_with_lines(mu_path, 2)
159 rho_text = fill_file_with_lines(rho_path, 2)
161 # Create expected output tree for initial commit
162 expected_output = wc.State(wc_dir, {
163 'A/mu' : Item(verb='Sending'),
164 'A/D/G/rho' : Item(verb='Sending'),
167 # Create expected status tree; all local revisions should be at 1,
168 # but mu and rho should be at revision 2.
169 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
170 expected_status.tweak('A/mu', 'A/D/G/rho', wc_rev=2)
172 # Initial commit.
173 svntest.actions.run_and_verify_commit(wc_dir,
174 expected_output,
175 expected_status,
176 None,
177 wc_dir)
179 # Make the "other" working copy
180 other_wc = sbox.add_wc_path('other')
181 svntest.actions.duplicate_dir(wc_dir, other_wc)
183 # Now commit some more mods from the original working copy, to
184 # produce revision 3.
185 lambda_path = os.path.join(wc_dir, 'A', 'B', 'lambda')
186 pi_path = os.path.join(wc_dir, 'A', 'D', 'G', 'pi')
187 tau_path = os.path.join(wc_dir, 'A', 'D', 'G', 'tau')
189 lambda_text = fill_file_with_lines(lambda_path, 2)
190 pi_text = fill_file_with_lines(pi_path, 2)
191 tau_text = fill_file_with_lines(tau_path, 2)
192 additional_rho_text = fill_file_with_lines(rho_path, 2)
194 # Created expected output tree for 'svn ci'
195 expected_output = wc.State(wc_dir, {
196 'A/B/lambda' : Item(verb='Sending'),
197 'A/D/G/pi' : Item(verb='Sending'),
198 'A/D/G/tau' : Item(verb='Sending'),
199 'A/D/G/rho' : Item(verb='Sending'),
202 # Create expected status tree.
203 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
204 expected_status.tweak('A/mu', wc_rev=2)
205 expected_status.tweak('A/B/lambda', 'A/D/G/pi', 'A/D/G/tau', 'A/D/G/rho',
206 wc_rev=3)
208 # Commit revision 3.
209 svntest.actions.run_and_verify_commit(wc_dir,
210 expected_output,
211 expected_status,
212 None,
213 wc_dir)
215 # Make local mods in wc.other
216 other_pi_path = os.path.join(other_wc, 'A', 'D', 'G', 'pi')
217 other_rho_path = os.path.join(other_wc, 'A', 'D', 'G', 'rho')
218 other_tau_path = os.path.join(other_wc, 'A', 'D', 'G', 'tau')
220 # For A/mu and A/B/lambda, we do nothing. For A/D/G/pi, we add the
221 # same ten lines as were already committed in revision 3.
222 # (Remember, wc.other is only at revision 2, so it doesn't have
223 # these changes.)
224 svntest.main.file_append(other_pi_path, pi_text)
226 # We skip A/D/G/rho in this merge; it will be tested with a separate
227 # merge command. Temporarily put it back to revision 1, so this
228 # merge succeeds cleanly.
229 svntest.actions.run_and_verify_svn(None, None, [],
230 'up', '-r', '1', other_rho_path)
232 # For A/D/G/tau, we append ten different lines, to conflict with the
233 # ten lines appended in revision 3.
234 other_tau_text = fill_file_with_lines(other_tau_path, 2,
235 line_descrip="Conflicting line")
237 # Do the first merge, revs 1:3. This tests all the cases except
238 # case 4, which we'll handle in a second pass.
239 expected_output = wc.State(other_wc, {'A/B/lambda' : Item(status='U '),
240 'A/D/G/rho' : Item(status='U '),
241 'A/D/G/tau' : Item(status='C '),
244 expected_disk = svntest.main.greek_state.copy()
245 expected_disk.tweak('A/mu',
246 contents=expected_disk.desc['A/mu'].contents
247 + mu_text)
248 expected_disk.tweak('A/B/lambda',
249 contents=expected_disk.desc['A/B/lambda'].contents
250 + lambda_text)
251 expected_disk.tweak('A/D/G/rho',
252 contents=expected_disk.desc['A/D/G/rho'].contents
253 + rho_text + additional_rho_text)
254 expected_disk.tweak('A/D/G/pi',
255 contents=expected_disk.desc['A/D/G/pi'].contents
256 + pi_text)
258 expected_status = svntest.actions.get_virginal_state(other_wc, 1)
259 expected_status.tweak('', status=' M')
260 expected_status.tweak('A/mu', wc_rev=2)
261 expected_status.tweak('A/B/lambda', status='M ')
262 expected_status.tweak('A/D/G/pi', status='M ')
263 expected_status.tweak('A/D/G/rho', status='M ')
265 inject_conflict_into_expected_state('A/D/G/tau', expected_disk,
266 expected_status, other_tau_text, tau_text,
269 expected_skip = wc.State('', { })
271 tau_conflict_support_files = ["tau\.working",
272 "tau\.merge-right\.r3",
273 "tau\.merge-left\.r1"]
275 svntest.actions.run_and_verify_merge(other_wc, '1', '3',
276 sbox.repo_url,
277 expected_output,
278 expected_disk,
279 expected_status,
280 expected_skip,
281 None,
282 svntest.tree.detect_conflict_files,
283 list(tau_conflict_support_files))
285 # Now reverse merge r3 into A/D/G/rho, give it non-conflicting local
286 # mods, then merge in the 2:3 change. ### Not bothering to do the
287 # whole expected_foo routine for these intermediate operations;
288 # they're not what we're here to test, after all, so it's enough to
289 # know that they worked. Is this a bad practice? ###
291 # run_and_verify_merge doesn't support merging to a file WCPATH
292 # so use run_and_verify_svn.
293 svntest.actions.run_and_verify_svn(None,
294 expected_merge_output([[-3]], 'G ' +
295 other_rho_path +
296 '\n'),
297 [], 'merge', '-c-3',
298 sbox.repo_url + '/A/D/G/rho',
299 other_rho_path)
301 # Now *prepend* ten or so lines to A/D/G/rho. Since rho had ten
302 # lines appended in revision 2, and then another ten in revision 3,
303 # these new local mods will be separated from the rev 3 changes by
304 # enough distance that they won't conflict, so the merge should be
305 # clean.
306 other_rho_text = ""
307 for x in range(1,10):
308 other_rho_text = other_rho_text + 'Unobtrusive line ' + `x` + ' in rho\n'
309 current_other_rho_text = svntest.main.file_read(other_rho_path)
310 svntest.main.file_write(other_rho_path,
311 other_rho_text + current_other_rho_text)
313 # We expect no merge attempt for pi and tau because they inherit
314 # mergeinfo from the WC root. There is explicit mergeinfo on rho
315 # ('/A/D/G/rho:2') so expect it to be merged (cleanly).
316 expected_output = wc.State(os.path.join(other_wc, 'A', 'D', 'G'),
317 {'rho' : Item(status='G ')})
318 expected_disk = wc.State("", {
319 'pi' : Item("This is the file 'pi'.\n"),
320 'rho' : Item("This is the file 'rho'.\n"),
321 'tau' : Item("This is the file 'tau'.\n"),
323 expected_disk.tweak('rho',
324 contents=other_rho_text
325 + expected_disk.desc['rho'].contents
326 + rho_text
327 + additional_rho_text)
328 expected_disk.tweak('pi',
329 contents=expected_disk.desc['pi'].contents
330 + pi_text)
332 expected_status = wc.State(os.path.join(other_wc, 'A', 'D', 'G'),
333 { '' : Item(wc_rev=1, status=' '),
334 'rho' : Item(wc_rev=1, status='M '),
335 'pi' : Item(wc_rev=1, status='M '),
336 'tau' : Item(wc_rev=1, status='C '),
339 inject_conflict_into_expected_state('tau', expected_disk, expected_status,
340 other_tau_text, tau_text, 3)
342 # Do the merge, but check svn:mergeinfo props separately since
343 # run_and_verify_merge would attempt to proplist tau's conflict
344 # files if we asked it to check props.
345 svntest.actions.run_and_verify_merge(
346 os.path.join(other_wc, 'A', 'D', 'G'),
347 '2', '3',
348 sbox.repo_url + '/A/D/G',
349 expected_output,
350 expected_disk,
351 expected_status,
352 expected_skip,
353 None,
354 svntest.tree.detect_conflict_files, list(tau_conflict_support_files))
357 svntest.actions.run_and_verify_svn(None, [], [],
358 'propget', SVN_PROP_MERGEINFO,
359 os.path.join(other_wc,
360 "A", "D", "G", "rho"))
363 #----------------------------------------------------------------------
365 # Merge should copy-with-history when adding files or directories
367 def add_with_history(sbox):
368 "merge and add new files/dirs with history"
370 sbox.build()
371 wc_dir = sbox.wc_dir
373 C_path = os.path.join(wc_dir, 'A', 'C')
374 F_path = os.path.join(wc_dir, 'A', 'B', 'F')
375 F_url = sbox.repo_url + '/A/B/F'
377 Q_path = os.path.join(F_path, 'Q')
378 Q2_path = os.path.join(F_path, 'Q2')
379 foo_path = os.path.join(F_path, 'foo')
380 foo2_path = os.path.join(F_path, 'foo2')
381 bar_path = os.path.join(F_path, 'Q', 'bar')
382 bar2_path = os.path.join(F_path, 'Q', 'bar2')
384 svntest.main.run_svn(None, 'mkdir', Q_path)
385 svntest.main.run_svn(None, 'mkdir', Q2_path)
386 svntest.main.file_append(foo_path, "foo")
387 svntest.main.file_append(foo2_path, "foo2")
388 svntest.main.file_append(bar_path, "bar")
389 svntest.main.file_append(bar2_path, "bar2")
390 svntest.main.run_svn(None, 'add', foo_path, foo2_path, bar_path, bar2_path)
391 svntest.main.run_svn(None, 'propset', 'x', 'x', Q2_path)
392 svntest.main.run_svn(None, 'propset', 'y', 'y', foo2_path)
393 svntest.main.run_svn(None, 'propset', 'z', 'z', bar2_path)
395 expected_output = wc.State(wc_dir, {
396 'A/B/F/Q' : Item(verb='Adding'),
397 'A/B/F/Q2' : Item(verb='Adding'),
398 'A/B/F/Q/bar' : Item(verb='Adding'),
399 'A/B/F/Q/bar2': Item(verb='Adding'),
400 'A/B/F/foo' : Item(verb='Adding'),
401 'A/B/F/foo2' : Item(verb='Adding'),
403 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
404 expected_status.add({
405 'A/B/F/Q' : Item(status=' ', wc_rev=2),
406 'A/B/F/Q2' : Item(status=' ', wc_rev=2),
407 'A/B/F/Q/bar' : Item(status=' ', wc_rev=2),
408 'A/B/F/Q/bar2': Item(status=' ', wc_rev=2),
409 'A/B/F/foo' : Item(status=' ', wc_rev=2),
410 'A/B/F/foo2' : Item(status=' ', wc_rev=2),
412 svntest.actions.run_and_verify_commit(wc_dir,
413 expected_output,
414 expected_status,
415 None,
416 wc_dir)
418 ### "The Merge Kluge"
420 ### *****************************************************
421 ### *** ***
422 ### *** Before erasing this comment, please check ***
423 ### *** for references to "The Merge Kluge" ***
424 ### *** elsewhere in this file, update_tests.py ***
425 ### *** and switch_tests.py. ***
426 ### *** ***
427 ### *****************************************************
429 ### The shortening of C_path and the chdir() below are a kluge to
430 ### work around
432 ### http://subversion.tigris.org/issues/show_bug.cgi?id=767#desc16
434 ### Note that the problem isn't simply that 'svn merge' sometimes
435 ### puts temp files in cwd. That's bad enough, but even if svn
436 ### were to choose /tmp or some other static place blessed by
437 ### apr_get_temp_dir(), we'd still experience the error
439 ### svn: Move failed
440 ### svn: Can't move 'tmp.2' to '.../.svn/tmp/text-base/file1.svn-base':
441 ### Invalid cross-device link
443 ### when running the tests on a ramdisk. After all, there's no
444 ### reason why apr_get_temp_dir() would return a path inside
445 ### svn-test-work/, which is the mount point for the ramdisk.
447 ### http://subversion.tigris.org/issues/show_bug.cgi?id=767#desc20
448 ### starts a discussion on how to solve this in Subversion itself.
449 ### However, until that's settled, we still want to be able to run
450 ### the tests in a ramdisk, hence this kluge.
452 short_C_path = shorten_path_kludge(C_path)
453 expected_output = wc.State(short_C_path, {
454 'Q' : Item(status='A '),
455 'Q2' : Item(status='A '),
456 'Q/bar' : Item(status='A '),
457 'Q/bar2' : Item(status='A '),
458 'foo' : Item(status='A '),
459 'foo2' : Item(status='A '),
461 expected_disk = wc.State('', {
462 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B/F:2'}),
463 'Q' : Item(),
464 'Q2' : Item(props={'x' : 'x'}),
465 'Q/bar' : Item("bar"),
466 'Q/bar2' : Item("bar2", props={'z' : 'z'}),
467 'foo' : Item("foo"),
468 'foo2' : Item("foo2", props={'y' : 'y'}),
470 expected_status = wc.State(short_C_path, {
471 '' : Item(status=' M', wc_rev=1),
472 'Q' : Item(status='A ', wc_rev='-', copied='+'),
473 'Q2' : Item(status='A ', wc_rev='-', copied='+'),
474 'Q/bar' : Item(status='A ', wc_rev='-', copied='+'),
475 'Q/bar2' : Item(status='A ', wc_rev='-', copied='+'),
476 'foo' : Item(status='A ', wc_rev='-', copied='+'),
477 'foo2' : Item(status='A ', wc_rev='-', copied='+'),
479 expected_skip = wc.State(short_C_path, { })
481 saved_cwd = os.getcwd()
483 os.chdir(svntest.main.work_dir)
484 svntest.actions.run_and_verify_merge(short_C_path, '1', '2', F_url,
485 expected_output,
486 expected_disk,
487 expected_status,
488 expected_skip,
489 None, None, None, None, None,
490 1) # check props
491 os.chdir(saved_cwd)
493 expected_output = svntest.wc.State(wc_dir, {
494 'A/C' : Item(verb='Sending'),
495 'A/C/Q' : Item(verb='Adding'),
496 'A/C/Q2' : Item(verb='Adding'),
497 'A/C/Q/bar' : Item(verb='Adding'),
498 'A/C/Q/bar2': Item(verb='Adding'),
499 'A/C/foo' : Item(verb='Adding'),
500 'A/C/foo2' : Item(verb='Adding'),
502 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
503 expected_status.add({
504 'A/C' : Item(status=' ', wc_rev=3),
505 'A/B/F/Q' : Item(status=' ', wc_rev=2),
506 'A/B/F/Q2' : Item(status=' ', wc_rev=2),
507 'A/B/F/Q/bar' : Item(status=' ', wc_rev=2),
508 'A/B/F/Q/bar2': Item(status=' ', wc_rev=2),
509 'A/B/F/foo' : Item(status=' ', wc_rev=2),
510 'A/B/F/foo2' : Item(status=' ', wc_rev=2),
511 'A/C/Q' : Item(status=' ', wc_rev=3),
512 'A/C/Q2' : Item(status=' ', wc_rev=3),
513 'A/C/Q/bar' : Item(status=' ', wc_rev=3),
514 'A/C/Q/bar2' : Item(status=' ', wc_rev=3),
515 'A/C/foo' : Item(status=' ', wc_rev=3),
516 'A/C/foo2' : Item(status=' ', wc_rev=3),
518 svntest.actions.run_and_verify_commit(wc_dir,
519 expected_output,
520 expected_status,
521 None,
522 wc_dir)
524 #----------------------------------------------------------------------
526 def delete_file_and_dir(sbox):
527 "merge that deletes items"
529 sbox.build()
530 wc_dir = sbox.wc_dir
532 # Rev 2 copy B to B2
533 B_path = os.path.join(wc_dir, 'A', 'B')
534 B2_path = os.path.join(wc_dir, 'A', 'B2')
535 B_url = sbox.repo_url + '/A/B'
537 svntest.actions.run_and_verify_svn(None, None, [],
538 'copy', B_path, B2_path)
540 expected_output = wc.State(wc_dir, {
541 'A/B2' : Item(verb='Adding'),
543 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
544 expected_status.add({
545 'A/B2' : Item(status=' ', wc_rev=2),
546 'A/B2/E' : Item(status=' ', wc_rev=2),
547 'A/B2/E/alpha' : Item(status=' ', wc_rev=2),
548 'A/B2/E/beta' : Item(status=' ', wc_rev=2),
549 'A/B2/F' : Item(status=' ', wc_rev=2),
550 'A/B2/lambda' : Item(status=' ', wc_rev=2),
552 svntest.actions.run_and_verify_commit(wc_dir,
553 expected_output,
554 expected_status,
555 None,
556 wc_dir)
558 # Rev 3 delete E and lambda from B
559 E_path = os.path.join(B_path, 'E')
560 lambda_path = os.path.join(B_path, 'lambda')
561 svntest.actions.run_and_verify_svn(None, None, [],
562 'delete', E_path, lambda_path)
564 expected_output = wc.State(wc_dir, {
565 'A/B/E' : Item(verb='Deleting'),
566 'A/B/lambda' : Item(verb='Deleting'),
568 expected_status.remove('A/B/E',
569 'A/B/E/alpha',
570 'A/B/E/beta',
571 'A/B/lambda')
572 svntest.actions.run_and_verify_commit(wc_dir,
573 expected_output,
574 expected_status,
575 None,
576 wc_dir)
578 def modify_B2():
579 # Local mods in B2
580 B2_E_path = os.path.join(B2_path, 'E')
581 B2_lambda_path = os.path.join(B2_path, 'lambda')
582 svntest.actions.run_and_verify_svn(None, None, [],
583 'propset', 'foo', 'foo_val',
584 B2_E_path, B2_lambda_path)
585 expected_status.tweak(
586 'A/B2/E', 'A/B2/lambda', status=' M'
588 svntest.actions.run_and_verify_status(wc_dir, expected_status)
590 modify_B2()
592 # Merge rev 3 into B2
594 # The local mods to the paths modified in r3 cause the paths to be
595 # skipped (without --force), resulting in only mergeinfo changes. The
596 # target of the merge 'B2' gets mergeinfo for r3 and B2's two skipped
597 # children, 'E' and 'lambda', get override mergeinfo reflecting their
598 # mergeinfo prior to the merge (in this case empty mergeinfo).
599 expected_output = wc.State(B2_path, { })
600 expected_disk = wc.State('', {
601 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B:3'}),
602 'E' : Item(props={SVN_PROP_MERGEINFO : '',
603 'foo' : 'foo_val'}),
604 'E/alpha' : Item("This is the file 'alpha'.\n"),
605 'E/beta' : Item("This is the file 'beta'.\n"),
606 'F' : Item(),
607 'lambda' : Item("This is the file 'lambda'.\n",
608 props={SVN_PROP_MERGEINFO : '',
609 'foo' : 'foo_val'}),
611 expected_status2 = wc.State(B2_path, {
612 '' : Item(status=' M'),
613 'E' : Item(status=' M'),
614 'E/alpha' : Item(status=' '),
615 'E/beta' : Item(status=' '),
616 'F' : Item(status=' '),
617 'lambda' : Item(status=' M'),
619 expected_status2.tweak(wc_rev=2)
620 expected_skip = wc.State(B2_path, {
621 'lambda' : Item(),
622 'E' : Item(),
624 svntest.actions.run_and_verify_merge(B2_path, '2', '3', B_url,
625 expected_output,
626 expected_disk,
627 expected_status2,
628 expected_skip,
629 None, None, None, None, None,
630 True)
632 # Revert the previous merge attempt and redo the local changes to B2.
633 # Why do we need to do this? Because 'B2' already has mergeinfo reflecting
634 # r3 has been merged. If we didn't revert we'd need to use
635 # --ignore-ancestry' to force B2's children 'E' and 'lamda' to actually be
636 # deleted. This is another facet of issue #2898.
637 svntest.actions.run_and_verify_svn(None, None, [],
638 'revert', '-R', wc_dir)
639 modify_B2()
641 expected_output = wc.State(B2_path, {
642 'E' : Item(status='D '),
643 'lambda' : Item(status='D '),
645 expected_disk.remove('E/alpha', 'E/beta', 'lambda')
646 expected_disk.tweak('E', props={'foo' : 'foo_val'})
647 expected_status2.tweak('E', 'E/alpha', 'E/beta', 'lambda', status='D ')
648 expected_status2.tweak('', status=' M')
649 expected_skip.remove('lambda', 'E')
651 ### Full-to-dry-run automatic comparison disabled because a) dry-run
652 ### doesn't descend into deleted directories, and b) the full merge
653 ### notifies deleted directories twice.
654 svntest.actions.run_and_verify_merge(B2_path, '2', '3', B_url,
655 expected_output,
656 expected_disk,
657 expected_status2,
658 expected_skip,
659 None, None, None, None, None,
660 True, 0, '--force')
664 #----------------------------------------------------------------------
666 # Issue 953
667 def simple_property_merges(sbox):
668 "some simple property merges"
670 sbox.build()
671 wc_dir = sbox.wc_dir
673 # Add a property to a file and a directory
674 alpha_path = os.path.join(wc_dir, 'A', 'B', 'E', 'alpha')
675 beta_path = os.path.join(wc_dir, 'A', 'B', 'E', 'beta')
676 E_path = os.path.join(wc_dir, 'A', 'B', 'E')
678 svntest.actions.run_and_verify_svn(None, None, [],
679 'propset', 'foo', 'foo_val',
680 alpha_path)
681 # A binary, non-UTF8 property value
682 svntest.actions.run_and_verify_svn(None, None, [],
683 'propset', 'foo', 'foo\201val',
684 beta_path)
685 svntest.actions.run_and_verify_svn(None, None, [],
686 'propset', 'foo', 'foo_val',
687 E_path)
689 # Commit change as rev 2
690 expected_output = svntest.wc.State(wc_dir, {
691 'A/B/E' : Item(verb='Sending'),
692 'A/B/E/alpha' : Item(verb='Sending'),
693 'A/B/E/beta' : Item(verb='Sending'),
695 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
696 expected_status.tweak('A/B/E', 'A/B/E/alpha', 'A/B/E/beta',
697 wc_rev=2, status=' ')
698 svntest.actions.run_and_verify_commit(wc_dir,
699 expected_output, expected_status,
700 None, wc_dir)
701 svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
703 # Copy B to B2 as rev 3
704 B_url = sbox.repo_url + '/A/B'
705 B2_url = sbox.repo_url + '/A/B2'
707 svntest.actions.run_and_verify_svn(None, None, [],
708 'copy', '-m', 'copy B to B2',
709 B_url, B2_url)
710 svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
712 # Modify a property and add a property for the file and directory
713 svntest.actions.run_and_verify_svn(None, None, [],
714 'propset', 'foo', 'mod_foo', alpha_path)
715 svntest.actions.run_and_verify_svn(None, None, [],
716 'propset', 'bar', 'bar_val', alpha_path)
717 svntest.actions.run_and_verify_svn(None, None, [],
718 'propset', 'foo', 'mod\201foo', beta_path)
719 svntest.actions.run_and_verify_svn(None, None, [],
720 'propset', 'bar', 'bar\201val', beta_path)
721 svntest.actions.run_and_verify_svn(None, None, [],
722 'propset', 'foo', 'mod_foo', E_path)
723 svntest.actions.run_and_verify_svn(None, None, [],
724 'propset', 'bar', 'bar_val', E_path)
726 # Commit change as rev 4
727 expected_status = svntest.actions.get_virginal_state(wc_dir, 3)
728 expected_status.tweak('A/B/E', 'A/B/E/alpha', 'A/B/E/beta',
729 wc_rev=4, status=' ')
730 expected_status.add({
731 'A/B2' : Item(status=' ', wc_rev=3),
732 'A/B2/E' : Item(status=' ', wc_rev=3),
733 'A/B2/E/alpha' : Item(status=' ', wc_rev=3),
734 'A/B2/E/beta' : Item(status=' ', wc_rev=3),
735 'A/B2/F' : Item(status=' ', wc_rev=3),
736 'A/B2/lambda' : Item(status=' ', wc_rev=3),
738 svntest.actions.run_and_verify_commit(wc_dir,
739 expected_output, expected_status,
740 None, wc_dir)
741 svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
743 pristine_status = expected_status
744 pristine_status.tweak(wc_rev=4)
746 # Merge B 3:4 into B2
747 B2_path = os.path.join(wc_dir, 'A', 'B2')
748 expected_output = wc.State(B2_path, {
749 'E' : Item(status=' U'),
750 'E/alpha' : Item(status=' U'),
751 'E/beta' : Item(status=' U'),
753 expected_disk = wc.State('', {
754 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B:4'}),
755 'E' : Item(),
756 'E/alpha' : Item("This is the file 'alpha'.\n"),
757 'E/beta' : Item("This is the file 'beta'.\n"),
758 'F' : Item(),
759 'lambda' : Item("This is the file 'lambda'.\n"),
761 expected_disk.tweak('E', 'E/alpha',
762 props={'foo' : 'mod_foo', 'bar' : 'bar_val'})
763 expected_disk.tweak('E/beta',
764 props={'foo' : 'mod\201foo', 'bar' : 'bar\201val'})
765 expected_status = wc.State(B2_path, {
766 '' : Item(status=' M'),
767 'E' : Item(status=' M'),
768 'E/alpha' : Item(status=' M'),
769 'E/beta' : Item(status=' M'),
770 'F' : Item(status=' '),
771 'lambda' : Item(status=' '),
773 expected_status.tweak(wc_rev=4)
774 expected_skip = wc.State('', { })
775 svntest.actions.run_and_verify_merge(B2_path, '3', '4', B_url,
776 expected_output,
777 expected_disk,
778 expected_status,
779 expected_skip,
780 None, None, None, None, None, 1)
782 # Revert merge
783 svntest.actions.run_and_verify_svn(None, None, [],
784 'revert', '--recursive', wc_dir)
785 svntest.actions.run_and_verify_status(wc_dir, pristine_status)
787 # Merge B 2:1 into B2 (B2's mergeinfo should get elided away)
788 expected_status.tweak('', status=' ')
789 expected_disk.remove('')
790 expected_disk.tweak('E', 'E/alpha', 'E/beta', props={})
791 svntest.actions.run_and_verify_merge(B2_path, '2', '1', B_url,
792 expected_output,
793 expected_disk,
794 expected_status,
795 expected_skip,
796 None, None, None, None, None, 1)
798 # Merge B 3:4 into B2 now causes a conflict
799 expected_disk.add({
800 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B:4'}),
801 'E/dir_conflicts.prej'
802 : Item("Trying to change property 'foo' from 'foo_val' to 'mod_foo',\n"
803 + "but it has been locally deleted.\n"),
804 'E/alpha.prej'
805 : Item("Trying to change property 'foo' from 'foo_val' to 'mod_foo',\n"
806 + "but it has been locally deleted.\n"),
807 'E/beta.prej'
808 : Item("Trying to change property 'foo' from 'foo?\\129val' to"
809 + " 'mod?\\129foo',\n"
810 + "but it has been locally deleted.\n"),
812 expected_disk.tweak('E', 'E/alpha', props={'bar' : 'bar_val'})
813 expected_disk.tweak('E/beta', props={'bar' : 'bar\201val'})
814 expected_status.tweak('', status=' M')
815 expected_status.tweak('E', 'E/alpha', 'E/beta', status=' C')
816 expected_output.tweak('E', 'E/alpha', 'E/beta', status=' C')
817 svntest.actions.run_and_verify_merge(B2_path, '3', '4', B_url,
818 expected_output,
819 expected_disk,
820 expected_status,
821 expected_skip,
822 None, None, None, None, None, 1)
824 # issue 1109 : single file property merge. This test performs a merge
825 # that should be a no-op (adding properties that are already present).
826 svntest.actions.run_and_verify_svn(None, None, [],
827 'revert', '--recursive', wc_dir)
828 svntest.actions.run_and_verify_status(wc_dir, pristine_status)
830 # Copy A at rev 4 to A2 to make revision 5.
831 A_url = sbox.repo_url + '/A'
832 A2_url = sbox.repo_url + '/A2'
833 svntest.actions.run_and_verify_svn(None,
834 ['\n', 'Committed revision 5.\n'], [],
835 'copy', '-m', 'copy A to A2',
836 A_url, A2_url)
838 # Re-root the WC at A2.
839 svntest.actions.run_and_verify_svn(None, None, [], 'switch', A2_url, wc_dir)
841 # Attempt to re-merge rev 4 of the original A's alpha. Mergeinfo
842 # inherited from A2 (created by its copy from A) allows us to avoid
843 # a repeated merge.
844 alpha_url = sbox.repo_url + '/A/B/E/alpha'
845 alpha_path = os.path.join(wc_dir, 'B', 'E', 'alpha')
847 # Cannot use run_and_verify_merge with a file target
848 svntest.actions.run_and_verify_svn(None, [], [], 'merge', '-r', '3:4',
849 alpha_url, alpha_path)
851 exit_code, output, err = svntest.actions.run_and_verify_svn(None, None, [],
852 'pl', alpha_path)
854 saw_foo = 0
855 saw_bar = 0
856 for line in output:
857 if re.match("\\s*foo\\s*$", line):
858 saw_foo = 1
859 if re.match("\\s*bar\\s*$", line):
860 saw_bar = 1
862 if not saw_foo or not saw_bar:
863 raise svntest.Failure("Expected properties not found")
866 #----------------------------------------------------------------------
867 # This is a regression for issue #1176.
869 def merge_catches_nonexistent_target(sbox):
870 "merge should not die if a target file is absent"
872 sbox.build()
873 wc_dir = sbox.wc_dir
875 # Copy G to a new directory, Q. Create Q/newfile. Commit a change
876 # to Q/newfile. Now merge that change... into G. Merge should not
877 # error, but should do nothing.
879 G_path = os.path.join(wc_dir, 'A', 'D', 'G')
880 Q_path = os.path.join(wc_dir, 'A', 'D', 'Q')
881 newfile_path = os.path.join(Q_path, 'newfile')
882 Q_url = sbox.repo_url + '/A/D/Q'
884 # Copy dir A/D/G to A/D/Q
885 svntest.actions.run_and_verify_svn(None, None, [], 'cp', G_path, Q_path)
887 svntest.main.file_append(newfile_path, 'This is newfile.\n')
888 svntest.actions.run_and_verify_svn(None, None, [], 'add', newfile_path)
890 # Add newfile to dir G, creating r2.
891 expected_output = wc.State(wc_dir, {
892 'A/D/Q' : Item(verb='Adding'),
893 'A/D/Q/newfile' : Item(verb='Adding'),
895 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
896 expected_status.add({
897 'A/D/Q' : Item(status=' ', wc_rev=2),
898 'A/D/Q/pi' : Item(status=' ', wc_rev=2),
899 'A/D/Q/rho' : Item(status=' ', wc_rev=2),
900 'A/D/Q/tau' : Item(status=' ', wc_rev=2),
901 'A/D/Q/newfile' : Item(status=' ', wc_rev=2),
903 svntest.actions.run_and_verify_commit(wc_dir,
904 expected_output,
905 expected_status,
906 None, wc_dir)
908 # Change newfile, creating r3.
909 svntest.main.file_append(newfile_path, 'A change to newfile.\n')
910 expected_output = wc.State(wc_dir, {
911 'A/D/Q/newfile' : Item(verb='Sending'),
913 expected_status.tweak('A/D/Q/newfile', wc_rev=3)
914 svntest.actions.run_and_verify_commit(wc_dir,
915 expected_output,
916 expected_status,
917 None, wc_dir)
919 # Merge the change to newfile (from r3) into G, where newfile
920 # doesn't exist.
921 os.chdir(G_path)
922 expected_output = wc.State('', { })
923 expected_status = wc.State('', {
924 '' : Item(status=' M' ),
925 'pi' : Item(status=' ' ),
926 'rho' : Item(status=' ' ),
927 'tau' : Item(status=' ' ),
929 expected_status.tweak(wc_rev=1)
930 expected_disk = wc.State('', {
931 '' : Item(props={SVN_PROP_MERGEINFO : '/A/D/Q:3'}),
932 'pi' : Item("This is the file 'pi'.\n"),
933 'rho' : Item("This is the file 'rho'.\n"),
934 'tau' : Item("This is the file 'tau'.\n"),
936 expected_skip = wc.State('', {
937 'newfile' :Item(),
939 svntest.actions.run_and_verify_merge('', '2', '3', Q_url,
940 expected_output,
941 expected_disk,
942 expected_status,
943 expected_skip,
944 None, None, None, None, None, True)
946 #----------------------------------------------------------------------
948 def merge_tree_deleted_in_target(sbox):
949 "merge on deleted directory in target"
951 sbox.build()
952 wc_dir = sbox.wc_dir
954 # Copy B to a new directory, I. Modify B/E/alpha, Remove I/E. Now
955 # merge that change... into I. Merge should not error
957 B_path = os.path.join(wc_dir, 'A', 'B')
958 I_path = os.path.join(wc_dir, 'A', 'I')
959 alpha_path = os.path.join(B_path, 'E', 'alpha')
960 B_url = sbox.repo_url + '/A/B'
961 I_url = sbox.repo_url + '/A/I'
964 # Copy B to I, creating r1.
965 svntest.actions.run_and_verify_svn(None, None, [],
966 'cp', B_url, I_url, '-m', 'rev 2')
968 # Change some files, creating r2.
969 svntest.main.file_append(alpha_path, 'A change to alpha.\n')
970 svntest.main.file_append(os.path.join(B_path, 'lambda'), 'change lambda.\n')
971 svntest.actions.run_and_verify_svn(None, None, [],
972 'ci', '-m', 'rev 3', B_path)
974 # Remove E, creating r3.
975 E_url = sbox.repo_url + '/A/I/E'
976 svntest.actions.run_and_verify_svn(None, None, [],
977 'rm', E_url, '-m', 'rev 4')
979 svntest.actions.run_and_verify_svn(None, None, [],
980 'up', os.path.join(wc_dir,'A'))
982 expected_output = wc.State(I_path, {
983 'lambda' : Item(status='U '),
985 expected_disk = wc.State('', {
986 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B:3'}),
987 'F' : Item(),
988 'lambda' : Item("This is the file 'lambda'.\nchange lambda.\n"),
990 expected_status = wc.State(I_path, {
991 '' : Item(status=' M'),
992 'F' : Item(status=' '),
993 'lambda' : Item(status='M '),
995 expected_status.tweak(wc_rev=4)
996 expected_skip = wc.State(I_path, {
997 'E' : Item(),
998 'E/alpha' : Item(),
1000 svntest.actions.run_and_verify_merge(I_path, '2', '3', B_url,
1001 expected_output,
1002 expected_disk,
1003 expected_status,
1004 expected_skip,
1005 None, None, None, None, None,
1006 1, 0)
1008 #----------------------------------------------------------------------
1009 # Issue #2515
1011 def merge_added_dir_to_deleted_in_target(sbox):
1012 "merge an added dir on a deleted dir in target"
1014 sbox.build()
1015 wc_dir = sbox.wc_dir
1017 # copy B to a new directory, I.
1018 # delete F in I.
1019 # add J to B/F.
1020 # merge add to I.
1022 B_url = sbox.repo_url + '/A/B'
1023 I_url = sbox.repo_url + '/A/I'
1024 F_url = sbox.repo_url + '/A/I/F'
1025 J_url = sbox.repo_url + '/A/B/F/J'
1026 I_path = os.path.join(wc_dir, 'A', 'I')
1029 svntest.actions.run_and_verify_svn(None, None, [],
1030 'cp', B_url, I_url, '-m', 'rev 2')
1032 svntest.actions.run_and_verify_svn(None, None, [],
1033 'rm', F_url, '-m', 'rev 3')
1035 svntest.actions.run_and_verify_svn(None, None, [],
1036 'mkdir', '-m', 'rev 4', J_url)
1038 svntest.actions.run_and_verify_svn(None, None, [],
1039 'up', os.path.join(wc_dir,'A'))
1041 expected_output = wc.State(I_path, {})
1042 expected_disk = wc.State('', {
1043 'E' : Item(),
1044 'E/alpha' : Item("This is the file 'alpha'.\n"),
1045 'E/beta' : Item("This is the file 'beta'.\n"),
1046 'lambda' : Item("This is the file 'lambda'.\n"),
1048 expected_skip = wc.State(I_path, {
1049 'F/J' : Item(),
1050 'F' : Item(),
1053 svntest.actions.run_and_verify_merge(I_path, '2', '4', B_url,
1054 expected_output,
1055 expected_disk,
1056 None,
1057 expected_skip,
1058 None, None, None, None, None,
1059 0, 0)
1061 #----------------------------------------------------------------------
1062 # This is a regression for issue #1176.
1064 def merge_similar_unrelated_trees(sbox):
1065 "merging similar trees ancestrally unrelated"
1067 ## See http://subversion.tigris.org/issues/show_bug.cgi?id=1249. ##
1069 sbox.build()
1070 wc_dir = sbox.wc_dir
1072 # Simple test. Make three directories with the same content.
1073 # Modify some stuff in the second one. Now merge
1074 # (firstdir:seconddir->thirddir).
1076 base1_path = os.path.join(wc_dir, 'base1')
1077 base2_path = os.path.join(wc_dir, 'base2')
1078 apply_path = os.path.join(wc_dir, 'apply')
1080 base1_url = os.path.join(sbox.repo_url + '/base1')
1081 base2_url = os.path.join(sbox.repo_url + '/base2')
1083 # Make a tree of stuff ...
1084 os.mkdir(base1_path)
1085 svntest.main.file_append(os.path.join(base1_path, 'iota'),
1086 "This is the file iota\n")
1087 os.mkdir(os.path.join(base1_path, 'A'))
1088 svntest.main.file_append(os.path.join(base1_path, 'A', 'mu'),
1089 "This is the file mu\n")
1090 os.mkdir(os.path.join(base1_path, 'A', 'B'))
1091 svntest.main.file_append(os.path.join(base1_path, 'A', 'B', 'alpha'),
1092 "This is the file alpha\n")
1093 svntest.main.file_append(os.path.join(base1_path, 'A', 'B', 'beta'),
1094 "This is the file beta\n")
1096 # ... Copy it twice ...
1097 shutil.copytree(base1_path, base2_path)
1098 shutil.copytree(base1_path, apply_path)
1100 # ... Gonna see if merge is naughty or nice!
1101 svntest.main.file_append(os.path.join(base2_path, 'A', 'mu'),
1102 "A new line in mu.\n")
1103 os.rename(os.path.join(base2_path, 'A', 'B', 'beta'),
1104 os.path.join(base2_path, 'A', 'B', 'zeta'))
1106 svntest.actions.run_and_verify_svn(None, None, [],
1107 'add', base1_path, base2_path, apply_path)
1109 svntest.actions.run_and_verify_svn(None, None, [],
1110 'ci', '-m', 'rev 2', wc_dir)
1112 expected_output = wc.State(apply_path, {
1113 'A/mu' : Item(status='U '),
1114 'A/B/zeta' : Item(status='A '),
1115 'A/B/beta' : Item(status='D '),
1118 # Search for the comment entitled "The Merge Kluge" elsewhere in
1119 # this file, to understand why we shorten and chdir() below.
1120 saved_cwd = os.getcwd()
1121 os.chdir(svntest.main.work_dir)
1122 # run_and_verify_merge doesn't support 'svn merge URL URL path'
1123 svntest.actions.run_and_verify_svn(None, None, [],
1124 'merge',
1125 '--ignore-ancestry',
1126 base1_url, base2_url,
1127 shorten_path_kludge(apply_path))
1128 os.chdir(saved_cwd)
1130 expected_status = wc.State(apply_path, {
1131 '' : Item(status=' '),
1132 'A' : Item(status=' '),
1133 'A/mu' : Item(status='M '),
1134 'A/B' : Item(status=' '),
1135 'A/B/zeta' : Item(status='A ', copied='+'),
1136 'A/B/alpha' : Item(status=' '),
1137 'A/B/beta' : Item(status='D '),
1138 'iota' : Item(status=' '),
1140 expected_status.tweak(wc_rev=2)
1141 expected_status.tweak('A/B/zeta', wc_rev='-')
1142 svntest.actions.run_and_verify_status(apply_path, expected_status)
1144 #----------------------------------------------------------------------
1145 def merge_one_file_helper(sbox, arg_flav, record_only = 0):
1146 "ARG_FLAV is one of 'r' (revision range) or 'c' (single change)."
1148 if arg_flav not in ('r', 'c', '*'):
1149 raise svntest.Failure("Unrecognized flavor of merge argument")
1151 sbox.build()
1152 wc_dir = sbox.wc_dir
1154 rho_rel_path = os.path.join('A', 'D', 'G', 'rho')
1155 rho_path = os.path.join(wc_dir, rho_rel_path)
1156 G_path = os.path.join(wc_dir, 'A', 'D', 'G')
1157 rho_url = sbox.repo_url + '/A/D/G/rho'
1159 # Change rho for revision 2
1160 svntest.main.file_append(rho_path, 'A new line in rho.\n')
1162 expected_output = wc.State(wc_dir, { rho_rel_path : Item(verb='Sending'), })
1163 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
1164 expected_status.tweak('A/D/G/rho', wc_rev=2)
1165 svntest.actions.run_and_verify_commit(wc_dir,
1166 expected_output,
1167 expected_status,
1168 None,
1169 wc_dir)
1171 # Backdate rho to revision 1, so we can merge in the rev 2 changes.
1172 svntest.actions.run_and_verify_svn(None, None, [],
1173 'up', '-r', '1', rho_path)
1175 # Try one merge with an explicit target; it should succeed.
1176 # ### Yes, it would be nice to use run_and_verify_merge(), but it
1177 # appears to be impossible to get the expected_foo trees working
1178 # right. I think something is still assuming a directory target.
1179 if arg_flav == 'r':
1180 svntest.actions.run_and_verify_svn(None ,
1181 expected_merge_output([[2]], 'U ' +
1182 rho_path + '\n'),
1184 'merge', '-r', '1:2',
1185 rho_url, rho_path)
1186 elif arg_flav == 'c':
1187 svntest.actions.run_and_verify_svn(None ,
1188 expected_merge_output([[2]], 'U ' +
1189 rho_path + '\n'),
1191 'merge', '-c', '2',
1192 rho_url, rho_path)
1193 elif arg_flav == '*':
1194 svntest.actions.run_and_verify_svn(None ,
1195 expected_merge_output([[2]], 'U ' +
1196 rho_path + '\n'),
1198 'merge', rho_url, rho_path)
1200 expected_status.tweak(wc_rev=1)
1201 expected_status.tweak('A/D/G/rho', status='MM')
1202 svntest.actions.run_and_verify_status(wc_dir, expected_status)
1204 # Inspect rho, make sure it's right.
1205 rho_text = svntest.tree.get_text(rho_path)
1206 if rho_text != "This is the file 'rho'.\nA new line in rho.\n":
1207 raise svntest.Failure("Unexpected text in merged '" + rho_path + "'")
1209 # Restore rho to pristine revision 1, for another merge.
1210 svntest.actions.run_and_verify_svn(None, None, [], 'revert', rho_path)
1211 expected_status.tweak('A/D/G/rho', status=' ')
1212 svntest.actions.run_and_verify_status(wc_dir, expected_status)
1214 # Cd into the directory and run merge with no targets.
1215 # It should still merge into rho.
1216 saved_cwd = os.getcwd()
1217 os.chdir(G_path)
1219 # Cannot use run_and_verify_merge with a file target
1220 merge_cmd = ['merge']
1221 if arg_flav == 'r':
1222 merge_cmd += ['-r', '1:2']
1223 elif arg_flav == 'c':
1224 merge_cmd += ['-c', '2']
1226 if record_only:
1227 expected_output = []
1228 merge_cmd.append('--record-only')
1229 rho_expected_status = ' M'
1230 else:
1231 expected_output = expected_merge_output([[2]], 'U rho\n')
1232 rho_expected_status = 'MM'
1233 merge_cmd.append(rho_url)
1235 svntest.actions.run_and_verify_svn(None, expected_output, [], *merge_cmd)
1237 # Inspect rho, make sure it's right.
1238 rho_text = svntest.tree.get_text('rho')
1239 if record_only:
1240 expected_text = "This is the file 'rho'.\n"
1241 else:
1242 expected_text = "This is the file 'rho'.\nA new line in rho.\n"
1243 if rho_text != expected_text:
1244 print
1245 raise svntest.Failure("Unexpected text merged to 'rho' in '" +
1246 G_path + "'")
1247 os.chdir(saved_cwd)
1249 expected_status.tweak('A/D/G/rho', status=rho_expected_status)
1250 svntest.actions.run_and_verify_status(wc_dir, expected_status)
1252 def merge_one_file_using_r(sbox):
1253 "merge one file (issue #1150) using the -r option"
1254 merge_one_file_helper(sbox, 'r')
1256 def merge_one_file_using_c(sbox):
1257 "merge one file (issue #1150) using the -c option"
1258 merge_one_file_helper(sbox, 'c')
1260 def merge_one_file_using_implicit_revs(sbox):
1261 "merge one file without explicit revisions"
1262 merge_one_file_helper(sbox, '*')
1264 def merge_record_only(sbox):
1265 "mark a revision range as merged"
1266 merge_one_file_helper(sbox, 'r', 1)
1268 #----------------------------------------------------------------------
1269 # This is a regression for the enhancement added in issue #785.
1271 def merge_with_implicit_target_helper(sbox, arg_flav):
1272 "ARG_FLAV is one of 'r' (revision range) or 'c' (single change)."
1274 if arg_flav not in ('r', 'c', '*'):
1275 raise svntest.Failure("Unrecognized flavor of merge argument")
1277 sbox.build()
1278 wc_dir = sbox.wc_dir
1280 # Change mu for revision 2
1281 mu_path = os.path.join(wc_dir, 'A', 'mu')
1282 orig_mu_text = svntest.tree.get_text(mu_path)
1283 added_mu_text = ""
1284 for x in range(2,11):
1285 added_mu_text = added_mu_text + 'This is line ' + `x` + ' in mu\n'
1286 svntest.main.file_append(mu_path, added_mu_text)
1288 # Create expected output tree for initial commit
1289 expected_output = wc.State(wc_dir, {
1290 'A/mu' : Item(verb='Sending'),
1293 # Create expected status tree; all local revisions should be at 1,
1294 # but mu should be at revision 2.
1295 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
1296 expected_status.tweak('A/mu', wc_rev=2)
1298 # Initial commit.
1299 svntest.actions.run_and_verify_commit(wc_dir,
1300 expected_output,
1301 expected_status,
1302 None,
1303 wc_dir)
1305 # Make the "other" working copy, at r1
1306 other_wc = sbox.add_wc_path('other')
1307 svntest.actions.duplicate_dir(wc_dir, other_wc)
1308 svntest.main.run_svn(None, 'up', '-r', 1, other_wc)
1310 # Try the merge without an explicit target; it should succeed.
1311 # Can't use run_and_verify_merge cuz it expects a directory argument.
1312 mu_url = sbox.repo_url + '/A/mu'
1314 os.chdir(os.path.join(other_wc, 'A'))
1316 # merge using filename for sourcepath
1317 # Cannot use run_and_verify_merge with a file target
1318 if arg_flav == 'r':
1319 svntest.actions.run_and_verify_svn(None,
1320 expected_merge_output([[2]],
1321 'U mu\n'),
1323 'merge', '-r', '1:2', 'mu')
1324 elif arg_flav == 'c':
1325 svntest.actions.run_and_verify_svn(None,
1326 expected_merge_output([[2]],
1327 'U mu\n'),
1329 'merge', '-c', '2', 'mu')
1331 elif arg_flav == '*':
1332 # Without a peg revision, the default merge range of BASE:1 (which
1333 # is a no-op) will be chosen. Let's do it both ways (no-op first,
1334 # of course).
1335 svntest.actions.run_and_verify_svn(None, None, [], 'merge', 'mu')
1336 svntest.actions.run_and_verify_svn(None,
1337 expected_merge_output([[2]],
1338 'U mu\n'),
1340 'merge', 'mu@2')
1342 # sanity-check resulting file
1343 if (svntest.tree.get_text('mu') != orig_mu_text + added_mu_text):
1344 raise svntest.Failure("Unexpected text in 'mu'")
1346 # merge using URL for sourcepath
1347 if arg_flav == 'r':
1348 svntest.actions.run_and_verify_svn(None,
1349 expected_merge_output([[-2]],
1350 'G mu\n'),
1352 'merge', '-r', '2:1', mu_url)
1353 elif arg_flav == 'c':
1354 svntest.actions.run_and_verify_svn(None,
1355 expected_merge_output([[-2]],
1356 'G mu\n'),
1358 'merge', '-c', '-2', mu_url)
1359 elif arg_flav == '*':
1360 # Implicit merge source URL and revision range detection is for
1361 # forward merges only (e.g. non-reverts). Undo application of
1362 # r2 to enable continuation of the test case.
1363 svntest.actions.run_and_verify_svn(None,
1364 expected_merge_output([[-2]],
1365 'G mu\n'),
1367 'merge', '-c', '-2', mu_url)
1369 # sanity-check resulting file
1370 if (svntest.tree.get_text('mu') != orig_mu_text):
1371 raise svntest.Failure("Unexpected text '%s' in 'mu', expected '%s'" %
1372 (svntest.tree.get_text('mu'), orig_mu_text))
1376 def merge_with_implicit_target_using_r(sbox):
1377 "merging a file w/no explicit target path using -r"
1378 merge_with_implicit_target_helper(sbox, 'r')
1380 def merge_with_implicit_target_using_c(sbox):
1381 "merging a file w/no explicit target path using -c"
1382 merge_with_implicit_target_helper(sbox, 'c')
1384 def merge_with_implicit_target_and_revs(sbox):
1385 "merging a file w/no explicit target path or revs"
1386 merge_with_implicit_target_helper(sbox, '*')
1389 #----------------------------------------------------------------------
1391 def merge_with_prev (sbox):
1392 "merge operations using PREV revision"
1394 sbox.build()
1395 wc_dir = sbox.wc_dir
1397 # Change mu for revision 2
1398 mu_path = os.path.join(wc_dir, 'A', 'mu')
1399 orig_mu_text = svntest.tree.get_text(mu_path)
1400 added_mu_text = ""
1401 for x in range(2,11):
1402 added_mu_text = added_mu_text + '\nThis is line ' + `x` + ' in mu'
1403 added_mu_text += "\n"
1404 svntest.main.file_append(mu_path, added_mu_text)
1406 zot_path = os.path.join(wc_dir, 'A', 'zot')
1408 svntest.main.file_append(zot_path, "bar")
1409 svntest.main.run_svn(None, 'add', zot_path)
1411 # Create expected output tree for initial commit
1412 expected_output = wc.State(wc_dir, {
1413 'A/mu' : Item(verb='Sending'),
1414 'A/zot' : Item(verb='Adding'),
1417 # Create expected status tree; all local revisions should be at 1,
1418 # but mu should be at revision 2.
1419 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
1420 expected_status.tweak('A/mu', wc_rev=2)
1421 expected_status.add({'A/zot' : Item(status=' ', wc_rev=2)})
1423 # Initial commit.
1424 svntest.actions.run_and_verify_commit(wc_dir,
1425 expected_output,
1426 expected_status,
1427 None,
1428 wc_dir)
1430 # Make some other working copies
1431 other_wc = sbox.add_wc_path('other')
1432 svntest.actions.duplicate_dir(wc_dir, other_wc)
1434 another_wc = sbox.add_wc_path('another')
1435 svntest.actions.duplicate_dir(wc_dir, another_wc)
1437 was_cwd = os.getcwd()
1439 os.chdir(os.path.join(other_wc, 'A'))
1441 # Try to revert the last change to mu via svn merge
1442 # Cannot use run_and_verify_merge with a file target
1443 svntest.actions.run_and_verify_svn(None,
1444 expected_merge_output([[-2]],
1445 'U mu\n'),
1447 'merge', '-r', 'HEAD:PREV', 'mu')
1449 # sanity-check resulting file
1450 if (svntest.tree.get_text('mu') != orig_mu_text):
1451 raise svntest.Failure("Unexpected text in 'mu'")
1453 os.chdir(was_cwd)
1455 other_status = expected_status
1456 other_status.wc_dir = other_wc
1457 other_status.tweak('A/mu', status='M ', wc_rev=2)
1458 other_status.tweak('A/zot', wc_rev=2)
1459 svntest.actions.run_and_verify_status(other_wc, other_status)
1461 os.chdir(another_wc)
1463 # ensure 'A' will be at revision 2
1464 svntest.actions.run_and_verify_svn(None, None, [], 'up')
1466 # now try a revert on a directory, and verify that it removed the zot
1467 # file we had added previously
1468 svntest.actions.run_and_verify_svn(None, None, [],
1469 'merge', '-r', 'COMMITTED:PREV',
1470 'A', 'A')
1472 if (svntest.tree.get_text('A/zot') != None):
1473 raise svntest.Failure("Unexpected text in 'A/zot'")
1475 os.chdir(was_cwd)
1477 another_status = expected_status
1478 another_status.wc_dir = another_wc
1479 another_status.tweak(wc_rev=2)
1480 another_status.tweak('A/mu', status='M ')
1481 another_status.tweak('A/zot', status='D ')
1482 svntest.actions.run_and_verify_status(another_wc, another_status)
1484 #----------------------------------------------------------------------
1485 # Regression test for issue #1319: 'svn merge' should *not* 'C' when
1486 # merging a change into a binary file, unless it has local mods, or has
1487 # different contents from the left side of the merge.
1489 def merge_binary_file (sbox):
1490 "merge change into unchanged binary file"
1492 sbox.build()
1493 wc_dir = sbox.wc_dir
1495 # Add a binary file to the project
1496 theta_contents = svntest.main.file_read(
1497 os.path.join(sys.path[0], "theta.bin"), 'rb')
1498 # Write PNG file data into 'A/theta'.
1499 theta_path = os.path.join(wc_dir, 'A', 'theta')
1500 svntest.main.file_write(theta_path, theta_contents, 'wb')
1502 svntest.main.run_svn(None, 'add', theta_path)
1504 # Commit the new binary file, creating revision 2.
1505 expected_output = svntest.wc.State(wc_dir, {
1506 'A/theta' : Item(verb='Adding (bin)'),
1508 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
1509 expected_status.add({
1510 'A/theta' : Item(status=' ', wc_rev=2),
1512 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
1513 expected_status, None,
1514 wc_dir)
1516 # Make the "other" working copy
1517 other_wc = sbox.add_wc_path('other')
1518 svntest.actions.duplicate_dir(wc_dir, other_wc)
1520 # Change the binary file in first working copy, commit revision 3.
1521 svntest.main.file_append(theta_path, "some extra junk")
1522 expected_output = wc.State(wc_dir, {
1523 'A/theta' : Item(verb='Sending'),
1525 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
1526 expected_status.add({
1527 'A/theta' : Item(status=' ', wc_rev=3),
1529 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
1530 expected_status, None,
1531 wc_dir)
1533 # Search for the comment entitled "The Merge Kluge" elsewhere in
1534 # this file, to understand why we shorten and chdir() below.
1535 short_other_wc = shorten_path_kludge(other_wc)
1537 # In second working copy, attempt to 'svn merge -r 2:3'.
1538 # We should *not* see a conflict during the update, but a 'U'.
1539 # And after the merge, the status should be 'M'.
1540 expected_output = wc.State(short_other_wc, {
1541 'A/theta' : Item(status='U '),
1543 expected_disk = svntest.main.greek_state.copy()
1544 expected_disk.add({
1545 '' : Item(props={SVN_PROP_MERGEINFO : '/:3'}),
1546 'A/theta' : Item(theta_contents + "some extra junk",
1547 props={'svn:mime-type' : 'application/octet-stream'}),
1549 expected_status = svntest.actions.get_virginal_state(short_other_wc, 1)
1550 expected_status.add({
1551 '' : Item(status=' M', wc_rev=1),
1552 'A/theta' : Item(status='M ', wc_rev=2),
1554 expected_skip = wc.State('', { })
1556 os.chdir(svntest.main.work_dir)
1557 svntest.actions.run_and_verify_merge(short_other_wc, '2', '3',
1558 sbox.repo_url,
1559 expected_output,
1560 expected_disk,
1561 expected_status,
1562 expected_skip,
1563 None, None, None, None, None,
1566 #----------------------------------------------------------------------
1567 # Regression test for issue #2403: Incorrect 3-way merge of "added"
1568 # binary file which already exists (unmodified) in the WC
1570 def three_way_merge_add_of_existing_binary_file(sbox):
1571 "3-way merge of 'file add' into existing binary"
1573 sbox.build()
1574 wc_dir = sbox.wc_dir
1576 # Create a branch of A, creating revision 2.
1577 A_url = sbox.repo_url + "/A"
1578 branch_A_url = sbox.repo_url + "/copy-of-A"
1579 svntest.actions.run_and_verify_svn(None, None, [],
1580 "cp",
1581 A_url, branch_A_url,
1582 "-m", "Creating copy-of-A")
1584 # Add a binary file to the WC.
1585 theta_contents = svntest.main.file_read(
1586 os.path.join(sys.path[0], "theta.bin"), 'rb')
1587 # Write PNG file data into 'A/theta'.
1588 theta_path = os.path.join(wc_dir, 'A', 'theta')
1589 svntest.main.file_write(theta_path, theta_contents, 'wb')
1591 svntest.main.run_svn(None, "add", theta_path)
1593 # Commit the new binary file to the repos, creating revision 3.
1594 expected_output = svntest.wc.State(wc_dir, {
1595 "A/theta" : Item(verb="Adding (bin)"),
1597 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
1598 expected_status.add({
1599 "A/theta" : Item(status=" ", wc_rev=3),
1601 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
1602 expected_status, None,
1603 wc_dir)
1605 # Search for the comment entitled "The Merge Kluge" elsewhere in
1606 # this file, to understand why we shorten and chdir() below.
1607 short_wc = shorten_path_kludge(wc_dir)
1609 # In the working copy, attempt to 'svn merge branch_A_url@2 A_url@3 A'.
1610 # We should *not* see a conflict during the merge, but an 'A'.
1611 # And after the merge, the status should not report any differences.
1613 expected_output = wc.State(short_wc, {
1614 "A/theta" : Item(status="A "),
1617 # As greek_state is rooted at / instead of /A (our merge target), we
1618 # need a sub-tree of it rather than straight copy.
1619 expected_disk = svntest.main.greek_state.subtree("A")
1620 expected_disk.add({
1621 "" : Item(props={SVN_PROP_MERGEINFO : '/A:2-3'}),
1622 "theta" : Item(theta_contents,
1623 props={"svn:mime-type" : "application/octet-stream"}),
1625 expected_status = svntest.actions.get_virginal_state(short_wc, 1)
1626 expected_status.add({
1627 "A/theta" : Item(status=" ", wc_rev=3),
1629 expected_status.tweak("A", status=" M")
1630 expected_status.remove("") # top-level of the WC
1631 expected_status.remove("iota")
1632 expected_skip = wc.State("", { })
1634 os.chdir(svntest.main.work_dir)
1635 # If we merge into short_wc alone, theta appears at the WC root,
1636 # which is in the wrong location -- append "/A" to stay on target.
1637 svntest.actions.run_and_verify_merge2(short_wc + "/A", "2", "3",
1638 branch_A_url, A_url,
1639 expected_output,
1640 expected_disk,
1641 expected_status,
1642 expected_skip,
1643 None, None, None, None, None,
1646 #----------------------------------------------------------------------
1647 # Regression test for Issue #1297:
1648 # A merge that creates a new file followed by an immediate diff
1649 # The diff should succeed.
1651 def merge_in_new_file_and_diff(sbox):
1652 "diff after merge that creates a new file"
1654 sbox.build()
1655 wc_dir = sbox.wc_dir
1657 trunk_url = sbox.repo_url + '/A/B/E'
1659 # Create a branch
1660 svntest.actions.run_and_verify_svn(None, None, [], 'cp',
1661 trunk_url,
1662 sbox.repo_url + '/branch',
1663 '-m', "Creating the Branch")
1665 # Update to revision 2.
1666 svntest.actions.run_and_verify_svn(None, None, [],
1667 'update', wc_dir)
1669 new_file_path = os.path.join(wc_dir, 'A', 'B', 'E', 'newfile')
1670 svntest.main.file_write(new_file_path, "newfile\n")
1672 # Add the new file, and commit revision 3.
1673 svntest.actions.run_and_verify_svn(None, None, [], "add", new_file_path)
1674 svntest.actions.run_and_verify_svn(None, None, [],
1675 'ci', '-m',
1676 "Changing the trunk.", wc_dir)
1678 # Search for the comment entitled "The Merge Kluge" elsewhere in
1679 # this file, to understand why we shorten and chdir() below.
1680 branch_path = os.path.join(wc_dir, "branch")
1681 short_branch_path = shorten_path_kludge(branch_path)
1683 # Merge our addition into the branch.
1684 expected_output = svntest.wc.State(short_branch_path, {
1685 'newfile' : Item(status='A '),
1687 expected_disk = wc.State('', {
1688 'alpha' : Item("This is the file 'alpha'.\n"),
1689 'beta' : Item("This is the file 'beta'.\n"),
1690 'newfile' : Item("newfile\n"),
1692 expected_status = wc.State(short_branch_path, {
1693 '' : Item(status=' M', wc_rev=2),
1694 'alpha' : Item(status=' ', wc_rev=2),
1695 'beta' : Item(status=' ', wc_rev=2),
1696 'newfile' : Item(status='A ', wc_rev='-', copied='+')
1698 expected_skip = wc.State('', { })
1700 saved_cwd = os.getcwd()
1702 os.chdir(svntest.main.work_dir)
1703 svntest.actions.run_and_verify_merge(short_branch_path,
1704 '1', 'HEAD', trunk_url,
1705 expected_output,
1706 expected_disk,
1707 expected_status,
1708 expected_skip)
1710 os.chdir(saved_cwd)
1712 # Finally, run diff. This diff produces no output!
1713 expected_output = [
1714 "\n",
1715 "Property changes on: " + branch_path + "\n",
1716 "___________________________________________________________________\n",
1717 "Added: " + SVN_PROP_MERGEINFO + "\n",
1718 " Merged /A/B/E:r2-3\n",
1719 "\n", ]
1720 svntest.actions.run_and_verify_svn(None, expected_output, [], 'diff',
1721 branch_path)
1724 #----------------------------------------------------------------------
1726 # Issue #1425: 'svn merge' should skip over any unversioned obstructions.
1728 def merge_skips_obstructions(sbox):
1729 "merge should skip over unversioned obstructions"
1731 sbox.build()
1732 wc_dir = sbox.wc_dir
1734 C_path = os.path.join(wc_dir, 'A', 'C')
1735 F_path = os.path.join(wc_dir, 'A', 'B', 'F')
1736 F_url = sbox.repo_url + '/A/B/F'
1738 Q_path = os.path.join(F_path, 'Q')
1739 foo_path = os.path.join(F_path, 'foo')
1740 bar_path = os.path.join(F_path, 'Q', 'bar')
1742 svntest.main.run_svn(None, 'mkdir', Q_path)
1743 svntest.main.file_append(foo_path, "foo")
1744 svntest.main.file_append(bar_path, "bar")
1745 svntest.main.run_svn(None, 'add', foo_path, bar_path)
1747 expected_output = wc.State(wc_dir, {
1748 'A/B/F/Q' : Item(verb='Adding'),
1749 'A/B/F/Q/bar' : Item(verb='Adding'),
1750 'A/B/F/foo' : Item(verb='Adding'),
1752 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
1753 expected_status.add({
1754 'A/B/F/Q' : Item(status=' ', wc_rev=2),
1755 'A/B/F/Q/bar' : Item(status=' ', wc_rev=2),
1756 'A/B/F/foo' : Item(status=' ', wc_rev=2),
1758 svntest.actions.run_and_verify_commit(wc_dir,
1759 expected_output,
1760 expected_status,
1761 None,
1762 wc_dir)
1764 pre_merge_status = expected_status
1766 # Revision 2 now has A/B/F/foo, A/B/F/Q, A/B/F/Q/bar. Let's merge
1767 # those 'F' changes into empty dir 'C'. But first, create an
1768 # unversioned 'foo' within C, and make sure 'svn merge' doesn't
1769 # error when the addition of foo is obstructed.
1771 # Search for the comment entitled "The Merge Kluge" elsewhere in
1772 # this file, to understand why we shorten and chdir() below.
1773 short_C_path = shorten_path_kludge(C_path)
1775 expected_output = wc.State(short_C_path, {
1776 'Q' : Item(status='A '),
1777 'Q/bar' : Item(status='A '),
1779 expected_disk = wc.State('', {
1780 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B/F:2'}),
1781 'Q' : Item(),
1782 'Q/bar' : Item("bar"),
1783 'foo' : Item("foo"),
1785 expected_status = wc.State(short_C_path, {
1786 '' : Item(status=' M', wc_rev=1),
1787 'Q' : Item(status='A ', wc_rev='-', copied='+'),
1788 'Q/bar' : Item(status='A ', wc_rev='-', copied='+'),
1790 expected_skip = wc.State(short_C_path, {
1791 'foo' : Item(),
1793 # Unversioned:
1794 svntest.main.file_append(os.path.join(C_path, "foo"), "foo")
1796 saved_cwd = os.getcwd()
1798 os.chdir(svntest.main.work_dir)
1799 svntest.actions.run_and_verify_merge(short_C_path, '1', '2', F_url,
1800 expected_output,
1801 expected_disk,
1802 expected_status,
1803 expected_skip,
1804 None, None, None, None, None,
1805 1, 0)
1807 os.chdir(saved_cwd)
1809 # Revert the local mods, and this time make "Q" obstructed. An
1810 # unversioned file called "Q" will obstruct the adding of the
1811 # directory of the same name.
1813 svntest.actions.run_and_verify_svn(None, None, [],
1814 'revert', '-R', wc_dir)
1815 os.unlink(os.path.join(C_path, "foo"))
1816 svntest.main.safe_rmtree(os.path.join(C_path, "Q"))
1817 svntest.main.file_append(os.path.join(C_path, "Q"), "foo") # unversioned
1818 svntest.actions.run_and_verify_status(wc_dir, pre_merge_status)
1820 # Search for the comment entitled "The Merge Kluge" elsewhere in
1821 # this file, to understand why we use short_C_path and chdir() below.
1822 expected_output = wc.State(short_C_path, {
1823 'foo' : Item(status='A '),
1825 expected_disk = wc.State('', {
1826 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B/F:2'}),
1827 'Q' : Item("foo"),
1828 'foo' : Item("foo"),
1830 expected_status = wc.State(short_C_path, {
1831 '' : Item(status=' M', wc_rev=1),
1832 'foo' : Item(status='A ', wc_rev='-', copied='+'),
1834 expected_skip = wc.State(short_C_path, {
1835 'Q' : Item(),
1836 'Q/bar' : Item(),
1839 saved_cwd = os.getcwd()
1841 os.chdir(svntest.main.work_dir)
1842 svntest.actions.run_and_verify_merge(short_C_path, '1', '2', F_url,
1843 expected_output,
1844 expected_disk,
1845 expected_status,
1846 expected_skip,
1847 None, None, None, None, None,
1848 1, 0)
1850 os.chdir(saved_cwd)
1852 # Revert the local mods, and commit the deletion of iota and A/D/G. (r3)
1853 os.unlink(os.path.join(C_path, "foo"))
1854 svntest.actions.run_and_verify_svn(None, None, [], 'revert', '-R', wc_dir)
1855 svntest.actions.run_and_verify_status(wc_dir, pre_merge_status)
1857 iota_path = os.path.join(wc_dir, 'iota')
1858 G_path = os.path.join(wc_dir, 'A', 'D', 'G')
1859 svntest.actions.run_and_verify_svn(None, None, [], 'rm', iota_path, G_path)
1861 expected_output = wc.State(wc_dir, {
1862 'A/D/G' : Item(verb='Deleting'),
1863 'iota' : Item(verb='Deleting'),
1865 expected_status = pre_merge_status
1866 expected_status.remove('iota', 'A/D/G', 'A/D/G/pi', 'A/D/G/rho', 'A/D/G/tau')
1867 svntest.actions.run_and_verify_commit(wc_dir,
1868 expected_output,
1869 expected_status,
1870 None, wc_dir)
1872 # Now create unversioned iota and A/D/G, try running a merge -r2:3.
1873 # The merge process should skip over these targets, since they're
1874 # unversioned.
1876 # Search for the comment entitled "The Merge Kluge" elsewhere in
1877 # this file, to understand why we shorten and chdir() below.
1878 short_wc_dir = shorten_path_kludge(wc_dir)
1880 svntest.main.file_append(iota_path, "foo") # unversioned
1881 os.mkdir(G_path) # unversioned
1883 expected_output = wc.State(short_wc_dir, { })
1884 expected_disk = svntest.main.greek_state.copy()
1885 expected_disk.remove('A/D/G/pi', 'A/D/G/rho', 'A/D/G/tau')
1886 expected_disk.add({
1887 '' : Item(props={SVN_PROP_MERGEINFO : '/:3'}),
1888 'A/B/F/Q' : Item(),
1889 'A/B/F/Q/bar' : Item("bar"),
1890 'A/B/F/foo' : Item("foo"),
1891 'iota' : Item("foo"),
1892 'A/C/Q' : Item("foo"),
1894 # No-op merge still sets mergeinfo
1895 expected_status.tweak('', status=' M')
1896 expected_skip = wc.State(short_wc_dir, {
1897 'A/D/G' : Item(),
1898 'iota' : Item(),
1901 saved_cwd = os.getcwd()
1903 os.chdir(svntest.main.work_dir)
1904 svntest.actions.run_and_verify_merge(short_wc_dir, '2', '3',
1905 sbox.repo_url,
1906 expected_output,
1907 expected_disk,
1908 expected_status.copy(short_wc_dir),
1909 expected_skip,
1910 None, None, None, None, None,
1911 1, 0)
1913 os.chdir(saved_cwd)
1915 # Revert the local mods, and commit a change to A/B/lambda (r4), and then
1916 # commit the deletion of the same file. (r5)
1917 os.unlink(iota_path)
1918 svntest.main.safe_rmtree(G_path)
1919 svntest.actions.run_and_verify_svn(None, None, [], 'revert', '-R', wc_dir)
1920 expected_status.tweak('', status=' ')
1921 svntest.actions.run_and_verify_status(wc_dir, expected_status)
1923 lambda_path = os.path.join(wc_dir, 'A', 'B', 'lambda')
1924 svntest.main.file_append(lambda_path, "more text")
1925 expected_output = wc.State(wc_dir, {
1926 'A/B/lambda' : Item(verb='Sending'),
1928 expected_status.tweak('A/B/lambda', wc_rev=4)
1929 svntest.actions.run_and_verify_commit(wc_dir,
1930 expected_output,
1931 expected_status,
1932 None, wc_dir)
1934 svntest.actions.run_and_verify_svn(None, None, [], 'rm', lambda_path)
1936 expected_output = wc.State(wc_dir, {
1937 'A/B/lambda' : Item(verb='Deleting'),
1939 expected_status.remove('A/B/lambda')
1940 svntest.actions.run_and_verify_commit(wc_dir,
1941 expected_output,
1942 expected_status,
1943 None, wc_dir)
1945 # lambda is gone, so create an unversioned lambda in its place.
1946 # Then attempt to merge -r3:4, which is a change to lambda. The merge
1947 # should simply skip the unversioned file.
1949 svntest.main.file_append(lambda_path, "foo") # unversioned
1951 # Search for the comment entitled "The Merge Kluge" elsewhere in
1952 # this file, to understand why we use short_wc_dir and chdir() below.
1953 expected_output = wc.State(short_wc_dir, { })
1954 expected_disk.add({
1955 'A/B/lambda' : Item("foo"),
1957 expected_disk.remove('A/D/G', 'iota')
1958 expected_disk.tweak('', props={SVN_PROP_MERGEINFO : '/:4'})
1959 expected_skip = wc.State(short_wc_dir, {
1960 'A/B/lambda' : Item(),
1962 # No-op merge still sets mergeinfo.
1963 expected_status_short = expected_status.copy(short_wc_dir)
1964 expected_status_short.tweak('', status=' M')
1966 saved_cwd = os.getcwd()
1968 os.chdir(svntest.main.work_dir)
1969 svntest.actions.run_and_verify_merge(short_wc_dir, '3', '4',
1970 sbox.repo_url,
1971 expected_output,
1972 expected_disk,
1973 expected_status_short,
1974 expected_skip,
1975 None, None, None, None, None,
1976 1, 0)
1978 os.chdir(saved_cwd)
1980 # OK, so let's commit the new lambda (r6), and then delete the
1981 # working file. Then re-run the -r3:4 merge, and see how svn deals
1982 # with a file being under version control, but missing.
1984 svntest.actions.run_and_verify_svn(None, None, [], 'add', lambda_path)
1986 # Mergeinfo prop changed so update to avoid out of date error.
1987 svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
1989 expected_output = wc.State(wc_dir, {
1990 '' : Item(verb='Sending'),
1991 'A/B/lambda' : Item(verb='Adding'),
1993 expected_status.tweak(wc_rev=5)
1994 expected_status.add({
1995 'A/B/lambda' : Item(wc_rev=6, status=' '),
1997 expected_status.tweak('', status=' ', wc_rev=6)
1998 svntest.actions.run_and_verify_commit(wc_dir,
1999 expected_output,
2000 expected_status,
2001 None, wc_dir)
2002 os.unlink(lambda_path)
2004 # Search for the comment entitled "The Merge Kluge" elsewhere in
2005 # this file, to understand why we use short_wc_dir and chdir() below.
2006 expected_output = wc.State(short_wc_dir, { })
2007 expected_disk.remove('A/B/lambda')
2008 expected_status.tweak('A/B/lambda', status='! ')
2009 os.chdir(svntest.main.work_dir)
2010 expected_status.tweak('', status=' ')
2011 # Why do we need to --ignore-ancestry? Because the previous merge of r4,
2012 # despite being inoperative, set mergeinfo for r4 on the WC. With the
2013 # advent of merge tracking this repeat merge attempt would not be attempted.
2014 # By using --ignore-ancestry we disregard the mergeinfo and *really* try to
2015 # merge into a missing path. This is another facet of issue #2898.
2016 svntest.actions.run_and_verify_merge(short_wc_dir, '3', '4',
2017 sbox.repo_url,
2018 expected_output,
2019 expected_disk,
2020 expected_status.copy(short_wc_dir),
2021 expected_skip,
2022 None, None, None, None, None,
2023 1, 0, '--ignore-ancestry')
2025 #----------------------------------------------------------------------
2026 # At one time, a merge that added items with the same name as missing
2027 # items would attempt to add the items and fail, leaving the working
2028 # copy locked and broken.
2030 def merge_into_missing(sbox):
2031 "merge into missing must not break working copy"
2033 sbox.build()
2034 wc_dir = sbox.wc_dir
2036 F_path = os.path.join(wc_dir, 'A', 'B', 'F')
2037 F_url = sbox.repo_url + '/A/B/F'
2038 Q_path = os.path.join(F_path, 'Q')
2039 foo_path = os.path.join(F_path, 'foo')
2041 svntest.actions.run_and_verify_svn(None, None, [], 'mkdir', Q_path)
2042 svntest.main.file_append(foo_path, "foo")
2043 svntest.actions.run_and_verify_svn(None, None, [], 'add', foo_path)
2045 expected_output = wc.State(wc_dir, {
2046 'A/B/F/Q' : Item(verb='Adding'),
2047 'A/B/F/foo' : Item(verb='Adding'),
2049 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
2050 expected_status.add({
2051 'A/B/F/Q' : Item(status=' ', wc_rev=2),
2052 'A/B/F/foo' : Item(status=' ', wc_rev=2),
2054 svntest.actions.run_and_verify_commit(wc_dir,
2055 expected_output,
2056 expected_status,
2057 None, wc_dir)
2059 R_path = os.path.join(Q_path, 'R')
2060 bar_path = os.path.join(R_path, 'bar')
2061 baz_path = os.path.join(Q_path, 'baz')
2062 svntest.actions.run_and_verify_svn(None, None, [], 'mkdir', R_path)
2063 svntest.main.file_append(bar_path, "bar")
2064 svntest.actions.run_and_verify_svn(None, None, [], 'add', bar_path)
2065 svntest.main.file_append(baz_path, "baz")
2066 svntest.actions.run_and_verify_svn(None, None, [], 'add', baz_path)
2068 expected_output = wc.State(wc_dir, {
2069 'A/B/F/Q/R' : Item(verb='Adding'),
2070 'A/B/F/Q/R/bar' : Item(verb='Adding'),
2071 'A/B/F/Q/baz' : Item(verb='Adding'),
2073 expected_status.add({
2074 'A/B/F/Q/R' : Item(status=' ', wc_rev=3),
2075 'A/B/F/Q/R/bar' : Item(status=' ', wc_rev=3),
2076 'A/B/F/Q/baz' : Item(status=' ', wc_rev=3),
2078 svntest.actions.run_and_verify_commit(wc_dir,
2079 expected_output,
2080 expected_status,
2081 None, wc_dir)
2083 os.unlink(foo_path)
2084 svntest.main.safe_rmtree(Q_path)
2086 expected_output = wc.State(F_path, {
2088 expected_disk = wc.State('', {
2090 expected_status = wc.State(F_path, {
2091 '' : Item(status=' ', wc_rev=1),
2092 'foo' : Item(status='! ', wc_rev=2),
2093 'Q' : Item(status='! ', wc_rev='?'),
2095 expected_skip = wc.State(F_path, {
2096 'Q' : Item(),
2097 'foo' : Item(),
2100 ### Need to real and dry-run separately since real merge notifies Q
2101 ### twice!
2102 svntest.actions.run_and_verify_merge(F_path, '1', '2', F_url,
2103 expected_output,
2104 expected_disk,
2105 expected_status,
2106 expected_skip,
2107 None, None, None, None, None,
2108 0, 0, '--dry-run')
2110 expected_status.tweak('foo', status='!M')
2111 expected_status.tweak('', status=' M')
2112 svntest.actions.run_and_verify_merge(F_path, '1', '2', F_url,
2113 expected_output,
2114 expected_disk,
2115 expected_status,
2116 expected_skip,
2117 None, None, None, None, None,
2118 0, 0)
2120 # This merge fails when it attempts to descend into the missing
2121 # directory. That's OK, there is no real need to support merge into
2122 # an incomplete working copy, so long as when it fails it doesn't
2123 # break the working copy.
2124 svntest.main.run_svn('Working copy not locked',
2125 'merge', '-r1:3', '--dry-run', F_url, F_path)
2127 svntest.main.run_svn('Working copy not locked',
2128 'merge', '-r1:3', F_url, F_path)
2130 # Check working copy is not locked.
2131 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
2132 expected_status.add({
2133 'A/B/F' : Item(status=' M', wc_rev=1),
2134 'A/B/F/foo' : Item(status='!M', wc_rev=2),
2135 'A/B/F/Q' : Item(status='! ', wc_rev='?'),
2137 svntest.actions.run_and_verify_status(wc_dir, expected_status)
2139 #----------------------------------------------------------------------
2140 # A test for issue 1738
2142 def dry_run_adds_file_with_prop(sbox):
2143 "merge --dry-run adding a new file with props"
2145 sbox.build()
2146 wc_dir = sbox.wc_dir
2148 # Commit a new file which has a property.
2149 zig_path = os.path.join(wc_dir, 'A', 'B', 'E', 'zig')
2150 svntest.main.file_append(zig_path, "zig contents")
2151 svntest.actions.run_and_verify_svn(None, None, [], 'add', zig_path)
2152 svntest.actions.run_and_verify_svn(None, None, [],
2153 'propset', 'foo', 'foo_val',
2154 zig_path)
2156 expected_output = wc.State(wc_dir, {
2157 'A/B/E/zig' : Item(verb='Adding'),
2159 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
2160 expected_status.add({
2161 'A/B/E/zig' : Item(status=' ', wc_rev=2),
2163 svntest.actions.run_and_verify_commit(wc_dir,
2164 expected_output,
2165 expected_status,
2166 None, wc_dir)
2168 # Do a regular merge of that change into a different dir.
2169 F_path = os.path.join(wc_dir, 'A', 'B', 'F')
2170 E_url = sbox.repo_url + '/A/B/E'
2172 # Search for the comment entitled "The Merge Kluge" elsewhere in
2173 # this file, to understand why we shorten and chdir() below.
2174 short_F_path = shorten_path_kludge(F_path)
2176 expected_output = wc.State(short_F_path, {
2177 'zig' : Item(status='A '),
2179 expected_disk = wc.State('', {
2180 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B/E:2'}),
2181 'zig' : Item("zig contents", {'foo':'foo_val'}),
2183 expected_skip = wc.State('', { })
2184 expected_status = None # status is optional
2186 os.chdir(svntest.main.work_dir)
2187 svntest.actions.run_and_verify_merge(short_F_path, '1', '2', E_url,
2188 expected_output,
2189 expected_disk,
2190 expected_status,
2191 expected_skip,
2192 None, None, None, None, None,
2193 1, # please check props
2194 1) # and do a dry-run also)
2196 #----------------------------------------------------------------------
2198 # Regression test for issue #1673
2199 # Merge a binary file from two URL with a common ancestry
2201 def merge_binary_with_common_ancestry(sbox):
2202 "merge binary files with common ancestry"
2204 sbox.build()
2205 wc_dir = sbox.wc_dir
2207 # Create the common ancestry path
2208 I_path = os.path.join(wc_dir, 'I')
2209 svntest.main.run_svn(None, 'mkdir', I_path)
2211 # Add a binary file to the common ancestry path
2212 theta_contents = svntest.main.file_read(
2213 os.path.join(sys.path[0], "theta.bin"), 'rb')
2214 theta_I_path = os.path.join(I_path, 'theta')
2215 svntest.main.file_write(theta_I_path, theta_contents)
2216 svntest.main.run_svn(None, 'add', theta_I_path)
2217 svntest.main.run_svn(None, 'propset', 'svn:mime-type',
2218 'application/octet-stream', theta_I_path)
2220 # Commit the ancestry
2221 expected_output = wc.State(wc_dir, {
2222 'I' : Item(verb='Adding'),
2223 'I/theta' : Item(verb='Adding (bin)'),
2226 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
2227 expected_status.add({
2228 'I' : Item(status=' ', wc_rev=2),
2229 'I/theta' : Item(status=' ', wc_rev=2),
2232 svntest.actions.run_and_verify_commit(wc_dir,
2233 expected_output, expected_status,
2234 None,
2235 wc_dir)
2237 # Create the first branch
2238 J_path = os.path.join(wc_dir, 'J')
2239 svntest.main.run_svn(None, 'copy', I_path, J_path)
2241 # Commit the first branch
2242 expected_output = wc.State(wc_dir, {
2243 'J' : Item(verb='Adding'),
2246 expected_status.add({
2247 'J' : Item(status=' ', wc_rev=3),
2248 'J/theta' : Item(status=' ', wc_rev=3),
2251 svntest.actions.run_and_verify_commit(wc_dir,
2252 expected_output, expected_status,
2253 None,
2254 wc_dir)
2256 # Create the path where the files will be merged
2257 K_path = os.path.join(wc_dir, 'K')
2258 svntest.main.run_svn(None, 'mkdir', K_path)
2260 # Commit the new path
2261 expected_output = wc.State(wc_dir, {
2262 'K' : Item(verb='Adding'),
2265 expected_status.add({
2266 'K' : Item(status=' ', wc_rev=4),
2269 svntest.actions.run_and_verify_commit(wc_dir,
2270 expected_output, expected_status,
2271 None,
2272 wc_dir)
2274 # Copy 'I/theta' to 'K/'. This file will be merged later.
2275 theta_K_path = os.path.join(K_path, 'theta')
2276 svntest.main.run_svn(None, 'copy', theta_I_path, theta_K_path)
2278 # Commit the new file
2279 expected_output = wc.State(wc_dir, {
2280 'K/theta' : Item(verb='Adding (bin)'),
2283 expected_status.add({
2284 'K/theta' : Item(status=' ', wc_rev=5),
2287 svntest.actions.run_and_verify_commit(wc_dir,
2288 expected_output, expected_status,
2289 None,
2290 wc_dir)
2292 # Modify the original ancestry 'I/theta'
2293 svntest.main.file_append(theta_I_path, "some extra junk")
2295 # Commit the modification
2296 expected_output = wc.State(wc_dir, {
2297 'I/theta' : Item(verb='Sending'),
2300 expected_status.tweak('I/theta', wc_rev=6)
2302 svntest.actions.run_and_verify_commit(wc_dir,
2303 expected_output, expected_status,
2304 None,
2305 wc_dir)
2307 # Create the second branch from the modified ancestry
2308 L_path = os.path.join(wc_dir, 'L')
2309 svntest.main.run_svn(None, 'copy', I_path, L_path)
2311 # Commit the second branch
2312 expected_output = wc.State(wc_dir, {
2313 'L' : Item(verb='Adding'),
2314 'L/theta' : Item(verb='Adding (bin)'),
2317 expected_status.add({
2318 'L' : Item(status=' ', wc_rev=7),
2319 'L/theta' : Item(status=' ', wc_rev=7),
2322 svntest.actions.run_and_verify_commit(wc_dir,
2323 expected_output, expected_status,
2324 None,
2325 wc_dir)
2327 # Now merge first ('J/') and second ('L/') branches into 'K/'
2328 saved_cwd = os.getcwd()
2330 os.chdir(K_path)
2331 theta_J_url = sbox.repo_url + '/J/theta'
2332 theta_L_url = sbox.repo_url + '/L/theta'
2333 svntest.actions.run_and_verify_svn(None,
2334 expected_merge_output(None,
2335 'U theta\n'),
2337 'merge', theta_J_url, theta_L_url)
2338 os.chdir(saved_cwd)
2340 expected_status.tweak('K/theta', status='MM')
2341 svntest.actions.run_and_verify_status(wc_dir, expected_status)
2343 #----------------------------------------------------------------------
2344 # A test for issue 1905
2345 def merge_funny_chars_on_path(sbox):
2346 "merge with funny characters (issue #1905)"
2348 sbox.build()
2349 wc_dir = sbox.wc_dir
2351 # In following lists: 'd' stands for directory, 'f' for file
2352 # targets to be added by recursive add
2353 add_by_add = [
2354 ('d', 'dir_10', 'F%lename'),
2355 ('d', 'dir%20', 'F lename'),
2356 ('d', 'dir 30', 'Filename'),
2357 ('d', 'dir 40', None),
2358 ('f', 'F lename', None),
2361 # targets to be added by 'svn mkdir' + add
2362 add_by_mkdir = [
2363 ('d', 'dir_11', 'F%lename'),
2364 ('d', 'dir%21', 'Filename'),
2365 ('d', 'dir 31', 'F lename'),
2366 ('d', 'dir 41', None),
2369 for target in add_by_add:
2370 if target[0] == 'd':
2371 target_dir = os.path.join(wc_dir, 'A', 'B', 'E', target[1])
2372 os.mkdir(target_dir)
2373 if target[2]:
2374 target_path = os.path.join(wc_dir, 'A', 'B', 'E', '%s' % target[1],
2375 target[2])
2376 svntest.main.file_append(target_path, "%s/%s" % (target[1], target[2]))
2377 svntest.actions.run_and_verify_svn(None, None, [], 'add', target_dir)
2378 elif target[0] == 'f':
2379 target_path = os.path.join(wc_dir, 'A', 'B', 'E', '%s' % target[1])
2380 svntest.main.file_append(target_path, "%s" % target[1])
2381 svntest.actions.run_and_verify_svn(None, None, [], 'add', target_path)
2382 else:
2383 raise svntest.Failure
2386 for target in add_by_mkdir:
2387 if target[0] == 'd':
2388 target_dir = os.path.join(wc_dir, 'A', 'B', 'E', target[1])
2389 svntest.actions.run_and_verify_svn(None, None, [], 'mkdir', target_dir)
2390 if target[2]:
2391 target_path = os.path.join(wc_dir, 'A', 'B', 'E', '%s' % target[1],
2392 target[2])
2393 svntest.main.file_append(target_path, "%s/%s" % (target[1], target[2]))
2394 svntest.actions.run_and_verify_svn(None, None, [], 'add', target_path)
2396 expected_output_dic = {}
2397 expected_status_dic = {}
2399 for targets in add_by_add,add_by_mkdir:
2400 for target in targets:
2401 key = 'A/B/E/%s' % target[1]
2402 expected_output_dic[key] = Item(verb='Adding')
2403 expected_status_dic[key] = Item(status=' ', wc_rev=2)
2405 if target[2]:
2406 key = 'A/B/E/%s/%s' % (target[1], target[2])
2407 expected_output_dic[key] = Item(verb='Adding')
2408 expected_status_dic[key] = Item(status=' ', wc_rev=2)
2411 expected_output = wc.State(wc_dir, expected_output_dic)
2413 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
2414 expected_status.add(expected_status_dic)
2416 svntest.actions.run_and_verify_commit(wc_dir,
2417 expected_output,
2418 expected_status,
2419 None, wc_dir)
2421 # Do a regular merge of that change into a different dir.
2422 F_path = os.path.join(wc_dir, 'A', 'B', 'F')
2423 E_url = sbox.repo_url + '/A/B/E'
2425 expected_output_dic = {}
2426 expected_disk_dic = {}
2428 for targets in add_by_add,add_by_mkdir:
2429 for target in targets:
2430 key = '%s' % target[1]
2431 expected_output_dic[key] = Item(status='A ')
2432 if target[0] == 'd':
2433 expected_disk_dic[key] = Item(None, {})
2434 elif target[0] == 'f':
2435 expected_disk_dic[key] = Item("%s" % target[1], {})
2436 else:
2437 raise svntest.Failure
2438 if target[2]:
2439 key = '%s/%s' % (target[1], target[2])
2440 expected_output_dic[key] = Item(status='A ')
2441 expected_disk_dic[key] = Item('%s/%s' % (target[1], target[2]), {})
2444 # Search for the comment entitled "The Merge Kluge" elsewhere in
2445 # this file, to understand why we shorten and chdir() below.
2446 short_F_path = shorten_path_kludge(F_path)
2448 expected_output = wc.State(short_F_path, expected_output_dic)
2450 expected_disk = wc.State('', expected_disk_dic)
2451 expected_skip = wc.State('', { })
2452 expected_status = None # status is optional
2454 saved_cwd = os.getcwd()
2456 os.chdir(svntest.main.work_dir)
2457 svntest.actions.run_and_verify_merge(short_F_path, '1', '2', E_url,
2458 expected_output,
2459 expected_disk,
2460 expected_status,
2461 expected_skip,
2462 None, None, None, None, None,
2463 0, # don't check props
2464 1) # but do a dry-run
2465 os.chdir(saved_cwd)
2467 expected_output_dic = {}
2469 for targets in add_by_add,add_by_mkdir:
2470 for target in targets:
2471 key = '%s' % target[1]
2472 expected_output_dic[key] = Item(verb='Adding')
2473 if target[2]:
2474 key = '%s/%s' % (target[1], target[2])
2475 expected_output_dic[key] = Item(verb='Adding')
2477 expected_output = wc.State(F_path, expected_output_dic)
2478 expected_output.add({
2479 '' : Item(verb='Sending'),
2482 svntest.actions.run_and_verify_commit(F_path,
2483 expected_output,
2484 None,
2485 None, wc_dir)
2487 #-----------------------------------------------------------------------
2488 # Regression test for issue #2064
2490 def merge_keyword_expansions(sbox):
2491 "merge changes to keyword expansion property"
2493 sbox.build()
2495 wcpath = sbox.wc_dir
2496 tpath = os.path.join(wcpath, "t")
2497 bpath = os.path.join(wcpath, "b")
2498 t_fpath = os.path.join(tpath, 'f')
2499 b_fpath = os.path.join(bpath, 'f')
2501 os.mkdir(tpath)
2502 svntest.main.run_svn(None, "add", tpath)
2503 # Commit r2.
2504 svntest.actions.run_and_verify_svn(None, None, [],
2505 "ci", "-m", "r2", wcpath)
2507 # Copy t to b.
2508 svntest.main.run_svn(None, "cp", tpath, bpath)
2509 # Commit r3
2510 svntest.actions.run_and_verify_svn(None, None, [],
2511 "ci", "-m", "r3", wcpath)
2513 # Add a file to t.
2514 svntest.main.file_append(t_fpath, "$Revision$")
2515 svntest.actions.run_and_verify_svn(None, None, [],
2516 'add', t_fpath)
2517 # Ask for keyword expansion in the file.
2518 svntest.actions.run_and_verify_svn(None, None, [],
2519 'propset', 'svn:keywords', 'Revision',
2520 t_fpath)
2521 # Commit r4
2522 svntest.actions.run_and_verify_svn(None, None, [],
2523 'ci', '-m', 'r4', wcpath)
2525 # Update the wc before the merge.
2526 svntest.actions.run_and_verify_svn(None, None, [],
2527 'update', wcpath)
2529 expected_status = svntest.actions.get_virginal_state(wcpath, 4)
2530 expected_status.add({
2531 't' : Item(status=' ', wc_rev=4),
2532 't/f' : Item(status=' ', wc_rev=4),
2533 'b' : Item(status=' ', wc_rev=4),
2535 svntest.actions.run_and_verify_status(wcpath, expected_status)
2537 # Do the merge.
2539 # Search for the comment entitled "The Merge Kluge" elsewhere in
2540 # this file, to understand why we shorten and chdir() below.
2541 short_bpath = shorten_path_kludge(bpath)
2543 expected_output = wc.State(short_bpath, {
2544 'f' : Item(status='A '),
2546 expected_disk = wc.State('', {
2547 'f' : Item("$Revision: 4 $"),
2549 expected_status = wc.State(short_bpath, {
2550 '' : Item(status=' M', wc_rev=4),
2551 'f' : Item(status='A ', wc_rev='-', copied='+'),
2553 expected_skip = wc.State(short_bpath, { })
2555 os.chdir(svntest.main.work_dir)
2556 svntest.actions.run_and_verify_merge(short_bpath, '2', 'HEAD',
2557 sbox.repo_url + '/t',
2558 expected_output,
2559 expected_disk,
2560 expected_status,
2561 expected_skip)
2563 #----------------------------------------------------------------------
2564 def merge_prop_change_to_deleted_target(sbox):
2565 "merge prop change into deleted target"
2566 # For issue #2132.
2567 sbox.build()
2568 wc_dir = sbox.wc_dir
2570 # Add a property to alpha.
2571 alpha_path = os.path.join(wc_dir, 'A', 'B', 'E', 'alpha')
2572 svntest.actions.run_and_verify_svn(None, None, [],
2573 'propset', 'foo', 'foo_val',
2574 alpha_path)
2576 # Commit the property add as r2.
2577 expected_output = svntest.wc.State(wc_dir, {
2578 'A/B/E/alpha' : Item(verb='Sending'),
2580 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
2581 expected_status.tweak('A/B/E/alpha', wc_rev=2, status=' ')
2582 svntest.actions.run_and_verify_commit(wc_dir,
2583 expected_output, expected_status,
2584 None, wc_dir)
2585 svntest.actions.run_and_verify_svn(None, None, [],
2586 'up', wc_dir)
2588 # Remove alpha entirely.
2589 svntest.actions.run_and_verify_svn(None, None, [], 'rm', alpha_path)
2590 expected_output = wc.State(wc_dir, {
2591 'A/B/E/alpha' : Item(verb='Deleting'),
2593 expected_status.tweak(wc_rev=2)
2594 expected_status.remove('A/B/E/alpha')
2595 svntest.actions.run_and_verify_commit(wc_dir,
2596 expected_output,
2597 expected_status,
2598 None, alpha_path)
2600 # Try merging the original propset, which applies to a target that
2601 # no longer exists. The bug would only reproduce when run from
2602 # inside the wc, so we cd in there. We have to use
2603 # --ignore-ancestry here because our merge logic will otherwise
2604 # prevent a merge of changes we already have.
2605 os.chdir(wc_dir)
2606 svntest.actions.run_and_verify_svn("Merge errored unexpectedly",
2607 svntest.verify.AnyOutput, [], 'merge',
2608 '-r1:2', '--ignore-ancestry', '.')
2611 def set_up_dir_replace(sbox):
2612 """Set up the working copy for directory replace tests, creating
2613 directory 'A/B/F/foo' with files 'new file' and 'new file2' within
2614 it (r2), and merging 'foo' onto 'C' (r3), then deleting 'A/B/F/foo'
2615 (r4)."""
2617 sbox.build()
2618 wc_dir = sbox.wc_dir
2620 C_path = os.path.join(wc_dir, 'A', 'C')
2621 F_path = os.path.join(wc_dir, 'A', 'B', 'F')
2622 F_url = sbox.repo_url + '/A/B/F'
2624 foo_path = os.path.join(F_path, 'foo')
2625 new_file = os.path.join(foo_path, "new file")
2626 new_file2 = os.path.join(foo_path, "new file 2")
2628 # Make directory foo in F, and add some files within it.
2629 svntest.actions.run_and_verify_svn(None, None, [], 'mkdir', foo_path)
2630 svntest.main.file_append(new_file, "Initial text in new file.\n")
2631 svntest.main.file_append(new_file2, "Initial text in new file 2.\n")
2632 svntest.main.run_svn(None, "add", new_file)
2633 svntest.main.run_svn(None, "add", new_file2)
2635 # Commit all the new content, creating r2.
2636 expected_output = wc.State(wc_dir, {
2637 'A/B/F/foo' : Item(verb='Adding'),
2638 'A/B/F/foo/new file' : Item(verb='Adding'),
2639 'A/B/F/foo/new file 2' : Item(verb='Adding'),
2641 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
2642 expected_status.add({
2643 'A/B/F/foo' : Item(status=' ', wc_rev=2),
2644 'A/B/F/foo/new file' : Item(status=' ', wc_rev=2),
2645 'A/B/F/foo/new file 2' : Item(status=' ', wc_rev=2),
2647 svntest.actions.run_and_verify_commit(wc_dir,
2648 expected_output,
2649 expected_status,
2650 None, wc_dir)
2652 # Merge foo onto C
2653 expected_output = wc.State(C_path, {
2654 'foo' : Item(status='A '),
2655 'foo/new file' : Item(status='A '),
2656 'foo/new file 2' : Item(status='A '),
2658 expected_disk = wc.State('', {
2659 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B/F:2'}),
2660 'foo' : Item(),
2661 'foo/new file' : Item("Initial text in new file.\n"),
2662 'foo/new file 2' : Item("Initial text in new file 2.\n"),
2664 expected_status = wc.State(C_path, {
2665 '' : Item(status=' M', wc_rev=1),
2666 'foo' : Item(status='A ', wc_rev='-', copied='+'),
2667 'foo/new file' : Item(status='A ', wc_rev='-', copied='+'),
2668 'foo/new file 2' : Item(status='A ', wc_rev='-', copied='+'),
2670 expected_skip = wc.State(C_path, { })
2671 svntest.actions.run_and_verify_merge(C_path, '1', '2', F_url,
2672 expected_output,
2673 expected_disk,
2674 expected_status,
2675 expected_skip,
2676 None, None, None, None, None, 1)
2677 # Commit merge of foo onto C, creating r3.
2678 expected_output = svntest.wc.State(wc_dir, {
2679 'A/C' : Item(verb='Sending'),
2680 'A/C/foo' : Item(verb='Adding'),
2681 'A/C/foo/new file' : Item(verb='Adding'),
2682 'A/C/foo/new file 2' : Item(verb='Adding'),
2684 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
2685 expected_status.add({
2686 'A/B/F/foo' : Item(status=' ', wc_rev=2),
2687 'A/C' : Item(status=' ', wc_rev=3),
2688 'A/B/F/foo/new file' : Item(status=' ', wc_rev=2),
2689 'A/B/F/foo/new file 2' : Item(status=' ', wc_rev=2),
2690 'A/C/foo' : Item(status=' ', wc_rev=3),
2691 'A/C/foo/new file' : Item(status=' ', wc_rev=3),
2692 'A/C/foo/new file 2' : Item(status=' ', wc_rev=3),
2695 svntest.actions.run_and_verify_commit(wc_dir,
2696 expected_output,
2697 expected_status,
2698 None, wc_dir)
2700 # Delete foo on F, creating r4.
2701 svntest.actions.run_and_verify_svn(None, None, [], 'rm', foo_path)
2702 expected_output = svntest.wc.State(wc_dir, {
2703 'A/B/F/foo' : Item(verb='Deleting'),
2705 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
2706 expected_status.add({
2707 'A/C' : Item(status=' ', wc_rev=3),
2708 'A/C/foo' : Item(status=' ', wc_rev=3),
2709 'A/C/foo/new file' : Item(status=' ', wc_rev=3),
2710 'A/C/foo/new file 2' : Item(status=' ', wc_rev=3),
2712 svntest.actions.run_and_verify_commit(wc_dir,
2713 expected_output,
2714 expected_status,
2715 None, wc_dir)
2717 #----------------------------------------------------------------------
2718 # A merge that replaces a directory
2719 # Tests for Issue #2144 and Issue #2607
2721 def merge_dir_replace(sbox):
2722 "merge a replacement of a directory"
2724 set_up_dir_replace(sbox)
2725 wc_dir = sbox.wc_dir
2727 C_path = os.path.join(wc_dir, 'A', 'C')
2728 F_path = os.path.join(wc_dir, 'A', 'B', 'F')
2729 F_url = sbox.repo_url + '/A/B/F'
2730 foo_path = os.path.join(F_path, 'foo')
2731 new_file2 = os.path.join(foo_path, "new file 2")
2733 # Recreate foo in F and add a new folder and two files
2734 bar_path = os.path.join(foo_path, 'bar')
2735 foo_file = os.path.join(foo_path, "file foo")
2736 new_file3 = os.path.join(bar_path, "new file 3")
2738 # Make a couple of directories, and add some files within them.
2739 svntest.actions.run_and_verify_svn(None, None, [], 'mkdir', foo_path)
2740 svntest.actions.run_and_verify_svn(None, None, [], 'mkdir', bar_path)
2741 svntest.main.file_append(new_file3, "Initial text in new file 3.\n")
2742 svntest.main.run_svn(None, "add", new_file3)
2743 svntest.main.file_append(foo_file, "Initial text in file foo.\n")
2744 svntest.main.run_svn(None, "add", foo_file)
2746 # Commit the new content, creating r5.
2747 expected_output = wc.State(wc_dir, {
2748 'A/B/F/foo' : Item(verb='Adding'),
2749 'A/B/F/foo/file foo' : Item(verb='Adding'),
2750 'A/B/F/foo/bar' : Item(verb='Adding'),
2751 'A/B/F/foo/bar/new file 3' : Item(verb='Adding'),
2753 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
2754 expected_status.add({
2755 'A/B/F/foo' : Item(status=' ', wc_rev=5),
2756 'A/B/F/foo/file foo' : Item(status=' ', wc_rev=5),
2757 'A/B/F/foo/bar' : Item(status=' ', wc_rev=5),
2758 'A/B/F/foo/bar/new file 3' : Item(status=' ', wc_rev=5),
2759 'A/C' : Item(status=' ', wc_rev=3),
2760 'A/C/foo' : Item(status=' ', wc_rev=3),
2761 'A/C/foo/new file' : Item(status=' ', wc_rev=3),
2762 'A/C/foo/new file 2' : Item(status=' ', wc_rev=3),
2764 svntest.actions.run_and_verify_commit(wc_dir,
2765 expected_output,
2766 expected_status,
2767 None, wc_dir)
2768 # Merge replacement of foo onto C
2769 expected_output = wc.State(C_path, {
2770 'foo' : Item(status='R '),
2771 'foo/file foo' : Item(status='A '),
2772 'foo/bar' : Item(status='A '),
2773 'foo/bar/new file 3' : Item(status='A '),
2775 expected_disk = wc.State('', {
2776 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B/F:2-5'}),
2777 'foo' : Item(),
2778 'foo/file foo' : Item("Initial text in file foo.\n"),
2779 'foo/bar' : Item(),
2780 'foo/bar/new file 3' : Item("Initial text in new file 3.\n"),
2782 expected_status = wc.State(C_path, {
2783 '' : Item(status=' M', wc_rev=3),
2784 'foo' : Item(status='R ', wc_rev='-', copied='+'),
2785 'foo/new file 2' : Item(status='D ', wc_rev='-', copied='+'),
2786 'foo/file foo' : Item(status='A ', wc_rev='-', copied='+'),
2787 'foo/bar' : Item(status='A ', wc_rev='-', copied='+'),
2788 'foo/bar/new file 3' : Item(status='A ', wc_rev='-', copied='+'),
2789 'foo/new file' : Item(status='D ', wc_rev='-', copied='+'),
2791 expected_skip = wc.State(C_path, { })
2792 svntest.actions.run_and_verify_merge(C_path, '2', '5', F_url,
2793 expected_output,
2794 expected_disk,
2795 expected_status,
2796 expected_skip,
2797 None, None, None, None, None,
2799 0) # don't do a dry-run
2800 # the output differs
2802 # Commit merge of foo onto C
2803 expected_output = svntest.wc.State(wc_dir, {
2804 'A/C' : Item(verb='Sending'),
2805 'A/C/foo' : Item(verb='Replacing'),
2806 'A/C/foo/file foo' : Item(verb='Adding'),
2807 'A/C/foo/bar' : Item(verb='Adding'),
2808 'A/C/foo/bar/new file 3' : Item(verb='Adding'),
2809 'A/C/foo/new file' : Item(verb='Deleting'),
2810 'A/C/foo/new file 2' : Item(verb='Deleting'),
2812 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
2813 expected_status.add({
2814 'A/B/F/foo' : Item(status=' ', wc_rev=5),
2815 'A/B/F/foo/file foo' : Item(status=' ', wc_rev=5),
2816 'A/B/F/foo/bar' : Item(status=' ', wc_rev=5),
2817 'A/B/F/foo/bar/new file 3' : Item(status=' ', wc_rev=5),
2818 'A/C' : Item(status=' ', wc_rev=6),
2819 'A/C/foo' : Item(status=' ', wc_rev=6),
2820 'A/C/foo/file foo' : Item(status=' ', wc_rev=6),
2821 'A/C/foo/bar' : Item(status=' ', wc_rev=6),
2822 'A/C/foo/bar/new file 3' : Item(status=' ', wc_rev=6),
2824 svntest.actions.run_and_verify_commit(wc_dir,
2825 expected_output,
2826 expected_status,
2827 None, wc_dir)
2829 #----------------------------------------------------------------------
2830 # A merge that replaces a directory and one of its children
2831 # Tests for Issue #2690
2833 def merge_dir_and_file_replace(sbox):
2834 "replace both dir and one of its children"
2836 set_up_dir_replace(sbox)
2837 wc_dir = sbox.wc_dir
2839 C_path = os.path.join(wc_dir, 'A', 'C')
2840 F_path = os.path.join(wc_dir, 'A', 'B', 'F')
2841 F_url = sbox.repo_url + '/A/B/F'
2842 foo_path = os.path.join(F_path, 'foo')
2843 new_file2 = os.path.join(foo_path, "new file 2")
2845 # Recreate foo and 'new file 2' in F and add a new folder with a file
2846 bar_path = os.path.join(foo_path, 'bar')
2847 new_file3 = os.path.join(bar_path, "new file 3")
2848 svntest.actions.run_and_verify_svn(None, None, [], 'mkdir', foo_path)
2849 svntest.actions.run_and_verify_svn(None, None, [], 'mkdir', bar_path)
2850 svntest.main.file_append(new_file3, "Initial text in new file 3.\n")
2851 svntest.main.run_svn(None, "add", new_file3)
2852 svntest.main.file_append(new_file2, "New text in new file 2.\n")
2853 svntest.main.run_svn(None, "add", new_file2)
2855 expected_output = wc.State(wc_dir, {
2856 'A/B/F/foo' : Item(verb='Adding'),
2857 'A/B/F/foo/new file 2' : Item(verb='Adding'),
2858 'A/B/F/foo/bar' : Item(verb='Adding'),
2859 'A/B/F/foo/bar/new file 3' : Item(verb='Adding'),
2861 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
2862 expected_status.add({
2863 'A/B/F/foo' : Item(status=' ', wc_rev=5),
2864 'A/B/F/foo/new file 2' : Item(status=' ', wc_rev=5),
2865 'A/B/F/foo/bar' : Item(status=' ', wc_rev=5),
2866 'A/B/F/foo/bar/new file 3' : Item(status=' ', wc_rev=5),
2867 'A/C/foo' : Item(status=' ', wc_rev=3),
2868 'A/C/foo/new file' : Item(status=' ', wc_rev=3),
2869 'A/C/foo/new file 2' : Item(status=' ', wc_rev=3),
2871 svntest.actions.run_and_verify_commit(wc_dir,
2872 expected_output,
2873 expected_status,
2874 None, wc_dir)
2875 # Merge replacement of foo onto C
2876 expected_output = wc.State(C_path, {
2877 'foo' : Item(status='D '),
2878 'foo' : Item(status='A '),
2879 'foo/new file 2' : Item(status='D '),
2880 'foo/new file 2' : Item(status='A '),
2881 'foo/bar' : Item(status='A '),
2882 'foo/bar/new file 3' : Item(status='A '),
2883 'foo/new file' : Item(status='D '),
2885 expected_disk = wc.State('', {
2886 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B/F:2-5'}),
2887 'foo' : Item(),
2888 'foo/new file 2' : Item("New text in new file 2.\n"),
2889 'foo/bar' : Item(),
2890 'foo/bar/new file 3' : Item("Initial text in new file 3.\n"),
2892 expected_status = wc.State(C_path, {
2893 '' : Item(status=' ', wc_rev=1),
2894 'foo' : Item(status='R ', wc_rev='-', copied='+'),
2895 'foo/new file 2' : Item(status='R ', wc_rev='-', copied='+'),
2896 'foo/bar' : Item(status='A ', wc_rev='-', copied='+'),
2897 'foo/bar/new file 3' : Item(status='A ', wc_rev='-', copied='+'),
2898 'foo/new file' : Item(status='D ', wc_rev='-', copied='+'),
2900 expected_skip = wc.State(C_path, { })
2901 svntest.actions.run_and_verify_merge(C_path, '2', '5', F_url,
2902 expected_output,
2903 expected_disk,
2904 expected_status,
2905 expected_skip,
2906 None, None, None, None, None,
2908 0) # don't do a dry-run
2909 # the output differs
2911 # Commit merge of foo onto C
2912 expected_output = svntest.wc.State(wc_dir, {
2913 'A/C/foo' : Item(verb='Replacing'),
2914 'A/C/foo/new file 2' : Item(verb='Replacing'),
2915 'A/C/foo/new file' : Item(verb='Deleting'),
2916 'A/C/foo/bar' : Item(verb='Adding'),
2917 'A/C/foo/bar/new file 3' : Item(verb='Adding'),
2920 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
2921 expected_status.add({
2922 'A/B/F/foo' : Item(status=' ', wc_rev=5),
2923 'A/B/F/foo/new file 2' : Item(status=' ', wc_rev=5),
2924 'A/B/F/foo/bar' : Item(status=' ', wc_rev=5),
2925 'A/B/F/foo/bar/new file 3' : Item(status=' ', wc_rev=5),
2926 'A/C/foo' : Item(status=' ', wc_rev=6),
2927 'A/C/foo/new file 2' : Item(status=' ', wc_rev=6),
2928 'A/C/foo/bar' : Item(status=' ', wc_rev=6),
2929 'A/C/foo/bar/new file 3' : Item(status=' ', wc_rev=6),
2931 svntest.actions.run_and_verify_commit(wc_dir,
2932 expected_output,
2933 expected_status,
2934 None, wc_dir)
2936 #----------------------------------------------------------------------
2937 def merge_file_with_space_in_its_name(sbox):
2938 "merge a file whose name contains a space"
2939 # For issue #2144
2940 sbox.build()
2941 wc_dir = sbox.wc_dir
2942 new_file = os.path.join(wc_dir, "new file")
2944 # Make r2.
2945 svntest.main.file_append(new_file, "Initial text in the file.\n")
2946 svntest.main.run_svn(None, "add", new_file)
2947 svntest.actions.run_and_verify_svn(None, None, [],
2948 "ci", "-m", "r2", wc_dir)
2950 # Make r3.
2951 svntest.main.file_append(new_file, "Next line of text in the file.\n")
2952 svntest.actions.run_and_verify_svn(None, None, [],
2953 "ci", "-m", "r3", wc_dir)
2955 # Try to reverse merge.
2957 # The reproduction recipe requires that no explicit merge target be
2958 # passed, so we run merge from inside the wc dir where the target
2959 # file (i.e., the URL basename) lives.
2960 os.chdir(wc_dir)
2961 target_url = sbox.repo_url + '/new%20file'
2962 svntest.actions.run_and_verify_svn(None, None, [],
2963 "merge", "-r3:2", target_url)
2965 #----------------------------------------------------------------------
2966 # A merge between two branches using no revision number with the dir being
2967 # created already existing as an unversioned directory.
2968 # Tests for Issue #2222
2970 def merge_dir_branches(sbox):
2971 "merge between branches (Issue #2222)"
2973 sbox.build()
2974 wc_dir = sbox.wc_dir
2976 F_path = os.path.join(wc_dir, 'A', 'B', 'F')
2977 F_url = sbox.repo_url + '/A/B/F'
2978 C_url = sbox.repo_url + '/A/C'
2980 # Create foo in F
2981 foo_path = os.path.join(F_path, 'foo')
2982 svntest.actions.run_and_verify_svn(None, None, [], 'mkdir', foo_path)
2984 expected_output = wc.State(wc_dir, {
2985 'A/B/F/foo' : Item(verb='Adding'),
2987 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
2988 expected_status.add({
2989 'A/B/F/foo' : Item(status=' ', wc_rev=2),
2991 svntest.actions.run_and_verify_commit(wc_dir,
2992 expected_output,
2993 expected_status,
2994 None, wc_dir)
2996 # Create an unversioned foo
2997 foo_path = os.path.join(wc_dir, 'foo')
2998 os.mkdir(foo_path)
3000 # Merge from C to F onto the wc_dir
3001 # We can't use run_and_verify_merge because it doesn't support this
3002 # syntax of the merge command.
3003 ### TODO: We can use run_and_verify_merge2() here now.
3004 expected_output = expected_merge_output(None, "A " + foo_path + "\n")
3005 svntest.actions.run_and_verify_svn(None, expected_output, [],
3006 'merge', C_url, F_url, wc_dir)
3008 # Run info to check the copied rev to make sure it's right
3009 expected_output = ["Path: " + foo_path + "\n",
3010 "URL: " + sbox.repo_url + "/foo\n",
3011 "Repository Root: " + sbox.repo_url + "\n",
3012 "Revision: 2\n",
3013 "Node Kind: directory\n",
3014 "Schedule: add\n",
3015 "Copied From URL: " + F_url + "/foo\n",
3016 "Copied From Rev: 2\n", "\n"]
3017 svntest.actions.run_and_verify_svn(None, expected_output, [],
3018 'info', foo_path)
3021 #----------------------------------------------------------------------
3023 def safe_property_merge(sbox):
3024 "property merges don't overwrite existing prop-mods"
3026 sbox.build()
3027 wc_dir = sbox.wc_dir
3029 # Add a property to two files and a directory, commit as r2.
3030 alpha_path = os.path.join(wc_dir, 'A', 'B', 'E', 'alpha')
3031 beta_path = os.path.join(wc_dir, 'A', 'B', 'E', 'beta')
3032 E_path = os.path.join(wc_dir, 'A', 'B', 'E')
3034 svntest.actions.run_and_verify_svn(None, None, [],
3035 'propset', 'foo', 'foo_val',
3036 alpha_path, beta_path)
3037 svntest.actions.run_and_verify_svn(None, None, [],
3038 'propset', 'foo', 'foo_val',
3039 E_path)
3041 expected_output = svntest.wc.State(wc_dir, {
3042 'A/B/E' : Item(verb='Sending'),
3043 'A/B/E/alpha' : Item(verb='Sending'),
3044 'A/B/E/beta' : Item(verb='Sending'),
3046 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
3047 expected_status.tweak('A/B/E', 'A/B/E/alpha', 'A/B/E/beta',
3048 wc_rev=2, status=' ')
3049 svntest.actions.run_and_verify_commit(wc_dir,
3050 expected_output, expected_status,
3051 None, wc_dir)
3052 svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
3054 # Copy B to B2 as rev 3 (making a branch)
3055 B_url = sbox.repo_url + '/A/B'
3056 B2_url = sbox.repo_url + '/A/B2'
3058 svntest.actions.run_and_verify_svn(None, None, [],
3059 'copy', '-m', 'copy B to B2',
3060 B_url, B2_url)
3061 svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
3063 # Change the properties underneath B again, and commit as r4
3064 svntest.actions.run_and_verify_svn(None, None, [],
3065 'propset', 'foo', 'foo_val2',
3066 alpha_path)
3067 svntest.actions.run_and_verify_svn(None, None, [],
3068 'propdel', 'foo',
3069 beta_path)
3070 svntest.actions.run_and_verify_svn(None, None, [],
3071 'propset', 'foo', 'foo_val2',
3072 E_path)
3073 expected_output = svntest.wc.State(wc_dir, {
3074 'A/B/E' : Item(verb='Sending'),
3075 'A/B/E/alpha' : Item(verb='Sending'),
3076 'A/B/E/beta' : Item(verb='Sending'),
3078 svntest.actions.run_and_verify_commit(wc_dir,
3079 expected_output, None,
3080 None, wc_dir)
3081 svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
3083 # Make local propchanges to E, alpha and beta in the branch.
3084 alpha_path2 = os.path.join(wc_dir, 'A', 'B2', 'E', 'alpha')
3085 beta_path2 = os.path.join(wc_dir, 'A', 'B2', 'E', 'beta')
3086 E_path2 = os.path.join(wc_dir, 'A', 'B2', 'E')
3088 svntest.actions.run_and_verify_svn(None, None, [],
3089 'propset', 'foo', 'branchval',
3090 alpha_path2, beta_path2)
3091 svntest.actions.run_and_verify_svn(None, None, [],
3092 'propset', 'foo', 'branchval',
3093 E_path2)
3095 # Now merge the recent B change to the branch. Because we already
3096 # have local propmods, we should get property conflicts.
3097 B2_path = os.path.join(wc_dir, 'A', 'B2')
3099 expected_output = wc.State(B2_path, {
3100 'E' : Item(status=' C'),
3101 'E/alpha' : Item(status=' C'),
3102 'E/beta' : Item(status=' C'),
3105 expected_disk = wc.State('', {
3106 '' : Item(props={SVN_PROP_MERGEINFO : "/A/B:4"}),
3107 'E' : Item(),
3108 'E/alpha' : Item("This is the file 'alpha'.\n"),
3109 'E/beta' : Item("This is the file 'beta'.\n"),
3110 'F' : Item(),
3111 'lambda' : Item("This is the file 'lambda'.\n"),
3113 expected_disk.tweak('E', 'E/alpha', 'E/beta',
3114 props={'foo' : 'branchval'}) # local mods still present
3116 expected_status = wc.State(B2_path, {
3117 '' : Item(status=' M'),
3118 'E' : Item(status=' C'),
3119 'E/alpha' : Item(status=' C'),
3120 'E/beta' : Item(status=' C'),
3121 'F' : Item(status=' '),
3122 'lambda' : Item(status=' '),
3124 expected_status.tweak(wc_rev=4)
3126 expected_skip = wc.State('', { })
3128 # should have 3 'prej' files left behind, describing prop conflicts:
3129 extra_files = ['alpha.*\.prej', 'beta.*\.prej', 'dir_conflicts.*\.prej']
3131 svntest.actions.run_and_verify_merge(B2_path, '3', '4', B_url,
3132 expected_output,
3133 expected_disk,
3134 expected_status,
3135 expected_skip,
3136 None, # expected error string
3137 svntest.tree.detect_conflict_files,
3138 extra_files,
3139 None, None, # no B singleton handler
3140 1, # check props
3141 0) # dry_run
3143 #----------------------------------------------------------------------
3145 # Test for issue 2035, whereby 'svn merge' wouldn't always mark
3146 # property conflicts when it should.
3148 def property_merge_from_branch(sbox):
3149 "property merge conflict even without local mods"
3151 sbox.build()
3152 wc_dir = sbox.wc_dir
3154 # Add a property to a file and a directory, commit as r2.
3155 alpha_path = os.path.join(wc_dir, 'A', 'B', 'E', 'alpha')
3156 E_path = os.path.join(wc_dir, 'A', 'B', 'E')
3158 svntest.actions.run_and_verify_svn(None, None, [],
3159 'propset', 'foo', 'foo_val',
3160 alpha_path)
3161 svntest.actions.run_and_verify_svn(None, None, [],
3162 'propset', 'foo', 'foo_val',
3163 E_path)
3165 expected_output = svntest.wc.State(wc_dir, {
3166 'A/B/E' : Item(verb='Sending'),
3167 'A/B/E/alpha' : Item(verb='Sending'),
3169 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
3170 expected_status.tweak('A/B/E', 'A/B/E/alpha', wc_rev=2, status=' ')
3171 svntest.actions.run_and_verify_commit(wc_dir,
3172 expected_output, expected_status,
3173 None, wc_dir)
3174 svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
3176 # Copy B to B2 as rev 3 (making a branch)
3177 B_url = sbox.repo_url + '/A/B'
3178 B2_url = sbox.repo_url + '/A/B2'
3180 svntest.actions.run_and_verify_svn(None, None, [],
3181 'copy', '-m', 'copy B to B2',
3182 B_url, B2_url)
3183 svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
3185 # Change the properties underneath B again, and commit as r4
3186 svntest.actions.run_and_verify_svn(None, None, [],
3187 'propset', 'foo', 'foo_val2',
3188 alpha_path)
3189 svntest.actions.run_and_verify_svn(None, None, [],
3190 'propset', 'foo', 'foo_val2',
3191 E_path)
3192 expected_output = svntest.wc.State(wc_dir, {
3193 'A/B/E' : Item(verb='Sending'),
3194 'A/B/E/alpha' : Item(verb='Sending'),
3196 svntest.actions.run_and_verify_commit(wc_dir,
3197 expected_output, None,
3198 None, wc_dir)
3199 svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
3201 # Make different propchanges changes to the B2 branch and commit as r5.
3202 alpha_path2 = os.path.join(wc_dir, 'A', 'B2', 'E', 'alpha')
3203 E_path2 = os.path.join(wc_dir, 'A', 'B2', 'E')
3205 svntest.actions.run_and_verify_svn(None, None, [],
3206 'propset', 'foo', 'branchval',
3207 alpha_path2)
3208 svntest.actions.run_and_verify_svn(None, None, [],
3209 'propset', 'foo', 'branchval',
3210 E_path2)
3211 expected_output = svntest.wc.State(wc_dir, {
3212 'A/B2/E' : Item(verb='Sending'),
3213 'A/B2/E/alpha' : Item(verb='Sending'),
3215 svntest.actions.run_and_verify_commit(wc_dir,
3216 expected_output, None,
3217 None, wc_dir)
3218 svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
3220 # Now merge the recent B change to the branch. There are no local
3221 # mods anywhere, but we should still get property conflicts anyway!
3222 B2_path = os.path.join(wc_dir, 'A', 'B2')
3224 expected_output = wc.State(B2_path, {
3225 'E' : Item(status=' C'),
3226 'E/alpha' : Item(status=' C'),
3229 expected_disk = wc.State('', {
3230 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B:4'}),
3231 'E' : Item(),
3232 'E/alpha' : Item("This is the file 'alpha'.\n"),
3233 'E/beta' : Item("This is the file 'beta'.\n"),
3234 'F' : Item(),
3235 'lambda' : Item("This is the file 'lambda'.\n"),
3237 expected_disk.tweak('E', 'E/alpha',
3238 props={'foo' : 'branchval'})
3240 expected_status = wc.State(B2_path, {
3241 '' : Item(status=' M'),
3242 'E' : Item(status=' C'),
3243 'E/alpha' : Item(status=' C'),
3244 'E/beta' : Item(status=' '),
3245 'F' : Item(status=' '),
3246 'lambda' : Item(status=' '),
3248 expected_status.tweak(wc_rev=5)
3250 expected_skip = wc.State('', { })
3252 # should have 2 'prej' files left behind, describing prop conflicts:
3253 extra_files = ['alpha.*\.prej', 'dir_conflicts.*\.prej']
3255 svntest.actions.run_and_verify_merge(B2_path, '3', '4', B_url,
3256 expected_output,
3257 expected_disk,
3258 expected_status,
3259 expected_skip,
3260 None, # expected error string
3261 svntest.tree.detect_conflict_files,
3262 extra_files,
3263 None, None, # no B singleton handler
3264 1, # check props
3265 0) # dry_run
3267 #----------------------------------------------------------------------
3269 # Another test for issue 2035, whereby sometimes 'svn merge' marked
3270 # property conflicts when it shouldn't!
3272 def property_merge_undo_redo(sbox):
3273 "undo, then redo a property merge"
3275 sbox.build()
3276 wc_dir = sbox.wc_dir
3278 # Add a property to a file, commit as r2.
3279 alpha_path = os.path.join(wc_dir, 'A', 'B', 'E', 'alpha')
3280 svntest.actions.run_and_verify_svn(None, None, [],
3281 'propset', 'foo', 'foo_val',
3282 alpha_path)
3284 expected_output = svntest.wc.State(wc_dir, {
3285 'A/B/E/alpha' : Item(verb='Sending'),
3287 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
3288 expected_status.tweak('A/B/E/alpha', wc_rev=2, status=' ')
3289 svntest.actions.run_and_verify_commit(wc_dir,
3290 expected_output, expected_status,
3291 None, wc_dir)
3292 svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
3294 # Use 'svn merge' to undo the commit. ('svn merge -r2:1')
3295 # Result should be a single local-prop-mod.
3296 expected_output = wc.State(wc_dir, {'A/B/E/alpha' : Item(status=' U'), })
3298 expected_disk = svntest.main.greek_state.copy()
3300 expected_status = svntest.actions.get_virginal_state(wc_dir, 2)
3301 expected_status.tweak('A/B/E/alpha', status=' M')
3303 expected_skip = wc.State('', { })
3305 svntest.actions.run_and_verify_merge(wc_dir, '2', '1',
3306 sbox.repo_url,
3307 expected_output,
3308 expected_disk,
3309 expected_status,
3310 expected_skip,
3311 None, # expected error string
3312 None, None, # no A singleton handler
3313 None, None, # no B singleton handler
3314 1, # check props
3315 0) # dry_run
3317 # Change mind, re-apply the change ('svn merge -r1:2').
3318 # This should merge cleanly into existing prop-mod, status shows nothing.
3319 expected_output = wc.State(wc_dir, {'A/B/E/alpha' : Item(status=' C'), })
3321 expected_disk = svntest.main.greek_state.copy()
3322 expected_disk.add({'A/B/E/alpha.prej'
3323 : Item("Trying to create property 'foo' with value 'foo_val',\n"
3324 + "but it has been locally deleted.\n")})
3326 expected_status = svntest.actions.get_virginal_state(wc_dir, 2)
3327 expected_status.tweak('A/B/E/alpha', status=' C')
3329 expected_skip = wc.State('', { })
3331 # Re-merge r1. We have to use --ignore-ancestry here. Otherwise
3332 # the merge logic will claim we already have this change (because it
3333 # was unable to record the previous undoing merge).
3334 svntest.actions.run_and_verify_merge(wc_dir, '1', '2',
3335 sbox.repo_url,
3336 expected_output,
3337 expected_disk,
3338 expected_status,
3339 expected_skip,
3340 None, # expected error string
3341 None, None, # no A singleton handler
3342 None, None, # no B singleton handler
3343 1, # check props
3344 0, # dry_run
3345 '--ignore-ancestry')
3349 #----------------------------------------------------------------------
3350 def cherry_pick_text_conflict(sbox):
3351 "cherry-pick a dependent change, get conflict"
3353 sbox.build()
3354 wc_dir = sbox.wc_dir
3356 A_path = os.path.join(wc_dir, 'A')
3357 A_url = sbox.repo_url + '/A'
3358 mu_path = os.path.join(A_path, 'mu')
3359 branch_A_url = sbox.repo_url + '/copy-of-A'
3360 branch_mu_path = os.path.join(wc_dir, 'copy-of-A', 'mu')
3362 # Create a branch of A.
3363 svntest.actions.run_and_verify_svn(None, None, [], 'cp',
3364 A_url, branch_A_url,
3365 '-m', "Creating copy-of-A")
3367 # Update to get the branch.
3368 svntest.actions.run_and_verify_svn(None, None, [],
3369 'update', wc_dir)
3371 # Change mu's text on the branch, producing r3 through r6.
3372 for rev in range(3, 7):
3373 svntest.main.file_append(branch_mu_path, ("r%d\n" % rev) * 3)
3374 svntest.actions.run_and_verify_svn(None, None, [],
3375 'ci', '-m',
3376 'Add lines to mu in r%d.' % rev, wc_dir)
3378 # Mark r5 as merged into trunk, to create disparate revision ranges
3379 # which need to be merged.
3380 svntest.actions.run_and_verify_svn(None, [], [],
3381 'merge', '-c5', '--record-only',
3382 branch_A_url, A_path)
3385 # Try to merge r4:6 into trunk, without r3. It should fail.
3386 expected_output = wc.State(A_path, {
3387 'mu' : Item(status='C '),
3389 expected_disk = wc.State('', {
3390 'mu' : Item("This is the file 'mu'.\n"
3391 + make_conflict_marker_text("r3\n" * 3, "r4\n" * 3, 4)),
3392 'B' : Item(),
3393 'B/lambda' : Item("This is the file 'lambda'.\n"),
3394 'B/E' : Item(),
3395 'B/E/alpha' : Item("This is the file 'alpha'.\n"),
3396 'B/E/beta' : Item("This is the file 'beta'.\n"),
3397 'B/F' : Item(),
3398 'C' : Item(),
3399 'D' : Item(),
3400 'D/gamma' : Item("This is the file 'gamma'.\n"),
3401 'D/H' : Item(),
3402 'D/H/chi' : Item("This is the file 'chi'.\n"),
3403 'D/H/psi' : Item("This is the file 'psi'.\n"),
3404 'D/H/omega' : Item("This is the file 'omega'.\n"),
3405 'D/G' : Item(),
3406 'D/G/pi' : Item("This is the file 'pi'.\n"),
3407 'D/G/rho' : Item("This is the file 'rho'.\n"),
3408 'D/G/tau' : Item("This is the file 'tau'.\n"),
3410 expected_status = wc.State(A_path, {
3411 '' : Item(status=' M'),
3412 'mu' : Item(status='C '),
3413 'B' : Item(status=' '),
3414 'B/lambda' : Item(status=' '),
3415 'B/E' : Item(status=' '),
3416 'B/E/alpha' : Item(status=' '),
3417 'B/E/beta' : Item(status=' '),
3418 'B/F' : Item(status=' '),
3419 'C' : Item(status=' '),
3420 'D' : Item(status=' '),
3421 'D/gamma' : Item(status=' '),
3422 'D/H' : Item(status=' '),
3423 'D/H/chi' : Item(status=' '),
3424 'D/H/psi' : Item(status=' '),
3425 'D/H/omega' : Item(status=' '),
3426 'D/G' : Item(status=' '),
3427 'D/G/pi' : Item(status=' '),
3428 'D/G/rho' : Item(status=' '),
3429 'D/G/tau' : Item(status=' '),
3431 expected_status.tweak(wc_rev=2)
3432 expected_skip = wc.State('', { })
3433 expected_error = "conflicts were produced while merging r3:4"
3434 svntest.actions.run_and_verify_merge(A_path, '3', '6', branch_A_url,
3435 expected_output,
3436 expected_disk,
3437 expected_status,
3438 expected_skip,
3439 expected_error,
3440 svntest.tree.detect_conflict_files,
3441 ["mu\.working",
3442 "mu\.merge-right\.r4",
3443 "mu\.merge-left\.r3"],
3444 None, None, # no singleton handler
3445 0, # don't check props
3446 0) # not a dry_run
3450 # Test for issue 2135
3451 def merge_file_replace(sbox):
3452 "merge a replacement of a file"
3454 sbox.build()
3455 wc_dir = sbox.wc_dir
3457 # File scheduled for deletion
3458 rho_path = os.path.join(wc_dir, 'A', 'D', 'G', 'rho')
3459 svntest.actions.run_and_verify_svn(None, None, [], 'rm', rho_path)
3461 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
3462 expected_status.tweak('A/D/G/rho', status='D ')
3463 svntest.actions.run_and_verify_status(wc_dir, expected_status)
3465 expected_output = svntest.wc.State(wc_dir, {
3466 'A/D/G/rho': Item(verb='Deleting'),
3469 expected_status.remove('A/D/G/rho')
3471 # Commit rev 2
3472 svntest.actions.run_and_verify_commit(wc_dir,
3473 expected_output,
3474 expected_status,
3475 None, wc_dir)
3476 # Create and add a new file.
3477 svntest.main.file_write(rho_path, "new rho\n")
3478 svntest.actions.run_and_verify_svn(None, None, [], 'add', rho_path)
3480 # Commit revsion 3
3481 expected_status.add({
3482 'A/D/G/rho' : Item(status='A ', wc_rev='0')
3484 svntest.actions.run_and_verify_status(wc_dir, expected_status)
3485 expected_output = svntest.wc.State(wc_dir, {
3486 'A/D/G/rho': Item(verb='Adding'),
3489 svntest.actions.run_and_verify_commit(wc_dir,
3490 expected_output,
3491 None,
3492 None, wc_dir)
3494 # Update working copy
3495 expected_output = svntest.wc.State(wc_dir, {})
3496 expected_disk = svntest.main.greek_state.copy()
3497 expected_disk.tweak('A/D/G/rho', contents='new rho\n' )
3498 expected_status.tweak(wc_rev='3')
3499 expected_status.tweak('A/D/G/rho', status=' ')
3501 svntest.actions.run_and_verify_update(wc_dir,
3502 expected_output,
3503 expected_disk,
3504 expected_status)
3506 # merge changes from r3:1
3507 expected_output = svntest.wc.State(wc_dir, {
3508 'A/D/G/rho': Item(status='R ')
3510 expected_status.tweak('A/D/G/rho', status='R ', copied='+', wc_rev='-')
3511 expected_skip = wc.State(wc_dir, { })
3512 expected_disk.tweak('A/D/G/rho', contents="This is the file 'rho'.\n")
3513 svntest.actions.run_and_verify_merge(wc_dir, '3', '1',
3514 sbox.repo_url,
3515 expected_output,
3516 expected_disk,
3517 expected_status,
3518 expected_skip)
3520 # Now commit merged wc
3521 expected_output = svntest.wc.State(wc_dir, {
3522 'A/D/G/rho': Item(verb='Replacing'),
3524 expected_status.tweak('A/D/G/rho', status=' ', copied=None, wc_rev='4')
3525 svntest.actions.run_and_verify_commit(wc_dir,
3526 expected_output,
3527 expected_status,
3528 None,
3529 wc_dir)
3530 # Test for issue 2522
3531 # Same as merge_file_replace, but without update before merge.
3532 def merge_file_replace_to_mixed_rev_wc(sbox):
3533 "merge a replacement of a file to mixed rev wc"
3535 sbox.build()
3536 wc_dir = sbox.wc_dir
3538 # File scheduled for deletion
3539 rho_path = os.path.join(wc_dir, 'A', 'D', 'G', 'rho')
3540 svntest.actions.run_and_verify_svn(None, None, [], 'rm', rho_path)
3542 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
3543 expected_status.tweak('A/D/G/rho', status='D ')
3544 svntest.actions.run_and_verify_status(wc_dir, expected_status)
3546 expected_output = svntest.wc.State(wc_dir, {
3547 'A/D/G/rho': Item(verb='Deleting'),
3550 expected_status.remove('A/D/G/rho')
3552 # Commit rev 2
3553 svntest.actions.run_and_verify_commit(wc_dir,
3554 expected_output,
3555 expected_status,
3556 None, wc_dir)
3558 # Update working copy
3559 expected_disk = svntest.main.greek_state.copy()
3560 expected_disk.remove('A/D/G/rho' )
3561 expected_output = svntest.wc.State(wc_dir, {})
3562 expected_status.tweak(wc_rev='2')
3564 svntest.actions.run_and_verify_update(wc_dir,
3565 expected_output,
3566 expected_disk,
3567 expected_status)
3569 # Create and add a new file.
3570 svntest.main.file_write(rho_path, "new rho\n")
3571 svntest.actions.run_and_verify_svn(None, None, [], 'add', rho_path)
3573 # Commit revsion 3
3574 expected_status.add({
3575 'A/D/G/rho' : Item(status='A ', wc_rev='0')
3577 svntest.actions.run_and_verify_status(wc_dir, expected_status)
3578 expected_output = svntest.wc.State(wc_dir, {
3579 'A/D/G/rho': Item(verb='Adding'),
3582 expected_disk.add({'A/D/G/rho' : Item(contents='new rho\n')} )
3583 expected_status.tweak(wc_rev='2')
3584 expected_status.tweak('A/D/G/rho', status=' ', wc_rev='3')
3586 svntest.actions.run_and_verify_commit(wc_dir,
3587 expected_output,
3588 expected_status,
3589 None, wc_dir)
3592 # merge changes from r3:1
3593 expected_output = svntest.wc.State(wc_dir, {
3594 'A/D/G/rho': Item(status='R ')
3596 expected_status.tweak('A/D/G/rho', status='R ', copied='+', wc_rev='-')
3597 expected_skip = wc.State(wc_dir, { })
3598 expected_disk.tweak('A/D/G/rho', contents="This is the file 'rho'.\n")
3599 svntest.actions.run_and_verify_merge(wc_dir, '3', '1',
3600 sbox.repo_url,
3601 expected_output,
3602 expected_disk,
3603 expected_status,
3604 expected_skip)
3606 # At this point WC is broken, because file rho has invalid revision
3607 # Try to update
3608 expected_output = svntest.wc.State(wc_dir, {})
3609 expected_status.tweak(wc_rev='3')
3610 expected_status.tweak('A/D/G/rho', status='R ', copied='+', wc_rev='-')
3611 svntest.actions.run_and_verify_update(wc_dir,
3612 expected_output,
3613 expected_disk,
3614 expected_status)
3616 # Now commit merged wc
3617 expected_output = svntest.wc.State(wc_dir, {
3618 'A/D/G/rho': Item(verb='Replacing'),
3620 expected_status.tweak('A/D/G/rho', status=' ', copied=None, wc_rev='4')
3621 svntest.actions.run_and_verify_commit(wc_dir,
3622 expected_output,
3623 expected_status,
3624 None,
3625 wc_dir)
3627 # use -x -w option for ignoring whitespace during merge
3628 def merge_ignore_whitespace(sbox):
3629 "ignore whitespace when merging"
3631 sbox.build()
3632 wc_dir = sbox.wc_dir
3634 # commit base version of iota
3635 file_name = "iota"
3636 file_path = os.path.join(wc_dir, file_name)
3637 file_url = sbox.repo_url + '/iota'
3639 svntest.main.file_write(file_path,
3640 "Aa\n"
3641 "Bb\n"
3642 "Cc\n")
3643 expected_output = svntest.wc.State(wc_dir, {
3644 'iota' : Item(verb='Sending'),
3646 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
3647 None, None, wc_dir)
3649 # change the file, mostly whitespace changes + an extra line
3650 svntest.main.file_write(file_path, "A a\nBb \n Cc\nNew line in iota\n")
3651 expected_output = wc.State(wc_dir, { file_name : Item(verb='Sending'), })
3652 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
3653 expected_status.tweak(file_name, wc_rev=3)
3654 svntest.actions.run_and_verify_commit(wc_dir,
3655 expected_output,
3656 expected_status,
3657 None,
3658 wc_dir)
3660 # Backdate iota to revision 2, so we can merge in the rev 3 changes.
3661 svntest.actions.run_and_verify_svn(None, None, [],
3662 'up', '-r', '2', file_path)
3663 # Make some local whitespace changes, these should not conflict
3664 # with the remote whitespace changes as both will be ignored.
3665 svntest.main.file_write(file_path, " Aa\nB b\nC c\n")
3667 # Lines changed only by whitespaces - both in local or remote -
3668 # should be ignored
3669 expected_output = wc.State(sbox.wc_dir, { file_name : Item(status='G ') })
3670 expected_disk = svntest.main.greek_state.copy()
3671 expected_disk.tweak(file_name,
3672 contents=" Aa\n"
3673 "B b\n"
3674 "C c\n"
3675 "New line in iota\n")
3676 expected_status = svntest.actions.get_virginal_state(sbox.wc_dir, 1)
3677 expected_status.tweak('', status=' M', wc_rev=1)
3678 expected_status.tweak(file_name, status='M ', wc_rev=2)
3679 expected_skip = wc.State('', { })
3681 svntest.actions.run_and_verify_merge(sbox.wc_dir, '2', '3',
3682 sbox.repo_url,
3683 expected_output,
3684 expected_disk,
3685 expected_status,
3686 expected_skip,
3687 None, None, None, None, None,
3688 0, 0,
3689 '-x', '-w')
3691 # use -x --ignore-eol-style option for ignoring eolstyle during merge
3692 def merge_ignore_eolstyle(sbox):
3693 "ignore eolstyle when merging"
3695 sbox.build()
3696 wc_dir = sbox.wc_dir
3698 # commit base version of iota
3699 file_name = "iota"
3700 file_path = os.path.join(wc_dir, file_name)
3701 file_url = sbox.repo_url + '/iota'
3703 svntest.main.file_write(file_path,
3704 "Aa\r\n"
3705 "Bb\r\n"
3706 "Cc\r\n",
3707 "wb")
3708 expected_output = svntest.wc.State(wc_dir, {
3709 'iota' : Item(verb='Sending'),
3711 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
3712 None, None, wc_dir)
3714 # change the file, mostly eol changes + an extra line
3715 svntest.main.file_write(file_path,
3716 "Aa\r"
3717 "Bb\n"
3718 "Cc\r"
3719 "New line in iota\n",
3720 "wb")
3721 expected_output = wc.State(wc_dir, { file_name : Item(verb='Sending'), })
3722 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
3723 expected_status.tweak(file_name, wc_rev=3)
3724 svntest.actions.run_and_verify_commit(wc_dir,
3725 expected_output,
3726 expected_status,
3727 None,
3728 wc_dir)
3730 # Backdate iota to revision 2, so we can merge in the rev 3 changes.
3731 svntest.actions.run_and_verify_svn(None, None, [],
3732 'up', '-r', '2', file_path)
3733 # Make some local eol changes, these should not conflict
3734 # with the remote eol changes as both will be ignored.
3735 svntest.main.file_write(file_path,
3736 "Aa\n"
3737 "Bb\r"
3738 "Cc\n",
3739 "wb")
3741 # Lines changed only by eolstyle - both in local or remote -
3742 # should be ignored
3743 expected_output = wc.State(sbox.wc_dir, { file_name : Item(status='G ') })
3744 expected_disk = svntest.main.greek_state.copy()
3745 expected_disk.tweak(file_name,
3746 contents="Aa\n"
3747 "Bb\r"
3748 "Cc\n"
3749 "New line in iota\n")
3750 expected_status = svntest.actions.get_virginal_state(sbox.wc_dir, 1)
3751 expected_status.tweak('', status=' M')
3752 expected_status.tweak(file_name, status='M ', wc_rev=2)
3753 expected_skip = wc.State('', { })
3755 svntest.actions.run_and_verify_merge(sbox.wc_dir, '2', '3',
3756 sbox.repo_url,
3757 expected_output,
3758 expected_disk,
3759 expected_status,
3760 expected_skip,
3761 None, None, None, None, None,
3762 0, 0,
3763 '-x', '--ignore-eol-style')
3765 #----------------------------------------------------------------------
3766 # Issue 2584
3767 def merge_add_over_versioned_file_conflicts(sbox):
3768 "conflict from merge of add over versioned file"
3770 sbox.build()
3771 wc_dir = sbox.wc_dir
3773 E_path = os.path.join(wc_dir, 'A', 'B', 'E')
3774 alpha_path = os.path.join(E_path, 'alpha')
3775 new_alpha_path = os.path.join(wc_dir, 'A', 'C', 'alpha')
3777 # Create a new "alpha" file, with enough differences to cause a conflict.
3778 ### TODO: Leverage inject_conflict_into_wc() here.
3779 svntest.main.file_write(new_alpha_path, 'new alpha content\n')
3781 # Add and commit the new "alpha" file, creating revision 2.
3782 svntest.main.run_svn(None, "add", new_alpha_path)
3784 expected_output = svntest.wc.State(wc_dir, {
3785 'A/C/alpha' : Item(verb='Adding'),
3787 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
3788 expected_status.add({
3789 'A/C/alpha' : Item(status=' ', wc_rev=2),
3791 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
3792 expected_status, None,
3793 wc_dir)
3795 # Search for the comment entitled "The Merge Kluge" elsewhere in
3796 # this file, to understand why we shorten and chdir() below.
3797 short_E_path = shorten_path_kludge(E_path)
3799 # Merge changes from r1:2 into our pre-existing "alpha" file,
3800 # causing a conflict.
3801 expected_output = wc.State(short_E_path, {
3802 'alpha' : Item(status='C '),
3804 expected_disk = wc.State('', {
3805 'alpha' : Item(""), # state filled in below
3806 'beta' : Item("This is the file 'beta'.\n"),
3808 expected_status = wc.State(short_E_path, {
3809 '' : Item(status=' M', wc_rev=1),
3810 'alpha' : Item(status='C ', wc_rev=1),
3811 'beta' : Item(status=' ', wc_rev=1),
3814 inject_conflict_into_expected_state('alpha', expected_disk, expected_status,
3815 "This is the file 'alpha'.\n",
3816 "new alpha content\n", 2)
3817 expected_skip = wc.State(short_E_path, { })
3819 os.chdir(svntest.main.work_dir)
3820 svntest.actions.run_and_verify_merge(short_E_path, '1', '2',
3821 sbox.repo_url + \
3822 '/A/C',
3823 expected_output,
3824 expected_disk,
3825 expected_status,
3826 expected_skip,
3827 None,
3828 svntest.tree.detect_conflict_files,
3829 ["alpha\.working",
3830 "alpha\.merge-right\.r2",
3831 "alpha\.merge-left\.r0"])
3833 #----------------------------------------------------------------------
3834 # eol-style handling during merge with conflicts, scenario 1:
3835 # when a merge creates a conflict on a file, make sure the file and files
3836 # r<left>, r<right> and .mine are in the eol-style defined for that file.
3838 # This test for 'svn update' can be found in update_tests.py as
3839 # conflict_markers_matching_eol.
3840 def merge_conflict_markers_matching_eol(sbox):
3841 "conflict markers should match the file's eol style"
3843 sbox.build()
3844 wc_dir = sbox.wc_dir
3845 filecount = 1
3847 mu_path = os.path.join(wc_dir, 'A', 'mu')
3849 if os.name == 'nt':
3850 crlf = '\n'
3851 else:
3852 crlf = '\r\n'
3854 # Checkout a second working copy
3855 wc_backup = sbox.add_wc_path('backup')
3856 svntest.actions.run_and_verify_svn(None, None, [], 'checkout',
3857 sbox.repo_url, wc_backup)
3859 # set starting revision
3860 cur_rev = 1
3862 expected_disk = svntest.main.greek_state.copy()
3863 expected_status = svntest.actions.get_virginal_state(wc_dir, cur_rev)
3864 expected_backup_status = svntest.actions.get_virginal_state(wc_backup,
3865 cur_rev)
3867 path_backup = os.path.join(wc_backup, 'A', 'mu')
3869 # do the test for each eol-style
3870 for eol, eolchar in zip(['CRLF', 'CR', 'native', 'LF'],
3871 [crlf, '\015', '\n', '\012']):
3872 # rewrite file mu and set the eol-style property.
3873 svntest.main.file_write(mu_path, "This is the file 'mu'."+ eolchar, 'wb')
3874 svntest.main.run_svn(None, 'propset', 'svn:eol-style', eol, mu_path)
3876 expected_disk.add({
3877 'A/mu' : Item("This is the file 'mu'." + eolchar)
3879 expected_output = svntest.wc.State(wc_dir, {
3880 'A/mu' : Item(verb='Sending'),
3882 expected_status.tweak(wc_rev = cur_rev)
3883 expected_status.add({
3884 'A/mu' : Item(status=' ', wc_rev = cur_rev + 1),
3887 # Commit the original change and note the 'base' revision number
3888 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
3889 expected_status, None,
3890 wc_dir)
3891 cur_rev = cur_rev + 1
3892 base_rev = cur_rev
3894 svntest.main.run_svn(None, 'update', wc_backup)
3896 # Make a local mod to mu
3897 svntest.main.file_append(mu_path,
3898 'Original appended text for mu' + eolchar)
3900 # Commit the original change and note the 'theirs' revision number
3901 svntest.main.run_svn(None, 'commit', '-m', 'test log', wc_dir)
3902 cur_rev = cur_rev + 1
3903 theirs_rev = cur_rev
3905 # Make a local mod to mu, will conflict with the previous change
3906 svntest.main.file_append(path_backup,
3907 'Conflicting appended text for mu' + eolchar)
3909 # Create expected output tree for an update of the wc_backup.
3910 expected_backup_output = svntest.wc.State(wc_backup, {
3911 'A/mu' : Item(status='C '),
3914 # Create expected disk tree for the update.
3915 expected_backup_disk = expected_disk.copy()
3917 # verify content of resulting conflicted file
3918 expected_backup_disk.add({
3919 'A/mu' : Item(contents= "This is the file 'mu'." + eolchar +
3920 "<<<<<<< .working" + eolchar +
3921 "Conflicting appended text for mu" + eolchar +
3922 "=======" + eolchar +
3923 "Original appended text for mu" + eolchar +
3924 ">>>>>>> .merge-right.r" + str(cur_rev) + eolchar),
3926 # verify content of base(left) file
3927 expected_backup_disk.add({
3928 'A/mu.merge-left.r' + str(base_rev) :
3929 Item(contents= "This is the file 'mu'." + eolchar)
3931 # verify content of theirs(right) file
3932 expected_backup_disk.add({
3933 'A/mu.merge-right.r' + str(theirs_rev) :
3934 Item(contents= "This is the file 'mu'." + eolchar +
3935 "Original appended text for mu" + eolchar)
3937 # verify content of mine file
3938 expected_backup_disk.add({
3939 'A/mu.working' : Item(contents= "This is the file 'mu'." +
3940 eolchar +
3941 "Conflicting appended text for mu" + eolchar)
3944 # Create expected status tree for the update.
3945 expected_backup_status.add({
3946 'A/mu' : Item(status=' ', wc_rev=cur_rev),
3948 expected_backup_status.tweak('A/mu', status='C ')
3949 expected_backup_status.tweak(wc_rev = cur_rev - 1)
3950 expected_backup_status.tweak('', status= ' M')
3952 expected_backup_skip = wc.State('', { })
3954 svntest.actions.run_and_verify_merge(wc_backup, cur_rev - 1, cur_rev,
3955 sbox.repo_url,
3956 expected_backup_output,
3957 expected_backup_disk,
3958 expected_backup_status,
3959 expected_backup_skip)
3961 # cleanup for next run
3962 svntest.main.run_svn(None, 'revert', '-R', wc_backup)
3963 svntest.main.run_svn(None, 'update', wc_dir)
3965 # eol-style handling during merge, scenario 2:
3966 # if part of that merge is a propchange (add, change, delete) of
3967 # svn:eol-style, make sure the correct eol-style is applied before
3968 # calculating the merge (and conflicts if any)
3970 # This test for 'svn update' can be found in update_tests.py as
3971 # update_eolstyle_handling.
3972 def merge_eolstyle_handling(sbox):
3973 "handle eol-style propchange during merge"
3975 sbox.build()
3976 wc_dir = sbox.wc_dir
3978 mu_path = os.path.join(wc_dir, 'A', 'mu')
3980 if os.name == 'nt':
3981 crlf = '\n'
3982 else:
3983 crlf = '\r\n'
3985 # Checkout a second working copy
3986 wc_backup = sbox.add_wc_path('backup')
3987 svntest.actions.run_and_verify_svn(None, None, [], 'checkout',
3988 sbox.repo_url, wc_backup)
3989 path_backup = os.path.join(wc_backup, 'A', 'mu')
3991 # Test 1: add the eol-style property and commit, change mu in the second
3992 # working copy and merge the last revision; there should be no conflict!
3993 svntest.main.run_svn(None, 'propset', 'svn:eol-style', "CRLF", mu_path)
3994 svntest.main.run_svn(None,
3995 'commit', '-m', 'set eol-style property', wc_dir)
3997 svntest.main.file_append_binary(path_backup, 'Added new line of text.\012')
3999 expected_backup_disk = svntest.main.greek_state.copy()
4000 expected_backup_disk.tweak(
4001 'A/mu', contents= "This is the file 'mu'." + crlf +
4002 "Added new line of text." + crlf)
4003 expected_backup_output = svntest.wc.State(wc_backup, {
4004 'A/mu' : Item(status='GU'),
4006 expected_backup_status = svntest.actions.get_virginal_state(wc_backup, 1)
4007 expected_backup_status.tweak('', status=' M')
4008 expected_backup_status.tweak('A/mu', status='MM')
4010 expected_backup_skip = wc.State('', { })
4012 svntest.actions.run_and_verify_merge(wc_backup, '1', '2', sbox.repo_url,
4013 expected_backup_output,
4014 expected_backup_disk,
4015 expected_backup_status,
4016 expected_backup_skip)
4018 # Test 2: now change the eol-style property to another value and commit,
4019 # merge this revision in the still changed mu in the second working copy;
4020 # there should be a property conflict! (Since this will overwrite a
4021 # local change to a versioned resource.)
4022 svntest.main.run_svn(None, 'propset', 'svn:eol-style', "CR", mu_path)
4023 svntest.main.run_svn(None,
4024 'commit', '-m', 'set eol-style property', wc_dir)
4026 expected_backup_disk = svntest.main.greek_state.copy()
4027 expected_backup_disk.add({
4028 'A/mu' : Item(contents= "This is the file 'mu'." + crlf +
4029 "Added new line of text." + crlf)
4031 expected_backup_disk.add({
4032 'A/mu.prej' : Item("Trying to change property 'svn:eol-style' from 'CRLF'"
4033 + " to 'CR',\nbut property has been locally added with"
4034 + " value 'CRLF'\n")})
4035 expected_backup_output = svntest.wc.State(wc_backup, {
4036 'A/mu' : Item(status='GC'),
4038 expected_backup_status = svntest.actions.get_virginal_state(wc_backup, 1)
4039 expected_backup_status.tweak('', status=' M')
4040 expected_backup_status.tweak('A/mu', status='MC')
4041 svntest.actions.run_and_verify_merge(wc_backup, '2', '3', sbox.repo_url,
4042 expected_backup_output,
4043 expected_backup_disk,
4044 expected_backup_status,
4045 expected_backup_skip)
4047 # Test 3: now delete the eol-style property and commit, merge this revision
4048 # in the still changed mu in the second working copy; there should be no
4049 # conflict! (after marking mu resolved from Test 2)
4050 # EOL of mu should be unchanged (=CRLF).
4051 svntest.main.run_svn(None, 'propdel', 'svn:eol-style', mu_path)
4052 svntest.main.run_svn(None,
4053 'commit', '-m', 'del eol-style property', wc_dir)
4055 expected_backup_disk = svntest.main.greek_state.copy()
4056 expected_backup_disk.add({
4057 'A/mu' : Item(contents= "This is the file 'mu'." + crlf +
4058 "Added new line of text." + crlf)
4060 expected_backup_output = svntest.wc.State(wc_backup, {
4061 'A/mu' : Item(status=' G'),
4063 expected_backup_status = svntest.actions.get_virginal_state(wc_backup, 1)
4064 expected_backup_status.tweak('', status=' M')
4065 expected_backup_status.tweak('A/mu', status='M ')
4066 svntest.main.run_svn(None, 'resolved', path_backup)
4067 svntest.actions.run_and_verify_merge(wc_backup, '3', '4', sbox.repo_url,
4068 expected_backup_output,
4069 expected_backup_disk,
4070 expected_backup_status,
4071 expected_backup_skip)
4073 def create_deep_trees(wc_dir):
4074 """Create A/B/F/E by moving A/B/E to A/B/F/E.
4075 Copy A/B/F/E to A/B/F/E1.
4076 Copy A/B to A/copy-of-B, and return the expected status.
4077 At the end of this function WC would be at r4"""
4079 A_path = os.path.join(wc_dir, 'A')
4080 A_B_path = os.path.join(A_path, 'B')
4081 A_B_E_path = os.path.join(A_B_path, 'E')
4082 A_B_F_path = os.path.join(A_B_path, 'F')
4083 A_B_F_E_path = os.path.join(A_B_F_path, 'E')
4084 A_B_F_E1_path = os.path.join(A_B_F_path, 'E1')
4086 # Deepen the directory structure we're working with by moving E to
4087 # underneath F and committing, creating revision 2.
4088 svntest.main.run_svn(None, 'mv', A_B_E_path, A_B_F_path)
4090 # A/B/F/E now has empty mergeinfo
4092 expected_output = wc.State(wc_dir, {
4093 'A/B/E' : Item(verb='Deleting'),
4094 'A/B/F/E' : Item(verb='Adding')
4096 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
4097 expected_status.remove('A/B/E', 'A/B/E/alpha', 'A/B/E/beta')
4098 expected_status.add({
4099 'A/B/F/E' : Item(status=' ', wc_rev=2),
4100 'A/B/F/E/alpha' : Item(status=' ', wc_rev=2),
4101 'A/B/F/E/beta' : Item(status=' ', wc_rev=2),
4103 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
4104 expected_status, None,
4105 wc_dir)
4107 svntest.main.run_svn(None, 'cp', A_B_F_E_path, A_B_F_E1_path)
4109 # A/B/F/E1 now has empty mergeinfo
4111 expected_output = wc.State(wc_dir, {
4112 'A/B/F/E1' : Item(verb='Adding')
4114 expected_status.add({
4115 'A/B/F/E1' : Item(status=' ', wc_rev=3),
4116 'A/B/F/E1/alpha' : Item(status=' ', wc_rev=3),
4117 'A/B/F/E1/beta' : Item(status=' ', wc_rev=3),
4119 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
4120 expected_status, None,
4121 wc_dir)
4123 # Bring the entire WC up to date with rev 3.
4124 svntest.actions.run_and_verify_svn(None, None, [], 'update', wc_dir)
4125 expected_status.tweak(wc_rev=3)
4127 # Copy B and commit, creating revision 4.
4128 copy_of_B_path = os.path.join(A_path, 'copy-of-B')
4129 svntest.main.run_svn(None, "cp", A_B_path, copy_of_B_path)
4131 # A/copy-of-B, A/copy-of-B/F/E, and A/copy-of-B/F/E1 now have empty mergeinfo
4133 expected_output = svntest.wc.State(wc_dir, {
4134 'A/copy-of-B' : Item(verb='Adding'),
4136 expected_status.add({
4137 'A/copy-of-B' : Item(status=' ', wc_rev=4),
4138 'A/copy-of-B/F' : Item(status=' ', wc_rev=4),
4139 'A/copy-of-B/F/E' : Item(status=' ', wc_rev=4),
4140 'A/copy-of-B/F/E/alpha' : Item(status=' ', wc_rev=4),
4141 'A/copy-of-B/F/E/beta' : Item(status=' ', wc_rev=4),
4142 'A/copy-of-B/F/E1' : Item(status=' ', wc_rev=4),
4143 'A/copy-of-B/F/E1/alpha' : Item(status=' ', wc_rev=4),
4144 'A/copy-of-B/F/E1/beta' : Item(status=' ', wc_rev=4),
4145 'A/copy-of-B/lambda' : Item(status=' ', wc_rev=4),
4147 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
4148 expected_status, None,
4149 wc_dir)
4151 # pre-update, empty mergeinfo can be found on:
4153 # /A/B/F/E
4154 # /A/B/F/E1
4155 # /A/copy-of-B
4156 # /A/copy-of-B/F/E
4157 # /A/copy-of-B/F/E1
4159 expected_disk = svntest.main.greek_state.copy()
4160 expected_disk.remove('A/B/E', 'A/B/E/alpha', 'A/B/E/beta')
4161 expected_disk.add({
4162 'A/B/F/E' : Item(props={SVN_PROP_MERGEINFO : ''}),
4163 'A/B/F/E/alpha' : Item(contents="This is the file 'alpha'.\n"),
4164 'A/B/F/E/beta' : Item(contents="This is the file 'beta'.\n"),
4165 'A/B/F/E1' : Item(props={SVN_PROP_MERGEINFO : ''}),
4166 'A/B/F/E1/alpha' : Item(contents="This is the file 'alpha'.\n"),
4167 'A/B/F/E1/beta' : Item(contents="This is the file 'beta'.\n"),
4168 'A/copy-of-B' : Item(props={SVN_PROP_MERGEINFO : ''}),
4169 'A/copy-of-B/F' : Item(props={}),
4170 'A/copy-of-B/F/E' : Item(props={SVN_PROP_MERGEINFO : ''}),
4171 'A/copy-of-B/F/E/alpha' : Item(contents="This is the file 'alpha'.\n"),
4172 'A/copy-of-B/F/E/beta' : Item(contents="This is the file 'beta'.\n"),
4173 'A/copy-of-B/F/E1' : Item(props={SVN_PROP_MERGEINFO : ''}),
4174 'A/copy-of-B/F/E1/alpha' : Item(contents="This is the file 'alpha'.\n"),
4175 'A/copy-of-B/F/E1/beta' : Item(contents="This is the file 'beta'.\n"),
4176 'A/copy-of-B/lambda' : Item(contents="This is the file 'lambda'.\n"),
4178 svntest.actions.verify_disk(wc_dir, expected_disk,
4179 None, None, None, None, 1)
4181 # Bring the entire WC up to date with rev 4.
4182 svntest.actions.run_and_verify_svn(None, None, [], 'update', wc_dir)
4184 svntest.actions.verify_disk(wc_dir, expected_disk,
4185 None, None, None, None, 1)
4187 expected_status.tweak(wc_rev=4)
4188 expected_disk.tweak('A/copy-of-B/F/E', 'A/copy-of-B/F/E1', status=' M')
4189 return expected_status
4191 def avoid_repeated_merge_using_inherited_merge_info(sbox):
4192 "use inherited mergeinfo to avoid repeated merge"
4194 sbox.build()
4195 wc_dir = sbox.wc_dir
4197 A_path = os.path.join(wc_dir, 'A')
4198 A_B_path = os.path.join(A_path, 'B')
4199 A_B_E_path = os.path.join(A_B_path, 'E')
4200 A_B_F_path = os.path.join(A_B_path, 'F')
4201 copy_of_B_path = os.path.join(A_path, 'copy-of-B')
4203 # Create a deeper directory structure.
4204 expected_status = create_deep_trees(wc_dir)
4206 # Edit alpha and commit it, creating revision 5.
4207 alpha_path = os.path.join(A_B_F_path, 'E', 'alpha')
4208 new_content_for_alpha = 'new content to alpha\n'
4209 svntest.main.file_write(alpha_path, new_content_for_alpha)
4210 expected_output = svntest.wc.State(wc_dir, {
4211 'A/B/F/E/alpha' : Item(verb='Sending'),
4213 expected_status.tweak('A/B/F/E/alpha', wc_rev=5)
4214 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
4215 expected_status, None,
4216 wc_dir)
4218 # Search for the comment entitled "The Merge Kluge" elsewhere in
4219 # this file, to understand why we shorten and chdir() below.
4220 short_copy_of_B_path = shorten_path_kludge(copy_of_B_path)
4222 # Bring the entire WC up to date with rev 5.
4223 svntest.actions.run_and_verify_svn(None, None, [], 'update', wc_dir)
4225 # Merge changes from rev 5 of B (to alpha) into copy_of_B.
4226 # A_COPY/copy_of_B/F/E and A_COPY/copy_of_B/F/E1 both exist in the merge
4227 # source at r5, so their empty mergeinfo should be updted with r5, which
4228 # then should elide to A_COPY/copy_of_B leaving no mergeinfo on either.
4229 expected_output = wc.State(short_copy_of_B_path, {
4230 'F/E/alpha' : Item(status='U '),
4232 expected_status = wc.State(short_copy_of_B_path, {
4233 '' : Item(status=' M', wc_rev=5),
4234 'F/E' : Item(status=' M', wc_rev=5),
4235 'F/E/alpha' : Item(status='M ', wc_rev=5),
4236 'F/E/beta' : Item(status=' ', wc_rev=5),
4237 'F/E1' : Item(status=' M', wc_rev=5),
4238 'F/E1/alpha' : Item(status=' ', wc_rev=5),
4239 'F/E1/beta' : Item(status=' ', wc_rev=5),
4240 'lambda' : Item(status=' ', wc_rev=5),
4241 'F' : Item(status=' ', wc_rev=5),
4243 expected_disk = wc.State('', {
4244 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B:5'}),
4245 'F/E' : Item(),
4246 'F/E/alpha' : Item(new_content_for_alpha),
4247 'F/E/beta' : Item("This is the file 'beta'.\n"),
4248 'F/E1' : Item(),
4249 'F/E1/alpha' : Item("This is the file 'alpha'.\n"),
4250 'F/E1/beta' : Item("This is the file 'beta'.\n"),
4251 'F' : Item(),
4252 'lambda' : Item("This is the file 'lambda'.\n")
4254 expected_skip = wc.State(short_copy_of_B_path, { })
4255 saved_cwd = os.getcwd()
4257 os.chdir(svntest.main.work_dir)
4258 svntest.actions.run_and_verify_merge(short_copy_of_B_path, '4', '5',
4259 sbox.repo_url + \
4260 '/A/B',
4261 expected_output,
4262 expected_disk,
4263 expected_status,
4264 expected_skip,
4265 None,
4266 None,
4267 None,
4268 None,
4269 None, 1)
4270 os.chdir(saved_cwd)
4272 # Commit the result of the merge, creating revision 6.
4273 expected_output = svntest.wc.State(copy_of_B_path, {
4274 '' : Item(verb='Sending'),
4275 'F/E' : Item(verb='Sending'),
4276 'F/E/alpha' : Item(verb='Sending'),
4277 'F/E1' : Item(verb='Sending'),
4279 svntest.actions.run_and_verify_commit(short_copy_of_B_path, expected_output,
4280 None, None, wc_dir)
4282 # Update the WC to bring /A/copy_of_B/F from rev 4 to rev 6.
4283 # Without this update, a subsequent merge will not find any merge
4284 # info for /A/copy_of_B/F -- nor its parent dir in the repos -- at
4285 # rev 4. Mergeinfo wasn't introduced until rev 6.
4286 copy_of_B_F_E_path = os.path.join(copy_of_B_path, 'F', 'E')
4287 svntest.actions.run_and_verify_svn(None, None, [], 'update', wc_dir)
4289 # Search for the comment entitled "The Merge Kluge" elsewhere in
4290 # this file, to understand why we shorten and chdir() below.
4291 short_copy_of_B_F_E_path = shorten_path_kludge(copy_of_B_F_E_path)
4293 # Attempt to re-merge changes to alpha from rev 4. Use the merge
4294 # info inherited from the grandparent (copy-of-B) of our merge
4295 # target (/A/copy-of-B/F/E) to avoid a repeated merge.
4296 expected_status = wc.State(short_copy_of_B_F_E_path, {
4297 '' : Item(status=' ', wc_rev=6),
4298 'alpha' : Item(status=' ', wc_rev=6),
4299 'beta' : Item(status=' ', wc_rev=6),
4301 os.chdir(svntest.main.work_dir)
4302 svntest.actions.run_and_verify_svn(None, [], [], 'merge', '-r4:5',
4303 sbox.repo_url + '/A/B/F/E',
4304 short_copy_of_B_F_E_path)
4305 svntest.actions.run_and_verify_status(short_copy_of_B_F_E_path,
4306 expected_status)
4307 os.chdir(saved_cwd)
4309 def avoid_repeated_merge_on_subtree_with_merge_info(sbox):
4310 "use subtree's mergeinfo to avoid repeated merge"
4311 # Create deep trees A/B/F/E and A/B/F/E1 and copy A/B to A/copy-of-B
4312 # with the help of 'create_deep_trees'
4313 # As /A/copy-of-B/F/E1 is not a child of /A/copy-of-B/F/E,
4314 # set_path should not be called on /A/copy-of-B/F/E1 while
4315 # doing a implicit subtree merge on /A/copy-of-B/F/E.
4316 sbox.build()
4317 wc_dir = sbox.wc_dir
4319 A_path = os.path.join(wc_dir, 'A')
4320 A_B_path = os.path.join(A_path, 'B')
4321 A_B_E_path = os.path.join(A_B_path, 'E')
4322 A_B_F_path = os.path.join(A_B_path, 'F')
4323 A_B_F_E_path = os.path.join(A_B_F_path, 'E')
4324 copy_of_B_path = os.path.join(A_path, 'copy-of-B')
4325 copy_of_B_F_path = os.path.join(A_path, 'copy-of-B', 'F')
4326 A_copy_of_B_F_E_alpha_path = os.path.join(A_path, 'copy-of-B', 'F',
4327 'E', 'alpha')
4329 # Create a deeper directory structure.
4330 expected_status = create_deep_trees(wc_dir)
4332 # Edit alpha and commit it, creating revision 5.
4333 alpha_path = os.path.join(A_B_F_E_path, 'alpha')
4334 new_content_for_alpha1 = 'new content to alpha\n'
4335 svntest.main.file_write(alpha_path, new_content_for_alpha1)
4337 expected_output = svntest.wc.State(wc_dir, {
4338 'A/B/F/E/alpha' : Item(verb='Sending'),
4340 expected_status.tweak('A/B/F/E/alpha', wc_rev=5)
4341 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
4342 expected_status, None, wc_dir)
4344 for path_and_mergeinfo in (('E', '/A/B/F/E:5'),
4345 ('E1', '/A/B/F/E:5')):
4346 path_name = os.path.join(copy_of_B_path, 'F', path_and_mergeinfo[0])
4347 # Search for the comment entitled "The Merge Kluge" elsewhere in
4348 # this file, to understand why we shorten and chdir() below.
4349 short_path_name = shorten_path_kludge(path_name)
4351 # Merge r5 to path_name.
4352 expected_output = wc.State(short_path_name, {
4353 'alpha' : Item(status='U '),
4355 expected_status = wc.State(short_path_name, {
4356 '' : Item(status=' M', wc_rev=4),
4357 'alpha' : Item(status='M ', wc_rev=4),
4358 'beta' : Item(status=' ', wc_rev=4),
4360 expected_disk = wc.State('', {
4361 '' : Item(props={SVN_PROP_MERGEINFO : path_and_mergeinfo[1]}),
4362 'alpha' : Item(new_content_for_alpha1),
4363 'beta' : Item("This is the file 'beta'.\n"),
4365 expected_skip = wc.State(short_path_name, { })
4366 saved_cwd = os.getcwd()
4368 os.chdir(svntest.main.work_dir)
4369 svntest.actions.run_and_verify_merge(short_path_name, '4', '5',
4370 sbox.repo_url + '/A/B/F/E',
4371 expected_output,
4372 expected_disk,
4373 expected_status,
4374 expected_skip,
4375 None,
4376 None,
4377 None,
4378 None,
4379 None, 1)
4380 os.chdir(saved_cwd)
4382 # Commit the result of the merge, creating new revision.
4383 expected_output = svntest.wc.State(path_name, {
4384 '' : Item(verb='Sending'),
4385 'alpha' : Item(verb='Sending'),
4387 svntest.actions.run_and_verify_commit(short_path_name,
4388 expected_output, None, None, wc_dir)
4390 # Edit A/B/F/E/alpha and commit it, creating revision 8.
4391 new_content_for_alpha = 'new content to alpha\none more line\n'
4392 svntest.main.file_write(alpha_path, new_content_for_alpha)
4394 expected_output = svntest.wc.State(A_B_F_E_path, {
4395 'alpha' : Item(verb='Sending'),
4397 expected_status = wc.State(A_B_F_E_path, {
4398 '' : Item(status=' ', wc_rev=4),
4399 'alpha' : Item(status=' ', wc_rev=8),
4400 'beta' : Item(status=' ', wc_rev=4),
4402 svntest.actions.run_and_verify_commit(A_B_F_E_path, expected_output,
4403 expected_status, None, wc_dir)
4405 # Search for the comment entitled "The Merge Kluge" elsewhere in
4406 # this file, to understand why we shorten and chdir() below.
4407 short_copy_of_B_path = shorten_path_kludge(copy_of_B_path)
4409 # Update the WC to bring /A/copy_of_B to rev 8.
4410 # Without this update expected_status tree would be cumbersome to
4411 # understand.
4412 svntest.actions.run_and_verify_svn(None, None, [], 'update', wc_dir)
4414 # Merge changes from rev 4:8 of A/B into A/copy_of_B. A/copy_of_B/F/E1
4415 # has explicit mergeinfo and exists at r4 in the merge source, so it
4416 # should be treated as a subtree with intersecting mergeinfo and its
4417 # mergeinfo updated.
4418 expected_output = wc.State(short_copy_of_B_path, {
4419 'F/E/alpha' : Item(status='U ')
4421 expected_status = wc.State(short_copy_of_B_path, {
4422 # When we merge multiple sub-targets, we record mergeinfo on each
4423 # child.
4424 '' : Item(status=' M', wc_rev=8),
4425 'F/E' : Item(status=' M', wc_rev=8),
4426 'F/E/alpha' : Item(status='M ', wc_rev=8),
4427 'F/E/beta' : Item(status=' ', wc_rev=8),
4428 'F/E1' : Item(status=' M', wc_rev=8),
4429 'F/E1/alpha' : Item(status=' ', wc_rev=8),
4430 'F/E1/beta' : Item(status=' ', wc_rev=8),
4431 'lambda' : Item(status=' ', wc_rev=8),
4432 'F' : Item(status=' ', wc_rev=8)
4434 expected_disk = wc.State('', {
4435 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B:5-8'}),
4436 'F/E' : Item(props={}), # elision!
4437 'F/E/alpha' : Item(new_content_for_alpha),
4438 'F/E/beta' : Item("This is the file 'beta'.\n"),
4439 'F' : Item(),
4440 'F/E1' : Item(props={SVN_PROP_MERGEINFO :
4441 '/A/B/F/E:5\n/A/B/F/E1:5-8\n'}),
4442 'F/E1/alpha' : Item(new_content_for_alpha1),
4443 'F/E1/beta' : Item("This is the file 'beta'.\n"),
4444 'lambda' : Item("This is the file 'lambda'.\n")
4446 expected_skip = wc.State(short_copy_of_B_path, { })
4447 os.chdir(svntest.main.work_dir)
4448 svntest.actions.run_and_verify_merge(short_copy_of_B_path, '4', '8',
4449 sbox.repo_url + '/A/B',
4450 expected_output,
4451 expected_disk,
4452 expected_status,
4453 expected_skip,
4454 None,
4455 None,
4456 None,
4457 None,
4458 None, 1)
4459 os.chdir(saved_cwd)
4461 # Test for part of Issue #2821, see
4462 # http://subversion.tigris.org/issues/show_bug.cgi?id=2821#desc22
4464 # Revert all local changes.
4465 svntest.actions.run_and_verify_svn(None, None, [], 'revert', '-R', wc_dir)
4467 # Make a text mod to A/copy-of-B/F/E/alpha
4468 newer_content_for_alpha = "Conflicting content"
4469 svntest.main.file_write(A_copy_of_B_F_E_alpha_path,
4470 newer_content_for_alpha)
4472 # Re-merge r5 to A/copy-of-B/F, this *should* be a no-op as the mergeinfo
4473 # on A/copy-of-B/F/E should prevent any attempt to merge r5 into that
4474 # subtree. The merge will leave a few local changes as mergeinfo is set
4475 # on A/copy-of-B/F, the mergeinfo on A/copy-of-B/F/E elides to it, and
4476 # the mergeinfo on A/copy-of-B/F/E1 picks up r5 from /A/B/F/E1.
4477 short_copy_of_B_F_path = shorten_path_kludge(copy_of_B_F_path)
4478 expected_output = wc.State(short_copy_of_B_F_path, {})
4479 expected_status = wc.State(short_copy_of_B_F_path, {
4480 '' : Item(status=' M', wc_rev=8),
4481 'E' : Item(status=' M', wc_rev=8),
4482 'E/alpha' : Item(status='M ', wc_rev=8),
4483 'E/beta' : Item(status=' ', wc_rev=8),
4484 'E1' : Item(status=' M', wc_rev=8),
4485 'E1/alpha' : Item(status=' ', wc_rev=8),
4486 'E1/beta' : Item(status=' ', wc_rev=8),
4488 expected_disk = wc.State('', {
4489 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B/F:5'}),
4490 'E' : Item(props={}),
4491 'E/alpha' : Item(newer_content_for_alpha),
4492 'E/beta' : Item("This is the file 'beta'.\n"),
4493 'E1' : Item(props={SVN_PROP_MERGEINFO :
4494 '/A/B/F/E:5\n/A/B/F/E1:5\n'}),
4495 'E1/alpha' : Item(new_content_for_alpha1),
4496 'E1/beta' : Item("This is the file 'beta'.\n")
4498 expected_skip = wc.State(short_copy_of_B_F_path, { })
4499 saved_cwd = os.getcwd()
4501 os.chdir(svntest.main.work_dir)
4502 svntest.actions.run_and_verify_merge(short_copy_of_B_F_path, '4', '5',
4503 sbox.repo_url + '/A/B/F',
4504 expected_output,
4505 expected_disk,
4506 expected_status,
4507 expected_skip,
4508 None,
4509 None,
4510 None,
4511 None,
4512 None, 1)
4513 os.chdir(saved_cwd)
4515 def tweak_src_then_merge_to_dest(sbox, src_path, dst_path,
4516 canon_src_path, contents, cur_rev):
4517 """Edit src and commit it. This results in new_rev.
4518 Merge new_rev to dst_path. Return new_rev."""
4520 wc_dir = sbox.wc_dir
4521 new_rev = cur_rev + 1
4522 svntest.main.file_write(src_path, contents)
4524 expected_output = svntest.wc.State(src_path, {
4525 '': Item(verb='Sending'),
4528 expected_status = wc.State(src_path,
4529 { '': Item(wc_rev=new_rev, status=' ')})
4531 svntest.actions.run_and_verify_commit(src_path, expected_output,
4532 expected_status, None, src_path)
4534 # Update the WC to new_rev so that it would be easier to expect everyone
4535 # to be at new_rev.
4536 svntest.actions.run_and_verify_svn(None, None, [], 'update', wc_dir)
4538 # Merge new_rev of src_path to dst_path.
4540 # Search for the comment entitled "The Merge Kluge" elsewhere in
4541 # this file, to understand why we shorten and chdir() below.
4542 short_dst_path = shorten_path_kludge(dst_path)
4543 expected_status = wc.State(dst_path,
4544 { '': Item(wc_rev=new_rev, status='MM')})
4545 saved_cwd = os.getcwd()
4547 os.chdir(svntest.main.work_dir)
4549 merge_url = sbox.repo_url + '/' + canon_src_path
4550 if sys.platform == 'win32':
4551 merge_url = merge_url.replace('\\', '/')
4553 svntest.actions.run_and_verify_svn(None,
4554 expected_merge_output([[new_rev]],
4555 'U ' +
4556 short_dst_path +
4557 '\n'),
4559 'merge', '-c', str(new_rev),
4560 merge_url,
4561 short_dst_path)
4562 os.chdir(saved_cwd)
4564 svntest.actions.run_and_verify_status(dst_path, expected_status)
4566 return new_rev
4568 def obey_reporter_api_semantics_while_doing_subtree_merges(sbox):
4569 "drive reporter api in depth first order"
4571 # Copy /A/D to /A/copy-of-D it results in rONE.
4572 # Create children at different hierarchies having some merge-info
4573 # to test the set_path calls on a reporter in a depth-first order.
4574 # On all 'file' descendants of /A/copy-of-D/ we run merges.
4575 # We create /A/D/umlaut directly over URL it results in rev rTWO.
4576 # When we merge rONE+1:TWO of /A/D on /A/copy-of-D it should merge smoothly.
4578 sbox.build()
4579 wc_dir = sbox.wc_dir
4581 A_path = os.path.join(wc_dir, 'A')
4582 A_D_path = os.path.join(wc_dir, 'A', 'D')
4583 copy_of_A_D_path = os.path.join(wc_dir, 'A', 'copy-of-D')
4585 svntest.main.run_svn(None, "cp", A_D_path, copy_of_A_D_path)
4587 expected_output = svntest.wc.State(wc_dir, {
4588 'A/copy-of-D' : Item(verb='Adding'),
4590 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
4591 expected_status.add({
4592 'A/copy-of-D' : Item(status=' ', wc_rev=2),
4593 'A/copy-of-D/G' : Item(status=' ', wc_rev=2),
4594 'A/copy-of-D/G/pi' : Item(status=' ', wc_rev=2),
4595 'A/copy-of-D/G/rho' : Item(status=' ', wc_rev=2),
4596 'A/copy-of-D/G/tau' : Item(status=' ', wc_rev=2),
4597 'A/copy-of-D/H' : Item(status=' ', wc_rev=2),
4598 'A/copy-of-D/H/chi' : Item(status=' ', wc_rev=2),
4599 'A/copy-of-D/H/omega' : Item(status=' ', wc_rev=2),
4600 'A/copy-of-D/H/psi' : Item(status=' ', wc_rev=2),
4601 'A/copy-of-D/gamma' : Item(status=' ', wc_rev=2),
4603 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
4604 expected_status, None, wc_dir)
4607 cur_rev = 2
4608 for path in (["A", "D", "G", "pi"],
4609 ["A", "D", "G", "rho"],
4610 ["A", "D", "G", "tau"],
4611 ["A", "D", "H", "chi"],
4612 ["A", "D", "H", "omega"],
4613 ["A", "D", "H", "psi"],
4614 ["A", "D", "gamma"]):
4615 path_name = os.path.join(wc_dir, *path)
4616 canon_path_name = os.path.join(*path)
4617 path[1] = "copy-of-D"
4618 copy_of_path_name = os.path.join(wc_dir, *path)
4619 var_name = 'new_content_for_' + path[len(path) - 1]
4620 file_contents = "new content to " + path[len(path) - 1] + "\n"
4621 globals()[var_name] = file_contents
4622 cur_rev = tweak_src_then_merge_to_dest(sbox, path_name,
4623 copy_of_path_name, canon_path_name,
4624 file_contents, cur_rev)
4626 copy_of_A_D_wc_rev = cur_rev
4627 svntest.actions.run_and_verify_svn(None,
4628 ['\n',
4629 'Committed revision ' + str(cur_rev+1) +
4630 '.\n'],
4632 'mkdir', sbox.repo_url + '/A/D/umlaut',
4633 '-m', "log msg")
4634 rev_to_merge_to_copy_of_D = cur_rev + 1
4636 # Search for the comment entitled "The Merge Kluge" elsewhere in
4637 # this file, to understand why we shorten and chdir() below.
4638 short_copy_of_A_D_path = shorten_path_kludge(copy_of_A_D_path)
4640 # All the file descendants of /A/copy-of-D/ have already been merged
4641 # so the only notification we expect is for the added 'umlaut'.
4642 expected_output = wc.State(short_copy_of_A_D_path, {
4643 'umlaut' : Item(status='A '),
4646 # All the local svn:mergeinfo under A/copy-of-D elides
4647 # to A/copy-of-D.
4648 expected_status = wc.State(short_copy_of_A_D_path, {
4649 '' : Item(status=' M', wc_rev=copy_of_A_D_wc_rev),
4650 'G' : Item(status=' ', wc_rev=copy_of_A_D_wc_rev),
4651 'G/pi' : Item(status='M ', wc_rev=copy_of_A_D_wc_rev),
4652 'G/rho' : Item(status='M ', wc_rev=copy_of_A_D_wc_rev),
4653 'G/tau' : Item(status='M ', wc_rev=copy_of_A_D_wc_rev),
4654 'H' : Item(status=' ', wc_rev=copy_of_A_D_wc_rev),
4655 'H/chi' : Item(status='M ', wc_rev=copy_of_A_D_wc_rev),
4656 'H/omega' : Item(status='M ', wc_rev=copy_of_A_D_wc_rev),
4657 'H/psi' : Item(status='M ', wc_rev=copy_of_A_D_wc_rev),
4658 'gamma' : Item(status='M ', wc_rev=copy_of_A_D_wc_rev),
4659 'umlaut' : Item(status='A ', copied='+', wc_rev='-'),
4662 merged_rangelist = "3-%d" % rev_to_merge_to_copy_of_D
4665 expected_disk = wc.State('', {
4666 '' : Item(props={SVN_PROP_MERGEINFO : '/A/D:' + merged_rangelist}),
4667 'G' : Item(),
4668 'G/pi' : Item(new_content_for_pi),
4669 'G/rho' : Item(new_content_for_rho),
4670 'G/tau' : Item(new_content_for_tau),
4671 'H' : Item(),
4672 'H/chi' : Item(new_content_for_chi,),
4673 'H/omega' : Item(new_content_for_omega,),
4674 'H/psi' : Item(new_content_for_psi,),
4675 'gamma' : Item(new_content_for_gamma,),
4676 'umlaut' : Item(),
4678 expected_skip = wc.State(short_copy_of_A_D_path, { })
4679 os.chdir(svntest.main.work_dir)
4680 svntest.actions.run_and_verify_merge(short_copy_of_A_D_path,
4682 str(rev_to_merge_to_copy_of_D),
4683 sbox.repo_url + '/A/D',
4684 expected_output,
4685 expected_disk,
4686 expected_status,
4687 expected_skip,
4688 None,
4689 None,
4690 None,
4691 None,
4692 None, 1)
4694 def set_up_branch(sbox, branch_only = False, nbr_of_branches = 1):
4695 '''Starting with standard greek tree, copy 'A' NBR_OF_BRANCHES times
4696 to A_COPY, A_COPY_2, A_COPY_3, and so on. Then make four modifications
4697 (setting file contents to "New content") under A:
4698 r(2 + NBR_OF_BRANCHES) - A/D/H/psi
4699 r(3 + NBR_OF_BRANCHES) - A/D/G/rho
4700 r(4 + NBR_OF_BRANCHES) - A/B/E/beta
4701 r(5 + NBR_OF_BRANCHES) - A/D/H/omega'''
4703 wc_dir = sbox.wc_dir
4705 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
4706 expected_disk = svntest.main.greek_state.copy()
4708 def copy_A(dest_name, rev):
4709 expected = svntest.verify.UnorderedOutput(
4710 ["A " + os.path.join(wc_dir, dest_name, "B") + "\n",
4711 "A " + os.path.join(wc_dir, dest_name, "B", "lambda") + "\n",
4712 "A " + os.path.join(wc_dir, dest_name, "B", "E") + "\n",
4713 "A " + os.path.join(wc_dir, dest_name, "B", "E", "alpha") + "\n",
4714 "A " + os.path.join(wc_dir, dest_name, "B", "E", "beta") + "\n",
4715 "A " + os.path.join(wc_dir, dest_name, "B", "F") + "\n",
4716 "A " + os.path.join(wc_dir, dest_name, "mu") + "\n",
4717 "A " + os.path.join(wc_dir, dest_name, "C") + "\n",
4718 "A " + os.path.join(wc_dir, dest_name, "D") + "\n",
4719 "A " + os.path.join(wc_dir, dest_name, "D", "gamma") + "\n",
4720 "A " + os.path.join(wc_dir, dest_name, "D", "G") + "\n",
4721 "A " + os.path.join(wc_dir, dest_name, "D", "G", "pi") + "\n",
4722 "A " + os.path.join(wc_dir, dest_name, "D", "G", "rho") + "\n",
4723 "A " + os.path.join(wc_dir, dest_name, "D", "G", "tau") + "\n",
4724 "A " + os.path.join(wc_dir, dest_name, "D", "H") + "\n",
4725 "A " + os.path.join(wc_dir, dest_name, "D", "H", "chi") + "\n",
4726 "A " + os.path.join(wc_dir, dest_name, "D", "H", "omega") + "\n",
4727 "A " + os.path.join(wc_dir, dest_name, "D", "H", "psi") + "\n",
4728 "Checked out revision " + str(rev - 1) + ".\n",
4729 "A " + os.path.join(wc_dir, dest_name) + "\n"])
4730 expected_status.add({
4731 dest_name + "/B" : Item(status=' ', wc_rev=rev),
4732 dest_name + "/B/lambda" : Item(status=' ', wc_rev=rev),
4733 dest_name + "/B/E" : Item(status=' ', wc_rev=rev),
4734 dest_name + "/B/E/alpha" : Item(status=' ', wc_rev=rev),
4735 dest_name + "/B/E/beta" : Item(status=' ', wc_rev=rev),
4736 dest_name + "/B/F" : Item(status=' ', wc_rev=rev),
4737 dest_name + "/mu" : Item(status=' ', wc_rev=rev),
4738 dest_name + "/C" : Item(status=' ', wc_rev=rev),
4739 dest_name + "/D" : Item(status=' ', wc_rev=rev),
4740 dest_name + "/D/gamma" : Item(status=' ', wc_rev=rev),
4741 dest_name + "/D/G" : Item(status=' ', wc_rev=rev),
4742 dest_name + "/D/G/pi" : Item(status=' ', wc_rev=rev),
4743 dest_name + "/D/G/rho" : Item(status=' ', wc_rev=rev),
4744 dest_name + "/D/G/tau" : Item(status=' ', wc_rev=rev),
4745 dest_name + "/D/H" : Item(status=' ', wc_rev=rev),
4746 dest_name + "/D/H/chi" : Item(status=' ', wc_rev=rev),
4747 dest_name + "/D/H/omega" : Item(status=' ', wc_rev=rev),
4748 dest_name + "/D/H/psi" : Item(status=' ', wc_rev=rev),
4749 dest_name : Item(status=' ', wc_rev=rev)})
4750 expected_disk.add({
4751 dest_name : Item(),
4752 dest_name + '/B' : Item(),
4753 dest_name + '/B/lambda' : Item("This is the file 'lambda'.\n"),
4754 dest_name + '/B/E' : Item(),
4755 dest_name + '/B/E/alpha' : Item("This is the file 'alpha'.\n"),
4756 dest_name + '/B/E/beta' : Item("This is the file 'beta'.\n"),
4757 dest_name + '/B/F' : Item(),
4758 dest_name + '/mu' : Item("This is the file 'mu'.\n"),
4759 dest_name + '/C' : Item(),
4760 dest_name + '/D' : Item(),
4761 dest_name + '/D/gamma' : Item("This is the file 'gamma'.\n"),
4762 dest_name + '/D/G' : Item(),
4763 dest_name + '/D/G/pi' : Item("This is the file 'pi'.\n"),
4764 dest_name + '/D/G/rho' : Item("This is the file 'rho'.\n"),
4765 dest_name + '/D/G/tau' : Item("This is the file 'tau'.\n"),
4766 dest_name + '/D/H' : Item(),
4767 dest_name + '/D/H/chi' : Item("This is the file 'chi'.\n"),
4768 dest_name + '/D/H/omega' : Item("This is the file 'omega'.\n"),
4769 dest_name + '/D/H/psi' : Item("This is the file 'psi'.\n"),
4772 # Make a branch A_COPY to merge into.
4773 svntest.actions.run_and_verify_svn(None, expected, [], 'copy',
4774 sbox.repo_url + "/A",
4775 os.path.join(wc_dir,
4776 dest_name))
4778 expected_output = wc.State(wc_dir, {dest_name : Item(verb='Adding')})
4779 svntest.actions.run_and_verify_commit(wc_dir,
4780 expected_output,
4781 expected_status,
4782 None,
4783 wc_dir)
4784 for i in range(nbr_of_branches):
4785 if i == 0:
4786 copy_A('A_COPY', i + 2)
4787 else:
4788 copy_A('A_COPY_' + str(i + 1), i + 2)
4790 if (branch_only):
4791 return expected_disk, expected_status
4793 # Make some changes under A which we'll later merge under A_COPY:
4795 # r(nbr_of_branches + 2) - modify and commit A/D/H/psi
4796 svntest.main.file_write(os.path.join(wc_dir, "A", "D", "H", "psi"),
4797 "New content")
4798 expected_output = wc.State(wc_dir, {'A/D/H/psi' : Item(verb='Sending')})
4799 expected_status.tweak('A/D/H/psi', wc_rev=nbr_of_branches + 2)
4800 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
4801 expected_status, None, wc_dir)
4802 expected_disk.tweak('A/D/H/psi', contents="New content")
4804 # r(nbr_of_branches + 3) - modify and commit A/D/G/rho
4805 svntest.main.file_write(os.path.join(wc_dir, "A", "D", "G", "rho"),
4806 "New content")
4807 expected_output = wc.State(wc_dir, {'A/D/G/rho' : Item(verb='Sending')})
4808 expected_status.tweak('A/D/G/rho', wc_rev=nbr_of_branches + 3)
4809 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
4810 expected_status, None, wc_dir)
4811 expected_disk.tweak('A/D/G/rho', contents="New content")
4813 # r(nbr_of_branches + 4) - modify and commit A/B/E/beta
4814 svntest.main.file_write(os.path.join(wc_dir, "A", "B", "E", "beta"),
4815 "New content")
4816 expected_output = wc.State(wc_dir, {'A/B/E/beta' : Item(verb='Sending')})
4817 expected_status.tweak('A/B/E/beta', wc_rev=nbr_of_branches + 4)
4818 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
4819 expected_status, None, wc_dir)
4820 expected_disk.tweak('A/B/E/beta', contents="New content")
4822 # r(nbr_of_branches + 5) - modify and commit A/D/H/omega
4823 svntest.main.file_write(os.path.join(wc_dir, "A", "D", "H", "omega"),
4824 "New content")
4825 expected_output = wc.State(wc_dir, {'A/D/H/omega' : Item(verb='Sending')})
4826 expected_status.tweak('A/D/H/omega', wc_rev=nbr_of_branches + 5)
4827 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
4828 expected_status, None, wc_dir)
4829 expected_disk.tweak('A/D/H/omega', contents="New content")
4831 return expected_disk, expected_status
4834 def mergeinfo_inheritance(sbox):
4835 "target inherits mergeinfo from nearest ancestor"
4837 # Test for Issues #2733 and #2734.
4839 # When the target of a merge has no explicit mergeinfo and the merge
4840 # would result in mergeinfo being added to the target which...
4842 # ...is a subset of the *local* mergeinfo on one of the target's
4843 # ancestors (it's nearest ancestor takes precedence), then the merge is
4844 # not repeated and no mergeinfo should be set on the target (Issue #2734).
4846 # OR
4848 # ...is not a subset it's nearest ancestor, the target should inherit the
4849 # non-inersecting mergeinfo (local or committed, the former takes
4850 # precedence) from it's nearest ancestor (Issue #2733).
4852 sbox.build()
4853 wc_dir = sbox.wc_dir
4854 wc_disk, wc_status = set_up_branch(sbox)
4856 # Some paths we'll care about
4857 A_COPY_path = os.path.join(wc_dir, "A_COPY")
4858 B_COPY_path = os.path.join(wc_dir, "A_COPY", "B")
4859 beta_COPY_path = os.path.join(wc_dir, "A_COPY", "B", "E", "beta")
4860 E_COPY_path = os.path.join(wc_dir, "A_COPY", "B", "E")
4861 omega_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "H", "omega")
4862 D_COPY_path = os.path.join(wc_dir, "A_COPY", "D")
4863 G_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "G")
4865 # Now start merging...
4867 # Merge r4 into A_COPY/D/
4868 # Search for the comment entitled "The Merge Kluge" elsewhere in
4869 # this file, to understand why we shorten and chdir() below.
4870 short_D_COPY_path = shorten_path_kludge(D_COPY_path)
4871 expected_output = wc.State(short_D_COPY_path, {
4872 'G/rho' : Item(status='U '),
4874 expected_status = wc.State(short_D_COPY_path, {
4875 '' : Item(status=' M', wc_rev=2),
4876 'G' : Item(status=' ', wc_rev=2),
4877 'G/pi' : Item(status=' ', wc_rev=2),
4878 'G/rho' : Item(status='M ', wc_rev=2),
4879 'G/tau' : Item(status=' ', wc_rev=2),
4880 'H' : Item(status=' ', wc_rev=2),
4881 'H/chi' : Item(status=' ', wc_rev=2),
4882 'H/psi' : Item(status=' ', wc_rev=2),
4883 'H/omega' : Item(status=' ', wc_rev=2),
4884 'gamma' : Item(status=' ', wc_rev=2),
4886 # We test issue #2733 here (with a directory as the merge target).
4887 # r1 should be inherited from 'A_COPY'.
4888 expected_disk = wc.State('', {
4889 '' : Item(props={SVN_PROP_MERGEINFO : '/A/D:4'}),
4890 'G' : Item(),
4891 'G/pi' : Item("This is the file 'pi'.\n"),
4892 'G/rho' : Item("New content"),
4893 'G/tau' : Item("This is the file 'tau'.\n"),
4894 'H' : Item(),
4895 'H/chi' : Item("This is the file 'chi'.\n"),
4896 'H/psi' : Item("This is the file 'psi'.\n"),
4897 'H/omega' : Item("This is the file 'omega'.\n"),
4898 'gamma' : Item("This is the file 'gamma'.\n")
4900 expected_skip = wc.State(short_D_COPY_path, { })
4901 saved_cwd = os.getcwd()
4902 os.chdir(svntest.main.work_dir)
4903 svntest.actions.run_and_verify_merge(short_D_COPY_path, '3', '4',
4904 sbox.repo_url + \
4905 '/A/D',
4906 expected_output,
4907 expected_disk,
4908 expected_status,
4909 expected_skip,
4910 None, None, None, None,
4911 None, 1)
4912 os.chdir(saved_cwd)
4914 # Merge r4 again, this time into A_COPY/D/G. An ancestor directory
4915 # (A_COPY/D) exists with identical local mergeinfo, so the merge
4916 # should not be repeated. We test issue #2734 here with (with a
4917 # directory as the merge target).
4918 short_G_COPY_path = shorten_path_kludge(G_COPY_path)
4919 expected_output = wc.State(short_G_COPY_path, { })
4920 expected_status = wc.State(short_G_COPY_path, {
4921 '' : Item(status=' ', wc_rev=2),
4922 'pi' : Item(status=' ', wc_rev=2),
4923 'rho' : Item(status='M ', wc_rev=2),
4924 'tau' : Item(status=' ', wc_rev=2),
4926 expected_disk = wc.State('', {
4927 'pi' : Item("This is the file 'pi'.\n"),
4928 'rho' : Item("New content"),
4929 'tau' : Item("This is the file 'tau'.\n"),
4931 expected_skip = wc.State(short_G_COPY_path, { })
4932 os.chdir(svntest.main.work_dir)
4933 svntest.actions.run_and_verify_merge(short_G_COPY_path, '3', '4',
4934 sbox.repo_url + \
4935 '/A/D/G',
4936 expected_output,
4937 expected_disk,
4938 expected_status,
4939 expected_skip,
4940 None, None, None, None,
4941 None, 1)
4942 os.chdir(saved_cwd)
4943 # Merge r5 into A_COPY/B. Again, r1 should be inherited from
4944 # A_COPY (Issue #2733)
4945 short_B_COPY_path = shorten_path_kludge(B_COPY_path)
4946 expected_output = wc.State(short_B_COPY_path, {
4947 'E/beta' : Item(status='U '),
4949 expected_status = wc.State(short_B_COPY_path, {
4950 '' : Item(status=' M', wc_rev=2),
4951 'E' : Item(status=' ', wc_rev=2),
4952 'E/alpha' : Item(status=' ', wc_rev=2),
4953 'E/beta' : Item(status='M ', wc_rev=2),
4954 'lambda' : Item(status=' ', wc_rev=2),
4955 'F' : Item(status=' ', wc_rev=2),
4957 expected_disk = wc.State('', {
4958 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B:5'}),
4959 'E' : Item(),
4960 'E/alpha' : Item("This is the file 'alpha'.\n"),
4961 'E/beta' : Item("New content"),
4962 'F' : Item(),
4963 'lambda' : Item("This is the file 'lambda'.\n")
4965 expected_skip = wc.State(short_B_COPY_path, { })
4967 os.chdir(svntest.main.work_dir)
4968 svntest.actions.run_and_verify_merge(short_B_COPY_path, '4', '5',
4969 sbox.repo_url + \
4970 '/A/B',
4971 expected_output,
4972 expected_disk,
4973 expected_status,
4974 expected_skip,
4975 None, None, None, None,
4976 None, 1)
4977 os.chdir(saved_cwd)
4979 # Merge r5 again, this time into A_COPY/B/E/beta. An ancestor
4980 # directory (A_COPY/B) exists with identical local mergeinfo, so
4981 # the merge should not be repeated (Issue #2734 with a file as the
4982 # merge target).
4983 short_beta_COPY_path = shorten_path_kludge(beta_COPY_path)
4984 expected_skip = wc.State(short_beta_COPY_path, { })
4985 saved_cwd = os.getcwd()
4987 os.chdir(svntest.main.work_dir)
4988 # run_and_verify_merge doesn't support merging to a file WCPATH
4989 # so use run_and_verify_svn.
4990 svntest.actions.run_and_verify_svn(None, [], [], 'merge', '-c5',
4991 sbox.repo_url + '/A/B/E/beta',
4992 short_beta_COPY_path)
4993 os.chdir(saved_cwd)
4995 # The merge wasn't repeated so beta shouldn't have any mergeinfo.
4996 # We are implicitly testing that without looking at the prop value
4997 # itself, just beta's prop modification status.
4998 expected_status = wc.State(beta_COPY_path, {
4999 '' : Item(status='M ', wc_rev=2),
5001 svntest.actions.run_and_verify_status(beta_COPY_path, expected_status)
5003 # Merge r3 into A_COPY. A_COPY's two descendants with mergeinfo,
5004 # A_COPY/B/E/beta and A_COPY/D/G/rho must have complete mergeinfo
5005 # so they both should pick up r3 too.
5006 short_A_COPY_path = shorten_path_kludge(A_COPY_path)
5007 expected_output = wc.State(short_A_COPY_path, {
5008 'D/H/psi' : Item(status='U '),
5010 expected_status = wc.State(short_A_COPY_path, {
5011 '' : Item(status=' M', wc_rev=2),
5012 'B' : Item(status=' M', wc_rev=2),
5013 'mu' : Item(status=' ', wc_rev=2),
5014 'B/E' : Item(status=' ', wc_rev=2),
5015 'B/E/alpha' : Item(status=' ', wc_rev=2),
5016 'B/E/beta' : Item(status='M ', wc_rev=2),
5017 'B/lambda' : Item(status=' ', wc_rev=2),
5018 'B/F' : Item(status=' ', wc_rev=2),
5019 'C' : Item(status=' ', wc_rev=2),
5020 'D' : Item(status=' M', wc_rev=2),
5021 'D/G' : Item(status=' ', wc_rev=2),
5022 'D/G/pi' : Item(status=' ', wc_rev=2),
5023 'D/G/rho' : Item(status='M ', wc_rev=2),
5024 'D/G/tau' : Item(status=' ', wc_rev=2),
5025 'D/gamma' : Item(status=' ', wc_rev=2),
5026 'D/H' : Item(status=' ', wc_rev=2),
5027 'D/H/chi' : Item(status=' ', wc_rev=2),
5028 'D/H/psi' : Item(status='M ', wc_rev=2),
5029 'D/H/omega' : Item(status=' ', wc_rev=2),
5031 expected_disk = wc.State('', {
5032 '' : Item(props={SVN_PROP_MERGEINFO : '/A:3'}),
5033 'B' : Item(props={SVN_PROP_MERGEINFO : '/A/B:3,5'}),
5034 'mu' : Item("This is the file 'mu'.\n"),
5035 'B/E' : Item(),
5036 'B/E/alpha' : Item("This is the file 'alpha'.\n"),
5037 'B/E/beta' : Item("New content"),
5038 'B/lambda' : Item("This is the file 'lambda'.\n"),
5039 'B/F' : Item(),
5040 'C' : Item(),
5041 'D' : Item(props={SVN_PROP_MERGEINFO : '/A/D:3-4'}),
5042 'D/G' : Item(),
5043 'D/G/pi' : Item("This is the file 'pi'.\n"),
5044 'D/G/rho' : Item("New content"),
5045 'D/G/tau' : Item("This is the file 'tau'.\n"),
5046 'D/gamma' : Item("This is the file 'gamma'.\n"),
5047 'D/H' : Item(),
5048 'D/H/chi' : Item("This is the file 'chi'.\n"),
5049 'D/H/psi' : Item("New content"),
5050 'D/H/omega' : Item("This is the file 'omega'.\n"),
5052 expected_skip = wc.State(short_A_COPY_path, { })
5053 saved_cwd = os.getcwd()
5054 os.chdir(svntest.main.work_dir)
5055 svntest.actions.run_and_verify_merge(short_A_COPY_path, '2', '3',
5056 sbox.repo_url + \
5057 '/A',
5058 expected_output,
5059 expected_disk,
5060 expected_status,
5061 expected_skip,
5062 None, None, None, None,
5063 None, 1)
5064 os.chdir(saved_cwd)
5066 # Merge r6 into A_COPY/D/H/omega, it should inherit it's nearest
5067 # ancestor's (A_COPY/D) mergeinfo (Issue #2733 with a file as the
5068 # merge target).
5069 short_omega_COPY_path = shorten_path_kludge(omega_COPY_path)
5070 expected_skip = wc.State(short_omega_COPY_path, { })
5071 saved_cwd = os.getcwd()
5072 os.chdir(svntest.main.work_dir)
5073 # run_and_verify_merge doesn't support merging to a file WCPATH
5074 # so use run_and_verify_svn.
5075 svntest.actions.run_and_verify_svn(None,
5076 expected_merge_output([[6]],
5077 'U ' + short_omega_COPY_path + '\n'),
5078 [], 'merge', '-c6',
5079 sbox.repo_url + '/A/D/H/omega',
5080 short_omega_COPY_path)
5081 os.chdir(saved_cwd)
5083 # Check that mergeinfo was properly set on A_COPY/D/H/omega
5084 svntest.actions.run_and_verify_svn(None,
5085 ["/A/D/H/omega:3-4,6\n"],
5087 'propget', SVN_PROP_MERGEINFO,
5088 omega_COPY_path)
5090 # Given a merge target *without* any of the following:
5092 # 1) Explicit mergeinfo set on itself in the WC
5093 # 2) Any WC ancestor to inherit mergeinfo from
5094 # 3) Any mergeinfo for the target in the repository
5096 # Check that the target still inherits mergeinfo from it's nearest
5097 # repository ancestor.
5099 # Commit all the merges thus far
5100 expected_output = wc.State(wc_dir, {
5101 'A_COPY' : Item(verb='Sending'),
5102 'A_COPY/B' : Item(verb='Sending'),
5103 'A_COPY/B/E/beta' : Item(verb='Sending'),
5104 'A_COPY/D' : Item(verb='Sending'),
5105 'A_COPY/D/G/rho' : Item(verb='Sending'),
5106 'A_COPY/D/H/omega' : Item(verb='Sending'),
5107 'A_COPY/D/H/psi' : Item(verb='Sending'),
5109 wc_status.tweak('A_COPY', 'A_COPY/B', 'A_COPY/B/E/beta', 'A_COPY/D',
5110 'A_COPY/D/G/rho', 'A_COPY/D/H/omega', 'A_COPY/D/H/psi',
5111 wc_rev=7)
5112 svntest.actions.run_and_verify_commit(wc_dir,
5113 expected_output,
5114 wc_status,
5115 None,
5116 wc_dir)
5118 # Copy the subtree A_COPY/B/E from the working copy, making the
5119 # disconnected WC E_only.
5120 other_wc = sbox.add_wc_path('E_only')
5121 svntest.actions.duplicate_dir(E_COPY_path, other_wc)
5123 # Update the disconnected WC it so it will get the most recent mergeinfo
5124 # from the repos when merging.
5125 svntest.actions.run_and_verify_svn(None, ["At revision 7.\n"], [], 'up',
5126 other_wc)
5128 # Merge r5:4 into the root of the disconnected WC.
5129 # E_only has no explicit mergeinfo and since it's the root of the WC
5130 # cannot inherit and mergeinfo from a working copy ancestor path. Nor
5131 # does it have any mergeinfo explicitly set on it in the repository.
5132 # An ancestor path on the repository side, A_COPY/B does have the merge
5133 # info '/A/B:1,3,5' however and E_only should inherit this, resulting in
5134 # mergeinfo of 'A/B/E:1,3' after the removal of r5.
5135 short_other_wc_path = shorten_path_kludge(other_wc)
5136 expected_output = wc.State(short_other_wc_path,
5137 {'beta' : Item(status='U ')})
5138 expected_status = wc.State(short_other_wc_path, {
5139 '' : Item(status=' M', wc_rev=7),
5140 'alpha' : Item(status=' ', wc_rev=7),
5141 'beta' : Item(status='M ', wc_rev=7),
5143 expected_disk = wc.State('', {
5144 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B/E:3'}),
5145 'alpha' : Item("This is the file 'alpha'.\n"),
5146 'beta' : Item("This is the file 'beta'.\n"),
5148 expected_skip = wc.State(short_other_wc_path, { })
5150 os.chdir(svntest.main.work_dir)
5151 svntest.actions.run_and_verify_merge(short_other_wc_path, '5', '4',
5152 sbox.repo_url + \
5153 '/A/B/E',
5154 expected_output,
5155 expected_disk,
5156 expected_status,
5157 expected_skip,
5158 None, None, None, None,
5159 None, 1)
5161 def mergeinfo_elision(sbox):
5162 "mergeinfo elides to ancestor with identical info"
5164 # When a merge would result in mergeinfo on a target which is identical
5165 # to mergeinfo (local or committed) on one of the node's ancestors (the
5166 # nearest ancestor takes precedence), then the mergeinfo elides from the
5167 # target to the nearest ancestor (e.g. no mergeinfo is set on the target
5168 # or committed mergeinfo is removed).
5170 sbox.build()
5171 wc_dir = sbox.wc_dir
5172 wc_disk, wc_status = set_up_branch(sbox)
5174 # Some paths we'll care about
5175 A_COPY_path = os.path.join(wc_dir, "A_COPY")
5176 beta_COPY_path = os.path.join(wc_dir, "A_COPY", "B", "E", "beta")
5177 G_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "G")
5179 # Now start merging...
5181 # Merge r5 into A_COPY/B/E/beta.
5182 short_beta_COPY_path = shorten_path_kludge(beta_COPY_path)
5183 expected_skip = wc.State(short_beta_COPY_path, { })
5184 saved_cwd = os.getcwd()
5186 os.chdir(svntest.main.work_dir)
5187 # run_and_verify_merge doesn't support merging to a file WCPATH
5188 # so use run_and_verify_svn.
5189 svntest.actions.run_and_verify_svn(None,
5190 expected_merge_output([[5]],
5191 'U ' + short_beta_COPY_path + '\n'),
5192 [], 'merge', '-c5',
5193 sbox.repo_url + '/A/B/E/beta',
5194 short_beta_COPY_path)
5195 os.chdir(saved_cwd)
5197 # Check beta's status and props.
5198 expected_status = wc.State(beta_COPY_path, {
5199 '' : Item(status='MM', wc_rev=2),
5201 svntest.actions.run_and_verify_status(beta_COPY_path, expected_status)
5203 svntest.actions.run_and_verify_svn(None, ["/A/B/E/beta:5\n"], [],
5204 'propget', SVN_PROP_MERGEINFO,
5205 beta_COPY_path)
5207 # Commit the merge
5208 expected_output = wc.State(wc_dir, {
5209 'A_COPY/B/E/beta' : Item(verb='Sending'),
5211 wc_status.tweak('A_COPY/B/E/beta', wc_rev=7)
5212 svntest.actions.run_and_verify_commit(wc_dir,
5213 expected_output,
5214 wc_status,
5215 None,
5216 wc_dir)
5218 # Update A_COPY to get all paths to the same working revision.
5219 svntest.actions.run_and_verify_svn(None, ["At revision 7.\n"], [],
5220 'up', wc_dir)
5221 wc_status.tweak(wc_rev=7)
5223 # Merge r4 into A_COPY/D/G.
5224 # Search for the comment entitled "The Merge Kluge" elsewhere in
5225 # this file, to understand why we shorten and chdir() below.
5226 short_G_COPY_path = shorten_path_kludge(G_COPY_path)
5227 expected_output = wc.State(short_G_COPY_path, {
5228 'rho' : Item(status='U ')
5230 expected_status = wc.State(short_G_COPY_path, {
5231 '' : Item(status=' M', wc_rev=7),
5232 'pi' : Item(status=' ', wc_rev=7),
5233 'rho' : Item(status='M ', wc_rev=7),
5234 'tau' : Item(status=' ', wc_rev=7),
5236 expected_disk = wc.State('', {
5237 '' : Item(props={SVN_PROP_MERGEINFO : '/A/D/G:4'}),
5238 'pi' : Item("This is the file 'pi'.\n"),
5239 'rho' : Item("New content"),
5240 'tau' : Item("This is the file 'tau'.\n"),
5242 expected_skip = wc.State(short_G_COPY_path, { })
5244 os.chdir(svntest.main.work_dir)
5245 svntest.actions.run_and_verify_merge(short_G_COPY_path, '3', '4',
5246 sbox.repo_url + \
5247 '/A/D/G',
5248 expected_output,
5249 expected_disk,
5250 expected_status,
5251 expected_skip,
5252 None, None, None, None,
5253 None, 1)
5254 os.chdir(saved_cwd)
5256 # Merge r3:6 into A_COPY. This would result in identical mergeinfo
5257 # (r4-6) on A_COPY and two of it's descendants, A_COPY/D/G and
5258 # A_COPY/B/E/beta, so the mergeinfo on the latter two should elide
5259 # to A_COPY. In the case of A_COPY/D/G this means its wholly uncommitted
5260 # mergeinfo is removed leaving no prop mods. In the case of
5261 # A_COPY/B/E/beta its committed mergeinfo prop is removed leaving a prop
5262 # change.
5263 short_A_COPY_path = shorten_path_kludge(A_COPY_path)
5264 expected_output = wc.State(short_A_COPY_path, {
5265 'D/H/omega' : Item(status='U ')
5267 expected_status = wc.State(short_A_COPY_path, {
5268 '' : Item(status=' M', wc_rev=7),
5269 'B' : Item(status=' ', wc_rev=7),
5270 'mu' : Item(status=' ', wc_rev=7),
5271 'B/E' : Item(status=' ', wc_rev=7),
5272 'B/E/alpha' : Item(status=' ', wc_rev=7),
5273 'B/E/beta' : Item(status=' M', wc_rev=7),
5274 'B/lambda' : Item(status=' ', wc_rev=7),
5275 'B/F' : Item(status=' ', wc_rev=7),
5276 'C' : Item(status=' ', wc_rev=7),
5277 'D' : Item(status=' ', wc_rev=7),
5278 'D/G' : Item(status=' ', wc_rev=7),
5279 'D/G/pi' : Item(status=' ', wc_rev=7),
5280 'D/G/rho' : Item(status='M ', wc_rev=7),
5281 'D/G/tau' : Item(status=' ', wc_rev=7),
5282 'D/gamma' : Item(status=' ', wc_rev=7),
5283 'D/H' : Item(status=' ', wc_rev=7),
5284 'D/H/chi' : Item(status=' ', wc_rev=7),
5285 'D/H/psi' : Item(status=' ', wc_rev=7),
5286 'D/H/omega' : Item(status='M ', wc_rev=7),
5288 expected_disk = wc.State('', {
5289 '' : Item(props={SVN_PROP_MERGEINFO : '/A:4-6'}),
5290 'B' : Item(),
5291 'mu' : Item("This is the file 'mu'.\n"),
5292 'B/E' : Item(),
5293 'B/E/alpha' : Item("This is the file 'alpha'.\n"),
5294 'B/E/beta' : Item("New content"),
5295 'B/lambda' : Item("This is the file 'lambda'.\n"),
5296 'B/F' : Item(),
5297 'C' : Item(),
5298 'D' : Item(),
5299 'D/G' : Item(),
5300 'D/G/pi' : Item("This is the file 'pi'.\n"),
5301 'D/G/rho' : Item("New content"),
5302 'D/G/tau' : Item("This is the file 'tau'.\n"),
5303 'D/gamma' : Item("This is the file 'gamma'.\n"),
5304 'D/H' : Item(),
5305 'D/H/chi' : Item("This is the file 'chi'.\n"),
5306 'D/H/psi' : Item("This is the file 'psi'.\n"),
5307 'D/H/omega' : Item("New content"),
5309 expected_skip = wc.State(short_A_COPY_path, { })
5311 os.chdir(svntest.main.work_dir)
5312 svntest.actions.run_and_verify_merge(short_A_COPY_path, '3', '6',
5313 sbox.repo_url + \
5314 '/A',
5315 expected_output,
5316 expected_disk,
5317 expected_status,
5318 expected_skip,
5319 None, None, None, None,
5320 None, 1)
5321 os.chdir(saved_cwd)
5323 # Reverse merge r5 out of A_COPY/B/E/beta. The mergeinfo on
5324 # A_COPY/B/E/beta which previously elided will now return,
5325 # minus r5 of course.
5326 expected_skip = wc.State(short_beta_COPY_path, { })
5328 os.chdir(svntest.main.work_dir)
5329 # run_and_verify_merge doesn't support merging to a file WCPATH
5330 # so use run_and_verify_svn.
5331 svntest.actions.run_and_verify_svn(None,
5332 expected_merge_output([[-5]],
5333 'U ' + short_beta_COPY_path + '\n'),
5334 [], 'merge', '-c-5',
5335 sbox.repo_url + '/A/B/E/beta',
5336 short_beta_COPY_path)
5337 os.chdir(saved_cwd)
5339 # Check beta's status and props.
5340 expected_status = wc.State(beta_COPY_path, {
5341 '' : Item(status='MM', wc_rev=7),
5343 svntest.actions.run_and_verify_status(beta_COPY_path, expected_status)
5345 svntest.actions.run_and_verify_svn(None, ["/A/B/E/beta:4,6\n"], [],
5346 'propget', SVN_PROP_MERGEINFO,
5347 beta_COPY_path)
5349 # Merge r5 back into A_COPY/B/E/beta. Now the mergeinfo on the merge
5350 # target (A_COPY/B/E/beta) is identical to it's nearest ancestor with
5351 # mergeinfo (A_COPY) and so the former should elide.
5352 os.chdir(svntest.main.work_dir)
5353 # run_and_verify_merge doesn't support merging to a file WCPATH
5354 # so use run_and_verify_svn.
5355 svntest.actions.run_and_verify_svn(None,
5356 expected_merge_output([[5]],
5357 'G ' + short_beta_COPY_path + '\n'),
5358 [], 'merge', '-c5',
5359 sbox.repo_url + '/A/B/E/beta',
5360 short_beta_COPY_path)
5361 os.chdir(saved_cwd)
5363 # Check beta's status and props.
5364 expected_status = wc.State(beta_COPY_path, {
5365 '' : Item(status=' M', wc_rev=7),
5367 svntest.actions.run_and_verify_status(beta_COPY_path, expected_status)
5369 # Once again A_COPY/B/E/beta has no mergeinfo.
5370 svntest.actions.run_and_verify_svn(None, [], [],
5371 'propget', SVN_PROP_MERGEINFO,
5372 beta_COPY_path)
5374 def mergeinfo_inheritance_and_discontinuous_ranges(sbox):
5375 "discontinuous merges produce correct mergeinfo"
5377 # When a merge target has no explicit mergeinfo and is subject
5378 # to multiple merges, the resulting mergeinfo on the target
5379 # should reflect the combination of the inherited mergeinfo
5380 # with each merge performed.
5382 # Also tests implied merge source and target when only a revision
5383 # range is specified.
5385 sbox.build()
5386 wc_dir = sbox.wc_dir
5388 # Some paths we'll care about
5389 A_url = sbox.repo_url + '/A'
5390 A_COPY_path = os.path.join(wc_dir, "A_COPY")
5391 D_COPY_path = os.path.join(wc_dir, "A_COPY", "D")
5392 A_COPY_rho_path = os.path.join(wc_dir, "A_COPY", "D", "G", "rho")
5394 expected_disk, expected_status = set_up_branch(sbox)
5396 # Merge r4 into A_COPY
5397 saved_cwd = os.getcwd()
5399 os.chdir(A_COPY_path)
5400 svntest.actions.run_and_verify_svn(None,
5401 expected_merge_output([[4]], 'U ' +
5402 os.path.join("D", "G", "rho") + '\n'),
5403 [], 'merge', '-c4', A_url)
5404 os.chdir(saved_cwd)
5406 # Check the results of the merge.
5407 expected_status.tweak("A_COPY", status=' M')
5408 expected_status.tweak("A_COPY/D/G/rho", status='M ')
5409 svntest.actions.run_and_verify_status(wc_dir, expected_status)
5410 svntest.actions.run_and_verify_svn(None, ["/A:4\n"], [],
5411 'propget', SVN_PROP_MERGEINFO,
5412 A_COPY_path)
5414 # Merge r2:6 into A_COPY/D
5416 # Search for the comment entitled "The Merge Kluge" elsewhere in
5417 # this file, to understand why we shorten and chdir() below.
5419 # A_COPY/D should inherit the mergeinfo '/A:4' from A_COPY
5420 # combine it with the discontinous merges performed directly on
5421 # it (A/D/ 2:3 and A/D 4:6) resulting in '/A/D:3-6'.
5422 short_D_COPY_path = shorten_path_kludge(D_COPY_path)
5423 expected_output = wc.State(short_D_COPY_path, {
5424 'H/psi' : Item(status='U '),
5425 'H/omega' : Item(status='U '),
5427 expected_status = wc.State(short_D_COPY_path, {
5428 '' : Item(status=' M', wc_rev=2),
5429 'G' : Item(status=' ', wc_rev=2),
5430 'G/pi' : Item(status=' ', wc_rev=2),
5431 'G/rho' : Item(status='M ', wc_rev=2),
5432 'G/tau' : Item(status=' ', wc_rev=2),
5433 'H' : Item(status=' ', wc_rev=2),
5434 'H/chi' : Item(status=' ', wc_rev=2),
5435 'H/psi' : Item(status='M ', wc_rev=2),
5436 'H/omega' : Item(status='M ', wc_rev=2),
5437 'gamma' : Item(status=' ', wc_rev=2),
5439 expected_disk = wc.State('', {
5440 '' : Item(props={SVN_PROP_MERGEINFO : '/A/D:3-6'}),
5441 'G' : Item(),
5442 'G/pi' : Item("This is the file 'pi'.\n"),
5443 'G/rho' : Item("New content"),
5444 'G/tau' : Item("This is the file 'tau'.\n"),
5445 'H' : Item(),
5446 'H/chi' : Item("This is the file 'chi'.\n"),
5447 'H/psi' : Item("New content"),
5448 'H/omega' : Item("New content"),
5449 'gamma' : Item("This is the file 'gamma'.\n")
5451 expected_skip = wc.State(short_D_COPY_path, { })
5453 os.chdir(svntest.main.work_dir)
5454 svntest.actions.run_and_verify_merge(short_D_COPY_path, '2', '6',
5455 sbox.repo_url + '/A/D',
5456 expected_output,
5457 expected_disk,
5458 expected_status,
5459 expected_skip,
5460 None, None, None, None,
5461 None, 1)
5462 os.chdir(saved_cwd)
5464 # Wipe the memory of a portion of the previous merge...
5465 ### It'd be nice to use 'merge --record-only' here, but we can't (yet)
5466 ### wipe all ranges for a file due to the bug pointed out in r24645.
5467 mu_copy_path = os.path.join(A_COPY_path, 'mu')
5468 svntest.actions.run_and_verify_svn(None,
5469 ["property '" + SVN_PROP_MERGEINFO
5470 + "' set on '" +
5471 mu_copy_path + "'\n"], [], 'propset',
5472 SVN_PROP_MERGEINFO, '', mu_copy_path)
5473 # ...and confirm that we can commit the wiped mergeinfo...
5474 expected_output = wc.State(wc_dir, {
5475 'A_COPY/mu' : Item(verb='Sending'),
5477 svntest.actions.run_and_verify_commit(wc_dir,
5478 expected_output,
5479 None,
5480 None,
5481 mu_copy_path)
5482 # ...and that the presence of the property is retained, even when
5483 # the value has been wiped.
5484 svntest.actions.run_and_verify_svn(None, ['\n'], [], 'propget',
5485 SVN_PROP_MERGEINFO, mu_copy_path)
5487 def merge_to_target_with_copied_children(sbox):
5488 "merge works when target has copied children"
5490 # Test for Issue #2754 Can't merge to target with copied/moved children
5492 sbox.build()
5493 wc_dir = sbox.wc_dir
5494 expected_disk, expected_status = set_up_branch(sbox)
5496 # Some paths we'll care about
5497 D_COPY_path = os.path.join(wc_dir, "A_COPY", "D")
5498 G_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "G")
5499 rho_COPY_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "G", "rho_copy")
5501 # URL to URL copy A_COPY/D/G/rho to A_COPY/D/G/rho_copy
5502 svntest.actions.run_and_verify_svn(None, None, [], 'copy',
5503 sbox.repo_url + '/A_COPY/D/G/rho',
5504 sbox.repo_url + '/A_COPY/D/G/rho_copy',
5505 '-m', 'copy')
5507 # Update WC.
5508 expected_output = wc.State(wc_dir,
5509 {'A_COPY/D/G/rho_copy' : Item(status='A ')})
5510 expected_disk.add({
5511 'A_COPY/D/G/rho_copy' : Item("This is the file 'rho'.\n", props={})
5513 expected_status.tweak(wc_rev=7)
5514 expected_status.add({'A_COPY/D/G/rho_copy' : Item(status=' ', wc_rev=7)})
5515 svntest.actions.run_and_verify_update(wc_dir,
5516 expected_output,
5517 expected_disk,
5518 expected_status,
5519 None, None, None,
5520 None, None, 1)
5522 # Merge r4 into A_COPY/D/G/rho_copy.
5524 # Search for the comment entitled "The Merge Kluge" elsewhere in
5525 # this file, to understand why we shorten and chdir() below.
5526 os.chdir(svntest.main.work_dir)
5527 short_rho_COPY_COPY_path = shorten_path_kludge(rho_COPY_COPY_path)
5528 svntest.actions.run_and_verify_svn(None,
5529 expected_merge_output([[4]],
5530 'U ' + short_rho_COPY_COPY_path +
5531 '\n'),
5532 [], 'merge', '-c4',
5533 sbox.repo_url + '/A/D/G/rho',
5534 short_rho_COPY_COPY_path)
5536 # Merge r3:5 into A_COPY/D/G.
5537 short_G_COPY_path = shorten_path_kludge(G_COPY_path)
5538 expected_output = wc.State(short_G_COPY_path, {
5539 'rho' : Item(status='U ')
5541 expected_status = wc.State(short_G_COPY_path, {
5542 '' : Item(status=' M', wc_rev=7),
5543 'pi' : Item(status=' ', wc_rev=7),
5544 'rho' : Item(status='M ', wc_rev=7),
5545 'rho_copy' : Item(status='MM', wc_rev=7),
5546 'tau' : Item(status=' ', wc_rev=7),
5548 expected_disk = wc.State('', {
5549 '' : Item(props={SVN_PROP_MERGEINFO : '/A/D/G:4-5'}),
5550 'pi' : Item("This is the file 'pi'.\n"),
5551 'rho' : Item("New content"),
5552 'rho_copy' : Item("New content",
5553 props={SVN_PROP_MERGEINFO : '/A/D/G/rho:4'}),
5554 'tau' : Item("This is the file 'tau'.\n"),
5556 expected_skip = wc.State(short_G_COPY_path, { })
5557 svntest.actions.run_and_verify_merge(short_G_COPY_path, '3', '5',
5558 sbox.repo_url + \
5559 '/A/D/G',
5560 expected_output,
5561 expected_disk,
5562 expected_status,
5563 expected_skip,
5564 None, None, None, None,
5565 None, 1)
5567 def merge_to_switched_path(sbox):
5568 "merge to switched path does not inherit or elide"
5570 # When the target of a merge is a switched path we don't inherit WC
5571 # mergeinfo from above the target or attempt to elide the mergeinfo
5572 # set on the target as a result of the merge.
5574 sbox.build()
5575 wc_dir = sbox.wc_dir
5576 wc_disk, wc_status = set_up_branch(sbox)
5578 # Some paths we'll care about
5579 A_COPY_D_path = os.path.join(wc_dir, "A_COPY", "D")
5580 G_COPY_path = os.path.join(wc_dir, "A", "D", "G_COPY")
5581 A_COPY_D_G_path = os.path.join(wc_dir, "A_COPY", "D", "G")
5582 A_COPY_D_G_rho_path = os.path.join(wc_dir, "A_COPY", "D", "G", "rho")
5584 expected = svntest.verify.UnorderedOutput(
5585 ["A " + os.path.join(G_COPY_path, "pi") + "\n",
5586 "A " + os.path.join(G_COPY_path, "rho") + "\n",
5587 "A " + os.path.join(G_COPY_path, "tau") + "\n",
5588 "Checked out revision 6.\n",
5589 "A " + G_COPY_path + "\n"])
5591 # r7 - Copy A/D/G to A/D/G_COPY and commit.
5592 svntest.actions.run_and_verify_svn(None, expected, [], 'copy',
5593 sbox.repo_url + "/A/D/G",
5594 G_COPY_path)
5596 expected_output = wc.State(wc_dir, {'A/D/G_COPY' : Item(verb='Adding')})
5597 wc_status.add({
5598 "A/D/G_COPY" : Item(status=' ', wc_rev=7),
5599 "A/D/G_COPY/pi" : Item(status=' ', wc_rev=7),
5600 "A/D/G_COPY/rho" : Item(status=' ', wc_rev=7),
5601 "A/D/G_COPY/tau" : Item(status=' ', wc_rev=7),
5604 svntest.actions.run_and_verify_commit(wc_dir, expected_output, wc_status,
5605 None, wc_dir)
5607 # r8 - modify and commit A/D/G_COPY/rho
5608 svntest.main.file_write(os.path.join(wc_dir, "A", "D", "G_COPY", "rho"),
5609 "New *and* improved rho content")
5610 expected_output = wc.State(wc_dir, {'A/D/G_COPY/rho' : Item(verb='Sending')})
5611 wc_status.tweak('A/D/G_COPY/rho', wc_rev=8)
5612 svntest.actions.run_and_verify_commit(wc_dir, expected_output, wc_status,
5613 None, wc_dir)
5615 # Switch A_COPY/D/G to A/D/G.
5616 wc_disk.add({
5617 "A" : Item(),
5618 "A/D/G_COPY" : Item(),
5619 "A/D/G_COPY/pi" : Item("This is the file 'pi'.\n"),
5620 "A/D/G_COPY/rho" : Item("New *and* improved rho content"),
5621 "A/D/G_COPY/tau" : Item("This is the file 'tau'.\n"),
5623 wc_disk.tweak('A_COPY/D/G/rho',contents="New content")
5624 wc_status.tweak("A_COPY/D/G", wc_rev=8, switched='S')
5625 wc_status.tweak("A_COPY/D/G/pi", wc_rev=8)
5626 wc_status.tweak("A_COPY/D/G/rho", wc_rev=8)
5627 wc_status.tweak("A_COPY/D/G/tau", wc_rev=8)
5628 expected_output = svntest.wc.State(sbox.wc_dir, {
5629 "A_COPY/D/G/rho" : Item(status='U '),
5631 svntest.actions.run_and_verify_switch(sbox.wc_dir, A_COPY_D_G_path,
5632 sbox.repo_url + "/A/D/G",
5633 expected_output, wc_disk, wc_status,
5634 None, None, None, None, None, 1)
5636 # Merge r8 from A/D/G_COPY into our switched target A_COPY/D/G.
5637 # A_COPY/D/G should get mergeinfo for r8 as a result of the merge,
5638 # but because it's switched should not inherit the mergeinfo from
5639 # its nearest WC ancestor with mergeinfo (A_COPY: svn:mergeinfo : /A:1)
5641 # Search for the comment entitled "The Merge Kluge" elsewhere in
5642 # this file, to understand why we shorten and chdir() below.
5643 short_G_COPY_path = shorten_path_kludge(A_COPY_D_G_path)
5644 expected_output = wc.State(short_G_COPY_path, {
5645 'rho' : Item(status='U ')
5647 # Note: A_COPY/D/G won't show as switched because of the Merge Kluge.
5648 expected_status = wc.State(short_G_COPY_path, {
5649 '' : Item(status=' M', wc_rev=8),
5650 'pi' : Item(status=' ', wc_rev=8),
5651 'rho' : Item(status='M ', wc_rev=8),
5652 'tau' : Item(status=' ', wc_rev=8),
5654 expected_disk = wc.State('', {
5655 '' : Item(props={SVN_PROP_MERGEINFO : '/A/D/G_COPY:8'}),
5656 'pi' : Item("This is the file 'pi'.\n"),
5657 'rho' : Item("New *and* improved rho content"),
5658 'tau' : Item("This is the file 'tau'.\n"),
5660 expected_skip = wc.State(short_G_COPY_path, { })
5661 saved_cwd = os.getcwd()
5663 os.chdir(svntest.main.work_dir)
5664 svntest.actions.run_and_verify_merge(short_G_COPY_path, '7', '8',
5665 sbox.repo_url + '/A/D/G_COPY',
5666 expected_output, expected_disk,
5667 expected_status, expected_skip,
5668 None, None, None, None, None, 1)
5669 os.chdir(saved_cwd)
5671 # Check that the mergeinfo set on a target doesn't elide when that
5672 # target is switched.
5674 # Revert the previous merge and manually set 'svn:mergeinfo : '
5675 # on 'merge_tests-1\A_COPY\D'. Now merge -c-4 from /A/D/G into A_COPY/D/G.
5676 # This should still set 'svn:mergeinfo : ' on
5677 # 'merge_tests-1\A_COPY\D\G'. This would normally elide to A_COPY/D,
5678 # but since A_COPY/D/G is switched it should not.
5679 svntest.actions.run_and_verify_svn(None,
5680 ["Reverted '" + A_COPY_D_G_path+ "'\n",
5681 "Reverted '" + A_COPY_D_G_rho_path +
5682 "'\n"],
5683 [], 'revert', '-R', wc_dir)
5684 svntest.actions.run_and_verify_svn(None,
5685 ["property '" + SVN_PROP_MERGEINFO +
5686 "' set on '" + A_COPY_D_path+ "'" +
5687 "\n"], [], 'ps', SVN_PROP_MERGEINFO,
5688 '', A_COPY_D_path)
5689 svntest.actions.run_and_verify_svn(None,
5690 expected_merge_output([[-4]],
5691 'U ' + A_COPY_D_G_rho_path + '\n'),
5692 [], 'merge', '-c-4',
5693 sbox.repo_url + '/A/D/G_COPY',
5694 A_COPY_D_G_path)
5695 wc_status.tweak("A_COPY/D", status=' M')
5696 wc_status.tweak("A_COPY/D/G", status=' M')
5697 wc_status.tweak("A_COPY/D/G/rho", status='M ')
5698 svntest.actions.run_and_verify_status(wc_dir, wc_status)
5699 expected = svntest.verify.UnorderedOutput(
5700 ["A " + os.path.join(G_COPY_path, "pi") + "\n",
5701 "A " + os.path.join(G_COPY_path, "rho") + "\n",
5702 "A " + os.path.join(G_COPY_path, "tau") + "\n",
5703 "Checked out revision 6.\n",
5704 "A " + G_COPY_path + "\n"])
5705 expected = svntest.verify.UnorderedOutput(
5706 ["Properties on '" + A_COPY_D_path + "':\n",
5707 " " + SVN_PROP_MERGEINFO + " : \n",
5708 "Properties on '" + A_COPY_D_G_path + "':\n",
5709 " " + SVN_PROP_MERGEINFO +" : \n"])
5710 svntest.actions.run_and_verify_svn(None,
5711 expected, [],
5712 'pl', '-vR', A_COPY_D_path)
5714 # Test for issues
5716 # 2823: Account for mergeinfo differences for switched
5717 # directories when gathering mergeinfo
5719 # 2839: Support non-inheritable mergeinfo revision ranges
5720 def merge_to_path_with_switched_children(sbox):
5721 "merge to path with switched children"
5723 # Merging to a target with switched children requires special handling
5724 # to keep mergeinfo correct:
5726 # 1) If the target of a merge has switched children without explicit
5727 # mergeinfo, the switched children should get mergeinfo set on
5728 # them as a result of the merge. This mergeinfo includes the
5729 # mergeinfo resulting from the merge *and* any mergeinfo inherited
5730 # from the repos for the switched path.
5732 # 2) Mergeinfo on switched children should never elide.
5734 # 3) The path the switched child overrides cannot be modified by the
5735 # merge (it isn't present in the WC) so should not inherit any
5736 # mergeinfo added as a result of the merge. To prevent this, the
5737 # immediate parent of any switched child should have non-inheritable
5738 # mergeinfo added/modified for the merge performed.
5740 # 4) Because of 3, siblings of switched children will not inherit the
5741 # mergeinfo resulting from the merge, so must get their own, full set
5742 # of mergeinfo.
5744 sbox.build()
5745 wc_dir = sbox.wc_dir
5746 wc_disk, wc_status = set_up_branch(sbox, False, 3)
5748 # Some paths we'll care about
5749 D_path = os.path.join(wc_dir, "A", "D")
5750 A_COPY_path = os.path.join(wc_dir, "A_COPY")
5751 A_COPY_beta_path = os.path.join(wc_dir, "A_COPY", "B", "E", "beta")
5752 A_COPY_chi_path = os.path.join(wc_dir, "A_COPY", "D", "H", "chi")
5753 A_COPY_omega_path = os.path.join(wc_dir, "A_COPY", "D", "H", "omega")
5754 A_COPY_psi_path = os.path.join(wc_dir, "A_COPY", "D", "H", "psi")
5755 A_COPY_G_path = os.path.join(wc_dir, "A_COPY", "D", "G")
5756 A_COPY_rho_path = os.path.join(wc_dir, "A_COPY", "D", "G", "rho")
5757 A_COPY_H_path = os.path.join(wc_dir, "A_COPY", "D", "H")
5758 A_COPY_D_path = os.path.join(wc_dir, "A_COPY", "D")
5759 A_COPY_gamma_path = os.path.join(wc_dir, "A_COPY", "D", "gamma")
5760 H_COPY_2_path = os.path.join(wc_dir, "A_COPY_2", "D", "H")
5762 svntest.actions.run_and_verify_svn(None, ["At revision 8.\n"], [], 'up',
5763 wc_dir)
5764 wc_status.tweak(wc_rev=8)
5766 # Switch a file and dir path in the branch:
5768 # Switch A_COPY/D/G to A_COPY_2/D/G.
5769 wc_status.tweak("A_COPY/D/G", switched='S')
5770 expected_output = svntest.wc.State(sbox.wc_dir, {})
5771 svntest.actions.run_and_verify_switch(sbox.wc_dir, A_COPY_G_path,
5772 sbox.repo_url + "/A_COPY_2/D/G",
5773 expected_output, wc_disk, wc_status,
5774 None, None, None, None, None, 1)
5776 # Switch A_COPY/D/G/rho to A_COPY_3/D/G/rho.
5777 wc_status.tweak("A_COPY/D/G/rho", switched='S')
5778 expected_output = svntest.wc.State(sbox.wc_dir, {})
5779 svntest.actions.run_and_verify_switch(sbox.wc_dir, A_COPY_rho_path,
5780 sbox.repo_url + "/A_COPY_3/D/G/rho",
5781 expected_output, wc_disk, wc_status,
5782 None, None, None, None, None, 1)
5784 # Switch A_COPY/D/H/psi to A_COPY_2/D/H/psi.
5785 wc_status.tweak("A_COPY/D/H/psi", switched='S')
5786 expected_output = svntest.wc.State(sbox.wc_dir, {})
5787 svntest.actions.run_and_verify_switch(sbox.wc_dir, A_COPY_psi_path,
5788 sbox.repo_url + "/A_COPY_2/D/H/psi",
5789 expected_output, wc_disk, wc_status,
5790 None, None, None, None, None, 1)
5792 # Target with switched file child:
5794 # Merge r8 from A/D/H into A_COPY/D/H. The switched child of
5795 # A_COPY/D/H, file A_COPY/D/H/psi (which has no mergeinfo prior
5796 # to the merge), should get their own mergeinfo, both r8 from the
5797 # merge itself, and r1-2 inherited from A_COPY_2 in the repository.
5799 # A_COPY/D/H/psi's parent A_COPY/D/H has no pre-exiting explicit
5800 # mergeinfo so should get its own mergeinfo, made up of r1 inherited
5801 # from A_COPY and the non-inheritable r8 resulting from the merge.
5803 # A_COPY/D/H/psi's two unswitched siblings, A_COPY/D/H/chi and
5804 # A_COPY/D/H/omega won't inherit r8 from A_COPY/D/H, so need their
5805 # own mergeinfo.
5807 # Search for the comment entitled "The Merge Kluge" elsewhere in
5808 # this file, to understand why we shorten and chdir() below.
5809 short_H_COPY_path = shorten_path_kludge(A_COPY_H_path)
5810 expected_output = wc.State(short_H_COPY_path, {
5811 'omega' : Item(status='U ')
5813 expected_status = wc.State(short_H_COPY_path, {
5814 '' : Item(status=' M', wc_rev=8),
5815 'psi' : Item(status=' M', wc_rev=8, switched='S'),
5816 'omega' : Item(status='MM', wc_rev=8),
5817 'chi' : Item(status=' M', wc_rev=8),
5819 expected_disk = wc.State('', {
5820 '' : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:8*'}),
5821 'psi' : Item("This is the file 'psi'.\n",
5822 props={SVN_PROP_MERGEINFO : '/A/D/H/psi:8'}),
5823 'omega' : Item("New content",
5824 props={SVN_PROP_MERGEINFO : '/A/D/H/omega:8'}),
5825 'chi' : Item("This is the file 'chi'.\n",
5826 props={SVN_PROP_MERGEINFO : '/A/D/H/chi:8'}),
5828 expected_skip = wc.State(short_H_COPY_path, { })
5829 saved_cwd = os.getcwd()
5831 os.chdir(svntest.main.work_dir)
5833 svntest.actions.run_and_verify_merge(short_H_COPY_path, '7', '8',
5834 sbox.repo_url + '/A/D/H',
5835 expected_output, expected_disk,
5836 expected_status, expected_skip,
5837 None, None, None, None, None, 1)
5838 os.chdir(saved_cwd)
5840 # Target with switched dir child:
5842 # Merge r6 from A/D into A_COPY/D. The switched child of A_COPY/D,
5843 # directory A_COPY/D/G (which has no mergeinfo prior to the merge),
5844 # should get its own mergeinfo, both for r6 from the merge itself and
5845 # r1-2 from the repository.
5847 # A_COPY/D/G's parent A_COPY/D has no pre-exiting explicit
5848 # mergeinfo so should get its own mergeinfo, made up of r1 inherited
5849 # from A_COPY and the non-inheritable r5 resulting from the merge.
5851 # A_COPY/D/G's two unswitched siblings, A_COPY/D/gamma and A_COPY/D/H
5852 # won't inherit r5 from A_COPY/D, so need their own mergeinfo
5853 # added/updated respectively. A_COPY/D/H itself has a switched child
5854 # so r5 is set as non-inheritable on it. All of A_COPY/D/H's children,
5855 # which have explict mergeinfo from the previous merge, get r5 in their
5856 # mergeinfo.
5857 short_D_COPY_path = shorten_path_kludge(A_COPY_D_path)
5858 expected_output = wc.State(short_D_COPY_path, {
5859 'G/rho' : Item(status='U ')
5861 expected_status_D = wc.State(short_D_COPY_path, {
5862 '' : Item(status=' M', wc_rev=8),
5863 'H' : Item(status=' M', wc_rev=8),
5864 'H/chi' : Item(status=' M', wc_rev=8),
5865 'H/omega' : Item(status='MM', wc_rev=8),
5866 'H/psi' : Item(status=' M', wc_rev=8, switched='S'),
5867 'G' : Item(status=' M', wc_rev=8, switched='S'),
5868 'G/pi' : Item(status=' M', wc_rev=8),
5869 'G/rho' : Item(status='MM', wc_rev=8, switched='S'),
5870 'G/tau' : Item(status=' M', wc_rev=8),
5871 'gamma' : Item(status=' M', wc_rev=8),
5873 expected_disk_D = wc.State('', {
5874 '' : Item(props={SVN_PROP_MERGEINFO : '/A/D:6*'}),
5875 'H' : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:6*,8*'}),
5876 'H/chi' : Item("This is the file 'chi'.\n",
5877 props={SVN_PROP_MERGEINFO : '/A/D/H/chi:6,8'}),
5878 'H/omega' : Item("New content",
5879 props={SVN_PROP_MERGEINFO : '/A/D/H/omega:6,8'}),
5880 'H/psi' : Item("This is the file 'psi'.\n",
5881 props={SVN_PROP_MERGEINFO : '/A/D/H/psi:6,8'}),
5882 'G' : Item(props={SVN_PROP_MERGEINFO : '/A/D/G:6*'}),
5883 'G/pi' : Item("This is the file 'pi'.\n",
5884 props={SVN_PROP_MERGEINFO : '/A/D/G/pi:6'}),
5885 'G/rho' : Item("New content",
5886 props={SVN_PROP_MERGEINFO : '/A/D/G/rho:6'}),
5887 'G/tau' : Item("This is the file 'tau'.\n",
5888 props={SVN_PROP_MERGEINFO : '/A/D/G/tau:6'}),
5889 'gamma' : Item("This is the file 'gamma'.\n",
5890 props={SVN_PROP_MERGEINFO : '/A/D/gamma:6'}),
5892 expected_skip_D = wc.State(short_D_COPY_path, { })
5893 os.chdir(svntest.main.work_dir)
5894 svntest.actions.run_and_verify_merge(short_D_COPY_path, '5', '6',
5895 sbox.repo_url + '/A/D',
5896 expected_output, expected_disk_D,
5897 expected_status_D, expected_skip_D,
5898 None, None, None, None, None, 1)
5900 # Merge r5 from A/D into A_COPY/D. A_COPY/D's switched child A_COPY/D/G
5901 # gets r5 as does A_COPY/D/G's unswitched sibling A_COPY/D/gamma. Since
5902 # A_COPY/D has a switched child, it gets an uninheritable r5. Every other
5903 # path with existing mergeinfo already has r8 so are unchanged.
5905 # A_COPY/D/H/chi and A_COPY/D/H/omega both have mergeinfo for r1,6,8 but
5906 # won't elide to A_COPY/D/H becuase the latter's mergeinfo, while
5907 # encompassing the same ranges, r1,6*,8*, has some non-inheritable ranges.
5908 # The same is true of A_COPY/D/gamma and A_COPY/D.
5909 expected_output = wc.State(short_D_COPY_path, {
5910 'H/psi' : Item(status='U ')})
5911 expected_disk_D.tweak('', props={SVN_PROP_MERGEINFO : '/A/D:5-6*'})
5912 expected_disk_D.tweak('G', props={SVN_PROP_MERGEINFO : '/A/D/G:5-6*'})
5913 expected_disk_D.tweak('G/pi',
5914 props={SVN_PROP_MERGEINFO : '/A/D/G/pi:5-6'})
5915 expected_disk_D.tweak('G/rho',
5916 props={SVN_PROP_MERGEINFO : '/A/D/G/rho:5-6'})
5917 expected_disk_D.tweak('G/tau',
5918 props={SVN_PROP_MERGEINFO : '/A/D/G/tau:5-6'})
5919 expected_disk_D.tweak('H', props={SVN_PROP_MERGEINFO : '/A/D/H:5-6*,8*'})
5920 expected_disk_D.tweak('gamma',
5921 props={SVN_PROP_MERGEINFO : '/A/D/gamma:5-6'})
5922 expected_disk_D.tweak('H/chi',
5923 props={SVN_PROP_MERGEINFO :'/A/D/H/chi:5-6,8'})
5924 expected_disk_D.tweak('H/psi', contents="New content",
5925 props={SVN_PROP_MERGEINFO :'/A/D/H/psi:5-6,8'})
5926 expected_disk_D.tweak('H/omega',
5927 props={SVN_PROP_MERGEINFO :'/A/D/H/omega:5-6,8'})
5928 expected_status_D.tweak('H/psi', status='MM')
5929 svntest.actions.run_and_verify_merge(short_D_COPY_path, '4', '5',
5930 sbox.repo_url + '/A/D',
5931 expected_output, expected_disk_D,
5932 expected_status_D, expected_skip_D,
5933 None, None, None, None, None, 1)
5934 os.chdir(saved_cwd)
5936 # Finally, merge r4:8 into A_COPY. A_COPY gets r5-8 added and every path
5937 # under it with with explicit mergeinfo gets r5,7 added (they all have r6,8
5938 # already). Again there is no elision, since the only possibilities, e.g.
5939 # A_COPY/D/H's '/A/D/H:1,5-8*' to A_COPY/D's '/A/D:1,5-8*', involve
5940 # non-inheritable mergeinfo one ond side or the other.
5941 short_A_COPY_path = shorten_path_kludge(A_COPY_path)
5942 expected_output = wc.State(short_A_COPY_path, {
5943 'B/E/beta' : Item(status='U ')
5945 expected_status = wc.State(short_A_COPY_path, {
5946 '' : Item(status=' M', wc_rev=8),
5947 'B' : Item(status=' ', wc_rev=8),
5948 'mu' : Item(status=' ', wc_rev=8),
5949 'B/E' : Item(status=' ', wc_rev=8),
5950 'B/E/alpha' : Item(status=' ', wc_rev=8),
5951 'B/E/beta' : Item(status='M ', wc_rev=8),
5952 'B/lambda' : Item(status=' ', wc_rev=8),
5953 'B/F' : Item(status=' ', wc_rev=8),
5954 'C' : Item(status=' ', wc_rev=8),
5955 'D' : Item(status=' M', wc_rev=8),
5956 'D/G' : Item(status=' M', wc_rev=8, switched='S'),
5957 'D/G/pi' : Item(status=' M', wc_rev=8),
5958 'D/G/rho' : Item(status='MM', wc_rev=8, switched='S'),
5959 'D/G/tau' : Item(status=' M', wc_rev=8),
5960 'D/gamma' : Item(status=' M', wc_rev=8),
5961 'D/H' : Item(status=' M', wc_rev=8),
5962 'D/H/chi' : Item(status=' M', wc_rev=8),
5963 'D/H/psi' : Item(status='MM', wc_rev=8, switched='S'),
5964 'D/H/omega' : Item(status='MM', wc_rev=8),
5966 expected_disk = wc.State('', {
5967 '' : Item(props={SVN_PROP_MERGEINFO : '/A:5-8'}),
5968 'B' : Item(),
5969 'mu' : Item("This is the file 'mu'.\n"),
5970 'B/E' : Item(),
5971 'B/E/alpha' : Item("This is the file 'alpha'.\n"),
5972 'B/E/beta' : Item("New content"),
5973 'B/lambda' : Item("This is the file 'lambda'.\n"),
5974 'B/F' : Item(),
5975 'C' : Item(),
5976 'D' : Item(props={SVN_PROP_MERGEINFO : '/A/D:5-8*'}),
5977 'D/G' : Item(props={SVN_PROP_MERGEINFO : '/A/D/G:5-8*'}),
5978 'D/G/pi' : Item("This is the file 'pi'.\n",
5979 props={SVN_PROP_MERGEINFO : '/A/D/G/pi:5-8'}),
5980 'D/G/rho' : Item("New content",
5981 props={SVN_PROP_MERGEINFO : '/A/D/G/rho:5-8'}),
5982 'D/G/tau' : Item("This is the file 'tau'.\n",
5983 props={SVN_PROP_MERGEINFO : '/A/D/G/tau:5-8'}),
5984 'D/gamma' : Item("This is the file 'gamma'.\n",
5985 props={SVN_PROP_MERGEINFO : '/A/D/gamma:5-8'}),
5986 'D/H' : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:5-8*'}),
5987 'D/H/chi' : Item("This is the file 'chi'.\n",
5988 props={SVN_PROP_MERGEINFO : '/A/D/H/chi:5-8'}),
5989 'D/H/psi' : Item("New content",
5990 props={SVN_PROP_MERGEINFO : '/A/D/H/psi:5-8'}),
5991 'D/H/omega' : Item("New content",
5992 props={SVN_PROP_MERGEINFO : '/A/D/H/omega:5-8'}),
5994 expected_skip = wc.State(short_A_COPY_path, { })
5995 os.chdir(svntest.main.work_dir)
5996 svntest.actions.run_and_verify_merge(short_A_COPY_path, '4', '8',
5997 sbox.repo_url + '/A',
5998 expected_output, expected_disk,
5999 expected_status, expected_skip,
6000 None, None, None, None, None, 1)
6002 os.chdir(saved_cwd)
6004 # Commit changes thus far.
6005 expected_output = svntest.wc.State(wc_dir, {
6006 'A_COPY' : Item(verb='Sending'),
6007 'A_COPY/B/E/beta' : Item(verb='Sending'),
6008 'A_COPY/D' : Item(verb='Sending'),
6009 'A_COPY/D/gamma' : Item(verb='Sending'),
6010 'A_COPY/D/G' : Item(verb='Sending'),
6011 'A_COPY/D/G/pi' : Item(verb='Sending'),
6012 'A_COPY/D/G/rho' : Item(verb='Sending'),
6013 'A_COPY/D/G/tau' : Item(verb='Sending'),
6014 'A_COPY/D/H' : Item(verb='Sending'),
6015 'A_COPY/D/H/chi' : Item(verb='Sending'),
6016 'A_COPY/D/H/omega' : Item(verb='Sending'),
6017 'A_COPY/D/H/psi' : Item(verb='Sending'),
6019 wc_status.tweak('A_COPY', 'A_COPY/B/E/beta', 'A_COPY/D', 'A_COPY/D/gamma',
6020 'A_COPY/D/G', 'A_COPY/D/G/pi', 'A_COPY/D/G/rho',
6021 'A_COPY/D/G/tau','A_COPY/D/H', 'A_COPY/D/H/chi',
6022 'A_COPY/D/H/omega', 'A_COPY/D/H/psi',
6023 wc_rev=9)
6024 svntest.actions.run_and_verify_commit(wc_dir, expected_output, wc_status,
6025 None, wc_dir)
6027 # Unswitch A_COPY/D/H/psi.
6028 expected_output = svntest.wc.State(wc_dir, {
6029 'A_COPY/D/H/psi' : Item(status='UU')})
6030 wc_status.tweak("A_COPY/D/H/psi", switched=None, wc_rev=9)
6031 wc_disk.tweak("A_COPY",
6032 props={SVN_PROP_MERGEINFO : '/A:5-8'})
6033 wc_disk.tweak("A_COPY/B/E/beta",
6034 contents="New content")
6035 wc_disk.tweak("A_COPY/D",
6036 props={SVN_PROP_MERGEINFO : '/A/D:5-8*'})
6037 wc_disk.tweak("A_COPY/D/gamma",
6038 props={SVN_PROP_MERGEINFO : '/A/D/gamma:5-8'})
6039 wc_disk.tweak("A_COPY/D/G",
6040 props={SVN_PROP_MERGEINFO : '/A/D/G:5-8*'})
6041 wc_disk.tweak("A_COPY/D/G/pi",
6042 props={SVN_PROP_MERGEINFO : '/A/D/G/pi:5-8'})
6043 wc_disk.tweak("A_COPY/D/G/rho",
6044 contents="New content",
6045 props={SVN_PROP_MERGEINFO : '/A/D/G/rho:5-8'})
6046 wc_disk.tweak("A_COPY/D/G/tau",
6047 props={SVN_PROP_MERGEINFO : '/A/D/G/tau:5-8'})
6048 wc_disk.tweak("A_COPY/D/H",
6049 props={SVN_PROP_MERGEINFO : '/A/D/H:5-8*'})
6050 wc_disk.tweak("A_COPY/D/H/chi",
6051 props={SVN_PROP_MERGEINFO : '/A/D/H/chi:5-8'})
6052 wc_disk.tweak("A_COPY/D/H/omega",
6053 contents="New content",
6054 props={SVN_PROP_MERGEINFO : '/A/D/H/omega:5-8'})
6055 wc_disk.tweak("A_COPY_2", props={})
6056 svntest.actions.run_and_verify_switch(sbox.wc_dir, A_COPY_psi_path,
6057 sbox.repo_url + "/A_COPY/D/H/psi",
6058 expected_output, wc_disk, wc_status,
6059 None, None, None, None, None, 1)
6061 # Non-inheritable mergeinfo ranges on a target don't prevent repeat
6062 # merges of that range on the target's children.
6064 # Non-inheritable mergeinfo ranges on a target are removed if the target
6065 # no longer has any switched children and a repeat merge is performed.
6067 # Merge r4:8 from A/D/H into A_COPY/D/H. A_COPY/D/H already has mergeinfo
6068 # for r4:8 but it is marked as uninheritable so the repeat merge is
6069 # allowed on its children, notably the now unswitched A_COPY/D/H/psi.
6070 # Since A_COPY/D/H no longer has any switched children and the merge of
6071 # r4:8 has been repeated the previously uninheritable ranges 5-8* on
6072 # A_COPY/D/H are made inheritable. This also means that the mergeinfo on
6073 # A_COPY/D/H's children will elide to it.
6074 short_H_COPY_path = shorten_path_kludge(A_COPY_H_path)
6075 expected_output = wc.State(short_H_COPY_path, {
6076 'psi' : Item(status='U ')
6078 expected_status = wc.State(short_H_COPY_path, {
6079 '' : Item(status=' M', wc_rev=9),
6080 'psi' : Item(status='M ', wc_rev=9),
6081 'omega' : Item(status=' M', wc_rev=9),
6082 'chi' : Item(status=' M', wc_rev=9),
6084 expected_disk = wc.State('', {
6085 '' : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:5-8'}),
6086 'psi' : Item("New content"),
6087 'omega' : Item("New content"),
6088 'chi' : Item("This is the file 'chi'.\n"),
6090 expected_skip = wc.State(short_H_COPY_path, { })
6091 os.chdir(svntest.main.work_dir)
6092 svntest.actions.run_and_verify_merge(short_H_COPY_path, '4', '8',
6093 sbox.repo_url + '/A/D/H',
6094 expected_output, expected_disk,
6095 expected_status, expected_skip,
6096 None, None, None, None, None, 1)
6097 os.chdir(saved_cwd)
6099 # Non-inheritable mergeinfo ranges on a target do prevent repeat
6100 # merges on the target itself.
6102 # Add a prop A/D and commit it as r10. Merge r10 into A_COPY/D. Since
6103 # A_COPY/D has a switched child it gets r10 added as a non-inheritable
6104 # range. Repeat the same merge checking that no repeat merge is
6105 # attempted on A_COPY/D.
6106 svntest.actions.run_and_verify_svn(None,
6107 ["property 'prop:name' set on '" +
6108 D_path + "'\n"], [], 'ps',
6109 'prop:name', 'propval', D_path)
6110 expected_output = svntest.wc.State(wc_dir, {
6111 'A/D' : Item(verb='Sending'),
6112 'A_COPY/D/H' : Item(verb='Sending'),
6113 'A_COPY/D/H/chi' : Item(verb='Sending'),
6114 'A_COPY/D/H/psi' : Item(verb='Sending'),
6115 'A_COPY/D/H/omega' : Item(verb='Sending'),
6117 wc_status.tweak('A_COPY/D', wc_rev=9)
6118 wc_status.tweak('A/D', 'A_COPY/D/H', 'A_COPY/D/H/chi', 'A_COPY/D/H/psi',
6119 'A_COPY/D/H/omega', wc_rev=10)
6120 svntest.actions.run_and_verify_commit(wc_dir, expected_output, wc_status,
6121 None, wc_dir)
6122 expected_output = wc.State(short_D_COPY_path, {
6123 '' : Item(status=' U')
6125 # Reuse expected status and disk from last merge to A_COPY/D
6126 expected_status_D.tweak('', 'gamma', status=' M', wc_rev=9)
6127 expected_status_D.tweak('H', status=' M', wc_rev=10)
6128 expected_status_D.tweak('H/psi', 'H/chi', 'H/omega', status=' ', wc_rev=10,
6129 switched=None)
6130 expected_status_D.tweak('G', switched='S', status=' M', wc_rev=9)
6131 expected_status_D.tweak('G/tau', 'G/pi', 'G/rho', status=' M', wc_rev=9)
6132 expected_disk_D.tweak('', props={SVN_PROP_MERGEINFO : '/A/D:5-8*,10*',
6133 "prop:name" : "propval"})
6134 expected_disk_D.tweak('G',
6135 props={SVN_PROP_MERGEINFO : '/A/D/G:5-8*,10*'})
6136 expected_disk_D.tweak('G/pi',
6137 props={SVN_PROP_MERGEINFO : '/A/D/G/pi:5-8,10'})
6138 expected_disk_D.tweak('G/rho',
6139 props={SVN_PROP_MERGEINFO : '/A/D/G/rho:5-8,10'})
6140 expected_disk_D.tweak('G/tau',
6141 props={SVN_PROP_MERGEINFO : '/A/D/G/tau:5-8,10'})
6142 expected_disk_D.tweak('H', props={SVN_PROP_MERGEINFO : '/A/D/H:5-8,10'})
6143 expected_disk_D.tweak('gamma',
6144 props={SVN_PROP_MERGEINFO : '/A/D/gamma:5-8,10'})
6145 expected_disk_D.tweak('H/chi', 'H/omega', props={})
6146 expected_disk_D.tweak('H/psi', contents="New content", props={})
6147 os.chdir(svntest.main.work_dir)
6148 svntest.actions.run_and_verify_merge(short_D_COPY_path, '9', '10',
6149 sbox.repo_url + '/A/D',
6150 expected_output, expected_disk_D,
6151 expected_status_D, expected_skip_D,
6152 None, None, None, None, None, 1)
6153 # Repeated merge is a no-op.
6154 expected_output = wc.State(short_D_COPY_path, {})
6155 svntest.actions.run_and_verify_merge(short_D_COPY_path, '9', '10',
6156 sbox.repo_url + '/A/D',
6157 expected_output, expected_disk_D,
6158 expected_status_D, expected_skip_D,
6159 None, None, None, None, None, 1)
6160 os.chdir(saved_cwd)
6163 # Test for issue 2047: Merge from parent dir fails while it succeeds from
6164 # the direct dir
6165 def merge_with_implicit_target_file(sbox):
6166 "merge a change to a file, using relative path"
6168 sbox.build()
6169 wc_dir = sbox.wc_dir
6171 # Make a change to A/mu, then revert it using 'svn merge -r 2:1 A/mu'
6173 # change A/mu and commit
6174 A_path = os.path.join(wc_dir, 'A')
6175 mu_path = os.path.join(A_path, 'mu')
6177 svntest.main.file_append(mu_path, "A whole new line.\n")
6179 expected_output = svntest.wc.State(wc_dir, {
6180 'A/mu' : Item(verb='Sending'),
6182 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
6183 expected_status.tweak('A/mu', wc_rev=2)
6184 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
6185 expected_status, None, wc_dir)
6187 # Update to revision 2.
6188 svntest.actions.run_and_verify_svn(None, None, [], 'update', wc_dir)
6190 # Revert the change committed in r2
6191 os.chdir(wc_dir)
6193 # run_and_verify_merge doesn't accept file paths.
6194 svntest.actions.run_and_verify_svn(None, None, [], 'merge', '-r', '2:1',
6195 'A/mu')
6197 # Test practical application of issue #2769 fix, empty rev range elision,
6198 # and elision to the repos.
6199 def empty_mergeinfo(sbox):
6200 "mergeinfo can explicitly be empty"
6202 # A bit o' history: The fix for issue #2769 originally permitted mergeinfo
6203 # with empty range lists and as a result we permitted partial elision and
6204 # had a whole slew of tests here for that. But the fix of issue #3029 now
6205 # prevents svn ps or svn merge from creating mergeinfo with paths mapped to
6206 # empty ranges, only empty mergeinfo is allowed. As a result this test now
6207 # covers the following areas:
6209 # A) Merging a set of revisions into a path, then reverse merging the
6210 # same set out of a subtree of path results in empty mergeinfo
6211 # (i.e. "") on the subtree.
6213 # B) Empty mergeinfo elides to empty mergeinfo.
6215 # C) If a merge sets empty mergeinfo on its target and that target has
6216 # no ancestor in either the WC or the repository with explict
6217 # mergeinfo, then the target's mergeinfo is removed (a.k.a. elides
6218 # to nothing).
6219 sbox.build()
6220 wc_dir = sbox.wc_dir
6221 wc_disk, wc_status = set_up_branch(sbox)
6223 # Some paths we'll care about
6224 A_COPY_path = os.path.join(wc_dir, "A_COPY")
6225 H_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "H")
6226 psi_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "H", "psi")
6227 rho_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "G", "rho")
6229 # Test area A -- Merge r2:4 into A_COPY then reverse merge 4:2 to
6230 # A_COPY/D/G. A_COPY/D/G should end up with empty mergeinfo to
6231 # override that of A_COPY.
6233 # Search for the comment entitled "The Merge Kluge" elsewhere in
6234 # this file, to understand why we shorten and chdir() below.
6235 short_A_COPY_path = shorten_path_kludge(A_COPY_path)
6236 expected_output = wc.State(short_A_COPY_path, {
6237 'D/H/psi' : Item(status='U '),
6238 'D/G/rho' : Item(status='U '),
6240 expected_status = wc.State(short_A_COPY_path, {
6241 '' : Item(status=' M', wc_rev=2),
6242 'B' : Item(status=' ', wc_rev=2),
6243 'mu' : Item(status=' ', wc_rev=2),
6244 'B/E' : Item(status=' ', wc_rev=2),
6245 'B/E/alpha' : Item(status=' ', wc_rev=2),
6246 'B/E/beta' : Item(status=' ', wc_rev=2),
6247 'B/lambda' : Item(status=' ', wc_rev=2),
6248 'B/F' : Item(status=' ', wc_rev=2),
6249 'C' : Item(status=' ', wc_rev=2),
6250 'D' : Item(status=' ', wc_rev=2),
6251 'D/G' : Item(status=' ', wc_rev=2),
6252 'D/G/pi' : Item(status=' ', wc_rev=2),
6253 'D/G/rho' : Item(status='M ', wc_rev=2),
6254 'D/G/tau' : Item(status=' ', wc_rev=2),
6255 'D/gamma' : Item(status=' ', wc_rev=2),
6256 'D/H' : Item(status=' ', wc_rev=2),
6257 'D/H/chi' : Item(status=' ', wc_rev=2),
6258 'D/H/psi' : Item(status='M ', wc_rev=2),
6259 'D/H/omega' : Item(status=' ', wc_rev=2),
6261 expected_disk = wc.State('', {
6262 '' : Item(props={SVN_PROP_MERGEINFO : '/A:3-4'}),
6263 'B' : Item(),
6264 'mu' : Item("This is the file 'mu'.\n"),
6265 'B/E' : Item(),
6266 'B/E/alpha' : Item("This is the file 'alpha'.\n"),
6267 'B/E/beta' : Item("This is the file 'beta'.\n"),
6268 'B/lambda' : Item("This is the file 'lambda'.\n"),
6269 'B/F' : Item(),
6270 'C' : Item(),
6271 'D' : Item(),
6272 'D/G' : Item(),
6273 'D/G/pi' : Item("This is the file 'pi'.\n"),
6274 'D/G/rho' : Item("New content"),
6275 'D/G/tau' : Item("This is the file 'tau'.\n"),
6276 'D/gamma' : Item("This is the file 'gamma'.\n"),
6277 'D/H' : Item(),
6278 'D/H/chi' : Item("This is the file 'chi'.\n"),
6279 'D/H/psi' : Item("New content"),
6280 'D/H/omega' : Item("This is the file 'omega'.\n"),
6282 expected_skip = wc.State(short_A_COPY_path, { })
6283 saved_cwd = os.getcwd()
6284 os.chdir(svntest.main.work_dir)
6285 svntest.actions.run_and_verify_merge(short_A_COPY_path, '2', '4',
6286 sbox.repo_url + \
6287 '/A',
6288 expected_output,
6289 expected_disk,
6290 expected_status,
6291 expected_skip,
6292 None, None, None, None,
6293 None, 1)
6294 # Now do the reverse merge into the subtree.
6295 short_H_COPY_path = shorten_path_kludge(H_COPY_path)
6296 expected_output = wc.State(short_H_COPY_path, {
6297 'psi' : Item(status='G '),
6299 expected_status = wc.State(short_H_COPY_path, {
6300 '' : Item(status=' M', wc_rev=2),
6301 'chi' : Item(status=' ', wc_rev=2),
6302 'psi' : Item(status=' ', wc_rev=2),
6303 'omega' : Item(status=' ', wc_rev=2),
6305 expected_disk = wc.State('', {
6306 '' : Item(props={SVN_PROP_MERGEINFO : ''}),
6307 'chi' : Item("This is the file 'chi'.\n"),
6308 'psi' : Item("This is the file 'psi'.\n"),
6309 'omega' : Item("This is the file 'omega'.\n"),
6311 expected_skip = wc.State(short_H_COPY_path, { })
6312 svntest.actions.run_and_verify_merge(short_H_COPY_path, '4', '2',
6313 sbox.repo_url + \
6314 '/A/D/H',
6315 expected_output,
6316 expected_disk,
6317 expected_status,
6318 expected_skip,
6319 None, None, None, None,
6320 None, 1)
6322 # Test areas B and C -- Reverse merge r3 into A_COPY, this would result in
6323 # empty mergeinfo on A_COPY and A_COPY/D/H, but the empty mergeinfo on the
6324 # latter elides to the former. And then the empty mergeinfo on A_COPY,
6325 # which has no parent with explicit mergeinfo to override (in either the WC
6326 # or the repos) itself elides. This leaves the WC in the same unmodified
6327 # state as after the call to set_up_branch().
6328 short_rho_COPY_path = shorten_path_kludge(rho_COPY_path)
6329 expected_output = expected_merge_output(
6330 [[4,3]], 'G ' + short_rho_COPY_path + '\n')
6331 svntest.actions.run_and_verify_svn(None, expected_output,
6332 [], 'merge', '-r4:2',
6333 sbox.repo_url + '/A',
6334 short_A_COPY_path)
6335 os.chdir(saved_cwd)
6336 svntest.actions.run_and_verify_status(wc_dir, wc_status)
6337 # Check that A_COPY's mergeinfo is gone.
6338 svntest.actions.run_and_verify_svn(None, [], [], 'pg', 'svn:mergeinfo',
6339 A_COPY_path)
6341 def prop_add_to_child_with_mergeinfo(sbox):
6342 "merge adding prop to child of merge target works"
6344 # Test for Issue #2781 Prop add to child of merge target corrupts WC if
6345 # child has mergeinfo.
6347 sbox.build()
6348 wc_dir = sbox.wc_dir
6349 expected_disk, expected_status = set_up_branch(sbox)
6351 # Some paths we'll care about
6352 beta_path = os.path.join(wc_dir, "A", "B", "E", "beta")
6353 beta_COPY_path = os.path.join(wc_dir, "A_COPY", "B", "E", "beta")
6354 B_COPY_path = os.path.join(wc_dir, "A_COPY", "B")
6356 # Set a non-mergeinfo prop on a file.
6357 svntest.actions.run_and_verify_svn(None,
6358 ["property 'prop:name' set on '" +
6359 beta_path + "'\n"], [], 'ps',
6360 'prop:name', 'propval', beta_path)
6361 expected_disk.tweak('A/B/E/beta', props={'prop:name' : 'propval'})
6362 expected_status.tweak('A/B/E/beta', wc_rev=7)
6363 expected_output = wc.State(wc_dir,
6364 {'A/B/E/beta' : Item(verb='Sending')})
6365 svntest.actions.run_and_verify_commit(wc_dir,
6366 expected_output,
6367 expected_status,
6368 None,
6369 wc_dir)
6371 # Merge r4:5 from A/B/E/beta into A_COPY/B/E/beta.
6372 short_beta_COPY_path = shorten_path_kludge(beta_COPY_path)
6373 saved_cwd = os.getcwd()
6374 os.chdir(svntest.main.work_dir)
6375 svntest.actions.run_and_verify_svn(None,
6376 expected_merge_output([[5]],
6377 'U ' + short_beta_COPY_path +'\n'),
6378 [], 'merge', '-c5',
6379 sbox.repo_url + '/A/B/E/beta',
6380 short_beta_COPY_path)
6381 os.chdir(saved_cwd)
6383 # Merge r6:7 into A_COPY/B. In issue #2781 this adds a bogus
6384 # and incomplete entry in A_COPY/B/.svn/entries for 'beta'.
6385 short_B_COPY_path = shorten_path_kludge(B_COPY_path)
6386 expected_output = wc.State(short_B_COPY_path, {
6387 'E/beta' : Item(status=' U'),
6389 expected_status = wc.State(short_B_COPY_path, {
6390 '' : Item(status=' M', wc_rev=2),
6391 'E' : Item(status=' ', wc_rev=2),
6392 'E/alpha' : Item(status=' ', wc_rev=2),
6393 'E/beta' : Item(status='MM', wc_rev=2),
6394 'lambda' : Item(status=' ', wc_rev=2),
6395 'F' : Item(status=' ', wc_rev=2),
6397 expected_disk = wc.State('', {
6398 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B:7'}),
6399 'E' : Item(),
6400 'E/alpha' : Item("This is the file 'alpha'.\n"),
6401 'E/beta' : Item(contents="New content",
6402 props={SVN_PROP_MERGEINFO : '/A/B/E/beta:5,7',
6403 'prop:name' : 'propval'}),
6404 'F' : Item(),
6405 'lambda' : Item("This is the file 'lambda'.\n")
6407 expected_skip = wc.State(short_B_COPY_path, { })
6408 os.chdir(svntest.main.work_dir)
6409 svntest.actions.run_and_verify_merge(short_B_COPY_path, '6', '7',
6410 sbox.repo_url + \
6411 '/A/B',
6412 expected_output,
6413 expected_disk,
6414 expected_status,
6415 expected_skip,
6416 None, None, None, None,
6417 None, 1)
6419 def foreign_repos_does_not_update_mergeinfo(sbox):
6420 "set no mergeinfo when merging from foreign repos"
6422 # Test for issue #2788.
6424 sbox.build()
6425 wc_dir = sbox.wc_dir
6426 expected_disk, expected_status = set_up_branch(sbox)
6428 # Create a second repository with the same greek tree
6429 repo_dir = sbox.repo_dir
6430 other_repo_dir, other_repo_url = sbox.add_repo_path("other")
6431 svntest.main.copy_repos(repo_dir, other_repo_dir, 6, 1)
6433 # Merge r3:4 (using implied peg revisions) from 'other' repos into
6434 # A_COPY/D/G. Merge should succeed, but no mergeinfo should be set.
6436 # Search for the comment entitled "The Merge Kluge" elsewhere in
6437 # this file, to understand why we shorten and chdir() below.
6438 short_G_COPY_path = shorten_path_kludge(os.path.join(wc_dir,
6439 "A_COPY", "D", "G"))
6440 saved_cwd = os.getcwd()
6442 os.chdir(svntest.main.work_dir)
6443 svntest.actions.run_and_verify_svn(None,
6444 expected_merge_output([[4]],
6445 'U ' +
6446 os.path.join(short_G_COPY_path,
6447 "rho") + '\n', True),
6448 [], 'merge', '-c4',
6449 other_repo_url + '/A/D/G',
6450 short_G_COPY_path)
6451 os.chdir(saved_cwd)
6453 # Merge r4:5 (using explicit peg revisions) from 'other' repos into
6454 # A_COPY/B/E. Merge should succeed, but no mergeinfo should be set.
6455 short_E_COPY_path = shorten_path_kludge(os.path.join(wc_dir,
6456 "A_COPY", "B", "E"))
6458 os.chdir(svntest.main.work_dir)
6459 svntest.actions.run_and_verify_svn(None,
6460 expected_merge_output([[5]],
6461 'U ' +
6462 os.path.join(short_E_COPY_path,
6463 "beta") +'\n', True),
6464 [], 'merge',
6465 other_repo_url + '/A/B/E@4',
6466 other_repo_url + '/A/B/E@5',
6467 short_E_COPY_path)
6468 os.chdir(saved_cwd)
6470 expected_status.tweak('A_COPY/D/G/rho', 'A_COPY/B/E/beta', status='M ')
6471 svntest.actions.run_and_verify_status(wc_dir, expected_status)
6473 def avoid_reflected_revs(sbox):
6474 "avoid repeated merges for cyclic merging"
6476 ## See http://subversion.tigris.org/issues/show_bug.cgi?id=2897. ##
6478 # Create a WC with a single branch
6479 sbox.build()
6480 wc_dir = sbox.wc_dir
6481 wc_disk, wc_status = set_up_branch(sbox, True, 1)
6483 # Some paths we'll care about
6484 A_path = os.path.join(wc_dir, 'A')
6485 A_COPY_path = os.path.join(wc_dir, 'A_COPY')
6486 tfile1_path = os.path.join(wc_dir, 'A', 'tfile1')
6487 tfile2_path = os.path.join(wc_dir, 'A', 'tfile2')
6488 bfile1_path = os.path.join(A_COPY_path, 'bfile1')
6489 bfile2_path = os.path.join(A_COPY_path, 'bfile2')
6491 # Contents to be added to files
6492 tfile1_content = "This is tfile1\n"
6493 tfile2_content = "This is tfile2\n"
6494 bfile1_content = "This is bfile1\n"
6495 bfile2_content = "This is bfile2\n"
6497 # We'll consider A as the trunk and A_COPY as the feature branch
6498 # Create a tfile1 in A
6499 svntest.main.file_write(tfile1_path, tfile1_content)
6500 svntest.actions.run_and_verify_svn(None, None, [], 'add', tfile1_path)
6501 expected_output = wc.State(wc_dir, {'A/tfile1' : Item(verb='Adding')})
6502 wc_status.add({'A/tfile1' : Item(status=' ', wc_rev=3)})
6503 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
6504 wc_status, None, wc_dir)
6506 # Create a bfile1 in A_COPY
6507 svntest.main.file_write(bfile1_path, bfile1_content)
6508 svntest.actions.run_and_verify_svn(None, None, [], 'add', bfile1_path)
6509 expected_output = wc.State(wc_dir, {'A_COPY/bfile1' : Item(verb='Adding')})
6510 wc_status.add({'A_COPY/bfile1' : Item(status=' ', wc_rev=4)})
6511 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
6512 wc_status, None, wc_dir)
6514 # Create one more file in A
6515 svntest.main.file_write(tfile2_path, tfile2_content)
6516 svntest.actions.run_and_verify_svn(None, None, [], 'add', tfile2_path)
6517 expected_output = wc.State(wc_dir, {'A/tfile2' : Item(verb='Adding')})
6518 wc_status.add({'A/tfile2' : Item(status=' ', wc_rev=5)})
6519 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
6520 wc_status, None, wc_dir)
6522 short_A_COPY = shorten_path_kludge(A_COPY_path)
6523 saved_cwd = os.getcwd()
6524 os.chdir(svntest.main.work_dir)
6526 # Merge r5 from /A to /A_COPY, creating r6
6527 expected_output = wc.State(short_A_COPY, {
6528 'tfile2' : Item(status='A '),
6530 expected_status = wc.State(short_A_COPY, {
6531 '' : Item(status=' M', wc_rev=2),
6532 'tfile2' : Item(status='A ', wc_rev='-', copied='+'),
6533 'bfile1' : Item(status=' ', wc_rev=4),
6534 'mu' : Item(status=' ', wc_rev=2),
6535 'C' : Item(status=' ', wc_rev=2),
6536 'D' : Item(status=' ', wc_rev=2),
6537 'B' : Item(status=' ', wc_rev=2),
6538 'B/lambda' : Item(status=' ', wc_rev=2),
6539 'B/E' : Item(status=' ', wc_rev=2),
6540 'B/E/alpha': Item(status=' ', wc_rev=2),
6541 'B/E/beta' : Item(status=' ', wc_rev=2),
6542 'B/F' : Item(status=' ', wc_rev=2),
6543 'D/gamma' : Item(status=' ', wc_rev=2),
6544 'D/G' : Item(status=' ', wc_rev=2),
6545 'D/G/pi' : Item(status=' ', wc_rev=2),
6546 'D/G/rho' : Item(status=' ', wc_rev=2),
6547 'D/G/tau' : Item(status=' ', wc_rev=2),
6548 'D/H' : Item(status=' ', wc_rev=2),
6549 'D/H/chi' : Item(status=' ', wc_rev=2),
6550 'D/H/omega': Item(status=' ', wc_rev=2),
6551 'D/H/psi' : Item(status=' ', wc_rev=2),
6553 expected_disk = wc.State('', {
6554 '' : Item(props={SVN_PROP_MERGEINFO : '/A:5'}),
6555 'tfile2' : Item(tfile2_content),
6556 'bfile1' : Item(bfile1_content),
6557 'mu' : Item("This is the file 'mu'.\n"),
6558 'C' : Item(),
6559 'D' : Item(),
6560 'B' : Item(),
6561 'B/lambda' : Item("This is the file 'lambda'.\n"),
6562 'B/E' : Item(),
6563 'B/E/alpha': Item("This is the file 'alpha'.\n"),
6564 'B/E/beta' : Item("This is the file 'beta'.\n"),
6565 'B/F' : Item(),
6566 'D/gamma' : Item("This is the file 'gamma'.\n"),
6567 'D/G' : Item(),
6568 'D/G/pi' : Item("This is the file 'pi'.\n"),
6569 'D/G/rho' : Item("This is the file 'rho'.\n"),
6570 'D/G/tau' : Item("This is the file 'tau'.\n"),
6571 'D/H' : Item(),
6572 'D/H/chi' : Item("This is the file 'chi'.\n"),
6573 'D/H/omega': Item("This is the file 'omega'.\n"),
6574 'D/H/psi' : Item("This is the file 'psi'.\n"),
6576 expected_skip = wc.State(short_A_COPY, {})
6578 svntest.actions.run_and_verify_merge(short_A_COPY, '4', '5',
6579 sbox.repo_url + '/A',
6580 expected_output,
6581 expected_disk,
6582 expected_status,
6583 expected_skip,
6584 None, None, None, None, None, 1)
6585 os.chdir(saved_cwd)
6587 # Sync up with the trunk ie., A
6588 svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
6589 expected_output = wc.State(wc_dir, {
6590 'A_COPY' : Item(verb='Sending'),
6591 'A_COPY/tfile2' : Item(verb='Adding'),
6593 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
6594 None, None, wc_dir)
6595 os.chdir(svntest.main.work_dir)
6597 # Merge r3 from /A to /A_COPY, creating r7
6598 expected_output = wc.State(short_A_COPY, {
6599 'tfile1' : Item(status='A '),
6601 expected_status.tweak(wc_rev=5)
6602 expected_status.tweak('', wc_rev=6)
6603 expected_status.tweak('tfile2', status=' ', copied=None, wc_rev=6)
6604 expected_status.add({
6605 'tfile1' : Item(status='A ', wc_rev='-', copied='+'),
6607 expected_disk.tweak('', props={SVN_PROP_MERGEINFO : '/A:3,5'})
6608 expected_disk.add({
6609 'tfile1' : Item(tfile1_content),
6612 svntest.actions.run_and_verify_merge(short_A_COPY, '2', '3',
6613 sbox.repo_url + '/A',
6614 expected_output,
6615 expected_disk,
6616 expected_status,
6617 expected_skip,
6618 None, None, None, None, None, 1)
6620 os.chdir(saved_cwd)
6622 svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
6623 expected_output = wc.State(wc_dir, {
6624 'A_COPY' : Item(verb='Sending'),
6625 'A_COPY/tfile1' : Item(verb='Adding'),
6627 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
6628 None, None, wc_dir)
6630 # Add bfile2 to A_COPY
6631 svntest.main.file_write(bfile2_path, bfile2_content)
6632 svntest.actions.run_and_verify_svn(None, None, [], 'add', bfile2_path)
6633 expected_output = wc.State(wc_dir, {'A_COPY/bfile2' : Item(verb='Adding')})
6634 wc_status.tweak(wc_rev=6)
6635 wc_status.add({
6636 'A_COPY/bfile2' : Item(status=' ', wc_rev=8),
6637 'A_COPY' : Item(status=' ', wc_rev=7),
6638 'A_COPY/tfile2' : Item(status=' ', wc_rev=6),
6639 'A_COPY/tfile1' : Item(status=' ', wc_rev=7),
6641 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
6642 wc_status, None, wc_dir)
6644 # Merge 2:8 from A_COPY(feature branch) to A(trunk).
6645 short_A_path = shorten_path_kludge(A_path)
6646 expected_output = wc.State(short_A_path, {
6647 'bfile2' : Item(status='A '),
6648 'bfile1' : Item(status='A '),
6650 expected_status = wc.State(short_A_path, {
6651 '' : Item(status=' M', wc_rev=6),
6652 'bfile2' : Item(status='A ', wc_rev='-', copied='+'),
6653 'bfile1' : Item(status='A ', wc_rev='-', copied='+'),
6654 'tfile2' : Item(status=' ', wc_rev=6),
6655 'tfile1' : Item(status=' ', wc_rev=6),
6656 'mu' : Item(status=' ', wc_rev=6),
6657 'C' : Item(status=' ', wc_rev=6),
6658 'D' : Item(status=' ', wc_rev=6),
6659 'B' : Item(status=' ', wc_rev=6),
6660 'B/lambda' : Item(status=' ', wc_rev=6),
6661 'B/E' : Item(status=' ', wc_rev=6),
6662 'B/E/alpha' : Item(status=' ', wc_rev=6),
6663 'B/E/beta' : Item(status=' ', wc_rev=6),
6664 'B/F' : Item(status=' ', wc_rev=6),
6665 'D/gamma' : Item(status=' ', wc_rev=6),
6666 'D/G' : Item(status=' ', wc_rev=6),
6667 'D/G/pi' : Item(status=' ', wc_rev=6),
6668 'D/G/rho' : Item(status=' ', wc_rev=6),
6669 'D/G/tau' : Item(status=' ', wc_rev=6),
6670 'D/H' : Item(status=' ', wc_rev=6),
6671 'D/H/chi' : Item(status=' ', wc_rev=6),
6672 'D/H/omega' : Item(status=' ', wc_rev=6),
6673 'D/H/psi' : Item(status=' ', wc_rev=6),
6675 expected_disk = wc.State('', {
6676 '' : Item(props={SVN_PROP_MERGEINFO : '/A_COPY:3-8'}),
6677 'bfile2' : Item(bfile2_content),
6678 'bfile1' : Item(bfile1_content),
6679 'tfile2' : Item(tfile2_content),
6680 'tfile1' : Item(tfile1_content),
6681 'mu' : Item("This is the file 'mu'.\n"),
6682 'C' : Item(),
6683 'D' : Item(),
6684 'B' : Item(),
6685 'B/lambda' : Item("This is the file 'lambda'.\n"),
6686 'B/E' : Item(),
6687 'B/E/alpha' : Item("This is the file 'alpha'.\n"),
6688 'B/E/beta' : Item("This is the file 'beta'.\n"),
6689 'B/F' : Item(),
6690 'D/gamma' : Item("This is the file 'gamma'.\n"),
6691 'D/G' : Item(),
6692 'D/G/pi' : Item("This is the file 'pi'.\n"),
6693 'D/G/rho' : Item("This is the file 'rho'.\n"),
6694 'D/G/tau' : Item("This is the file 'tau'.\n"),
6695 'D/H' : Item(),
6696 'D/H/chi' : Item("This is the file 'chi'.\n"),
6697 'D/H/omega' : Item("This is the file 'omega'.\n"),
6698 'D/H/psi' : Item("This is the file 'psi'.\n"),
6701 expected_skip = wc.State(short_A_path, {})
6702 saved_cwd = os.getcwd()
6703 os.chdir(svntest.main.work_dir)
6705 svntest.actions.run_and_verify_merge(short_A_path, '2', '8',
6706 sbox.repo_url + '/A_COPY',
6707 expected_output,
6708 expected_disk,
6709 expected_status,
6710 expected_skip,
6711 None, None, None, None, None, 1)
6712 os.chdir(saved_cwd)
6715 def update_loses_mergeinfo(sbox):
6716 "update does not merge mergeinfo"
6719 When a working copy receives a fresh svn:mergeinfo property due to update,
6720 and working copy has some local mergeinfo(yet to be added), local mergeinfo
6721 is left unchanged.
6724 sbox.build()
6725 wc_dir = sbox.wc_dir
6726 A_C_wc_dir = wc_dir + '/A/C'
6727 A_B_url = sbox.repo_url + '/A/B'
6728 A_B_J_url = sbox.repo_url + '/A/B/J'
6729 A_B_K_url = sbox.repo_url + '/A/B/K'
6730 svntest.actions.run_and_verify_svn(None, ['\n', 'Committed revision 2.\n'],
6732 'mkdir', '-m', 'rev 2', A_B_J_url)
6733 svntest.actions.run_and_verify_svn(None, ['\n', 'Committed revision 3.\n'],
6735 'mkdir', '-m', 'rev 3', A_B_K_url)
6737 other_wc = sbox.add_wc_path('other')
6738 svntest.actions.duplicate_dir(wc_dir, other_wc)
6740 short_A_C_wc_dir = shorten_path_kludge(A_C_wc_dir)
6741 expected_output = wc.State(short_A_C_wc_dir, {'J' : Item(status='A ')})
6742 expected_disk = wc.State('', {
6743 'J' : Item(),
6744 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B:2'}),
6746 expected_status = wc.State(short_A_C_wc_dir,
6747 { '' : Item(wc_rev=1, status=' M'),
6748 'J' : Item(status='A ',
6749 wc_rev='-', copied='+')
6752 expected_skip = wc.State('', { })
6753 saved_cwd = os.getcwd()
6754 os.chdir(svntest.main.work_dir)
6755 svntest.actions.run_and_verify_merge(short_A_C_wc_dir, '1', '2',
6756 A_B_url,
6757 expected_output,
6758 expected_disk,
6759 expected_status,
6760 expected_skip,
6761 check_props=1)
6762 os.chdir(saved_cwd)
6763 expected_output = wc.State(A_C_wc_dir, {
6764 '' : Item(verb='Sending'),
6765 'J' : Item(verb='Adding')
6767 expected_status = wc.State(A_C_wc_dir,
6768 { '' : Item(status=' ', wc_rev=4),
6769 'J' : Item(status=' ', wc_rev=4)
6772 svntest.actions.run_and_verify_commit(A_C_wc_dir,
6773 expected_output,
6774 expected_status,
6775 None,
6776 A_C_wc_dir)
6778 other_A_C_wc_dir = other_wc + '/A/C'
6779 short_other_A_C_wc_dir = shorten_path_kludge(other_A_C_wc_dir)
6780 expected_output = wc.State(short_other_A_C_wc_dir, {'K' : Item(status='A ')})
6781 expected_disk = wc.State('', {
6782 'K' : Item(),
6783 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B:3'}),
6785 expected_status = wc.State(short_other_A_C_wc_dir,
6786 { '' : Item(wc_rev=1, status=' M'),
6787 'K' : Item(status='A ',
6788 wc_rev='-', copied='+')
6791 expected_skip = wc.State('', { })
6792 saved_cwd = os.getcwd()
6793 os.chdir(svntest.main.work_dir)
6794 svntest.actions.run_and_verify_merge(short_other_A_C_wc_dir, '2', '3',
6795 A_B_url,
6796 expected_output,
6797 expected_disk,
6798 expected_status,
6799 expected_skip,
6800 check_props=1)
6801 os.chdir(saved_cwd)
6802 expected_output = wc.State(other_A_C_wc_dir,
6803 {'J' : Item(status='A '),
6804 '' : Item(status=' G')
6807 expected_disk = wc.State('', {
6808 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B:2-3'}),
6809 'J' : Item(),
6810 'K' : Item(),
6812 expected_status = wc.State(other_A_C_wc_dir,
6813 { '' : Item(wc_rev=4, status=' M'),
6814 'J' : Item(status=' ', wc_rev='4'),
6815 'K' : Item(status='A ',
6816 wc_rev='-', copied='+')
6819 svntest.actions.run_and_verify_update(other_A_C_wc_dir,
6820 expected_output,
6821 expected_disk,
6822 expected_status,
6823 check_props=1)
6825 # Tests part of issue# 2829, marked as XFail until that issue is fixed.
6826 def merge_loses_mergeinfo(sbox):
6827 "merge should merge mergeinfo"
6830 When a working copy has no mergeinfo(due to local full revert of all merges),
6831 and merge is attempted for someother revision rX, The new mergeinfo should be
6832 /merge/src: rX not all the reverted ones reappearing along with rX.
6835 sbox.build()
6836 wc_dir = sbox.wc_dir
6837 A_C_wc_dir = wc_dir + '/A/C'
6838 A_B_url = sbox.repo_url + '/A/B'
6839 A_B_J_url = sbox.repo_url + '/A/B/J'
6840 A_B_K_url = sbox.repo_url + '/A/B/K'
6841 svntest.actions.run_and_verify_svn(None, ['\n', 'Committed revision 2.\n'],
6843 'mkdir', '-m', 'rev 2', A_B_J_url)
6844 svntest.actions.run_and_verify_svn(None, ['\n', 'Committed revision 3.\n'],
6846 'mkdir', '-m', 'rev 3', A_B_K_url)
6848 short_A_C_wc_dir = shorten_path_kludge(A_C_wc_dir)
6849 expected_output = wc.State(short_A_C_wc_dir, {'J' : Item(status='A ')})
6850 expected_disk = wc.State('', {
6851 'J' : Item(),
6852 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B:2'}),
6854 expected_status = wc.State(short_A_C_wc_dir,
6855 { '' : Item(wc_rev=1, status=' M'),
6856 'J' : Item(status='A ',
6857 wc_rev='-', copied='+')
6860 expected_skip = wc.State('', { })
6861 saved_cwd = os.getcwd()
6862 os.chdir(svntest.main.work_dir)
6863 svntest.actions.run_and_verify_merge(short_A_C_wc_dir, '1', '2',
6864 A_B_url,
6865 expected_output,
6866 expected_disk,
6867 expected_status,
6868 expected_skip,
6869 check_props=1)
6870 os.chdir(saved_cwd)
6871 expected_output = wc.State(A_C_wc_dir, {
6872 '' : Item(verb='Sending'),
6873 'J' : Item(verb='Adding')
6875 expected_status = wc.State(A_C_wc_dir,
6876 { '' : Item(status=' ', wc_rev=4),
6877 'J' : Item(status=' ', wc_rev=4)
6880 svntest.actions.run_and_verify_commit(A_C_wc_dir,
6881 expected_output,
6882 expected_status,
6883 None,
6884 A_C_wc_dir)
6885 expected_output = wc.State(short_A_C_wc_dir, {'J' : Item(status='D ')})
6886 expected_disk = wc.State('', {'J': Item()})
6887 expected_status = wc.State(short_A_C_wc_dir,
6888 { '' : Item(wc_rev=4, status=' M'),
6889 'J' : Item(wc_rev=4, status='D ')
6892 saved_cwd = os.getcwd()
6893 os.chdir(svntest.main.work_dir)
6894 svntest.actions.run_and_verify_merge(short_A_C_wc_dir, '2', '1',
6895 A_B_url,
6896 expected_output,
6897 expected_disk,
6898 expected_status,
6899 expected_skip,
6900 check_props=1)
6901 os.chdir(saved_cwd)
6903 expected_output = wc.State(short_A_C_wc_dir, {'K' : Item(status='A ')})
6904 expected_disk = wc.State('', {
6905 'K' : Item(),
6906 'J' : Item(),
6907 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B:3'}),
6909 expected_status = wc.State(short_A_C_wc_dir,
6910 { '' : Item(wc_rev=4, status=' M'),
6911 'K' : Item(status='A ',
6912 wc_rev='-', copied='+'),
6913 'J' : Item(wc_rev=4, status='D ')
6916 saved_cwd = os.getcwd()
6917 os.chdir(svntest.main.work_dir)
6918 svntest.actions.run_and_verify_merge(short_A_C_wc_dir, '2', '3',
6919 A_B_url,
6920 expected_output,
6921 expected_disk,
6922 expected_status,
6923 expected_skip,
6924 check_props=1)
6926 def single_file_replace_style_merge_capability(sbox):
6927 "replace-style merge capability for a single file"
6929 # Test for issue #2853, do_single_file_merge() lacks "Replace-style
6930 # merge" capability
6932 sbox.build()
6933 wc_dir = sbox.wc_dir
6934 iota_path = os.path.join(wc_dir, 'iota')
6935 mu_path = os.path.join(wc_dir, 'A', 'mu')
6937 # delete mu and replace it with a copy of iota
6938 svntest.main.run_svn(None, 'rm', mu_path)
6939 svntest.main.run_svn(None, 'mv', iota_path, mu_path)
6941 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
6942 expected_status.tweak('A/mu', status=' ', wc_rev=2)
6943 expected_status.remove('iota')
6944 expected_output = svntest.wc.State(wc_dir, {
6945 'iota': Item(verb='Deleting'),
6946 'A/mu': Item(verb='Replacing'),
6948 svntest.actions.run_and_verify_commit(wc_dir,
6949 expected_output,
6950 expected_status,
6951 None, wc_dir)
6953 # Merge the file mu alone to rev1
6954 svntest.actions.run_and_verify_svn(None,
6955 expected_merge_output(None,
6956 ['D ' + mu_path + '\n',
6957 'A ' + mu_path + '\n']),
6959 'merge',
6960 mu_path + '@2',
6961 mu_path + '@1',
6962 mu_path)
6964 # Test for issue 2786 fix.
6965 def merge_to_out_of_date_target(sbox):
6966 "merge to ood path can lead to inaccurate mergeinfo"
6968 # Create a WC with a branch.
6969 sbox.build()
6970 wc_dir = sbox.wc_dir
6971 wc_disk, wc_status = set_up_branch(sbox, False, 1)
6973 # Make second working copy
6974 other_wc = sbox.add_wc_path('other')
6975 svntest.actions.duplicate_dir(wc_dir, other_wc)
6977 # Some paths we'll care about
6978 A_COPY_H_path = os.path.join(wc_dir, "A_COPY", "D", "H")
6979 other_A_COPY_H_path = os.path.join(other_wc, "A_COPY", "D", "H")
6981 # Merge -c3 into A_COPY/D/H of first WC.
6983 # Search for the comment entitled "The Merge Kluge" elsewhere in
6984 # this file, to understand why we shorten and chdir() below.
6985 short_H_COPY_path = shorten_path_kludge(A_COPY_H_path)
6986 expected_output = wc.State(short_H_COPY_path, {
6987 'psi' : Item(status='U ')
6989 expected_status = wc.State(short_H_COPY_path, {
6990 '' : Item(status=' M', wc_rev=2),
6991 'psi' : Item(status='M ', wc_rev=2),
6992 'omega' : Item(status=' ', wc_rev=2),
6993 'chi' : Item(status=' ', wc_rev=2),
6995 expected_disk = wc.State('', {
6996 '' : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:3'}),
6997 'psi' : Item("New content"),
6998 'omega' : Item("This is the file 'omega'.\n"),
6999 'chi' : Item("This is the file 'chi'.\n"),
7001 expected_skip = wc.State(short_H_COPY_path, { })
7002 saved_cwd = os.getcwd()
7003 os.chdir(svntest.main.work_dir)
7004 svntest.actions.run_and_verify_merge(short_H_COPY_path, '2', '3',
7005 sbox.repo_url + '/A/D/H',
7006 expected_output, expected_disk,
7007 expected_status, expected_skip,
7008 None, None, None, None, None, 1)
7009 os.chdir(saved_cwd)
7011 # Commit merge to first WC.
7012 wc_status.tweak('A_COPY/D/H/psi', 'A_COPY/D/H', wc_rev=7)
7013 expected_output = svntest.wc.State(wc_dir, {
7014 'A_COPY/D/H' : Item(verb='Sending'),
7015 'A_COPY/D/H/psi': Item(verb='Sending'),
7017 svntest.actions.run_and_verify_commit(wc_dir,
7018 expected_output,
7019 wc_status,
7020 None, wc_dir)
7022 # Merge -c6 into A_COPY/D/H of other WC.
7023 short_H_COPY_path = shorten_path_kludge(other_A_COPY_H_path)
7024 expected_output = wc.State(short_H_COPY_path, {
7025 'omega' : Item(status='U ')
7027 expected_status = wc.State(short_H_COPY_path, {
7028 '' : Item(status=' M', wc_rev=2),
7029 'psi' : Item(status=' ', wc_rev=2),
7030 'omega' : Item(status='M ', wc_rev=2),
7031 'chi' : Item(status=' ', wc_rev=2),
7033 expected_disk = wc.State('', {
7034 '' : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:6'}),
7035 'psi' : Item("This is the file 'psi'.\n"),
7036 'omega' : Item("New content"),
7037 'chi' : Item("This is the file 'chi'.\n"),
7039 expected_skip = wc.State(short_H_COPY_path, { })
7040 os.chdir(svntest.main.work_dir)
7041 svntest.actions.run_and_verify_merge(short_H_COPY_path, '5', '6',
7042 sbox.repo_url + '/A/D/H',
7043 expected_output, expected_disk,
7044 expected_status, expected_skip,
7045 None, None, None, None, None, 1)
7046 os.chdir(saved_cwd)
7048 # Update A_COPY/D/H in other WC. Local mergeinfo for r6 on A_COPY/D/H
7049 # should be *merged* with r3 from first WC.
7050 expected_output = svntest.wc.State(other_A_COPY_H_path, {
7051 '' : Item(status=' G'),
7052 'psi' : Item(status='U ')
7054 other_disk = wc.State('', {
7055 '' : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:3,6'}),
7056 'psi' : Item(contents="New content"),
7057 'chi' : Item("This is the file 'chi'.\n"),
7058 'omega' : Item(contents="New content"),
7060 other_status = wc.State(other_A_COPY_H_path,{
7061 '' : Item(wc_rev=7, status=' M'),
7062 'chi' : Item(wc_rev=7, status=' '),
7063 'psi' : Item(wc_rev=7, status=' '),
7064 'omega' : Item(wc_rev=7, status='M ')
7066 svntest.actions.run_and_verify_update(other_A_COPY_H_path,
7067 expected_output,
7068 other_disk,
7069 other_status,
7070 check_props=1)
7072 def merge_with_depth_files(sbox):
7073 "merge test for --depth files"
7075 sbox.build()
7076 wc_dir = sbox.wc_dir
7078 # Some paths we'll care about
7079 mu_path = os.path.join(wc_dir, 'A', 'mu')
7080 gamma_path = os.path.join(wc_dir, 'A', 'D', 'gamma')
7081 Acopy_path = os.path.join(wc_dir, 'A_copy')
7082 Acopy_mu_path = os.path.join(wc_dir, 'A_copy', 'mu')
7083 A_url = sbox.repo_url + '/A'
7084 Acopy_url = sbox.repo_url + '/A_copy'
7086 # Copy A_url to A_copy_url
7087 svntest.actions.run_and_verify_svn(None, None, [], 'cp',
7088 A_url, Acopy_url,
7089 '-m', 'create a new copy of A')
7091 svntest.main.file_write(mu_path, "this is file 'mu' modified.\n")
7092 svntest.main.file_write(gamma_path, "this is file 'gamma' modified.\n")
7094 # Create expected output tree for commit
7095 expected_output = wc.State(wc_dir, {
7096 'A/mu' : Item(verb='Sending'),
7097 'A/D/gamma' : Item(verb='Sending'),
7100 # Create expected status tree for commit
7101 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
7102 expected_status.add({
7103 'A/mu' : Item(status=' ', wc_rev=3),
7104 'A/D/gamma' : Item(status=' ', wc_rev=3),
7107 # Commit the modified contents
7108 svntest.actions.run_and_verify_commit(wc_dir,
7109 expected_output,
7110 expected_status,
7111 None,
7112 wc_dir)
7114 # Update working copy
7115 svntest.actions.run_and_verify_svn(None, None, [],
7116 'up', Acopy_path)
7118 # Merge r1:3 into A_copy with --depth files. The merge only affects
7119 # 'A_copy' and its one file child 'mu', so 'A_copy' gets non-inheritable
7120 # mergeinfo for -r1:3 and 'mu' gets its own complete set of mergeinfo:
7121 # r1 from its parent, and r1:3 from the merge itself.
7123 # Search for the comment entitled "The Merge Kluge" elsewhere in
7124 # this file, to understand why we shorten and chdir() below.
7125 short_A_COPY_path = shorten_path_kludge(Acopy_path)
7126 expected_output = wc.State(short_A_COPY_path, {
7127 'mu' : Item(status='U '),
7129 expected_status = wc.State(short_A_COPY_path, {
7130 '' : Item(status=' M'),
7131 'B' : Item(status=' '),
7132 'mu' : Item(status='MM'),
7133 'B/E' : Item(status=' '),
7134 'B/E/alpha' : Item(status=' '),
7135 'B/E/beta' : Item(status=' '),
7136 'B/lambda' : Item(status=' '),
7137 'B/F' : Item(status=' '),
7138 'C' : Item(status=' '),
7139 'D' : Item(status=' '),
7140 'D/G' : Item(status=' '),
7141 'D/G/pi' : Item(status=' '),
7142 'D/G/rho' : Item(status=' '),
7143 'D/G/tau' : Item(status=' '),
7144 'D/gamma' : Item(status=' '),
7145 'D/H' : Item(status=' '),
7146 'D/H/chi' : Item(status=' '),
7147 'D/H/psi' : Item(status=' '),
7148 'D/H/omega' : Item(status=' '),
7150 expected_status.tweak(wc_rev=3)
7151 expected_disk = wc.State('', {
7152 '' : Item(props={SVN_PROP_MERGEINFO : '/A:2-3*'}),
7153 'B' : Item(),
7154 'mu' : Item("this is file 'mu' modified.\n",
7155 props={SVN_PROP_MERGEINFO : '/A/mu:2-3'}),
7156 'B/E' : Item(),
7157 'B/E/alpha' : Item("This is the file 'alpha'.\n"),
7158 'B/E/beta' : Item("This is the file 'beta'.\n"),
7159 'B/lambda' : Item("This is the file 'lambda'.\n"),
7160 'B/F' : Item(),
7161 'C' : Item(),
7162 'D' : Item(),
7163 'D/G' : Item(),
7164 'D/G/pi' : Item("This is the file 'pi'.\n"),
7165 'D/G/rho' : Item("This is the file 'rho'.\n"),
7166 'D/G/tau' : Item("This is the file 'tau'.\n"),
7167 'D/gamma' : Item("This is the file 'gamma'.\n"),
7168 'D/H' : Item(),
7169 'D/H/chi' : Item("This is the file 'chi'.\n"),
7170 'D/H/psi' : Item("This is the file 'psi'.\n"),
7171 'D/H/omega' : Item("This is the file 'omega'.\n"),
7173 expected_skip = wc.State(short_A_COPY_path, { })
7174 saved_cwd = os.getcwd()
7175 os.chdir(svntest.main.work_dir)
7176 svntest.actions.run_and_verify_merge(short_A_COPY_path, '1', '3',
7177 sbox.repo_url + '/A',
7178 expected_output, expected_disk,
7179 expected_status, expected_skip,
7180 None, None, None, None, None, 1, 1,
7181 '--depth', 'files')
7182 os.chdir(saved_cwd)
7184 def merge_fails_if_subtree_is_deleted_on_src(sbox):
7185 "merge fails if subtree is deleted on src"
7187 ## See http://subversion.tigris.org/issues/show_bug.cgi?id=2876. ##
7189 # Create a WC
7190 sbox.build()
7191 wc_dir = sbox.wc_dir
7193 # Some paths we'll care about
7194 Acopy_path = os.path.join(wc_dir, 'A_copy')
7195 gamma_path = os.path.join(wc_dir, 'A', 'D', 'gamma')
7196 Acopy_gamma_path = os.path.join(wc_dir, 'A_copy', 'D', 'gamma')
7197 A_url = sbox.repo_url + '/A'
7198 Acopy_url = sbox.repo_url + '/A_copy'
7200 # Contents to be added to 'gamma'
7201 new_content = "line1\nline2\nline3\nline4\nline5\n"
7203 svntest.main.file_write(gamma_path, new_content)
7205 # Create expected output tree for commit
7206 expected_output = wc.State(wc_dir, {
7207 'A/D/gamma' : Item(verb='Sending'),
7210 # Create expected status tree for commit
7211 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
7212 expected_status.tweak('A/D/gamma', wc_rev=2)
7214 # Commit the new content
7215 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
7216 expected_status, None, wc_dir)
7218 svntest.actions.run_and_verify_svn(None, None, [], 'cp', A_url, Acopy_url,
7219 '-m', 'create a new copy of A')
7221 # Update working copy
7222 svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
7224 svntest.main.file_substitute(gamma_path, "line1", "this is line1")
7225 # Create expected output tree for commit
7226 expected_output = wc.State(wc_dir, {
7227 'A/D/gamma' : Item(verb='Sending'),
7230 # Create expected status tree for commit
7231 expected_status.tweak(wc_rev=3)
7232 expected_status.tweak('A/D/gamma', wc_rev=4)
7233 expected_status.add({
7234 'A_copy' : Item(status=' ', wc_rev=3),
7235 'A_copy/B' : Item(status=' ', wc_rev=3),
7236 'A_copy/B/lambda' : Item(status=' ', wc_rev=3),
7237 'A_copy/B/E' : Item(status=' ', wc_rev=3),
7238 'A_copy/B/E/alpha': Item(status=' ', wc_rev=3),
7239 'A_copy/B/E/beta' : Item(status=' ', wc_rev=3),
7240 'A_copy/B/F' : Item(status=' ', wc_rev=3),
7241 'A_copy/mu' : Item(status=' ', wc_rev=3),
7242 'A_copy/C' : Item(status=' ', wc_rev=3),
7243 'A_copy/D' : Item(status=' ', wc_rev=3),
7244 'A_copy/D/gamma' : Item(status=' ', wc_rev=3),
7245 'A_copy/D/G' : Item(status=' ', wc_rev=3),
7246 'A_copy/D/G/pi' : Item(status=' ', wc_rev=3),
7247 'A_copy/D/G/rho' : Item(status=' ', wc_rev=3),
7248 'A_copy/D/G/tau' : Item(status=' ', wc_rev=3),
7249 'A_copy/D/H' : Item(status=' ', wc_rev=3),
7250 'A_copy/D/H/chi' : Item(status=' ', wc_rev=3),
7251 'A_copy/D/H/omega': Item(status=' ', wc_rev=3),
7252 'A_copy/D/H/psi' : Item(status=' ', wc_rev=3),
7255 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
7256 expected_status, None, wc_dir)
7258 # Delete A/D/gamma from working copy
7259 svntest.actions.run_and_verify_svn(None, None, [], 'delete', gamma_path)
7260 # Create expected output tree for commit
7261 expected_output = wc.State(wc_dir, {
7262 'A/D/gamma' : Item(verb='Deleting'),
7265 expected_status.remove('A/D/gamma')
7267 svntest.actions.run_and_verify_commit(wc_dir,
7268 expected_output,
7269 expected_status,
7270 None,
7271 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)
7277 svntest.actions.run_and_verify_svn(None, expected_merge_output([[3,5]],
7278 'D ' + Acopy_gamma_path + '\n'),
7279 [], 'merge', '-r1:5', '--force',
7280 A_url, Acopy_path)
7282 # Test for issue #2976 Subtrees can lose non-inheritable ranges
7283 def merge_away_subtrees_noninheritable_ranges(sbox):
7284 "subtrees can lose non-inheritable ranges"
7286 sbox.build()
7287 wc_dir = sbox.wc_dir
7288 wc_disk, wc_status = set_up_branch(sbox)
7290 # Some paths we'll care about
7291 H_path = os.path.join(wc_dir, "A", "D", "H")
7292 D_COPY_path = os.path.join(wc_dir, "A_COPY", "D")
7294 # Make a change to directory A/D/H and commit as r7.
7295 svntest.actions.run_and_verify_svn(None, ['At revision 6.\n'], [],
7296 'update', wc_dir)
7298 svntest.actions.run_and_verify_svn(
7299 None, ["property 'prop:name' set on '" + H_path + "'\n"], [],
7300 'ps', 'prop:name', 'propval', H_path)
7301 expected_output = svntest.wc.State(wc_dir, {
7302 'A/D/H' : Item(verb='Sending'),})
7303 wc_status.tweak(wc_rev=6)
7304 wc_status.tweak('A/D/H', wc_rev=7)
7305 svntest.actions.run_and_verify_commit(wc_dir, expected_output, wc_status,
7306 None, wc_dir)
7308 # Merge r5:7 --depth immediates to A_COPY/D. This should merge the
7309 # prop change from r7 to A_COPY/H but not the change to A_COPY/D/H/omega
7310 # from r6 since that is below the depth we are merging to. Instead,
7311 # non-inheritable mergeinfo should be set on the immediate directory
7312 # children of A_COPY/D: A_COPY/D/G and A_COPY/D/H.
7314 # Search for the comment entitled "The Merge Kluge" elsewhere in
7315 # this file, to understand why we shorten and chdir() below.
7316 short_D_COPY_path = shorten_path_kludge(D_COPY_path)
7317 expected_output = wc.State(short_D_COPY_path, {
7318 'H' : Item(status=' U'),
7320 expected_status = wc.State(short_D_COPY_path, {
7321 '' : Item(status=' M', wc_rev=6),
7322 'H' : Item(status=' M', wc_rev=6),
7323 'H/chi' : Item(status=' ', wc_rev=6),
7324 'H/omega' : Item(status=' ', wc_rev=6),
7325 'H/psi' : Item(status=' ', wc_rev=6),
7326 'G' : Item(status=' M', wc_rev=6),
7327 'G/pi' : Item(status=' ', wc_rev=6),
7328 'G/rho' : Item(status=' ', wc_rev=6),
7329 'G/tau' : Item(status=' ', wc_rev=6),
7330 'gamma' : Item(status=' ', wc_rev=6),
7332 expected_disk = wc.State('', {
7333 '' : Item(props={SVN_PROP_MERGEINFO : '/A/D:6-7'}),
7334 'H' : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:6-7*',
7335 'prop:name' : 'propval'}),
7336 'H/chi' : Item("This is the file 'chi'.\n"),
7337 'H/omega' : Item("This is the file 'omega'.\n"),
7338 'H/psi' : Item("This is the file 'psi'.\n"),
7339 'G' : Item(props={SVN_PROP_MERGEINFO : '/A/D/G:6-7*'}),
7340 'G/pi' : Item("This is the file 'pi'.\n"),
7341 'G/rho' : Item("This is the file 'rho'.\n"),
7342 'G/tau' : Item("This is the file 'tau'.\n"),
7343 'gamma' : Item("This is the file 'gamma'.\n"),
7345 expected_skip = wc.State(short_D_COPY_path, { })
7346 saved_cwd = os.getcwd()
7347 os.chdir(svntest.main.work_dir)
7348 svntest.actions.run_and_verify_merge(short_D_COPY_path, '5', '7',
7349 sbox.repo_url + '/A/D',
7350 expected_output, expected_disk,
7351 expected_status, expected_skip,
7352 None, None, None, None, None, 1, 1,
7353 '--depth', 'immediates')
7354 os.chdir(saved_cwd)
7356 # Repeat the previous merge but at default depth of infinity. The change
7357 # to A_COPY/D/H/omega should now happen and the non-inheritable ranges on
7358 # A_COPY/D/G and A_COPY/D/H be changed to inheritable and then elide to
7359 # A_COPY/D.
7360 expected_output = wc.State(short_D_COPY_path, {
7361 'H/omega' : Item(status='U '),
7363 expected_disk.tweak('', props={SVN_PROP_MERGEINFO : '/A/D:6-7'})
7364 expected_disk.tweak('H', props={'prop:name' : 'propval'})
7365 expected_disk.tweak('G', props={})
7366 expected_disk.tweak('H/omega', contents="New content")
7367 expected_status.tweak('G', status=' ')
7368 expected_status.tweak('H/omega', status='M ')
7369 os.chdir(svntest.main.work_dir)
7370 svntest.actions.run_and_verify_merge(short_D_COPY_path, '5', '7',
7371 sbox.repo_url + '/A/D',
7372 expected_output, expected_disk,
7373 expected_status, expected_skip,
7374 None, None, None, None, None, 1, 1)
7375 os.chdir(saved_cwd)
7377 # Test for issue #2827
7378 # Handle merge info for sparsely-populated directories
7379 def merge_to_sparse_directories(sbox):
7380 "merge to sparse directories"
7382 # Merges into sparse working copies should set non-inheritable mergeinfo
7383 # on the deepest directories present in the WC.
7385 sbox.build()
7386 wc_dir = sbox.wc_dir
7387 wc_disk, wc_status = set_up_branch(sbox, False, 1)
7389 # Some paths we'll care about
7390 A_path = os.path.join(wc_dir, "A")
7391 D_path = os.path.join(wc_dir, "A", "D")
7392 I_path = os.path.join(wc_dir, "A", "C", "I")
7393 G_path = os.path.join(wc_dir, "A", "D", "G")
7394 A_COPY_path = os.path.join(wc_dir, "A_COPY")
7396 # Make a few more changes to the merge source...
7398 # r7 - modify and commit A/mu
7399 svntest.main.file_write(os.path.join(wc_dir, "A", "mu"),
7400 "New content")
7401 expected_output = wc.State(wc_dir, {'A/mu' : Item(verb='Sending')})
7402 wc_status.tweak('A/mu', wc_rev=7)
7403 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
7404 wc_status, None, wc_dir)
7405 wc_disk.tweak('A/mu', contents="New content")
7407 # r8 - Add a prop to A/D and commit.
7408 svntest.actions.run_and_verify_svn(None, ["At revision 7.\n"], [],
7409 'up', wc_dir)
7410 svntest.actions.run_and_verify_svn(None,
7411 ["property 'prop:name' set on '" +
7412 D_path + "'\n"], [], 'ps',
7413 'prop:name', 'propval', D_path)
7414 expected_output = svntest.wc.State(wc_dir, {
7415 'A/D' : Item(verb='Sending'),
7417 wc_status.tweak(wc_rev=7)
7418 wc_status.tweak('A/D', wc_rev=8)
7419 svntest.actions.run_and_verify_commit(wc_dir, expected_output, wc_status,
7420 None, wc_dir)
7422 # r9 - Add a prop to A and commit.
7423 svntest.actions.run_and_verify_svn(None, ["At revision 8.\n"], [],
7424 'up', wc_dir)
7425 svntest.actions.run_and_verify_svn(None,
7426 ["property 'prop:name' set on '" +
7427 A_path + "'\n"], [], 'ps',
7428 'prop:name', 'propval', A_path)
7429 expected_output = svntest.wc.State(wc_dir, {
7430 'A' : Item(verb='Sending'),
7432 wc_status.tweak(wc_rev=8)
7433 wc_status.tweak('A', wc_rev=9)
7434 svntest.actions.run_and_verify_commit(wc_dir, expected_output, wc_status,
7435 None, wc_dir)
7437 # Do an --immediates checkout of A_COPY
7438 immediates_dir = sbox.add_wc_path('immediates')
7439 expected_output = wc.State(immediates_dir, {
7440 'B' : Item(status='A '),
7441 'mu' : Item(status='A '),
7442 'C' : Item(status='A '),
7443 'D' : Item(status='A '),
7445 expected_disk = wc.State('', {
7446 'B' : Item(),
7447 'mu' : Item("This is the file 'mu'.\n"),
7448 'C' : Item(),
7449 'D' : Item(),
7451 svntest.actions.run_and_verify_checkout(sbox.repo_url + "/A_COPY",
7452 immediates_dir,
7453 expected_output, expected_disk,
7454 None, None, None, None,
7455 "--depth", "immediates")
7457 # Merge r4:9 into the immediates WC.
7458 # The root of the immediates WC should get inheritable r4:9 as should
7459 # the one file present 'mu'. The three directory children present, 'B',
7460 # 'C', and 'D' are checked out at depth empty, so get non-inheritable
7461 # mergeinfo for r4:9. The root and 'D' do should also get the changes
7462 # that affect them directly (the prop adds from r8 and r9). Any changes
7463 # deeper than the immediates should be skipped.
7465 # Search for the comment entitled "The Merge Kluge" elsewhere in
7466 # this file, to understand why we shorten and chdir() below.
7467 short_immediates_dir = shorten_path_kludge(immediates_dir)
7468 expected_output = wc.State(short_immediates_dir, {
7469 'D' : Item(status=' U'),
7470 'mu' : Item(status='U '),
7471 '' : Item(status=' U'),
7473 expected_status = wc.State(short_immediates_dir, {
7474 '' : Item(status=' M', wc_rev=9),
7475 'B' : Item(status=' M', wc_rev=9),
7476 'mu' : Item(status='M ', wc_rev=9),
7477 'C' : Item(status=' M', wc_rev=9),
7478 'D' : Item(status=' M', wc_rev=9),
7480 expected_disk = wc.State('', {
7481 '' : Item(props={SVN_PROP_MERGEINFO : '/A:5-9',
7482 "prop:name" : "propval"}),
7483 'B' : Item(props={SVN_PROP_MERGEINFO : '/A/B:5-9*'}),
7484 'mu' : Item("New content"),
7485 'C' : Item(props={SVN_PROP_MERGEINFO : '/A/C:5-9*'}),
7486 'D' : Item(props={SVN_PROP_MERGEINFO : '/A/D:5-9*',
7487 "prop:name" : "propval"}),
7489 expected_skip = wc.State(short_immediates_dir, {})
7490 saved_cwd = os.getcwd()
7491 os.chdir(svntest.main.work_dir)
7492 svntest.actions.run_and_verify_merge(short_immediates_dir, '4', '9',
7493 sbox.repo_url + \
7494 '/A',
7495 expected_output,
7496 expected_disk,
7497 expected_status,
7498 expected_skip,
7499 None, None, None, None,
7500 None, 1)
7501 os.chdir(saved_cwd)
7503 # Do a --files checkout of A_COPY
7504 files_dir = sbox.add_wc_path('files')
7505 expected_output = wc.State(files_dir, {
7506 'mu' : Item(status='A '),
7508 expected_disk = wc.State('', {
7509 'mu' : Item("This is the file 'mu'.\n"),
7511 svntest.actions.run_and_verify_checkout(sbox.repo_url + "/A_COPY",
7512 files_dir,
7513 expected_output, expected_disk,
7514 None, None, None, None,
7515 "--depth", "files")
7517 # Merge r4:9 into the files WC.
7518 # The root of the files WC should get non-inheritable r4:9 and its one
7519 # present child 'mu' should get the same but inheritable. The root
7520 # should also get the change that affects it directly (the prop add
7521 # from r9).
7522 short_files_dir = shorten_path_kludge(files_dir)
7523 expected_output = wc.State(short_files_dir, {
7524 'mu' : Item(status='U '),
7525 '' : Item(status=' U'),
7527 expected_status = wc.State(short_files_dir, {
7528 '' : Item(status=' M', wc_rev=9),
7529 'mu' : Item(status='MM', wc_rev=9),
7531 expected_disk = wc.State('', {
7532 '' : Item(props={SVN_PROP_MERGEINFO : '/A:5-9*',
7533 "prop:name" : "propval"}),
7534 'mu' : Item("New content",
7535 props={SVN_PROP_MERGEINFO : '/A/mu:5-9'}),
7537 expected_skip = wc.State(short_files_dir, {})
7538 os.chdir(svntest.main.work_dir)
7539 svntest.actions.run_and_verify_merge(short_files_dir, '4', '9',
7540 sbox.repo_url + \
7541 '/A',
7542 expected_output,
7543 expected_disk,
7544 expected_status,
7545 expected_skip,
7546 None, None, None, None,
7547 None, 1)
7548 os.chdir(saved_cwd)
7550 # Do an --empty checkout of A_COPY
7551 empty_dir = sbox.add_wc_path('empty')
7552 expected_output = wc.State(empty_dir, {})
7553 expected_disk = wc.State('', {})
7554 svntest.actions.run_and_verify_checkout(sbox.repo_url + "/A_COPY",
7555 empty_dir,
7556 expected_output, expected_disk,
7557 None, None, None, None,
7558 "--depth", "empty")
7560 # Merge r4:9 into the empty WC.
7561 # The root of the files WC should get non-inheritable r4:9 and also get
7562 # the one change that affects it directly (the prop add from r9).
7563 short_empty_dir = shorten_path_kludge(empty_dir)
7564 expected_output = wc.State(short_empty_dir, {
7565 '' : Item(status=' U'),
7567 expected_status = wc.State(short_empty_dir, {
7568 '' : Item(status=' M', wc_rev=9),
7570 expected_disk = wc.State('', {
7571 '' : Item(props={SVN_PROP_MERGEINFO : '/A:5-9*',
7572 "prop:name" : "propval"}),
7574 expected_skip = wc.State(short_empty_dir, {})
7575 os.chdir(svntest.main.work_dir)
7576 svntest.actions.run_and_verify_merge(short_empty_dir, '4', '9',
7577 sbox.repo_url + \
7578 '/A',
7579 expected_output,
7580 expected_disk,
7581 expected_status,
7582 expected_skip,
7583 None, None, None, None,
7584 None, 1)
7585 os.chdir(saved_cwd)
7587 def merge_old_and_new_revs_from_renamed_dir(sbox):
7588 "merge -rold(before rename):head renamed dir"
7590 ## See http://svn.haxx.se/dev/archive-2007-09/0706.shtml ##
7592 # Create a WC with a single branch
7593 sbox.build()
7594 wc_dir = sbox.wc_dir
7595 wc_disk, wc_status = set_up_branch(sbox, True, 1)
7597 # Some paths we'll care about
7598 A_url = sbox.repo_url + '/A'
7599 A_MOVED_url = sbox.repo_url + '/A_MOVED'
7600 A_COPY_path = os.path.join(wc_dir, 'A_COPY')
7601 mu_path = os.path.join(wc_dir, 'A', 'mu')
7602 A_MOVED_mu_path = os.path.join(wc_dir, 'A_MOVED', 'mu')
7604 # Make a modification to A/mu
7605 svntest.main.file_write(mu_path, "This is the file 'mu' modified.\n")
7606 expected_output = wc.State(wc_dir, {'A/mu' : Item(verb='Sending')})
7607 wc_status.add({'A/mu' : Item(status=' ', wc_rev=3)})
7608 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
7609 wc_status, None, wc_dir)
7611 # Move A to A_MOVED
7612 svntest.actions.run_and_verify_svn(None, ['\n', 'Committed revision 4.\n'],
7613 [], 'mv', '-m', 'mv A to A_MOVED',
7614 A_url, A_MOVED_url)
7616 # Update the working copy to get A_MOVED
7617 svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
7619 # Make a modification to A_MOVED/mu
7620 svntest.main.file_write(A_MOVED_mu_path, "This is 'mu' in A_MOVED.\n")
7621 expected_output = wc.State(wc_dir, {'A_MOVED/mu' : Item(verb='Sending')})
7622 expected_status = svntest.actions.get_virginal_state(wc_dir, 4)
7623 expected_status.remove('A', 'A/mu', 'A/C', 'A/D', 'A/B', 'A/B/lambda',
7624 'A/B/E', 'A/B/E/alpha', 'A/B/E/beta', 'A/B/F',
7625 'A/D/gamma', 'A/D/G', 'A/D/G/pi', 'A/D/G/rho',
7626 'A/D/G/tau', 'A/D/H', 'A/D/H/chi', 'A/D/H/omega',
7627 'A/D/H/psi')
7628 expected_status.add({
7629 '' : Item(status=' ', wc_rev=4),
7630 'iota' : Item(status=' ', wc_rev=4),
7631 'A_MOVED' : Item(status=' ', wc_rev=4),
7632 'A_MOVED/mu' : Item(status=' ', wc_rev=5),
7633 'A_MOVED/C' : Item(status=' ', wc_rev=4),
7634 'A_MOVED/D' : Item(status=' ', wc_rev=4),
7635 'A_MOVED/B' : Item(status=' ', wc_rev=4),
7636 'A_MOVED/B/lambda' : Item(status=' ', wc_rev=4),
7637 'A_MOVED/B/E' : Item(status=' ', wc_rev=4),
7638 'A_MOVED/B/E/alpha': Item(status=' ', wc_rev=4),
7639 'A_MOVED/B/E/beta' : Item(status=' ', wc_rev=4),
7640 'A_MOVED/B/F' : Item(status=' ', wc_rev=4),
7641 'A_MOVED/D/gamma' : Item(status=' ', wc_rev=4),
7642 'A_MOVED/D/G' : Item(status=' ', wc_rev=4),
7643 'A_MOVED/D/G/pi' : Item(status=' ', wc_rev=4),
7644 'A_MOVED/D/G/rho' : Item(status=' ', wc_rev=4),
7645 'A_MOVED/D/G/tau' : Item(status=' ', wc_rev=4),
7646 'A_MOVED/D/H' : Item(status=' ', wc_rev=4),
7647 'A_MOVED/D/H/chi' : Item(status=' ', wc_rev=4),
7648 'A_MOVED/D/H/omega': Item(status=' ', wc_rev=4),
7649 'A_MOVED/D/H/psi' : Item(status=' ', wc_rev=4),
7650 'A_COPY' : Item(status=' ', wc_rev=4),
7651 'A_COPY/mu' : Item(status=' ', wc_rev=4),
7652 'A_COPY/C' : Item(status=' ', wc_rev=4),
7653 'A_COPY/D' : Item(status=' ', wc_rev=4),
7654 'A_COPY/B' : Item(status=' ', wc_rev=4),
7655 'A_COPY/B/lambda' : Item(status=' ', wc_rev=4),
7656 'A_COPY/B/E' : Item(status=' ', wc_rev=4),
7657 'A_COPY/B/E/alpha' : Item(status=' ', wc_rev=4),
7658 'A_COPY/B/E/beta' : Item(status=' ', wc_rev=4),
7659 'A_COPY/B/F' : Item(status=' ', wc_rev=4),
7660 'A_COPY/D/gamma' : Item(status=' ', wc_rev=4),
7661 'A_COPY/D/G' : Item(status=' ', wc_rev=4),
7662 'A_COPY/D/G/pi' : Item(status=' ', wc_rev=4),
7663 'A_COPY/D/G/rho' : Item(status=' ', wc_rev=4),
7664 'A_COPY/D/G/tau' : Item(status=' ', wc_rev=4),
7665 'A_COPY/D/H' : Item(status=' ', wc_rev=4),
7666 'A_COPY/D/H/chi' : Item(status=' ', wc_rev=4),
7667 'A_COPY/D/H/omega' : Item(status=' ', wc_rev=4),
7668 'A_COPY/D/H/psi' : Item(status=' ', wc_rev=4),
7670 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
7671 expected_status, None, wc_dir)
7673 short_A_COPY = shorten_path_kludge(A_COPY_path)
7674 saved_cwd = os.getcwd()
7675 os.chdir(svntest.main.work_dir)
7677 # Merge /A_MOVED to /A_COPY - this happens in multiple passes
7678 # because /A_MOVED has renames in its history between the boundaries
7679 # of the requested merge range.
7680 expected_output = wc.State(short_A_COPY, {
7681 'mu' : Item(status='G '), # mu gets touched twice
7683 expected_status = wc.State(short_A_COPY, {
7684 '' : Item(status=' M', wc_rev=4),
7685 'mu' : Item(status='M ', wc_rev=4),
7686 'C' : Item(status=' ', wc_rev=4),
7687 'D' : Item(status=' ', wc_rev=4),
7688 'B' : Item(status=' ', wc_rev=4),
7689 'B/lambda' : Item(status=' ', wc_rev=4),
7690 'B/E' : Item(status=' ', wc_rev=4),
7691 'B/E/alpha': Item(status=' ', wc_rev=4),
7692 'B/E/beta' : Item(status=' ', wc_rev=4),
7693 'B/F' : Item(status=' ', wc_rev=4),
7694 'D/gamma' : Item(status=' ', wc_rev=4),
7695 'D/G' : Item(status=' ', wc_rev=4),
7696 'D/G/pi' : Item(status=' ', wc_rev=4),
7697 'D/G/rho' : Item(status=' ', wc_rev=4),
7698 'D/G/tau' : Item(status=' ', wc_rev=4),
7699 'D/H' : Item(status=' ', wc_rev=4),
7700 'D/H/chi' : Item(status=' ', wc_rev=4),
7701 'D/H/omega': Item(status=' ', wc_rev=4),
7702 'D/H/psi' : Item(status=' ', wc_rev=4),
7704 expected_disk = wc.State('', {
7705 '' : Item(props={SVN_PROP_MERGEINFO : '/A:3\n/A_MOVED:4-5\n'}),
7706 'mu' : Item("This is 'mu' in A_MOVED.\n"),
7707 'C' : Item(),
7708 'D' : Item(),
7709 'B' : Item(),
7710 'B/lambda' : Item("This is the file 'lambda'.\n"),
7711 'B/E' : Item(),
7712 'B/E/alpha': Item("This is the file 'alpha'.\n"),
7713 'B/E/beta' : Item("This is the file 'beta'.\n"),
7714 'B/F' : Item(),
7715 'D/gamma' : Item("This is the file 'gamma'.\n"),
7716 'D/G' : Item(),
7717 'D/G/pi' : Item("This is the file 'pi'.\n"),
7718 'D/G/rho' : Item("This is the file 'rho'.\n"),
7719 'D/G/tau' : Item("This is the file 'tau'.\n"),
7720 'D/H' : Item(),
7721 'D/H/chi' : Item("This is the file 'chi'.\n"),
7722 'D/H/omega': Item("This is the file 'omega'.\n"),
7723 'D/H/psi' : Item("This is the file 'psi'.\n"),
7725 expected_skip = wc.State(short_A_COPY, {})
7727 ### Disabling dry_run mode because currently it can't handle the way
7728 ### 'mu' gets textually modified in multiple passes.
7729 svntest.actions.run_and_verify_merge(short_A_COPY, '2', '5',
7730 A_MOVED_url,
7731 expected_output,
7732 expected_disk,
7733 expected_status,
7734 expected_skip,
7735 None, None, None, None, None,
7736 True, False)
7737 os.chdir(saved_cwd)
7739 def merge_with_child_having_different_rev_ranges_to_merge(sbox):
7740 "child having different rev ranges to merge"
7741 #Modify A/mu to 30 lines with a content 'line1'...'line30' commit it at r2.
7742 #Create a branch A_COPY from A, commit it at r3.
7743 #Modify A/mu line number 7 to 'LINE7' modify and commit at r4.
7744 #Modify A/mu line number 17 to 'LINE17' modify, set prop 'prop1' on 'A'
7745 #with a value 'val1' and commit at r5.
7746 #Modify A/mu line number 27 to 'LINE27' modify and commit at r6.
7747 #Merge r5 to 'A/mu' as a single file merge explicitly to 'A_COPY/mu'.
7748 #Merge r3:6 from 'A' to 'A_COPY
7749 #This should merge r4 and then r5 through r6.
7750 #Revert r5 and r6 via single file merge on A_COPY/mu.
7751 #Revert r6 through r4 on A_COPY this should get back us the pristine copy.
7752 #Merge r3:6 from 'A' to 'A_COPY
7753 #Revert r5 on A_COPY/mu
7754 #Modify line number 17 with 'some other line17' of A_COPY/mu
7755 #Merge r6:3 from 'A' to 'A_COPY, This should leave line number 17
7756 #undisturbed in A_COPY/mu, rest should be reverted.
7758 # Create a WC
7759 sbox.build()
7760 wc_dir = sbox.wc_dir
7761 A_path = os.path.join(wc_dir, 'A')
7762 mu_path = os.path.join(wc_dir, 'A', 'mu')
7763 A_url = sbox.repo_url + '/A'
7764 A_mu_url = sbox.repo_url + '/A/mu'
7765 A_COPY_url = sbox.repo_url + '/A_COPY'
7766 A_COPY_path = os.path.join(wc_dir, 'A_COPY')
7767 A_COPY_mu_path = os.path.join(wc_dir, 'A_COPY', 'mu')
7768 thirty_line_dummy_text = 'line1\n'
7769 for i in range(2, 31):
7770 thirty_line_dummy_text += 'line' + str(i) + '\n'
7772 svntest.main.file_write(mu_path, thirty_line_dummy_text)
7773 expected_output = wc.State(wc_dir, {'A/mu' : Item(verb='Sending')})
7774 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
7775 expected_status.tweak('A/mu', wc_rev=2)
7776 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
7777 expected_status, None, wc_dir)
7778 svntest.actions.run_and_verify_svn(None, None, [],
7779 'cp', A_url, A_COPY_url, '-m', 'rev 3')
7780 # Update the working copy to get A_COPY
7781 svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
7782 expected_status.add({'A_COPY' : Item(status=' '),
7783 'A_COPY/mu' : Item(status=' '),
7784 'A_COPY/C' : Item(status=' '),
7785 'A_COPY/D' : Item(status=' '),
7786 'A_COPY/B' : Item(status=' '),
7787 'A_COPY/B/lambda' : Item(status=' '),
7788 'A_COPY/B/E' : Item(status=' '),
7789 'A_COPY/B/E/alpha' : Item(status=' '),
7790 'A_COPY/B/E/beta' : Item(status=' '),
7791 'A_COPY/B/F' : Item(status=' '),
7792 'A_COPY/D/gamma' : Item(status=' '),
7793 'A_COPY/D/G' : Item(status=' '),
7794 'A_COPY/D/G/pi' : Item(status=' '),
7795 'A_COPY/D/G/rho' : Item(status=' '),
7796 'A_COPY/D/G/tau' : Item(status=' '),
7797 'A_COPY/D/H' : Item(status=' '),
7798 'A_COPY/D/H/chi' : Item(status=' '),
7799 'A_COPY/D/H/omega' : Item(status=' '),
7800 'A_COPY/D/H/psi' : Item(status=' ')})
7801 expected_status.tweak(wc_rev=3)
7802 tweaked_7th_line = thirty_line_dummy_text.replace('line7', 'LINE 7')
7803 svntest.main.file_write(mu_path, tweaked_7th_line)
7804 expected_status.tweak('A/mu', wc_rev=4)
7805 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
7806 expected_status, None, wc_dir)
7807 svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
7808 expected_status.tweak(wc_rev=4)
7809 tweaked_17th_line = tweaked_7th_line.replace('line17', 'LINE 17')
7810 svntest.main.file_write(mu_path, tweaked_17th_line)
7811 svntest.main.run_svn(None, 'propset', 'prop1', 'val1', A_path)
7812 expected_output = wc.State(wc_dir,
7814 'A' : Item(verb='Sending'),
7815 'A/mu' : Item(verb='Sending')
7818 expected_status.tweak('A', wc_rev=5)
7819 expected_status.tweak('A/mu', wc_rev=5)
7820 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
7821 expected_status, None, wc_dir)
7822 tweaked_27th_line = tweaked_17th_line.replace('line27', 'LINE 27')
7823 svntest.main.file_write(mu_path, tweaked_27th_line)
7824 expected_status.tweak('A/mu', wc_rev=6)
7825 expected_output = wc.State(wc_dir, {'A/mu' : Item(verb='Sending')})
7826 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
7827 expected_status, None, wc_dir)
7828 # Merge r5 to A_COPY/mu
7829 svntest.actions.run_and_verify_svn(None,
7830 expected_merge_output([[5]],
7831 ['U ' + A_COPY_mu_path + '\n']),
7833 'merge', '-r4:5',
7834 A_mu_url,
7835 A_COPY_mu_path)
7837 short_A_COPY = shorten_path_kludge(A_COPY_path)
7838 saved_cwd = os.getcwd()
7839 os.chdir(svntest.main.work_dir)
7840 expected_skip = wc.State(short_A_COPY, {})
7841 expected_output = wc.State(short_A_COPY, {
7842 '' : Item(status=' U'),
7843 'mu' : Item(status='G '),
7845 expected_status = wc.State(short_A_COPY, {
7846 '' : Item(status=' M', wc_rev=4),
7847 'mu' : Item(status='M ', wc_rev=4),
7848 'C' : Item(status=' ', wc_rev=4),
7849 'D' : Item(status=' ', wc_rev=4),
7850 'B' : Item(status=' ', wc_rev=4),
7851 'B/lambda' : Item(status=' ', wc_rev=4),
7852 'B/E' : Item(status=' ', wc_rev=4),
7853 'B/E/alpha': Item(status=' ', wc_rev=4),
7854 'B/E/beta' : Item(status=' ', wc_rev=4),
7855 'B/F' : Item(status=' ', wc_rev=4),
7856 'D/gamma' : Item(status=' ', wc_rev=4),
7857 'D/G' : Item(status=' ', wc_rev=4),
7858 'D/G/pi' : Item(status=' ', wc_rev=4),
7859 'D/G/rho' : Item(status=' ', wc_rev=4),
7860 'D/G/tau' : Item(status=' ', wc_rev=4),
7861 'D/H' : Item(status=' ', wc_rev=4),
7862 'D/H/chi' : Item(status=' ', wc_rev=4),
7863 'D/H/omega': Item(status=' ', wc_rev=4),
7864 'D/H/psi' : Item(status=' ', wc_rev=4),
7866 expected_disk = wc.State('', {
7867 '' : Item(props={SVN_PROP_MERGEINFO : '/A:4-6',
7868 'prop1' : 'val1'}),
7869 'mu' : Item(tweaked_27th_line),
7870 'C' : Item(),
7871 'D' : Item(),
7872 'B' : Item(),
7873 'B/lambda' : Item("This is the file 'lambda'.\n"),
7874 'B/E' : Item(),
7875 'B/E/alpha': Item("This is the file 'alpha'.\n"),
7876 'B/E/beta' : Item("This is the file 'beta'.\n"),
7877 'B/F' : Item(),
7878 'D/gamma' : Item("This is the file 'gamma'.\n"),
7879 'D/G' : Item(),
7880 'D/G/pi' : Item("This is the file 'pi'.\n"),
7881 'D/G/rho' : Item("This is the file 'rho'.\n"),
7882 'D/G/tau' : Item("This is the file 'tau'.\n"),
7883 'D/H' : Item(),
7884 'D/H/chi' : Item("This is the file 'chi'.\n"),
7885 'D/H/omega': Item("This is the file 'omega'.\n"),
7886 'D/H/psi' : Item("This is the file 'psi'.\n"),
7888 svntest.actions.run_and_verify_merge(short_A_COPY, '3', '6',
7889 A_url,
7890 expected_output,
7891 expected_disk,
7892 expected_status,
7893 expected_skip,
7894 None, None, None, None, None, 1)
7895 os.chdir(saved_cwd)
7896 # Revert r5 and r6 on A_COPY/mu
7897 svntest.actions.run_and_verify_svn(None,
7898 expected_merge_output([[6,5]],
7899 ['G ' + A_COPY_mu_path + '\n']),
7901 'merge', '-r6:4',
7902 A_mu_url,
7903 A_COPY_mu_path)
7905 expected_output = wc.State(short_A_COPY, {
7906 '' : Item(status=' G'), # merged removal of prop1 property
7907 'mu' : Item(status='G '), # merged reversion of text changes
7910 expected_status.tweak('', status=' ')
7911 expected_status.tweak('mu', status=' ')
7912 expected_disk.tweak('', props={})
7913 expected_disk.remove('')
7914 expected_disk.tweak('mu', contents=thirty_line_dummy_text)
7915 os.chdir(svntest.main.work_dir)
7916 svntest.actions.run_and_verify_merge(short_A_COPY, '6', '3',
7917 A_url,
7918 expected_output,
7919 expected_disk,
7920 expected_status,
7921 expected_skip,
7922 None, None, None, None, None, 1)
7923 os.chdir(saved_cwd)
7925 expected_disk.add({'' : Item(props={SVN_PROP_MERGEINFO : '/A:4-6',
7926 'prop1' : 'val1'})})
7927 expected_disk.tweak('mu', contents=tweaked_27th_line)
7928 expected_output = wc.State(short_A_COPY, {
7929 '' : Item(status=' U'), # new mergeinfo and prop1 property
7930 'mu' : Item(status='U '), # text changes
7932 expected_status.tweak('', status=' M')
7933 expected_status.tweak('mu', status='M ')
7934 os.chdir(svntest.main.work_dir)
7935 svntest.actions.run_and_verify_merge(short_A_COPY, '3', '6',
7936 A_url,
7937 expected_output,
7938 expected_disk,
7939 expected_status,
7940 expected_skip,
7941 None, None, None, None, None, 1)
7942 os.chdir(saved_cwd)
7943 #Revert r5 on A_COPY/mu
7944 svntest.actions.run_and_verify_svn(None,
7945 expected_merge_output([[-5]],
7946 ['G ' + A_COPY_mu_path + '\n']),
7948 'merge', '-r5:4',
7949 A_mu_url,
7950 A_COPY_mu_path)
7951 tweaked_17th_line_1 = tweaked_27th_line.replace('LINE 17',
7952 'some other line17')
7953 tweaked_17th_line_2 = thirty_line_dummy_text.replace('line17',
7954 'some other line17')
7955 svntest.main.file_write(A_COPY_mu_path, tweaked_17th_line_1)
7956 expected_output = wc.State(short_A_COPY, {
7957 '' : Item(status=' G'),
7958 'mu' : Item(status='G '),
7960 expected_status.tweak('', status=' ')
7961 expected_status.tweak('mu', status='M ')
7962 expected_disk.remove('')
7963 expected_disk.tweak('mu', contents=tweaked_17th_line_2)
7964 os.chdir(svntest.main.work_dir)
7965 svntest.actions.run_and_verify_merge(short_A_COPY, '6', '3',
7966 A_url,
7967 expected_output,
7968 expected_disk,
7969 expected_status,
7970 expected_skip,
7971 None, None, None, None, None, 1)
7972 os.chdir(saved_cwd)
7974 def merge_old_and_new_revs_from_renamed_file(sbox):
7975 "merge -rold(before rename):head renamed file"
7977 ## See http://svn.haxx.se/dev/archive-2007-09/0706.shtml ##
7979 # Create a WC
7980 sbox.build()
7981 wc_dir = sbox.wc_dir
7983 # Some paths we'll care about
7984 mu_url = sbox.repo_url + '/A/mu'
7985 mu_MOVED_url = sbox.repo_url + '/A/mu_MOVED'
7986 mu_COPY_url = sbox.repo_url + '/A/mu_COPY'
7987 mu_COPY_path = os.path.join(wc_dir, 'A', 'mu_COPY')
7988 mu_path = os.path.join(wc_dir, 'A', 'mu')
7989 mu_MOVED_path = os.path.join(wc_dir, 'A', 'mu_MOVED')
7991 # Copy mu to mu_COPY
7992 svntest.actions.run_and_verify_svn(None, ['\n', 'Committed revision 2.\n'],
7993 [], 'cp', '-m', 'cp mu to mu_COPY',
7994 mu_url, mu_COPY_url)
7996 # Make a modification to A/mu
7997 svntest.main.file_write(mu_path, "This is the file 'mu' modified.\n")
7998 expected_output = wc.State(wc_dir, {'A/mu' : Item(verb='Sending')})
7999 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
8000 expected_status.tweak('A/mu', wc_rev=3)
8001 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
8002 expected_status, None, wc_dir)
8004 # Move mu to mu_MOVED
8005 svntest.actions.run_and_verify_svn(None, ['\n', 'Committed revision 4.\n'],
8006 [], 'mv', '-m', 'mv mu to mu_MOVED',
8007 mu_url, mu_MOVED_url)
8009 # Update the working copy to get mu_MOVED
8010 svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
8012 # Make a modification to mu_MOVED
8013 svntest.main.file_write(mu_MOVED_path, "This is 'mu' in mu_MOVED.\n")
8014 expected_output = wc.State(wc_dir, {'A/mu_MOVED' : Item(verb='Sending')})
8015 expected_status = svntest.actions.get_virginal_state(wc_dir, 4)
8016 expected_status.remove('A/mu')
8017 expected_status.add({
8018 'A/mu_MOVED' : Item(status=' ', wc_rev=5),
8019 'A/mu_COPY' : Item(status=' ', wc_rev=4),
8021 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
8022 expected_status, None, wc_dir)
8024 # Merge A/mu_MOVED to A/mu_COPY - this happens in multiple passes
8025 # because A/mu_MOVED has renames in its history between the
8026 # boundaries of the requested merge range.
8027 expected_output = expected_merge_output([[2,3],[4,5]],
8028 ['U %s\n' % (mu_COPY_path),
8029 'G %s\n' % (mu_COPY_path)])
8030 svntest.actions.run_and_verify_svn(None, expected_output,
8031 [], 'merge', '-r', '1:5',
8032 mu_MOVED_url,
8033 mu_COPY_path)
8034 svntest.actions.run_and_verify_svn(None, ['/A/mu:2-3\n',
8035 '/A/mu_MOVED:4-5\n'],
8036 [], 'propget', SVN_PROP_MERGEINFO,
8037 mu_COPY_path)
8040 def merge_with_auto_rev_range_detection(sbox):
8041 "merge with auto detection of revision ranges"
8043 ## See http://svn.haxx.se/dev/archive-2007-09/0735.shtml ##
8045 # Create a WC
8046 sbox.build()
8047 wc_dir = sbox.wc_dir
8049 # Some paths we'll care about
8050 A_url = sbox.repo_url + '/A'
8051 A_COPY_url = sbox.repo_url + '/A_COPY'
8052 B1_path = os.path.join(wc_dir, 'A', 'B1')
8053 B1_mu_path = os.path.join(wc_dir, 'A', 'B1', 'mu')
8054 A_COPY_path = os.path.join(wc_dir, 'A_COPY')
8056 # Create B1 inside A
8057 svntest.actions.run_and_verify_svn(None, ["A " + B1_path + "\n"],
8058 [], 'mkdir',
8059 B1_path)
8061 # Add a file mu inside B1
8062 svntest.main.file_write(B1_mu_path, "This is the file 'mu'.\n")
8063 svntest.actions.run_and_verify_svn(None, ["A " + B1_mu_path + "\n"],
8064 [], 'add', B1_mu_path)
8066 # Commit B1 and B1/mu
8067 expected_output = wc.State(wc_dir, {
8068 'A/B1' : Item(verb='Adding'),
8069 'A/B1/mu' : Item(verb='Adding'),
8071 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
8072 expected_status.add({
8073 'A/B1' : Item(status=' ', wc_rev=2),
8074 'A/B1/mu' : Item(status=' ', wc_rev=2),
8076 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
8077 expected_status, None, wc_dir)
8079 # Copy A to A_COPY
8080 svntest.actions.run_and_verify_svn(None, ['\n', 'Committed revision 3.\n'],
8081 [], 'cp', '-m', 'cp A to A_COPY',
8082 A_url, A_COPY_url)
8084 # Make a modification to A/B1/mu
8085 svntest.main.file_write(B1_mu_path, "This is the file 'mu' modified.\n")
8086 expected_output = wc.State(wc_dir, {'A/B1/mu' : Item(verb='Sending')})
8087 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
8088 expected_status.add({
8089 'A/B1' : Item(status=' ', wc_rev=2),
8090 'A/B1/mu' : Item(status=' ', wc_rev=4),
8092 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
8093 expected_status, None, wc_dir)
8095 # Update the working copy to get A_COPY
8096 svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
8098 short_A_COPY = shorten_path_kludge(A_COPY_path)
8099 saved_cwd = os.getcwd()
8100 os.chdir(svntest.main.work_dir)
8102 # Merge /A to /A_COPY
8103 expected_output = wc.State(short_A_COPY, {
8104 'B1/mu' : Item(status='U '),
8106 expected_status = wc.State(short_A_COPY, {
8107 '' : Item(status=' M', wc_rev=4),
8108 'mu' : Item(status=' ', wc_rev=4),
8109 'C' : Item(status=' ', wc_rev=4),
8110 'D' : Item(status=' ', wc_rev=4),
8111 'B' : Item(status=' ', wc_rev=4),
8112 'B/lambda' : Item(status=' ', wc_rev=4),
8113 'B/E' : Item(status=' ', wc_rev=4),
8114 'B/E/alpha': Item(status=' ', wc_rev=4),
8115 'B/E/beta' : Item(status=' ', wc_rev=4),
8116 'B/F' : Item(status=' ', wc_rev=4),
8117 'B1' : Item(status=' ', wc_rev=4),
8118 'B1/mu' : Item(status='M ', wc_rev=4),
8119 'D/gamma' : Item(status=' ', wc_rev=4),
8120 'D/G' : Item(status=' ', wc_rev=4),
8121 'D/G/pi' : Item(status=' ', wc_rev=4),
8122 'D/G/rho' : Item(status=' ', wc_rev=4),
8123 'D/G/tau' : Item(status=' ', wc_rev=4),
8124 'D/H' : Item(status=' ', wc_rev=4),
8125 'D/H/chi' : Item(status=' ', wc_rev=4),
8126 'D/H/omega': Item(status=' ', wc_rev=4),
8127 'D/H/psi' : Item(status=' ', wc_rev=4),
8129 expected_disk = wc.State('', {
8130 '' : Item(props={SVN_PROP_MERGEINFO : '/A:3-4'}),
8131 'mu' : Item("This is the file 'mu'.\n"),
8132 'C' : Item(),
8133 'D' : Item(),
8134 'B' : Item(),
8135 'B/lambda' : Item("This is the file 'lambda'.\n"),
8136 'B/E' : Item(),
8137 'B/E/alpha': Item("This is the file 'alpha'.\n"),
8138 'B/E/beta' : Item("This is the file 'beta'.\n"),
8139 'B/F' : Item(),
8140 'B1' : Item(),
8141 'B1/mu' : Item("This is the file 'mu' modified.\n"),
8142 'D/gamma' : Item("This is the file 'gamma'.\n"),
8143 'D/G' : Item(),
8144 'D/G/pi' : Item("This is the file 'pi'.\n"),
8145 'D/G/rho' : Item("This is the file 'rho'.\n"),
8146 'D/G/tau' : Item("This is the file 'tau'.\n"),
8147 'D/H' : Item(),
8148 'D/H/chi' : Item("This is the file 'chi'.\n"),
8149 'D/H/omega': Item("This is the file 'omega'.\n"),
8150 'D/H/psi' : Item("This is the file 'psi'.\n"),
8152 expected_skip = wc.State(short_A_COPY, {})
8153 svntest.actions.run_and_verify_merge(short_A_COPY, None, None,
8154 A_url,
8155 expected_output,
8156 expected_disk,
8157 expected_status,
8158 expected_skip,
8159 None, None, None, None, None,
8160 1, 1)
8161 os.chdir(saved_cwd)
8163 def mergeinfo_recording_in_skipped_merge(sbox):
8164 "mergeinfo recording in skipped merge"
8166 ## See http://subversion.tigris.org/issues/show_bug.cgi?id=2829. ##
8168 # Create a WC with a single branch
8169 sbox.build()
8170 wc_dir = sbox.wc_dir
8171 wc_disk, wc_status = set_up_branch(sbox, True, 1)
8173 # Some paths we'll care about
8174 A_url = sbox.repo_url + '/A'
8175 A_COPY_path = os.path.join(wc_dir, 'A_COPY')
8176 mu_path = os.path.join(wc_dir, 'A', 'mu')
8177 alpha_path = os.path.join(wc_dir, 'A', 'B', 'E', 'alpha')
8178 A_COPY_B_E_path = os.path.join(wc_dir, 'A_COPY', 'B', 'E')
8179 A_COPY_alpha_path = os.path.join(wc_dir, 'A_COPY', 'B', 'E', 'alpha')
8180 A_COPY_beta_path = os.path.join(wc_dir, 'A_COPY', 'B', 'E', 'beta')
8182 # Make a modification to A/mu
8183 svntest.main.file_write(mu_path, "This is the file 'mu' modified.\n")
8184 expected_output = wc.State(wc_dir, {'A/mu' : Item(verb='Sending')})
8185 wc_status.add({'A/mu' : Item(status=' ', wc_rev=3)})
8186 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
8187 wc_status, None, wc_dir)
8189 # Make a modification to A/B/E/alpha
8190 svntest.main.file_write(alpha_path, "This is the file 'alpha' modified.\n")
8191 expected_output = wc.State(wc_dir, {'A/B/E/alpha' : Item(verb='Sending')})
8192 wc_status.add({'A/B/E/alpha' : Item(status=' ', wc_rev=4)})
8193 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
8194 wc_status, None, wc_dir)
8196 # Delete A_COPY/B/E
8197 svntest.actions.run_and_verify_svn(None, None, [], 'rm',
8198 A_COPY_B_E_path)
8200 short_A_COPY = shorten_path_kludge(A_COPY_path)
8201 saved_cwd = os.getcwd()
8202 os.chdir(svntest.main.work_dir)
8204 # Merge /A to /A_COPY ie., r1 to r4
8205 expected_output = wc.State(short_A_COPY, {
8206 'mu' : Item(status='U '),
8208 expected_status = wc.State(short_A_COPY, {
8209 '' : Item(status=' M', wc_rev=2),
8210 'mu' : Item(status='M ', wc_rev=2),
8211 'B' : Item(status=' ', wc_rev=2),
8212 'B/lambda' : Item(status=' ', wc_rev=2),
8213 'B/F' : Item(status=' ', wc_rev=2),
8214 'B/E' : Item(status='D ', wc_rev=2),
8215 'B/E/alpha': Item(status='D ', wc_rev=2),
8216 'B/E/beta' : Item(status='D ', wc_rev=2),
8217 'C' : Item(status=' ', wc_rev=2),
8218 'D' : Item(status=' ', wc_rev=2),
8219 'D/gamma' : Item(status=' ', wc_rev=2),
8220 'D/G' : Item(status=' ', wc_rev=2),
8221 'D/G/pi' : Item(status=' ', wc_rev=2),
8222 'D/G/rho' : Item(status=' ', wc_rev=2),
8223 'D/G/tau' : Item(status=' ', wc_rev=2),
8224 'D/H' : Item(status=' ', wc_rev=2),
8225 'D/H/chi' : Item(status=' ', wc_rev=2),
8226 'D/H/omega': Item(status=' ', wc_rev=2),
8227 'D/H/psi' : Item(status=' ', wc_rev=2),
8229 expected_disk = wc.State('', {
8230 '' : Item(props={SVN_PROP_MERGEINFO : '/A:2-4'}),
8231 'mu' : Item("This is the file 'mu' modified.\n"),
8232 'C' : Item(),
8233 'D' : Item(),
8234 'B' : Item(),
8235 'B/lambda' : Item(contents="This is the file 'lambda'.\n"),
8236 'B/F' : Item(),
8237 'B/E' : Item(),
8238 'D/gamma' : Item("This is the file 'gamma'.\n"),
8239 'D/G' : Item(),
8240 'D/G/pi' : Item("This is the file 'pi'.\n"),
8241 'D/G/rho' : Item("This is the file 'rho'.\n"),
8242 'D/G/tau' : Item("This is the file 'tau'.\n"),
8243 'D/H' : Item(),
8244 'D/H/chi' : Item("This is the file 'chi'.\n"),
8245 'D/H/omega': Item("This is the file 'omega'.\n"),
8246 'D/H/psi' : Item("This is the file 'psi'.\n"),
8248 expected_skip = wc.State(short_A_COPY, {
8249 'B/E/alpha' : Item(),
8251 svntest.actions.run_and_verify_merge(short_A_COPY, None, None,
8252 A_url,
8253 expected_output,
8254 expected_disk,
8255 expected_status,
8256 expected_skip,
8257 None, None, None, None, None,
8258 1, 1)
8260 os.chdir(saved_cwd)
8262 # Test for issue 2818: Provide a 'merge' API which allows for merging of
8263 # arbitrary revision ranges (e.g. '-c 3,5,7')
8264 def cherry_picking(sbox):
8265 "command line supports cherry picked merge ranges"
8267 sbox.build()
8268 wc_dir = sbox.wc_dir
8269 wc_disk, wc_status = set_up_branch(sbox)
8271 # Some paths we'll care about
8272 H_path = os.path.join(wc_dir, "A", "D", "H")
8273 G_path = os.path.join(wc_dir, "A", "D", "G")
8274 A_COPY_path = os.path.join(wc_dir, "A_COPY")
8275 D_COPY_path = os.path.join(wc_dir, "A_COPY", "D")
8276 G_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "G")
8277 H_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "H")
8278 rho_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "G", "rho")
8279 omega_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "H", "omega")
8280 psi_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "H", "psi")
8282 # Update working copy
8283 expected_output = svntest.wc.State(wc_dir, {})
8284 wc_status.tweak(wc_rev='6')
8285 svntest.actions.run_and_verify_update(wc_dir, expected_output,
8286 wc_disk, wc_status,
8287 None, None, None, None, None, True)
8289 # Make some prop changes to some dirs.
8290 svntest.actions.run_and_verify_svn(None,
8291 ["property 'prop:name' set on '" +
8292 G_path + "'\n"], [], 'ps',
8293 'prop:name', 'propval', G_path)
8294 expected_output = svntest.wc.State(wc_dir, {'A/D/G': Item(verb='Sending'),})
8295 wc_status.tweak('A/D/G', wc_rev=7)
8296 wc_disk.tweak('A/D/G', props={'prop:name' : 'propval'})
8298 svntest.actions.run_and_verify_commit(wc_dir, expected_output, wc_status,
8299 None, wc_dir)
8300 svntest.actions.run_and_verify_svn(None,
8301 ["property 'prop:name' set on '" +
8302 H_path + "'\n"], [], 'ps',
8303 'prop:name', 'propval', H_path)
8304 expected_output = svntest.wc.State(wc_dir, {'A/D/H': Item(verb='Sending'),})
8305 wc_status.tweak('A/D/H', wc_rev=8)
8306 wc_disk.tweak('A/D/H', props={'prop:name' : 'propval'})
8307 svntest.actions.run_and_verify_commit(wc_dir, expected_output, wc_status,
8308 None, wc_dir)
8310 # Do multiple additive merges to a file"
8311 # Merge -r2:4 -c6 into A_COPY/D/G/rho.
8312 short_rho_COPY_path = shorten_path_kludge(rho_COPY_path)
8313 expected_skip = wc.State(short_rho_COPY_path, { })
8314 saved_cwd = os.getcwd()
8315 os.chdir(svntest.main.work_dir)
8316 # run_and_verify_merge doesn't support merging to a file WCPATH
8317 # so use run_and_verify_svn.
8318 svntest.actions.run_and_verify_svn(None,
8319 expected_merge_output(
8320 [[3,4],[6]],
8321 'U ' + short_rho_COPY_path + '\n'),
8322 [], 'merge', '-r2:4', '-c6',
8323 sbox.repo_url + '/A/D/G/rho',
8324 short_rho_COPY_path)
8325 os.chdir(saved_cwd)
8327 # Check rho's status and props.
8328 expected_status = wc.State(rho_COPY_path,
8329 {'' : Item(status='MM', wc_rev=6)})
8330 svntest.actions.run_and_verify_status(rho_COPY_path, expected_status)
8331 svntest.actions.run_and_verify_svn(None, ["/A/D/G/rho:3-4,6\n"], [],
8332 'propget', SVN_PROP_MERGEINFO,
8333 rho_COPY_path)
8335 #Do multiple additive merges to a directory:
8336 # Merge -c6 -c8 into A_COPY/D/H
8337 short_H_COPY_path = shorten_path_kludge(H_COPY_path)
8338 short_omega_COPY_path = shorten_path_kludge(omega_COPY_path)
8339 expected_output = expected_merge_output(
8340 [[6],[8]],
8341 ['U ' + short_omega_COPY_path + '\n',
8342 ' U ' + short_H_COPY_path + '\n'])
8343 os.chdir(svntest.main.work_dir)
8344 svntest.actions.run_and_verify_svn(None, expected_output,
8345 [], 'merge', '-c6', '-c8',
8346 sbox.repo_url + '/A/D/H',
8347 short_H_COPY_path)
8348 os.chdir(saved_cwd)
8350 # Check A_COPY/D/H's status and props.
8351 expected_status = wc.State(H_COPY_path,
8352 {'' : Item(status=' M', wc_rev=6),
8353 'psi' : Item(status=' ', wc_rev=6),
8354 'chi' : Item(status=' ', wc_rev=6),
8355 'omega': Item(status='M ', wc_rev=6),})
8356 svntest.actions.run_and_verify_status(H_COPY_path, expected_status)
8357 svntest.actions.run_and_verify_svn(None,
8358 [H_COPY_path + " - /A/D/H:6,8\n"],
8359 [], 'propget', '-R', SVN_PROP_MERGEINFO,
8360 H_COPY_path)
8362 # Do multiple reverse merges to a directory:
8363 # Merge -c-6 -c-3 into A_COPY
8364 short_A_COPY_path = shorten_path_kludge(A_COPY_path)
8365 short_omega_COPY_path = shorten_path_kludge(omega_COPY_path)
8366 expected_output = expected_merge_output(
8367 [[-3],[-6]], 'G ' + short_omega_COPY_path + '\n')
8368 os.chdir(svntest.main.work_dir)
8369 svntest.actions.run_and_verify_svn(None, expected_output,
8370 [], 'merge', '-c-3', '-c-6',
8371 sbox.repo_url + '/A',
8372 short_A_COPY_path)
8373 os.chdir(saved_cwd)
8374 expected_status = wc.State(A_COPY_path,
8375 {'' : Item(status=' ', wc_rev=6),
8376 'B' : Item(status=' ', wc_rev=6),
8377 'B/lambda' : Item(status=' ', wc_rev=6),
8378 'B/E' : Item(status=' ', wc_rev=6),
8379 'B/E/alpha' : Item(status=' ', wc_rev=6),
8380 'B/E/beta' : Item(status=' ', wc_rev=6),
8381 'B/F' : Item(status=' ', wc_rev=6),
8382 'mu' : Item(status=' ', wc_rev=6),
8383 'C' : Item(status=' ', wc_rev=6),
8384 'D' : Item(status=' ', wc_rev=6),
8385 'D/gamma' : Item(status=' ', wc_rev=6),
8386 'D/G' : Item(status=' ', wc_rev=6),
8387 'D/G/pi' : Item(status=' ', wc_rev=6),
8388 'D/G/rho' : Item(status='MM', wc_rev=6),
8389 'D/G/tau' : Item(status=' ', wc_rev=6),
8390 'D/H' : Item(status=' M', wc_rev=6),
8391 'D/H/chi' : Item(status=' ', wc_rev=6),
8392 'D/H/psi' : Item(status=' ', wc_rev=6),
8393 'D/H/omega' : Item(status=' ', wc_rev=6),})
8394 svntest.actions.run_and_verify_status(A_COPY_path, expected_status)
8395 expected_out = H_COPY_path + " - /A/D/H:8\n|" + \
8396 rho_COPY_path + " - /A/D/G/rho:4\n"
8397 # Construct proper regex for '\' infested Windows paths.
8398 if sys.platform == 'win32':
8399 expected_out = expected_out.replace("\\", "\\\\")
8400 svntest.actions.run_and_verify_svn(None, expected_out, [],
8401 'propget', '-R', SVN_PROP_MERGEINFO,
8402 A_COPY_path)
8404 # Do both additive and reverse merges to a directory:
8405 # Merge -r2:3 -c-4 -r4:7 to A_COPY/D
8406 short_D_COPY_path = shorten_path_kludge(D_COPY_path)
8407 short_psi_COPY_path = shorten_path_kludge(psi_COPY_path)
8408 short_G_COPY_path = shorten_path_kludge(G_COPY_path)
8409 expected_output = expected_merge_output(
8410 [[3],[-4], [5,7]],
8411 [' U ' + short_G_COPY_path + '\n',
8412 'U ' + short_omega_COPY_path + '\n',
8413 'U ' + short_psi_COPY_path + '\n',
8414 'G ' + short_rho_COPY_path + '\n'])
8416 os.chdir(svntest.main.work_dir)
8417 svntest.actions.run_and_verify_svn(None, expected_output, [], 'merge',
8418 '-r2:3', '-c-4', '-r4:7',
8419 sbox.repo_url + '/A/D',
8420 short_D_COPY_path)
8421 os.chdir(saved_cwd)
8422 expected_status = wc.State(D_COPY_path,
8423 {'' : Item(status=' M', wc_rev=6),
8424 'gamma' : Item(status=' ', wc_rev=6),
8425 'G' : Item(status=' M', wc_rev=6),
8426 'G/pi' : Item(status=' ', wc_rev=6),
8427 'G/rho' : Item(status=' ', wc_rev=6),
8428 'G/tau' : Item(status=' ', wc_rev=6),
8429 'H' : Item(status=' M', wc_rev=6),
8430 'H/chi' : Item(status=' ', wc_rev=6),
8431 'H/psi' : Item(status='M ', wc_rev=6),
8432 'H/omega' : Item(status='M ', wc_rev=6),})
8433 svntest.actions.run_and_verify_status(D_COPY_path, expected_status)
8434 expected_out = D_COPY_path + " - /A/D:3,5-7\n|" + \
8435 H_COPY_path + " - /A/D/H:3,5-8\n"
8436 # Construct proper regex for '\' infested Windows paths.
8437 if sys.platform == 'win32':
8438 expected_out = expected_out.replace("\\", "\\\\")
8439 svntest.actions.run_and_verify_svn(None, expected_out, [],
8440 'propget', '-R', SVN_PROP_MERGEINFO,
8441 D_COPY_path)
8443 def propchange_of_subdir_raises_conflict(sbox):
8444 "merge of propchange on subdir raises conflict"
8446 ## See http://subversion.tigris.org/issues/show_bug.cgi?id=2969. ##
8448 # Create a WC with a single branch
8449 sbox.build()
8450 wc_dir = sbox.wc_dir
8451 wc_disk, wc_status = set_up_branch(sbox, True, 1)
8453 # Some paths we'll care about
8454 B_url = sbox.repo_url + '/A/B'
8455 E_path = os.path.join(wc_dir, 'A', 'B', 'E')
8456 lambda_path = os.path.join(wc_dir, 'A', 'B', 'lambda')
8457 A_COPY_B_path = os.path.join(wc_dir, 'A_COPY', 'B')
8458 A_COPY_B_E_path = os.path.join(wc_dir, 'A_COPY', 'B', 'E')
8459 A_COPY_lambda_path = os.path.join(wc_dir, 'A_COPY', 'B', 'E', 'lambda')
8461 # Set a property on A/B/E and Make a modification to A/B/lambda
8462 svntest.main.run_svn(None, 'propset', 'x', 'x', E_path)
8464 svntest.main.file_write(lambda_path, "This is the file 'lambda' modified.\n")
8465 expected_output = wc.State(wc_dir, {
8466 'A/B/lambda' : Item(verb='Sending'),
8467 'A/B/E' : Item(verb='Sending'),
8469 wc_status.add({
8470 'A/B/lambda' : Item(status=' ', wc_rev=3),
8471 'A/B/E' : Item(status=' ', wc_rev=3),
8473 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
8474 wc_status, None, wc_dir)
8476 short_A_COPY_B = shorten_path_kludge(A_COPY_B_path)
8477 saved_cwd = os.getcwd()
8478 os.chdir(svntest.main.work_dir)
8480 # Merge /A/B to /A_COPY/B ie., r1 to r3 with depth files
8481 expected_output = wc.State(short_A_COPY_B, {
8482 'lambda' : Item(status='U '),
8484 expected_disk = wc.State('', {
8485 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B:2-3*'}),
8486 'lambda' : Item(contents="This is the file 'lambda' modified.\n",
8487 props={SVN_PROP_MERGEINFO : '/A/B/lambda:2-3'}),
8488 'F' : Item(),
8489 'E' : Item(),
8490 'E/alpha' : Item(contents="This is the file 'alpha'.\n"),
8491 'E/beta' : Item(contents="This is the file 'beta'.\n"),
8493 expected_status = wc.State(short_A_COPY_B, {
8494 '' : Item(status=' M', wc_rev=2),
8495 'lambda' : Item(status='MM', wc_rev=2),
8496 'F' : Item(status=' ', wc_rev=2),
8497 'E' : Item(status=' ', wc_rev=2),
8498 'E/alpha' : Item(status=' ', wc_rev=2),
8499 'E/beta' : Item(status=' ', wc_rev=2),
8501 expected_skip = wc.State(short_A_COPY_B, {})
8503 svntest.actions.run_and_verify_merge(short_A_COPY_B, None, None,
8504 B_url,
8505 expected_output,
8506 expected_disk,
8507 expected_status,
8508 expected_skip,
8509 None, None, None, None, None,
8510 1, 1, '--depth', 'files')
8512 # Merge /A/B to /A_COPY/B ie., r1 to r3 with infinite depth
8513 expected_output = wc.State(short_A_COPY_B, {
8514 'E' : Item(status=' U'),
8516 expected_disk = wc.State('', {
8517 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B:2-3'}),
8518 'lambda' : Item(contents="This is the file 'lambda' modified.\n"),
8519 'F' : Item(),
8520 'E' : Item(props={'x': 'x'}),
8521 'E/alpha' : Item(contents="This is the file 'alpha'.\n"),
8522 'E/beta' : Item(contents="This is the file 'beta'.\n"),
8524 expected_status = wc.State(short_A_COPY_B, {
8525 '' : Item(status=' M', wc_rev=2),
8526 'lambda' : Item(status='M ', wc_rev=2),
8527 'F' : Item(status=' ', wc_rev=2),
8528 'E' : Item(status=' M', wc_rev=2),
8529 'E/alpha' : Item(status=' ', wc_rev=2),
8530 'E/beta' : Item(status=' ', wc_rev=2),
8533 svntest.actions.run_and_verify_merge(short_A_COPY_B, None, None,
8534 B_url,
8535 expected_output,
8536 expected_disk,
8537 expected_status,
8538 expected_skip,
8539 None, None, None, None, None,
8540 1, 1)
8542 os.chdir(saved_cwd)
8544 # Test for issue #2971: Reverse merge of prop add segfaults if
8545 # merging to parent of first merge
8546 def reverse_merge_prop_add_on_child(sbox):
8547 "reverse merge of prop add on child"
8549 sbox.build()
8550 wc_dir = sbox.wc_dir
8551 wc_disk, wc_status = set_up_branch(sbox, True, 1)
8553 # Some paths we'll care about
8554 G_path = os.path.join(wc_dir, "A", "D", "G")
8555 D_COPY_path = os.path.join(wc_dir, "A_COPY", "D")
8556 G_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "G")
8558 # Make some prop changes to some dirs.
8559 svntest.actions.run_and_verify_svn(None,
8560 ["property 'prop:name' set on '" +
8561 G_path + "'\n"], [], 'ps',
8562 'prop:name', 'propval', G_path)
8563 expected_output = svntest.wc.State(wc_dir, {'A/D/G': Item(verb='Sending'),})
8564 wc_status.tweak('A/D/G', wc_rev=3)
8565 wc_disk.tweak('A/D/G', props={'prop:name' : 'propval'})
8567 svntest.actions.run_and_verify_commit(wc_dir, expected_output, wc_status,
8568 None, wc_dir)
8570 # Merge -c3's prop add to A_COPY/D/G
8571 saved_cwd = os.getcwd()
8572 short_G_COPY_path = shorten_path_kludge(G_COPY_path)
8573 expected_output = wc.State(short_G_COPY_path, {
8574 '' : Item(status=' U')
8576 expected_status = wc.State(short_G_COPY_path, {
8577 '' : Item(status=' M', wc_rev=2),
8578 'pi' : Item(status=' ', wc_rev=2),
8579 'rho' : Item(status=' ', wc_rev=2),
8580 'tau' : Item(status=' ', wc_rev=2),
8582 expected_disk = wc.State('', {
8583 '' : Item(props={SVN_PROP_MERGEINFO : '/A/D/G:3',
8584 'prop:name' : 'propval'}),
8585 'pi' : Item("This is the file 'pi'.\n"),
8586 'rho' : Item("This is the file 'rho'.\n"),
8587 'tau' : Item("This is the file 'tau'.\n"),
8589 expected_skip = wc.State(short_G_COPY_path, { })
8590 os.chdir(svntest.main.work_dir)
8591 svntest.actions.run_and_verify_merge(short_G_COPY_path, '2', '3',
8592 sbox.repo_url + \
8593 '/A/D/G',
8594 expected_output,
8595 expected_disk,
8596 expected_status,
8597 expected_skip,
8598 None, None, None, None,
8599 None, 1)
8600 os.chdir(saved_cwd)
8602 # Now merge -c-3 but target the previous target's parent instead.
8603 short_D_COPY_path = shorten_path_kludge(D_COPY_path)
8604 expected_output = wc.State(short_D_COPY_path, {
8605 'G' : Item(status=' G'),
8607 expected_status = wc.State(short_D_COPY_path, {
8608 '' : Item(status=' ', wc_rev=2),
8609 'G' : Item(status=' ', wc_rev=2),
8610 'G/pi' : Item(status=' ', wc_rev=2),
8611 'G/rho' : Item(status=' ', wc_rev=2),
8612 'G/tau' : Item(status=' ', wc_rev=2),
8613 'H' : Item(status=' ', wc_rev=2),
8614 'H/chi' : Item(status=' ', wc_rev=2),
8615 'H/psi' : Item(status=' ', wc_rev=2),
8616 'H/omega' : Item(status=' ', wc_rev=2),
8617 'gamma' : Item(status=' ', wc_rev=2),
8619 expected_disk = wc.State('', {
8620 'G' : Item(),
8621 'G/pi' : Item("This is the file 'pi'.\n"),
8622 'G/rho' : Item("This is the file 'rho'.\n"),
8623 'G/tau' : Item("This is the file 'tau'.\n"),
8624 'H' : Item(),
8625 'H/chi' : Item("This is the file 'chi'.\n"),
8626 'H/psi' : Item("This is the file 'psi'.\n"),
8627 'H/omega' : Item("This is the file 'omega'.\n"),
8628 'gamma' : Item("This is the file 'gamma'.\n")
8630 expected_skip = wc.State(short_D_COPY_path, { })
8631 os.chdir(svntest.main.work_dir)
8632 svntest.actions.run_and_verify_merge(short_D_COPY_path, '3', '2',
8633 sbox.repo_url + \
8634 '/A/D',
8635 expected_output,
8636 expected_disk,
8637 expected_status,
8638 expected_skip,
8639 None, None, None, None,
8640 None, 1)
8641 os.chdir(saved_cwd)
8643 def merge_target_with_non_inheritable_mergeinfo(sbox):
8644 "merge target with non inheritable mergeinfo"
8646 ## See http://subversion.tigris.org/issues/show_bug.cgi?id=2970. ##
8648 # Create a WC with a single branch
8649 sbox.build()
8650 wc_dir = sbox.wc_dir
8651 wc_disk, wc_status = set_up_branch(sbox, True, 1)
8653 # Some paths we'll care about
8654 B_url = sbox.repo_url + '/A/B'
8655 lambda_path = os.path.join(wc_dir, 'A', 'B', 'lambda')
8656 newfile_path = os.path.join(wc_dir, 'A', 'B', 'E', 'newfile')
8657 A_COPY_B_path = os.path.join(wc_dir, 'A_COPY', 'B')
8659 # Make a modifications to A/B/lambda and add A/B/E/newfile
8660 svntest.main.file_write(lambda_path, "This is the file 'lambda' modified.\n")
8661 svntest.main.file_write(newfile_path, "This is the file 'newfile'.\n")
8662 svntest.actions.run_and_verify_svn(None, None, [], 'add', newfile_path)
8663 expected_output = wc.State(wc_dir, {
8664 'A/B/lambda' : Item(verb='Sending'),
8665 'A/B/E/newfile' : Item(verb='Adding'),
8667 wc_status.add({
8668 'A/B/lambda' : Item(status=' ', wc_rev=3),
8669 'A/B/E/newfile' : Item(status=' ', wc_rev=3),
8671 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
8672 wc_status, None, wc_dir)
8674 short_A_COPY_B = shorten_path_kludge(A_COPY_B_path)
8675 saved_cwd = os.getcwd()
8676 os.chdir(svntest.main.work_dir)
8678 # Merge /A/B to /A_COPY/B ie., r1 to r3 with depth immediates
8679 expected_output = wc.State(short_A_COPY_B, {
8680 'lambda' : Item(status='U '),
8682 expected_disk = wc.State('', {
8683 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B:1-3'}),
8684 'lambda' : Item(contents="This is the file 'lambda' modified.\n"),
8685 'F' : Item(props={SVN_PROP_MERGEINFO : '/A/B/F:1,2-3*'}),
8686 'E' : Item(props={SVN_PROP_MERGEINFO : '/A/B/E:1,2-3*'}),
8687 'E/alpha' : Item(contents="This is the file 'alpha'.\n"),
8688 'E/beta' : Item(contents="This is the file 'beta'.\n"),
8690 expected_status = wc.State(short_A_COPY_B, {
8691 '' : Item(status=' M', wc_rev=2),
8692 'lambda' : Item(status='M ', wc_rev=2),
8693 'F' : Item(status=' M', wc_rev=2),
8694 'E' : Item(status=' M', wc_rev=2),
8695 'E/alpha' : Item(status=' ', wc_rev=2),
8696 'E/beta' : Item(status=' ', wc_rev=2),
8698 expected_skip = wc.State(short_A_COPY_B, {})
8700 svntest.actions.run_and_verify_merge(short_A_COPY_B, None, None,
8701 B_url,
8702 expected_output,
8703 expected_disk,
8704 expected_status,
8705 expected_skip,
8706 None, None, None, None, None,
8707 1, 1, '--depth', 'immediates')
8709 # Merge /A/B to /A_COPY/B ie., r1 to r3 with infinite depth
8710 expected_output = wc.State(short_A_COPY_B, {
8711 'E/newfile' : Item(status='A '),
8713 expected_disk = wc.State('', {
8714 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B:1-3'}),
8715 'lambda' : Item(contents="This is the file 'lambda' modified.\n"),
8716 'F' : Item(),
8717 'E' : Item(),
8718 'E/alpha' : Item(contents="This is the file 'alpha'.\n"),
8719 'E/beta' : Item(contents="This is the file 'beta'.\n"),
8720 'E/newfile' : Item(contents="This is the file 'newfile'.\n"),
8722 expected_status = wc.State(short_A_COPY_B, {
8723 '' : Item(status=' M', wc_rev=2),
8724 'lambda' : Item(status='M ', wc_rev=2),
8725 'F' : Item(status=' ', wc_rev=2),
8726 'E' : Item(status=' ', wc_rev=2),
8727 'E/alpha' : Item(status=' ', wc_rev=2),
8728 'E/beta' : Item(status=' ', wc_rev=2),
8729 'E/newfile' : Item(status='A ', wc_rev=2),
8732 svntest.actions.run_and_verify_merge(short_A_COPY_B, None, None,
8733 B_url,
8734 expected_output,
8735 expected_disk,
8736 expected_status,
8737 expected_skip,
8738 None, None, None, None, None,
8739 1, 1)
8741 os.chdir(saved_cwd)
8743 def self_reverse_merge(sbox):
8744 "revert a commit on a target"
8746 sbox.build()
8747 wc_dir = sbox.wc_dir
8749 # Make changes to the working copy
8750 mu_path = os.path.join(wc_dir, 'A', 'mu')
8751 svntest.main.file_append(mu_path, 'appended mu text')
8753 # Created expected output tree for 'svn ci'
8754 expected_output = wc.State(wc_dir, {
8755 'A/mu' : Item(verb='Sending'),
8758 # Create expected status tree; all local revisions should be at 1,
8759 # but mu should be at revision 2.
8760 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
8761 expected_status.tweak('A/mu', wc_rev=2)
8763 svntest.actions.run_and_verify_commit(wc_dir,
8764 expected_output,
8765 expected_status,
8766 None,
8767 wc_dir)
8769 # update to HEAD so that the to-be-undone revision is found in the
8770 # implicit mergeinfo (the natural history) of the target.
8771 svntest.actions.run_and_verify_svn(None, None, [], 'update', wc_dir)
8773 expected_output = wc.State(wc_dir, {
8774 'A/mu' : Item(status='U ')
8776 expected_skip = wc.State(wc_dir, { })
8777 expected_disk = svntest.main.greek_state.copy()
8778 expected_status = svntest.actions.get_virginal_state(wc_dir, 2)
8779 expected_status.tweak('A/mu', status='M ')
8780 svntest.actions.run_and_verify_merge(wc_dir, '2', '1', sbox.repo_url,
8781 expected_output, expected_disk,
8782 expected_status, expected_skip,
8783 None, None, None, None, None, 1, 1)
8784 svntest.actions.run_and_verify_svn(None, None, [], 'revert', '-R', wc_dir)
8786 # record dummy self mergeinfo to test the fact that self-reversal should work
8787 # irrespective of mergeinfo.
8788 svntest.actions.run_and_verify_svn(None, None, [], 'merge', '-c', '1',
8789 '--record-only', sbox.repo_url, wc_dir)
8791 # Bad svntest.main.greek_state does not have '', so adding it explicitly.
8792 expected_disk.add({'' : Item(props={SVN_PROP_MERGEINFO : '/:1'})})
8793 expected_status = svntest.actions.get_virginal_state(wc_dir, 2)
8794 expected_status.tweak('', status = ' M')
8795 expected_status.tweak('A/mu', status = 'M ')
8796 svntest.actions.run_and_verify_merge(wc_dir, '2', '1', sbox.repo_url,
8797 expected_output, expected_disk,
8798 expected_status, expected_skip,
8799 None, None, None, None, None, 1, 1)
8801 def ignore_ancestry_and_mergeinfo(sbox):
8802 "--ignore-ancestry also ignores mergeinfo"
8804 # Create a WC with a single branch
8805 sbox.build()
8806 wc_dir = sbox.wc_dir
8807 wc_disk, wc_status = set_up_branch(sbox, True, 1)
8809 # Some paths we'll care about
8810 A_B_url = sbox.repo_url + '/A/B'
8811 A_COPY_B_path = os.path.join(wc_dir, 'A_COPY', 'B')
8812 lambda_path = os.path.join(wc_dir, 'A', 'B', 'lambda')
8813 A_COPY_lambda_path = os.path.join(wc_dir, 'A_COPY', 'B', 'lambda')
8815 # Make modifications to A/B/lambda
8816 svntest.main.file_write(lambda_path, "This is the file 'lambda' modified.\n")
8817 expected_output = wc.State(wc_dir, {
8818 'A/B/lambda' : Item(verb='Sending'),
8820 wc_status.add({
8821 'A/B/lambda' : Item(status=' ', wc_rev=3),
8823 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
8824 wc_status, None, wc_dir)
8826 svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
8828 short_A_COPY_B = shorten_path_kludge(A_COPY_B_path)
8829 short_A_COPY_lambda = shorten_path_kludge(A_COPY_lambda_path)
8830 saved_cwd = os.getcwd()
8831 os.chdir(svntest.main.work_dir)
8833 # Merge /A/B to /A_COPY/B ie., r1 to r3 with depth immediates
8834 expected_output = wc.State(short_A_COPY_B, {
8835 'lambda' : Item(status='U '),
8837 expected_disk = wc.State('', {
8838 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B:2-3'}),
8839 'lambda' : Item(contents="This is the file 'lambda' modified.\n"),
8840 'F' : Item(props={}),
8841 'E' : Item(props={}),
8842 'E/alpha' : Item(contents="This is the file 'alpha'.\n"),
8843 'E/beta' : Item(contents="This is the file 'beta'.\n"),
8845 expected_status = wc.State(short_A_COPY_B, {
8846 '' : Item(status=' M', wc_rev=3),
8847 'lambda' : Item(status='M ', wc_rev=3),
8848 'F' : Item(status=' ', wc_rev=3),
8849 'E' : Item(status=' ', wc_rev=3),
8850 'E/alpha' : Item(status=' ', wc_rev=3),
8851 'E/beta' : Item(status=' ', wc_rev=3),
8853 expected_skip = wc.State(short_A_COPY_B, {})
8855 svntest.actions.run_and_verify_merge(short_A_COPY_B, 1, 3,
8856 A_B_url,
8857 expected_output,
8858 expected_disk,
8859 expected_status,
8860 expected_skip,
8861 None, None, None, None, None, 1, 1)
8863 # Now, revert lambda and repeat the merge. Nothing should happen.
8864 svntest.actions.run_and_verify_svn(None, None, [], 'revert', '-R',
8865 short_A_COPY_lambda)
8866 expected_output.remove('lambda')
8867 expected_disk.tweak('lambda', contents="This is the file 'lambda'.\n")
8868 expected_status.tweak('lambda', status=' ')
8869 svntest.actions.run_and_verify_merge(short_A_COPY_B, 1, 3,
8870 A_B_url,
8871 expected_output,
8872 expected_disk,
8873 expected_status,
8874 expected_skip,
8875 None, None, None, None, None, 1, 1)
8877 # Now, try the merge again with --ignore-ancestry. We should get
8878 # lambda re-modified. */
8879 expected_output = wc.State(short_A_COPY_B, {
8880 'lambda' : Item(status='U '),
8882 expected_disk.tweak('lambda',
8883 contents="This is the file 'lambda' modified.\n")
8884 expected_status.tweak('lambda', status='M ')
8885 svntest.actions.run_and_verify_merge(short_A_COPY_B, 1, 3,
8886 A_B_url,
8887 expected_output,
8888 expected_disk,
8889 expected_status,
8890 expected_skip,
8891 None, None, None, None, None, 1, 1,
8892 '--ignore-ancestry')
8894 os.chdir(saved_cwd)
8896 def merge_from_renamed_branch_fails_while_avoiding_repeat_merge(sbox):
8897 "merge from renamed branch"
8898 #Copy A/C to A/COPY_C results in r2.
8899 #Rename A/COPY_C to A/RENAMED_C results in r3.
8900 #Add A/RENAMED_C/file1 and commit, results in r4.
8901 #Change A/RENAMED_C/file1 and commit, results in r5.
8902 #Merge r4 from A/RENAMED_C to A/C
8903 #Merge r2:5 from A/RENAMED_C to A/C <-- This fails tracked via #3032.
8905 ## See http://subversion.tigris.org/issues/show_bug.cgi?id=3032. ##
8907 # Create a WC with a single branch
8908 sbox.build()
8909 wc_dir = sbox.wc_dir
8910 # Some paths we'll care about
8911 A_C_url = sbox.repo_url + '/A/C'
8912 A_COPY_C_url = sbox.repo_url + '/A/COPY_C'
8913 A_RENAMED_C_url = sbox.repo_url + '/A/RENAMED_C'
8914 A_C_path = os.path.join(wc_dir, 'A', 'C')
8915 A_RENAMED_C_path = os.path.join(wc_dir, 'A', 'RENAMED_C')
8916 A_RENAMED_C_file1_path = os.path.join(wc_dir, 'A', 'RENAMED_C', 'file1')
8918 svntest.main.run_svn(None, 'cp', A_C_url, A_COPY_C_url, '-m', 'copy...')
8919 svntest.main.run_svn(None, 'mv', A_COPY_C_url, A_RENAMED_C_url, '-m',
8920 'rename...')
8921 svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
8923 svntest.main.file_write(A_RENAMED_C_file1_path, "This is the file1.\n")
8924 svntest.main.run_svn(None, 'add', A_RENAMED_C_file1_path)
8925 expected_output = wc.State(A_RENAMED_C_path, {
8926 'file1' : Item(verb='Adding'),
8928 expected_status = wc.State(A_RENAMED_C_path, {
8929 '' : Item(status=' ', wc_rev=3),
8930 'file1' : Item(status=' ', wc_rev=4),
8932 svntest.actions.run_and_verify_commit(A_RENAMED_C_path, expected_output,
8933 expected_status, None,
8934 A_RENAMED_C_path)
8935 svntest.main.file_write(A_RENAMED_C_file1_path,
8936 "This is the file1 modified.\n")
8937 expected_output = wc.State(A_RENAMED_C_path, {
8938 'file1' : Item(verb='Sending'),
8940 expected_status.tweak('file1', wc_rev=5)
8941 svntest.actions.run_and_verify_commit(A_RENAMED_C_path, expected_output,
8942 expected_status, None,
8943 A_RENAMED_C_path)
8945 short_A_C = shorten_path_kludge(A_C_path)
8946 expected_skip = wc.State(short_A_C, {})
8947 expected_output = wc.State(short_A_C, {
8948 'file1' : Item(status='A '),
8950 expected_disk = wc.State('', {
8951 '' : Item(props={SVN_PROP_MERGEINFO : '/A/RENAMED_C:4'}),
8952 'file1' : Item("This is the file1.\n"),
8954 expected_status = wc.State(short_A_C, {
8955 '' : Item(status=' M', wc_rev=3),
8956 'file1' : Item(status='A ', wc_rev='-', copied='+'),
8958 saved_cwd = os.getcwd()
8959 os.chdir(svntest.main.work_dir)
8960 svntest.actions.run_and_verify_merge(short_A_C, 3, 4,
8961 A_RENAMED_C_url,
8962 expected_output,
8963 expected_disk,
8964 expected_status,
8965 expected_skip,
8966 None, None, None, None, None, 1, 1)
8968 expected_output = wc.State(short_A_C, {
8969 'file1' : Item(status='U '),
8971 expected_disk = wc.State('', {
8972 '' : Item(props={SVN_PROP_MERGEINFO : '/A/RENAMED_C:3-5'}),
8973 'file1' : Item("This is the file1 modified.\n"),
8975 expected_status = wc.State(short_A_C, {
8976 '' : Item(status=' M', wc_rev=3),
8977 'file1' : Item(status='A ', wc_rev='-', copied='+'),
8979 svntest.actions.run_and_verify_merge(short_A_C, 2, 5,
8980 A_RENAMED_C_url,
8981 expected_output,
8982 expected_disk,
8983 expected_status,
8984 expected_skip,
8985 None, None, None, None, None, 1, 1)
8986 os.chdir(saved_cwd)
8988 # Test for part of issue #2877: 'do subtree merge only if subtree has
8989 # explicit mergeinfo set and exists in the merge source'
8990 def merge_source_normalization_and_subtree_merges(sbox):
8991 "normalized mergeinfo is recorded on subtrees"
8993 sbox.build()
8994 wc_dir = sbox.wc_dir
8996 # Some paths we'll care about
8997 D_COPY_path = os.path.join(wc_dir, "A_COPY", "D")
8998 G_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "G")
9000 # Use our helper to copy 'A' to 'A_COPY' then make some changes under 'A'
9001 wc_disk, wc_status = set_up_branch(sbox)
9003 # r7 - Move A to A_MOVED
9004 svntest.actions.run_and_verify_svn(None, ['\n', 'Committed revision 7.\n'],
9005 [], 'mv', '-m', 'mv A to A_MOVED',
9006 sbox.repo_url + '/A',
9007 sbox.repo_url + '/A_MOVED')
9008 wc_status.add({
9009 'A_MOVED/B' : Item(),
9010 'A_MOVED/B/lambda' : Item(),
9011 'A_MOVED/B/E' : Item(),
9012 'A_MOVED/B/E/alpha' : Item(),
9013 'A_MOVED/B/E/beta' : Item(),
9014 'A_MOVED/B/F' : Item(),
9015 'A_MOVED/mu' : Item(),
9016 'A_MOVED/C' : Item(),
9017 'A_MOVED/D' : Item(),
9018 'A_MOVED/D/gamma' : Item(),
9019 'A_MOVED/D/G' : Item(),
9020 'A_MOVED/D/G/pi' : Item(),
9021 'A_MOVED/D/G/rho' : Item(),
9022 'A_MOVED/D/G/tau' : Item(),
9023 'A_MOVED/D/H' : Item(),
9024 'A_MOVED/D/H/chi' : Item(),
9025 'A_MOVED/D/H/omega' : Item(),
9026 'A_MOVED/D/H/psi' : Item(),
9027 'A_MOVED' : Item()})
9028 wc_status.remove('A', 'A/B', 'A/B/lambda', 'A/B/E', 'A/B/E/alpha',
9029 'A/B/E/beta', 'A/B/F', 'A/mu', 'A/C', 'A/D',
9030 'A/D/gamma', 'A/D/G', 'A/D/G/pi', 'A/D/G/rho',
9031 'A/D/G/tau' , 'A/D/H', 'A/D/H/chi', 'A/D/H/omega',
9032 'A/D/H/psi')
9033 wc_status.tweak(status=' ', wc_rev=7)
9035 # Update the WC
9036 svntest.actions.run_and_verify_svn(None, None, [],
9037 'update', wc_dir)
9039 # r8 - Make a text mod to 'A_MOVED/D/G/tau'
9040 svntest.main.file_write(os.path.join(wc_dir, "A_MOVED", "D", "G", "tau"),
9041 "New content")
9042 expected_output = wc.State(wc_dir,
9043 {'A_MOVED/D/G/tau' : Item(verb='Sending')})
9044 wc_status.tweak('A_MOVED/D/G/tau', status=' ', wc_rev=8)
9045 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
9046 wc_status, None, wc_dir)
9048 # Merge -c4 URL/A_MOVED/D/G A_COPY/D/G.
9050 # Search for the comment entitled "The Merge Kluge" elsewhere in
9051 # this file, to understand why we shorten and chdir() below.
9053 # A_MOVED/D/G doesn't exist at r3:4, it's still A/D/G,
9054 # so the merge source normalization logic should set
9055 # mergeinfo of '/A/D/G:4' on A_COPY/D/G, *not* 'A_MOVED/D/G:4',
9056 # see issue #2953.
9057 short_G_COPY_path = shorten_path_kludge(G_COPY_path)
9058 expected_output = wc.State(short_G_COPY_path, {
9059 'rho' : Item(status='U ')
9061 expected_status = wc.State(short_G_COPY_path, {
9062 '' : Item(status=' M', wc_rev=7),
9063 'pi' : Item(status=' ', wc_rev=7),
9064 'rho' : Item(status='M ', wc_rev=7),
9065 'tau' : Item(status=' ', wc_rev=7),
9067 expected_disk = wc.State('', {
9068 '' : Item(props={SVN_PROP_MERGEINFO : '/A/D/G:4'}),
9069 'pi' : Item("This is the file 'pi'.\n"),
9070 'rho' : Item("New content"),
9071 'tau' : Item("This is the file 'tau'.\n"),
9073 expected_skip = wc.State(short_G_COPY_path, { })
9074 saved_cwd = os.getcwd()
9075 os.chdir(svntest.main.work_dir)
9076 svntest.actions.run_and_verify_merge(short_G_COPY_path, '3', '4',
9077 sbox.repo_url + '/A_MOVED/D/G',
9078 expected_output,
9079 expected_disk,
9080 expected_status,
9081 expected_skip,
9082 None, None, None, None,
9083 None, 1)
9084 os.chdir(saved_cwd)
9086 # Merge -c8 URL/A_MOVED/D A_COPY/D.
9088 # The merge target A_COPY/D and the subtree at A_COPY/D/G
9089 # should both have their mergeinfo updated with r8
9090 # from A_MOVED_D, see reopened issue #2877.
9091 short_D_COPY_path = shorten_path_kludge(D_COPY_path)
9092 expected_output = wc.State(short_D_COPY_path, {
9093 'G/tau' : Item(status='U '),
9095 expected_status = wc.State(short_D_COPY_path, {
9096 '' : Item(status=' M', wc_rev=7),
9097 'G' : Item(status=' M', wc_rev=7),
9098 'G/pi' : Item(status=' ', wc_rev=7),
9099 'G/rho' : Item(status='M ', wc_rev=7),
9100 'G/tau' : Item(status='M ', wc_rev=7),
9101 'H' : Item(status=' ', wc_rev=7),
9102 'H/chi' : Item(status=' ', wc_rev=7),
9103 'H/psi' : Item(status=' ', wc_rev=7),
9104 'H/omega' : Item(status=' ', wc_rev=7),
9105 'gamma' : Item(status=' ', wc_rev=7),
9107 expected_disk = wc.State('', {
9108 '' : Item(props={SVN_PROP_MERGEINFO : '/A_MOVED/D:8'}),
9109 'G' : Item(props={SVN_PROP_MERGEINFO :
9110 '/A/D/G:4\n/A_MOVED/D/G:8\n'}),
9111 'G/pi' : Item("This is the file 'pi'.\n"),
9112 'G/rho' : Item("New content"),
9113 'G/tau' : Item("New content"),
9114 'H' : Item(),
9115 'H/chi' : Item("This is the file 'chi'.\n"),
9116 'H/psi' : Item("This is the file 'psi'.\n"),
9117 'H/omega' : Item("This is the file 'omega'.\n"),
9118 'gamma' : Item("This is the file 'gamma'.\n")
9120 expected_skip = wc.State(short_D_COPY_path, { })
9121 os.chdir(svntest.main.work_dir)
9122 svntest.actions.run_and_verify_merge(short_D_COPY_path, '7', '8',
9123 sbox.repo_url + \
9124 '/A_MOVED/D',
9125 expected_output,
9126 expected_disk,
9127 expected_status,
9128 expected_skip,
9129 None, None, None, None,
9130 None, 1)
9131 os.chdir(saved_cwd)
9133 # Test for issue #3067: 'subtrees with intersecting mergeinfo, that don't
9134 # exist at the start of a merge range shouldn't break the merge'
9135 def new_subtrees_should_not_break_merge(sbox):
9136 "subtrees added after start of merge range are ok"
9138 sbox.build()
9139 wc_dir = sbox.wc_dir
9140 wc_disk, wc_status = set_up_branch(sbox)
9142 # Some paths we'll care about
9143 A_COPY_path = os.path.join(wc_dir, "A_COPY")
9144 D_COPY_path = os.path.join(wc_dir, "A_COPY", "D")
9145 nu_path = os.path.join(wc_dir, "A", "D", "H", "nu")
9146 nu_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "H", "nu")
9147 H_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "H")
9149 # Create 'A/D/H/nu', commit it as r7, make a text mod to it in r8.
9150 svntest.main.file_write(nu_path, "This is the file 'nu'.\n")
9151 svntest.actions.run_and_verify_svn(None, None, [], 'add', nu_path)
9152 expected_output = wc.State(wc_dir, {'A/D/H/nu' : Item(verb='Adding')})
9153 wc_status.add({'A/D/H/nu' : Item(status=' ', wc_rev=7)})
9154 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
9155 wc_status, None, wc_dir)
9156 svntest.main.file_write(nu_path, "New content")
9157 expected_output = wc.State(wc_dir, {'A/D/H/nu' : Item(verb='Sending')})
9158 wc_status.tweak('A/D/H/nu', wc_rev=8)
9159 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
9160 wc_status, None, wc_dir)
9162 # Merge r7 to A_COPY/D/H, then, so it has it's own explicit mergeinfo,
9163 # then merge r8 to A_COPY/D/H/nu so it too has explicit mergeinfo.
9164 short_H_COPY_path = shorten_path_kludge(H_COPY_path)
9165 expected_output = wc.State(short_H_COPY_path, {
9166 'nu' : Item(status='A '),
9168 expected_status = wc.State(short_H_COPY_path, {
9169 '' : Item(status=' M', wc_rev=2),
9170 'psi' : Item(status=' ', wc_rev=2),
9171 'omega' : Item(status=' ', wc_rev=2),
9172 'chi' : Item(status=' ', wc_rev=2),
9173 'nu' : Item(status='A ', copied='+', wc_rev='-'),
9175 expected_disk = wc.State('', {
9176 '' : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:7'}),
9177 'psi' : Item("This is the file 'psi'.\n"),
9178 'omega' : Item("This is the file 'omega'.\n"),
9179 'chi' : Item("This is the file 'chi'.\n"),
9180 'nu' : Item("This is the file 'nu'.\n"),
9182 expected_skip = wc.State(short_H_COPY_path, {})
9183 saved_cwd = os.getcwd()
9184 os.chdir(svntest.main.work_dir)
9185 svntest.actions.run_and_verify_merge(short_H_COPY_path, '6', '7',
9186 sbox.repo_url + '/A/D/H',
9187 expected_output, expected_disk,
9188 expected_status, expected_skip,
9189 None, None, None, None, None, 1)
9190 # run_and_verify_merge doesn't support merging to a file WCPATH
9191 # so use run_and_verify_svn.
9192 short_nu_COPY_path = shorten_path_kludge(nu_COPY_path)
9193 svntest.actions.run_and_verify_svn(None,
9194 expected_merge_output([[8]],
9195 'U ' + short_nu_COPY_path + '\n'),
9196 [], 'merge', '-c8',
9197 sbox.repo_url + '/A/D/H/nu',
9198 short_nu_COPY_path)
9199 # Merge -r4:6 to A_COPY, then reverse merge r6 from A_COPY/D.
9200 short_A_COPY_path = shorten_path_kludge(A_COPY_path)
9201 expected_output = wc.State(short_A_COPY_path, {
9202 'B/E/beta' : Item(status='U '),
9203 'D/H/omega': Item(status='U '),
9205 expected_status = wc.State(short_A_COPY_path, {
9206 '' : Item(status=' M', wc_rev=2),
9207 'B' : Item(status=' ', wc_rev=2),
9208 'mu' : Item(status=' ', wc_rev=2),
9209 'B/E' : Item(status=' ', wc_rev=2),
9210 'B/E/alpha' : Item(status=' ', wc_rev=2),
9211 'B/E/beta' : Item(status='M ', wc_rev=2),
9212 'B/lambda' : Item(status=' ', wc_rev=2),
9213 'B/F' : Item(status=' ', wc_rev=2),
9214 'C' : Item(status=' ', wc_rev=2),
9215 'D' : Item(status=' ', wc_rev=2),
9216 'D/G' : Item(status=' ', wc_rev=2),
9217 'D/G/pi' : Item(status=' ', wc_rev=2),
9218 'D/G/rho' : Item(status=' ', wc_rev=2),
9219 'D/G/tau' : Item(status=' ', wc_rev=2),
9220 'D/gamma' : Item(status=' ', wc_rev=2),
9221 'D/H' : Item(status=' M', wc_rev=2),
9222 'D/H/chi' : Item(status=' ', wc_rev=2),
9223 'D/H/psi' : Item(status=' ', wc_rev=2),
9224 'D/H/omega' : Item(status='M ', wc_rev=2),
9225 'D/H/nu' : Item(status='A ', copied='+', wc_rev='-'),
9227 expected_disk = wc.State('', {
9228 '' : Item(props={SVN_PROP_MERGEINFO : '/A:5-6'}),
9229 'B' : Item(),
9230 'mu' : Item("This is the file 'mu'.\n"),
9231 'B/E' : Item(),
9232 'B/E/alpha' : Item("This is the file 'alpha'.\n"),
9233 'B/E/beta' : Item("New content"),
9234 'B/lambda' : Item("This is the file 'lambda'.\n"),
9235 'B/F' : Item(),
9236 'C' : Item(),
9237 'D' : Item(),
9238 'D/G' : Item(),
9239 'D/G/pi' : Item("This is the file 'pi'.\n"),
9240 'D/G/rho' : Item("This is the file 'rho'.\n"),
9241 'D/G/tau' : Item("This is the file 'tau'.\n"),
9242 'D/gamma' : Item("This is the file 'gamma'.\n"),
9243 'D/H' : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:5-7'}),
9244 'D/H/chi' : Item("This is the file 'chi'.\n"),
9245 'D/H/psi' : Item("This is the file 'psi'.\n"),
9246 'D/H/omega' : Item("New content"),
9247 'D/H/nu' : Item("New content",
9248 props={SVN_PROP_MERGEINFO : '/A/D/H/nu:5-8'}),
9250 expected_skip = wc.State(short_A_COPY_path, { })
9251 svntest.actions.run_and_verify_merge(short_A_COPY_path, '4', '6',
9252 sbox.repo_url + \
9253 '/A',
9254 expected_output,
9255 expected_disk,
9256 expected_status,
9257 expected_skip,
9258 None, None, None, None,
9259 None, 1)
9260 short_D_COPY_path = shorten_path_kludge(D_COPY_path)
9261 expected_output = wc.State(short_D_COPY_path, {
9262 'H/omega': Item(status='G '),
9264 expected_status = wc.State(short_D_COPY_path, {
9265 '' : Item(status=' M', wc_rev=2),
9266 'G' : Item(status=' ', wc_rev=2),
9267 'G/pi' : Item(status=' ', wc_rev=2),
9268 'G/rho' : Item(status=' ', wc_rev=2),
9269 'G/tau' : Item(status=' ', wc_rev=2),
9270 'gamma' : Item(status=' ', wc_rev=2),
9271 'H' : Item(status=' M', wc_rev=2),
9272 'H/chi' : Item(status=' ', wc_rev=2),
9273 'H/psi' : Item(status=' ', wc_rev=2),
9274 'H/omega' : Item(status=' ', wc_rev=2),
9275 'H/nu' : Item(status='A ', copied='+', wc_rev='-'),
9277 expected_disk = wc.State('', {
9278 '' : Item(props={SVN_PROP_MERGEINFO : '/A/D:5'}),
9279 'G/pi' : Item("This is the file 'pi'.\n"),
9280 'G/rho' : Item("This is the file 'rho'.\n"),
9281 'G/tau' : Item("This is the file 'tau'.\n"),
9282 'gamma' : Item("This is the file 'gamma'.\n"),
9283 'H' : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:5,7'}),
9284 'H/chi' : Item("This is the file 'chi'.\n"),
9285 'H/psi' : Item("This is the file 'psi'.\n"),
9286 'H/omega' : Item("This is the file 'omega'.\n"),
9287 'H/nu' : Item("New content",
9288 props={SVN_PROP_MERGEINFO : '/A/D/H/nu:5,7-8'}),
9290 expected_skip = wc.State(short_D_COPY_path, { })
9291 svntest.actions.run_and_verify_merge(short_D_COPY_path, '6', '5',
9292 sbox.repo_url + \
9293 '/A/D',
9294 expected_output,
9295 expected_disk,
9296 expected_status,
9297 expected_skip,
9298 None, None, None, None,
9299 None, 1)
9300 # Now once again merge r6 to A_COPY. A_COPY already has r6 in its mergeinfo
9301 # so we expect only subtree merges on A_COPY/D, A_COPY_D_H, and
9302 # A_COPY/D/H/nu. The fact that A/D/H/nu doesn't exist at r6 should not cause
9303 # the merge to fail.
9304 short_A_COPY_path = shorten_path_kludge(A_COPY_path)
9305 expected_output = wc.State(short_A_COPY_path, {
9306 'D/H/omega': Item(status='U '),
9308 expected_status = wc.State(short_A_COPY_path, {
9309 '' : Item(status=' M', wc_rev=2),
9310 'B' : Item(status=' ', wc_rev=2),
9311 'mu' : Item(status=' ', wc_rev=2),
9312 'B/E' : Item(status=' ', wc_rev=2),
9313 'B/E/alpha' : Item(status=' ', wc_rev=2),
9314 'B/E/beta' : Item(status='M ', wc_rev=2),
9315 'B/lambda' : Item(status=' ', wc_rev=2),
9316 'B/F' : Item(status=' ', wc_rev=2),
9317 'C' : Item(status=' ', wc_rev=2),
9318 'D' : Item(status=' ', wc_rev=2),
9319 'D/G' : Item(status=' ', wc_rev=2),
9320 'D/G/pi' : Item(status=' ', wc_rev=2),
9321 'D/G/rho' : Item(status=' ', wc_rev=2),
9322 'D/G/tau' : Item(status=' ', wc_rev=2),
9323 'D/gamma' : Item(status=' ', wc_rev=2),
9324 'D/H' : Item(status=' M', wc_rev=2),
9325 'D/H/chi' : Item(status=' ', wc_rev=2),
9326 'D/H/psi' : Item(status=' ', wc_rev=2),
9327 'D/H/omega' : Item(status='M ', wc_rev=2),
9328 'D/H/nu' : Item(status='A ', copied='+', wc_rev='-'),
9330 expected_disk = wc.State('', {
9331 '' : Item(props={SVN_PROP_MERGEINFO : '/A:5-6'}),
9332 'B' : Item(),
9333 'mu' : Item("This is the file 'mu'.\n"),
9334 'B/E' : Item(),
9335 'B/E/alpha' : Item("This is the file 'alpha'.\n"),
9336 'B/E/beta' : Item("New content"),
9337 'B/lambda' : Item("This is the file 'lambda'.\n"),
9338 'B/F' : Item(),
9339 'C' : Item(),
9340 'D' : Item(), # Mergeinfo elides to 'A_COPY'
9341 'D/G' : Item(),
9342 'D/G/pi' : Item("This is the file 'pi'.\n"),
9343 'D/G/rho' : Item("This is the file 'rho'.\n"),
9344 'D/G/tau' : Item("This is the file 'tau'.\n"),
9345 'D/gamma' : Item("This is the file 'gamma'.\n"),
9346 'D/H' : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:5-7'}),
9347 'D/H/chi' : Item("This is the file 'chi'.\n"),
9348 'D/H/psi' : Item("This is the file 'psi'.\n"),
9349 'D/H/omega' : Item("New content"),
9350 'D/H/nu' : Item("New content",
9351 props={SVN_PROP_MERGEINFO : '/A/D/H/nu:5-8'}),
9353 expected_skip = wc.State(short_A_COPY_path, { })
9354 svntest.actions.run_and_verify_merge(short_A_COPY_path, '5', '6',
9355 sbox.repo_url + \
9356 '/A',
9357 expected_output,
9358 expected_disk,
9359 expected_status,
9360 expected_skip,
9361 None, None, None, None,
9362 None, 1)
9363 os.chdir(saved_cwd)
9365 def basic_reintegrate(sbox):
9366 "basic merge --reintegrate support"
9368 # Make A_COPY branch in r2, and do a few more commits to A in r3-6.
9369 sbox.build()
9370 wc_dir = sbox.wc_dir
9371 expected_disk, expected_status = set_up_branch(sbox)
9373 # Make a change on the branch, to A/mu. Commit in r7.
9374 svntest.main.file_write(os.path.join(wc_dir, "A_COPY", "mu"),
9375 "Changed on the branch.")
9376 expected_output = wc.State(wc_dir, {'A_COPY/mu' : Item(verb='Sending')})
9377 expected_status.tweak('A_COPY/mu', wc_rev=7)
9378 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
9379 expected_status, None, wc_dir)
9380 expected_disk.tweak('A_COPY/mu', contents='Changed on the branch.')
9382 # Update the wcs.
9383 expected_output = wc.State(wc_dir, {})
9384 expected_status.tweak(wc_rev='7')
9385 svntest.actions.run_and_verify_update(wc_dir, expected_output,
9386 expected_disk, expected_status,
9387 None, None, None, None, None, True)
9389 # Merge from trunk to branch (ie, r3-6), using normal cherry-harvest.
9390 A_COPY_path = os.path.join(wc_dir, "A_COPY")
9391 short_A_COPY_path = shorten_path_kludge(A_COPY_path)
9392 expected_output = wc.State(short_A_COPY_path, {
9393 'D/H/psi' : Item(status='U '),
9394 'D/G/rho' : Item(status='U '),
9395 'B/E/beta' : Item(status='U '),
9396 'D/H/omega' : Item(status='U '),
9398 k_expected_status = wc.State(short_A_COPY_path, {
9399 "B" : Item(status=' ', wc_rev=7),
9400 "B/lambda" : Item(status=' ', wc_rev=7),
9401 "B/E" : Item(status=' ', wc_rev=7),
9402 "B/E/alpha" : Item(status=' ', wc_rev=7),
9403 "B/E/beta" : Item(status='M ', wc_rev=7),
9404 "B/F" : Item(status=' ', wc_rev=7),
9405 "mu" : Item(status=' ', wc_rev=7),
9406 "C" : Item(status=' ', wc_rev=7),
9407 "D" : Item(status=' ', wc_rev=7),
9408 "D/gamma" : Item(status=' ', wc_rev=7),
9409 "D/G" : Item(status=' ', wc_rev=7),
9410 "D/G/pi" : Item(status=' ', wc_rev=7),
9411 "D/G/rho" : Item(status='M ', wc_rev=7),
9412 "D/G/tau" : Item(status=' ', wc_rev=7),
9413 "D/H" : Item(status=' ', wc_rev=7),
9414 "D/H/chi" : Item(status=' ', wc_rev=7),
9415 "D/H/omega" : Item(status='M ', wc_rev=7),
9416 "D/H/psi" : Item(status='M ', wc_rev=7),
9417 "" : Item(status=' M', wc_rev=7),
9419 k_expected_disk = wc.State('', {
9420 '' : Item(props={SVN_PROP_MERGEINFO : '/A:2-7'}),
9421 'B' : Item(),
9422 'B/lambda' : Item("This is the file 'lambda'.\n"),
9423 'B/E' : Item(),
9424 'B/E/alpha' : Item("This is the file 'alpha'.\n"),
9425 'B/E/beta' : Item("New content"),
9426 'B/F' : Item(),
9427 'mu' : Item("Changed on the branch."),
9428 'C' : Item(),
9429 'D' : Item(),
9430 'D/gamma' : Item("This is the file 'gamma'.\n"),
9431 'D/G' : Item(),
9432 'D/G/pi' : Item("This is the file 'pi'.\n"),
9433 'D/G/rho' : Item("New content"),
9434 'D/G/tau' : Item("This is the file 'tau'.\n"),
9435 'D/H' : Item(),
9436 'D/H/chi' : Item("This is the file 'chi'.\n"),
9437 'D/H/omega' : Item("New content"),
9438 'D/H/psi' : Item("New content"),
9440 expected_skip = wc.State(short_A_COPY_path, {})
9441 saved_cwd = os.getcwd()
9442 os.chdir(svntest.main.work_dir)
9443 svntest.actions.run_and_verify_merge(short_A_COPY_path, None, None,
9444 sbox.repo_url + '/A',
9445 expected_output,
9446 k_expected_disk,
9447 k_expected_status,
9448 expected_skip,
9449 None, None, None, None,
9450 None, True)
9451 os.chdir(saved_cwd)
9452 expected_disk.tweak('A_COPY', props={SVN_PROP_MERGEINFO: '/A:2-7'})
9453 expected_disk.tweak('A_COPY/B/E/beta', contents="New content")
9454 expected_disk.tweak('A_COPY/D/G/rho', contents="New content")
9455 expected_disk.tweak('A_COPY/D/H/omega', contents="New content")
9456 expected_disk.tweak('A_COPY/D/H/psi', contents="New content")
9458 # Commit the merge to branch (r8).
9459 expected_output = wc.State(wc_dir, {
9460 'A_COPY/D/H/psi' : Item(verb='Sending'),
9461 'A_COPY/D/G/rho' : Item(verb='Sending'),
9462 'A_COPY/B/E/beta' : Item(verb='Sending'),
9463 'A_COPY/D/H/omega' : Item(verb='Sending'),
9464 'A_COPY' : Item(verb='Sending'),
9466 expected_status.tweak('A_COPY', 'A_COPY/D/H/psi', 'A_COPY/D/G/rho',
9467 'A_COPY/B/E/beta', 'A_COPY/D/H/omega', wc_rev=8)
9468 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
9469 expected_status, None, wc_dir)
9471 # Update the wcs again.
9472 expected_output = wc.State(wc_dir, {})
9473 expected_status.tweak(wc_rev='8')
9474 svntest.actions.run_and_verify_update(wc_dir, expected_output,
9475 expected_disk, expected_status,
9476 None, None, None, None, None, True)
9479 # *finally*, actually run merge --reintegrate in trunk with the
9480 # branch URL. This should bring in the mu change and the tauprime
9481 # change.
9482 A_path = os.path.join(wc_dir, "A")
9483 short_A_path = shorten_path_kludge(A_path)
9484 expected_output = wc.State(short_A_path, {
9485 '' : Item(status=' U'),
9486 'mu' : Item(status='U '),
9488 k_expected_status = wc.State(short_A_path, {
9489 "B" : Item(status=' ', wc_rev=8),
9490 "B/lambda" : Item(status=' ', wc_rev=8),
9491 "B/E" : Item(status=' ', wc_rev=8),
9492 "B/E/alpha" : Item(status=' ', wc_rev=8),
9493 "B/E/beta" : Item(status=' ', wc_rev=8),
9494 "B/F" : Item(status=' ', wc_rev=8),
9495 "mu" : Item(status='M ', wc_rev=8),
9496 "C" : Item(status=' ', wc_rev=8),
9497 "D" : Item(status=' ', wc_rev=8),
9498 "D/gamma" : Item(status=' ', wc_rev=8),
9499 "D/G" : Item(status=' ', wc_rev=8),
9500 "D/G/pi" : Item(status=' ', wc_rev=8),
9501 "D/G/rho" : Item(status=' ', wc_rev=8),
9502 "D/G/tau" : Item(status=' ', wc_rev=8),
9503 "D/H" : Item(status=' ', wc_rev=8),
9504 "D/H/chi" : Item(status=' ', wc_rev=8),
9505 "D/H/omega" : Item(status=' ', wc_rev=8),
9506 "D/H/psi" : Item(status=' ', wc_rev=8),
9507 "" : Item(status=' M', wc_rev=8),
9509 k_expected_disk.tweak('', props={SVN_PROP_MERGEINFO : '/A_COPY:2-8'})
9510 expected_skip = wc.State(short_A_path, {})
9511 saved_cwd = os.getcwd()
9512 os.chdir(svntest.main.work_dir)
9513 svntest.actions.run_and_verify_merge(short_A_path, None, None,
9514 sbox.repo_url + '/A_COPY',
9515 expected_output,
9516 k_expected_disk,
9517 k_expected_status,
9518 expected_skip,
9519 None, None, None, None,
9520 None, True, True,
9521 '--reintegrate')
9522 os.chdir(saved_cwd)
9524 # Finally, commit the result of the merge (r9).
9525 expected_output = wc.State(wc_dir, {
9526 'A/mu' : Item(verb='Sending'),
9527 'A' : Item(verb='Sending'),
9529 expected_status.tweak('A', 'A/mu', wc_rev=9)
9530 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
9531 expected_status, None, wc_dir)
9533 def reintegrate_with_rename(sbox):
9534 "merge --reintegrate with renamed file on branch"
9536 # Make A_COPY branch in r2, and do a few more commits to A in r3-6.
9537 sbox.build()
9538 wc_dir = sbox.wc_dir
9539 expected_disk, expected_status = set_up_branch(sbox)
9541 # Make a change on the branch, to A/mu. Commit in r7.
9542 svntest.main.file_write(os.path.join(wc_dir, "A_COPY", "mu"),
9543 "Changed on the branch.")
9544 expected_output = wc.State(wc_dir, {'A_COPY/mu' : Item(verb='Sending')})
9545 expected_status.tweak('A_COPY/mu', wc_rev=7)
9546 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
9547 expected_status, None, wc_dir)
9548 expected_disk.tweak('A_COPY/mu', contents='Changed on the branch.')
9550 # Update the wcs.
9551 expected_output = wc.State(wc_dir, {})
9552 expected_status.tweak(wc_rev='7')
9553 svntest.actions.run_and_verify_update(wc_dir, expected_output,
9554 expected_disk, expected_status,
9555 None, None, None, None, None, True)
9557 # Merge from trunk to branch (ie, r3-6), using normal cherry-harvest.
9558 A_COPY_path = os.path.join(wc_dir, "A_COPY")
9559 short_A_COPY_path = shorten_path_kludge(A_COPY_path)
9560 expected_output = wc.State(short_A_COPY_path, {
9561 'D/H/psi' : Item(status='U '),
9562 'D/G/rho' : Item(status='U '),
9563 'B/E/beta' : Item(status='U '),
9564 'D/H/omega' : Item(status='U '),
9566 k_expected_status = wc.State(short_A_COPY_path, {
9567 "B" : Item(status=' ', wc_rev=7),
9568 "B/lambda" : Item(status=' ', wc_rev=7),
9569 "B/E" : Item(status=' ', wc_rev=7),
9570 "B/E/alpha" : Item(status=' ', wc_rev=7),
9571 "B/E/beta" : Item(status='M ', wc_rev=7),
9572 "B/F" : Item(status=' ', wc_rev=7),
9573 "mu" : Item(status=' ', wc_rev=7),
9574 "C" : Item(status=' ', wc_rev=7),
9575 "D" : Item(status=' ', wc_rev=7),
9576 "D/gamma" : Item(status=' ', wc_rev=7),
9577 "D/G" : Item(status=' ', wc_rev=7),
9578 "D/G/pi" : Item(status=' ', wc_rev=7),
9579 "D/G/rho" : Item(status='M ', wc_rev=7),
9580 "D/G/tau" : Item(status=' ', wc_rev=7),
9581 "D/H" : Item(status=' ', wc_rev=7),
9582 "D/H/chi" : Item(status=' ', wc_rev=7),
9583 "D/H/omega" : Item(status='M ', wc_rev=7),
9584 "D/H/psi" : Item(status='M ', wc_rev=7),
9585 "" : Item(status=' M', wc_rev=7),
9587 k_expected_disk = wc.State('', {
9588 '' : Item(props={SVN_PROP_MERGEINFO : '/A:2-7'}),
9589 'B' : Item(),
9590 'B/lambda' : Item("This is the file 'lambda'.\n"),
9591 'B/E' : Item(),
9592 'B/E/alpha' : Item("This is the file 'alpha'.\n"),
9593 'B/E/beta' : Item("New content"),
9594 'B/F' : Item(),
9595 'mu' : Item("Changed on the branch."),
9596 'C' : Item(),
9597 'D' : Item(),
9598 'D/gamma' : Item("This is the file 'gamma'.\n"),
9599 'D/G' : Item(),
9600 'D/G/pi' : Item("This is the file 'pi'.\n"),
9601 'D/G/rho' : Item("New content"),
9602 'D/G/tau' : Item("This is the file 'tau'.\n"),
9603 'D/H' : Item(),
9604 'D/H/chi' : Item("This is the file 'chi'.\n"),
9605 'D/H/omega' : Item("New content"),
9606 'D/H/psi' : Item("New content"),
9608 expected_skip = wc.State(short_A_COPY_path, {})
9609 saved_cwd = os.getcwd()
9610 os.chdir(svntest.main.work_dir)
9611 svntest.actions.run_and_verify_merge(short_A_COPY_path, None, None,
9612 sbox.repo_url + '/A',
9613 expected_output,
9614 k_expected_disk,
9615 k_expected_status,
9616 expected_skip,
9617 None, None, None, None,
9618 None, True)
9619 os.chdir(saved_cwd)
9620 expected_disk.tweak('A_COPY', props={SVN_PROP_MERGEINFO: '/A:2-7'})
9621 expected_disk.tweak('A_COPY/B/E/beta', contents="New content")
9622 expected_disk.tweak('A_COPY/D/G/rho', contents="New content")
9623 expected_disk.tweak('A_COPY/D/H/omega', contents="New content")
9624 expected_disk.tweak('A_COPY/D/H/psi', contents="New content")
9626 # Commit the merge to branch (r8).
9627 expected_output = wc.State(wc_dir, {
9628 'A_COPY/D/H/psi' : Item(verb='Sending'),
9629 'A_COPY/D/G/rho' : Item(verb='Sending'),
9630 'A_COPY/B/E/beta' : Item(verb='Sending'),
9631 'A_COPY/D/H/omega' : Item(verb='Sending'),
9632 'A_COPY' : Item(verb='Sending'),
9634 expected_status.tweak('A_COPY', 'A_COPY/D/H/psi', 'A_COPY/D/G/rho',
9635 'A_COPY/B/E/beta', 'A_COPY/D/H/omega', wc_rev=8)
9636 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
9637 expected_status, None, wc_dir)
9640 # Update the wcs again.
9642 # Note: this update had to be added because of r28942 (which was
9643 # merged into the reintegrate branch in r28947). Without this
9644 # update, the mergeinfo will not be inherited properly as part of
9645 # the 'svn cp tau tauprime' step, and later (during the post-commit
9646 # update, with the new expected_disk) we'll get an error like this:
9648 # =============================================================
9649 # Expected 'tauprime' and actual 'tauprime' in disk tree are different!
9650 # =============================================================
9651 # EXPECTED NODE TO BE:
9652 # =============================================================
9653 # * Node name: tauprime
9654 # Path: A_COPY/D/G/tauprime
9655 # Contents: This is the file 'tau'.
9657 # Properties: {'svn:mergeinfo': '/A/D/G/tau:2-7'}
9658 # Attributes: {}
9659 # Children: N/A (node is a file)
9660 # =============================================================
9661 # ACTUAL NODE FOUND:
9662 # =============================================================
9663 # * Node name: tauprime
9664 # Path: G/tauprime
9665 # Contents: This is the file 'tau'.
9667 # Properties: {'svn:mergeinfo': ''}
9668 # Attributes: {}
9669 # Children: N/A (node is a file)
9671 expected_output = wc.State(wc_dir, {})
9672 expected_status.tweak(wc_rev='8')
9673 svntest.actions.run_and_verify_update(wc_dir, expected_output,
9674 expected_disk, expected_status,
9675 None, None, None, None, None, True)
9677 # Make another change on the branch: copy tau to tauprime. Commit
9678 # in r9.
9679 svntest.actions.run_and_verify_svn(None, None, [], 'cp',
9680 os.path.join(wc_dir, 'A_COPY', 'D', 'G',
9681 'tau'),
9682 os.path.join(wc_dir, 'A_COPY', 'D', 'G',
9683 'tauprime'))
9685 expected_output = wc.State(wc_dir, {
9686 'A_COPY/D/G/tauprime' : Item(verb='Adding')
9688 expected_status.add({'A_COPY/D/G/tauprime': Item(status=' ', wc_rev=9)})
9689 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
9690 expected_status, None, wc_dir)
9692 expected_disk.add({
9693 'A_COPY/D/G/tauprime' : Item(props={SVN_PROP_MERGEINFO: '/A/D/G/tau:2-7'},
9694 contents="This is the file 'tau'.\n")
9697 # Update the trunk (well, the whole wc) (since reintegrate really
9698 # wants a clean wc).
9699 expected_output = wc.State(wc_dir, {})
9700 expected_status.tweak(wc_rev='9')
9701 svntest.actions.run_and_verify_update(wc_dir, expected_output,
9702 expected_disk, expected_status,
9703 None, None, None, None, None, True)
9705 # *finally*, actually run merge --reintegrate in trunk with the
9706 # branch URL. This should bring in the mu change and the tauprime
9707 # change.
9708 A_path = os.path.join(wc_dir, "A")
9709 short_A_path = shorten_path_kludge(A_path)
9710 expected_output = wc.State(short_A_path, {
9711 '' : Item(status=' U'),
9712 'mu' : Item(status='U '),
9713 'D/G/tauprime' : Item(status='A '),
9715 k_expected_status = wc.State(short_A_path, {
9716 "B" : Item(status=' ', wc_rev=9),
9717 "B/lambda" : Item(status=' ', wc_rev=9),
9718 "B/E" : Item(status=' ', wc_rev=9),
9719 "B/E/alpha" : Item(status=' ', wc_rev=9),
9720 "B/E/beta" : Item(status=' ', wc_rev=9),
9721 "B/F" : Item(status=' ', wc_rev=9),
9722 "mu" : Item(status='M ', wc_rev=9),
9723 "C" : Item(status=' ', wc_rev=9),
9724 "D" : Item(status=' ', wc_rev=9),
9725 "D/gamma" : Item(status=' ', wc_rev=9),
9726 "D/G" : Item(status=' ', wc_rev=9),
9727 "D/G/pi" : Item(status=' ', wc_rev=9),
9728 "D/G/rho" : Item(status=' ', wc_rev=9),
9729 "D/G/tau" : Item(status=' ', wc_rev=9),
9730 "D/G/tauprime" : Item(status='A ', wc_rev='-', copied='+'),
9731 "D/H" : Item(status=' ', wc_rev=9),
9732 "D/H/chi" : Item(status=' ', wc_rev=9),
9733 "D/H/omega" : Item(status=' ', wc_rev=9),
9734 "D/H/psi" : Item(status=' ', wc_rev=9),
9735 "" : Item(status=' M', wc_rev=9),
9737 k_expected_disk.tweak('', props={SVN_PROP_MERGEINFO : '/A_COPY:2-9'})
9738 k_expected_disk.add({
9739 'D/G/tauprime' : Item(props={SVN_PROP_MERGEINFO: '/A/D/G/tau:2-7'},
9740 contents="This is the file 'tau'.\n")
9742 expected_skip = wc.State(short_A_path, {})
9743 saved_cwd = os.getcwd()
9744 os.chdir(svntest.main.work_dir)
9745 svntest.actions.run_and_verify_merge(short_A_path, None, None,
9746 sbox.repo_url + '/A_COPY',
9747 expected_output,
9748 k_expected_disk,
9749 k_expected_status,
9750 expected_skip,
9751 None, None, None, None,
9752 None, True, True,
9753 '--reintegrate')
9754 os.chdir(saved_cwd)
9756 # Finally, commit the result of the merge (r10).
9757 expected_output = wc.State(wc_dir, {
9758 'A/D/G/tauprime' : Item(verb='Adding'),
9759 'A/mu' : Item(verb='Sending'),
9760 'A' : Item(verb='Sending'),
9762 expected_status.add({
9763 'A/D/G/tauprime' : Item(status=' ', wc_rev=10),
9765 expected_status.tweak('A', 'A/mu', wc_rev=10)
9766 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
9767 expected_status, None, wc_dir)
9769 def reintegrate_branch_never_merged_to(sbox):
9770 "merge --reintegrate on a never-updated branch"
9772 # Make A_COPY branch in r2, and do a few more commits to A in r3-6.
9773 sbox.build()
9774 wc_dir = sbox.wc_dir
9775 expected_disk, expected_status = set_up_branch(sbox)
9777 # Make a change on the branch, to A/mu. Commit in r7.
9778 svntest.main.file_write(os.path.join(wc_dir, "A_COPY", "mu"),
9779 "Changed on the branch.")
9780 expected_output = wc.State(wc_dir, {'A_COPY/mu' : Item(verb='Sending')})
9781 expected_status.tweak('A_COPY/mu', wc_rev=7)
9782 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
9783 expected_status, None, wc_dir)
9784 expected_disk.tweak('A_COPY/mu', contents='Changed on the branch.')
9786 # Update the wcs.
9787 expected_output = wc.State(wc_dir, {})
9788 expected_status.tweak(wc_rev='7')
9789 svntest.actions.run_and_verify_update(wc_dir, expected_output,
9790 expected_disk, expected_status,
9791 None, None, None, None, None, True)
9793 # Make another change on the branch: copy tau to tauprime. Commit
9794 # in r8.
9795 svntest.actions.run_and_verify_svn(None, None, [], 'cp',
9796 os.path.join(wc_dir, 'A_COPY', 'D', 'G',
9797 'tau'),
9798 os.path.join(wc_dir, 'A_COPY', 'D', 'G',
9799 'tauprime'))
9800 expected_output = wc.State(wc_dir, {
9801 'A_COPY/D/G/tauprime' : Item(verb='Adding')
9803 expected_status.add({'A_COPY/D/G/tauprime': Item(status=' ', wc_rev=8)})
9804 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
9805 expected_status, None, wc_dir)
9806 expected_disk.add({
9807 'A_COPY/D/G/tauprime' : Item(props={SVN_PROP_MERGEINFO: ''},
9808 ### TODO(reint): why empty?
9809 contents="This is the file 'tau'.\n")
9812 # Update the trunk (well, the whole wc) (since reintegrate really
9813 # wants a clean wc).
9814 expected_output = wc.State(wc_dir, {})
9815 expected_status.tweak(wc_rev='8')
9816 svntest.actions.run_and_verify_update(wc_dir, expected_output,
9817 expected_disk, expected_status,
9818 None, None, None, None, None, True)
9820 # *finally*, actually run merge --reintegrate in trunk with the
9821 # branch URL. This should bring in the mu change and the tauprime
9822 # change.
9823 A_path = os.path.join(wc_dir, "A")
9824 short_A_path = shorten_path_kludge(A_path)
9825 expected_output = wc.State(short_A_path, {
9826 'mu' : Item(status='U '),
9827 'D/G/tauprime' : Item(status='A '),
9829 k_expected_status = wc.State(short_A_path, {
9830 "B" : Item(status=' ', wc_rev=8),
9831 "B/lambda" : Item(status=' ', wc_rev=8),
9832 "B/E" : Item(status=' ', wc_rev=8),
9833 "B/E/alpha" : Item(status=' ', wc_rev=8),
9834 "B/E/beta" : Item(status=' ', wc_rev=8),
9835 "B/F" : Item(status=' ', wc_rev=8),
9836 "mu" : Item(status='M ', wc_rev=8),
9837 "C" : Item(status=' ', wc_rev=8),
9838 "D" : Item(status=' ', wc_rev=8),
9839 "D/gamma" : Item(status=' ', wc_rev=8),
9840 "D/G" : Item(status=' ', wc_rev=8),
9841 "D/G/pi" : Item(status=' ', wc_rev=8),
9842 "D/G/rho" : Item(status=' ', wc_rev=8),
9843 "D/G/tau" : Item(status=' ', wc_rev=8),
9844 "D/G/tauprime" : Item(status='A ', wc_rev='-', copied='+'),
9845 "D/H" : Item(status=' ', wc_rev=8),
9846 "D/H/chi" : Item(status=' ', wc_rev=8),
9847 "D/H/omega" : Item(status=' ', wc_rev=8),
9848 "D/H/psi" : Item(status=' ', wc_rev=8),
9849 "" : Item(status=' M', wc_rev=8),
9851 k_expected_disk = wc.State('', {
9852 '' : Item(props={SVN_PROP_MERGEINFO : '/A_COPY:2-8'}),
9853 'B' : Item(),
9854 'B/lambda' : Item("This is the file 'lambda'.\n"),
9855 'B/E' : Item(),
9856 'B/E/alpha' : Item("This is the file 'alpha'.\n"),
9857 'B/E/beta' : Item("New content"),
9858 'B/F' : Item(),
9859 'mu' : Item("Changed on the branch."),
9860 'C' : Item(),
9861 'D' : Item(),
9862 'D/gamma' : Item("This is the file 'gamma'.\n"),
9863 'D/G' : Item(),
9864 'D/G/pi' : Item("This is the file 'pi'.\n"),
9865 'D/G/rho' : Item("New content"),
9866 'D/G/tau' : Item("This is the file 'tau'.\n"),
9867 'D/G/tauprime' : Item("This is the file 'tau'.\n",
9868 ### TODO(reint): why empty?
9869 props={SVN_PROP_MERGEINFO: ''}),
9870 'D/H' : Item(),
9871 'D/H/chi' : Item("This is the file 'chi'.\n"),
9872 'D/H/omega' : Item("New content"),
9873 'D/H/psi' : Item("New content"),
9875 expected_skip = wc.State(short_A_path, {})
9876 saved_cwd = os.getcwd()
9877 os.chdir(svntest.main.work_dir)
9878 svntest.actions.run_and_verify_merge(short_A_path, None, None,
9879 sbox.repo_url + '/A_COPY',
9880 expected_output,
9881 k_expected_disk,
9882 k_expected_status,
9883 expected_skip,
9884 None, None, None, None,
9885 None, True, True,
9886 '--reintegrate')
9887 os.chdir(saved_cwd)
9889 # Finally, commit the result of the merge (r9).
9890 expected_output = wc.State(wc_dir, {
9891 'A/D/G/tauprime' : Item(verb='Adding'),
9892 'A/mu' : Item(verb='Sending'),
9893 'A' : Item(verb='Sending'),
9895 expected_status.add({
9896 'A/D/G/tauprime' : Item(status=' ', wc_rev=9),
9898 expected_status.tweak('A', 'A/mu', wc_rev=9)
9899 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
9900 expected_status, None, wc_dir)
9902 def reintegrate_fail_on_modified_wc(sbox):
9903 "merge --reintegrate should fail in modified wc"
9904 sbox.build()
9905 wc_dir = sbox.wc_dir
9906 A_path = os.path.join(wc_dir, "A")
9907 mu_path = os.path.join(A_path, "mu")
9908 ignored_expected_disk, ignored_expected_status = set_up_branch(sbox)
9909 svntest.main.file_write(mu_path, "Changed on 'trunk' (the merge target).")
9910 svntest.actions.run_and_verify_merge(
9911 A_path, None, None, sbox.repo_url + '/A_COPY', None, None, None, None,
9912 ".*Cannot reintegrate into a working copy that has local modifications.*",
9913 None, None, None, None, True, False, '--reintegrate')
9915 def reintegrate_fail_on_mixed_rev_wc(sbox):
9916 "merge --reintegrate should fail in mixed-rev wc"
9917 sbox.build()
9918 wc_dir = sbox.wc_dir
9919 A_path = os.path.join(wc_dir, "A")
9920 mu_path = os.path.join(A_path, "mu")
9921 ignored_expected_disk, expected_status = set_up_branch(sbox)
9922 # Make and commit a change, in order to get a mixed-rev wc.
9923 svntest.main.file_write(mu_path, "Changed on 'trunk' (the merge target).")
9924 expected_output = wc.State(wc_dir, {
9925 'A/mu' : Item(verb='Sending'),
9927 expected_status.tweak('A/mu', wc_rev=7)
9928 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
9929 expected_status, None, wc_dir)
9930 # Try merging into that same wc, expecting failure.
9931 svntest.actions.run_and_verify_merge(
9932 A_path, None, None, sbox.repo_url + '/A_COPY', None, None, None, None,
9933 ".*Cannot reintegrate into mixed-revision working copy.*",
9934 None, None, None, None, True, False, '--reintegrate')
9936 def reintegrate_fail_on_switched_wc(sbox):
9937 "merge --reintegrate should fail in switched wc"
9938 sbox.build()
9939 wc_dir = sbox.wc_dir
9940 A_path = os.path.join(wc_dir, "A")
9941 G_path = os.path.join(A_path, "D", "G")
9942 switch_url = sbox.repo_url + "/A/D/H"
9943 expected_disk, expected_status = set_up_branch(sbox)
9945 # Switch a subdir of the target.
9946 expected_output = svntest.wc.State(wc_dir, {
9947 'A/D/G/pi' : Item(status='D '),
9948 'A/D/G/rho' : Item(status='D '),
9949 'A/D/G/tau' : Item(status='D '),
9950 'A/D/G/chi' : Item(status='A '),
9951 'A/D/G/psi' : Item(status='A '),
9952 'A/D/G/omega' : Item(status='A '),
9954 expected_disk.remove('A/D/G/pi', 'A/D/G/rho', 'A/D/G/tau')
9955 expected_disk.add({
9956 'A/D/G/chi' : Item(contents="This is the file 'chi'.\n"),
9957 'A/D/G/psi' : Item(contents="New content"),
9958 'A/D/G/omega' : Item(contents="New content"),
9960 expected_status.remove('A/D/G/pi', 'A/D/G/rho', 'A/D/G/tau')
9961 expected_status.add({
9962 'A/D/G' : Item(status=' ', wc_rev=6, switched='S'),
9963 'A/D/G/chi' : Item(status=' ', wc_rev=6),
9964 'A/D/G/psi' : Item(status=' ', wc_rev=6),
9965 'A/D/G/omega' : Item(status=' ', wc_rev=6),
9967 svntest.actions.run_and_verify_switch(wc_dir,
9968 G_path,
9969 switch_url,
9970 expected_output,
9971 expected_disk,
9972 expected_status,
9973 None, None, None, None, False);
9974 svntest.actions.run_and_verify_merge(
9975 A_path, None, None, sbox.repo_url + '/A_COPY', None, None, None, None,
9976 ".*Cannot reintegrate into a working copy with a switched subtree.*",
9977 None, None, None, None, True, False, '--reintegrate')
9979 def reintegrate_fail_on_shallow_wc(sbox):
9980 "merge --reintegrate should fail in shallow wc"
9981 sbox.build()
9982 wc_dir = sbox.wc_dir
9983 expected_disk, expected_status = set_up_branch(sbox)
9984 A_path = os.path.join(wc_dir, "A")
9985 G_path = os.path.join(A_path, "D", "G")
9986 # Our default checkout doesn't have any subdirs at non-infinite
9987 # depth, so we'll have to create one the old-fashioned way: remove a
9988 # tree, then "update" it back into existence at a shallower depth.
9989 svntest.main.safe_rmtree(G_path)
9990 svntest.actions.run_and_verify_svn(None, None, [], 'update', G_path,
9991 '--depth=files')
9992 # Even though everything is actually present (as G has no subdirs
9993 # anyway), the reintegration should fail, because G's depth is other
9994 # than infinity.
9995 svntest.actions.run_and_verify_merge(
9996 A_path, None, None, sbox.repo_url + '/A_COPY', None, None, None, None,
9997 ".*Cannot reintegrate into a working copy not.*at infinite depth.*",
9998 None, None, None, None, True, False, '--reintegrate')
10000 def reintegrate_fail_on_stale_source(sbox):
10001 "merge --reintegrate should fail on stale source"
10002 sbox.build()
10003 wc_dir = sbox.wc_dir
10004 expected_disk, expected_status = set_up_branch(sbox)
10005 A_path = os.path.join(wc_dir, "A")
10006 mu_path = os.path.join(A_path, "mu")
10007 svntest.main.file_append(mu_path, 'some text appended to mu\n')
10008 svntest.actions.run_and_verify_svn(None, None, [], 'commit',
10009 '-m', 'a change to mu', mu_path);
10010 # Unmix the revisions in the working copy.
10011 svntest.actions.run_and_verify_svn(None, None, [], 'update', wc_dir);
10012 # The merge --reintegrate should fail because target has changes not
10013 # present in source.
10014 svntest.actions.run_and_verify_merge(
10015 A_path, None, None, sbox.repo_url + '/A_COPY', None, None, None, None,
10016 ".*", ###TODO(reint): need a more specific check here
10017 None, None, None, None, True, False, '--reintegrate')
10019 def dont_add_mergeinfo_from_own_history(sbox):
10020 "cyclic merges don't add mergeinfo from own history"
10022 sbox.build()
10023 wc_dir = sbox.wc_dir
10024 wc_disk, wc_status = set_up_branch(sbox)
10026 # Some paths we'll care about
10027 A_path = os.path.join(wc_dir, "A")
10028 A_MOVED_path = os.path.join(wc_dir, "A_MOVED")
10029 mu_path = os.path.join(wc_dir, "A", "mu")
10030 mu_MOVED_path = os.path.join(wc_dir, "A_MOVED", "mu")
10031 A_COPY_path = os.path.join(wc_dir, "A_COPY")
10032 mu_COPY_path = os.path.join(wc_dir, "A_COPY", "mu")
10034 # Merge r3 from 'A' to 'A_COPY', make a text mod to 'A_COPY/mu' and
10035 # commit both as r7. This results in mergeinfo of '/A:3' on 'A_COPY'.
10036 # Then merge r7 from 'A_COPY' to 'A'. This attempts to add the mergeinfo
10037 # '/A:3' to 'A', but that is self-referrential and should be filtered out,
10038 # leaving only the mergeinfo '/A_COPY:7' on 'A'.
10040 # Search for the comment entitled "The Merge Kluge" elsewhere in
10041 # this file, to understand why we shorten and chdir() below.
10042 short_A_COPY_path = shorten_path_kludge(A_COPY_path)
10043 expected_output = wc.State(short_A_COPY_path, {
10044 'D/H/psi' : Item(status='U '),
10046 expected_A_COPY_status = wc.State(short_A_COPY_path, {
10047 '' : Item(status=' M', wc_rev=2),
10048 'B' : Item(status=' ', wc_rev=2),
10049 'mu' : Item(status=' ', wc_rev=2),
10050 'B/E' : Item(status=' ', wc_rev=2),
10051 'B/E/alpha' : Item(status=' ', wc_rev=2),
10052 'B/E/beta' : Item(status=' ', wc_rev=2),
10053 'B/lambda' : Item(status=' ', wc_rev=2),
10054 'B/F' : Item(status=' ', wc_rev=2),
10055 'C' : Item(status=' ', wc_rev=2),
10056 'D' : Item(status=' ', wc_rev=2),
10057 'D/G' : Item(status=' ', wc_rev=2),
10058 'D/G/pi' : Item(status=' ', wc_rev=2),
10059 'D/G/rho' : Item(status=' ', wc_rev=2),
10060 'D/G/tau' : Item(status=' ', wc_rev=2),
10061 'D/gamma' : Item(status=' ', wc_rev=2),
10062 'D/H' : Item(status=' ', wc_rev=2),
10063 'D/H/chi' : Item(status=' ', wc_rev=2),
10064 'D/H/psi' : Item(status='M ', wc_rev=2),
10065 'D/H/omega' : Item(status=' ', wc_rev=2),
10067 expected_A_COPY_disk = wc.State('', {
10068 '' : Item(props={SVN_PROP_MERGEINFO : '/A:3'}),
10069 'B' : Item(),
10070 'mu' : Item("This is the file 'mu'.\n"),
10071 'B/E' : Item(),
10072 'B/E/alpha' : Item("This is the file 'alpha'.\n"),
10073 'B/E/beta' : Item("This is the file 'beta'.\n"),
10074 'B/lambda' : Item("This is the file 'lambda'.\n"),
10075 'B/F' : Item(),
10076 'C' : Item(),
10077 'D' : Item(),
10078 'D/G' : Item(),
10079 'D/G/pi' : Item("This is the file 'pi'.\n"),
10080 'D/G/rho' : Item("This is the file 'rho'.\n"),
10081 'D/G/tau' : Item("This is the file 'tau'.\n"),
10082 'D/gamma' : Item("This is the file 'gamma'.\n"),
10083 'D/H' : Item(),
10084 'D/H/chi' : Item("This is the file 'chi'.\n"),
10085 'D/H/psi' : Item("New content"),
10086 'D/H/omega' : Item("This is the file 'omega'.\n"),
10088 expected_A_COPY_skip = wc.State(short_A_COPY_path, { })
10089 saved_cwd = os.getcwd()
10090 os.chdir(svntest.main.work_dir)
10091 svntest.actions.run_and_verify_merge(short_A_COPY_path, '2', '3',
10092 sbox.repo_url + \
10093 '/A',
10094 expected_output,
10095 expected_A_COPY_disk,
10096 expected_A_COPY_status,
10097 expected_A_COPY_skip,
10098 None, None, None, None,
10099 None, 1)
10100 os.chdir(saved_cwd)
10102 # Change 'A_COPY/mu'
10103 svntest.main.file_write(mu_COPY_path, "New content")
10105 # Commit r7
10106 expected_output = wc.State(wc_dir, {
10107 'A_COPY' : Item(verb='Sending'),
10108 'A_COPY/D/H/psi' : Item(verb='Sending'),
10109 'A_COPY/mu' : Item(verb='Sending'),
10111 wc_status.tweak('A_COPY', 'A_COPY/D/H/psi', 'A_COPY/mu', wc_rev=7)
10112 svntest.actions.run_and_verify_commit(wc_dir,
10113 expected_output,
10114 wc_status,
10115 None,
10116 wc_dir)
10118 # Merge r7 back to the 'A'
10119 short_A_path = shorten_path_kludge(A_path)
10120 expected_output = wc.State(short_A_path, {
10121 'mu' : Item(status='U '),
10123 expected_A_status = wc.State(short_A_path, {
10124 '' : Item(status=' M', wc_rev=1),
10125 'B' : Item(status=' ', wc_rev=1),
10126 'mu' : Item(status='M ', wc_rev=1),
10127 'B/E' : Item(status=' ', wc_rev=1),
10128 'B/E/alpha' : Item(status=' ', wc_rev=1),
10129 'B/E/beta' : Item(status=' ', wc_rev=5),
10130 'B/lambda' : Item(status=' ', wc_rev=1),
10131 'B/F' : Item(status=' ', wc_rev=1),
10132 'C' : Item(status=' ', wc_rev=1),
10133 'D' : Item(status=' ', wc_rev=1),
10134 'D/G' : Item(status=' ', wc_rev=1),
10135 'D/G/pi' : Item(status=' ', wc_rev=1),
10136 'D/G/rho' : Item(status=' ', wc_rev=4),
10137 'D/G/tau' : Item(status=' ', wc_rev=1),
10138 'D/gamma' : Item(status=' ', wc_rev=1),
10139 'D/H' : Item(status=' ', wc_rev=1),
10140 'D/H/chi' : Item(status=' ', wc_rev=1),
10141 'D/H/psi' : Item(status=' ', wc_rev=3),
10142 'D/H/omega' : Item(status=' ', wc_rev=6),
10144 expected_A_disk = wc.State('', {
10145 '' : Item(props={SVN_PROP_MERGEINFO : '/A_COPY:7'}),
10146 'B' : Item(),
10147 'mu' : Item("New content"),
10148 'B/E' : Item(),
10149 'B/E/alpha' : Item("This is the file 'alpha'.\n"),
10150 'B/E/beta' : Item("New content"),
10151 'B/lambda' : Item("This is the file 'lambda'.\n"),
10152 'B/F' : Item(),
10153 'C' : Item(),
10154 'D' : Item(),
10155 'D/G' : Item(),
10156 'D/G/pi' : Item("This is the file 'pi'.\n"),
10157 'D/G/rho' : Item("New content"),
10158 'D/G/tau' : Item("This is the file 'tau'.\n"),
10159 'D/gamma' : Item("This is the file 'gamma'.\n"),
10160 'D/H' : Item(),
10161 'D/H/chi' : Item("This is the file 'chi'.\n"),
10162 'D/H/psi' : Item("New content"),
10163 'D/H/omega' : Item("New content"),
10165 expected_A_skip = wc.State(short_A_path, {})
10166 os.chdir(svntest.main.work_dir)
10167 svntest.actions.run_and_verify_merge(short_A_path, '6', '7',
10168 sbox.repo_url + \
10169 '/A_COPY',
10170 expected_output,
10171 expected_A_disk,
10172 expected_A_status,
10173 expected_A_skip,
10174 None, None, None, None,
10175 None, 1)
10176 os.chdir(saved_cwd)
10178 # Revert all local mods
10179 svntest.actions.run_and_verify_svn(None,
10180 ["Reverted '" + A_path + "'\n",
10181 "Reverted '" + mu_path + "'\n"],
10182 [], 'revert', '-R', wc_dir)
10184 # Move 'A' to 'A_MOVED' and once again merge r7 from 'A_COPY', this time
10185 # to 'A_MOVED'. This attempts to add the mergeinfo '/A:3' to
10186 # 'A_MOVED', but 'A_MOVED@3' is 'A', so again this mergeinfo is filtered
10187 # out, leaving the only the mergeinfo created from the merge itself:
10188 # '/A_COPY:7'.
10189 svntest.actions.run_and_verify_svn(None,
10190 ['\n', 'Committed revision 8.\n'],
10191 [], 'move',
10192 sbox.repo_url + '/A',
10193 sbox.repo_url + '/A_MOVED',
10194 '-m', 'Copy A to A_MOVED')
10195 wc_status.remove('A', 'A/B', 'A/B/lambda', 'A/B/E', 'A/B/E/alpha',
10196 'A/B/E/beta', 'A/B/F', 'A/mu', 'A/C', 'A/D', 'A/D/gamma', 'A/D/G',
10197 'A/D/G/pi', 'A/D/G/rho', 'A/D/G/tau', 'A/D/H', 'A/D/H/chi',
10198 'A/D/H/omega', 'A/D/H/psi')
10199 wc_status.add({
10200 'A_MOVED' : Item(),
10201 'A_MOVED/B' : Item(),
10202 'A_MOVED/B/lambda' : Item(),
10203 'A_MOVED/B/E' : Item(),
10204 'A_MOVED/B/E/alpha' : Item(),
10205 'A_MOVED/B/E/beta' : Item(),
10206 'A_MOVED/B/F' : Item(),
10207 'A_MOVED/mu' : Item(),
10208 'A_MOVED/C' : Item(),
10209 'A_MOVED/D' : Item(),
10210 'A_MOVED/D/gamma' : Item(),
10211 'A_MOVED/D/G' : Item(),
10212 'A_MOVED/D/G/pi' : Item(),
10213 'A_MOVED/D/G/rho' : Item(),
10214 'A_MOVED/D/G/tau' : Item(),
10215 'A_MOVED/D/H' : Item(),
10216 'A_MOVED/D/H/chi' : Item(),
10217 'A_MOVED/D/H/omega' : Item(),
10218 'A_MOVED/D/H/psi' : Item(),
10220 wc_status.tweak(wc_rev=8, status=' ')
10221 wc_disk.remove('A', 'A/B', 'A/B/lambda', 'A/B/E', 'A/B/E/alpha',
10222 'A/B/E/beta', 'A/B/F', 'A/mu', 'A/C', 'A/D', 'A/D/gamma',
10223 'A/D/G', 'A/D/G/pi', 'A/D/G/rho', 'A/D/G/tau', 'A/D/H',
10224 'A/D/H/chi', 'A/D/H/omega', 'A/D/H/psi' )
10225 wc_disk.add({
10226 'A_MOVED' : Item(),
10227 'A_MOVED/B' : Item(),
10228 'A_MOVED/B/lambda' : Item("This is the file 'lambda'.\n"),
10229 'A_MOVED/B/E' : Item(),
10230 'A_MOVED/B/E/alpha' : Item("This is the file 'alpha'.\n"),
10231 'A_MOVED/B/E/beta' : Item("New content"),
10232 'A_MOVED/B/F' : Item(),
10233 'A_MOVED/mu' : Item("This is the file 'mu'.\n"),
10234 'A_MOVED/C' : Item(),
10235 'A_MOVED/D' : Item(),
10236 'A_MOVED/D/gamma' : Item("This is the file 'gamma'.\n"),
10237 'A_MOVED/D/G' : Item(),
10238 'A_MOVED/D/G/pi' : Item("This is the file 'pi'.\n"),
10239 'A_MOVED/D/G/rho' : Item("New content"),
10240 'A_MOVED/D/G/tau' : Item("This is the file 'tau'.\n"),
10241 'A_MOVED/D/H' : Item(),
10242 'A_MOVED/D/H/chi' : Item("This is the file 'chi'.\n"),
10243 'A_MOVED/D/H/omega' : Item("New content"),
10244 'A_MOVED/D/H/psi' : Item("New content"),
10246 wc_disk.tweak('A_COPY/D/H/psi', 'A_COPY/mu', contents='New content')
10247 wc_disk.tweak('A_COPY', props={SVN_PROP_MERGEINFO : '/A:3'})
10248 expected_output = wc.State(wc_dir, {
10249 'A' : Item(status='D '),
10250 'A_MOVED' : Item(status='A '),
10251 'A_MOVED/B' : Item(status='A '),
10252 'A_MOVED/B/lambda' : Item(status='A '),
10253 'A_MOVED/B/E' : Item(status='A '),
10254 'A_MOVED/B/E/alpha' : Item(status='A '),
10255 'A_MOVED/B/E/beta' : Item(status='A '),
10256 'A_MOVED/B/F' : Item(status='A '),
10257 'A_MOVED/mu' : Item(status='A '),
10258 'A_MOVED/C' : Item(status='A '),
10259 'A_MOVED/D' : Item(status='A '),
10260 'A_MOVED/D/gamma' : Item(status='A '),
10261 'A_MOVED/D/G' : Item(status='A '),
10262 'A_MOVED/D/G/pi' : Item(status='A '),
10263 'A_MOVED/D/G/rho' : Item(status='A '),
10264 'A_MOVED/D/G/tau' : Item(status='A '),
10265 'A_MOVED/D/H' : Item(status='A '),
10266 'A_MOVED/D/H/chi' : Item(status='A '),
10267 'A_MOVED/D/H/omega' : Item(status='A '),
10268 'A_MOVED/D/H/psi' : Item(status='A ')
10270 svntest.actions.run_and_verify_update(wc_dir,
10271 expected_output,
10272 wc_disk,
10273 wc_status,
10274 None, None, None, None, None,
10275 True)
10277 short_A_MOVED_path = shorten_path_kludge(A_MOVED_path)
10278 expected_output = wc.State(short_A_MOVED_path, {
10279 'mu' : Item(status='U '),
10281 expected_A_status = wc.State(short_A_MOVED_path, {
10282 '' : Item(status=' M', wc_rev=8),
10283 'B' : Item(status=' ', wc_rev=8),
10284 'mu' : Item(status='M ', wc_rev=8),
10285 'B/E' : Item(status=' ', wc_rev=8),
10286 'B/E/alpha' : Item(status=' ', wc_rev=8),
10287 'B/E/beta' : Item(status=' ', wc_rev=8),
10288 'B/lambda' : Item(status=' ', wc_rev=8),
10289 'B/F' : Item(status=' ', wc_rev=8),
10290 'C' : Item(status=' ', wc_rev=8),
10291 'D' : Item(status=' ', wc_rev=8),
10292 'D/G' : Item(status=' ', wc_rev=8),
10293 'D/G/pi' : Item(status=' ', wc_rev=8),
10294 'D/G/rho' : Item(status=' ', wc_rev=8),
10295 'D/G/tau' : Item(status=' ', wc_rev=8),
10296 'D/gamma' : Item(status=' ', wc_rev=8),
10297 'D/H' : Item(status=' ', wc_rev=8),
10298 'D/H/chi' : Item(status=' ', wc_rev=8),
10299 'D/H/psi' : Item(status=' ', wc_rev=8),
10300 'D/H/omega' : Item(status=' ', wc_rev=8),
10302 # We can reuse expected_A_disk from above without change.
10303 os.chdir(svntest.main.work_dir)
10304 svntest.actions.run_and_verify_merge(short_A_MOVED_path, '6', '7',
10305 sbox.repo_url + \
10306 '/A_COPY',
10307 expected_output,
10308 expected_A_disk,
10309 expected_A_status,
10310 expected_A_skip,
10311 None, None, None, None,
10312 None, 1)
10313 os.chdir(saved_cwd)
10315 # Revert all local mods
10316 svntest.actions.run_and_verify_svn(None,
10317 ["Reverted '" + A_MOVED_path + "'\n",
10318 "Reverted '" + mu_MOVED_path + "'\n"],
10319 [], 'revert', '-R', wc_dir)
10321 # Create a new 'A' unrelated to the old 'A' which was moved. Then merge
10322 # r7 from 'A_COPY' to this new 'A'. Since the new 'A' shares no history
10323 # with the mergeinfo 'A@3', the mergeinfo '/A:3' is added and when combined
10324 # with the mergeinfo created from the merge should result in
10325 # '/A:3\n/A_COPY:7'
10327 # Create the new 'A' by exporting the old 'A@1'.
10328 expected_output = svntest.verify.UnorderedOutput(
10329 ["A " + os.path.join(wc_dir, "A") + "\n",
10330 "A " + os.path.join(wc_dir, "A", "B") + "\n",
10331 "A " + os.path.join(wc_dir, "A", "B", "lambda") + "\n",
10332 "A " + os.path.join(wc_dir, "A", "B", "E") + "\n",
10333 "A " + os.path.join(wc_dir, "A", "B", "E", "alpha") + "\n",
10334 "A " + os.path.join(wc_dir, "A", "B", "E", "beta") + "\n",
10335 "A " + os.path.join(wc_dir, "A", "B", "F") + "\n",
10336 "A " + os.path.join(wc_dir, "A", "mu") + "\n",
10337 "A " + os.path.join(wc_dir, "A", "C") + "\n",
10338 "A " + os.path.join(wc_dir, "A", "D") + "\n",
10339 "A " + os.path.join(wc_dir, "A", "D", "gamma") + "\n",
10340 "A " + os.path.join(wc_dir, "A", "D", "G") + "\n",
10341 "A " + os.path.join(wc_dir, "A", "D", "G", "pi") + "\n",
10342 "A " + os.path.join(wc_dir, "A", "D", "G", "rho") + "\n",
10343 "A " + os.path.join(wc_dir, "A", "D", "G", "tau") + "\n",
10344 "A " + os.path.join(wc_dir, "A", "D", "H") + "\n",
10345 "A " + os.path.join(wc_dir, "A", "D", "H", "chi") + "\n",
10346 "A " + os.path.join(wc_dir, "A", "D", "H", "omega") + "\n",
10347 "A " + os.path.join(wc_dir, "A", "D", "H", "psi") + "\n",
10348 "Exported revision 1.\n",]
10350 svntest.actions.run_and_verify_svn(None, expected_output, [],
10351 'export', sbox.repo_url + '/A@1',
10352 A_path)
10353 expected_output = svntest.verify.UnorderedOutput(
10354 ["A " + os.path.join(wc_dir, "A") + "\n",
10355 "A " + os.path.join(wc_dir, "A", "B") + "\n",
10356 "A " + os.path.join(wc_dir, "A", "B", "lambda") + "\n",
10357 "A " + os.path.join(wc_dir, "A", "B", "E") + "\n",
10358 "A " + os.path.join(wc_dir, "A", "B", "E", "alpha") + "\n",
10359 "A " + os.path.join(wc_dir, "A", "B", "E", "beta") + "\n",
10360 "A " + os.path.join(wc_dir, "A", "B", "F") + "\n",
10361 "A " + os.path.join(wc_dir, "A", "mu") + "\n",
10362 "A " + os.path.join(wc_dir, "A", "C") + "\n",
10363 "A " + os.path.join(wc_dir, "A", "D") + "\n",
10364 "A " + os.path.join(wc_dir, "A", "D", "gamma") + "\n",
10365 "A " + os.path.join(wc_dir, "A", "D", "G") + "\n",
10366 "A " + os.path.join(wc_dir, "A", "D", "G", "pi") + "\n",
10367 "A " + os.path.join(wc_dir, "A", "D", "G", "rho") + "\n",
10368 "A " + os.path.join(wc_dir, "A", "D", "G", "tau") + "\n",
10369 "A " + os.path.join(wc_dir, "A", "D", "H") + "\n",
10370 "A " + os.path.join(wc_dir, "A", "D", "H", "chi") + "\n",
10371 "A " + os.path.join(wc_dir, "A", "D", "H", "omega") + "\n",
10372 "A " + os.path.join(wc_dir, "A", "D", "H", "psi") + "\n",]
10374 svntest.actions.run_and_verify_svn(None, expected_output, [],
10375 'add', A_path)
10376 # Commit the new 'A' as r9
10377 expected_output = wc.State(wc_dir, {
10378 'A' : Item(verb='Adding'),
10379 'A/B' : Item(verb='Adding'),
10380 'A/mu' : Item(verb='Adding'),
10381 'A/B/E' : Item(verb='Adding'),
10382 'A/B/E/alpha' : Item(verb='Adding'),
10383 'A/B/E/beta' : Item(verb='Adding'),
10384 'A/B/lambda' : Item(verb='Adding'),
10385 'A/B/F' : Item(verb='Adding'),
10386 'A/C' : Item(verb='Adding'),
10387 'A/D' : Item(verb='Adding'),
10388 'A/D/G' : Item(verb='Adding'),
10389 'A/D/G/pi' : Item(verb='Adding'),
10390 'A/D/G/rho' : Item(verb='Adding'),
10391 'A/D/G/tau' : Item(verb='Adding'),
10392 'A/D/gamma' : Item(verb='Adding'),
10393 'A/D/H' : Item(verb='Adding'),
10394 'A/D/H/chi' : Item(verb='Adding'),
10395 'A/D/H/psi' : Item(verb='Adding'),
10396 'A/D/H/omega' : Item(verb='Adding'),
10398 wc_status.tweak(wc_rev=8)
10399 wc_status.add({
10400 'A' : Item(wc_rev=9),
10401 'A/B' : Item(wc_rev=9),
10402 'A/B/lambda' : Item(wc_rev=9),
10403 'A/B/E' : Item(wc_rev=9),
10404 'A/B/E/alpha' : Item(wc_rev=9),
10405 'A/B/E/beta' : Item(wc_rev=9),
10406 'A/B/F' : Item(wc_rev=9),
10407 'A/mu' : Item(wc_rev=9),
10408 'A/C' : Item(wc_rev=9),
10409 'A/D' : Item(wc_rev=9),
10410 'A/D/gamma' : Item(wc_rev=9),
10411 'A/D/G' : Item(wc_rev=9),
10412 'A/D/G/pi' : Item(wc_rev=9),
10413 'A/D/G/rho' : Item(wc_rev=9),
10414 'A/D/G/tau' : Item(wc_rev=9),
10415 'A/D/H' : Item(wc_rev=9),
10416 'A/D/H/chi' : Item(wc_rev=9),
10417 'A/D/H/omega' : Item(wc_rev=9),
10418 'A/D/H/psi' : Item(wc_rev=9),
10420 wc_status.tweak(status=' ')
10421 svntest.actions.run_and_verify_commit(wc_dir,
10422 expected_output,
10423 wc_status,
10424 None,
10425 wc_dir)
10427 expected_output = wc.State(short_A_path, {
10428 'mu' : Item(status='U '),
10429 'D/H/psi' : Item(status='U '),
10430 '' : Item(status=' U'),
10432 expected_A_status = wc.State(short_A_path, {
10433 '' : Item(status=' M', wc_rev=9),
10434 'B' : Item(status=' ', wc_rev=9),
10435 'mu' : Item(status='M ', wc_rev=9),
10436 'B/E' : Item(status=' ', wc_rev=9),
10437 'B/E/alpha' : Item(status=' ', wc_rev=9),
10438 'B/E/beta' : Item(status=' ', wc_rev=9),
10439 'B/lambda' : Item(status=' ', wc_rev=9),
10440 'B/F' : Item(status=' ', wc_rev=9),
10441 'C' : Item(status=' ', wc_rev=9),
10442 'D' : Item(status=' ', wc_rev=9),
10443 'D/G' : Item(status=' ', wc_rev=9),
10444 'D/G/pi' : Item(status=' ', wc_rev=9),
10445 'D/G/rho' : Item(status=' ', wc_rev=9),
10446 'D/G/tau' : Item(status=' ', wc_rev=9),
10447 'D/gamma' : Item(status=' ', wc_rev=9),
10448 'D/H' : Item(status=' ', wc_rev=9),
10449 'D/H/chi' : Item(status=' ', wc_rev=9),
10450 'D/H/psi' : Item(status='M ', wc_rev=9),
10451 'D/H/omega' : Item(status=' ', wc_rev=9),
10453 expected_A_disk = wc.State('', {
10454 '' : Item(props={SVN_PROP_MERGEINFO : '/A:3\n/A_COPY:7\n'}),
10455 'B' : Item(),
10456 'mu' : Item("New content"),
10457 'B/E' : Item(),
10458 'B/E/alpha' : Item("This is the file 'alpha'.\n"),
10459 'B/E/beta' : Item("This is the file 'beta'.\n"),
10460 'B/lambda' : Item("This is the file 'lambda'.\n"),
10461 'B/F' : Item(),
10462 'C' : Item(),
10463 'D' : Item(),
10464 'D/G' : Item(),
10465 'D/G/pi' : Item("This is the file 'pi'.\n"),
10466 'D/G/rho' : Item("This is the file 'rho'.\n"),
10467 'D/G/tau' : Item("This is the file 'tau'.\n"),
10468 'D/gamma' : Item("This is the file 'gamma'.\n"),
10469 'D/H' : Item(),
10470 'D/H/chi' : Item("This is the file 'chi'.\n"),
10471 'D/H/psi' : Item("New content"),
10472 'D/H/omega' : Item("This is the file 'omega'.\n"),
10474 expected_A_skip = wc.State(short_A_path, {})
10475 os.chdir(svntest.main.work_dir)
10476 svntest.actions.run_and_verify_merge(short_A_path, '6', '7',
10477 sbox.repo_url + \
10478 '/A_COPY',
10479 expected_output,
10480 expected_A_disk,
10481 expected_A_status,
10482 expected_A_skip,
10483 None, None, None, None,
10484 None, 1)
10486 def merge_range_predates_history(sbox):
10487 "merge range predates history (issue #3094)"
10489 sbox.build()
10490 wc_dir = sbox.wc_dir
10492 iota_path = os.path.join(wc_dir, "iota")
10493 trunk_file_path = os.path.join(wc_dir, "trunk", "file")
10494 trunk_url = sbox.repo_url + "/trunk"
10495 branches_url = sbox.repo_url + "/branches"
10496 branch_path = os.path.join(wc_dir, "branches", "branch")
10497 branch_file_path = os.path.join(wc_dir, "branches", "branch", "file")
10498 branch_url = sbox.repo_url + "/branches/branch"
10500 # Tweak a file and commit. (r2)
10501 svntest.main.file_append(iota_path, "More data.\n")
10502 svntest.main.run_svn(None, 'ci', '-m', 'tweak iota', wc_dir)
10504 # Create our trunk and branches directory, and update working copy. (r3)
10505 svntest.main.run_svn(None, 'mkdir', trunk_url, branches_url,
10506 '-m', 'add trunk and branches dirs')
10507 svntest.main.run_svn(None, 'up', wc_dir)
10509 # Add a file to the trunk and commit. (r4)
10510 svntest.main.file_append(trunk_file_path, "This is the file 'file'.\n")
10511 svntest.main.run_svn(None, 'add', trunk_file_path)
10512 svntest.main.run_svn(None, 'ci', '-m', 'add trunk file', wc_dir)
10514 # Branch trunk from r3, and update working copy. (r5)
10515 svntest.main.run_svn(None, 'cp', trunk_url, branch_url, '-r3',
10516 '-m', 'branch trunk@2')
10517 svntest.main.run_svn(None, 'up', wc_dir)
10519 # Now, try to merge trunk into the branch. There should be one
10520 # outstanding change -- the addition of the file.
10521 expected_output = expected_merge_output([[4,5]],
10522 'A ' + branch_file_path + '\n')
10523 svntest.actions.run_and_verify_svn(None, expected_output, [], 'merge',
10524 trunk_url, branch_path)
10527 def foreign_repos(sbox):
10528 "merge from a foreign repository"
10530 sbox.build()
10531 wc_dir = sbox.wc_dir
10533 # Make a copy of this repository and associated working copy. Both
10534 # should have nothing but a Greek tree in them, and the two
10535 # repository UUIDs should differ.
10536 sbox2 = sbox.clone_dependent(True)
10537 sbox2.build()
10538 wc_dir2 = sbox2.wc_dir
10540 # Convenience variables for working copy paths.
10541 Z_path = os.path.join(wc_dir, 'A', 'D', 'G', 'Z')
10542 Q_path = os.path.join(wc_dir, 'Q')
10543 H_path = os.path.join(wc_dir, 'A', 'D', 'H')
10544 iota_path = os.path.join(wc_dir, 'iota')
10545 beta_path = os.path.join(wc_dir, 'A', 'B', 'E', 'beta')
10546 alpha_path = os.path.join(wc_dir, 'A', 'B', 'E', 'alpha')
10547 zeta_path = os.path.join(wc_dir, 'A', 'D', 'G', 'Z', 'zeta')
10548 fred_path = os.path.join(wc_dir, 'A', 'C', 'fred')
10550 # Add new directories
10551 svntest.main.run_svn(None, 'mkdir', Q_path, Z_path)
10553 # Add new files
10554 zeta_contents = "This is the file 'zeta'.\n"
10555 fred_contents = "This is the file 'fred'.\n"
10556 svntest.main.file_append(zeta_path, zeta_contents)
10557 svntest.main.file_append(fred_path, fred_contents)
10558 svntest.main.run_svn(None, 'add', zeta_path, fred_path)
10560 # Modify existing files
10561 added_contents = "This is another line of text.\n"
10562 svntest.main.file_append(iota_path, added_contents)
10563 svntest.main.file_append(beta_path, added_contents)
10565 # Delete some stuff
10566 svntest.main.run_svn(None, 'delete', alpha_path, H_path)
10568 # Commit up these changes.
10569 expected_output = wc.State(wc_dir, {
10570 'Q' : Item(verb='Adding'),
10571 'A/D/G/Z' : Item(verb='Adding'),
10572 'A/D/G/Z/zeta' : Item(verb='Adding'),
10573 'A/C/fred' : Item(verb='Adding'),
10574 'iota' : Item(verb='Sending'),
10575 'A/B/E/beta' : Item(verb='Sending'),
10576 'A/B/E/alpha' : Item(verb='Deleting'),
10577 'A/D/H' : Item(verb='Deleting'),
10579 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
10580 expected_status.add({
10581 'Q' : Item(status=' ', wc_rev=2),
10582 'A/D/G/Z' : Item(status=' ', wc_rev=2),
10583 'A/D/G/Z/zeta' : Item(status=' ', wc_rev=2),
10584 'A/C/fred' : Item(status=' ', wc_rev=2),
10586 expected_status.tweak('iota', 'A/B/E/beta', wc_rev=2)
10587 expected_status.remove('A/B/E/alpha', 'A/D/H', 'A/D/H/chi',
10588 'A/D/H/psi', 'A/D/H/omega')
10589 expected_disk = svntest.main.greek_state.copy()
10590 expected_disk.add({
10591 'Q' : Item(),
10592 'A/D/G/Z' : Item(),
10593 'A/D/G/Z/zeta' : Item(contents=zeta_contents),
10594 'A/C/fred' : Item(contents=fred_contents),
10596 expected_disk.remove('A/B/E/alpha', 'A/D/H', 'A/D/H/chi',
10597 'A/D/H/psi', 'A/D/H/omega')
10598 expected_disk.tweak('iota',
10599 contents=expected_disk.desc['iota'].contents
10600 + added_contents)
10601 expected_disk.tweak('A/B/E/beta',
10602 contents=expected_disk.desc['A/B/E/beta'].contents
10603 + added_contents)
10604 svntest.actions.run_and_verify_commit(wc_dir,
10605 expected_output,
10606 expected_status,
10607 None,
10608 wc_dir)
10609 svntest.actions.verify_disk(wc_dir, expected_disk,
10610 None, None, None, None, 1)
10612 # Now, merge our committed revision into a working copy of another
10613 # repository. Not only should the merge succeed, but the results on
10614 # disk should match those in our first working copy.
10616 ### TODO: Use run_and_verify_merge() ###
10617 svntest.main.run_svn(None, 'merge', '-c2', sbox.repo_url, wc_dir2)
10618 svntest.main.run_svn(None, 'ci', '-m', 'Merge from foreign repos', wc_dir2)
10619 svntest.actions.verify_disk(wc_dir2, expected_disk,
10620 None, None, None, None, 1)
10623 def foreign_repos_2_url(sbox):
10624 "2-url merge from a foreign repository"
10626 sbox.build()
10627 wc_dir = sbox.wc_dir
10629 # Make a copy of this repository and associated working copy. Both
10630 # should have nothing but a Greek tree in them, and the two
10631 # repository UUIDs should differ.
10632 sbox2 = sbox.clone_dependent(True)
10633 sbox2.build()
10634 wc_dir2 = sbox2.wc_dir
10636 # Convenience variables for working copy paths.
10637 Z_path = os.path.join(wc_dir, 'A', 'D', 'G', 'Z')
10638 Q_path = os.path.join(wc_dir, 'A', 'Q')
10639 H_path = os.path.join(wc_dir, 'A', 'D', 'H')
10640 beta_path = os.path.join(wc_dir, 'A', 'B', 'E', 'beta')
10641 alpha_path = os.path.join(wc_dir, 'A', 'B', 'E', 'alpha')
10642 zeta_path = os.path.join(wc_dir, 'A', 'D', 'G', 'Z', 'zeta')
10643 fred_path = os.path.join(wc_dir, 'A', 'C', 'fred')
10645 # First, "tag" the current state of the repository.
10646 svntest.main.run_svn(None, 'copy', sbox.repo_url + '/A',
10647 sbox.repo_url + '/A-tag1', '-m', 'tag1')
10649 # Add new directories
10650 svntest.main.run_svn(None, 'mkdir', Q_path, Z_path)
10652 # Add new files
10653 zeta_contents = "This is the file 'zeta'.\n"
10654 fred_contents = "This is the file 'fred'.\n"
10655 svntest.main.file_append(zeta_path, zeta_contents)
10656 svntest.main.file_append(fred_path, fred_contents)
10657 svntest.main.run_svn(None, 'add', zeta_path, fred_path)
10659 # Modify existing files
10660 added_contents = "This is another line of text.\n"
10661 svntest.main.file_append(beta_path, added_contents)
10663 # Delete some stuff
10664 svntest.main.run_svn(None, 'delete', alpha_path, H_path)
10666 # Commit up these changes.
10667 expected_output = wc.State(wc_dir, {
10668 'A/Q' : Item(verb='Adding'),
10669 'A/D/G/Z' : Item(verb='Adding'),
10670 'A/D/G/Z/zeta' : Item(verb='Adding'),
10671 'A/C/fred' : Item(verb='Adding'),
10672 'A/B/E/beta' : Item(verb='Sending'),
10673 'A/B/E/alpha' : Item(verb='Deleting'),
10674 'A/D/H' : Item(verb='Deleting'),
10676 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
10677 expected_status.add({
10678 'A/Q' : Item(status=' ', wc_rev=3),
10679 'A/D/G/Z' : Item(status=' ', wc_rev=3),
10680 'A/D/G/Z/zeta' : Item(status=' ', wc_rev=3),
10681 'A/C/fred' : Item(status=' ', wc_rev=3),
10683 expected_status.tweak('A/B/E/beta', wc_rev=3)
10684 expected_status.remove('A/B/E/alpha', 'A/D/H', 'A/D/H/chi',
10685 'A/D/H/psi', 'A/D/H/omega')
10686 expected_disk = svntest.main.greek_state.copy()
10687 expected_disk.add({
10688 'A/Q' : Item(),
10689 'A/D/G/Z' : Item(),
10690 'A/D/G/Z/zeta' : Item(contents=zeta_contents),
10691 'A/C/fred' : Item(contents=fred_contents),
10693 expected_disk.remove('A/B/E/alpha', 'A/D/H', 'A/D/H/chi',
10694 'A/D/H/psi', 'A/D/H/omega')
10695 expected_disk.tweak('A/B/E/beta',
10696 contents=expected_disk.desc['A/B/E/beta'].contents
10697 + added_contents)
10698 svntest.actions.run_and_verify_commit(wc_dir,
10699 expected_output,
10700 expected_status,
10701 None,
10702 wc_dir)
10703 svntest.actions.verify_disk(wc_dir, expected_disk,
10704 None, None, None, None, 1)
10706 # Now, "tag" the new state of the repository.
10707 svntest.main.run_svn(None, 'copy', sbox.repo_url + '/A',
10708 sbox.repo_url + '/A-tag2', '-m', 'tag2')
10710 # Now, merge across our "tags" (copies of /A) into the /A of a
10711 # working copy of another repository. Not only should the merge
10712 # succeed, but the results on disk should match those in our first
10713 # working copy.
10715 ### TODO: Use run_and_verify_merge() ###
10716 svntest.main.run_svn(None, 'merge', sbox.repo_url + '/A-tag1',
10717 sbox.repo_url + '/A-tag2',
10718 os.path.join(wc_dir2, 'A'))
10719 svntest.main.run_svn(None, 'ci', '-m', 'Merge from foreign repos', wc_dir2)
10720 svntest.actions.verify_disk(wc_dir2, expected_disk,
10721 None, None, None, None, 1)
10724 def merge_added_subtree(sbox):
10725 "merge added subtree"
10727 # The result of a subtree added by copying
10728 # or merging an added subtree, should be the same on disk
10729 ### with the exception of mergeinfo?!
10731 # test for issue 1962
10732 sbox.build()
10733 wc_dir = sbox.wc_dir
10734 url = sbox.repo_url
10736 # make a branch of A
10737 # svn cp A A_COPY
10738 A_url = url + "/A"
10739 A_COPY_url = url + "/A_COPY"
10740 A_path = os.path.join(wc_dir, "A")
10742 svntest.actions.run_and_verify_svn("",["\n", "Committed revision 2.\n"], [],
10743 "cp", "-m", "", A_url, A_COPY_url)
10744 svntest.actions.run_and_verify_svn("",["\n", "Committed revision 3.\n"], [],
10745 "cp", "-m", "",
10746 A_COPY_url + '/D',
10747 A_COPY_url + '/D2')
10748 expected_output = wc.State(A_path, {
10749 'D2' : Item(status='A '),
10750 'D2/gamma' : Item(status='A '),
10751 'D2/H/' : Item(status='A '),
10752 'D2/H/chi' : Item(status='A '),
10753 'D2/H/psi' : Item(status='A '),
10754 'D2/H/omega': Item(status='A '),
10755 'D2/G/' : Item(status='A '),
10756 'D2/G/pi' : Item(status='A '),
10757 'D2/G/rho' : Item(status='A '),
10758 'D2/G/tau' : Item(status='A ')
10761 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
10762 expected_status.add({
10763 'A/D2' : Item(status='A ', copied='+', wc_rev='-'),
10764 'A/D2/gamma' : Item(status=' ', copied='+', wc_rev='-'),
10765 'A/D2/H/' : Item(status=' ', copied='+', wc_rev='-'),
10766 'A/D2/H/chi' : Item(status=' ', copied='+', wc_rev='-'),
10767 'A/D2/H/psi' : Item(status=' ', copied='+', wc_rev='-'),
10768 'A/D2/H/omega': Item(status=' ', copied='+', wc_rev='-'),
10769 'A/D2/G/' : Item(status=' ', copied='+', wc_rev='-'),
10770 'A/D2/G/pi' : Item(status=' ', copied='+', wc_rev='-'),
10771 'A/D2/G/rho' : Item(status=' ', copied='+', wc_rev='-'),
10772 'A/D2/G/tau' : Item(status=' ', copied='+', wc_rev='-')
10774 expected_status.remove('', 'iota')
10776 expected_skip = wc.State('', {})
10777 expected_disk = svntest.main.greek_state.subtree("A")
10778 dest_name = ''
10779 expected_disk.add({
10780 dest_name + 'D2' : Item(),
10781 dest_name + 'D2/gamma' : Item("This is the file 'gamma'.\n"),
10782 dest_name + 'D2/G' : Item(),
10783 dest_name + 'D2/G/pi' : Item("This is the file 'pi'.\n"),
10784 dest_name + 'D2/G/rho' : Item("This is the file 'rho'.\n"),
10785 dest_name + 'D2/G/tau' : Item("This is the file 'tau'.\n"),
10786 dest_name + 'D2/H' : Item(),
10787 dest_name + 'D2/H/chi' : Item("This is the file 'chi'.\n"),
10788 dest_name + 'D2/H/omega' : Item("This is the file 'omega'.\n"),
10789 dest_name + 'D2/H/psi' : Item("This is the file 'psi'.\n")
10792 # Using the above information, verify a REPO->WC copy
10793 svntest.actions.run_and_verify_svn("", None, [],
10794 "cp", A_COPY_url + '/D2',
10795 os.path.join(A_path, "D2"))
10796 actual_tree = svntest.tree.build_tree_from_wc (A_path, 0)
10797 svntest.tree.compare_trees (actual_tree, expected_disk.old_tree(),
10798 None, None, None, None)
10799 svntest.actions.run_and_verify_status(A_path, expected_status)
10801 # Remove the copy artifacts
10802 svntest.actions.run_and_verify_svn("", None, [],
10803 "revert", "-R", A_path)
10804 svntest.main.safe_rmtree(os.path.join(A_path, "D2"))
10806 # Add merge-tracking differences between copying and merging
10807 # Verify a merge using the otherwise unchanged disk and status trees
10808 expected_status.tweak('A',status=' M')
10809 svntest.actions.run_and_verify_merge(A_path, 2, 3, A_COPY_url,
10810 expected_output, expected_disk,
10811 expected_status, expected_skip)
10813 #----------------------------------------------------------------------
10814 # Issue #3138
10815 def merge_unknown_url(sbox):
10816 "merging an unknown url should return error"
10818 sbox.build()
10819 wc_dir = sbox.wc_dir
10821 # remove a path from the repo and commit.
10822 iota_path = os.path.join(wc_dir, 'iota')
10823 svntest.actions.run_and_verify_svn(None, None, [], 'rm', iota_path)
10824 svntest.actions.run_and_verify_svn("", None, [],
10825 "ci", wc_dir, "-m", "log message")
10828 url = sbox.repo_url + "/iota"
10829 expected_err = ".*File not found.*iota.*|.*iota.*path not found.*"
10830 svntest.actions.run_and_verify_svn("", None, expected_err,
10831 "merge", url, wc_dir)
10833 def reverse_merge_away_all_mergeinfo(sbox):
10834 "merges that remove all mergeinfo work"
10838 sbox.build()
10839 wc_dir = sbox.wc_dir
10840 wc_disk, wc_status = set_up_branch(sbox)
10842 # Some paths we'll care about
10843 A_COPY_H_path = os.path.join(wc_dir, "A_COPY", "D", "H")
10845 # Merge r4:8 from A/D/H into A_COPY/D/H.
10846 short_H_COPY_path = shorten_path_kludge(A_COPY_H_path)
10847 expected_output = wc.State(short_H_COPY_path, {
10848 'omega' : Item(status='U '),
10849 'psi' : Item(status='U ')
10851 expected_status = wc.State(short_H_COPY_path, {
10852 '' : Item(status=' M', wc_rev=2),
10853 'psi' : Item(status='M ', wc_rev=2),
10854 'omega' : Item(status='M ', wc_rev=2),
10855 'chi' : Item(status=' ', wc_rev=2),
10857 expected_disk = wc.State('', {
10858 '' : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:3-6'}),
10859 'psi' : Item("New content"),
10860 'omega' : Item("New content"),
10861 'chi' : Item("This is the file 'chi'.\n"),
10863 expected_skip = wc.State(short_H_COPY_path, { })
10864 saved_cwd = os.getcwd()
10865 os.chdir(svntest.main.work_dir)
10866 svntest.actions.run_and_verify_merge(short_H_COPY_path, '2', '6',
10867 sbox.repo_url + '/A/D/H',
10868 expected_output, expected_disk,
10869 expected_status, expected_skip,
10870 None, None, None, None, None, 1)
10871 os.chdir(saved_cwd)
10873 # Commit the merge as r7
10874 expected_output = wc.State(wc_dir, {
10875 'A_COPY/D/H' : Item(verb='Sending'),
10876 'A_COPY/D/H/omega' : Item(verb='Sending'),
10877 'A_COPY/D/H/psi' : Item(verb='Sending'),
10879 wc_status.tweak('A_COPY/D/H', 'A_COPY/D/H/omega', 'A_COPY/D/H/psi',
10880 wc_rev=7)
10881 svntest.actions.run_and_verify_commit(wc_dir,
10882 expected_output,
10883 wc_status,
10884 None,
10885 wc_dir)
10887 # Now reverse merge r7 from itself, all mergeinfo should be removed.
10888 expected_output = wc.State(short_H_COPY_path, {
10889 '' : Item(status=' U'),
10890 'omega' : Item(status='U '),
10891 'psi' : Item(status='U ')
10893 expected_status = wc.State(short_H_COPY_path, {
10894 '' : Item(status=' M', wc_rev=7),
10895 'psi' : Item(status='M ', wc_rev=7),
10896 'omega' : Item(status='M ', wc_rev=7),
10897 'chi' : Item(status=' ', wc_rev=2),
10899 expected_disk = wc.State('', {
10900 'psi' : Item("This is the file 'psi'.\n"),
10901 'omega' : Item("This is the file 'omega'.\n"),
10902 'chi' : Item("This is the file 'chi'.\n"),
10904 expected_skip = wc.State(short_H_COPY_path, { })
10905 os.chdir(svntest.main.work_dir)
10906 svntest.actions.run_and_verify_merge(short_H_COPY_path, '7', '6',
10907 sbox.repo_url + '/A_COPY/D/H',
10908 expected_output, expected_disk,
10909 expected_status, expected_skip,
10910 None, None, None, None, None, 1)
10911 os.chdir(saved_cwd)
10914 # Another test for issue #3067: 'subtrees with intersecting mergeinfo, that don't
10915 # exist at the start of a merge range shouldn't break the merge'. Specifically see
10916 # http://subversion.tigris.org/issues/show_bug.cgi?id=3067#desc5
10918 # Set as XFail until that issue is resolved.
10919 def dont_merge_revs_into_subtree_that_predate_it(sbox):
10920 "dont merge revs into a subtree that predate it"
10922 # Create our good 'ole greek tree.
10923 sbox.build()
10924 wc_dir = sbox.wc_dir
10926 # Some paths we'll care about
10927 psi_path = os.path.join(wc_dir, "A", "D", "H", "psi")
10928 nu_path = os.path.join(wc_dir, "A", "D", "H", "nu")
10929 H_COPY_path = os.path.join(wc_dir, "H_COPY")
10930 nu_COPY_path = os.path.join(wc_dir, "H_COPY", "nu")
10932 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
10933 expected_disk = svntest.main.greek_state.copy()
10935 # Make a text mod to 'A/D/H/psi' and commit it as r2
10936 svntest.main.file_write(psi_path, "New content")
10937 expected_output = wc.State(wc_dir, {'A/D/H/psi' : Item(verb='Sending')})
10938 expected_status.tweak('A/D/H/psi', wc_rev=2)
10939 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
10940 expected_status, None, wc_dir)
10941 expected_disk.tweak('A/D/H/psi', contents="New content")
10943 # Create 'A/D/H/nu' and commit it as r3.
10944 svntest.main.file_write(nu_path, "This is the file 'nu'.\n")
10945 svntest.actions.run_and_verify_svn(None, None, [], 'add', nu_path)
10946 expected_output = wc.State(wc_dir, {'A/D/H/nu' : Item(verb='Adding')})
10947 expected_status.add({'A/D/H/nu' : Item(status=' ', wc_rev=3)})
10948 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
10949 expected_status, None, wc_dir)
10951 # Copy 'A/D/H' to 'H_COPY' in r4.
10952 svntest.actions.run_and_verify_svn(None,
10953 ['\n', 'Committed revision 4.\n'],
10954 [], 'copy',
10955 sbox.repo_url + "/A/D/H",
10956 sbox.repo_url + "/H_COPY",
10957 "-m", "Copy A/D/H to H_COPY")
10958 expected_status.add({
10959 "H_COPY" : Item(),
10960 "H_COPY/chi" : Item(),
10961 "H_COPY/omega" : Item(),
10962 "H_COPY/psi" : Item(),
10963 "H_COPY/nu" : Item()})
10965 # Update to pull the previous copy into the WC
10966 svntest.main.run_svn(None, 'up', wc_dir)
10967 expected_status.tweak(status=' ', wc_rev=4)
10969 # Make a text mod to 'A/D/H/nu' and commit it as r5.
10970 svntest.main.file_write(nu_path, "New content")
10971 expected_output = wc.State(wc_dir, {'A/D/H/nu' : Item(verb='Sending')})
10972 expected_status.tweak('A/D/H/nu', wc_rev=5)
10973 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
10974 expected_status, None, wc_dir)
10976 # Make another text mod to 'A/D/H/psi' that can be merged to 'H_COPY'
10977 # during a cherry harvest and commit it as r6.
10978 svntest.main.file_write(psi_path, "Even *newer* content")
10979 expected_output = wc.State(wc_dir, {'A/D/H/psi' : Item(verb='Sending')})
10980 expected_status.tweak('A/D/H/psi', wc_rev=6)
10981 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
10982 expected_status, None, wc_dir)
10983 expected_disk.tweak('A/D/H/psi', contents="Even *newer* content")
10985 # Update WC so elision occurs smoothly.
10986 svntest.main.run_svn(None, 'up', wc_dir)
10987 expected_status.tweak(status=' ', wc_rev=6)
10989 # Merge r5 from 'A/D/H/nu' to 'H_COPY/nu'.
10990 saved_cwd = os.getcwd()
10991 os.chdir(svntest.main.work_dir)
10992 short_nu_COPY_path = shorten_path_kludge(nu_COPY_path)
10993 svntest.actions.run_and_verify_svn(None,
10994 expected_merge_output([[5]], 'U ' +
10995 short_nu_COPY_path +
10996 '\n'),
10997 [], 'merge', '-c5',
10998 sbox.repo_url + '/A/D/H/nu',
10999 short_nu_COPY_path)
11000 os.chdir(saved_cwd)
11002 # Cherry harvest all eligible revisions from 'A/D/H' to 'H_COPY'.
11004 # This is where we see the problem described in
11005 # http://subversion.tigris.org/issues/show_bug.cgi?id=3067#desc5.
11007 # Search for the comment entitled "The Merge Kluge" elsewhere in
11008 # this file, to understand why we shorten and chdir() below.
11010 # Use run_and_verify_svn() because run_and_verify_merge*() require
11011 # explicit revision ranges.
11012 short_H_COPY_path = shorten_path_kludge(H_COPY_path)
11014 expected_skip = wc.State(short_H_COPY_path, { })
11015 os.chdir(svntest.main.work_dir)
11016 # H_COPY needs r4-6 applied while H_COPY/nu needs only 4,6.
11017 # This means r4 will be done as a separate editor drive targeted
11018 # on H_COPY. But r4 was only the copy of A/D/H to H_COPY and
11019 # so is a no-op and there will no notification for r4.
11020 svntest.actions.run_and_verify_svn(
11021 None,
11022 expected_merge_output(
11023 [[5,6]], 'U ' + os.path.join(short_H_COPY_path, "psi") + '\n'),
11024 [], 'merge', sbox.repo_url + '/A/D/H', short_H_COPY_path)
11025 os.chdir(saved_cwd)
11027 # Check the status after the merge. The mergeinfo set on 'H_COPY/nu set
11028 # by the first merge should elide.
11029 expected_status.tweak('H_COPY', status=' M')
11030 expected_status.tweak('H_COPY/nu', 'H_COPY/psi', status='M ')
11031 svntest.actions.run_and_verify_status(wc_dir, expected_status)
11032 expected_props = svntest.verify.UnorderedOutput(
11033 ["Properties on '" + H_COPY_path + "':\n",
11034 " " + SVN_PROP_MERGEINFO + " : /A/D/H:4-6\n"])
11035 svntest.actions.run_and_verify_svn(None,
11036 expected_props, [],
11037 'pl', '-vR', wc_dir)
11039 # Test for issue #3174: 'Merge algorithm chokes on subtrees needing
11040 # special attention that have been renamed'
11042 # Set as XFail until that issue is resolved.
11043 def merge_chokes_on_renamed_subtrees(sbox):
11044 "merge fails with renamed subtrees with mergeinfo"
11046 # Create our good 'ole greek tree.
11047 sbox.build()
11048 wc_dir = sbox.wc_dir
11050 # Some paths we'll care about
11051 psi_path = os.path.join(wc_dir, "A", "D", "H", "psi")
11052 psi_moved_path = os.path.join(wc_dir, "A", "D", "H", "psi_moved")
11053 psi_COPY_moved_path = os.path.join(wc_dir, "H_COPY", "psi_moved")
11054 H_COPY_path = os.path.join(wc_dir, "H_COPY")
11056 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
11057 expected_disk = svntest.main.greek_state.copy()
11059 # Make a text mod to 'A/D/H/psi' and commit it as r2
11060 svntest.main.file_write(psi_path, "New content")
11061 expected_output = wc.State(wc_dir, {'A/D/H/psi' : Item(verb='Sending')})
11062 expected_status.tweak('A/D/H/psi', wc_rev=2)
11063 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
11064 expected_status, None, wc_dir)
11065 expected_disk.tweak('A/D/H/psi', contents="New content")
11067 # Move 'A/D/H/psi' to 'A/D/H/psi_moved' and commit it as r3.
11068 svntest.actions.run_and_verify_svn(None, None, [], 'move',
11069 psi_path, psi_moved_path)
11070 expected_output = wc.State(wc_dir, {
11071 'A/D/H/psi' : Item(verb='Deleting'),
11072 'A/D/H/psi_moved' : Item(verb='Adding')
11074 expected_status.add({'A/D/H/psi_moved' : Item(status=' ', wc_rev=3)})
11075 expected_status.remove('A/D/H/psi')
11076 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
11077 expected_status, None, wc_dir)
11079 # Copy 'A/D/H' to 'H_COPY' in r4.
11080 svntest.actions.run_and_verify_svn(None,
11081 ['\n', 'Committed revision 4.\n'],
11082 [], 'copy',
11083 sbox.repo_url + "/A/D/H",
11084 sbox.repo_url + "/H_COPY",
11085 "-m", "Copy A/D/H to H_COPY")
11086 expected_status.add({
11087 "H_COPY" : Item(),
11088 "H_COPY/chi" : Item(),
11089 "H_COPY/omega" : Item(),
11090 "H_COPY/psi_moved" : Item()})
11092 # Update to pull the previous copy into the WC
11093 svntest.main.run_svn(None, 'up', wc_dir)
11094 expected_status.tweak(status=' ', wc_rev=4)
11096 # Make a text mod to 'A/D/H/psi_moved' and commit it as r5
11097 svntest.main.file_write(psi_moved_path, "Even *Newer* content")
11098 expected_output = wc.State(wc_dir,
11099 {'A/D/H/psi_moved' : Item(verb='Sending')})
11100 expected_status.tweak('A/D/H/psi_moved', wc_rev=5)
11101 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
11102 expected_status, None, wc_dir)
11103 expected_disk.remove('A/D/H/psi')
11104 expected_disk.add({
11105 'A/D/H/psi_moved' : Item("Even *Newer* content"),
11108 # Update for a uniform working copy before merging.
11109 svntest.main.run_svn(None, 'up', wc_dir)
11110 expected_status.tweak(status=' ', wc_rev=5)
11112 # Cherry harvest all available revsions from 'A/D/H/psi' to 'H_COPY/psi'.
11114 # Here is where issue #3174 appears, the merge fails with:
11115 # svn: svn: File not found: revision 3, path '/A/D/H/psi'
11117 # Search for the comment entitled "The Merge Kluge" elsewhere in
11118 # this file, to understand why we shorten and chdir() below.
11119 saved_cwd = os.getcwd()
11120 os.chdir(svntest.main.work_dir)
11121 short_psi_COPY_moved_path = shorten_path_kludge(psi_COPY_moved_path)
11122 svntest.actions.run_and_verify_svn(
11123 None,
11124 expected_merge_output([[4,5]], 'U ' + short_psi_COPY_moved_path + '\n'),
11125 [], 'merge', sbox.repo_url + '/A/D/H/psi_moved',
11126 short_psi_COPY_moved_path)
11127 os.chdir(saved_cwd)
11129 expected_status.tweak('H_COPY/psi_moved', status='MM')
11130 svntest.actions.run_and_verify_status(wc_dir, expected_status)
11132 ########################################################################
11133 # Run the tests
11136 # list all tests here, starting with None:
11137 test_list = [ None,
11138 SkipUnless(textual_merges_galore,
11139 server_has_mergeinfo),
11140 SkipUnless(add_with_history,
11141 server_has_mergeinfo),
11142 SkipUnless(delete_file_and_dir,
11143 server_has_mergeinfo),
11144 SkipUnless(simple_property_merges,
11145 server_has_mergeinfo),
11146 merge_with_implicit_target_using_r,
11147 merge_with_implicit_target_using_c,
11148 merge_with_implicit_target_and_revs,
11149 SkipUnless(merge_catches_nonexistent_target,
11150 server_has_mergeinfo),
11151 SkipUnless(merge_tree_deleted_in_target,
11152 server_has_mergeinfo),
11153 merge_similar_unrelated_trees,
11154 merge_with_prev,
11155 SkipUnless(merge_binary_file,
11156 server_has_mergeinfo),
11157 three_way_merge_add_of_existing_binary_file,
11158 SkipUnless(merge_one_file_using_r,
11159 server_has_mergeinfo),
11160 SkipUnless(merge_one_file_using_c,
11161 server_has_mergeinfo),
11162 SkipUnless(merge_one_file_using_implicit_revs,
11163 server_has_mergeinfo),
11164 SkipUnless(merge_record_only,
11165 server_has_mergeinfo),
11166 SkipUnless(merge_in_new_file_and_diff,
11167 server_has_mergeinfo),
11168 SkipUnless(merge_skips_obstructions,
11169 server_has_mergeinfo),
11170 SkipUnless(merge_into_missing,
11171 server_has_mergeinfo),
11172 SkipUnless(dry_run_adds_file_with_prop,
11173 server_has_mergeinfo),
11174 merge_binary_with_common_ancestry,
11175 SkipUnless(merge_funny_chars_on_path,
11176 server_has_mergeinfo),
11177 merge_keyword_expansions,
11178 merge_prop_change_to_deleted_target,
11179 merge_file_with_space_in_its_name,
11180 merge_dir_branches,
11181 SkipUnless(safe_property_merge,
11182 server_has_mergeinfo),
11183 SkipUnless(property_merge_from_branch,
11184 server_has_mergeinfo),
11185 property_merge_undo_redo,
11186 SkipUnless(cherry_pick_text_conflict,
11187 server_has_mergeinfo),
11188 merge_file_replace,
11189 SkipUnless(merge_dir_replace,
11190 server_has_mergeinfo),
11191 XFail(merge_dir_and_file_replace),
11192 merge_file_replace_to_mixed_rev_wc,
11193 merge_added_dir_to_deleted_in_target,
11194 SkipUnless(merge_ignore_whitespace,
11195 server_has_mergeinfo),
11196 SkipUnless(merge_ignore_eolstyle,
11197 server_has_mergeinfo),
11198 SkipUnless(merge_add_over_versioned_file_conflicts,
11199 server_has_mergeinfo),
11200 SkipUnless(merge_conflict_markers_matching_eol,
11201 server_has_mergeinfo),
11202 SkipUnless(merge_eolstyle_handling,
11203 server_has_mergeinfo),
11204 SkipUnless(avoid_repeated_merge_using_inherited_merge_info,
11205 server_has_mergeinfo),
11206 SkipUnless(avoid_repeated_merge_on_subtree_with_merge_info,
11207 server_has_mergeinfo),
11208 SkipUnless(obey_reporter_api_semantics_while_doing_subtree_merges,
11209 server_has_mergeinfo),
11210 SkipUnless(mergeinfo_inheritance,
11211 server_has_mergeinfo),
11212 SkipUnless(mergeinfo_elision,
11213 server_has_mergeinfo),
11214 SkipUnless(mergeinfo_inheritance_and_discontinuous_ranges,
11215 server_has_mergeinfo),
11216 SkipUnless(merge_to_target_with_copied_children,
11217 server_has_mergeinfo),
11218 SkipUnless(merge_to_switched_path,
11219 server_has_mergeinfo),
11220 SkipUnless(merge_to_path_with_switched_children,
11221 server_has_mergeinfo),
11222 merge_with_implicit_target_file,
11223 SkipUnless(empty_mergeinfo,
11224 server_has_mergeinfo),
11225 SkipUnless(prop_add_to_child_with_mergeinfo,
11226 server_has_mergeinfo),
11227 foreign_repos_does_not_update_mergeinfo,
11228 XFail(avoid_reflected_revs),
11229 SkipUnless(update_loses_mergeinfo,
11230 server_has_mergeinfo),
11231 SkipUnless(merge_loses_mergeinfo,
11232 server_has_mergeinfo),
11233 single_file_replace_style_merge_capability,
11234 SkipUnless(merge_to_out_of_date_target,
11235 server_has_mergeinfo),
11236 SkipUnless(merge_with_depth_files,
11237 server_has_mergeinfo),
11238 SkipUnless(merge_fails_if_subtree_is_deleted_on_src,
11239 server_has_mergeinfo),
11240 SkipUnless(merge_away_subtrees_noninheritable_ranges,
11241 server_has_mergeinfo),
11242 SkipUnless(merge_to_sparse_directories,
11243 server_has_mergeinfo),
11244 SkipUnless(merge_old_and_new_revs_from_renamed_dir,
11245 server_has_mergeinfo),
11246 SkipUnless(merge_with_child_having_different_rev_ranges_to_merge,
11247 server_has_mergeinfo),
11248 SkipUnless(merge_old_and_new_revs_from_renamed_file,
11249 server_has_mergeinfo),
11250 SkipUnless(merge_with_auto_rev_range_detection,
11251 server_has_mergeinfo),
11252 SkipUnless(mergeinfo_recording_in_skipped_merge,
11253 server_has_mergeinfo),
11254 SkipUnless(cherry_picking,
11255 server_has_mergeinfo),
11256 SkipUnless(propchange_of_subdir_raises_conflict,
11257 server_has_mergeinfo),
11258 SkipUnless(reverse_merge_prop_add_on_child,
11259 server_has_mergeinfo),
11260 XFail(merge_target_with_non_inheritable_mergeinfo),
11261 self_reverse_merge,
11262 SkipUnless(ignore_ancestry_and_mergeinfo,
11263 server_has_mergeinfo),
11264 SkipUnless(merge_from_renamed_branch_fails_while_avoiding_repeat_merge,
11265 server_has_mergeinfo),
11266 SkipUnless(merge_source_normalization_and_subtree_merges,
11267 server_has_mergeinfo),
11268 SkipUnless(new_subtrees_should_not_break_merge,
11269 server_has_mergeinfo),
11270 SkipUnless(basic_reintegrate,
11271 server_has_mergeinfo),
11272 XFail(reintegrate_with_rename),
11273 XFail(reintegrate_branch_never_merged_to),
11274 reintegrate_fail_on_modified_wc,
11275 reintegrate_fail_on_mixed_rev_wc,
11276 reintegrate_fail_on_switched_wc,
11277 reintegrate_fail_on_shallow_wc,
11278 SkipUnless(XFail(reintegrate_fail_on_stale_source),
11279 server_has_mergeinfo),
11280 SkipUnless(dont_add_mergeinfo_from_own_history,
11281 server_has_mergeinfo),
11282 merge_range_predates_history,
11283 foreign_repos,
11284 foreign_repos_2_url,
11285 XFail(merge_added_subtree),
11286 SkipUnless(merge_unknown_url,
11287 server_has_mergeinfo),
11288 SkipUnless(reverse_merge_away_all_mergeinfo,
11289 server_has_mergeinfo),
11290 XFail(SkipUnless(dont_merge_revs_into_subtree_that_predate_it,
11291 server_has_mergeinfo)),
11292 XFail(SkipUnless(merge_chokes_on_renamed_subtrees,
11293 server_has_mergeinfo)),
11296 if __name__ == '__main__':
11297 svntest.main.run_tests(test_list)
11298 # NOTREACHED
11301 ### End of file.