Skip a test when run against old servers.
[svn.git] / subversion / tests / cmdline / merge_tests.py
blob2b2b8cf5b212ff8c21d48f7c053f7cd83f977383
1 #!/usr/bin/env python
3 # merge_tests.py: testing merge
5 # Subversion is a tool for revision control.
6 # See http://subversion.tigris.org for more information.
8 # ====================================================================
9 # Copyright (c) 2000-2007 CollabNet. All rights reserved.
11 # This software is licensed as described in the file COPYING, which
12 # you should have received as part of this distribution. The terms
13 # are also available at http://subversion.tigris.org/license-1.html.
14 # If newer versions of this license are posted there, you may use a
15 # newer version instead, at your option.
17 ######################################################################
19 # General modules
20 import shutil, sys, re, os
21 import time
23 # Our testing module
24 import svntest
25 from svntest import wc
27 # (abbreviation)
28 Item = wc.StateItem
29 XFail = svntest.testcase.XFail
30 Skip = svntest.testcase.Skip
31 SkipUnless = svntest.testcase.SkipUnless
33 from svntest.main import SVN_PROP_MERGEINFO
34 from svntest.main import server_has_mergeinfo
35 from svntest.actions import fill_file_with_lines
36 from svntest.actions import make_conflict_marker_text
37 from svntest.actions import inject_conflict_into_expected_state
39 def shorten_path_kludge(path):
40 '''Search for the comment entitled "The Merge Kluge" elsewhere in
41 this file, to understand why we shorten, and subsequently chdir()
42 after calling this function.'''
43 shorten_by = len(svntest.main.work_dir) + len(os.sep)
44 return path[shorten_by:]
46 def expected_merge_output(rev_ranges, additional_lines=None):
47 """Generate an (inefficient) regex representing the expected merge
48 output from REV_RANGES (a list of 'range' lists of the form [start, end] or
49 [single_rev] --> [single_rev - 1, single_rev]), and ADDITIONAL_LINES (a list
50 of strings). If REV_RANGES is None then only the standard notification for
51 a 3-way merge is expected."""
52 if rev_ranges is None:
53 lines = [svntest.main.merge_notify_line(None, None, False)]
54 else:
55 lines = []
56 for rng in rev_ranges:
57 start_rev = rng[0]
58 if len(rng) > 1:
59 end_rev = rng[1]
60 else:
61 end_rev = None
62 lines += [svntest.main.merge_notify_line(start_rev, end_rev, True)]
63 if isinstance(additional_lines, list):
64 # Address "The Backslash Plague"
66 # If ADDITIONAL_LINES are present there are possibly paths in it with
67 # multiple components and on Windows these components are separated with
68 # '\'. These need to be escaped properly in the regexp for the match to
69 # work correctly. See http://aspn.activestate.com/ASPN/docs/ActivePython
70 # /2.2/howto/regex/regex.html#SECTION000420000000000000000.
71 if sys.platform == 'win32':
72 for i in range(0, len(additional_lines)):
73 additional_lines[i] = additional_lines[i].replace("\\", "\\\\")
74 lines.extend(additional_lines)
75 else:
76 if sys.platform == 'win32' and additional_lines != None:
77 additional_lines = additional_lines.replace("\\", "\\\\")
78 lines.append(str(additional_lines))
79 return "|".join(lines)
81 ######################################################################
82 # Tests
84 # Each test must return on success or raise on failure.
87 #----------------------------------------------------------------------
89 def textual_merges_galore(sbox):
90 "performing a merge, with mixed results"
92 ## The Plan:
94 ## The goal is to test that "svn merge" does the right thing in the
95 ## following cases:
97 ## 1 : _ : Received changes already present in unmodified local file
98 ## 2 : U : No local mods, received changes folded in without trouble
99 ## 3 : G : Received changes already exist as local mods
100 ## 4 : G : Received changes do not conflict with local mods
101 ## 5 : C : Received changes conflict with local mods
103 ## So first modify these files and commit:
105 ## Revision 2:
106 ## -----------
107 ## A/mu ............... add ten or so lines
108 ## A/D/G/rho .......... add ten or so lines
110 ## Now check out an "other" working copy, from revision 2.
112 ## Next further modify and commit some files from the original
113 ## working copy:
115 ## Revision 3:
116 ## -----------
117 ## A/B/lambda ......... add ten or so lines
118 ## A/D/G/pi ........... add ten or so lines
119 ## A/D/G/tau .......... add ten or so lines
120 ## A/D/G/rho .......... add an additional ten or so lines
122 ## In the other working copy (which is at rev 2), update rho back
123 ## to revision 1, while giving other files local mods. This sets
124 ## things up so that "svn merge -r 1:3" will test all of the above
125 ## cases except case 4:
127 ## case 1: A/mu .......... do nothing, the only change was in rev 2
128 ## case 2: A/B/lambda .... do nothing, so we accept the merge easily
129 ## case 3: A/D/G/pi ...... add same ten lines as committed in rev 3
130 ## case 5: A/D/G/tau ..... add ten or so lines at the end
131 ## [none]: A/D/G/rho ..... ignore what happens to this file for now
133 ## Now run
135 ## $ cd wc.other
136 ## $ svn merge -r 1:3 url-to-repo
138 ## ...and expect the right output.
140 ## Now revert rho, then update it to revision 2, then *prepend* a
141 ## bunch of lines, which will be separated by enough distance from
142 ## the changes about to be received that the merge will be clean.
144 ## $ cd wc.other/A/D/G
145 ## $ svn merge -r 2:3 url-to-repo/A/D/G
147 ## Which tests case 4. (Ignore the changes to the other files,
148 ## we're only interested in rho here.)
150 sbox.build()
151 wc_dir = sbox.wc_dir
152 # url = os.path.join(svntest.main.test_area_url, sbox.repo_dir)
154 # Change mu and rho for revision 2
155 mu_path = os.path.join(wc_dir, 'A', 'mu')
156 rho_path = os.path.join(wc_dir, 'A', 'D', 'G', 'rho')
157 mu_text = fill_file_with_lines(mu_path, 2)
158 rho_text = fill_file_with_lines(rho_path, 2)
160 # Create expected output tree for initial commit
161 expected_output = wc.State(wc_dir, {
162 'A/mu' : Item(verb='Sending'),
163 'A/D/G/rho' : Item(verb='Sending'),
166 # Create expected status tree; all local revisions should be at 1,
167 # but mu and rho should be at revision 2.
168 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
169 expected_status.tweak('A/mu', 'A/D/G/rho', wc_rev=2)
171 # Initial commit.
172 svntest.actions.run_and_verify_commit(wc_dir,
173 expected_output,
174 expected_status,
175 None,
176 wc_dir)
178 # Make the "other" working copy
179 other_wc = sbox.add_wc_path('other')
180 svntest.actions.duplicate_dir(wc_dir, other_wc)
182 # Now commit some more mods from the original working copy, to
183 # produce revision 3.
184 lambda_path = os.path.join(wc_dir, 'A', 'B', 'lambda')
185 pi_path = os.path.join(wc_dir, 'A', 'D', 'G', 'pi')
186 tau_path = os.path.join(wc_dir, 'A', 'D', 'G', 'tau')
188 lambda_text = fill_file_with_lines(lambda_path, 2)
189 pi_text = fill_file_with_lines(pi_path, 2)
190 tau_text = fill_file_with_lines(tau_path, 2)
191 additional_rho_text = fill_file_with_lines(rho_path, 2)
193 # Created expected output tree for 'svn ci'
194 expected_output = wc.State(wc_dir, {
195 'A/B/lambda' : Item(verb='Sending'),
196 'A/D/G/pi' : Item(verb='Sending'),
197 'A/D/G/tau' : Item(verb='Sending'),
198 'A/D/G/rho' : Item(verb='Sending'),
201 # Create expected status tree.
202 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
203 expected_status.tweak('A/mu', wc_rev=2)
204 expected_status.tweak('A/B/lambda', 'A/D/G/pi', 'A/D/G/tau', 'A/D/G/rho',
205 wc_rev=3)
207 # Commit revision 3.
208 svntest.actions.run_and_verify_commit(wc_dir,
209 expected_output,
210 expected_status,
211 None,
212 wc_dir)
214 # Make local mods in wc.other
215 other_pi_path = os.path.join(other_wc, 'A', 'D', 'G', 'pi')
216 other_rho_path = os.path.join(other_wc, 'A', 'D', 'G', 'rho')
217 other_tau_path = os.path.join(other_wc, 'A', 'D', 'G', 'tau')
219 # For A/mu and A/B/lambda, we do nothing. For A/D/G/pi, we add the
220 # same ten lines as were already committed in revision 3.
221 # (Remember, wc.other is only at revision 2, so it doesn't have
222 # these changes.)
223 svntest.main.file_append(other_pi_path, pi_text)
225 # We skip A/D/G/rho in this merge; it will be tested with a separate
226 # merge command. Temporarily put it back to revision 1, so this
227 # merge succeeds cleanly.
228 svntest.actions.run_and_verify_svn(None, None, [],
229 'up', '-r', '1', other_rho_path)
231 # For A/D/G/tau, we append ten different lines, to conflict with the
232 # ten lines appended in revision 3.
233 other_tau_text = fill_file_with_lines(other_tau_path, 2,
234 line_descrip="Conflicting line")
236 # Do the first merge, revs 1:3. This tests all the cases except
237 # case 4, which we'll handle in a second pass.
238 expected_output = wc.State(other_wc, {'A/B/lambda' : Item(status='U '),
239 'A/D/G/rho' : Item(status='U '),
240 'A/D/G/tau' : Item(status='C '),
243 expected_disk = svntest.main.greek_state.copy()
244 expected_disk.tweak('A/mu',
245 contents=expected_disk.desc['A/mu'].contents
246 + mu_text)
247 expected_disk.tweak('A/B/lambda',
248 contents=expected_disk.desc['A/B/lambda'].contents
249 + lambda_text)
250 expected_disk.tweak('A/D/G/rho',
251 contents=expected_disk.desc['A/D/G/rho'].contents
252 + rho_text + additional_rho_text)
253 expected_disk.tweak('A/D/G/pi',
254 contents=expected_disk.desc['A/D/G/pi'].contents
255 + pi_text)
257 expected_status = svntest.actions.get_virginal_state(other_wc, 1)
258 expected_status.tweak('', status=' M')
259 expected_status.tweak('A/mu', wc_rev=2)
260 expected_status.tweak('A/B/lambda', status='M ')
261 expected_status.tweak('A/D/G/pi', status='M ')
262 expected_status.tweak('A/D/G/rho', status='M ')
264 inject_conflict_into_expected_state('A/D/G/tau', expected_disk,
265 expected_status, other_tau_text, tau_text,
268 expected_skip = wc.State('', { })
270 tau_conflict_support_files = ["tau\.working",
271 "tau\.merge-right\.r3",
272 "tau\.merge-left\.r1"]
274 svntest.actions.run_and_verify_merge(other_wc, '1', '3',
275 sbox.repo_url,
276 expected_output,
277 expected_disk,
278 expected_status,
279 expected_skip,
280 None,
281 svntest.tree.detect_conflict_files,
282 list(tau_conflict_support_files))
284 # Now reverse merge r3 into A/D/G/rho, give it non-conflicting local
285 # mods, then merge in the 2:3 change. ### Not bothering to do the
286 # whole expected_foo routine for these intermediate operations;
287 # they're not what we're here to test, after all, so it's enough to
288 # know that they worked. Is this a bad practice? ###
290 # run_and_verify_merge doesn't support merging to a file WCPATH
291 # so use run_and_verify_svn.
292 svntest.actions.run_and_verify_svn(None,
293 expected_merge_output([[-3]], 'G ' +
294 other_rho_path +
295 '\n'),
296 [], 'merge', '-c-3',
297 sbox.repo_url + '/A/D/G/rho',
298 other_rho_path)
300 # Now *prepend* ten or so lines to A/D/G/rho. Since rho had ten
301 # lines appended in revision 2, and then another ten in revision 3,
302 # these new local mods will be separated from the rev 3 changes by
303 # enough distance that they won't conflict, so the merge should be
304 # clean.
305 other_rho_text = ""
306 for x in range(1,10):
307 other_rho_text = other_rho_text + 'Unobtrusive line ' + `x` + ' in rho\n'
308 current_other_rho_text = svntest.main.file_read(other_rho_path)
309 svntest.main.file_write(other_rho_path,
310 other_rho_text + current_other_rho_text)
312 # We expect no merge attempt for pi and tau because they inherit
313 # mergeinfo from the WC root. There is explicit mergeinfo on rho
314 # ('/A/D/G/rho:2') so expect it to be merged (cleanly).
315 expected_output = wc.State(os.path.join(other_wc, 'A', 'D', 'G'),
316 {'rho' : Item(status='G ')})
317 expected_disk = wc.State("", {
318 'pi' : Item("This is the file 'pi'.\n"),
319 'rho' : Item("This is the file 'rho'.\n"),
320 'tau' : Item("This is the file 'tau'.\n"),
322 expected_disk.tweak('rho',
323 contents=other_rho_text
324 + expected_disk.desc['rho'].contents
325 + rho_text
326 + additional_rho_text)
327 expected_disk.tweak('pi',
328 contents=expected_disk.desc['pi'].contents
329 + pi_text)
331 expected_status = wc.State(os.path.join(other_wc, 'A', 'D', 'G'),
332 { '' : Item(wc_rev=1, status=' '),
333 'rho' : Item(wc_rev=1, status='M '),
334 'pi' : Item(wc_rev=1, status='M '),
335 'tau' : Item(wc_rev=1, status='C '),
338 inject_conflict_into_expected_state('tau', expected_disk, expected_status,
339 other_tau_text, tau_text, 3)
341 # Do the merge, but check svn:mergeinfo props separately since
342 # run_and_verify_merge would attempt to proplist tau's conflict
343 # files if we asked it to check props.
344 svntest.actions.run_and_verify_merge(
345 os.path.join(other_wc, 'A', 'D', 'G'),
346 '2', '3',
347 sbox.repo_url + '/A/D/G',
348 expected_output,
349 expected_disk,
350 expected_status,
351 expected_skip,
352 None,
353 svntest.tree.detect_conflict_files, list(tau_conflict_support_files))
356 svntest.actions.run_and_verify_svn(None, [], [],
357 'propget', SVN_PROP_MERGEINFO,
358 os.path.join(other_wc,
359 "A", "D", "G", "rho"))
362 #----------------------------------------------------------------------
364 # Merge should copy-with-history when adding files or directories
366 def add_with_history(sbox):
367 "merge and add new files/dirs with history"
369 sbox.build()
370 wc_dir = sbox.wc_dir
372 C_path = os.path.join(wc_dir, 'A', 'C')
373 F_path = os.path.join(wc_dir, 'A', 'B', 'F')
374 F_url = sbox.repo_url + '/A/B/F'
376 Q_path = os.path.join(F_path, 'Q')
377 Q2_path = os.path.join(F_path, 'Q2')
378 foo_path = os.path.join(F_path, 'foo')
379 foo2_path = os.path.join(F_path, 'foo2')
380 bar_path = os.path.join(F_path, 'Q', 'bar')
381 bar2_path = os.path.join(F_path, 'Q', 'bar2')
383 svntest.main.run_svn(None, 'mkdir', Q_path)
384 svntest.main.run_svn(None, 'mkdir', Q2_path)
385 svntest.main.file_append(foo_path, "foo")
386 svntest.main.file_append(foo2_path, "foo2")
387 svntest.main.file_append(bar_path, "bar")
388 svntest.main.file_append(bar2_path, "bar2")
389 svntest.main.run_svn(None, 'add', foo_path, foo2_path, bar_path, bar2_path)
390 svntest.main.run_svn(None, 'propset', 'x', 'x', Q2_path)
391 svntest.main.run_svn(None, 'propset', 'y', 'y', foo2_path)
392 svntest.main.run_svn(None, 'propset', 'z', 'z', bar2_path)
394 expected_output = wc.State(wc_dir, {
395 'A/B/F/Q' : Item(verb='Adding'),
396 'A/B/F/Q2' : Item(verb='Adding'),
397 'A/B/F/Q/bar' : Item(verb='Adding'),
398 'A/B/F/Q/bar2': Item(verb='Adding'),
399 'A/B/F/foo' : Item(verb='Adding'),
400 'A/B/F/foo2' : Item(verb='Adding'),
402 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
403 expected_status.add({
404 'A/B/F/Q' : Item(status=' ', wc_rev=2),
405 'A/B/F/Q2' : Item(status=' ', wc_rev=2),
406 'A/B/F/Q/bar' : Item(status=' ', wc_rev=2),
407 'A/B/F/Q/bar2': Item(status=' ', wc_rev=2),
408 'A/B/F/foo' : Item(status=' ', wc_rev=2),
409 'A/B/F/foo2' : Item(status=' ', wc_rev=2),
411 svntest.actions.run_and_verify_commit(wc_dir,
412 expected_output,
413 expected_status,
414 None,
415 wc_dir)
417 ### "The Merge Kluge"
419 ### *****************************************************
420 ### *** ***
421 ### *** Before erasing this comment, please check ***
422 ### *** for references to "The Merge Kluge" ***
423 ### *** elsewhere in this file, update_tests.py ***
424 ### *** and switch_tests.py. ***
425 ### *** ***
426 ### *****************************************************
428 ### The shortening of C_path and the chdir() below are a kluge to
429 ### work around
431 ### http://subversion.tigris.org/issues/show_bug.cgi?id=767#desc16
433 ### Note that the problem isn't simply that 'svn merge' sometimes
434 ### puts temp files in cwd. That's bad enough, but even if svn
435 ### were to choose /tmp or some other static place blessed by
436 ### apr_get_temp_dir(), we'd still experience the error
438 ### svn: Move failed
439 ### svn: Can't move 'tmp.2' to '.../.svn/tmp/text-base/file1.svn-base':
440 ### Invalid cross-device link
442 ### when running the tests on a ramdisk. After all, there's no
443 ### reason why apr_get_temp_dir() would return a path inside
444 ### svn-test-work/, which is the mount point for the ramdisk.
446 ### http://subversion.tigris.org/issues/show_bug.cgi?id=767#desc20
447 ### starts a discussion on how to solve this in Subversion itself.
448 ### However, until that's settled, we still want to be able to run
449 ### the tests in a ramdisk, hence this kluge.
451 short_C_path = shorten_path_kludge(C_path)
452 expected_output = wc.State(short_C_path, {
453 'Q' : Item(status='A '),
454 'Q2' : Item(status='A '),
455 'Q/bar' : Item(status='A '),
456 'Q/bar2' : Item(status='A '),
457 'foo' : Item(status='A '),
458 'foo2' : Item(status='A '),
460 expected_disk = wc.State('', {
461 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B/F:2'}),
462 'Q' : Item(),
463 'Q2' : Item(props={'x' : 'x'}),
464 'Q/bar' : Item("bar"),
465 'Q/bar2' : Item("bar2", props={'z' : 'z'}),
466 'foo' : Item("foo"),
467 'foo2' : Item("foo2", props={'y' : 'y'}),
469 expected_status = wc.State(short_C_path, {
470 '' : Item(status=' M', wc_rev=1),
471 'Q' : Item(status='A ', wc_rev='-', copied='+'),
472 'Q2' : Item(status='A ', wc_rev='-', copied='+'),
473 'Q/bar' : Item(status='A ', wc_rev='-', copied='+'),
474 'Q/bar2' : Item(status='A ', wc_rev='-', copied='+'),
475 'foo' : Item(status='A ', wc_rev='-', copied='+'),
476 'foo2' : Item(status='A ', wc_rev='-', copied='+'),
478 expected_skip = wc.State(short_C_path, { })
480 saved_cwd = os.getcwd()
482 os.chdir(svntest.main.work_dir)
483 svntest.actions.run_and_verify_merge(short_C_path, '1', '2', F_url,
484 expected_output,
485 expected_disk,
486 expected_status,
487 expected_skip,
488 None, None, None, None, None,
489 1) # check props
490 os.chdir(saved_cwd)
492 expected_output = svntest.wc.State(wc_dir, {
493 'A/C' : Item(verb='Sending'),
494 'A/C/Q' : Item(verb='Adding'),
495 'A/C/Q2' : Item(verb='Adding'),
496 'A/C/Q/bar' : Item(verb='Adding'),
497 'A/C/Q/bar2': Item(verb='Adding'),
498 'A/C/foo' : Item(verb='Adding'),
499 'A/C/foo2' : Item(verb='Adding'),
501 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
502 expected_status.add({
503 'A/C' : Item(status=' ', wc_rev=3),
504 'A/B/F/Q' : Item(status=' ', wc_rev=2),
505 'A/B/F/Q2' : Item(status=' ', wc_rev=2),
506 'A/B/F/Q/bar' : Item(status=' ', wc_rev=2),
507 'A/B/F/Q/bar2': Item(status=' ', wc_rev=2),
508 'A/B/F/foo' : Item(status=' ', wc_rev=2),
509 'A/B/F/foo2' : Item(status=' ', wc_rev=2),
510 'A/C/Q' : Item(status=' ', wc_rev=3),
511 'A/C/Q2' : Item(status=' ', wc_rev=3),
512 'A/C/Q/bar' : Item(status=' ', wc_rev=3),
513 'A/C/Q/bar2' : Item(status=' ', wc_rev=3),
514 'A/C/foo' : Item(status=' ', wc_rev=3),
515 'A/C/foo2' : Item(status=' ', wc_rev=3),
517 svntest.actions.run_and_verify_commit(wc_dir,
518 expected_output,
519 expected_status,
520 None,
521 wc_dir)
523 #----------------------------------------------------------------------
525 def delete_file_and_dir(sbox):
526 "merge that deletes items"
528 sbox.build()
529 wc_dir = sbox.wc_dir
531 # Rev 2 copy B to B2
532 B_path = os.path.join(wc_dir, 'A', 'B')
533 B2_path = os.path.join(wc_dir, 'A', 'B2')
534 B_url = sbox.repo_url + '/A/B'
536 svntest.actions.run_and_verify_svn(None, None, [],
537 'copy', B_path, B2_path)
539 expected_output = wc.State(wc_dir, {
540 'A/B2' : Item(verb='Adding'),
542 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
543 expected_status.add({
544 'A/B2' : Item(status=' ', wc_rev=2),
545 'A/B2/E' : Item(status=' ', wc_rev=2),
546 'A/B2/E/alpha' : Item(status=' ', wc_rev=2),
547 'A/B2/E/beta' : Item(status=' ', wc_rev=2),
548 'A/B2/F' : Item(status=' ', wc_rev=2),
549 'A/B2/lambda' : Item(status=' ', wc_rev=2),
551 svntest.actions.run_and_verify_commit(wc_dir,
552 expected_output,
553 expected_status,
554 None,
555 wc_dir)
557 # Rev 3 delete E and lambda from B
558 E_path = os.path.join(B_path, 'E')
559 lambda_path = os.path.join(B_path, 'lambda')
560 svntest.actions.run_and_verify_svn(None, None, [],
561 'delete', E_path, lambda_path)
563 expected_output = wc.State(wc_dir, {
564 'A/B/E' : Item(verb='Deleting'),
565 'A/B/lambda' : Item(verb='Deleting'),
567 expected_status.remove('A/B/E',
568 'A/B/E/alpha',
569 'A/B/E/beta',
570 'A/B/lambda')
571 svntest.actions.run_and_verify_commit(wc_dir,
572 expected_output,
573 expected_status,
574 None,
575 wc_dir)
577 def modify_B2():
578 # Local mods in B2
579 B2_E_path = os.path.join(B2_path, 'E')
580 B2_lambda_path = os.path.join(B2_path, 'lambda')
581 svntest.actions.run_and_verify_svn(None, None, [],
582 'propset', 'foo', 'foo_val',
583 B2_E_path, B2_lambda_path)
584 expected_status.tweak(
585 'A/B2/E', 'A/B2/lambda', status=' M'
587 svntest.actions.run_and_verify_status(wc_dir, expected_status)
589 modify_B2()
591 # Merge rev 3 into B2
593 # The local mods to the paths modified in r3 cause the paths to be
594 # skipped (without --force), resulting in only mergeinfo changes. The
595 # target of the merge 'B2' gets mergeinfo for r3 and B2's two skipped
596 # children, 'E' and 'lambda', get override mergeinfo reflecting their
597 # mergeinfo prior to the merge (in this case empty mergeinfo).
598 expected_output = wc.State(B2_path, { })
599 expected_disk = wc.State('', {
600 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B:3'}),
601 'E' : Item(props={SVN_PROP_MERGEINFO : '',
602 'foo' : 'foo_val'}),
603 'E/alpha' : Item("This is the file 'alpha'.\n"),
604 'E/beta' : Item("This is the file 'beta'.\n"),
605 'F' : Item(),
606 'lambda' : Item("This is the file 'lambda'.\n",
607 props={SVN_PROP_MERGEINFO : '',
608 'foo' : 'foo_val'}),
610 expected_status2 = wc.State(B2_path, {
611 '' : Item(status=' M'),
612 'E' : Item(status=' M'),
613 'E/alpha' : Item(status=' '),
614 'E/beta' : Item(status=' '),
615 'F' : Item(status=' '),
616 'lambda' : Item(status=' M'),
618 expected_status2.tweak(wc_rev=2)
619 expected_skip = wc.State(B2_path, {
620 'lambda' : Item(),
621 'E' : Item(),
623 svntest.actions.run_and_verify_merge(B2_path, '2', '3', B_url,
624 expected_output,
625 expected_disk,
626 expected_status2,
627 expected_skip,
628 None, None, None, None, None,
629 True)
631 # Revert the previous merge attempt and redo the local changes to B2.
632 # Why do we need to do this? Because 'B2' already has mergeinfo reflecting
633 # r3 has been merged. If we didn't revert we'd need to use
634 # --ignore-ancestry' to force B2's children 'E' and 'lamda' to actually be
635 # deleted. This is another facet of issue #2898.
636 svntest.actions.run_and_verify_svn(None, None, [],
637 'revert', '-R', wc_dir)
638 modify_B2()
640 expected_output = wc.State(B2_path, {
641 'E' : Item(status='D '),
642 'lambda' : Item(status='D '),
644 expected_disk.remove('E/alpha', 'E/beta', 'lambda')
645 expected_disk.tweak('E', props={'foo' : 'foo_val'})
646 expected_status2.tweak('E', 'E/alpha', 'E/beta', 'lambda', status='D ')
647 expected_status2.tweak('', status=' M')
648 expected_skip.remove('lambda', 'E')
650 ### Full-to-dry-run automatic comparison disabled because a) dry-run
651 ### doesn't descend into deleted directories, and b) the full merge
652 ### notifies deleted directories twice.
653 svntest.actions.run_and_verify_merge(B2_path, '2', '3', B_url,
654 expected_output,
655 expected_disk,
656 expected_status2,
657 expected_skip,
658 None, None, None, None, None,
659 True, 0, '--force')
663 #----------------------------------------------------------------------
665 # Issue 953
666 def simple_property_merges(sbox):
667 "some simple property merges"
669 sbox.build()
670 wc_dir = sbox.wc_dir
672 # Add a property to a file and a directory
673 alpha_path = os.path.join(wc_dir, 'A', 'B', 'E', 'alpha')
674 beta_path = os.path.join(wc_dir, 'A', 'B', 'E', 'beta')
675 E_path = os.path.join(wc_dir, 'A', 'B', 'E')
677 svntest.actions.run_and_verify_svn(None, None, [],
678 'propset', 'foo', 'foo_val',
679 alpha_path)
680 # A binary, non-UTF8 property value
681 svntest.actions.run_and_verify_svn(None, None, [],
682 'propset', 'foo', 'foo\201val',
683 beta_path)
684 svntest.actions.run_and_verify_svn(None, None, [],
685 'propset', 'foo', 'foo_val',
686 E_path)
688 # Commit change as rev 2
689 expected_output = svntest.wc.State(wc_dir, {
690 'A/B/E' : Item(verb='Sending'),
691 'A/B/E/alpha' : Item(verb='Sending'),
692 'A/B/E/beta' : Item(verb='Sending'),
694 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
695 expected_status.tweak('A/B/E', 'A/B/E/alpha', 'A/B/E/beta',
696 wc_rev=2, status=' ')
697 svntest.actions.run_and_verify_commit(wc_dir,
698 expected_output, expected_status,
699 None, wc_dir)
700 svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
702 # Copy B to B2 as rev 3
703 B_url = sbox.repo_url + '/A/B'
704 B2_url = sbox.repo_url + '/A/B2'
706 svntest.actions.run_and_verify_svn(None, None, [],
707 'copy', '-m', 'copy B to B2',
708 B_url, B2_url)
709 svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
711 # Modify a property and add a property for the file and directory
712 svntest.actions.run_and_verify_svn(None, None, [],
713 'propset', 'foo', 'mod_foo', alpha_path)
714 svntest.actions.run_and_verify_svn(None, None, [],
715 'propset', 'bar', 'bar_val', alpha_path)
716 svntest.actions.run_and_verify_svn(None, None, [],
717 'propset', 'foo', 'mod\201foo', beta_path)
718 svntest.actions.run_and_verify_svn(None, None, [],
719 'propset', 'bar', 'bar\201val', beta_path)
720 svntest.actions.run_and_verify_svn(None, None, [],
721 'propset', 'foo', 'mod_foo', E_path)
722 svntest.actions.run_and_verify_svn(None, None, [],
723 'propset', 'bar', 'bar_val', E_path)
725 # Commit change as rev 4
726 expected_status = svntest.actions.get_virginal_state(wc_dir, 3)
727 expected_status.tweak('A/B/E', 'A/B/E/alpha', 'A/B/E/beta',
728 wc_rev=4, status=' ')
729 expected_status.add({
730 'A/B2' : Item(status=' ', wc_rev=3),
731 'A/B2/E' : Item(status=' ', wc_rev=3),
732 'A/B2/E/alpha' : Item(status=' ', wc_rev=3),
733 'A/B2/E/beta' : Item(status=' ', wc_rev=3),
734 'A/B2/F' : Item(status=' ', wc_rev=3),
735 'A/B2/lambda' : Item(status=' ', wc_rev=3),
737 svntest.actions.run_and_verify_commit(wc_dir,
738 expected_output, expected_status,
739 None, wc_dir)
740 svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
742 pristine_status = expected_status
743 pristine_status.tweak(wc_rev=4)
745 # Merge B 3:4 into B2
746 B2_path = os.path.join(wc_dir, 'A', 'B2')
747 expected_output = wc.State(B2_path, {
748 'E' : Item(status=' U'),
749 'E/alpha' : Item(status=' U'),
750 'E/beta' : Item(status=' U'),
752 expected_disk = wc.State('', {
753 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B:4'}),
754 'E' : Item(),
755 'E/alpha' : Item("This is the file 'alpha'.\n"),
756 'E/beta' : Item("This is the file 'beta'.\n"),
757 'F' : Item(),
758 'lambda' : Item("This is the file 'lambda'.\n"),
760 expected_disk.tweak('E', 'E/alpha',
761 props={'foo' : 'mod_foo', 'bar' : 'bar_val'})
762 expected_disk.tweak('E/beta',
763 props={'foo' : 'mod\201foo', 'bar' : 'bar\201val'})
764 expected_status = wc.State(B2_path, {
765 '' : Item(status=' M'),
766 'E' : Item(status=' M'),
767 'E/alpha' : Item(status=' M'),
768 'E/beta' : Item(status=' M'),
769 'F' : Item(status=' '),
770 'lambda' : Item(status=' '),
772 expected_status.tweak(wc_rev=4)
773 expected_skip = wc.State('', { })
774 svntest.actions.run_and_verify_merge(B2_path, '3', '4', B_url,
775 expected_output,
776 expected_disk,
777 expected_status,
778 expected_skip,
779 None, None, None, None, None, 1)
781 # Revert merge
782 svntest.actions.run_and_verify_svn(None, None, [],
783 'revert', '--recursive', wc_dir)
784 svntest.actions.run_and_verify_status(wc_dir, pristine_status)
786 # Merge B 2:1 into B2 (B2's mergeinfo should get elided away)
787 expected_status.tweak('', status=' ')
788 expected_disk.remove('')
789 expected_disk.tweak('E', 'E/alpha', 'E/beta', props={})
790 svntest.actions.run_and_verify_merge(B2_path, '2', '1', B_url,
791 expected_output,
792 expected_disk,
793 expected_status,
794 expected_skip,
795 None, None, None, None, None, 1)
797 # Merge B 3:4 into B2 now causes a conflict
798 expected_disk.add({
799 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B:4'}),
800 'E/dir_conflicts.prej'
801 : Item("Trying to change property 'foo' from 'foo_val' to 'mod_foo',\n"
802 + "but it has been locally deleted.\n"),
803 'E/alpha.prej'
804 : Item("Trying to change property 'foo' from 'foo_val' to 'mod_foo',\n"
805 + "but it has been locally deleted.\n"),
806 'E/beta.prej'
807 : Item("Trying to change property 'foo' from 'foo?\\129val' to"
808 + " 'mod?\\129foo',\n"
809 + "but it has been locally deleted.\n"),
811 expected_disk.tweak('E', 'E/alpha', props={'bar' : 'bar_val'})
812 expected_disk.tweak('E/beta', props={'bar' : 'bar\201val'})
813 expected_status.tweak('', status=' M')
814 expected_status.tweak('E', 'E/alpha', 'E/beta', status=' C')
815 expected_output.tweak('E', 'E/alpha', 'E/beta', status=' C')
816 svntest.actions.run_and_verify_merge(B2_path, '3', '4', B_url,
817 expected_output,
818 expected_disk,
819 expected_status,
820 expected_skip,
821 None, None, None, None, None, 1)
823 # issue 1109 : single file property merge. This test performs a merge
824 # that should be a no-op (adding properties that are already present).
825 svntest.actions.run_and_verify_svn(None, None, [],
826 'revert', '--recursive', wc_dir)
827 svntest.actions.run_and_verify_status(wc_dir, pristine_status)
829 # Copy A at rev 4 to A2 to make revision 5.
830 A_url = sbox.repo_url + '/A'
831 A2_url = sbox.repo_url + '/A2'
832 svntest.actions.run_and_verify_svn(None,
833 ['\n', 'Committed revision 5.\n'], [],
834 'copy', '-m', 'copy A to A2',
835 A_url, A2_url)
837 # Re-root the WC at A2.
838 svntest.actions.run_and_verify_svn(None, None, [], 'switch', A2_url, wc_dir)
840 # Attempt to re-merge rev 4 of the original A's alpha. Mergeinfo
841 # inherited from A2 (created by its copy from A) allows us to avoid
842 # a repeated merge.
843 alpha_url = sbox.repo_url + '/A/B/E/alpha'
844 alpha_path = os.path.join(wc_dir, 'B', 'E', 'alpha')
846 # Cannot use run_and_verify_merge with a file target
847 svntest.actions.run_and_verify_svn(None, [], [], 'merge', '-r', '3:4',
848 alpha_url, alpha_path)
850 exit_code, output, err = svntest.actions.run_and_verify_svn(None, None, [],
851 'pl', alpha_path)
853 saw_foo = 0
854 saw_bar = 0
855 for line in output:
856 if re.match("\\s*foo\\s*$", line):
857 saw_foo = 1
858 if re.match("\\s*bar\\s*$", line):
859 saw_bar = 1
861 if not saw_foo or not saw_bar:
862 raise svntest.Failure("Expected properties not found")
865 #----------------------------------------------------------------------
866 # This is a regression for issue #1176.
868 def merge_catches_nonexistent_target(sbox):
869 "merge should not die if a target file is absent"
871 sbox.build()
872 wc_dir = sbox.wc_dir
874 # Copy G to a new directory, Q. Create Q/newfile. Commit a change
875 # to Q/newfile. Now merge that change... into G. Merge should not
876 # error, but should do nothing.
878 G_path = os.path.join(wc_dir, 'A', 'D', 'G')
879 Q_path = os.path.join(wc_dir, 'A', 'D', 'Q')
880 newfile_path = os.path.join(Q_path, 'newfile')
881 Q_url = sbox.repo_url + '/A/D/Q'
883 # Copy dir A/D/G to A/D/Q
884 svntest.actions.run_and_verify_svn(None, None, [], 'cp', G_path, Q_path)
886 svntest.main.file_append(newfile_path, 'This is newfile.\n')
887 svntest.actions.run_and_verify_svn(None, None, [], 'add', newfile_path)
889 # Add newfile to dir G, creating r2.
890 expected_output = wc.State(wc_dir, {
891 'A/D/Q' : Item(verb='Adding'),
892 'A/D/Q/newfile' : Item(verb='Adding'),
894 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
895 expected_status.add({
896 'A/D/Q' : Item(status=' ', wc_rev=2),
897 'A/D/Q/pi' : Item(status=' ', wc_rev=2),
898 'A/D/Q/rho' : Item(status=' ', wc_rev=2),
899 'A/D/Q/tau' : Item(status=' ', wc_rev=2),
900 'A/D/Q/newfile' : Item(status=' ', wc_rev=2),
902 svntest.actions.run_and_verify_commit(wc_dir,
903 expected_output,
904 expected_status,
905 None, wc_dir)
907 # Change newfile, creating r3.
908 svntest.main.file_append(newfile_path, 'A change to newfile.\n')
909 expected_output = wc.State(wc_dir, {
910 'A/D/Q/newfile' : Item(verb='Sending'),
912 expected_status.tweak('A/D/Q/newfile', wc_rev=3)
913 svntest.actions.run_and_verify_commit(wc_dir,
914 expected_output,
915 expected_status,
916 None, wc_dir)
918 # Merge the change to newfile (from r3) into G, where newfile
919 # doesn't exist.
920 os.chdir(G_path)
921 expected_output = wc.State('', { })
922 expected_status = wc.State('', {
923 '' : Item(status=' M' ),
924 'pi' : Item(status=' ' ),
925 'rho' : Item(status=' ' ),
926 'tau' : Item(status=' ' ),
928 expected_status.tweak(wc_rev=1)
929 expected_disk = wc.State('', {
930 '' : Item(props={SVN_PROP_MERGEINFO : '/A/D/Q:3'}),
931 'pi' : Item("This is the file 'pi'.\n"),
932 'rho' : Item("This is the file 'rho'.\n"),
933 'tau' : Item("This is the file 'tau'.\n"),
935 expected_skip = wc.State('', {
936 'newfile' :Item(),
938 svntest.actions.run_and_verify_merge('', '2', '3', Q_url,
939 expected_output,
940 expected_disk,
941 expected_status,
942 expected_skip,
943 None, None, None, None, None, True)
945 #----------------------------------------------------------------------
947 def merge_tree_deleted_in_target(sbox):
948 "merge on deleted directory in target"
950 sbox.build()
951 wc_dir = sbox.wc_dir
953 # Copy B to a new directory, I. Modify B/E/alpha, Remove I/E. Now
954 # merge that change... into I. Merge should not error
956 B_path = os.path.join(wc_dir, 'A', 'B')
957 I_path = os.path.join(wc_dir, 'A', 'I')
958 alpha_path = os.path.join(B_path, 'E', 'alpha')
959 B_url = sbox.repo_url + '/A/B'
960 I_url = sbox.repo_url + '/A/I'
963 # Copy B to I, creating r1.
964 svntest.actions.run_and_verify_svn(None, None, [],
965 'cp', B_url, I_url, '-m', 'rev 2')
967 # Change some files, creating r2.
968 svntest.main.file_append(alpha_path, 'A change to alpha.\n')
969 svntest.main.file_append(os.path.join(B_path, 'lambda'), 'change lambda.\n')
970 svntest.actions.run_and_verify_svn(None, None, [],
971 'ci', '-m', 'rev 3', B_path)
973 # Remove E, creating r3.
974 E_url = sbox.repo_url + '/A/I/E'
975 svntest.actions.run_and_verify_svn(None, None, [],
976 'rm', E_url, '-m', 'rev 4')
978 svntest.actions.run_and_verify_svn(None, None, [],
979 'up', os.path.join(wc_dir,'A'))
981 expected_output = wc.State(I_path, {
982 'lambda' : Item(status='U '),
984 expected_disk = wc.State('', {
985 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B:3'}),
986 'F' : Item(),
987 'lambda' : Item("This is the file 'lambda'.\nchange lambda.\n"),
989 expected_status = wc.State(I_path, {
990 '' : Item(status=' M'),
991 'F' : Item(status=' '),
992 'lambda' : Item(status='M '),
994 expected_status.tweak(wc_rev=4)
995 expected_skip = wc.State(I_path, {
996 'E' : Item(),
997 'E/alpha' : Item(),
999 svntest.actions.run_and_verify_merge(I_path, '2', '3', B_url,
1000 expected_output,
1001 expected_disk,
1002 expected_status,
1003 expected_skip,
1004 None, None, None, None, None,
1005 1, 0)
1007 #----------------------------------------------------------------------
1008 # Issue #2515
1010 def merge_added_dir_to_deleted_in_target(sbox):
1011 "merge an added dir on a deleted dir in target"
1013 sbox.build()
1014 wc_dir = sbox.wc_dir
1016 # copy B to a new directory, I.
1017 # delete F in I.
1018 # add J to B/F.
1019 # merge add to I.
1021 B_url = sbox.repo_url + '/A/B'
1022 I_url = sbox.repo_url + '/A/I'
1023 F_url = sbox.repo_url + '/A/I/F'
1024 J_url = sbox.repo_url + '/A/B/F/J'
1025 I_path = os.path.join(wc_dir, 'A', 'I')
1028 svntest.actions.run_and_verify_svn(None, None, [],
1029 'cp', B_url, I_url, '-m', 'rev 2')
1031 svntest.actions.run_and_verify_svn(None, None, [],
1032 'rm', F_url, '-m', 'rev 3')
1034 svntest.actions.run_and_verify_svn(None, None, [],
1035 'mkdir', '-m', 'rev 4', J_url)
1037 svntest.actions.run_and_verify_svn(None, None, [],
1038 'up', os.path.join(wc_dir,'A'))
1040 expected_output = wc.State(I_path, {})
1041 expected_disk = wc.State('', {
1042 'E' : Item(),
1043 'E/alpha' : Item("This is the file 'alpha'.\n"),
1044 'E/beta' : Item("This is the file 'beta'.\n"),
1045 'lambda' : Item("This is the file 'lambda'.\n"),
1047 expected_skip = wc.State(I_path, {
1048 'F/J' : Item(),
1049 'F' : Item(),
1052 svntest.actions.run_and_verify_merge(I_path, '2', '4', B_url,
1053 expected_output,
1054 expected_disk,
1055 None,
1056 expected_skip,
1057 None, None, None, None, None,
1058 0, 0)
1060 #----------------------------------------------------------------------
1061 # This is a regression for issue #1176.
1063 def merge_similar_unrelated_trees(sbox):
1064 "merging similar trees ancestrally unrelated"
1066 ## See http://subversion.tigris.org/issues/show_bug.cgi?id=1249. ##
1068 sbox.build()
1069 wc_dir = sbox.wc_dir
1071 # Simple test. Make three directories with the same content.
1072 # Modify some stuff in the second one. Now merge
1073 # (firstdir:seconddir->thirddir).
1075 base1_path = os.path.join(wc_dir, 'base1')
1076 base2_path = os.path.join(wc_dir, 'base2')
1077 apply_path = os.path.join(wc_dir, 'apply')
1079 base1_url = os.path.join(sbox.repo_url + '/base1')
1080 base2_url = os.path.join(sbox.repo_url + '/base2')
1082 # Make a tree of stuff ...
1083 os.mkdir(base1_path)
1084 svntest.main.file_append(os.path.join(base1_path, 'iota'),
1085 "This is the file iota\n")
1086 os.mkdir(os.path.join(base1_path, 'A'))
1087 svntest.main.file_append(os.path.join(base1_path, 'A', 'mu'),
1088 "This is the file mu\n")
1089 os.mkdir(os.path.join(base1_path, 'A', 'B'))
1090 svntest.main.file_append(os.path.join(base1_path, 'A', 'B', 'alpha'),
1091 "This is the file alpha\n")
1092 svntest.main.file_append(os.path.join(base1_path, 'A', 'B', 'beta'),
1093 "This is the file beta\n")
1095 # ... Copy it twice ...
1096 shutil.copytree(base1_path, base2_path)
1097 shutil.copytree(base1_path, apply_path)
1099 # ... Gonna see if merge is naughty or nice!
1100 svntest.main.file_append(os.path.join(base2_path, 'A', 'mu'),
1101 "A new line in mu.\n")
1102 os.rename(os.path.join(base2_path, 'A', 'B', 'beta'),
1103 os.path.join(base2_path, 'A', 'B', 'zeta'))
1105 svntest.actions.run_and_verify_svn(None, None, [],
1106 'add', base1_path, base2_path, apply_path)
1108 svntest.actions.run_and_verify_svn(None, None, [],
1109 'ci', '-m', 'rev 2', wc_dir)
1111 expected_output = wc.State(apply_path, {
1112 'A/mu' : Item(status='U '),
1113 'A/B/zeta' : Item(status='A '),
1114 'A/B/beta' : Item(status='D '),
1117 # Search for the comment entitled "The Merge Kluge" elsewhere in
1118 # this file, to understand why we shorten and chdir() below.
1119 saved_cwd = os.getcwd()
1120 os.chdir(svntest.main.work_dir)
1121 # run_and_verify_merge doesn't support 'svn merge URL URL path'
1122 svntest.actions.run_and_verify_svn(None, None, [],
1123 'merge',
1124 '--ignore-ancestry',
1125 base1_url, base2_url,
1126 shorten_path_kludge(apply_path))
1127 os.chdir(saved_cwd)
1129 expected_status = wc.State(apply_path, {
1130 '' : Item(status=' '),
1131 'A' : Item(status=' '),
1132 'A/mu' : Item(status='M '),
1133 'A/B' : Item(status=' '),
1134 'A/B/zeta' : Item(status='A ', copied='+'),
1135 'A/B/alpha' : Item(status=' '),
1136 'A/B/beta' : Item(status='D '),
1137 'iota' : Item(status=' '),
1139 expected_status.tweak(wc_rev=2)
1140 expected_status.tweak('A/B/zeta', wc_rev='-')
1141 svntest.actions.run_and_verify_status(apply_path, expected_status)
1143 #----------------------------------------------------------------------
1144 def merge_one_file_helper(sbox, arg_flav, record_only = 0):
1145 "ARG_FLAV is one of 'r' (revision range) or 'c' (single change)."
1147 if arg_flav not in ('r', 'c', '*'):
1148 raise svntest.Failure("Unrecognized flavor of merge argument")
1150 sbox.build()
1151 wc_dir = sbox.wc_dir
1153 rho_rel_path = os.path.join('A', 'D', 'G', 'rho')
1154 rho_path = os.path.join(wc_dir, rho_rel_path)
1155 G_path = os.path.join(wc_dir, 'A', 'D', 'G')
1156 rho_url = sbox.repo_url + '/A/D/G/rho'
1158 # Change rho for revision 2
1159 svntest.main.file_append(rho_path, 'A new line in rho.\n')
1161 expected_output = wc.State(wc_dir, { rho_rel_path : Item(verb='Sending'), })
1162 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
1163 expected_status.tweak('A/D/G/rho', wc_rev=2)
1164 svntest.actions.run_and_verify_commit(wc_dir,
1165 expected_output,
1166 expected_status,
1167 None,
1168 wc_dir)
1170 # Backdate rho to revision 1, so we can merge in the rev 2 changes.
1171 svntest.actions.run_and_verify_svn(None, None, [],
1172 'up', '-r', '1', rho_path)
1174 # Try one merge with an explicit target; it should succeed.
1175 # ### Yes, it would be nice to use run_and_verify_merge(), but it
1176 # appears to be impossible to get the expected_foo trees working
1177 # right. I think something is still assuming a directory target.
1178 if arg_flav == 'r':
1179 svntest.actions.run_and_verify_svn(None ,
1180 expected_merge_output([[2]], 'U ' +
1181 rho_path + '\n'),
1183 'merge', '-r', '1:2',
1184 rho_url, rho_path)
1185 elif arg_flav == 'c':
1186 svntest.actions.run_and_verify_svn(None ,
1187 expected_merge_output([[2]], 'U ' +
1188 rho_path + '\n'),
1190 'merge', '-c', '2',
1191 rho_url, rho_path)
1192 elif arg_flav == '*':
1193 svntest.actions.run_and_verify_svn(None ,
1194 expected_merge_output([[2]], 'U ' +
1195 rho_path + '\n'),
1197 'merge', rho_url, rho_path)
1199 expected_status.tweak(wc_rev=1)
1200 expected_status.tweak('A/D/G/rho', status='MM')
1201 svntest.actions.run_and_verify_status(wc_dir, expected_status)
1203 # Inspect rho, make sure it's right.
1204 rho_text = svntest.tree.get_text(rho_path)
1205 if rho_text != "This is the file 'rho'.\nA new line in rho.\n":
1206 raise svntest.Failure("Unexpected text in merged '" + rho_path + "'")
1208 # Restore rho to pristine revision 1, for another merge.
1209 svntest.actions.run_and_verify_svn(None, None, [], 'revert', rho_path)
1210 expected_status.tweak('A/D/G/rho', status=' ')
1211 svntest.actions.run_and_verify_status(wc_dir, expected_status)
1213 # Cd into the directory and run merge with no targets.
1214 # It should still merge into rho.
1215 saved_cwd = os.getcwd()
1216 os.chdir(G_path)
1218 # Cannot use run_and_verify_merge with a file target
1219 merge_cmd = ['merge']
1220 if arg_flav == 'r':
1221 merge_cmd += ['-r', '1:2']
1222 elif arg_flav == 'c':
1223 merge_cmd += ['-c', '2']
1225 if record_only:
1226 expected_output = []
1227 merge_cmd.append('--record-only')
1228 rho_expected_status = ' M'
1229 else:
1230 expected_output = expected_merge_output([[2]], 'U rho\n')
1231 rho_expected_status = 'MM'
1232 merge_cmd.append(rho_url)
1234 svntest.actions.run_and_verify_svn(None, expected_output, [], *merge_cmd)
1236 # Inspect rho, make sure it's right.
1237 rho_text = svntest.tree.get_text('rho')
1238 if record_only:
1239 expected_text = "This is the file 'rho'.\n"
1240 else:
1241 expected_text = "This is the file 'rho'.\nA new line in rho.\n"
1242 if rho_text != expected_text:
1243 print
1244 raise svntest.Failure("Unexpected text merged to 'rho' in '" +
1245 G_path + "'")
1246 os.chdir(saved_cwd)
1248 expected_status.tweak('A/D/G/rho', status=rho_expected_status)
1249 svntest.actions.run_and_verify_status(wc_dir, expected_status)
1251 def merge_one_file_using_r(sbox):
1252 "merge one file (issue #1150) using the -r option"
1253 merge_one_file_helper(sbox, 'r')
1255 def merge_one_file_using_c(sbox):
1256 "merge one file (issue #1150) using the -c option"
1257 merge_one_file_helper(sbox, 'c')
1259 def merge_one_file_using_implicit_revs(sbox):
1260 "merge one file without explicit revisions"
1261 merge_one_file_helper(sbox, '*')
1263 def merge_record_only(sbox):
1264 "mark a revision range as merged"
1265 merge_one_file_helper(sbox, 'r', 1)
1267 #----------------------------------------------------------------------
1268 # This is a regression for the enhancement added in issue #785.
1270 def merge_with_implicit_target_helper(sbox, arg_flav):
1271 "ARG_FLAV is one of 'r' (revision range) or 'c' (single change)."
1273 if arg_flav not in ('r', 'c', '*'):
1274 raise svntest.Failure("Unrecognized flavor of merge argument")
1276 sbox.build()
1277 wc_dir = sbox.wc_dir
1279 # Change mu for revision 2
1280 mu_path = os.path.join(wc_dir, 'A', 'mu')
1281 orig_mu_text = svntest.tree.get_text(mu_path)
1282 added_mu_text = ""
1283 for x in range(2,11):
1284 added_mu_text = added_mu_text + 'This is line ' + `x` + ' in mu\n'
1285 svntest.main.file_append(mu_path, added_mu_text)
1287 # Create expected output tree for initial commit
1288 expected_output = wc.State(wc_dir, {
1289 'A/mu' : Item(verb='Sending'),
1292 # Create expected status tree; all local revisions should be at 1,
1293 # but mu should be at revision 2.
1294 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
1295 expected_status.tweak('A/mu', wc_rev=2)
1297 # Initial commit.
1298 svntest.actions.run_and_verify_commit(wc_dir,
1299 expected_output,
1300 expected_status,
1301 None,
1302 wc_dir)
1304 # Make the "other" working copy, at r1
1305 other_wc = sbox.add_wc_path('other')
1306 svntest.actions.duplicate_dir(wc_dir, other_wc)
1307 svntest.main.run_svn(None, 'up', '-r', 1, other_wc)
1309 # Try the merge without an explicit target; it should succeed.
1310 # Can't use run_and_verify_merge cuz it expects a directory argument.
1311 mu_url = sbox.repo_url + '/A/mu'
1313 os.chdir(os.path.join(other_wc, 'A'))
1315 # merge using filename for sourcepath
1316 # Cannot use run_and_verify_merge with a file target
1317 if arg_flav == 'r':
1318 svntest.actions.run_and_verify_svn(None,
1319 expected_merge_output([[2]],
1320 'U mu\n'),
1322 'merge', '-r', '1:2', 'mu')
1323 elif arg_flav == 'c':
1324 svntest.actions.run_and_verify_svn(None,
1325 expected_merge_output([[2]],
1326 'U mu\n'),
1328 'merge', '-c', '2', 'mu')
1330 elif arg_flav == '*':
1331 svntest.actions.run_and_verify_svn(None,
1332 expected_merge_output([[2]],
1333 'U mu\n'),
1335 'merge', 'mu')
1337 # sanity-check resulting file
1338 if (svntest.tree.get_text('mu') != orig_mu_text + added_mu_text):
1339 raise svntest.Failure("Unexpected text in 'mu'")
1341 # merge using URL for sourcepath
1342 if arg_flav == 'r':
1343 svntest.actions.run_and_verify_svn(None,
1344 expected_merge_output([[-2]],
1345 'G mu\n'),
1347 'merge', '-r', '2:1', mu_url)
1348 elif arg_flav == 'c':
1349 svntest.actions.run_and_verify_svn(None,
1350 expected_merge_output([[-2]],
1351 'G mu\n'),
1353 'merge', '-c', '-2', mu_url)
1354 elif arg_flav == '*':
1355 # Implicit merge source URL and revision range detection is for
1356 # forward merges only (e.g. non-reverts). Undo application of
1357 # r2 to enable continuation of the test case.
1358 svntest.actions.run_and_verify_svn(None,
1359 expected_merge_output([[-2]],
1360 'G mu\n'),
1362 'merge', '-c', '-2', mu_url)
1364 # sanity-check resulting file
1365 if (svntest.tree.get_text('mu') != orig_mu_text):
1366 raise svntest.Failure("Unexpected text '%s' in 'mu', expected '%s'" %
1367 (svntest.tree.get_text('mu'), orig_mu_text))
1371 def merge_with_implicit_target_using_r(sbox):
1372 "merging a file w/no explicit target path using -r"
1373 merge_with_implicit_target_helper(sbox, 'r')
1375 def merge_with_implicit_target_using_c(sbox):
1376 "merging a file w/no explicit target path using -c"
1377 merge_with_implicit_target_helper(sbox, 'c')
1379 def merge_with_implicit_target_and_revs(sbox):
1380 "merging a file w/no explicit target path or revs"
1381 merge_with_implicit_target_helper(sbox, '*')
1384 #----------------------------------------------------------------------
1386 def merge_with_prev (sbox):
1387 "merge operations using PREV revision"
1389 sbox.build()
1390 wc_dir = sbox.wc_dir
1392 # Change mu for revision 2
1393 mu_path = os.path.join(wc_dir, 'A', 'mu')
1394 orig_mu_text = svntest.tree.get_text(mu_path)
1395 added_mu_text = ""
1396 for x in range(2,11):
1397 added_mu_text = added_mu_text + '\nThis is line ' + `x` + ' in mu'
1398 added_mu_text += "\n"
1399 svntest.main.file_append(mu_path, added_mu_text)
1401 zot_path = os.path.join(wc_dir, 'A', 'zot')
1403 svntest.main.file_append(zot_path, "bar")
1404 svntest.main.run_svn(None, 'add', zot_path)
1406 # Create expected output tree for initial commit
1407 expected_output = wc.State(wc_dir, {
1408 'A/mu' : Item(verb='Sending'),
1409 'A/zot' : Item(verb='Adding'),
1412 # Create expected status tree; all local revisions should be at 1,
1413 # but mu should be at revision 2.
1414 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
1415 expected_status.tweak('A/mu', wc_rev=2)
1416 expected_status.add({'A/zot' : Item(status=' ', wc_rev=2)})
1418 # Initial commit.
1419 svntest.actions.run_and_verify_commit(wc_dir,
1420 expected_output,
1421 expected_status,
1422 None,
1423 wc_dir)
1425 # Make some other working copies
1426 other_wc = sbox.add_wc_path('other')
1427 svntest.actions.duplicate_dir(wc_dir, other_wc)
1429 another_wc = sbox.add_wc_path('another')
1430 svntest.actions.duplicate_dir(wc_dir, another_wc)
1432 was_cwd = os.getcwd()
1434 os.chdir(os.path.join(other_wc, 'A'))
1436 # Try to revert the last change to mu via svn merge
1437 # Cannot use run_and_verify_merge with a file target
1438 svntest.actions.run_and_verify_svn(None,
1439 expected_merge_output([[-2]],
1440 'U mu\n'),
1442 'merge', '-r', 'HEAD:PREV', 'mu')
1444 # sanity-check resulting file
1445 if (svntest.tree.get_text('mu') != orig_mu_text):
1446 raise svntest.Failure("Unexpected text in 'mu'")
1448 os.chdir(was_cwd)
1450 other_status = expected_status
1451 other_status.wc_dir = other_wc
1452 other_status.tweak('A/mu', status='M ', wc_rev=2)
1453 other_status.tweak('A/zot', wc_rev=2)
1454 svntest.actions.run_and_verify_status(other_wc, other_status)
1456 os.chdir(another_wc)
1458 # ensure 'A' will be at revision 2
1459 svntest.actions.run_and_verify_svn(None, None, [], 'up')
1461 # now try a revert on a directory, and verify that it removed the zot
1462 # file we had added previously
1463 svntest.actions.run_and_verify_svn(None, None, [],
1464 'merge', '-r', 'COMMITTED:PREV',
1465 'A', 'A')
1467 if (svntest.tree.get_text('A/zot') != None):
1468 raise svntest.Failure("Unexpected text in 'A/zot'")
1470 os.chdir(was_cwd)
1472 another_status = expected_status
1473 another_status.wc_dir = another_wc
1474 another_status.tweak(wc_rev=2)
1475 another_status.tweak('A/mu', status='M ')
1476 another_status.tweak('A/zot', status='D ')
1477 svntest.actions.run_and_verify_status(another_wc, another_status)
1479 #----------------------------------------------------------------------
1480 # Regression test for issue #1319: 'svn merge' should *not* 'C' when
1481 # merging a change into a binary file, unless it has local mods, or has
1482 # different contents from the left side of the merge.
1484 def merge_binary_file (sbox):
1485 "merge change into unchanged binary file"
1487 sbox.build()
1488 wc_dir = sbox.wc_dir
1490 # Add a binary file to the project
1491 theta_contents = svntest.main.file_read(
1492 os.path.join(sys.path[0], "theta.bin"), 'rb')
1493 # Write PNG file data into 'A/theta'.
1494 theta_path = os.path.join(wc_dir, 'A', 'theta')
1495 svntest.main.file_write(theta_path, theta_contents, 'wb')
1497 svntest.main.run_svn(None, 'add', theta_path)
1499 # Commit the new binary file, creating revision 2.
1500 expected_output = svntest.wc.State(wc_dir, {
1501 'A/theta' : Item(verb='Adding (bin)'),
1503 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
1504 expected_status.add({
1505 'A/theta' : Item(status=' ', wc_rev=2),
1507 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
1508 expected_status, None,
1509 wc_dir)
1511 # Make the "other" working copy
1512 other_wc = sbox.add_wc_path('other')
1513 svntest.actions.duplicate_dir(wc_dir, other_wc)
1515 # Change the binary file in first working copy, commit revision 3.
1516 svntest.main.file_append(theta_path, "some extra junk")
1517 expected_output = wc.State(wc_dir, {
1518 'A/theta' : Item(verb='Sending'),
1520 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
1521 expected_status.add({
1522 'A/theta' : Item(status=' ', wc_rev=3),
1524 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
1525 expected_status, None,
1526 wc_dir)
1528 # Search for the comment entitled "The Merge Kluge" elsewhere in
1529 # this file, to understand why we shorten and chdir() below.
1530 short_other_wc = shorten_path_kludge(other_wc)
1532 # In second working copy, attempt to 'svn merge -r 2:3'.
1533 # We should *not* see a conflict during the update, but a 'U'.
1534 # And after the merge, the status should be 'M'.
1535 expected_output = wc.State(short_other_wc, {
1536 'A/theta' : Item(status='U '),
1538 expected_disk = svntest.main.greek_state.copy()
1539 expected_disk.add({
1540 '' : Item(props={SVN_PROP_MERGEINFO : '/:3'}),
1541 'A/theta' : Item(theta_contents + "some extra junk",
1542 props={'svn:mime-type' : 'application/octet-stream'}),
1544 expected_status = svntest.actions.get_virginal_state(short_other_wc, 1)
1545 expected_status.add({
1546 '' : Item(status=' M', wc_rev=1),
1547 'A/theta' : Item(status='M ', wc_rev=2),
1549 expected_skip = wc.State('', { })
1551 os.chdir(svntest.main.work_dir)
1552 svntest.actions.run_and_verify_merge(short_other_wc, '2', '3',
1553 sbox.repo_url,
1554 expected_output,
1555 expected_disk,
1556 expected_status,
1557 expected_skip,
1558 None, None, None, None, None,
1561 #----------------------------------------------------------------------
1562 # Regression test for issue #2403: Incorrect 3-way merge of "added"
1563 # binary file which already exists (unmodified) in the WC
1565 def three_way_merge_add_of_existing_binary_file(sbox):
1566 "3-way merge of 'file add' into existing binary"
1568 sbox.build()
1569 wc_dir = sbox.wc_dir
1571 # Create a branch of A, creating revision 2.
1572 A_url = sbox.repo_url + "/A"
1573 branch_A_url = sbox.repo_url + "/copy-of-A"
1574 svntest.actions.run_and_verify_svn(None, None, [],
1575 "cp",
1576 A_url, branch_A_url,
1577 "-m", "Creating copy-of-A")
1579 # Add a binary file to the WC.
1580 theta_contents = svntest.main.file_read(
1581 os.path.join(sys.path[0], "theta.bin"), 'rb')
1582 # Write PNG file data into 'A/theta'.
1583 theta_path = os.path.join(wc_dir, 'A', 'theta')
1584 svntest.main.file_write(theta_path, theta_contents, 'wb')
1586 svntest.main.run_svn(None, "add", theta_path)
1588 # Commit the new binary file to the repos, creating revision 3.
1589 expected_output = svntest.wc.State(wc_dir, {
1590 "A/theta" : Item(verb="Adding (bin)"),
1592 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
1593 expected_status.add({
1594 "A/theta" : Item(status=" ", wc_rev=3),
1596 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
1597 expected_status, None,
1598 wc_dir)
1600 # Search for the comment entitled "The Merge Kluge" elsewhere in
1601 # this file, to understand why we shorten and chdir() below.
1602 short_wc = shorten_path_kludge(wc_dir)
1604 # In the working copy, attempt to 'svn merge branch_A_url@2 A_url@3 A'.
1605 # We should *not* see a conflict during the merge, but an 'A'.
1606 # And after the merge, the status should not report any differences.
1608 expected_output = wc.State(short_wc, {
1609 "A/theta" : Item(status="A "),
1612 # As greek_state is rooted at / instead of /A (our merge target), we
1613 # need a sub-tree of it rather than straight copy.
1614 expected_disk = svntest.main.greek_state.subtree("A")
1615 expected_disk.add({
1616 "" : Item(props={SVN_PROP_MERGEINFO : '/A:2-3'}),
1617 "theta" : Item(theta_contents,
1618 props={"svn:mime-type" : "application/octet-stream"}),
1620 expected_status = svntest.actions.get_virginal_state(short_wc, 1)
1621 expected_status.add({
1622 "A/theta" : Item(status=" ", wc_rev=3),
1624 expected_status.tweak("A", status=" M")
1625 expected_status.remove("") # top-level of the WC
1626 expected_status.remove("iota")
1627 expected_skip = wc.State("", { })
1629 os.chdir(svntest.main.work_dir)
1630 # If we merge into short_wc alone, theta appears at the WC root,
1631 # which is in the wrong location -- append "/A" to stay on target.
1632 svntest.actions.run_and_verify_merge2(short_wc + "/A", "2", "3",
1633 branch_A_url, A_url,
1634 expected_output,
1635 expected_disk,
1636 expected_status,
1637 expected_skip,
1638 None, None, None, None, None,
1641 #----------------------------------------------------------------------
1642 # Regression test for Issue #1297:
1643 # A merge that creates a new file followed by an immediate diff
1644 # The diff should succeed.
1646 def merge_in_new_file_and_diff(sbox):
1647 "diff after merge that creates a new file"
1649 sbox.build()
1650 wc_dir = sbox.wc_dir
1652 trunk_url = sbox.repo_url + '/A/B/E'
1654 # Create a branch
1655 svntest.actions.run_and_verify_svn(None, None, [], 'cp',
1656 trunk_url,
1657 sbox.repo_url + '/branch',
1658 '-m', "Creating the Branch")
1660 # Update to revision 2.
1661 svntest.actions.run_and_verify_svn(None, None, [],
1662 'update', wc_dir)
1664 new_file_path = os.path.join(wc_dir, 'A', 'B', 'E', 'newfile')
1665 svntest.main.file_write(new_file_path, "newfile\n")
1667 # Add the new file, and commit revision 3.
1668 svntest.actions.run_and_verify_svn(None, None, [], "add", new_file_path)
1669 svntest.actions.run_and_verify_svn(None, None, [],
1670 'ci', '-m',
1671 "Changing the trunk.", wc_dir)
1673 # Search for the comment entitled "The Merge Kluge" elsewhere in
1674 # this file, to understand why we shorten and chdir() below.
1675 branch_path = os.path.join(wc_dir, "branch")
1676 short_branch_path = shorten_path_kludge(branch_path)
1678 # Merge our addition into the branch.
1679 expected_output = svntest.wc.State(short_branch_path, {
1680 'newfile' : Item(status='A '),
1682 expected_disk = wc.State('', {
1683 'alpha' : Item("This is the file 'alpha'.\n"),
1684 'beta' : Item("This is the file 'beta'.\n"),
1685 'newfile' : Item("newfile\n"),
1687 expected_status = wc.State(short_branch_path, {
1688 '' : Item(status=' M', wc_rev=2),
1689 'alpha' : Item(status=' ', wc_rev=2),
1690 'beta' : Item(status=' ', wc_rev=2),
1691 'newfile' : Item(status='A ', wc_rev='-', copied='+')
1693 expected_skip = wc.State('', { })
1695 saved_cwd = os.getcwd()
1697 os.chdir(svntest.main.work_dir)
1698 svntest.actions.run_and_verify_merge(short_branch_path,
1699 '1', 'HEAD', trunk_url,
1700 expected_output,
1701 expected_disk,
1702 expected_status,
1703 expected_skip)
1705 os.chdir(saved_cwd)
1707 # Finally, run diff. This diff produces no output!
1708 expected_output = [
1709 "\n",
1710 "Property changes on: " + branch_path + "\n",
1711 "___________________________________________________________________\n",
1712 "Added: " + SVN_PROP_MERGEINFO + "\n",
1713 " Merged /A/B/E:r2-3\n",
1714 "\n", ]
1715 svntest.actions.run_and_verify_svn(None, expected_output, [], 'diff',
1716 branch_path)
1719 #----------------------------------------------------------------------
1721 # Issue #1425: 'svn merge' should skip over any unversioned obstructions.
1723 def merge_skips_obstructions(sbox):
1724 "merge should skip over unversioned obstructions"
1726 sbox.build()
1727 wc_dir = sbox.wc_dir
1729 C_path = os.path.join(wc_dir, 'A', 'C')
1730 F_path = os.path.join(wc_dir, 'A', 'B', 'F')
1731 F_url = sbox.repo_url + '/A/B/F'
1733 Q_path = os.path.join(F_path, 'Q')
1734 foo_path = os.path.join(F_path, 'foo')
1735 bar_path = os.path.join(F_path, 'Q', 'bar')
1737 svntest.main.run_svn(None, 'mkdir', Q_path)
1738 svntest.main.file_append(foo_path, "foo")
1739 svntest.main.file_append(bar_path, "bar")
1740 svntest.main.run_svn(None, 'add', foo_path, bar_path)
1742 expected_output = wc.State(wc_dir, {
1743 'A/B/F/Q' : Item(verb='Adding'),
1744 'A/B/F/Q/bar' : Item(verb='Adding'),
1745 'A/B/F/foo' : Item(verb='Adding'),
1747 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
1748 expected_status.add({
1749 'A/B/F/Q' : Item(status=' ', wc_rev=2),
1750 'A/B/F/Q/bar' : Item(status=' ', wc_rev=2),
1751 'A/B/F/foo' : Item(status=' ', wc_rev=2),
1753 svntest.actions.run_and_verify_commit(wc_dir,
1754 expected_output,
1755 expected_status,
1756 None,
1757 wc_dir)
1759 pre_merge_status = expected_status
1761 # Revision 2 now has A/B/F/foo, A/B/F/Q, A/B/F/Q/bar. Let's merge
1762 # those 'F' changes into empty dir 'C'. But first, create an
1763 # unversioned 'foo' within C, and make sure 'svn merge' doesn't
1764 # error when the addition of foo is obstructed.
1766 # Search for the comment entitled "The Merge Kluge" elsewhere in
1767 # this file, to understand why we shorten and chdir() below.
1768 short_C_path = shorten_path_kludge(C_path)
1770 expected_output = wc.State(short_C_path, {
1771 'Q' : Item(status='A '),
1772 'Q/bar' : Item(status='A '),
1774 expected_disk = wc.State('', {
1775 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B/F:2'}),
1776 'Q' : Item(),
1777 'Q/bar' : Item("bar"),
1778 'foo' : Item("foo"),
1780 expected_status = wc.State(short_C_path, {
1781 '' : Item(status=' M', wc_rev=1),
1782 'Q' : Item(status='A ', wc_rev='-', copied='+'),
1783 'Q/bar' : Item(status='A ', wc_rev='-', copied='+'),
1785 expected_skip = wc.State(short_C_path, {
1786 'foo' : Item(),
1788 # Unversioned:
1789 svntest.main.file_append(os.path.join(C_path, "foo"), "foo")
1791 saved_cwd = os.getcwd()
1793 os.chdir(svntest.main.work_dir)
1794 svntest.actions.run_and_verify_merge(short_C_path, '1', '2', F_url,
1795 expected_output,
1796 expected_disk,
1797 expected_status,
1798 expected_skip,
1799 None, None, None, None, None,
1800 1, 0)
1802 os.chdir(saved_cwd)
1804 # Revert the local mods, and this time make "Q" obstructed. An
1805 # unversioned file called "Q" will obstruct the adding of the
1806 # directory of the same name.
1808 svntest.actions.run_and_verify_svn(None, None, [],
1809 'revert', '-R', wc_dir)
1810 os.unlink(os.path.join(C_path, "foo"))
1811 svntest.main.safe_rmtree(os.path.join(C_path, "Q"))
1812 svntest.main.file_append(os.path.join(C_path, "Q"), "foo") # unversioned
1813 svntest.actions.run_and_verify_status(wc_dir, pre_merge_status)
1815 # Search for the comment entitled "The Merge Kluge" elsewhere in
1816 # this file, to understand why we use short_C_path and chdir() below.
1817 expected_output = wc.State(short_C_path, {
1818 'foo' : Item(status='A '),
1820 expected_disk = wc.State('', {
1821 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B/F:2'}),
1822 'Q' : Item("foo"),
1823 'foo' : Item("foo"),
1825 expected_status = wc.State(short_C_path, {
1826 '' : Item(status=' M', wc_rev=1),
1827 'foo' : Item(status='A ', wc_rev='-', copied='+'),
1829 expected_skip = wc.State(short_C_path, {
1830 'Q' : Item(),
1831 'Q/bar' : Item(),
1834 saved_cwd = os.getcwd()
1836 os.chdir(svntest.main.work_dir)
1837 svntest.actions.run_and_verify_merge(short_C_path, '1', '2', F_url,
1838 expected_output,
1839 expected_disk,
1840 expected_status,
1841 expected_skip,
1842 None, None, None, None, None,
1843 1, 0)
1845 os.chdir(saved_cwd)
1847 # Revert the local mods, and commit the deletion of iota and A/D/G. (r3)
1848 os.unlink(os.path.join(C_path, "foo"))
1849 svntest.actions.run_and_verify_svn(None, None, [], 'revert', '-R', wc_dir)
1850 svntest.actions.run_and_verify_status(wc_dir, pre_merge_status)
1852 iota_path = os.path.join(wc_dir, 'iota')
1853 G_path = os.path.join(wc_dir, 'A', 'D', 'G')
1854 svntest.actions.run_and_verify_svn(None, None, [], 'rm', iota_path, G_path)
1856 expected_output = wc.State(wc_dir, {
1857 'A/D/G' : Item(verb='Deleting'),
1858 'iota' : Item(verb='Deleting'),
1860 expected_status = pre_merge_status
1861 expected_status.remove('iota', 'A/D/G', 'A/D/G/pi', 'A/D/G/rho', 'A/D/G/tau')
1862 svntest.actions.run_and_verify_commit(wc_dir,
1863 expected_output,
1864 expected_status,
1865 None, wc_dir)
1867 # Now create unversioned iota and A/D/G, try running a merge -r2:3.
1868 # The merge process should skip over these targets, since they're
1869 # unversioned.
1871 # Search for the comment entitled "The Merge Kluge" elsewhere in
1872 # this file, to understand why we shorten and chdir() below.
1873 short_wc_dir = shorten_path_kludge(wc_dir)
1875 svntest.main.file_append(iota_path, "foo") # unversioned
1876 os.mkdir(G_path) # unversioned
1878 expected_output = wc.State(short_wc_dir, { })
1879 expected_disk = svntest.main.greek_state.copy()
1880 expected_disk.remove('A/D/G/pi', 'A/D/G/rho', 'A/D/G/tau')
1881 expected_disk.add({
1882 '' : Item(props={SVN_PROP_MERGEINFO : '/:3'}),
1883 'A/B/F/Q' : Item(),
1884 'A/B/F/Q/bar' : Item("bar"),
1885 'A/B/F/foo' : Item("foo"),
1886 'iota' : Item("foo"),
1887 'A/C/Q' : Item("foo"),
1889 # No-op merge still sets mergeinfo
1890 expected_status.tweak('', status=' M')
1891 expected_skip = wc.State(short_wc_dir, {
1892 'A/D/G' : Item(),
1893 'iota' : Item(),
1896 saved_cwd = os.getcwd()
1898 os.chdir(svntest.main.work_dir)
1899 svntest.actions.run_and_verify_merge(short_wc_dir, '2', '3',
1900 sbox.repo_url,
1901 expected_output,
1902 expected_disk,
1903 expected_status.copy(short_wc_dir),
1904 expected_skip,
1905 None, None, None, None, None,
1906 1, 0)
1908 os.chdir(saved_cwd)
1910 # Revert the local mods, and commit a change to A/B/lambda (r4), and then
1911 # commit the deletion of the same file. (r5)
1912 os.unlink(iota_path)
1913 svntest.main.safe_rmtree(G_path)
1914 svntest.actions.run_and_verify_svn(None, None, [], 'revert', '-R', wc_dir)
1915 expected_status.tweak('', status=' ')
1916 svntest.actions.run_and_verify_status(wc_dir, expected_status)
1918 lambda_path = os.path.join(wc_dir, 'A', 'B', 'lambda')
1919 svntest.main.file_append(lambda_path, "more text")
1920 expected_output = wc.State(wc_dir, {
1921 'A/B/lambda' : Item(verb='Sending'),
1923 expected_status.tweak('A/B/lambda', wc_rev=4)
1924 svntest.actions.run_and_verify_commit(wc_dir,
1925 expected_output,
1926 expected_status,
1927 None, wc_dir)
1929 svntest.actions.run_and_verify_svn(None, None, [], 'rm', lambda_path)
1931 expected_output = wc.State(wc_dir, {
1932 'A/B/lambda' : Item(verb='Deleting'),
1934 expected_status.remove('A/B/lambda')
1935 svntest.actions.run_and_verify_commit(wc_dir,
1936 expected_output,
1937 expected_status,
1938 None, wc_dir)
1940 # lambda is gone, so create an unversioned lambda in its place.
1941 # Then attempt to merge -r3:4, which is a change to lambda. The merge
1942 # should simply skip the unversioned file.
1944 svntest.main.file_append(lambda_path, "foo") # unversioned
1946 # Search for the comment entitled "The Merge Kluge" elsewhere in
1947 # this file, to understand why we use short_wc_dir and chdir() below.
1948 expected_output = wc.State(short_wc_dir, { })
1949 expected_disk.add({
1950 'A/B/lambda' : Item("foo"),
1952 expected_disk.remove('A/D/G', 'iota')
1953 expected_disk.tweak('', props={SVN_PROP_MERGEINFO : '/:4'})
1954 expected_skip = wc.State(short_wc_dir, {
1955 'A/B/lambda' : Item(),
1957 # No-op merge still sets mergeinfo.
1958 expected_status_short = expected_status.copy(short_wc_dir)
1959 expected_status_short.tweak('', status=' M')
1961 saved_cwd = os.getcwd()
1963 os.chdir(svntest.main.work_dir)
1964 svntest.actions.run_and_verify_merge(short_wc_dir, '3', '4',
1965 sbox.repo_url,
1966 expected_output,
1967 expected_disk,
1968 expected_status_short,
1969 expected_skip,
1970 None, None, None, None, None,
1971 1, 0)
1973 os.chdir(saved_cwd)
1975 # OK, so let's commit the new lambda (r6), and then delete the
1976 # working file. Then re-run the -r3:4 merge, and see how svn deals
1977 # with a file being under version control, but missing.
1979 svntest.actions.run_and_verify_svn(None, None, [], 'add', lambda_path)
1981 # Mergeinfo prop changed so update to avoid out of date error.
1982 svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
1984 expected_output = wc.State(wc_dir, {
1985 '' : Item(verb='Sending'),
1986 'A/B/lambda' : Item(verb='Adding'),
1988 expected_status.tweak(wc_rev=5)
1989 expected_status.add({
1990 'A/B/lambda' : Item(wc_rev=6, status=' '),
1992 expected_status.tweak('', status=' ', wc_rev=6)
1993 svntest.actions.run_and_verify_commit(wc_dir,
1994 expected_output,
1995 expected_status,
1996 None, wc_dir)
1997 os.unlink(lambda_path)
1999 # Search for the comment entitled "The Merge Kluge" elsewhere in
2000 # this file, to understand why we use short_wc_dir and chdir() below.
2001 expected_output = wc.State(short_wc_dir, { })
2002 expected_disk.remove('A/B/lambda')
2003 expected_status.tweak('A/B/lambda', status='! ')
2004 os.chdir(svntest.main.work_dir)
2005 expected_status.tweak('', status=' ')
2006 # Why do we need to --ignore-ancestry? Because the previous merge of r4,
2007 # despite being inoperative, set mergeinfo for r4 on the WC. With the
2008 # advent of merge tracking this repeat merge attempt would not be attempted.
2009 # By using --ignore-ancestry we disregard the mergeinfo and *really* try to
2010 # merge into a missing path. This is another facet of issue #2898.
2011 svntest.actions.run_and_verify_merge(short_wc_dir, '3', '4',
2012 sbox.repo_url,
2013 expected_output,
2014 expected_disk,
2015 expected_status.copy(short_wc_dir),
2016 expected_skip,
2017 None, None, None, None, None,
2018 1, 0, '--ignore-ancestry')
2020 #----------------------------------------------------------------------
2021 # At one time, a merge that added items with the same name as missing
2022 # items would attempt to add the items and fail, leaving the working
2023 # copy locked and broken.
2025 def merge_into_missing(sbox):
2026 "merge into missing must not break working copy"
2028 sbox.build()
2029 wc_dir = sbox.wc_dir
2031 F_path = os.path.join(wc_dir, 'A', 'B', 'F')
2032 F_url = sbox.repo_url + '/A/B/F'
2033 Q_path = os.path.join(F_path, 'Q')
2034 foo_path = os.path.join(F_path, 'foo')
2036 svntest.actions.run_and_verify_svn(None, None, [], 'mkdir', Q_path)
2037 svntest.main.file_append(foo_path, "foo")
2038 svntest.actions.run_and_verify_svn(None, None, [], 'add', foo_path)
2040 expected_output = wc.State(wc_dir, {
2041 'A/B/F/Q' : Item(verb='Adding'),
2042 'A/B/F/foo' : Item(verb='Adding'),
2044 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
2045 expected_status.add({
2046 'A/B/F/Q' : Item(status=' ', wc_rev=2),
2047 'A/B/F/foo' : Item(status=' ', wc_rev=2),
2049 svntest.actions.run_and_verify_commit(wc_dir,
2050 expected_output,
2051 expected_status,
2052 None, wc_dir)
2054 R_path = os.path.join(Q_path, 'R')
2055 bar_path = os.path.join(R_path, 'bar')
2056 baz_path = os.path.join(Q_path, 'baz')
2057 svntest.actions.run_and_verify_svn(None, None, [], 'mkdir', R_path)
2058 svntest.main.file_append(bar_path, "bar")
2059 svntest.actions.run_and_verify_svn(None, None, [], 'add', bar_path)
2060 svntest.main.file_append(baz_path, "baz")
2061 svntest.actions.run_and_verify_svn(None, None, [], 'add', baz_path)
2063 expected_output = wc.State(wc_dir, {
2064 'A/B/F/Q/R' : Item(verb='Adding'),
2065 'A/B/F/Q/R/bar' : Item(verb='Adding'),
2066 'A/B/F/Q/baz' : Item(verb='Adding'),
2068 expected_status.add({
2069 'A/B/F/Q/R' : Item(status=' ', wc_rev=3),
2070 'A/B/F/Q/R/bar' : Item(status=' ', wc_rev=3),
2071 'A/B/F/Q/baz' : Item(status=' ', wc_rev=3),
2073 svntest.actions.run_and_verify_commit(wc_dir,
2074 expected_output,
2075 expected_status,
2076 None, wc_dir)
2078 os.unlink(foo_path)
2079 svntest.main.safe_rmtree(Q_path)
2081 expected_output = wc.State(F_path, {
2083 expected_disk = wc.State('', {
2085 expected_status = wc.State(F_path, {
2086 '' : Item(status=' ', wc_rev=1),
2087 'foo' : Item(status='! ', wc_rev=2),
2088 'Q' : Item(status='! ', wc_rev='?'),
2090 expected_skip = wc.State(F_path, {
2091 'Q' : Item(),
2092 'foo' : Item(),
2095 ### Need to real and dry-run separately since real merge notifies Q
2096 ### twice!
2097 svntest.actions.run_and_verify_merge(F_path, '1', '2', F_url,
2098 expected_output,
2099 expected_disk,
2100 expected_status,
2101 expected_skip,
2102 None, None, None, None, None,
2103 0, 0, '--dry-run')
2105 expected_status.tweak('foo', status='!M')
2106 expected_status.tweak('', status=' M')
2107 svntest.actions.run_and_verify_merge(F_path, '1', '2', F_url,
2108 expected_output,
2109 expected_disk,
2110 expected_status,
2111 expected_skip,
2112 None, None, None, None, None,
2113 0, 0)
2115 # This merge fails when it attempts to descend into the missing
2116 # directory. That's OK, there is no real need to support merge into
2117 # an incomplete working copy, so long as when it fails it doesn't
2118 # break the working copy.
2119 svntest.main.run_svn('Working copy not locked',
2120 'merge', '-r1:3', '--dry-run', F_url, F_path)
2122 svntest.main.run_svn('Working copy not locked',
2123 'merge', '-r1:3', F_url, F_path)
2125 # Check working copy is not locked.
2126 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
2127 expected_status.add({
2128 'A/B/F' : Item(status=' M', wc_rev=1),
2129 'A/B/F/foo' : Item(status='!M', wc_rev=2),
2130 'A/B/F/Q' : Item(status='! ', wc_rev='?'),
2132 svntest.actions.run_and_verify_status(wc_dir, expected_status)
2134 #----------------------------------------------------------------------
2135 # A test for issue 1738
2137 def dry_run_adds_file_with_prop(sbox):
2138 "merge --dry-run adding a new file with props"
2140 sbox.build()
2141 wc_dir = sbox.wc_dir
2143 # Commit a new file which has a property.
2144 zig_path = os.path.join(wc_dir, 'A', 'B', 'E', 'zig')
2145 svntest.main.file_append(zig_path, "zig contents")
2146 svntest.actions.run_and_verify_svn(None, None, [], 'add', zig_path)
2147 svntest.actions.run_and_verify_svn(None, None, [],
2148 'propset', 'foo', 'foo_val',
2149 zig_path)
2151 expected_output = wc.State(wc_dir, {
2152 'A/B/E/zig' : Item(verb='Adding'),
2154 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
2155 expected_status.add({
2156 'A/B/E/zig' : Item(status=' ', wc_rev=2),
2158 svntest.actions.run_and_verify_commit(wc_dir,
2159 expected_output,
2160 expected_status,
2161 None, wc_dir)
2163 # Do a regular merge of that change into a different dir.
2164 F_path = os.path.join(wc_dir, 'A', 'B', 'F')
2165 E_url = sbox.repo_url + '/A/B/E'
2167 # Search for the comment entitled "The Merge Kluge" elsewhere in
2168 # this file, to understand why we shorten and chdir() below.
2169 short_F_path = shorten_path_kludge(F_path)
2171 expected_output = wc.State(short_F_path, {
2172 'zig' : Item(status='A '),
2174 expected_disk = wc.State('', {
2175 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B/E:2'}),
2176 'zig' : Item("zig contents", {'foo':'foo_val'}),
2178 expected_skip = wc.State('', { })
2179 expected_status = None # status is optional
2181 os.chdir(svntest.main.work_dir)
2182 svntest.actions.run_and_verify_merge(short_F_path, '1', '2', E_url,
2183 expected_output,
2184 expected_disk,
2185 expected_status,
2186 expected_skip,
2187 None, None, None, None, None,
2188 1, # please check props
2189 1) # and do a dry-run also)
2191 #----------------------------------------------------------------------
2193 # Regression test for issue #1673
2194 # Merge a binary file from two URL with a common ancestry
2196 def merge_binary_with_common_ancestry(sbox):
2197 "merge binary files with common ancestry"
2199 sbox.build()
2200 wc_dir = sbox.wc_dir
2202 # Create the common ancestry path
2203 I_path = os.path.join(wc_dir, 'I')
2204 svntest.main.run_svn(None, 'mkdir', I_path)
2206 # Add a binary file to the common ancestry path
2207 theta_contents = svntest.main.file_read(
2208 os.path.join(sys.path[0], "theta.bin"), 'rb')
2209 theta_I_path = os.path.join(I_path, 'theta')
2210 svntest.main.file_write(theta_I_path, theta_contents)
2211 svntest.main.run_svn(None, 'add', theta_I_path)
2212 svntest.main.run_svn(None, 'propset', 'svn:mime-type',
2213 'application/octet-stream', theta_I_path)
2215 # Commit the ancestry
2216 expected_output = wc.State(wc_dir, {
2217 'I' : Item(verb='Adding'),
2218 'I/theta' : Item(verb='Adding (bin)'),
2221 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
2222 expected_status.add({
2223 'I' : Item(status=' ', wc_rev=2),
2224 'I/theta' : Item(status=' ', wc_rev=2),
2227 svntest.actions.run_and_verify_commit(wc_dir,
2228 expected_output, expected_status,
2229 None,
2230 wc_dir)
2232 # Create the first branch
2233 J_path = os.path.join(wc_dir, 'J')
2234 svntest.main.run_svn(None, 'copy', I_path, J_path)
2236 # Commit the first branch
2237 expected_output = wc.State(wc_dir, {
2238 'J' : Item(verb='Adding'),
2241 expected_status.add({
2242 'J' : Item(status=' ', wc_rev=3),
2243 'J/theta' : Item(status=' ', wc_rev=3),
2246 svntest.actions.run_and_verify_commit(wc_dir,
2247 expected_output, expected_status,
2248 None,
2249 wc_dir)
2251 # Create the path where the files will be merged
2252 K_path = os.path.join(wc_dir, 'K')
2253 svntest.main.run_svn(None, 'mkdir', K_path)
2255 # Commit the new path
2256 expected_output = wc.State(wc_dir, {
2257 'K' : Item(verb='Adding'),
2260 expected_status.add({
2261 'K' : Item(status=' ', wc_rev=4),
2264 svntest.actions.run_and_verify_commit(wc_dir,
2265 expected_output, expected_status,
2266 None,
2267 wc_dir)
2269 # Copy 'I/theta' to 'K/'. This file will be merged later.
2270 theta_K_path = os.path.join(K_path, 'theta')
2271 svntest.main.run_svn(None, 'copy', theta_I_path, theta_K_path)
2273 # Commit the new file
2274 expected_output = wc.State(wc_dir, {
2275 'K/theta' : Item(verb='Adding (bin)'),
2278 expected_status.add({
2279 'K/theta' : Item(status=' ', wc_rev=5),
2282 svntest.actions.run_and_verify_commit(wc_dir,
2283 expected_output, expected_status,
2284 None,
2285 wc_dir)
2287 # Modify the original ancestry 'I/theta'
2288 svntest.main.file_append(theta_I_path, "some extra junk")
2290 # Commit the modification
2291 expected_output = wc.State(wc_dir, {
2292 'I/theta' : Item(verb='Sending'),
2295 expected_status.tweak('I/theta', wc_rev=6)
2297 svntest.actions.run_and_verify_commit(wc_dir,
2298 expected_output, expected_status,
2299 None,
2300 wc_dir)
2302 # Create the second branch from the modified ancestry
2303 L_path = os.path.join(wc_dir, 'L')
2304 svntest.main.run_svn(None, 'copy', I_path, L_path)
2306 # Commit the second branch
2307 expected_output = wc.State(wc_dir, {
2308 'L' : Item(verb='Adding'),
2309 'L/theta' : Item(verb='Adding (bin)'),
2312 expected_status.add({
2313 'L' : Item(status=' ', wc_rev=7),
2314 'L/theta' : Item(status=' ', wc_rev=7),
2317 svntest.actions.run_and_verify_commit(wc_dir,
2318 expected_output, expected_status,
2319 None,
2320 wc_dir)
2322 # Now merge first ('J/') and second ('L/') branches into 'K/'
2323 saved_cwd = os.getcwd()
2325 os.chdir(K_path)
2326 theta_J_url = sbox.repo_url + '/J/theta'
2327 theta_L_url = sbox.repo_url + '/L/theta'
2328 svntest.actions.run_and_verify_svn(None,
2329 expected_merge_output(None,
2330 'U theta\n'),
2332 'merge', theta_J_url, theta_L_url)
2333 os.chdir(saved_cwd)
2335 expected_status.tweak('K/theta', status='MM')
2336 svntest.actions.run_and_verify_status(wc_dir, expected_status)
2338 #----------------------------------------------------------------------
2339 # A test for issue 1905
2340 def merge_funny_chars_on_path(sbox):
2341 "merge with funny characters (issue #1905)"
2343 sbox.build()
2344 wc_dir = sbox.wc_dir
2346 # In following lists: 'd' stands for directory, 'f' for file
2347 # targets to be added by recursive add
2348 add_by_add = [
2349 ('d', 'dir_10', 'F%lename'),
2350 ('d', 'dir%20', 'F lename'),
2351 ('d', 'dir 30', 'Filename'),
2352 ('d', 'dir 40', None),
2353 ('f', 'F lename', None),
2356 # targets to be added by 'svn mkdir' + add
2357 add_by_mkdir = [
2358 ('d', 'dir_11', 'F%lename'),
2359 ('d', 'dir%21', 'Filename'),
2360 ('d', 'dir 31', 'F lename'),
2361 ('d', 'dir 41', None),
2364 for target in add_by_add:
2365 if target[0] == 'd':
2366 target_dir = os.path.join(wc_dir, 'A', 'B', 'E', target[1])
2367 os.mkdir(target_dir)
2368 if target[2]:
2369 target_path = os.path.join(wc_dir, 'A', 'B', 'E', '%s' % target[1],
2370 target[2])
2371 svntest.main.file_append(target_path, "%s/%s" % (target[1], target[2]))
2372 svntest.actions.run_and_verify_svn(None, None, [], 'add', target_dir)
2373 elif target[0] == 'f':
2374 target_path = os.path.join(wc_dir, 'A', 'B', 'E', '%s' % target[1])
2375 svntest.main.file_append(target_path, "%s" % target[1])
2376 svntest.actions.run_and_verify_svn(None, None, [], 'add', target_path)
2377 else:
2378 raise svntest.Failure
2381 for target in add_by_mkdir:
2382 if target[0] == 'd':
2383 target_dir = os.path.join(wc_dir, 'A', 'B', 'E', target[1])
2384 svntest.actions.run_and_verify_svn(None, None, [], 'mkdir', target_dir)
2385 if target[2]:
2386 target_path = os.path.join(wc_dir, 'A', 'B', 'E', '%s' % target[1],
2387 target[2])
2388 svntest.main.file_append(target_path, "%s/%s" % (target[1], target[2]))
2389 svntest.actions.run_and_verify_svn(None, None, [], 'add', target_path)
2391 expected_output_dic = {}
2392 expected_status_dic = {}
2394 for targets in add_by_add,add_by_mkdir:
2395 for target in targets:
2396 key = 'A/B/E/%s' % target[1]
2397 expected_output_dic[key] = Item(verb='Adding')
2398 expected_status_dic[key] = Item(status=' ', wc_rev=2)
2400 if target[2]:
2401 key = 'A/B/E/%s/%s' % (target[1], target[2])
2402 expected_output_dic[key] = Item(verb='Adding')
2403 expected_status_dic[key] = Item(status=' ', wc_rev=2)
2406 expected_output = wc.State(wc_dir, expected_output_dic)
2408 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
2409 expected_status.add(expected_status_dic)
2411 svntest.actions.run_and_verify_commit(wc_dir,
2412 expected_output,
2413 expected_status,
2414 None, wc_dir)
2416 # Do a regular merge of that change into a different dir.
2417 F_path = os.path.join(wc_dir, 'A', 'B', 'F')
2418 E_url = sbox.repo_url + '/A/B/E'
2420 expected_output_dic = {}
2421 expected_disk_dic = {}
2423 for targets in add_by_add,add_by_mkdir:
2424 for target in targets:
2425 key = '%s' % target[1]
2426 expected_output_dic[key] = Item(status='A ')
2427 if target[0] == 'd':
2428 expected_disk_dic[key] = Item(None, {})
2429 elif target[0] == 'f':
2430 expected_disk_dic[key] = Item("%s" % target[1], {})
2431 else:
2432 raise svntest.Failure
2433 if target[2]:
2434 key = '%s/%s' % (target[1], target[2])
2435 expected_output_dic[key] = Item(status='A ')
2436 expected_disk_dic[key] = Item('%s/%s' % (target[1], target[2]), {})
2439 # Search for the comment entitled "The Merge Kluge" elsewhere in
2440 # this file, to understand why we shorten and chdir() below.
2441 short_F_path = shorten_path_kludge(F_path)
2443 expected_output = wc.State(short_F_path, expected_output_dic)
2445 expected_disk = wc.State('', expected_disk_dic)
2446 expected_skip = wc.State('', { })
2447 expected_status = None # status is optional
2449 saved_cwd = os.getcwd()
2451 os.chdir(svntest.main.work_dir)
2452 svntest.actions.run_and_verify_merge(short_F_path, '1', '2', E_url,
2453 expected_output,
2454 expected_disk,
2455 expected_status,
2456 expected_skip,
2457 None, None, None, None, None,
2458 0, # don't check props
2459 1) # but do a dry-run
2460 os.chdir(saved_cwd)
2462 expected_output_dic = {}
2464 for targets in add_by_add,add_by_mkdir:
2465 for target in targets:
2466 key = '%s' % target[1]
2467 expected_output_dic[key] = Item(verb='Adding')
2468 if target[2]:
2469 key = '%s/%s' % (target[1], target[2])
2470 expected_output_dic[key] = Item(verb='Adding')
2472 expected_output = wc.State(F_path, expected_output_dic)
2473 expected_output.add({
2474 '' : Item(verb='Sending'),
2477 svntest.actions.run_and_verify_commit(F_path,
2478 expected_output,
2479 None,
2480 None, wc_dir)
2482 #-----------------------------------------------------------------------
2483 # Regression test for issue #2064
2485 def merge_keyword_expansions(sbox):
2486 "merge changes to keyword expansion property"
2488 sbox.build()
2490 wcpath = sbox.wc_dir
2491 tpath = os.path.join(wcpath, "t")
2492 bpath = os.path.join(wcpath, "b")
2493 t_fpath = os.path.join(tpath, 'f')
2494 b_fpath = os.path.join(bpath, 'f')
2496 os.mkdir(tpath)
2497 svntest.main.run_svn(None, "add", tpath)
2498 # Commit r2.
2499 svntest.actions.run_and_verify_svn(None, None, [],
2500 "ci", "-m", "r2", wcpath)
2502 # Copy t to b.
2503 svntest.main.run_svn(None, "cp", tpath, bpath)
2504 # Commit r3
2505 svntest.actions.run_and_verify_svn(None, None, [],
2506 "ci", "-m", "r3", wcpath)
2508 # Add a file to t.
2509 svntest.main.file_append(t_fpath, "$Revision$")
2510 svntest.actions.run_and_verify_svn(None, None, [],
2511 'add', t_fpath)
2512 # Ask for keyword expansion in the file.
2513 svntest.actions.run_and_verify_svn(None, None, [],
2514 'propset', 'svn:keywords', 'Revision',
2515 t_fpath)
2516 # Commit r4
2517 svntest.actions.run_and_verify_svn(None, None, [],
2518 'ci', '-m', 'r4', wcpath)
2520 # Update the wc before the merge.
2521 svntest.actions.run_and_verify_svn(None, None, [],
2522 'update', wcpath)
2524 expected_status = svntest.actions.get_virginal_state(wcpath, 4)
2525 expected_status.add({
2526 't' : Item(status=' ', wc_rev=4),
2527 't/f' : Item(status=' ', wc_rev=4),
2528 'b' : Item(status=' ', wc_rev=4),
2530 svntest.actions.run_and_verify_status(wcpath, expected_status)
2532 # Do the merge.
2534 # Search for the comment entitled "The Merge Kluge" elsewhere in
2535 # this file, to understand why we shorten and chdir() below.
2536 short_bpath = shorten_path_kludge(bpath)
2538 expected_output = wc.State(short_bpath, {
2539 'f' : Item(status='A '),
2541 expected_disk = wc.State('', {
2542 'f' : Item("$Revision: 4 $"),
2544 expected_status = wc.State(short_bpath, {
2545 '' : Item(status=' M', wc_rev=4),
2546 'f' : Item(status='A ', wc_rev='-', copied='+'),
2548 expected_skip = wc.State(short_bpath, { })
2550 os.chdir(svntest.main.work_dir)
2551 svntest.actions.run_and_verify_merge(short_bpath, '2', 'HEAD',
2552 sbox.repo_url + '/t',
2553 expected_output,
2554 expected_disk,
2555 expected_status,
2556 expected_skip)
2558 #----------------------------------------------------------------------
2559 def merge_prop_change_to_deleted_target(sbox):
2560 "merge prop change into deleted target"
2561 # For issue #2132.
2562 sbox.build()
2563 wc_dir = sbox.wc_dir
2565 # Add a property to alpha.
2566 alpha_path = os.path.join(wc_dir, 'A', 'B', 'E', 'alpha')
2567 svntest.actions.run_and_verify_svn(None, None, [],
2568 'propset', 'foo', 'foo_val',
2569 alpha_path)
2571 # Commit the property add as r2.
2572 expected_output = svntest.wc.State(wc_dir, {
2573 'A/B/E/alpha' : Item(verb='Sending'),
2575 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
2576 expected_status.tweak('A/B/E/alpha', wc_rev=2, status=' ')
2577 svntest.actions.run_and_verify_commit(wc_dir,
2578 expected_output, expected_status,
2579 None, wc_dir)
2580 svntest.actions.run_and_verify_svn(None, None, [],
2581 'up', wc_dir)
2583 # Remove alpha entirely.
2584 svntest.actions.run_and_verify_svn(None, None, [], 'rm', alpha_path)
2585 expected_output = wc.State(wc_dir, {
2586 'A/B/E/alpha' : Item(verb='Deleting'),
2588 expected_status.tweak(wc_rev=2)
2589 expected_status.remove('A/B/E/alpha')
2590 svntest.actions.run_and_verify_commit(wc_dir,
2591 expected_output,
2592 expected_status,
2593 None, alpha_path)
2595 # Try merging the original propset, which applies to a target that
2596 # no longer exists. The bug would only reproduce when run from
2597 # inside the wc, so we cd in there. We have to use
2598 # --ignore-ancestry here because our merge logic will otherwise
2599 # prevent a merge of changes we already have.
2600 os.chdir(wc_dir)
2601 svntest.actions.run_and_verify_svn("Merge errored unexpectedly",
2602 svntest.verify.AnyOutput, [], 'merge',
2603 '-r1:2', '--ignore-ancestry', '.')
2606 def set_up_dir_replace(sbox):
2607 """Set up the working copy for directory replace tests, creating
2608 directory 'A/B/F/foo' with files 'new file' and 'new file2' within
2609 it (r2), and merging 'foo' onto 'C' (r3), then deleting 'A/B/F/foo'
2610 (r4)."""
2612 sbox.build()
2613 wc_dir = sbox.wc_dir
2615 C_path = os.path.join(wc_dir, 'A', 'C')
2616 F_path = os.path.join(wc_dir, 'A', 'B', 'F')
2617 F_url = sbox.repo_url + '/A/B/F'
2619 foo_path = os.path.join(F_path, 'foo')
2620 new_file = os.path.join(foo_path, "new file")
2621 new_file2 = os.path.join(foo_path, "new file 2")
2623 # Make directory foo in F, and add some files within it.
2624 svntest.actions.run_and_verify_svn(None, None, [], 'mkdir', foo_path)
2625 svntest.main.file_append(new_file, "Initial text in new file.\n")
2626 svntest.main.file_append(new_file2, "Initial text in new file 2.\n")
2627 svntest.main.run_svn(None, "add", new_file)
2628 svntest.main.run_svn(None, "add", new_file2)
2630 # Commit all the new content, creating r2.
2631 expected_output = wc.State(wc_dir, {
2632 'A/B/F/foo' : Item(verb='Adding'),
2633 'A/B/F/foo/new file' : Item(verb='Adding'),
2634 'A/B/F/foo/new file 2' : Item(verb='Adding'),
2636 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
2637 expected_status.add({
2638 'A/B/F/foo' : Item(status=' ', wc_rev=2),
2639 'A/B/F/foo/new file' : Item(status=' ', wc_rev=2),
2640 'A/B/F/foo/new file 2' : Item(status=' ', wc_rev=2),
2642 svntest.actions.run_and_verify_commit(wc_dir,
2643 expected_output,
2644 expected_status,
2645 None, wc_dir)
2647 # Merge foo onto C
2648 expected_output = wc.State(C_path, {
2649 'foo' : Item(status='A '),
2650 'foo/new file' : Item(status='A '),
2651 'foo/new file 2' : Item(status='A '),
2653 expected_disk = wc.State('', {
2654 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B/F:2'}),
2655 'foo' : Item(),
2656 'foo/new file' : Item("Initial text in new file.\n"),
2657 'foo/new file 2' : Item("Initial text in new file 2.\n"),
2659 expected_status = wc.State(C_path, {
2660 '' : Item(status=' M', wc_rev=1),
2661 'foo' : Item(status='A ', wc_rev='-', copied='+'),
2662 'foo/new file' : Item(status='A ', wc_rev='-', copied='+'),
2663 'foo/new file 2' : Item(status='A ', wc_rev='-', copied='+'),
2665 expected_skip = wc.State(C_path, { })
2666 svntest.actions.run_and_verify_merge(C_path, '1', '2', F_url,
2667 expected_output,
2668 expected_disk,
2669 expected_status,
2670 expected_skip,
2671 None, None, None, None, None, 1)
2672 # Commit merge of foo onto C, creating r3.
2673 expected_output = svntest.wc.State(wc_dir, {
2674 'A/C' : Item(verb='Sending'),
2675 'A/C/foo' : Item(verb='Adding'),
2676 'A/C/foo/new file' : Item(verb='Adding'),
2677 'A/C/foo/new file 2' : Item(verb='Adding'),
2679 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
2680 expected_status.add({
2681 'A/B/F/foo' : Item(status=' ', wc_rev=2),
2682 'A/C' : Item(status=' ', wc_rev=3),
2683 'A/B/F/foo/new file' : Item(status=' ', wc_rev=2),
2684 'A/B/F/foo/new file 2' : Item(status=' ', wc_rev=2),
2685 'A/C/foo' : Item(status=' ', wc_rev=3),
2686 'A/C/foo/new file' : Item(status=' ', wc_rev=3),
2687 'A/C/foo/new file 2' : Item(status=' ', wc_rev=3),
2690 svntest.actions.run_and_verify_commit(wc_dir,
2691 expected_output,
2692 expected_status,
2693 None, wc_dir)
2695 # Delete foo on F, creating r4.
2696 svntest.actions.run_and_verify_svn(None, None, [], 'rm', foo_path)
2697 expected_output = svntest.wc.State(wc_dir, {
2698 'A/B/F/foo' : Item(verb='Deleting'),
2700 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
2701 expected_status.add({
2702 'A/C' : Item(status=' ', wc_rev=3),
2703 'A/C/foo' : Item(status=' ', wc_rev=3),
2704 'A/C/foo/new file' : Item(status=' ', wc_rev=3),
2705 'A/C/foo/new file 2' : Item(status=' ', wc_rev=3),
2707 svntest.actions.run_and_verify_commit(wc_dir,
2708 expected_output,
2709 expected_status,
2710 None, wc_dir)
2712 #----------------------------------------------------------------------
2713 # A merge that replaces a directory
2714 # Tests for Issue #2144 and Issue #2607
2716 def merge_dir_replace(sbox):
2717 "merge a replacement of a directory"
2719 set_up_dir_replace(sbox)
2720 wc_dir = sbox.wc_dir
2722 C_path = os.path.join(wc_dir, 'A', 'C')
2723 F_path = os.path.join(wc_dir, 'A', 'B', 'F')
2724 F_url = sbox.repo_url + '/A/B/F'
2725 foo_path = os.path.join(F_path, 'foo')
2726 new_file2 = os.path.join(foo_path, "new file 2")
2728 # Recreate foo in F and add a new folder and two files
2729 bar_path = os.path.join(foo_path, 'bar')
2730 foo_file = os.path.join(foo_path, "file foo")
2731 new_file3 = os.path.join(bar_path, "new file 3")
2733 # Make a couple of directories, and add some files within them.
2734 svntest.actions.run_and_verify_svn(None, None, [], 'mkdir', foo_path)
2735 svntest.actions.run_and_verify_svn(None, None, [], 'mkdir', bar_path)
2736 svntest.main.file_append(new_file3, "Initial text in new file 3.\n")
2737 svntest.main.run_svn(None, "add", new_file3)
2738 svntest.main.file_append(foo_file, "Initial text in file foo.\n")
2739 svntest.main.run_svn(None, "add", foo_file)
2741 # Commit the new content, creating r5.
2742 expected_output = wc.State(wc_dir, {
2743 'A/B/F/foo' : Item(verb='Adding'),
2744 'A/B/F/foo/file foo' : Item(verb='Adding'),
2745 'A/B/F/foo/bar' : Item(verb='Adding'),
2746 'A/B/F/foo/bar/new file 3' : Item(verb='Adding'),
2748 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
2749 expected_status.add({
2750 'A/B/F/foo' : Item(status=' ', wc_rev=5),
2751 'A/B/F/foo/file foo' : Item(status=' ', wc_rev=5),
2752 'A/B/F/foo/bar' : Item(status=' ', wc_rev=5),
2753 'A/B/F/foo/bar/new file 3' : Item(status=' ', wc_rev=5),
2754 'A/C' : Item(status=' ', wc_rev=3),
2755 'A/C/foo' : Item(status=' ', wc_rev=3),
2756 'A/C/foo/new file' : Item(status=' ', wc_rev=3),
2757 'A/C/foo/new file 2' : Item(status=' ', wc_rev=3),
2759 svntest.actions.run_and_verify_commit(wc_dir,
2760 expected_output,
2761 expected_status,
2762 None, wc_dir)
2763 # Merge replacement of foo onto C
2764 expected_output = wc.State(C_path, {
2765 'foo' : Item(status='R '),
2766 'foo/file foo' : Item(status='A '),
2767 'foo/bar' : Item(status='A '),
2768 'foo/bar/new file 3' : Item(status='A '),
2770 expected_disk = wc.State('', {
2771 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B/F:2-5'}),
2772 'foo' : Item(),
2773 'foo/file foo' : Item("Initial text in file foo.\n"),
2774 'foo/bar' : Item(),
2775 'foo/bar/new file 3' : Item("Initial text in new file 3.\n"),
2777 expected_status = wc.State(C_path, {
2778 '' : Item(status=' M', wc_rev=3),
2779 'foo' : Item(status='R ', wc_rev='-', copied='+'),
2780 'foo/new file 2' : Item(status='D ', wc_rev='-', copied='+'),
2781 'foo/file foo' : Item(status='A ', wc_rev='-', copied='+'),
2782 'foo/bar' : Item(status='A ', wc_rev='-', copied='+'),
2783 'foo/bar/new file 3' : Item(status='A ', wc_rev='-', copied='+'),
2784 'foo/new file' : Item(status='D ', wc_rev='-', copied='+'),
2786 expected_skip = wc.State(C_path, { })
2787 svntest.actions.run_and_verify_merge(C_path, '2', '5', F_url,
2788 expected_output,
2789 expected_disk,
2790 expected_status,
2791 expected_skip,
2792 None, None, None, None, None,
2794 0) # don't do a dry-run
2795 # the output differs
2797 # Commit merge of foo onto C
2798 expected_output = svntest.wc.State(wc_dir, {
2799 'A/C' : Item(verb='Sending'),
2800 'A/C/foo' : Item(verb='Replacing'),
2801 'A/C/foo/file foo' : Item(verb='Adding'),
2802 'A/C/foo/bar' : Item(verb='Adding'),
2803 'A/C/foo/bar/new file 3' : Item(verb='Adding'),
2804 'A/C/foo/new file' : Item(verb='Deleting'),
2805 'A/C/foo/new file 2' : Item(verb='Deleting'),
2807 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
2808 expected_status.add({
2809 'A/B/F/foo' : Item(status=' ', wc_rev=5),
2810 'A/B/F/foo/file foo' : Item(status=' ', wc_rev=5),
2811 'A/B/F/foo/bar' : Item(status=' ', wc_rev=5),
2812 'A/B/F/foo/bar/new file 3' : Item(status=' ', wc_rev=5),
2813 'A/C' : Item(status=' ', wc_rev=6),
2814 'A/C/foo' : Item(status=' ', wc_rev=6),
2815 'A/C/foo/file foo' : Item(status=' ', wc_rev=6),
2816 'A/C/foo/bar' : Item(status=' ', wc_rev=6),
2817 'A/C/foo/bar/new file 3' : Item(status=' ', wc_rev=6),
2819 svntest.actions.run_and_verify_commit(wc_dir,
2820 expected_output,
2821 expected_status,
2822 None, wc_dir)
2824 #----------------------------------------------------------------------
2825 # A merge that replaces a directory and one of its children
2826 # Tests for Issue #2690
2828 def merge_dir_and_file_replace(sbox):
2829 "replace both dir and one of its children"
2831 set_up_dir_replace(sbox)
2832 wc_dir = sbox.wc_dir
2834 C_path = os.path.join(wc_dir, 'A', 'C')
2835 F_path = os.path.join(wc_dir, 'A', 'B', 'F')
2836 F_url = sbox.repo_url + '/A/B/F'
2837 foo_path = os.path.join(F_path, 'foo')
2838 new_file2 = os.path.join(foo_path, "new file 2")
2840 # Recreate foo and 'new file 2' in F and add a new folder with a file
2841 bar_path = os.path.join(foo_path, 'bar')
2842 new_file3 = os.path.join(bar_path, "new file 3")
2843 svntest.actions.run_and_verify_svn(None, None, [], 'mkdir', foo_path)
2844 svntest.actions.run_and_verify_svn(None, None, [], 'mkdir', bar_path)
2845 svntest.main.file_append(new_file3, "Initial text in new file 3.\n")
2846 svntest.main.run_svn(None, "add", new_file3)
2847 svntest.main.file_append(new_file2, "New text in new file 2.\n")
2848 svntest.main.run_svn(None, "add", new_file2)
2850 expected_output = wc.State(wc_dir, {
2851 'A/B/F/foo' : Item(verb='Adding'),
2852 'A/B/F/foo/new file 2' : Item(verb='Adding'),
2853 'A/B/F/foo/bar' : Item(verb='Adding'),
2854 'A/B/F/foo/bar/new file 3' : Item(verb='Adding'),
2856 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
2857 expected_status.add({
2858 'A/B/F/foo' : Item(status=' ', wc_rev=5),
2859 'A/B/F/foo/new file 2' : Item(status=' ', wc_rev=5),
2860 'A/B/F/foo/bar' : Item(status=' ', wc_rev=5),
2861 'A/B/F/foo/bar/new file 3' : Item(status=' ', wc_rev=5),
2862 'A/C/foo' : Item(status=' ', wc_rev=3),
2863 'A/C/foo/new file' : Item(status=' ', wc_rev=3),
2864 'A/C/foo/new file 2' : Item(status=' ', wc_rev=3),
2866 svntest.actions.run_and_verify_commit(wc_dir,
2867 expected_output,
2868 expected_status,
2869 None, wc_dir)
2870 # Merge replacement of foo onto C
2871 expected_output = wc.State(C_path, {
2872 'foo' : Item(status='D '),
2873 'foo' : Item(status='A '),
2874 'foo/new file 2' : Item(status='D '),
2875 'foo/new file 2' : Item(status='A '),
2876 'foo/bar' : Item(status='A '),
2877 'foo/bar/new file 3' : Item(status='A '),
2878 'foo/new file' : Item(status='D '),
2880 expected_disk = wc.State('', {
2881 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B/F:2-5'}),
2882 'foo' : Item(),
2883 'foo/new file 2' : Item("New text in new file 2.\n"),
2884 'foo/bar' : Item(),
2885 'foo/bar/new file 3' : Item("Initial text in new file 3.\n"),
2887 expected_status = wc.State(C_path, {
2888 '' : Item(status=' ', wc_rev=1),
2889 'foo' : Item(status='R ', wc_rev='-', copied='+'),
2890 'foo/new file 2' : Item(status='R ', wc_rev='-', copied='+'),
2891 'foo/bar' : Item(status='A ', wc_rev='-', copied='+'),
2892 'foo/bar/new file 3' : Item(status='A ', wc_rev='-', copied='+'),
2893 'foo/new file' : Item(status='D ', wc_rev='-', copied='+'),
2895 expected_skip = wc.State(C_path, { })
2896 svntest.actions.run_and_verify_merge(C_path, '2', '5', F_url,
2897 expected_output,
2898 expected_disk,
2899 expected_status,
2900 expected_skip,
2901 None, None, None, None, None,
2903 0) # don't do a dry-run
2904 # the output differs
2906 # Commit merge of foo onto C
2907 expected_output = svntest.wc.State(wc_dir, {
2908 'A/C/foo' : Item(verb='Replacing'),
2909 'A/C/foo/new file 2' : Item(verb='Replacing'),
2910 'A/C/foo/new file' : Item(verb='Deleting'),
2911 'A/C/foo/bar' : Item(verb='Adding'),
2912 'A/C/foo/bar/new file 3' : Item(verb='Adding'),
2915 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
2916 expected_status.add({
2917 'A/B/F/foo' : Item(status=' ', wc_rev=5),
2918 'A/B/F/foo/new file 2' : Item(status=' ', wc_rev=5),
2919 'A/B/F/foo/bar' : Item(status=' ', wc_rev=5),
2920 'A/B/F/foo/bar/new file 3' : Item(status=' ', wc_rev=5),
2921 'A/C/foo' : Item(status=' ', wc_rev=6),
2922 'A/C/foo/new file 2' : Item(status=' ', wc_rev=6),
2923 'A/C/foo/bar' : Item(status=' ', wc_rev=6),
2924 'A/C/foo/bar/new file 3' : Item(status=' ', wc_rev=6),
2926 svntest.actions.run_and_verify_commit(wc_dir,
2927 expected_output,
2928 expected_status,
2929 None, wc_dir)
2931 #----------------------------------------------------------------------
2932 def merge_file_with_space_in_its_name(sbox):
2933 "merge a file whose name contains a space"
2934 # For issue #2144
2935 sbox.build()
2936 wc_dir = sbox.wc_dir
2937 new_file = os.path.join(wc_dir, "new file")
2939 # Make r2.
2940 svntest.main.file_append(new_file, "Initial text in the file.\n")
2941 svntest.main.run_svn(None, "add", new_file)
2942 svntest.actions.run_and_verify_svn(None, None, [],
2943 "ci", "-m", "r2", wc_dir)
2945 # Make r3.
2946 svntest.main.file_append(new_file, "Next line of text in the file.\n")
2947 svntest.actions.run_and_verify_svn(None, None, [],
2948 "ci", "-m", "r3", wc_dir)
2950 # Try to reverse merge.
2952 # The reproduction recipe requires that no explicit merge target be
2953 # passed, so we run merge from inside the wc dir where the target
2954 # file (i.e., the URL basename) lives.
2955 os.chdir(wc_dir)
2956 target_url = sbox.repo_url + '/new%20file'
2957 svntest.actions.run_and_verify_svn(None, None, [],
2958 "merge", "-r3:2", target_url)
2960 #----------------------------------------------------------------------
2961 # A merge between two branches using no revision number with the dir being
2962 # created already existing as an unversioned directory.
2963 # Tests for Issue #2222
2965 def merge_dir_branches(sbox):
2966 "merge between branches (Issue #2222)"
2968 sbox.build()
2969 wc_dir = sbox.wc_dir
2971 F_path = os.path.join(wc_dir, 'A', 'B', 'F')
2972 F_url = sbox.repo_url + '/A/B/F'
2973 C_url = sbox.repo_url + '/A/C'
2975 # Create foo in F
2976 foo_path = os.path.join(F_path, 'foo')
2977 svntest.actions.run_and_verify_svn(None, None, [], 'mkdir', foo_path)
2979 expected_output = wc.State(wc_dir, {
2980 'A/B/F/foo' : Item(verb='Adding'),
2982 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
2983 expected_status.add({
2984 'A/B/F/foo' : Item(status=' ', wc_rev=2),
2986 svntest.actions.run_and_verify_commit(wc_dir,
2987 expected_output,
2988 expected_status,
2989 None, wc_dir)
2991 # Create an unversioned foo
2992 foo_path = os.path.join(wc_dir, 'foo')
2993 os.mkdir(foo_path)
2995 # Merge from C to F onto the wc_dir
2996 # We can't use run_and_verify_merge because it doesn't support this
2997 # syntax of the merge command.
2998 ### TODO: We can use run_and_verify_merge2() here now.
2999 expected_output = expected_merge_output(None, "A " + foo_path + "\n")
3000 svntest.actions.run_and_verify_svn(None, expected_output, [],
3001 'merge', C_url, F_url, wc_dir)
3003 # Run info to check the copied rev to make sure it's right
3004 expected_output = ["Path: " + foo_path + "\n",
3005 "URL: " + sbox.repo_url + "/foo\n",
3006 "Repository Root: " + sbox.repo_url + "\n",
3007 "Revision: 2\n",
3008 "Node Kind: directory\n",
3009 "Schedule: add\n",
3010 "Copied From URL: " + F_url + "/foo\n",
3011 "Copied From Rev: 2\n", "\n"]
3012 svntest.actions.run_and_verify_svn(None, expected_output, [],
3013 'info', foo_path)
3016 #----------------------------------------------------------------------
3018 def safe_property_merge(sbox):
3019 "property merges don't overwrite existing prop-mods"
3021 sbox.build()
3022 wc_dir = sbox.wc_dir
3024 # Add a property to two files and a directory, commit as r2.
3025 alpha_path = os.path.join(wc_dir, 'A', 'B', 'E', 'alpha')
3026 beta_path = os.path.join(wc_dir, 'A', 'B', 'E', 'beta')
3027 E_path = os.path.join(wc_dir, 'A', 'B', 'E')
3029 svntest.actions.run_and_verify_svn(None, None, [],
3030 'propset', 'foo', 'foo_val',
3031 alpha_path, beta_path)
3032 svntest.actions.run_and_verify_svn(None, None, [],
3033 'propset', 'foo', 'foo_val',
3034 E_path)
3036 expected_output = svntest.wc.State(wc_dir, {
3037 'A/B/E' : Item(verb='Sending'),
3038 'A/B/E/alpha' : Item(verb='Sending'),
3039 'A/B/E/beta' : Item(verb='Sending'),
3041 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
3042 expected_status.tweak('A/B/E', 'A/B/E/alpha', 'A/B/E/beta',
3043 wc_rev=2, status=' ')
3044 svntest.actions.run_and_verify_commit(wc_dir,
3045 expected_output, expected_status,
3046 None, wc_dir)
3047 svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
3049 # Copy B to B2 as rev 3 (making a branch)
3050 B_url = sbox.repo_url + '/A/B'
3051 B2_url = sbox.repo_url + '/A/B2'
3053 svntest.actions.run_and_verify_svn(None, None, [],
3054 'copy', '-m', 'copy B to B2',
3055 B_url, B2_url)
3056 svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
3058 # Change the properties underneath B again, and commit as r4
3059 svntest.actions.run_and_verify_svn(None, None, [],
3060 'propset', 'foo', 'foo_val2',
3061 alpha_path)
3062 svntest.actions.run_and_verify_svn(None, None, [],
3063 'propdel', 'foo',
3064 beta_path)
3065 svntest.actions.run_and_verify_svn(None, None, [],
3066 'propset', 'foo', 'foo_val2',
3067 E_path)
3068 expected_output = svntest.wc.State(wc_dir, {
3069 'A/B/E' : Item(verb='Sending'),
3070 'A/B/E/alpha' : Item(verb='Sending'),
3071 'A/B/E/beta' : Item(verb='Sending'),
3073 svntest.actions.run_and_verify_commit(wc_dir,
3074 expected_output, None,
3075 None, wc_dir)
3076 svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
3078 # Make local propchanges to E, alpha and beta in the branch.
3079 alpha_path2 = os.path.join(wc_dir, 'A', 'B2', 'E', 'alpha')
3080 beta_path2 = os.path.join(wc_dir, 'A', 'B2', 'E', 'beta')
3081 E_path2 = os.path.join(wc_dir, 'A', 'B2', 'E')
3083 svntest.actions.run_and_verify_svn(None, None, [],
3084 'propset', 'foo', 'branchval',
3085 alpha_path2, beta_path2)
3086 svntest.actions.run_and_verify_svn(None, None, [],
3087 'propset', 'foo', 'branchval',
3088 E_path2)
3090 # Now merge the recent B change to the branch. Because we already
3091 # have local propmods, we should get property conflicts.
3092 B2_path = os.path.join(wc_dir, 'A', 'B2')
3094 expected_output = wc.State(B2_path, {
3095 'E' : Item(status=' C'),
3096 'E/alpha' : Item(status=' C'),
3097 'E/beta' : Item(status=' C'),
3100 expected_disk = wc.State('', {
3101 '' : Item(props={SVN_PROP_MERGEINFO : "/A/B:4"}),
3102 'E' : Item(),
3103 'E/alpha' : Item("This is the file 'alpha'.\n"),
3104 'E/beta' : Item("This is the file 'beta'.\n"),
3105 'F' : Item(),
3106 'lambda' : Item("This is the file 'lambda'.\n"),
3108 expected_disk.tweak('E', 'E/alpha', 'E/beta',
3109 props={'foo' : 'branchval'}) # local mods still present
3111 expected_status = wc.State(B2_path, {
3112 '' : Item(status=' M'),
3113 'E' : Item(status=' C'),
3114 'E/alpha' : Item(status=' C'),
3115 'E/beta' : Item(status=' C'),
3116 'F' : Item(status=' '),
3117 'lambda' : Item(status=' '),
3119 expected_status.tweak(wc_rev=4)
3121 expected_skip = wc.State('', { })
3123 # should have 3 'prej' files left behind, describing prop conflicts:
3124 extra_files = ['alpha.*\.prej', 'beta.*\.prej', 'dir_conflicts.*\.prej']
3126 svntest.actions.run_and_verify_merge(B2_path, '3', '4', B_url,
3127 expected_output,
3128 expected_disk,
3129 expected_status,
3130 expected_skip,
3131 None, # expected error string
3132 svntest.tree.detect_conflict_files,
3133 extra_files,
3134 None, None, # no B singleton handler
3135 1, # check props
3136 0) # dry_run
3138 #----------------------------------------------------------------------
3140 # Test for issue 2035, whereby 'svn merge' wouldn't always mark
3141 # property conflicts when it should.
3143 def property_merge_from_branch(sbox):
3144 "property merge conflict even without local mods"
3146 sbox.build()
3147 wc_dir = sbox.wc_dir
3149 # Add a property to a file and a directory, commit as r2.
3150 alpha_path = os.path.join(wc_dir, 'A', 'B', 'E', 'alpha')
3151 E_path = os.path.join(wc_dir, 'A', 'B', 'E')
3153 svntest.actions.run_and_verify_svn(None, None, [],
3154 'propset', 'foo', 'foo_val',
3155 alpha_path)
3156 svntest.actions.run_and_verify_svn(None, None, [],
3157 'propset', 'foo', 'foo_val',
3158 E_path)
3160 expected_output = svntest.wc.State(wc_dir, {
3161 'A/B/E' : Item(verb='Sending'),
3162 'A/B/E/alpha' : Item(verb='Sending'),
3164 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
3165 expected_status.tweak('A/B/E', 'A/B/E/alpha', wc_rev=2, status=' ')
3166 svntest.actions.run_and_verify_commit(wc_dir,
3167 expected_output, expected_status,
3168 None, wc_dir)
3169 svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
3171 # Copy B to B2 as rev 3 (making a branch)
3172 B_url = sbox.repo_url + '/A/B'
3173 B2_url = sbox.repo_url + '/A/B2'
3175 svntest.actions.run_and_verify_svn(None, None, [],
3176 'copy', '-m', 'copy B to B2',
3177 B_url, B2_url)
3178 svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
3180 # Change the properties underneath B again, and commit as r4
3181 svntest.actions.run_and_verify_svn(None, None, [],
3182 'propset', 'foo', 'foo_val2',
3183 alpha_path)
3184 svntest.actions.run_and_verify_svn(None, None, [],
3185 'propset', 'foo', 'foo_val2',
3186 E_path)
3187 expected_output = svntest.wc.State(wc_dir, {
3188 'A/B/E' : Item(verb='Sending'),
3189 'A/B/E/alpha' : Item(verb='Sending'),
3191 svntest.actions.run_and_verify_commit(wc_dir,
3192 expected_output, None,
3193 None, wc_dir)
3194 svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
3196 # Make different propchanges changes to the B2 branch and commit as r5.
3197 alpha_path2 = os.path.join(wc_dir, 'A', 'B2', 'E', 'alpha')
3198 E_path2 = os.path.join(wc_dir, 'A', 'B2', 'E')
3200 svntest.actions.run_and_verify_svn(None, None, [],
3201 'propset', 'foo', 'branchval',
3202 alpha_path2)
3203 svntest.actions.run_and_verify_svn(None, None, [],
3204 'propset', 'foo', 'branchval',
3205 E_path2)
3206 expected_output = svntest.wc.State(wc_dir, {
3207 'A/B2/E' : Item(verb='Sending'),
3208 'A/B2/E/alpha' : Item(verb='Sending'),
3210 svntest.actions.run_and_verify_commit(wc_dir,
3211 expected_output, None,
3212 None, wc_dir)
3213 svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
3215 # Now merge the recent B change to the branch. There are no local
3216 # mods anywhere, but we should still get property conflicts anyway!
3217 B2_path = os.path.join(wc_dir, 'A', 'B2')
3219 expected_output = wc.State(B2_path, {
3220 'E' : Item(status=' C'),
3221 'E/alpha' : Item(status=' C'),
3224 expected_disk = wc.State('', {
3225 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B:4'}),
3226 'E' : Item(),
3227 'E/alpha' : Item("This is the file 'alpha'.\n"),
3228 'E/beta' : Item("This is the file 'beta'.\n"),
3229 'F' : Item(),
3230 'lambda' : Item("This is the file 'lambda'.\n"),
3232 expected_disk.tweak('E', 'E/alpha',
3233 props={'foo' : 'branchval'})
3235 expected_status = wc.State(B2_path, {
3236 '' : Item(status=' M'),
3237 'E' : Item(status=' C'),
3238 'E/alpha' : Item(status=' C'),
3239 'E/beta' : Item(status=' '),
3240 'F' : Item(status=' '),
3241 'lambda' : Item(status=' '),
3243 expected_status.tweak(wc_rev=5)
3245 expected_skip = wc.State('', { })
3247 # should have 2 'prej' files left behind, describing prop conflicts:
3248 extra_files = ['alpha.*\.prej', 'dir_conflicts.*\.prej']
3250 svntest.actions.run_and_verify_merge(B2_path, '3', '4', B_url,
3251 expected_output,
3252 expected_disk,
3253 expected_status,
3254 expected_skip,
3255 None, # expected error string
3256 svntest.tree.detect_conflict_files,
3257 extra_files,
3258 None, None, # no B singleton handler
3259 1, # check props
3260 0) # dry_run
3262 #----------------------------------------------------------------------
3264 # Another test for issue 2035, whereby sometimes 'svn merge' marked
3265 # property conflicts when it shouldn't!
3267 def property_merge_undo_redo(sbox):
3268 "undo, then redo a property merge"
3270 sbox.build()
3271 wc_dir = sbox.wc_dir
3273 # Add a property to a file, commit as r2.
3274 alpha_path = os.path.join(wc_dir, 'A', 'B', 'E', 'alpha')
3275 svntest.actions.run_and_verify_svn(None, None, [],
3276 'propset', 'foo', 'foo_val',
3277 alpha_path)
3279 expected_output = svntest.wc.State(wc_dir, {
3280 'A/B/E/alpha' : Item(verb='Sending'),
3282 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
3283 expected_status.tweak('A/B/E/alpha', wc_rev=2, status=' ')
3284 svntest.actions.run_and_verify_commit(wc_dir,
3285 expected_output, expected_status,
3286 None, wc_dir)
3287 svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
3289 # Use 'svn merge' to undo the commit. ('svn merge -r2:1')
3290 # Result should be a single local-prop-mod.
3291 expected_output = wc.State(wc_dir, {'A/B/E/alpha' : Item(status=' U'), })
3293 expected_disk = svntest.main.greek_state.copy()
3295 expected_status = svntest.actions.get_virginal_state(wc_dir, 2)
3296 expected_status.tweak('A/B/E/alpha', status=' M')
3298 expected_skip = wc.State('', { })
3300 svntest.actions.run_and_verify_merge(wc_dir, '2', '1',
3301 sbox.repo_url,
3302 expected_output,
3303 expected_disk,
3304 expected_status,
3305 expected_skip,
3306 None, # expected error string
3307 None, None, # no A singleton handler
3308 None, None, # no B singleton handler
3309 1, # check props
3310 0) # dry_run
3312 # Change mind, re-apply the change ('svn merge -r1:2').
3313 # This should merge cleanly into existing prop-mod, status shows nothing.
3314 expected_output = wc.State(wc_dir, {'A/B/E/alpha' : Item(status=' C'), })
3316 expected_disk = svntest.main.greek_state.copy()
3317 expected_disk.add({'A/B/E/alpha.prej'
3318 : Item("Trying to create property 'foo' with value 'foo_val',\n"
3319 + "but it has been locally deleted.\n")})
3321 expected_status = svntest.actions.get_virginal_state(wc_dir, 2)
3322 expected_status.tweak('A/B/E/alpha', status=' C')
3324 expected_skip = wc.State('', { })
3326 # Re-merge r1. We have to use --ignore-ancestry here. Otherwise
3327 # the merge logic will claim we already have this change (because it
3328 # was unable to record the previous undoing merge).
3329 svntest.actions.run_and_verify_merge(wc_dir, '1', '2',
3330 sbox.repo_url,
3331 expected_output,
3332 expected_disk,
3333 expected_status,
3334 expected_skip,
3335 None, # expected error string
3336 None, None, # no A singleton handler
3337 None, None, # no B singleton handler
3338 1, # check props
3339 0, # dry_run
3340 '--ignore-ancestry')
3344 #----------------------------------------------------------------------
3345 def cherry_pick_text_conflict(sbox):
3346 "cherry-pick a dependent change, get conflict"
3348 sbox.build()
3349 wc_dir = sbox.wc_dir
3351 A_path = os.path.join(wc_dir, 'A')
3352 A_url = sbox.repo_url + '/A'
3353 mu_path = os.path.join(A_path, 'mu')
3354 branch_A_url = sbox.repo_url + '/copy-of-A'
3355 branch_mu_path = os.path.join(wc_dir, 'copy-of-A', 'mu')
3357 # Create a branch of A.
3358 svntest.actions.run_and_verify_svn(None, None, [], 'cp',
3359 A_url, branch_A_url,
3360 '-m', "Creating copy-of-A")
3362 # Update to get the branch.
3363 svntest.actions.run_and_verify_svn(None, None, [],
3364 'update', wc_dir)
3366 # Change mu's text on the branch, producing r3 through r6.
3367 for rev in range(3, 7):
3368 svntest.main.file_append(branch_mu_path, ("r%d\n" % rev) * 3)
3369 svntest.actions.run_and_verify_svn(None, None, [],
3370 'ci', '-m',
3371 'Add lines to mu in r%d.' % rev, wc_dir)
3373 # Mark r5 as merged into trunk, to create disparate revision ranges
3374 # which need to be merged.
3375 svntest.actions.run_and_verify_svn(None, [], [],
3376 'merge', '-c5', '--record-only',
3377 branch_A_url, A_path)
3380 # Try to merge r4:6 into trunk, without r3. It should fail.
3381 expected_output = wc.State(A_path, {
3382 'mu' : Item(status='C '),
3384 expected_disk = wc.State('', {
3385 'mu' : Item("This is the file 'mu'.\n"
3386 + make_conflict_marker_text("r3\n" * 3, "r4\n" * 3, 4)),
3387 'B' : Item(),
3388 'B/lambda' : Item("This is the file 'lambda'.\n"),
3389 'B/E' : Item(),
3390 'B/E/alpha' : Item("This is the file 'alpha'.\n"),
3391 'B/E/beta' : Item("This is the file 'beta'.\n"),
3392 'B/F' : Item(),
3393 'C' : Item(),
3394 'D' : Item(),
3395 'D/gamma' : Item("This is the file 'gamma'.\n"),
3396 'D/H' : Item(),
3397 'D/H/chi' : Item("This is the file 'chi'.\n"),
3398 'D/H/psi' : Item("This is the file 'psi'.\n"),
3399 'D/H/omega' : Item("This is the file 'omega'.\n"),
3400 'D/G' : Item(),
3401 'D/G/pi' : Item("This is the file 'pi'.\n"),
3402 'D/G/rho' : Item("This is the file 'rho'.\n"),
3403 'D/G/tau' : Item("This is the file 'tau'.\n"),
3405 expected_status = wc.State(A_path, {
3406 '' : Item(status=' M'),
3407 'mu' : Item(status='C '),
3408 'B' : Item(status=' '),
3409 'B/lambda' : Item(status=' '),
3410 'B/E' : Item(status=' '),
3411 'B/E/alpha' : Item(status=' '),
3412 'B/E/beta' : Item(status=' '),
3413 'B/F' : Item(status=' '),
3414 'C' : Item(status=' '),
3415 'D' : Item(status=' '),
3416 'D/gamma' : Item(status=' '),
3417 'D/H' : Item(status=' '),
3418 'D/H/chi' : Item(status=' '),
3419 'D/H/psi' : Item(status=' '),
3420 'D/H/omega' : Item(status=' '),
3421 'D/G' : Item(status=' '),
3422 'D/G/pi' : Item(status=' '),
3423 'D/G/rho' : Item(status=' '),
3424 'D/G/tau' : Item(status=' '),
3426 expected_status.tweak(wc_rev=2)
3427 expected_skip = wc.State('', { })
3428 expected_error = "conflicts were produced while merging r3:4"
3429 svntest.actions.run_and_verify_merge(A_path, '3', '6', branch_A_url,
3430 expected_output,
3431 expected_disk,
3432 expected_status,
3433 expected_skip,
3434 expected_error,
3435 svntest.tree.detect_conflict_files,
3436 ["mu\.working",
3437 "mu\.merge-right\.r4",
3438 "mu\.merge-left\.r3"],
3439 None, None, # no singleton handler
3440 0, # don't check props
3441 0) # not a dry_run
3445 # Test for issue 2135
3446 def merge_file_replace(sbox):
3447 "merge a replacement of a file"
3449 sbox.build()
3450 wc_dir = sbox.wc_dir
3452 # File scheduled for deletion
3453 rho_path = os.path.join(wc_dir, 'A', 'D', 'G', 'rho')
3454 svntest.actions.run_and_verify_svn(None, None, [], 'rm', rho_path)
3456 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
3457 expected_status.tweak('A/D/G/rho', status='D ')
3458 svntest.actions.run_and_verify_status(wc_dir, expected_status)
3460 expected_output = svntest.wc.State(wc_dir, {
3461 'A/D/G/rho': Item(verb='Deleting'),
3464 expected_status.remove('A/D/G/rho')
3466 # Commit rev 2
3467 svntest.actions.run_and_verify_commit(wc_dir,
3468 expected_output,
3469 expected_status,
3470 None, wc_dir)
3471 # Create and add a new file.
3472 svntest.main.file_write(rho_path, "new rho\n")
3473 svntest.actions.run_and_verify_svn(None, None, [], 'add', rho_path)
3475 # Commit revsion 3
3476 expected_status.add({
3477 'A/D/G/rho' : Item(status='A ', wc_rev='0')
3479 svntest.actions.run_and_verify_status(wc_dir, expected_status)
3480 expected_output = svntest.wc.State(wc_dir, {
3481 'A/D/G/rho': Item(verb='Adding'),
3484 svntest.actions.run_and_verify_commit(wc_dir,
3485 expected_output,
3486 None,
3487 None, wc_dir)
3489 # Update working copy
3490 expected_output = svntest.wc.State(wc_dir, {})
3491 expected_disk = svntest.main.greek_state.copy()
3492 expected_disk.tweak('A/D/G/rho', contents='new rho\n' )
3493 expected_status.tweak(wc_rev='3')
3494 expected_status.tweak('A/D/G/rho', status=' ')
3496 svntest.actions.run_and_verify_update(wc_dir,
3497 expected_output,
3498 expected_disk,
3499 expected_status)
3501 # merge changes from r3:1
3502 expected_output = svntest.wc.State(wc_dir, {
3503 'A/D/G/rho': Item(status='R ')
3505 expected_status.tweak('A/D/G/rho', status='R ', copied='+', wc_rev='-')
3506 expected_skip = wc.State(wc_dir, { })
3507 expected_disk.tweak('A/D/G/rho', contents="This is the file 'rho'.\n")
3508 svntest.actions.run_and_verify_merge(wc_dir, '3', '1',
3509 sbox.repo_url,
3510 expected_output,
3511 expected_disk,
3512 expected_status,
3513 expected_skip)
3515 # Now commit merged wc
3516 expected_output = svntest.wc.State(wc_dir, {
3517 'A/D/G/rho': Item(verb='Replacing'),
3519 expected_status.tweak('A/D/G/rho', status=' ', copied=None, wc_rev='4')
3520 svntest.actions.run_and_verify_commit(wc_dir,
3521 expected_output,
3522 expected_status,
3523 None,
3524 wc_dir)
3525 # Test for issue 2522
3526 # Same as merge_file_replace, but without update before merge.
3527 def merge_file_replace_to_mixed_rev_wc(sbox):
3528 "merge a replacement of a file to mixed rev wc"
3530 sbox.build()
3531 wc_dir = sbox.wc_dir
3533 # File scheduled for deletion
3534 rho_path = os.path.join(wc_dir, 'A', 'D', 'G', 'rho')
3535 svntest.actions.run_and_verify_svn(None, None, [], 'rm', rho_path)
3537 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
3538 expected_status.tweak('A/D/G/rho', status='D ')
3539 svntest.actions.run_and_verify_status(wc_dir, expected_status)
3541 expected_output = svntest.wc.State(wc_dir, {
3542 'A/D/G/rho': Item(verb='Deleting'),
3545 expected_status.remove('A/D/G/rho')
3547 # Commit rev 2
3548 svntest.actions.run_and_verify_commit(wc_dir,
3549 expected_output,
3550 expected_status,
3551 None, wc_dir)
3553 # Update working copy
3554 expected_disk = svntest.main.greek_state.copy()
3555 expected_disk.remove('A/D/G/rho' )
3556 expected_output = svntest.wc.State(wc_dir, {})
3557 expected_status.tweak(wc_rev='2')
3559 svntest.actions.run_and_verify_update(wc_dir,
3560 expected_output,
3561 expected_disk,
3562 expected_status)
3564 # Create and add a new file.
3565 svntest.main.file_write(rho_path, "new rho\n")
3566 svntest.actions.run_and_verify_svn(None, None, [], 'add', rho_path)
3568 # Commit revsion 3
3569 expected_status.add({
3570 'A/D/G/rho' : Item(status='A ', wc_rev='0')
3572 svntest.actions.run_and_verify_status(wc_dir, expected_status)
3573 expected_output = svntest.wc.State(wc_dir, {
3574 'A/D/G/rho': Item(verb='Adding'),
3577 expected_disk.add({'A/D/G/rho' : Item(contents='new rho\n')} )
3578 expected_status.tweak(wc_rev='2')
3579 expected_status.tweak('A/D/G/rho', status=' ', wc_rev='3')
3581 svntest.actions.run_and_verify_commit(wc_dir,
3582 expected_output,
3583 expected_status,
3584 None, wc_dir)
3587 # merge changes from r3:1
3588 expected_output = svntest.wc.State(wc_dir, {
3589 'A/D/G/rho': Item(status='R ')
3591 expected_status.tweak('A/D/G/rho', status='R ', copied='+', wc_rev='-')
3592 expected_skip = wc.State(wc_dir, { })
3593 expected_disk.tweak('A/D/G/rho', contents="This is the file 'rho'.\n")
3594 svntest.actions.run_and_verify_merge(wc_dir, '3', '1',
3595 sbox.repo_url,
3596 expected_output,
3597 expected_disk,
3598 expected_status,
3599 expected_skip)
3601 # At this point WC is broken, because file rho has invalid revision
3602 # Try to update
3603 expected_output = svntest.wc.State(wc_dir, {})
3604 expected_status.tweak(wc_rev='3')
3605 expected_status.tweak('A/D/G/rho', status='R ', copied='+', wc_rev='-')
3606 svntest.actions.run_and_verify_update(wc_dir,
3607 expected_output,
3608 expected_disk,
3609 expected_status)
3611 # Now commit merged wc
3612 expected_output = svntest.wc.State(wc_dir, {
3613 'A/D/G/rho': Item(verb='Replacing'),
3615 expected_status.tweak('A/D/G/rho', status=' ', copied=None, wc_rev='4')
3616 svntest.actions.run_and_verify_commit(wc_dir,
3617 expected_output,
3618 expected_status,
3619 None,
3620 wc_dir)
3622 # use -x -w option for ignoring whitespace during merge
3623 def merge_ignore_whitespace(sbox):
3624 "ignore whitespace when merging"
3626 sbox.build()
3627 wc_dir = sbox.wc_dir
3629 # commit base version of iota
3630 file_name = "iota"
3631 file_path = os.path.join(wc_dir, file_name)
3632 file_url = sbox.repo_url + '/iota'
3634 svntest.main.file_write(file_path,
3635 "Aa\n"
3636 "Bb\n"
3637 "Cc\n")
3638 expected_output = svntest.wc.State(wc_dir, {
3639 'iota' : Item(verb='Sending'),
3641 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
3642 None, None, wc_dir)
3644 # change the file, mostly whitespace changes + an extra line
3645 svntest.main.file_write(file_path, "A a\nBb \n Cc\nNew line in iota\n")
3646 expected_output = wc.State(wc_dir, { file_name : Item(verb='Sending'), })
3647 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
3648 expected_status.tweak(file_name, wc_rev=3)
3649 svntest.actions.run_and_verify_commit(wc_dir,
3650 expected_output,
3651 expected_status,
3652 None,
3653 wc_dir)
3655 # Backdate iota to revision 2, so we can merge in the rev 3 changes.
3656 svntest.actions.run_and_verify_svn(None, None, [],
3657 'up', '-r', '2', file_path)
3658 # Make some local whitespace changes, these should not conflict
3659 # with the remote whitespace changes as both will be ignored.
3660 svntest.main.file_write(file_path, " Aa\nB b\nC c\n")
3662 # Lines changed only by whitespaces - both in local or remote -
3663 # should be ignored
3664 expected_output = wc.State(sbox.wc_dir, { file_name : Item(status='G ') })
3665 expected_disk = svntest.main.greek_state.copy()
3666 expected_disk.tweak(file_name,
3667 contents=" Aa\n"
3668 "B b\n"
3669 "C c\n"
3670 "New line in iota\n")
3671 expected_status = svntest.actions.get_virginal_state(sbox.wc_dir, 1)
3672 expected_status.tweak('', status=' M', wc_rev=1)
3673 expected_status.tweak(file_name, status='M ', wc_rev=2)
3674 expected_skip = wc.State('', { })
3676 svntest.actions.run_and_verify_merge(sbox.wc_dir, '2', '3',
3677 sbox.repo_url,
3678 expected_output,
3679 expected_disk,
3680 expected_status,
3681 expected_skip,
3682 None, None, None, None, None,
3683 0, 0,
3684 '-x', '-w')
3686 # use -x --ignore-eol-style option for ignoring eolstyle during merge
3687 def merge_ignore_eolstyle(sbox):
3688 "ignore eolstyle when merging"
3690 sbox.build()
3691 wc_dir = sbox.wc_dir
3693 # commit base version of iota
3694 file_name = "iota"
3695 file_path = os.path.join(wc_dir, file_name)
3696 file_url = sbox.repo_url + '/iota'
3698 svntest.main.file_write(file_path,
3699 "Aa\r\n"
3700 "Bb\r\n"
3701 "Cc\r\n",
3702 "wb")
3703 expected_output = svntest.wc.State(wc_dir, {
3704 'iota' : Item(verb='Sending'),
3706 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
3707 None, None, wc_dir)
3709 # change the file, mostly eol changes + an extra line
3710 svntest.main.file_write(file_path,
3711 "Aa\r"
3712 "Bb\n"
3713 "Cc\r"
3714 "New line in iota\n",
3715 "wb")
3716 expected_output = wc.State(wc_dir, { file_name : Item(verb='Sending'), })
3717 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
3718 expected_status.tweak(file_name, wc_rev=3)
3719 svntest.actions.run_and_verify_commit(wc_dir,
3720 expected_output,
3721 expected_status,
3722 None,
3723 wc_dir)
3725 # Backdate iota to revision 2, so we can merge in the rev 3 changes.
3726 svntest.actions.run_and_verify_svn(None, None, [],
3727 'up', '-r', '2', file_path)
3728 # Make some local eol changes, these should not conflict
3729 # with the remote eol changes as both will be ignored.
3730 svntest.main.file_write(file_path,
3731 "Aa\n"
3732 "Bb\r"
3733 "Cc\n",
3734 "wb")
3736 # Lines changed only by eolstyle - both in local or remote -
3737 # should be ignored
3738 expected_output = wc.State(sbox.wc_dir, { file_name : Item(status='G ') })
3739 expected_disk = svntest.main.greek_state.copy()
3740 expected_disk.tweak(file_name,
3741 contents="Aa\n"
3742 "Bb\r"
3743 "Cc\n"
3744 "New line in iota\n")
3745 expected_status = svntest.actions.get_virginal_state(sbox.wc_dir, 1)
3746 expected_status.tweak('', status=' M')
3747 expected_status.tweak(file_name, status='M ', wc_rev=2)
3748 expected_skip = wc.State('', { })
3750 svntest.actions.run_and_verify_merge(sbox.wc_dir, '2', '3',
3751 sbox.repo_url,
3752 expected_output,
3753 expected_disk,
3754 expected_status,
3755 expected_skip,
3756 None, None, None, None, None,
3757 0, 0,
3758 '-x', '--ignore-eol-style')
3760 #----------------------------------------------------------------------
3761 # Issue 2584
3762 def merge_add_over_versioned_file_conflicts(sbox):
3763 "conflict from merge of add over versioned file"
3765 sbox.build()
3766 wc_dir = sbox.wc_dir
3768 E_path = os.path.join(wc_dir, 'A', 'B', 'E')
3769 alpha_path = os.path.join(E_path, 'alpha')
3770 new_alpha_path = os.path.join(wc_dir, 'A', 'C', 'alpha')
3772 # Create a new "alpha" file, with enough differences to cause a conflict.
3773 ### TODO: Leverage inject_conflict_into_wc() here.
3774 svntest.main.file_write(new_alpha_path, 'new alpha content\n')
3776 # Add and commit the new "alpha" file, creating revision 2.
3777 svntest.main.run_svn(None, "add", new_alpha_path)
3779 expected_output = svntest.wc.State(wc_dir, {
3780 'A/C/alpha' : Item(verb='Adding'),
3782 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
3783 expected_status.add({
3784 'A/C/alpha' : Item(status=' ', wc_rev=2),
3786 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
3787 expected_status, None,
3788 wc_dir)
3790 # Search for the comment entitled "The Merge Kluge" elsewhere in
3791 # this file, to understand why we shorten and chdir() below.
3792 short_E_path = shorten_path_kludge(E_path)
3794 # Merge changes from r1:2 into our pre-existing "alpha" file,
3795 # causing a conflict.
3796 expected_output = wc.State(short_E_path, {
3797 'alpha' : Item(status='C '),
3799 expected_disk = wc.State('', {
3800 'alpha' : Item(""), # state filled in below
3801 'beta' : Item("This is the file 'beta'.\n"),
3803 expected_status = wc.State(short_E_path, {
3804 '' : Item(status=' M', wc_rev=1),
3805 'alpha' : Item(status='C ', wc_rev=1),
3806 'beta' : Item(status=' ', wc_rev=1),
3809 inject_conflict_into_expected_state('alpha', expected_disk, expected_status,
3810 "This is the file 'alpha'.\n",
3811 "new alpha content\n", 2)
3812 expected_skip = wc.State(short_E_path, { })
3814 os.chdir(svntest.main.work_dir)
3815 svntest.actions.run_and_verify_merge(short_E_path, '1', '2',
3816 sbox.repo_url + \
3817 '/A/C',
3818 expected_output,
3819 expected_disk,
3820 expected_status,
3821 expected_skip,
3822 None,
3823 svntest.tree.detect_conflict_files,
3824 ["alpha\.working",
3825 "alpha\.merge-right\.r2",
3826 "alpha\.merge-left\.r0"])
3828 #----------------------------------------------------------------------
3829 # eol-style handling during merge with conflicts, scenario 1:
3830 # when a merge creates a conflict on a file, make sure the file and files
3831 # r<left>, r<right> and .mine are in the eol-style defined for that file.
3833 # This test for 'svn update' can be found in update_tests.py as
3834 # conflict_markers_matching_eol.
3835 def merge_conflict_markers_matching_eol(sbox):
3836 "conflict markers should match the file's eol style"
3838 sbox.build()
3839 wc_dir = sbox.wc_dir
3840 filecount = 1
3842 mu_path = os.path.join(wc_dir, 'A', 'mu')
3844 if os.name == 'nt':
3845 crlf = '\n'
3846 else:
3847 crlf = '\r\n'
3849 # Checkout a second working copy
3850 wc_backup = sbox.add_wc_path('backup')
3851 svntest.actions.run_and_verify_svn(None, None, [], 'checkout',
3852 sbox.repo_url, wc_backup)
3854 # set starting revision
3855 cur_rev = 1
3857 expected_disk = svntest.main.greek_state.copy()
3858 expected_status = svntest.actions.get_virginal_state(wc_dir, cur_rev)
3859 expected_backup_status = svntest.actions.get_virginal_state(wc_backup,
3860 cur_rev)
3862 path_backup = os.path.join(wc_backup, 'A', 'mu')
3864 # do the test for each eol-style
3865 for eol, eolchar in zip(['CRLF', 'CR', 'native', 'LF'],
3866 [crlf, '\015', '\n', '\012']):
3867 # rewrite file mu and set the eol-style property.
3868 svntest.main.file_write(mu_path, "This is the file 'mu'."+ eolchar, 'wb')
3869 svntest.main.run_svn(None, 'propset', 'svn:eol-style', eol, mu_path)
3871 expected_disk.add({
3872 'A/mu' : Item("This is the file 'mu'." + eolchar)
3874 expected_output = svntest.wc.State(wc_dir, {
3875 'A/mu' : Item(verb='Sending'),
3877 expected_status.tweak(wc_rev = cur_rev)
3878 expected_status.add({
3879 'A/mu' : Item(status=' ', wc_rev = cur_rev + 1),
3882 # Commit the original change and note the 'base' revision number
3883 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
3884 expected_status, None,
3885 wc_dir)
3886 cur_rev = cur_rev + 1
3887 base_rev = cur_rev
3889 svntest.main.run_svn(None, 'update', wc_backup)
3891 # Make a local mod to mu
3892 svntest.main.file_append(mu_path,
3893 'Original appended text for mu' + eolchar)
3895 # Commit the original change and note the 'theirs' revision number
3896 svntest.main.run_svn(None, 'commit', '-m', 'test log', wc_dir)
3897 cur_rev = cur_rev + 1
3898 theirs_rev = cur_rev
3900 # Make a local mod to mu, will conflict with the previous change
3901 svntest.main.file_append(path_backup,
3902 'Conflicting appended text for mu' + eolchar)
3904 # Create expected output tree for an update of the wc_backup.
3905 expected_backup_output = svntest.wc.State(wc_backup, {
3906 'A/mu' : Item(status='C '),
3909 # Create expected disk tree for the update.
3910 expected_backup_disk = expected_disk.copy()
3912 # verify content of resulting conflicted file
3913 expected_backup_disk.add({
3914 'A/mu' : Item(contents= "This is the file 'mu'." + eolchar +
3915 "<<<<<<< .working" + eolchar +
3916 "Conflicting appended text for mu" + eolchar +
3917 "=======" + eolchar +
3918 "Original appended text for mu" + eolchar +
3919 ">>>>>>> .merge-right.r" + str(cur_rev) + eolchar),
3921 # verify content of base(left) file
3922 expected_backup_disk.add({
3923 'A/mu.merge-left.r' + str(base_rev) :
3924 Item(contents= "This is the file 'mu'." + eolchar)
3926 # verify content of theirs(right) file
3927 expected_backup_disk.add({
3928 'A/mu.merge-right.r' + str(theirs_rev) :
3929 Item(contents= "This is the file 'mu'." + eolchar +
3930 "Original appended text for mu" + eolchar)
3932 # verify content of mine file
3933 expected_backup_disk.add({
3934 'A/mu.working' : Item(contents= "This is the file 'mu'." +
3935 eolchar +
3936 "Conflicting appended text for mu" + eolchar)
3939 # Create expected status tree for the update.
3940 expected_backup_status.add({
3941 'A/mu' : Item(status=' ', wc_rev=cur_rev),
3943 expected_backup_status.tweak('A/mu', status='C ')
3944 expected_backup_status.tweak(wc_rev = cur_rev - 1)
3945 expected_backup_status.tweak('', status= ' M')
3947 expected_backup_skip = wc.State('', { })
3949 svntest.actions.run_and_verify_merge(wc_backup, cur_rev - 1, cur_rev,
3950 sbox.repo_url,
3951 expected_backup_output,
3952 expected_backup_disk,
3953 expected_backup_status,
3954 expected_backup_skip)
3956 # cleanup for next run
3957 svntest.main.run_svn(None, 'revert', '-R', wc_backup)
3958 svntest.main.run_svn(None, 'update', wc_dir)
3960 # eol-style handling during merge, scenario 2:
3961 # if part of that merge is a propchange (add, change, delete) of
3962 # svn:eol-style, make sure the correct eol-style is applied before
3963 # calculating the merge (and conflicts if any)
3965 # This test for 'svn update' can be found in update_tests.py as
3966 # update_eolstyle_handling.
3967 def merge_eolstyle_handling(sbox):
3968 "handle eol-style propchange during merge"
3970 sbox.build()
3971 wc_dir = sbox.wc_dir
3973 mu_path = os.path.join(wc_dir, 'A', 'mu')
3975 if os.name == 'nt':
3976 crlf = '\n'
3977 else:
3978 crlf = '\r\n'
3980 # Checkout a second working copy
3981 wc_backup = sbox.add_wc_path('backup')
3982 svntest.actions.run_and_verify_svn(None, None, [], 'checkout',
3983 sbox.repo_url, wc_backup)
3984 path_backup = os.path.join(wc_backup, 'A', 'mu')
3986 # Test 1: add the eol-style property and commit, change mu in the second
3987 # working copy and merge the last revision; there should be no conflict!
3988 svntest.main.run_svn(None, 'propset', 'svn:eol-style', "CRLF", mu_path)
3989 svntest.main.run_svn(None,
3990 'commit', '-m', 'set eol-style property', wc_dir)
3992 svntest.main.file_append_binary(path_backup, 'Added new line of text.\012')
3994 expected_backup_disk = svntest.main.greek_state.copy()
3995 expected_backup_disk.tweak(
3996 'A/mu', contents= "This is the file 'mu'." + crlf +
3997 "Added new line of text." + crlf)
3998 expected_backup_output = svntest.wc.State(wc_backup, {
3999 'A/mu' : Item(status='GU'),
4001 expected_backup_status = svntest.actions.get_virginal_state(wc_backup, 1)
4002 expected_backup_status.tweak('', status=' M')
4003 expected_backup_status.tweak('A/mu', status='MM')
4005 expected_backup_skip = wc.State('', { })
4007 svntest.actions.run_and_verify_merge(wc_backup, '1', '2', sbox.repo_url,
4008 expected_backup_output,
4009 expected_backup_disk,
4010 expected_backup_status,
4011 expected_backup_skip)
4013 # Test 2: now change the eol-style property to another value and commit,
4014 # merge this revision in the still changed mu in the second working copy;
4015 # there should be a property conflict! (Since this will overwrite a
4016 # local change to a versioned resource.)
4017 svntest.main.run_svn(None, 'propset', 'svn:eol-style', "CR", mu_path)
4018 svntest.main.run_svn(None,
4019 'commit', '-m', 'set eol-style property', wc_dir)
4021 expected_backup_disk = svntest.main.greek_state.copy()
4022 expected_backup_disk.add({
4023 'A/mu' : Item(contents= "This is the file 'mu'." + crlf +
4024 "Added new line of text." + crlf)
4026 expected_backup_disk.add({
4027 'A/mu.prej' : Item("Trying to change property 'svn:eol-style' from 'CRLF'"
4028 + " to 'CR',\nbut property has been locally added with"
4029 + " value 'CRLF'\n")})
4030 expected_backup_output = svntest.wc.State(wc_backup, {
4031 'A/mu' : Item(status='GC'),
4033 expected_backup_status = svntest.actions.get_virginal_state(wc_backup, 1)
4034 expected_backup_status.tweak('', status=' M')
4035 expected_backup_status.tweak('A/mu', status='MC')
4036 svntest.actions.run_and_verify_merge(wc_backup, '2', '3', sbox.repo_url,
4037 expected_backup_output,
4038 expected_backup_disk,
4039 expected_backup_status,
4040 expected_backup_skip)
4042 # Test 3: now delete the eol-style property and commit, merge this revision
4043 # in the still changed mu in the second working copy; there should be no
4044 # conflict! (after marking mu resolved from Test 2)
4045 # EOL of mu should be unchanged (=CRLF).
4046 svntest.main.run_svn(None, 'propdel', 'svn:eol-style', mu_path)
4047 svntest.main.run_svn(None,
4048 'commit', '-m', 'del eol-style property', wc_dir)
4050 expected_backup_disk = svntest.main.greek_state.copy()
4051 expected_backup_disk.add({
4052 'A/mu' : Item(contents= "This is the file 'mu'." + crlf +
4053 "Added new line of text." + crlf)
4055 expected_backup_output = svntest.wc.State(wc_backup, {
4056 'A/mu' : Item(status=' G'),
4058 expected_backup_status = svntest.actions.get_virginal_state(wc_backup, 1)
4059 expected_backup_status.tweak('', status=' M')
4060 expected_backup_status.tweak('A/mu', status='M ')
4061 svntest.main.run_svn(None, 'resolved', path_backup)
4062 svntest.actions.run_and_verify_merge(wc_backup, '3', '4', sbox.repo_url,
4063 expected_backup_output,
4064 expected_backup_disk,
4065 expected_backup_status,
4066 expected_backup_skip)
4068 def create_deep_trees(wc_dir):
4069 """Create A/B/F/E by moving A/B/E to A/B/F/E.
4070 Copy A/B/F/E to A/B/F/E1.
4071 Copy A/B to A/copy-of-B, and return the expected status.
4072 At the end of this function WC would be at r4"""
4074 A_path = os.path.join(wc_dir, 'A')
4075 A_B_path = os.path.join(A_path, 'B')
4076 A_B_E_path = os.path.join(A_B_path, 'E')
4077 A_B_F_path = os.path.join(A_B_path, 'F')
4078 A_B_F_E_path = os.path.join(A_B_F_path, 'E')
4079 A_B_F_E1_path = os.path.join(A_B_F_path, 'E1')
4081 # Deepen the directory structure we're working with by moving E to
4082 # underneath F and committing, creating revision 2.
4083 svntest.main.run_svn(None, 'mv', A_B_E_path, A_B_F_path)
4085 # A/B/F/E now has empty mergeinfo
4087 expected_output = wc.State(wc_dir, {
4088 'A/B/E' : Item(verb='Deleting'),
4089 'A/B/F/E' : Item(verb='Adding')
4091 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
4092 expected_status.remove('A/B/E', 'A/B/E/alpha', 'A/B/E/beta')
4093 expected_status.add({
4094 'A/B/F/E' : Item(status=' ', wc_rev=2),
4095 'A/B/F/E/alpha' : Item(status=' ', wc_rev=2),
4096 'A/B/F/E/beta' : Item(status=' ', wc_rev=2),
4098 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
4099 expected_status, None,
4100 wc_dir)
4102 svntest.main.run_svn(None, 'cp', A_B_F_E_path, A_B_F_E1_path)
4104 # A/B/F/E1 now has empty mergeinfo
4106 expected_output = wc.State(wc_dir, {
4107 'A/B/F/E1' : Item(verb='Adding')
4109 expected_status.add({
4110 'A/B/F/E1' : Item(status=' ', wc_rev=3),
4111 'A/B/F/E1/alpha' : Item(status=' ', wc_rev=3),
4112 'A/B/F/E1/beta' : Item(status=' ', wc_rev=3),
4114 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
4115 expected_status, None,
4116 wc_dir)
4118 # Bring the entire WC up to date with rev 3.
4119 svntest.actions.run_and_verify_svn(None, None, [], 'update', wc_dir)
4120 expected_status.tweak(wc_rev=3)
4122 # Copy B and commit, creating revision 4.
4123 copy_of_B_path = os.path.join(A_path, 'copy-of-B')
4124 svntest.main.run_svn(None, "cp", A_B_path, copy_of_B_path)
4126 # A/copy-of-B, A/copy-of-B/F/E, and A/copy-of-B/F/E1 now have empty mergeinfo
4128 expected_output = svntest.wc.State(wc_dir, {
4129 'A/copy-of-B' : Item(verb='Adding'),
4131 expected_status.add({
4132 'A/copy-of-B' : Item(status=' ', wc_rev=4),
4133 'A/copy-of-B/F' : Item(status=' ', wc_rev=4),
4134 'A/copy-of-B/F/E' : Item(status=' ', wc_rev=4),
4135 'A/copy-of-B/F/E/alpha' : Item(status=' ', wc_rev=4),
4136 'A/copy-of-B/F/E/beta' : Item(status=' ', wc_rev=4),
4137 'A/copy-of-B/F/E1' : Item(status=' ', wc_rev=4),
4138 'A/copy-of-B/F/E1/alpha' : Item(status=' ', wc_rev=4),
4139 'A/copy-of-B/F/E1/beta' : Item(status=' ', wc_rev=4),
4140 'A/copy-of-B/lambda' : Item(status=' ', wc_rev=4),
4142 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
4143 expected_status, None,
4144 wc_dir)
4146 # pre-update, empty mergeinfo can be found on:
4148 # /A/B/F/E
4149 # /A/B/F/E1
4150 # /A/copy-of-B
4151 # /A/copy-of-B/F/E
4152 # /A/copy-of-B/F/E1
4154 expected_disk = svntest.main.greek_state.copy()
4155 expected_disk.remove('A/B/E', 'A/B/E/alpha', 'A/B/E/beta')
4156 expected_disk.add({
4157 'A/B/F/E' : Item(props={SVN_PROP_MERGEINFO : ''}),
4158 'A/B/F/E/alpha' : Item(contents="This is the file 'alpha'.\n"),
4159 'A/B/F/E/beta' : Item(contents="This is the file 'beta'.\n"),
4160 'A/B/F/E1' : Item(props={SVN_PROP_MERGEINFO : ''}),
4161 'A/B/F/E1/alpha' : Item(contents="This is the file 'alpha'.\n"),
4162 'A/B/F/E1/beta' : Item(contents="This is the file 'beta'.\n"),
4163 'A/copy-of-B' : Item(props={SVN_PROP_MERGEINFO : ''}),
4164 'A/copy-of-B/F' : Item(props={}),
4165 'A/copy-of-B/F/E' : Item(props={SVN_PROP_MERGEINFO : ''}),
4166 'A/copy-of-B/F/E/alpha' : Item(contents="This is the file 'alpha'.\n"),
4167 'A/copy-of-B/F/E/beta' : Item(contents="This is the file 'beta'.\n"),
4168 'A/copy-of-B/F/E1' : Item(props={SVN_PROP_MERGEINFO : ''}),
4169 'A/copy-of-B/F/E1/alpha' : Item(contents="This is the file 'alpha'.\n"),
4170 'A/copy-of-B/F/E1/beta' : Item(contents="This is the file 'beta'.\n"),
4171 'A/copy-of-B/lambda' : Item(contents="This is the file 'lambda'.\n"),
4173 svntest.actions.verify_disk(wc_dir, expected_disk,
4174 None, None, None, None, 1)
4176 # Bring the entire WC up to date with rev 4.
4177 svntest.actions.run_and_verify_svn(None, None, [], 'update', wc_dir)
4179 svntest.actions.verify_disk(wc_dir, expected_disk,
4180 None, None, None, None, 1)
4182 expected_status.tweak(wc_rev=4)
4183 expected_disk.tweak('A/copy-of-B/F/E', 'A/copy-of-B/F/E1', status=' M')
4184 return expected_status
4186 def avoid_repeated_merge_using_inherited_merge_info(sbox):
4187 "use inherited mergeinfo to avoid repeated merge"
4189 sbox.build()
4190 wc_dir = sbox.wc_dir
4192 A_path = os.path.join(wc_dir, 'A')
4193 A_B_path = os.path.join(A_path, 'B')
4194 A_B_E_path = os.path.join(A_B_path, 'E')
4195 A_B_F_path = os.path.join(A_B_path, 'F')
4196 copy_of_B_path = os.path.join(A_path, 'copy-of-B')
4198 # Create a deeper directory structure.
4199 expected_status = create_deep_trees(wc_dir)
4201 # Edit alpha and commit it, creating revision 5.
4202 alpha_path = os.path.join(A_B_F_path, 'E', 'alpha')
4203 new_content_for_alpha = 'new content to alpha\n'
4204 svntest.main.file_write(alpha_path, new_content_for_alpha)
4205 expected_output = svntest.wc.State(wc_dir, {
4206 'A/B/F/E/alpha' : Item(verb='Sending'),
4208 expected_status.tweak('A/B/F/E/alpha', wc_rev=5)
4209 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
4210 expected_status, None,
4211 wc_dir)
4213 # Search for the comment entitled "The Merge Kluge" elsewhere in
4214 # this file, to understand why we shorten and chdir() below.
4215 short_copy_of_B_path = shorten_path_kludge(copy_of_B_path)
4217 # Bring the entire WC up to date with rev 5.
4218 svntest.actions.run_and_verify_svn(None, None, [], 'update', wc_dir)
4220 # Merge changes from rev 5 of B (to alpha) into copy_of_B.
4221 # A_COPY/copy_of_B/F/E and A_COPY/copy_of_B/F/E1 both exist in the merge
4222 # source at r5, so their empty mergeinfo should be updted with r5, which
4223 # then should elide to A_COPY/copy_of_B leaving no mergeinfo on either.
4224 expected_output = wc.State(short_copy_of_B_path, {
4225 'F/E/alpha' : Item(status='U '),
4227 expected_status = wc.State(short_copy_of_B_path, {
4228 '' : Item(status=' M', wc_rev=5),
4229 'F/E' : Item(status=' M', wc_rev=5),
4230 'F/E/alpha' : Item(status='M ', wc_rev=5),
4231 'F/E/beta' : Item(status=' ', wc_rev=5),
4232 'F/E1' : Item(status=' M', wc_rev=5),
4233 'F/E1/alpha' : Item(status=' ', wc_rev=5),
4234 'F/E1/beta' : Item(status=' ', wc_rev=5),
4235 'lambda' : Item(status=' ', wc_rev=5),
4236 'F' : Item(status=' ', wc_rev=5),
4238 expected_disk = wc.State('', {
4239 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B:5'}),
4240 'F/E' : Item(),
4241 'F/E/alpha' : Item(new_content_for_alpha),
4242 'F/E/beta' : Item("This is the file 'beta'.\n"),
4243 'F/E1' : Item(),
4244 'F/E1/alpha' : Item("This is the file 'alpha'.\n"),
4245 'F/E1/beta' : Item("This is the file 'beta'.\n"),
4246 'F' : Item(),
4247 'lambda' : Item("This is the file 'lambda'.\n")
4249 expected_skip = wc.State(short_copy_of_B_path, { })
4250 saved_cwd = os.getcwd()
4252 os.chdir(svntest.main.work_dir)
4253 svntest.actions.run_and_verify_merge(short_copy_of_B_path, '4', '5',
4254 sbox.repo_url + \
4255 '/A/B',
4256 expected_output,
4257 expected_disk,
4258 expected_status,
4259 expected_skip,
4260 None,
4261 None,
4262 None,
4263 None,
4264 None, 1)
4265 os.chdir(saved_cwd)
4267 # Commit the result of the merge, creating revision 6.
4268 expected_output = svntest.wc.State(copy_of_B_path, {
4269 '' : Item(verb='Sending'),
4270 'F/E' : Item(verb='Sending'),
4271 'F/E/alpha' : Item(verb='Sending'),
4272 'F/E1' : Item(verb='Sending'),
4274 svntest.actions.run_and_verify_commit(short_copy_of_B_path, expected_output,
4275 None, None, wc_dir)
4277 # Update the WC to bring /A/copy_of_B/F from rev 4 to rev 6.
4278 # Without this update, a subsequent merge will not find any merge
4279 # info for /A/copy_of_B/F -- nor its parent dir in the repos -- at
4280 # rev 4. Mergeinfo wasn't introduced until rev 6.
4281 copy_of_B_F_E_path = os.path.join(copy_of_B_path, 'F', 'E')
4282 svntest.actions.run_and_verify_svn(None, None, [], 'update', wc_dir)
4284 # Search for the comment entitled "The Merge Kluge" elsewhere in
4285 # this file, to understand why we shorten and chdir() below.
4286 short_copy_of_B_F_E_path = shorten_path_kludge(copy_of_B_F_E_path)
4288 # Attempt to re-merge changes to alpha from rev 4. Use the merge
4289 # info inherited from the grandparent (copy-of-B) of our merge
4290 # target (/A/copy-of-B/F/E) to avoid a repeated merge.
4291 expected_status = wc.State(short_copy_of_B_F_E_path, {
4292 '' : Item(status=' ', wc_rev=6),
4293 'alpha' : Item(status=' ', wc_rev=6),
4294 'beta' : Item(status=' ', wc_rev=6),
4296 os.chdir(svntest.main.work_dir)
4297 svntest.actions.run_and_verify_svn(None, [], [], 'merge', '-r4:5',
4298 sbox.repo_url + '/A/B/F/E',
4299 short_copy_of_B_F_E_path)
4300 svntest.actions.run_and_verify_status(short_copy_of_B_F_E_path,
4301 expected_status)
4302 os.chdir(saved_cwd)
4304 def avoid_repeated_merge_on_subtree_with_merge_info(sbox):
4305 "use subtree's mergeinfo to avoid repeated merge"
4306 # Create deep trees A/B/F/E and A/B/F/E1 and copy A/B to A/copy-of-B
4307 # with the help of 'create_deep_trees'
4308 # As /A/copy-of-B/F/E1 is not a child of /A/copy-of-B/F/E,
4309 # set_path should not be called on /A/copy-of-B/F/E1 while
4310 # doing a implicit subtree merge on /A/copy-of-B/F/E.
4311 sbox.build()
4312 wc_dir = sbox.wc_dir
4314 A_path = os.path.join(wc_dir, 'A')
4315 A_B_path = os.path.join(A_path, 'B')
4316 A_B_E_path = os.path.join(A_B_path, 'E')
4317 A_B_F_path = os.path.join(A_B_path, 'F')
4318 A_B_F_E_path = os.path.join(A_B_F_path, 'E')
4319 copy_of_B_path = os.path.join(A_path, 'copy-of-B')
4321 # Create a deeper directory structure.
4322 expected_status = create_deep_trees(wc_dir)
4324 # Edit alpha and commit it, creating revision 5.
4325 alpha_path = os.path.join(A_B_F_E_path, 'alpha')
4326 new_content_for_alpha1 = 'new content to alpha\n'
4327 svntest.main.file_write(alpha_path, new_content_for_alpha1)
4329 expected_output = svntest.wc.State(wc_dir, {
4330 'A/B/F/E/alpha' : Item(verb='Sending'),
4332 expected_status.tweak('A/B/F/E/alpha', wc_rev=5)
4333 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
4334 expected_status, None, wc_dir)
4336 for path_and_mergeinfo in (('E', '/A/B/F/E:5'),
4337 ('E1', '/A/B/F/E:5')):
4338 path_name = os.path.join(copy_of_B_path, 'F', path_and_mergeinfo[0])
4339 # Search for the comment entitled "The Merge Kluge" elsewhere in
4340 # this file, to understand why we shorten and chdir() below.
4341 short_path_name = shorten_path_kludge(path_name)
4343 # Merge r5 to path_name.
4344 expected_output = wc.State(short_path_name, {
4345 'alpha' : Item(status='U '),
4347 expected_status = wc.State(short_path_name, {
4348 '' : Item(status=' M', wc_rev=4),
4349 'alpha' : Item(status='M ', wc_rev=4),
4350 'beta' : Item(status=' ', wc_rev=4),
4352 expected_disk = wc.State('', {
4353 '' : Item(props={SVN_PROP_MERGEINFO : path_and_mergeinfo[1]}),
4354 'alpha' : Item(new_content_for_alpha1),
4355 'beta' : Item("This is the file 'beta'.\n"),
4357 expected_skip = wc.State(short_path_name, { })
4358 saved_cwd = os.getcwd()
4360 os.chdir(svntest.main.work_dir)
4361 svntest.actions.run_and_verify_merge(short_path_name, '4', '5',
4362 sbox.repo_url + '/A/B/F/E',
4363 expected_output,
4364 expected_disk,
4365 expected_status,
4366 expected_skip,
4367 None,
4368 None,
4369 None,
4370 None,
4371 None, 1)
4372 os.chdir(saved_cwd)
4374 # Commit the result of the merge, creating new revision.
4375 expected_output = svntest.wc.State(path_name, {
4376 '' : Item(verb='Sending'),
4377 'alpha' : Item(verb='Sending'),
4379 svntest.actions.run_and_verify_commit(short_path_name,
4380 expected_output, None, None, wc_dir)
4382 # Edit A/B/F/E/alpha and commit it, creating revision 8.
4383 new_content_for_alpha = 'new content to alpha\none more line\n'
4384 svntest.main.file_write(alpha_path, new_content_for_alpha)
4386 expected_output = svntest.wc.State(A_B_F_E_path, {
4387 'alpha' : Item(verb='Sending'),
4389 expected_status = wc.State(A_B_F_E_path, {
4390 '' : Item(status=' ', wc_rev=4),
4391 'alpha' : Item(status=' ', wc_rev=8),
4392 'beta' : Item(status=' ', wc_rev=4),
4394 svntest.actions.run_and_verify_commit(A_B_F_E_path, expected_output,
4395 expected_status, None, wc_dir)
4397 # Search for the comment entitled "The Merge Kluge" elsewhere in
4398 # this file, to understand why we shorten and chdir() below.
4399 short_copy_of_B_path = shorten_path_kludge(copy_of_B_path)
4401 # Update the WC to bring /A/copy_of_B to rev 8.
4402 # Without this update expected_status tree would be cumbersome to
4403 # understand.
4404 svntest.actions.run_and_verify_svn(None, None, [], 'update', wc_dir)
4406 # Merge changes from rev 4:8 of A/B into A/copy_of_B. A/copy_of_B/F/E1
4407 # has explicit mergeinfo and exists at r4 in the merge source, so it
4408 # should be treated as a subtree with intersecting mergeinfo and its
4409 # mergeinfo updated.
4410 expected_output = wc.State(short_copy_of_B_path, {
4411 'F/E/alpha' : Item(status='U ')
4413 expected_status = wc.State(short_copy_of_B_path, {
4414 # When we merge multiple sub-targets, we record mergeinfo on each
4415 # child.
4416 '' : Item(status=' M', wc_rev=8),
4417 'F/E' : Item(status=' M', wc_rev=8),
4418 'F/E/alpha' : Item(status='M ', wc_rev=8),
4419 'F/E/beta' : Item(status=' ', wc_rev=8),
4420 'F/E1' : Item(status=' M', wc_rev=8),
4421 'F/E1/alpha' : Item(status=' ', wc_rev=8),
4422 'F/E1/beta' : Item(status=' ', wc_rev=8),
4423 'lambda' : Item(status=' ', wc_rev=8),
4424 'F' : Item(status=' ', wc_rev=8)
4426 expected_disk = wc.State('', {
4427 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B:5-8'}),
4428 'F/E' : Item(props={}), # elision!
4429 'F/E/alpha' : Item(new_content_for_alpha),
4430 'F/E/beta' : Item("This is the file 'beta'.\n"),
4431 'F' : Item(),
4432 'F/E1' : Item(props={SVN_PROP_MERGEINFO :
4433 '/A/B/F/E:5\n/A/B/F/E1:5-8\n'}),
4434 'F/E1/alpha' : Item(new_content_for_alpha1),
4435 'F/E1/beta' : Item("This is the file 'beta'.\n"),
4436 'lambda' : Item("This is the file 'lambda'.\n")
4438 expected_skip = wc.State(short_copy_of_B_path, { })
4439 os.chdir(svntest.main.work_dir)
4440 svntest.actions.run_and_verify_merge(short_copy_of_B_path, '4', '8',
4441 sbox.repo_url + '/A/B',
4442 expected_output,
4443 expected_disk,
4444 expected_status,
4445 expected_skip,
4446 None,
4447 None,
4448 None,
4449 None,
4450 None, 1)
4452 def tweak_src_then_merge_to_dest(sbox, src_path, dst_path,
4453 canon_src_path, contents, cur_rev):
4454 """Edit src and commit it. This results in new_rev.
4455 Merge new_rev to dst_path. Return new_rev."""
4457 wc_dir = sbox.wc_dir
4458 new_rev = cur_rev + 1
4459 svntest.main.file_write(src_path, contents)
4461 expected_output = svntest.wc.State(src_path, {
4462 '': Item(verb='Sending'),
4465 expected_status = wc.State(src_path,
4466 { '': Item(wc_rev=new_rev, status=' ')})
4468 svntest.actions.run_and_verify_commit(src_path, expected_output,
4469 expected_status, None, src_path)
4471 # Update the WC to new_rev so that it would be easier to expect everyone
4472 # to be at new_rev.
4473 svntest.actions.run_and_verify_svn(None, None, [], 'update', wc_dir)
4475 # Merge new_rev of src_path to dst_path.
4477 # Search for the comment entitled "The Merge Kluge" elsewhere in
4478 # this file, to understand why we shorten and chdir() below.
4479 short_dst_path = shorten_path_kludge(dst_path)
4480 expected_status = wc.State(dst_path,
4481 { '': Item(wc_rev=new_rev, status='MM')})
4482 saved_cwd = os.getcwd()
4484 os.chdir(svntest.main.work_dir)
4486 merge_url = sbox.repo_url + '/' + canon_src_path
4487 if sys.platform == 'win32':
4488 merge_url = merge_url.replace('\\', '/')
4490 svntest.actions.run_and_verify_svn(None,
4491 expected_merge_output([[new_rev]],
4492 'U ' +
4493 short_dst_path +
4494 '\n'),
4496 'merge', '-c', str(new_rev),
4497 merge_url,
4498 short_dst_path)
4499 os.chdir(saved_cwd)
4501 svntest.actions.run_and_verify_status(dst_path, expected_status)
4503 return new_rev
4505 def obey_reporter_api_semantics_while_doing_subtree_merges(sbox):
4506 "drive reporter api in depth first order"
4508 # Copy /A/D to /A/copy-of-D it results in rONE.
4509 # Create children at different hierarchies having some merge-info
4510 # to test the set_path calls on a reporter in a depth-first order.
4511 # On all 'file' descendants of /A/copy-of-D/ we run merges.
4512 # We create /A/D/umlaut directly over URL it results in rev rTWO.
4513 # When we merge rONE+1:TWO of /A/D on /A/copy-of-D it should merge smoothly.
4515 sbox.build()
4516 wc_dir = sbox.wc_dir
4518 A_path = os.path.join(wc_dir, 'A')
4519 A_D_path = os.path.join(wc_dir, 'A', 'D')
4520 copy_of_A_D_path = os.path.join(wc_dir, 'A', 'copy-of-D')
4522 svntest.main.run_svn(None, "cp", A_D_path, copy_of_A_D_path)
4524 expected_output = svntest.wc.State(wc_dir, {
4525 'A/copy-of-D' : Item(verb='Adding'),
4527 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
4528 expected_status.add({
4529 'A/copy-of-D' : Item(status=' ', wc_rev=2),
4530 'A/copy-of-D/G' : Item(status=' ', wc_rev=2),
4531 'A/copy-of-D/G/pi' : Item(status=' ', wc_rev=2),
4532 'A/copy-of-D/G/rho' : Item(status=' ', wc_rev=2),
4533 'A/copy-of-D/G/tau' : Item(status=' ', wc_rev=2),
4534 'A/copy-of-D/H' : Item(status=' ', wc_rev=2),
4535 'A/copy-of-D/H/chi' : Item(status=' ', wc_rev=2),
4536 'A/copy-of-D/H/omega' : Item(status=' ', wc_rev=2),
4537 'A/copy-of-D/H/psi' : Item(status=' ', wc_rev=2),
4538 'A/copy-of-D/gamma' : Item(status=' ', wc_rev=2),
4540 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
4541 expected_status, None, wc_dir)
4544 cur_rev = 2
4545 for path in (["A", "D", "G", "pi"],
4546 ["A", "D", "G", "rho"],
4547 ["A", "D", "G", "tau"],
4548 ["A", "D", "H", "chi"],
4549 ["A", "D", "H", "omega"],
4550 ["A", "D", "H", "psi"],
4551 ["A", "D", "gamma"]):
4552 path_name = os.path.join(wc_dir, *path)
4553 canon_path_name = os.path.join(*path)
4554 path[1] = "copy-of-D"
4555 copy_of_path_name = os.path.join(wc_dir, *path)
4556 var_name = 'new_content_for_' + path[len(path) - 1]
4557 file_contents = "new content to " + path[len(path) - 1] + "\n"
4558 globals()[var_name] = file_contents
4559 cur_rev = tweak_src_then_merge_to_dest(sbox, path_name,
4560 copy_of_path_name, canon_path_name,
4561 file_contents, cur_rev)
4563 copy_of_A_D_wc_rev = cur_rev
4564 svntest.actions.run_and_verify_svn(None,
4565 ['\n',
4566 'Committed revision ' + str(cur_rev+1) +
4567 '.\n'],
4569 'mkdir', sbox.repo_url + '/A/D/umlaut',
4570 '-m', "log msg")
4571 rev_to_merge_to_copy_of_D = cur_rev + 1
4573 # Search for the comment entitled "The Merge Kluge" elsewhere in
4574 # this file, to understand why we shorten and chdir() below.
4575 short_copy_of_A_D_path = shorten_path_kludge(copy_of_A_D_path)
4577 # All the file descendants of /A/copy-of-D/ have already been merged
4578 # so the only notification we expect is for the added 'umlaut'.
4579 expected_output = wc.State(short_copy_of_A_D_path, {
4580 'umlaut' : Item(status='A '),
4583 # All the local svn:mergeinfo under A/copy-of-D elides
4584 # to A/copy-of-D.
4585 expected_status = wc.State(short_copy_of_A_D_path, {
4586 '' : Item(status=' M', wc_rev=copy_of_A_D_wc_rev),
4587 'G' : Item(status=' ', wc_rev=copy_of_A_D_wc_rev),
4588 'G/pi' : Item(status='M ', wc_rev=copy_of_A_D_wc_rev),
4589 'G/rho' : Item(status='M ', wc_rev=copy_of_A_D_wc_rev),
4590 'G/tau' : Item(status='M ', wc_rev=copy_of_A_D_wc_rev),
4591 'H' : Item(status=' ', wc_rev=copy_of_A_D_wc_rev),
4592 'H/chi' : Item(status='M ', wc_rev=copy_of_A_D_wc_rev),
4593 'H/omega' : Item(status='M ', wc_rev=copy_of_A_D_wc_rev),
4594 'H/psi' : Item(status='M ', wc_rev=copy_of_A_D_wc_rev),
4595 'gamma' : Item(status='M ', wc_rev=copy_of_A_D_wc_rev),
4596 'umlaut' : Item(status='A ', copied='+', wc_rev='-'),
4599 merged_rangelist = "3-%d" % rev_to_merge_to_copy_of_D
4602 expected_disk = wc.State('', {
4603 '' : Item(props={SVN_PROP_MERGEINFO : '/A/D:' + merged_rangelist}),
4604 'G' : Item(),
4605 'G/pi' : Item(new_content_for_pi),
4606 'G/rho' : Item(new_content_for_rho),
4607 'G/tau' : Item(new_content_for_tau),
4608 'H' : Item(),
4609 'H/chi' : Item(new_content_for_chi,),
4610 'H/omega' : Item(new_content_for_omega,),
4611 'H/psi' : Item(new_content_for_psi,),
4612 'gamma' : Item(new_content_for_gamma,),
4613 'umlaut' : Item(),
4615 expected_skip = wc.State(short_copy_of_A_D_path, { })
4616 os.chdir(svntest.main.work_dir)
4617 svntest.actions.run_and_verify_merge(short_copy_of_A_D_path,
4619 str(rev_to_merge_to_copy_of_D),
4620 sbox.repo_url + '/A/D',
4621 expected_output,
4622 expected_disk,
4623 expected_status,
4624 expected_skip,
4625 None,
4626 None,
4627 None,
4628 None,
4629 None, 1)
4631 def set_up_branch(sbox, branch_only = False, nbr_of_branches = 1):
4632 '''Starting with standard greek tree, copy 'A' NBR_OF_BRANCHES times
4633 to A_COPY, A_COPY_2, A_COPY_3, and so on. Then make four modifications
4634 (setting file contents to "New content") under A:
4635 r(2 + NBR_OF_BRANCHES) - A/D/H/psi
4636 r(3 + NBR_OF_BRANCHES) - A/D/G/rho
4637 r(4 + NBR_OF_BRANCHES) - A/B/E/beta
4638 r(5 + NBR_OF_BRANCHES) - A/D/H/omega'''
4640 wc_dir = sbox.wc_dir
4642 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
4643 expected_disk = svntest.main.greek_state.copy()
4645 def copy_A(dest_name, rev):
4646 expected = svntest.verify.UnorderedOutput(
4647 ["A " + os.path.join(wc_dir, dest_name, "B") + "\n",
4648 "A " + os.path.join(wc_dir, dest_name, "B", "lambda") + "\n",
4649 "A " + os.path.join(wc_dir, dest_name, "B", "E") + "\n",
4650 "A " + os.path.join(wc_dir, dest_name, "B", "E", "alpha") + "\n",
4651 "A " + os.path.join(wc_dir, dest_name, "B", "E", "beta") + "\n",
4652 "A " + os.path.join(wc_dir, dest_name, "B", "F") + "\n",
4653 "A " + os.path.join(wc_dir, dest_name, "mu") + "\n",
4654 "A " + os.path.join(wc_dir, dest_name, "C") + "\n",
4655 "A " + os.path.join(wc_dir, dest_name, "D") + "\n",
4656 "A " + os.path.join(wc_dir, dest_name, "D", "gamma") + "\n",
4657 "A " + os.path.join(wc_dir, dest_name, "D", "G") + "\n",
4658 "A " + os.path.join(wc_dir, dest_name, "D", "G", "pi") + "\n",
4659 "A " + os.path.join(wc_dir, dest_name, "D", "G", "rho") + "\n",
4660 "A " + os.path.join(wc_dir, dest_name, "D", "G", "tau") + "\n",
4661 "A " + os.path.join(wc_dir, dest_name, "D", "H") + "\n",
4662 "A " + os.path.join(wc_dir, dest_name, "D", "H", "chi") + "\n",
4663 "A " + os.path.join(wc_dir, dest_name, "D", "H", "omega") + "\n",
4664 "A " + os.path.join(wc_dir, dest_name, "D", "H", "psi") + "\n",
4665 "Checked out revision " + str(rev - 1) + ".\n",
4666 "A " + os.path.join(wc_dir, dest_name) + "\n"])
4667 expected_status.add({
4668 dest_name + "/B" : Item(status=' ', wc_rev=rev),
4669 dest_name + "/B/lambda" : Item(status=' ', wc_rev=rev),
4670 dest_name + "/B/E" : Item(status=' ', wc_rev=rev),
4671 dest_name + "/B/E/alpha" : Item(status=' ', wc_rev=rev),
4672 dest_name + "/B/E/beta" : Item(status=' ', wc_rev=rev),
4673 dest_name + "/B/F" : Item(status=' ', wc_rev=rev),
4674 dest_name + "/mu" : Item(status=' ', wc_rev=rev),
4675 dest_name + "/C" : Item(status=' ', wc_rev=rev),
4676 dest_name + "/D" : Item(status=' ', wc_rev=rev),
4677 dest_name + "/D/gamma" : Item(status=' ', wc_rev=rev),
4678 dest_name + "/D/G" : Item(status=' ', wc_rev=rev),
4679 dest_name + "/D/G/pi" : Item(status=' ', wc_rev=rev),
4680 dest_name + "/D/G/rho" : Item(status=' ', wc_rev=rev),
4681 dest_name + "/D/G/tau" : Item(status=' ', wc_rev=rev),
4682 dest_name + "/D/H" : Item(status=' ', wc_rev=rev),
4683 dest_name + "/D/H/chi" : Item(status=' ', wc_rev=rev),
4684 dest_name + "/D/H/omega" : Item(status=' ', wc_rev=rev),
4685 dest_name + "/D/H/psi" : Item(status=' ', wc_rev=rev),
4686 dest_name : Item(status=' ', wc_rev=rev)})
4687 expected_disk.add({
4688 dest_name : Item(),
4689 dest_name + '/B' : Item(),
4690 dest_name + '/B/lambda' : Item("This is the file 'lambda'.\n"),
4691 dest_name + '/B/E' : Item(),
4692 dest_name + '/B/E/alpha' : Item("This is the file 'alpha'.\n"),
4693 dest_name + '/B/E/beta' : Item("This is the file 'beta'.\n"),
4694 dest_name + '/B/F' : Item(),
4695 dest_name + '/mu' : Item("This is the file 'mu'.\n"),
4696 dest_name + '/C' : Item(),
4697 dest_name + '/D' : Item(),
4698 dest_name + '/D/gamma' : Item("This is the file 'gamma'.\n"),
4699 dest_name + '/D/G' : Item(),
4700 dest_name + '/D/G/pi' : Item("This is the file 'pi'.\n"),
4701 dest_name + '/D/G/rho' : Item("This is the file 'rho'.\n"),
4702 dest_name + '/D/G/tau' : Item("This is the file 'tau'.\n"),
4703 dest_name + '/D/H' : Item(),
4704 dest_name + '/D/H/chi' : Item("This is the file 'chi'.\n"),
4705 dest_name + '/D/H/omega' : Item("This is the file 'omega'.\n"),
4706 dest_name + '/D/H/psi' : Item("This is the file 'psi'.\n"),
4709 # Make a branch A_COPY to merge into.
4710 svntest.actions.run_and_verify_svn(None, expected, [], 'copy',
4711 sbox.repo_url + "/A",
4712 os.path.join(wc_dir,
4713 dest_name))
4715 expected_output = wc.State(wc_dir, {dest_name : Item(verb='Adding')})
4716 svntest.actions.run_and_verify_commit(wc_dir,
4717 expected_output,
4718 expected_status,
4719 None,
4720 wc_dir)
4721 for i in range(nbr_of_branches):
4722 if i == 0:
4723 copy_A('A_COPY', i + 2)
4724 else:
4725 copy_A('A_COPY_' + str(i + 1), i + 2)
4727 if (branch_only):
4728 return expected_disk, expected_status
4730 # Make some changes under A which we'll later merge under A_COPY:
4732 # r(nbr_of_branches + 2) - modify and commit A/D/H/psi
4733 svntest.main.file_write(os.path.join(wc_dir, "A", "D", "H", "psi"),
4734 "New content")
4735 expected_output = wc.State(wc_dir, {'A/D/H/psi' : Item(verb='Sending')})
4736 expected_status.tweak('A/D/H/psi', wc_rev=nbr_of_branches + 2)
4737 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
4738 expected_status, None, wc_dir)
4739 expected_disk.tweak('A/D/H/psi', contents="New content")
4741 # r(nbr_of_branches + 3) - modify and commit A/D/G/rho
4742 svntest.main.file_write(os.path.join(wc_dir, "A", "D", "G", "rho"),
4743 "New content")
4744 expected_output = wc.State(wc_dir, {'A/D/G/rho' : Item(verb='Sending')})
4745 expected_status.tweak('A/D/G/rho', wc_rev=nbr_of_branches + 3)
4746 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
4747 expected_status, None, wc_dir)
4748 expected_disk.tweak('A/D/G/rho', contents="New content")
4750 # r(nbr_of_branches + 4) - modify and commit A/B/E/beta
4751 svntest.main.file_write(os.path.join(wc_dir, "A", "B", "E", "beta"),
4752 "New content")
4753 expected_output = wc.State(wc_dir, {'A/B/E/beta' : Item(verb='Sending')})
4754 expected_status.tweak('A/B/E/beta', wc_rev=nbr_of_branches + 4)
4755 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
4756 expected_status, None, wc_dir)
4757 expected_disk.tweak('A/B/E/beta', contents="New content")
4759 # r(nbr_of_branches + 5) - modify and commit A/D/H/omega
4760 svntest.main.file_write(os.path.join(wc_dir, "A", "D", "H", "omega"),
4761 "New content")
4762 expected_output = wc.State(wc_dir, {'A/D/H/omega' : Item(verb='Sending')})
4763 expected_status.tweak('A/D/H/omega', wc_rev=nbr_of_branches + 5)
4764 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
4765 expected_status, None, wc_dir)
4766 expected_disk.tweak('A/D/H/omega', contents="New content")
4768 return expected_disk, expected_status
4771 def mergeinfo_inheritance(sbox):
4772 "target inherits mergeinfo from nearest ancestor"
4774 # Test for Issues #2733 and #2734.
4776 # When the target of a merge has no explicit mergeinfo and the merge
4777 # would result in mergeinfo being added to the target which...
4779 # ...is a subset of the *local* mergeinfo on one of the target's
4780 # ancestors (it's nearest ancestor takes precedence), then the merge is
4781 # not repeated and no mergeinfo should be set on the target (Issue #2734).
4783 # OR
4785 # ...is not a subset it's nearest ancestor, the target should inherit the
4786 # non-inersecting mergeinfo (local or committed, the former takes
4787 # precedence) from it's nearest ancestor (Issue #2733).
4789 sbox.build()
4790 wc_dir = sbox.wc_dir
4791 wc_disk, wc_status = set_up_branch(sbox)
4793 # Some paths we'll care about
4794 A_COPY_path = os.path.join(wc_dir, "A_COPY")
4795 B_COPY_path = os.path.join(wc_dir, "A_COPY", "B")
4796 beta_COPY_path = os.path.join(wc_dir, "A_COPY", "B", "E", "beta")
4797 E_COPY_path = os.path.join(wc_dir, "A_COPY", "B", "E")
4798 omega_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "H", "omega")
4799 D_COPY_path = os.path.join(wc_dir, "A_COPY", "D")
4800 G_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "G")
4802 # Now start merging...
4804 # Merge r4 into A_COPY/D/
4805 # Search for the comment entitled "The Merge Kluge" elsewhere in
4806 # this file, to understand why we shorten and chdir() below.
4807 short_D_COPY_path = shorten_path_kludge(D_COPY_path)
4808 expected_output = wc.State(short_D_COPY_path, {
4809 'G/rho' : Item(status='U '),
4811 expected_status = wc.State(short_D_COPY_path, {
4812 '' : Item(status=' M', wc_rev=2),
4813 'G' : Item(status=' ', wc_rev=2),
4814 'G/pi' : Item(status=' ', wc_rev=2),
4815 'G/rho' : Item(status='M ', wc_rev=2),
4816 'G/tau' : Item(status=' ', wc_rev=2),
4817 'H' : Item(status=' ', wc_rev=2),
4818 'H/chi' : Item(status=' ', wc_rev=2),
4819 'H/psi' : Item(status=' ', wc_rev=2),
4820 'H/omega' : Item(status=' ', wc_rev=2),
4821 'gamma' : Item(status=' ', wc_rev=2),
4823 # We test issue #2733 here (with a directory as the merge target).
4824 # r1 should be inherited from 'A_COPY'.
4825 expected_disk = wc.State('', {
4826 '' : Item(props={SVN_PROP_MERGEINFO : '/A/D:4'}),
4827 'G' : Item(),
4828 'G/pi' : Item("This is the file 'pi'.\n"),
4829 'G/rho' : Item("New content"),
4830 'G/tau' : Item("This is the file 'tau'.\n"),
4831 'H' : Item(),
4832 'H/chi' : Item("This is the file 'chi'.\n"),
4833 'H/psi' : Item("This is the file 'psi'.\n"),
4834 'H/omega' : Item("This is the file 'omega'.\n"),
4835 'gamma' : Item("This is the file 'gamma'.\n")
4837 expected_skip = wc.State(short_D_COPY_path, { })
4838 saved_cwd = os.getcwd()
4839 os.chdir(svntest.main.work_dir)
4840 svntest.actions.run_and_verify_merge(short_D_COPY_path, '3', '4',
4841 sbox.repo_url + \
4842 '/A/D',
4843 expected_output,
4844 expected_disk,
4845 expected_status,
4846 expected_skip,
4847 None, None, None, None,
4848 None, 1)
4849 os.chdir(saved_cwd)
4851 # Merge r4 again, this time into A_COPY/D/G. An ancestor directory
4852 # (A_COPY/D) exists with identical local mergeinfo, so the merge
4853 # should not be repeated. We test issue #2734 here with (with a
4854 # directory as the merge target).
4855 short_G_COPY_path = shorten_path_kludge(G_COPY_path)
4856 expected_output = wc.State(short_G_COPY_path, { })
4857 expected_status = wc.State(short_G_COPY_path, {
4858 '' : Item(status=' ', wc_rev=2),
4859 'pi' : Item(status=' ', wc_rev=2),
4860 'rho' : Item(status='M ', wc_rev=2),
4861 'tau' : Item(status=' ', wc_rev=2),
4863 expected_disk = wc.State('', {
4864 'pi' : Item("This is the file 'pi'.\n"),
4865 'rho' : Item("New content"),
4866 'tau' : Item("This is the file 'tau'.\n"),
4868 expected_skip = wc.State(short_G_COPY_path, { })
4869 os.chdir(svntest.main.work_dir)
4870 svntest.actions.run_and_verify_merge(short_G_COPY_path, '3', '4',
4871 sbox.repo_url + \
4872 '/A/D/G',
4873 expected_output,
4874 expected_disk,
4875 expected_status,
4876 expected_skip,
4877 None, None, None, None,
4878 None, 1)
4879 os.chdir(saved_cwd)
4880 # Merge r5 into A_COPY/B. Again, r1 should be inherited from
4881 # A_COPY (Issue #2733)
4882 short_B_COPY_path = shorten_path_kludge(B_COPY_path)
4883 expected_output = wc.State(short_B_COPY_path, {
4884 'E/beta' : Item(status='U '),
4886 expected_status = wc.State(short_B_COPY_path, {
4887 '' : Item(status=' M', wc_rev=2),
4888 'E' : Item(status=' ', wc_rev=2),
4889 'E/alpha' : Item(status=' ', wc_rev=2),
4890 'E/beta' : Item(status='M ', wc_rev=2),
4891 'lambda' : Item(status=' ', wc_rev=2),
4892 'F' : Item(status=' ', wc_rev=2),
4894 expected_disk = wc.State('', {
4895 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B:5'}),
4896 'E' : Item(),
4897 'E/alpha' : Item("This is the file 'alpha'.\n"),
4898 'E/beta' : Item("New content"),
4899 'F' : Item(),
4900 'lambda' : Item("This is the file 'lambda'.\n")
4902 expected_skip = wc.State(short_B_COPY_path, { })
4904 os.chdir(svntest.main.work_dir)
4905 svntest.actions.run_and_verify_merge(short_B_COPY_path, '4', '5',
4906 sbox.repo_url + \
4907 '/A/B',
4908 expected_output,
4909 expected_disk,
4910 expected_status,
4911 expected_skip,
4912 None, None, None, None,
4913 None, 1)
4914 os.chdir(saved_cwd)
4916 # Merge r5 again, this time into A_COPY/B/E/beta. An ancestor
4917 # directory (A_COPY/B) exists with identical local mergeinfo, so
4918 # the merge should not be repeated (Issue #2734 with a file as the
4919 # merge target).
4920 short_beta_COPY_path = shorten_path_kludge(beta_COPY_path)
4921 expected_skip = wc.State(short_beta_COPY_path, { })
4922 saved_cwd = os.getcwd()
4924 os.chdir(svntest.main.work_dir)
4925 # run_and_verify_merge doesn't support merging to a file WCPATH
4926 # so use run_and_verify_svn.
4927 svntest.actions.run_and_verify_svn(None, [], [], 'merge', '-c5',
4928 sbox.repo_url + '/A/B/E/beta',
4929 short_beta_COPY_path)
4930 os.chdir(saved_cwd)
4932 # The merge wasn't repeated so beta shouldn't have any mergeinfo.
4933 # We are implicitly testing that without looking at the prop value
4934 # itself, just beta's prop modification status.
4935 expected_status = wc.State(beta_COPY_path, {
4936 '' : Item(status='M ', wc_rev=2),
4938 svntest.actions.run_and_verify_status(beta_COPY_path, expected_status)
4940 # Merge r3 into A_COPY. A_COPY's two descendants with mergeinfo,
4941 # A_COPY/B/E/beta and A_COPY/D/G/rho must have complete mergeinfo
4942 # so they both should pick up r3 too.
4943 short_A_COPY_path = shorten_path_kludge(A_COPY_path)
4944 expected_output = wc.State(short_A_COPY_path, {
4945 'D/H/psi' : Item(status='U '),
4947 expected_status = wc.State(short_A_COPY_path, {
4948 '' : Item(status=' M', wc_rev=2),
4949 'B' : Item(status=' M', wc_rev=2),
4950 'mu' : Item(status=' ', wc_rev=2),
4951 'B/E' : Item(status=' ', wc_rev=2),
4952 'B/E/alpha' : Item(status=' ', wc_rev=2),
4953 'B/E/beta' : Item(status='M ', wc_rev=2),
4954 'B/lambda' : Item(status=' ', wc_rev=2),
4955 'B/F' : Item(status=' ', wc_rev=2),
4956 'C' : Item(status=' ', wc_rev=2),
4957 'D' : Item(status=' M', wc_rev=2),
4958 'D/G' : Item(status=' ', wc_rev=2),
4959 'D/G/pi' : Item(status=' ', wc_rev=2),
4960 'D/G/rho' : Item(status='M ', wc_rev=2),
4961 'D/G/tau' : Item(status=' ', wc_rev=2),
4962 'D/gamma' : Item(status=' ', wc_rev=2),
4963 'D/H' : Item(status=' ', wc_rev=2),
4964 'D/H/chi' : Item(status=' ', wc_rev=2),
4965 'D/H/psi' : Item(status='M ', wc_rev=2),
4966 'D/H/omega' : Item(status=' ', wc_rev=2),
4968 expected_disk = wc.State('', {
4969 '' : Item(props={SVN_PROP_MERGEINFO : '/A:3'}),
4970 'B' : Item(props={SVN_PROP_MERGEINFO : '/A/B:3,5'}),
4971 'mu' : Item("This is the file 'mu'.\n"),
4972 'B/E' : Item(),
4973 'B/E/alpha' : Item("This is the file 'alpha'.\n"),
4974 'B/E/beta' : Item("New content"),
4975 'B/lambda' : Item("This is the file 'lambda'.\n"),
4976 'B/F' : Item(),
4977 'C' : Item(),
4978 'D' : Item(props={SVN_PROP_MERGEINFO : '/A/D:3-4'}),
4979 'D/G' : Item(),
4980 'D/G/pi' : Item("This is the file 'pi'.\n"),
4981 'D/G/rho' : Item("New content"),
4982 'D/G/tau' : Item("This is the file 'tau'.\n"),
4983 'D/gamma' : Item("This is the file 'gamma'.\n"),
4984 'D/H' : Item(),
4985 'D/H/chi' : Item("This is the file 'chi'.\n"),
4986 'D/H/psi' : Item("New content"),
4987 'D/H/omega' : Item("This is the file 'omega'.\n"),
4989 expected_skip = wc.State(short_A_COPY_path, { })
4990 saved_cwd = os.getcwd()
4991 os.chdir(svntest.main.work_dir)
4992 svntest.actions.run_and_verify_merge(short_A_COPY_path, '2', '3',
4993 sbox.repo_url + \
4994 '/A',
4995 expected_output,
4996 expected_disk,
4997 expected_status,
4998 expected_skip,
4999 None, None, None, None,
5000 None, 1)
5001 os.chdir(saved_cwd)
5003 # Merge r6 into A_COPY/D/H/omega, it should inherit it's nearest
5004 # ancestor's (A_COPY/D) mergeinfo (Issue #2733 with a file as the
5005 # merge target).
5006 short_omega_COPY_path = shorten_path_kludge(omega_COPY_path)
5007 expected_skip = wc.State(short_omega_COPY_path, { })
5008 saved_cwd = os.getcwd()
5009 os.chdir(svntest.main.work_dir)
5010 # run_and_verify_merge doesn't support merging to a file WCPATH
5011 # so use run_and_verify_svn.
5012 svntest.actions.run_and_verify_svn(None,
5013 expected_merge_output([[6]],
5014 'U ' + short_omega_COPY_path + '\n'),
5015 [], 'merge', '-c6',
5016 sbox.repo_url + '/A/D/H/omega',
5017 short_omega_COPY_path)
5018 os.chdir(saved_cwd)
5020 # Check that mergeinfo was properly set on A_COPY/D/H/omega
5021 svntest.actions.run_and_verify_svn(None,
5022 ["/A/D/H/omega:3-4,6\n"],
5024 'propget', SVN_PROP_MERGEINFO,
5025 omega_COPY_path)
5027 # Given a merge target *without* any of the following:
5029 # 1) Explicit mergeinfo set on itself in the WC
5030 # 2) Any WC ancestor to inherit mergeinfo from
5031 # 3) Any mergeinfo for the target in the repository
5033 # Check that the target still inherits mergeinfo from it's nearest
5034 # repository ancestor.
5036 # Commit all the merges thus far
5037 expected_output = wc.State(wc_dir, {
5038 'A_COPY' : Item(verb='Sending'),
5039 'A_COPY/B' : Item(verb='Sending'),
5040 'A_COPY/B/E/beta' : Item(verb='Sending'),
5041 'A_COPY/D' : Item(verb='Sending'),
5042 'A_COPY/D/G/rho' : Item(verb='Sending'),
5043 'A_COPY/D/H/omega' : Item(verb='Sending'),
5044 'A_COPY/D/H/psi' : Item(verb='Sending'),
5046 wc_status.tweak('A_COPY', 'A_COPY/B', 'A_COPY/B/E/beta', 'A_COPY/D',
5047 'A_COPY/D/G/rho', 'A_COPY/D/H/omega', 'A_COPY/D/H/psi',
5048 wc_rev=7)
5049 svntest.actions.run_and_verify_commit(wc_dir,
5050 expected_output,
5051 wc_status,
5052 None,
5053 wc_dir)
5055 # Copy the subtree A_COPY/B/E from the working copy, making the
5056 # disconnected WC E_only.
5057 other_wc = sbox.add_wc_path('E_only')
5058 svntest.actions.duplicate_dir(E_COPY_path, other_wc)
5060 # Update the disconnected WC it so it will get the most recent mergeinfo
5061 # from the repos when merging.
5062 svntest.actions.run_and_verify_svn(None, ["At revision 7.\n"], [], 'up',
5063 other_wc)
5065 # Merge r5:4 into the root of the disconnected WC.
5066 # E_only has no explicit mergeinfo and since it's the root of the WC
5067 # cannot inherit and mergeinfo from a working copy ancestor path. Nor
5068 # does it have any mergeinfo explicitly set on it in the repository.
5069 # An ancestor path on the repository side, A_COPY/B does have the merge
5070 # info '/A/B:1,3,5' however and E_only should inherit this, resulting in
5071 # mergeinfo of 'A/B/E:1,3' after the removal of r5.
5072 short_other_wc_path = shorten_path_kludge(other_wc)
5073 expected_output = wc.State(short_other_wc_path,
5074 {'beta' : Item(status='U ')})
5075 expected_status = wc.State(short_other_wc_path, {
5076 '' : Item(status=' M', wc_rev=7),
5077 'alpha' : Item(status=' ', wc_rev=7),
5078 'beta' : Item(status='M ', wc_rev=7),
5080 expected_disk = wc.State('', {
5081 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B/E:3'}),
5082 'alpha' : Item("This is the file 'alpha'.\n"),
5083 'beta' : Item("This is the file 'beta'.\n"),
5085 expected_skip = wc.State(short_other_wc_path, { })
5087 os.chdir(svntest.main.work_dir)
5088 svntest.actions.run_and_verify_merge(short_other_wc_path, '5', '4',
5089 sbox.repo_url + \
5090 '/A/B/E',
5091 expected_output,
5092 expected_disk,
5093 expected_status,
5094 expected_skip,
5095 None, None, None, None,
5096 None, 1)
5098 def mergeinfo_elision(sbox):
5099 "mergeinfo elides to ancestor with identical info"
5101 # When a merge would result in mergeinfo on a target which is identical
5102 # to mergeinfo (local or committed) on one of the node's ancestors (the
5103 # nearest ancestor takes precedence), then the mergeinfo elides from the
5104 # target to the nearest ancestor (e.g. no mergeinfo is set on the target
5105 # or committed mergeinfo is removed).
5107 sbox.build()
5108 wc_dir = sbox.wc_dir
5109 wc_disk, wc_status = set_up_branch(sbox)
5111 # Some paths we'll care about
5112 A_COPY_path = os.path.join(wc_dir, "A_COPY")
5113 beta_COPY_path = os.path.join(wc_dir, "A_COPY", "B", "E", "beta")
5114 G_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "G")
5116 # Now start merging...
5118 # Merge r5 into A_COPY/B/E/beta.
5119 short_beta_COPY_path = shorten_path_kludge(beta_COPY_path)
5120 expected_skip = wc.State(short_beta_COPY_path, { })
5121 saved_cwd = os.getcwd()
5123 os.chdir(svntest.main.work_dir)
5124 # run_and_verify_merge doesn't support merging to a file WCPATH
5125 # so use run_and_verify_svn.
5126 svntest.actions.run_and_verify_svn(None,
5127 expected_merge_output([[5]],
5128 'U ' + short_beta_COPY_path + '\n'),
5129 [], 'merge', '-c5',
5130 sbox.repo_url + '/A/B/E/beta',
5131 short_beta_COPY_path)
5132 os.chdir(saved_cwd)
5134 # Check beta's status and props.
5135 expected_status = wc.State(beta_COPY_path, {
5136 '' : Item(status='MM', wc_rev=2),
5138 svntest.actions.run_and_verify_status(beta_COPY_path, expected_status)
5140 svntest.actions.run_and_verify_svn(None, ["/A/B/E/beta:5\n"], [],
5141 'propget', SVN_PROP_MERGEINFO,
5142 beta_COPY_path)
5144 # Commit the merge
5145 expected_output = wc.State(wc_dir, {
5146 'A_COPY/B/E/beta' : Item(verb='Sending'),
5148 wc_status.tweak('A_COPY/B/E/beta', wc_rev=7)
5149 svntest.actions.run_and_verify_commit(wc_dir,
5150 expected_output,
5151 wc_status,
5152 None,
5153 wc_dir)
5155 # Update A_COPY to get all paths to the same working revision.
5156 svntest.actions.run_and_verify_svn(None, ["At revision 7.\n"], [],
5157 'up', wc_dir)
5158 wc_status.tweak(wc_rev=7)
5160 # Merge r4 into A_COPY/D/G.
5161 # Search for the comment entitled "The Merge Kluge" elsewhere in
5162 # this file, to understand why we shorten and chdir() below.
5163 short_G_COPY_path = shorten_path_kludge(G_COPY_path)
5164 expected_output = wc.State(short_G_COPY_path, {
5165 'rho' : Item(status='U ')
5167 expected_status = wc.State(short_G_COPY_path, {
5168 '' : Item(status=' M', wc_rev=7),
5169 'pi' : Item(status=' ', wc_rev=7),
5170 'rho' : Item(status='M ', wc_rev=7),
5171 'tau' : Item(status=' ', wc_rev=7),
5173 expected_disk = wc.State('', {
5174 '' : Item(props={SVN_PROP_MERGEINFO : '/A/D/G:4'}),
5175 'pi' : Item("This is the file 'pi'.\n"),
5176 'rho' : Item("New content"),
5177 'tau' : Item("This is the file 'tau'.\n"),
5179 expected_skip = wc.State(short_G_COPY_path, { })
5181 os.chdir(svntest.main.work_dir)
5182 svntest.actions.run_and_verify_merge(short_G_COPY_path, '3', '4',
5183 sbox.repo_url + \
5184 '/A/D/G',
5185 expected_output,
5186 expected_disk,
5187 expected_status,
5188 expected_skip,
5189 None, None, None, None,
5190 None, 1)
5191 os.chdir(saved_cwd)
5193 # Merge r3:6 into A_COPY. This would result in identical mergeinfo
5194 # (r4-6) on A_COPY and two of it's descendants, A_COPY/D/G and
5195 # A_COPY/B/E/beta, so the mergeinfo on the latter two should elide
5196 # to A_COPY. In the case of A_COPY/D/G this means its wholly uncommitted
5197 # mergeinfo is removed leaving no prop mods. In the case of
5198 # A_COPY/B/E/beta its committed mergeinfo prop is removed leaving a prop
5199 # change.
5200 short_A_COPY_path = shorten_path_kludge(A_COPY_path)
5201 expected_output = wc.State(short_A_COPY_path, {
5202 'D/H/omega' : Item(status='U ')
5204 expected_status = wc.State(short_A_COPY_path, {
5205 '' : Item(status=' M', wc_rev=7),
5206 'B' : Item(status=' ', wc_rev=7),
5207 'mu' : Item(status=' ', wc_rev=7),
5208 'B/E' : Item(status=' ', wc_rev=7),
5209 'B/E/alpha' : Item(status=' ', wc_rev=7),
5210 'B/E/beta' : Item(status=' M', wc_rev=7),
5211 'B/lambda' : Item(status=' ', wc_rev=7),
5212 'B/F' : Item(status=' ', wc_rev=7),
5213 'C' : Item(status=' ', wc_rev=7),
5214 'D' : Item(status=' ', wc_rev=7),
5215 'D/G' : Item(status=' ', wc_rev=7),
5216 'D/G/pi' : Item(status=' ', wc_rev=7),
5217 'D/G/rho' : Item(status='M ', wc_rev=7),
5218 'D/G/tau' : Item(status=' ', wc_rev=7),
5219 'D/gamma' : Item(status=' ', wc_rev=7),
5220 'D/H' : Item(status=' ', wc_rev=7),
5221 'D/H/chi' : Item(status=' ', wc_rev=7),
5222 'D/H/psi' : Item(status=' ', wc_rev=7),
5223 'D/H/omega' : Item(status='M ', wc_rev=7),
5225 expected_disk = wc.State('', {
5226 '' : Item(props={SVN_PROP_MERGEINFO : '/A:4-6'}),
5227 'B' : Item(),
5228 'mu' : Item("This is the file 'mu'.\n"),
5229 'B/E' : Item(),
5230 'B/E/alpha' : Item("This is the file 'alpha'.\n"),
5231 'B/E/beta' : Item("New content"),
5232 'B/lambda' : Item("This is the file 'lambda'.\n"),
5233 'B/F' : Item(),
5234 'C' : Item(),
5235 'D' : Item(),
5236 'D/G' : Item(),
5237 'D/G/pi' : Item("This is the file 'pi'.\n"),
5238 'D/G/rho' : Item("New content"),
5239 'D/G/tau' : Item("This is the file 'tau'.\n"),
5240 'D/gamma' : Item("This is the file 'gamma'.\n"),
5241 'D/H' : Item(),
5242 'D/H/chi' : Item("This is the file 'chi'.\n"),
5243 'D/H/psi' : Item("This is the file 'psi'.\n"),
5244 'D/H/omega' : Item("New content"),
5246 expected_skip = wc.State(short_A_COPY_path, { })
5248 os.chdir(svntest.main.work_dir)
5249 svntest.actions.run_and_verify_merge(short_A_COPY_path, '3', '6',
5250 sbox.repo_url + \
5251 '/A',
5252 expected_output,
5253 expected_disk,
5254 expected_status,
5255 expected_skip,
5256 None, None, None, None,
5257 None, 1)
5258 os.chdir(saved_cwd)
5260 # Reverse merge r5 out of A_COPY/B/E/beta. The mergeinfo on
5261 # A_COPY/B/E/beta which previously elided will now return,
5262 # minus r5 of course.
5263 expected_skip = wc.State(short_beta_COPY_path, { })
5265 os.chdir(svntest.main.work_dir)
5266 # run_and_verify_merge doesn't support merging to a file WCPATH
5267 # so use run_and_verify_svn.
5268 svntest.actions.run_and_verify_svn(None,
5269 expected_merge_output([[-5]],
5270 'U ' + short_beta_COPY_path + '\n'),
5271 [], 'merge', '-c-5',
5272 sbox.repo_url + '/A/B/E/beta',
5273 short_beta_COPY_path)
5274 os.chdir(saved_cwd)
5276 # Check beta's status and props.
5277 expected_status = wc.State(beta_COPY_path, {
5278 '' : Item(status='MM', wc_rev=7),
5280 svntest.actions.run_and_verify_status(beta_COPY_path, expected_status)
5282 svntest.actions.run_and_verify_svn(None, ["/A/B/E/beta:4,6\n"], [],
5283 'propget', SVN_PROP_MERGEINFO,
5284 beta_COPY_path)
5286 # Merge r5 back into A_COPY/B/E/beta. Now the mergeinfo on the merge
5287 # target (A_COPY/B/E/beta) is identical to it's nearest ancestor with
5288 # mergeinfo (A_COPY) and so the former should elide.
5289 os.chdir(svntest.main.work_dir)
5290 # run_and_verify_merge doesn't support merging to a file WCPATH
5291 # so use run_and_verify_svn.
5292 svntest.actions.run_and_verify_svn(None,
5293 expected_merge_output([[5]],
5294 'G ' + short_beta_COPY_path + '\n'),
5295 [], 'merge', '-c5',
5296 sbox.repo_url + '/A/B/E/beta',
5297 short_beta_COPY_path)
5298 os.chdir(saved_cwd)
5300 # Check beta's status and props.
5301 expected_status = wc.State(beta_COPY_path, {
5302 '' : Item(status=' M', wc_rev=7),
5304 svntest.actions.run_and_verify_status(beta_COPY_path, expected_status)
5306 # Once again A_COPY/B/E/beta has no mergeinfo.
5307 svntest.actions.run_and_verify_svn(None, [], [],
5308 'propget', SVN_PROP_MERGEINFO,
5309 beta_COPY_path)
5311 def mergeinfo_inheritance_and_discontinuous_ranges(sbox):
5312 "discontinuous merges produce correct mergeinfo"
5314 # When a merge target has no explicit mergeinfo and is subject
5315 # to multiple merges, the resulting mergeinfo on the target
5316 # should reflect the combination of the inherited mergeinfo
5317 # with each merge performed.
5319 # Also tests implied merge source and target when only a revision
5320 # range is specified.
5322 sbox.build()
5323 wc_dir = sbox.wc_dir
5325 # Some paths we'll care about
5326 A_url = sbox.repo_url + '/A'
5327 A_COPY_path = os.path.join(wc_dir, "A_COPY")
5328 D_COPY_path = os.path.join(wc_dir, "A_COPY", "D")
5329 A_COPY_rho_path = os.path.join(wc_dir, "A_COPY", "D", "G", "rho")
5331 expected_disk, expected_status = set_up_branch(sbox)
5333 # Merge r4 into A_COPY
5334 saved_cwd = os.getcwd()
5336 os.chdir(A_COPY_path)
5337 svntest.actions.run_and_verify_svn(None,
5338 expected_merge_output([[4]], 'U ' +
5339 os.path.join("D", "G", "rho") + '\n'),
5340 [], 'merge', '-c4', A_url)
5341 os.chdir(saved_cwd)
5343 # Check the results of the merge.
5344 expected_status.tweak("A_COPY", status=' M')
5345 expected_status.tweak("A_COPY/D/G/rho", status='M ')
5346 svntest.actions.run_and_verify_status(wc_dir, expected_status)
5347 svntest.actions.run_and_verify_svn(None, ["/A:4\n"], [],
5348 'propget', SVN_PROP_MERGEINFO,
5349 A_COPY_path)
5351 # Merge r2:6 into A_COPY/D
5353 # Search for the comment entitled "The Merge Kluge" elsewhere in
5354 # this file, to understand why we shorten and chdir() below.
5356 # A_COPY/D should inherit the mergeinfo '/A:4' from A_COPY
5357 # combine it with the discontinous merges performed directly on
5358 # it (A/D/ 2:3 and A/D 4:6) resulting in '/A/D:3-6'.
5359 short_D_COPY_path = shorten_path_kludge(D_COPY_path)
5360 expected_output = wc.State(short_D_COPY_path, {
5361 'H/psi' : Item(status='U '),
5362 'H/omega' : Item(status='U '),
5364 expected_status = wc.State(short_D_COPY_path, {
5365 '' : Item(status=' M', wc_rev=2),
5366 'G' : Item(status=' ', wc_rev=2),
5367 'G/pi' : Item(status=' ', wc_rev=2),
5368 'G/rho' : Item(status='M ', wc_rev=2),
5369 'G/tau' : Item(status=' ', wc_rev=2),
5370 'H' : Item(status=' ', wc_rev=2),
5371 'H/chi' : Item(status=' ', wc_rev=2),
5372 'H/psi' : Item(status='M ', wc_rev=2),
5373 'H/omega' : Item(status='M ', wc_rev=2),
5374 'gamma' : Item(status=' ', wc_rev=2),
5376 expected_disk = wc.State('', {
5377 '' : Item(props={SVN_PROP_MERGEINFO : '/A/D:3-6'}),
5378 'G' : Item(),
5379 'G/pi' : Item("This is the file 'pi'.\n"),
5380 'G/rho' : Item("New content"),
5381 'G/tau' : Item("This is the file 'tau'.\n"),
5382 'H' : Item(),
5383 'H/chi' : Item("This is the file 'chi'.\n"),
5384 'H/psi' : Item("New content"),
5385 'H/omega' : Item("New content"),
5386 'gamma' : Item("This is the file 'gamma'.\n")
5388 expected_skip = wc.State(short_D_COPY_path, { })
5390 os.chdir(svntest.main.work_dir)
5391 svntest.actions.run_and_verify_merge(short_D_COPY_path, '2', '6',
5392 sbox.repo_url + '/A/D',
5393 expected_output,
5394 expected_disk,
5395 expected_status,
5396 expected_skip,
5397 None, None, None, None,
5398 None, 1)
5399 os.chdir(saved_cwd)
5401 # Wipe the memory of a portion of the previous merge...
5402 ### It'd be nice to use 'merge --record-only' here, but we can't (yet)
5403 ### wipe all ranges for a file due to the bug pointed out in r24645.
5404 mu_copy_path = os.path.join(A_COPY_path, 'mu')
5405 svntest.actions.run_and_verify_svn(None,
5406 ["property '" + SVN_PROP_MERGEINFO
5407 + "' set on '" +
5408 mu_copy_path + "'\n"], [], 'propset',
5409 SVN_PROP_MERGEINFO, '', mu_copy_path)
5410 # ...and confirm that we can commit the wiped mergeinfo...
5411 expected_output = wc.State(wc_dir, {
5412 'A_COPY/mu' : Item(verb='Sending'),
5414 svntest.actions.run_and_verify_commit(wc_dir,
5415 expected_output,
5416 None,
5417 None,
5418 mu_copy_path)
5419 # ...and that the presence of the property is retained, even when
5420 # the value has been wiped.
5421 svntest.actions.run_and_verify_svn(None, ['\n'], [], 'propget',
5422 SVN_PROP_MERGEINFO, mu_copy_path)
5424 def merge_to_target_with_copied_children(sbox):
5425 "merge works when target has copied children"
5427 # Test for Issue #2754 Can't merge to target with copied/moved children
5429 sbox.build()
5430 wc_dir = sbox.wc_dir
5431 expected_disk, expected_status = set_up_branch(sbox)
5433 # Some paths we'll care about
5434 D_COPY_path = os.path.join(wc_dir, "A_COPY", "D")
5435 G_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "G")
5436 rho_COPY_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "G", "rho_copy")
5438 # URL to URL copy A_COPY/D/G/rho to A_COPY/D/G/rho_copy
5439 svntest.actions.run_and_verify_svn(None, None, [], 'copy',
5440 sbox.repo_url + '/A_COPY/D/G/rho',
5441 sbox.repo_url + '/A_COPY/D/G/rho_copy',
5442 '-m', 'copy')
5444 # Update WC.
5445 expected_output = wc.State(wc_dir,
5446 {'A_COPY/D/G/rho_copy' : Item(status='A ')})
5447 expected_disk.add({
5448 'A_COPY/D/G/rho_copy' : Item("This is the file 'rho'.\n", props={})
5450 expected_status.tweak(wc_rev=7)
5451 expected_status.add({'A_COPY/D/G/rho_copy' : Item(status=' ', wc_rev=7)})
5452 svntest.actions.run_and_verify_update(wc_dir,
5453 expected_output,
5454 expected_disk,
5455 expected_status,
5456 None, None, None,
5457 None, None, 1)
5459 # Merge r4 into A_COPY/D/G/rho_copy.
5461 # Search for the comment entitled "The Merge Kluge" elsewhere in
5462 # this file, to understand why we shorten and chdir() below.
5463 os.chdir(svntest.main.work_dir)
5464 short_rho_COPY_COPY_path = shorten_path_kludge(rho_COPY_COPY_path)
5465 svntest.actions.run_and_verify_svn(None,
5466 expected_merge_output([[4]],
5467 'U ' + short_rho_COPY_COPY_path +
5468 '\n'),
5469 [], 'merge', '-c4',
5470 sbox.repo_url + '/A/D/G/rho',
5471 short_rho_COPY_COPY_path)
5473 # Merge r3:5 into A_COPY/D/G.
5474 short_G_COPY_path = shorten_path_kludge(G_COPY_path)
5475 expected_output = wc.State(short_G_COPY_path, {
5476 'rho' : Item(status='U ')
5478 expected_status = wc.State(short_G_COPY_path, {
5479 '' : Item(status=' M', wc_rev=7),
5480 'pi' : Item(status=' ', wc_rev=7),
5481 'rho' : Item(status='M ', wc_rev=7),
5482 'rho_copy' : Item(status='MM', wc_rev=7),
5483 'tau' : Item(status=' ', wc_rev=7),
5485 expected_disk = wc.State('', {
5486 '' : Item(props={SVN_PROP_MERGEINFO : '/A/D/G:4-5'}),
5487 'pi' : Item("This is the file 'pi'.\n"),
5488 'rho' : Item("New content"),
5489 'rho_copy' : Item("New content",
5490 props={SVN_PROP_MERGEINFO : '/A/D/G/rho:4'}),
5491 'tau' : Item("This is the file 'tau'.\n"),
5493 expected_skip = wc.State(short_G_COPY_path, { })
5494 svntest.actions.run_and_verify_merge(short_G_COPY_path, '3', '5',
5495 sbox.repo_url + \
5496 '/A/D/G',
5497 expected_output,
5498 expected_disk,
5499 expected_status,
5500 expected_skip,
5501 None, None, None, None,
5502 None, 1)
5504 def merge_to_switched_path(sbox):
5505 "merge to switched path does not inherit or elide"
5507 # When the target of a merge is a switched path we don't inherit WC
5508 # mergeinfo from above the target or attempt to elide the mergeinfo
5509 # set on the target as a result of the merge.
5511 sbox.build()
5512 wc_dir = sbox.wc_dir
5513 wc_disk, wc_status = set_up_branch(sbox)
5515 # Some paths we'll care about
5516 A_COPY_D_path = os.path.join(wc_dir, "A_COPY", "D")
5517 G_COPY_path = os.path.join(wc_dir, "A", "D", "G_COPY")
5518 A_COPY_D_G_path = os.path.join(wc_dir, "A_COPY", "D", "G")
5519 A_COPY_D_G_rho_path = os.path.join(wc_dir, "A_COPY", "D", "G", "rho")
5521 expected = svntest.verify.UnorderedOutput(
5522 ["A " + os.path.join(G_COPY_path, "pi") + "\n",
5523 "A " + os.path.join(G_COPY_path, "rho") + "\n",
5524 "A " + os.path.join(G_COPY_path, "tau") + "\n",
5525 "Checked out revision 6.\n",
5526 "A " + G_COPY_path + "\n"])
5528 # r7 - Copy A/D/G to A/D/G_COPY and commit.
5529 svntest.actions.run_and_verify_svn(None, expected, [], 'copy',
5530 sbox.repo_url + "/A/D/G",
5531 G_COPY_path)
5533 expected_output = wc.State(wc_dir, {'A/D/G_COPY' : Item(verb='Adding')})
5534 wc_status.add({
5535 "A/D/G_COPY" : Item(status=' ', wc_rev=7),
5536 "A/D/G_COPY/pi" : Item(status=' ', wc_rev=7),
5537 "A/D/G_COPY/rho" : Item(status=' ', wc_rev=7),
5538 "A/D/G_COPY/tau" : Item(status=' ', wc_rev=7),
5541 svntest.actions.run_and_verify_commit(wc_dir, expected_output, wc_status,
5542 None, wc_dir)
5544 # r8 - modify and commit A/D/G_COPY/rho
5545 svntest.main.file_write(os.path.join(wc_dir, "A", "D", "G_COPY", "rho"),
5546 "New *and* improved rho content")
5547 expected_output = wc.State(wc_dir, {'A/D/G_COPY/rho' : Item(verb='Sending')})
5548 wc_status.tweak('A/D/G_COPY/rho', wc_rev=8)
5549 svntest.actions.run_and_verify_commit(wc_dir, expected_output, wc_status,
5550 None, wc_dir)
5552 # Switch A_COPY/D/G to A/D/G.
5553 wc_disk.add({
5554 "A" : Item(),
5555 "A/D/G_COPY" : Item(),
5556 "A/D/G_COPY/pi" : Item("This is the file 'pi'.\n"),
5557 "A/D/G_COPY/rho" : Item("New *and* improved rho content"),
5558 "A/D/G_COPY/tau" : Item("This is the file 'tau'.\n"),
5560 wc_disk.tweak('A_COPY/D/G/rho',contents="New content")
5561 wc_status.tweak("A_COPY/D/G", wc_rev=8, switched='S')
5562 wc_status.tweak("A_COPY/D/G/pi", wc_rev=8)
5563 wc_status.tweak("A_COPY/D/G/rho", wc_rev=8)
5564 wc_status.tweak("A_COPY/D/G/tau", wc_rev=8)
5565 expected_output = svntest.wc.State(sbox.wc_dir, {
5566 "A_COPY/D/G/rho" : Item(status='U '),
5568 svntest.actions.run_and_verify_switch(sbox.wc_dir, A_COPY_D_G_path,
5569 sbox.repo_url + "/A/D/G",
5570 expected_output, wc_disk, wc_status,
5571 None, None, None, None, None, 1)
5573 # Merge r8 from A/D/G_COPY into our switched target A_COPY/D/G.
5574 # A_COPY/D/G should get mergeinfo for r8 as a result of the merge,
5575 # but because it's switched should not inherit the mergeinfo from
5576 # its nearest WC ancestor with mergeinfo (A_COPY: svn:mergeinfo : /A:1)
5578 # Search for the comment entitled "The Merge Kluge" elsewhere in
5579 # this file, to understand why we shorten and chdir() below.
5580 short_G_COPY_path = shorten_path_kludge(A_COPY_D_G_path)
5581 expected_output = wc.State(short_G_COPY_path, {
5582 'rho' : Item(status='U ')
5584 # Note: A_COPY/D/G won't show as switched because of the Merge Kluge.
5585 expected_status = wc.State(short_G_COPY_path, {
5586 '' : Item(status=' M', wc_rev=8),
5587 'pi' : Item(status=' ', wc_rev=8),
5588 'rho' : Item(status='M ', wc_rev=8),
5589 'tau' : Item(status=' ', wc_rev=8),
5591 expected_disk = wc.State('', {
5592 '' : Item(props={SVN_PROP_MERGEINFO : '/A/D/G_COPY:8'}),
5593 'pi' : Item("This is the file 'pi'.\n"),
5594 'rho' : Item("New *and* improved rho content"),
5595 'tau' : Item("This is the file 'tau'.\n"),
5597 expected_skip = wc.State(short_G_COPY_path, { })
5598 saved_cwd = os.getcwd()
5600 os.chdir(svntest.main.work_dir)
5601 svntest.actions.run_and_verify_merge(short_G_COPY_path, '7', '8',
5602 sbox.repo_url + '/A/D/G_COPY',
5603 expected_output, expected_disk,
5604 expected_status, expected_skip,
5605 None, None, None, None, None, 1)
5606 os.chdir(saved_cwd)
5608 # Check that the mergeinfo set on a target doesn't elide when that
5609 # target is switched.
5611 # Revert the previous merge and manually set 'svn:mergeinfo : '
5612 # on 'merge_tests-1\A_COPY\D'. Now merge -c-4 from /A/D/G into A_COPY/D/G.
5613 # This should still set 'svn:mergeinfo : ' on
5614 # 'merge_tests-1\A_COPY\D\G'. This would normally elide to A_COPY/D,
5615 # but since A_COPY/D/G is switched it should not.
5616 svntest.actions.run_and_verify_svn(None,
5617 ["Reverted '" + A_COPY_D_G_path+ "'\n",
5618 "Reverted '" + A_COPY_D_G_rho_path +
5619 "'\n"],
5620 [], 'revert', '-R', wc_dir)
5621 svntest.actions.run_and_verify_svn(None,
5622 ["property '" + SVN_PROP_MERGEINFO +
5623 "' set on '" + A_COPY_D_path+ "'" +
5624 "\n"], [], 'ps', SVN_PROP_MERGEINFO,
5625 '', A_COPY_D_path)
5626 svntest.actions.run_and_verify_svn(None,
5627 expected_merge_output([[-4]],
5628 'U ' + A_COPY_D_G_rho_path + '\n'),
5629 [], 'merge', '-c-4',
5630 sbox.repo_url + '/A/D/G_COPY',
5631 A_COPY_D_G_path)
5632 wc_status.tweak("A_COPY/D", status=' M')
5633 wc_status.tweak("A_COPY/D/G", status=' M')
5634 wc_status.tweak("A_COPY/D/G/rho", status='M ')
5635 svntest.actions.run_and_verify_status(wc_dir, wc_status)
5636 expected = svntest.verify.UnorderedOutput(
5637 ["A " + os.path.join(G_COPY_path, "pi") + "\n",
5638 "A " + os.path.join(G_COPY_path, "rho") + "\n",
5639 "A " + os.path.join(G_COPY_path, "tau") + "\n",
5640 "Checked out revision 6.\n",
5641 "A " + G_COPY_path + "\n"])
5642 expected = svntest.verify.UnorderedOutput(
5643 ["Properties on '" + A_COPY_D_path + "':\n",
5644 " " + SVN_PROP_MERGEINFO + " : \n",
5645 "Properties on '" + A_COPY_D_G_path + "':\n",
5646 " " + SVN_PROP_MERGEINFO +" : \n"])
5647 svntest.actions.run_and_verify_svn(None,
5648 expected, [],
5649 'pl', '-vR', A_COPY_D_path)
5651 # Test for issues
5653 # 2823: Account for mergeinfo differences for switched
5654 # directories when gathering mergeinfo
5656 # 2839: Support non-inheritable mergeinfo revision ranges
5657 def merge_to_path_with_switched_children(sbox):
5658 "merge to path with switched children"
5660 # Merging to a target with switched children requires special handling
5661 # to keep mergeinfo correct:
5663 # 1) If the target of a merge has switched children without explicit
5664 # mergeinfo, the switched children should get mergeinfo set on
5665 # them as a result of the merge. This mergeinfo includes the
5666 # mergeinfo resulting from the merge *and* any mergeinfo inherited
5667 # from the repos for the switched path.
5669 # 2) Mergeinfo on switched children should never elide.
5671 # 3) The path the switched child overrides cannot be modified by the
5672 # merge (it isn't present in the WC) so should not inherit any
5673 # mergeinfo added as a result of the merge. To prevent this, the
5674 # immediate parent of any switched child should have non-inheritable
5675 # mergeinfo added/modified for the merge performed.
5677 # 4) Because of 3, siblings of switched children will not inherit the
5678 # mergeinfo resulting from the merge, so must get their own, full set
5679 # of mergeinfo.
5681 sbox.build()
5682 wc_dir = sbox.wc_dir
5683 wc_disk, wc_status = set_up_branch(sbox, False, 3)
5685 # Some paths we'll care about
5686 D_path = os.path.join(wc_dir, "A", "D")
5687 A_COPY_path = os.path.join(wc_dir, "A_COPY")
5688 A_COPY_beta_path = os.path.join(wc_dir, "A_COPY", "B", "E", "beta")
5689 A_COPY_chi_path = os.path.join(wc_dir, "A_COPY", "D", "H", "chi")
5690 A_COPY_omega_path = os.path.join(wc_dir, "A_COPY", "D", "H", "omega")
5691 A_COPY_psi_path = os.path.join(wc_dir, "A_COPY", "D", "H", "psi")
5692 A_COPY_G_path = os.path.join(wc_dir, "A_COPY", "D", "G")
5693 A_COPY_rho_path = os.path.join(wc_dir, "A_COPY", "D", "G", "rho")
5694 A_COPY_H_path = os.path.join(wc_dir, "A_COPY", "D", "H")
5695 A_COPY_D_path = os.path.join(wc_dir, "A_COPY", "D")
5696 A_COPY_gamma_path = os.path.join(wc_dir, "A_COPY", "D", "gamma")
5697 H_COPY_2_path = os.path.join(wc_dir, "A_COPY_2", "D", "H")
5699 svntest.actions.run_and_verify_svn(None, ["At revision 8.\n"], [], 'up',
5700 wc_dir)
5701 wc_status.tweak(wc_rev=8)
5703 # Switch a file and dir path in the branch:
5705 # Switch A_COPY/D/G to A_COPY_2/D/G.
5706 wc_status.tweak("A_COPY/D/G", switched='S')
5707 expected_output = svntest.wc.State(sbox.wc_dir, {})
5708 svntest.actions.run_and_verify_switch(sbox.wc_dir, A_COPY_G_path,
5709 sbox.repo_url + "/A_COPY_2/D/G",
5710 expected_output, wc_disk, wc_status,
5711 None, None, None, None, None, 1)
5713 # Switch A_COPY/D/G/rho to A_COPY_3/D/G/rho.
5714 wc_status.tweak("A_COPY/D/G/rho", switched='S')
5715 expected_output = svntest.wc.State(sbox.wc_dir, {})
5716 svntest.actions.run_and_verify_switch(sbox.wc_dir, A_COPY_rho_path,
5717 sbox.repo_url + "/A_COPY_3/D/G/rho",
5718 expected_output, wc_disk, wc_status,
5719 None, None, None, None, None, 1)
5721 # Switch A_COPY/D/H/psi to A_COPY_2/D/H/psi.
5722 wc_status.tweak("A_COPY/D/H/psi", switched='S')
5723 expected_output = svntest.wc.State(sbox.wc_dir, {})
5724 svntest.actions.run_and_verify_switch(sbox.wc_dir, A_COPY_psi_path,
5725 sbox.repo_url + "/A_COPY_2/D/H/psi",
5726 expected_output, wc_disk, wc_status,
5727 None, None, None, None, None, 1)
5729 # Target with switched file child:
5731 # Merge r8 from A/D/H into A_COPY/D/H. The switched child of
5732 # A_COPY/D/H, file A_COPY/D/H/psi (which has no mergeinfo prior
5733 # to the merge), should get their own mergeinfo, both r8 from the
5734 # merge itself, and r1-2 inherited from A_COPY_2 in the repository.
5736 # A_COPY/D/H/psi's parent A_COPY/D/H has no pre-exiting explicit
5737 # mergeinfo so should get its own mergeinfo, made up of r1 inherited
5738 # from A_COPY and the non-inheritable r8 resulting from the merge.
5740 # A_COPY/D/H/psi's two unswitched siblings, A_COPY/D/H/chi and
5741 # A_COPY/D/H/omega won't inherit r8 from A_COPY/D/H, so need their
5742 # own mergeinfo.
5744 # Search for the comment entitled "The Merge Kluge" elsewhere in
5745 # this file, to understand why we shorten and chdir() below.
5746 short_H_COPY_path = shorten_path_kludge(A_COPY_H_path)
5747 expected_output = wc.State(short_H_COPY_path, {
5748 'omega' : Item(status='U ')
5750 expected_status = wc.State(short_H_COPY_path, {
5751 '' : Item(status=' M', wc_rev=8),
5752 'psi' : Item(status=' M', wc_rev=8, switched='S'),
5753 'omega' : Item(status='MM', wc_rev=8),
5754 'chi' : Item(status=' M', wc_rev=8),
5756 expected_disk = wc.State('', {
5757 '' : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:8*'}),
5758 'psi' : Item("This is the file 'psi'.\n",
5759 props={SVN_PROP_MERGEINFO : '/A/D/H/psi:8'}),
5760 'omega' : Item("New content",
5761 props={SVN_PROP_MERGEINFO : '/A/D/H/omega:8'}),
5762 'chi' : Item("This is the file 'chi'.\n",
5763 props={SVN_PROP_MERGEINFO : '/A/D/H/chi:8'}),
5765 expected_skip = wc.State(short_H_COPY_path, { })
5766 saved_cwd = os.getcwd()
5768 os.chdir(svntest.main.work_dir)
5770 svntest.actions.run_and_verify_merge(short_H_COPY_path, '7', '8',
5771 sbox.repo_url + '/A/D/H',
5772 expected_output, expected_disk,
5773 expected_status, expected_skip,
5774 None, None, None, None, None, 1)
5775 os.chdir(saved_cwd)
5777 # Target with switched dir child:
5779 # Merge r6 from A/D into A_COPY/D. The switched child of A_COPY/D,
5780 # directory A_COPY/D/G (which has no mergeinfo prior to the merge),
5781 # should get its own mergeinfo, both for r6 from the merge itself and
5782 # r1-2 from the repository.
5784 # A_COPY/D/G's parent A_COPY/D has no pre-exiting explicit
5785 # mergeinfo so should get its own mergeinfo, made up of r1 inherited
5786 # from A_COPY and the non-inheritable r5 resulting from the merge.
5788 # A_COPY/D/G's two unswitched siblings, A_COPY/D/gamma and A_COPY/D/H
5789 # won't inherit r5 from A_COPY/D, so need their own mergeinfo
5790 # added/updated respectively. A_COPY/D/H itself has a switched child
5791 # so r5 is set as non-inheritable on it. All of A_COPY/D/H's children,
5792 # which have explict mergeinfo from the previous merge, get r5 in their
5793 # mergeinfo.
5794 short_D_COPY_path = shorten_path_kludge(A_COPY_D_path)
5795 expected_output = wc.State(short_D_COPY_path, {
5796 'G/rho' : Item(status='U ')
5798 expected_status_D = wc.State(short_D_COPY_path, {
5799 '' : Item(status=' M', wc_rev=8),
5800 'H' : Item(status=' M', wc_rev=8),
5801 'H/chi' : Item(status=' M', wc_rev=8),
5802 'H/omega' : Item(status='MM', wc_rev=8),
5803 'H/psi' : Item(status=' M', wc_rev=8, switched='S'),
5804 'G' : Item(status=' M', wc_rev=8, switched='S'),
5805 'G/pi' : Item(status=' M', wc_rev=8),
5806 'G/rho' : Item(status='MM', wc_rev=8, switched='S'),
5807 'G/tau' : Item(status=' M', wc_rev=8),
5808 'gamma' : Item(status=' M', wc_rev=8),
5810 expected_disk_D = wc.State('', {
5811 '' : Item(props={SVN_PROP_MERGEINFO : '/A/D:6*'}),
5812 'H' : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:6*,8*'}),
5813 'H/chi' : Item("This is the file 'chi'.\n",
5814 props={SVN_PROP_MERGEINFO : '/A/D/H/chi:6,8'}),
5815 'H/omega' : Item("New content",
5816 props={SVN_PROP_MERGEINFO : '/A/D/H/omega:6,8'}),
5817 'H/psi' : Item("This is the file 'psi'.\n",
5818 props={SVN_PROP_MERGEINFO : '/A/D/H/psi:6,8'}),
5819 'G' : Item(props={SVN_PROP_MERGEINFO : '/A/D/G:6*'}),
5820 'G/pi' : Item("This is the file 'pi'.\n",
5821 props={SVN_PROP_MERGEINFO : '/A/D/G/pi:6'}),
5822 'G/rho' : Item("New content",
5823 props={SVN_PROP_MERGEINFO : '/A/D/G/rho:6'}),
5824 'G/tau' : Item("This is the file 'tau'.\n",
5825 props={SVN_PROP_MERGEINFO : '/A/D/G/tau:6'}),
5826 'gamma' : Item("This is the file 'gamma'.\n",
5827 props={SVN_PROP_MERGEINFO : '/A/D/gamma:6'}),
5829 expected_skip_D = wc.State(short_D_COPY_path, { })
5830 os.chdir(svntest.main.work_dir)
5831 svntest.actions.run_and_verify_merge(short_D_COPY_path, '5', '6',
5832 sbox.repo_url + '/A/D',
5833 expected_output, expected_disk_D,
5834 expected_status_D, expected_skip_D,
5835 None, None, None, None, None, 1)
5837 # Merge r5 from A/D into A_COPY/D. A_COPY/D's switched child A_COPY/D/G
5838 # gets r5 as does A_COPY/D/G's unswitched sibling A_COPY/D/gamma. Since
5839 # A_COPY/D has a switched child, it gets an uninheritable r5. Every other
5840 # path with existing mergeinfo already has r8 so are unchanged.
5842 # A_COPY/D/H/chi and A_COPY/D/H/omega both have mergeinfo for r1,6,8 but
5843 # won't elide to A_COPY/D/H becuase the latter's mergeinfo, while
5844 # encompassing the same ranges, r1,6*,8*, has some non-inheritable ranges.
5845 # The same is true of A_COPY/D/gamma and A_COPY/D.
5846 expected_output = wc.State(short_D_COPY_path, {
5847 'H/psi' : Item(status='U ')})
5848 expected_disk_D.tweak('', props={SVN_PROP_MERGEINFO : '/A/D:5-6*'})
5849 expected_disk_D.tweak('G', props={SVN_PROP_MERGEINFO : '/A/D/G:5-6*'})
5850 expected_disk_D.tweak('G/pi',
5851 props={SVN_PROP_MERGEINFO : '/A/D/G/pi:5-6'})
5852 expected_disk_D.tweak('G/rho',
5853 props={SVN_PROP_MERGEINFO : '/A/D/G/rho:5-6'})
5854 expected_disk_D.tweak('G/tau',
5855 props={SVN_PROP_MERGEINFO : '/A/D/G/tau:5-6'})
5856 expected_disk_D.tweak('H', props={SVN_PROP_MERGEINFO : '/A/D/H:5-6*,8*'})
5857 expected_disk_D.tweak('gamma',
5858 props={SVN_PROP_MERGEINFO : '/A/D/gamma:5-6'})
5859 expected_disk_D.tweak('H/chi',
5860 props={SVN_PROP_MERGEINFO :'/A/D/H/chi:5-6,8'})
5861 expected_disk_D.tweak('H/psi', contents="New content",
5862 props={SVN_PROP_MERGEINFO :'/A/D/H/psi:5-6,8'})
5863 expected_disk_D.tweak('H/omega',
5864 props={SVN_PROP_MERGEINFO :'/A/D/H/omega:5-6,8'})
5865 expected_status_D.tweak('H/psi', status='MM')
5866 svntest.actions.run_and_verify_merge(short_D_COPY_path, '4', '5',
5867 sbox.repo_url + '/A/D',
5868 expected_output, expected_disk_D,
5869 expected_status_D, expected_skip_D,
5870 None, None, None, None, None, 1)
5871 os.chdir(saved_cwd)
5873 # Finally, merge r4:8 into A_COPY. A_COPY gets r5-8 added and every path
5874 # under it with with explicit mergeinfo gets r5,7 added (they all have r6,8
5875 # already). Again there is no elision, since the only possibilities, e.g.
5876 # A_COPY/D/H's '/A/D/H:1,5-8*' to A_COPY/D's '/A/D:1,5-8*', involve
5877 # non-inheritable mergeinfo one ond side or the other.
5878 short_A_COPY_path = shorten_path_kludge(A_COPY_path)
5879 expected_output = wc.State(short_A_COPY_path, {
5880 'B/E/beta' : Item(status='U ')
5882 expected_status = wc.State(short_A_COPY_path, {
5883 '' : Item(status=' M', wc_rev=8),
5884 'B' : Item(status=' ', wc_rev=8),
5885 'mu' : Item(status=' ', wc_rev=8),
5886 'B/E' : Item(status=' ', wc_rev=8),
5887 'B/E/alpha' : Item(status=' ', wc_rev=8),
5888 'B/E/beta' : Item(status='M ', wc_rev=8),
5889 'B/lambda' : Item(status=' ', wc_rev=8),
5890 'B/F' : Item(status=' ', wc_rev=8),
5891 'C' : Item(status=' ', wc_rev=8),
5892 'D' : Item(status=' M', wc_rev=8),
5893 'D/G' : Item(status=' M', wc_rev=8, switched='S'),
5894 'D/G/pi' : Item(status=' M', wc_rev=8),
5895 'D/G/rho' : Item(status='MM', wc_rev=8, switched='S'),
5896 'D/G/tau' : Item(status=' M', wc_rev=8),
5897 'D/gamma' : Item(status=' M', wc_rev=8),
5898 'D/H' : Item(status=' M', wc_rev=8),
5899 'D/H/chi' : Item(status=' M', wc_rev=8),
5900 'D/H/psi' : Item(status='MM', wc_rev=8, switched='S'),
5901 'D/H/omega' : Item(status='MM', wc_rev=8),
5903 expected_disk = wc.State('', {
5904 '' : Item(props={SVN_PROP_MERGEINFO : '/A:5-8'}),
5905 'B' : Item(),
5906 'mu' : Item("This is the file 'mu'.\n"),
5907 'B/E' : Item(),
5908 'B/E/alpha' : Item("This is the file 'alpha'.\n"),
5909 'B/E/beta' : Item("New content"),
5910 'B/lambda' : Item("This is the file 'lambda'.\n"),
5911 'B/F' : Item(),
5912 'C' : Item(),
5913 'D' : Item(props={SVN_PROP_MERGEINFO : '/A/D:5-8*'}),
5914 'D/G' : Item(props={SVN_PROP_MERGEINFO : '/A/D/G:5-8*'}),
5915 'D/G/pi' : Item("This is the file 'pi'.\n",
5916 props={SVN_PROP_MERGEINFO : '/A/D/G/pi:5-8'}),
5917 'D/G/rho' : Item("New content",
5918 props={SVN_PROP_MERGEINFO : '/A/D/G/rho:5-8'}),
5919 'D/G/tau' : Item("This is the file 'tau'.\n",
5920 props={SVN_PROP_MERGEINFO : '/A/D/G/tau:5-8'}),
5921 'D/gamma' : Item("This is the file 'gamma'.\n",
5922 props={SVN_PROP_MERGEINFO : '/A/D/gamma:5-8'}),
5923 'D/H' : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:5-8*'}),
5924 'D/H/chi' : Item("This is the file 'chi'.\n",
5925 props={SVN_PROP_MERGEINFO : '/A/D/H/chi:5-8'}),
5926 'D/H/psi' : Item("New content",
5927 props={SVN_PROP_MERGEINFO : '/A/D/H/psi:5-8'}),
5928 'D/H/omega' : Item("New content",
5929 props={SVN_PROP_MERGEINFO : '/A/D/H/omega:5-8'}),
5931 expected_skip = wc.State(short_A_COPY_path, { })
5932 os.chdir(svntest.main.work_dir)
5933 svntest.actions.run_and_verify_merge(short_A_COPY_path, '4', '8',
5934 sbox.repo_url + '/A',
5935 expected_output, expected_disk,
5936 expected_status, expected_skip,
5937 None, None, None, None, None, 1)
5939 os.chdir(saved_cwd)
5941 # Commit changes thus far.
5942 expected_output = svntest.wc.State(wc_dir, {
5943 'A_COPY' : Item(verb='Sending'),
5944 'A_COPY/B/E/beta' : Item(verb='Sending'),
5945 'A_COPY/D' : Item(verb='Sending'),
5946 'A_COPY/D/gamma' : Item(verb='Sending'),
5947 'A_COPY/D/G' : Item(verb='Sending'),
5948 'A_COPY/D/G/pi' : Item(verb='Sending'),
5949 'A_COPY/D/G/rho' : Item(verb='Sending'),
5950 'A_COPY/D/G/tau' : Item(verb='Sending'),
5951 'A_COPY/D/H' : Item(verb='Sending'),
5952 'A_COPY/D/H/chi' : Item(verb='Sending'),
5953 'A_COPY/D/H/omega' : Item(verb='Sending'),
5954 'A_COPY/D/H/psi' : Item(verb='Sending'),
5956 wc_status.tweak('A_COPY', 'A_COPY/B/E/beta', 'A_COPY/D', 'A_COPY/D/gamma',
5957 'A_COPY/D/G', 'A_COPY/D/G/pi', 'A_COPY/D/G/rho',
5958 'A_COPY/D/G/tau','A_COPY/D/H', 'A_COPY/D/H/chi',
5959 'A_COPY/D/H/omega', 'A_COPY/D/H/psi',
5960 wc_rev=9)
5961 svntest.actions.run_and_verify_commit(wc_dir, expected_output, wc_status,
5962 None, wc_dir)
5964 # Unswitch A_COPY/D/H/psi.
5965 expected_output = svntest.wc.State(wc_dir, {
5966 'A_COPY/D/H/psi' : Item(status='UU')})
5967 wc_status.tweak("A_COPY/D/H/psi", switched=None, wc_rev=9)
5968 wc_disk.tweak("A_COPY",
5969 props={SVN_PROP_MERGEINFO : '/A:5-8'})
5970 wc_disk.tweak("A_COPY/B/E/beta",
5971 contents="New content")
5972 wc_disk.tweak("A_COPY/D",
5973 props={SVN_PROP_MERGEINFO : '/A/D:5-8*'})
5974 wc_disk.tweak("A_COPY/D/gamma",
5975 props={SVN_PROP_MERGEINFO : '/A/D/gamma:5-8'})
5976 wc_disk.tweak("A_COPY/D/G",
5977 props={SVN_PROP_MERGEINFO : '/A/D/G:5-8*'})
5978 wc_disk.tweak("A_COPY/D/G/pi",
5979 props={SVN_PROP_MERGEINFO : '/A/D/G/pi:5-8'})
5980 wc_disk.tweak("A_COPY/D/G/rho",
5981 contents="New content",
5982 props={SVN_PROP_MERGEINFO : '/A/D/G/rho:5-8'})
5983 wc_disk.tweak("A_COPY/D/G/tau",
5984 props={SVN_PROP_MERGEINFO : '/A/D/G/tau:5-8'})
5985 wc_disk.tweak("A_COPY/D/H",
5986 props={SVN_PROP_MERGEINFO : '/A/D/H:5-8*'})
5987 wc_disk.tweak("A_COPY/D/H/chi",
5988 props={SVN_PROP_MERGEINFO : '/A/D/H/chi:5-8'})
5989 wc_disk.tweak("A_COPY/D/H/omega",
5990 contents="New content",
5991 props={SVN_PROP_MERGEINFO : '/A/D/H/omega:5-8'})
5992 wc_disk.tweak("A_COPY_2", props={})
5993 svntest.actions.run_and_verify_switch(sbox.wc_dir, A_COPY_psi_path,
5994 sbox.repo_url + "/A_COPY/D/H/psi",
5995 expected_output, wc_disk, wc_status,
5996 None, None, None, None, None, 1)
5998 # Non-inheritable mergeinfo ranges on a target don't prevent repeat
5999 # merges of that range on the target's children.
6001 # Non-inheritable mergeinfo ranges on a target are removed if the target
6002 # no longer has any switched children and a repeat merge is performed.
6004 # Merge r4:8 from A/D/H into A_COPY/D/H. A_COPY/D/H already has mergeinfo
6005 # for r4:8 but it is marked as uninheritable so the repeat merge is
6006 # allowed on its children, notably the now unswitched A_COPY/D/H/psi.
6007 # Since A_COPY/D/H no longer has any switched children and the merge of
6008 # r4:8 has been repeated the previously uninheritable ranges 5-8* on
6009 # A_COPY/D/H are made inheritable. This also means that the mergeinfo on
6010 # A_COPY/D/H's children will elide to it.
6011 short_H_COPY_path = shorten_path_kludge(A_COPY_H_path)
6012 expected_output = wc.State(short_H_COPY_path, {
6013 'psi' : Item(status='U ')
6015 expected_status = wc.State(short_H_COPY_path, {
6016 '' : Item(status=' M', wc_rev=9),
6017 'psi' : Item(status='M ', wc_rev=9),
6018 'omega' : Item(status=' M', wc_rev=9),
6019 'chi' : Item(status=' M', wc_rev=9),
6021 expected_disk = wc.State('', {
6022 '' : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:5-8'}),
6023 'psi' : Item("New content"),
6024 'omega' : Item("New content"),
6025 'chi' : Item("This is the file 'chi'.\n"),
6027 expected_skip = wc.State(short_H_COPY_path, { })
6028 os.chdir(svntest.main.work_dir)
6029 svntest.actions.run_and_verify_merge(short_H_COPY_path, '4', '8',
6030 sbox.repo_url + '/A/D/H',
6031 expected_output, expected_disk,
6032 expected_status, expected_skip,
6033 None, None, None, None, None, 1)
6034 os.chdir(saved_cwd)
6036 # Non-inheritable mergeinfo ranges on a target do prevent repeat
6037 # merges on the target itself.
6039 # Add a prop A/D and commit it as r10. Merge r10 into A_COPY/D. Since
6040 # A_COPY/D has a switched child it gets r10 added as a non-inheritable
6041 # range. Repeat the same merge checking that no repeat merge is
6042 # attempted on A_COPY/D.
6043 svntest.actions.run_and_verify_svn(None,
6044 ["property 'prop:name' set on '" +
6045 D_path + "'\n"], [], 'ps',
6046 'prop:name', 'propval', D_path)
6047 expected_output = svntest.wc.State(wc_dir, {
6048 'A/D' : Item(verb='Sending'),
6049 'A_COPY/D/H' : Item(verb='Sending'),
6050 'A_COPY/D/H/chi' : Item(verb='Sending'),
6051 'A_COPY/D/H/psi' : Item(verb='Sending'),
6052 'A_COPY/D/H/omega' : Item(verb='Sending'),
6054 wc_status.tweak('A_COPY/D', wc_rev=9)
6055 wc_status.tweak('A/D', 'A_COPY/D/H', 'A_COPY/D/H/chi', 'A_COPY/D/H/psi',
6056 'A_COPY/D/H/omega', wc_rev=10)
6057 svntest.actions.run_and_verify_commit(wc_dir, expected_output, wc_status,
6058 None, wc_dir)
6059 expected_output = wc.State(short_D_COPY_path, {
6060 '' : Item(status=' U')
6062 # Reuse expected status and disk from last merge to A_COPY/D
6063 expected_status_D.tweak('', 'gamma', status=' M', wc_rev=9)
6064 expected_status_D.tweak('H', status=' M', wc_rev=10)
6065 expected_status_D.tweak('H/psi', 'H/chi', 'H/omega', status=' ', wc_rev=10,
6066 switched=None)
6067 expected_status_D.tweak('G', switched='S', status=' M', wc_rev=9)
6068 expected_status_D.tweak('G/tau', 'G/pi', 'G/rho', status=' M', wc_rev=9)
6069 expected_disk_D.tweak('', props={SVN_PROP_MERGEINFO : '/A/D:5-8*,10*',
6070 "prop:name" : "propval"})
6071 expected_disk_D.tweak('G',
6072 props={SVN_PROP_MERGEINFO : '/A/D/G:5-8*,10*'})
6073 expected_disk_D.tweak('G/pi',
6074 props={SVN_PROP_MERGEINFO : '/A/D/G/pi:5-8,10'})
6075 expected_disk_D.tweak('G/rho',
6076 props={SVN_PROP_MERGEINFO : '/A/D/G/rho:5-8,10'})
6077 expected_disk_D.tweak('G/tau',
6078 props={SVN_PROP_MERGEINFO : '/A/D/G/tau:5-8,10'})
6079 expected_disk_D.tweak('H', props={SVN_PROP_MERGEINFO : '/A/D/H:5-8,10'})
6080 expected_disk_D.tweak('gamma',
6081 props={SVN_PROP_MERGEINFO : '/A/D/gamma:5-8,10'})
6082 expected_disk_D.tweak('H/chi', 'H/omega', props={})
6083 expected_disk_D.tweak('H/psi', contents="New content", props={})
6084 os.chdir(svntest.main.work_dir)
6085 svntest.actions.run_and_verify_merge(short_D_COPY_path, '9', '10',
6086 sbox.repo_url + '/A/D',
6087 expected_output, expected_disk_D,
6088 expected_status_D, expected_skip_D,
6089 None, None, None, None, None, 1)
6090 # Repeated merge is a no-op.
6091 expected_output = wc.State(short_D_COPY_path, {})
6092 svntest.actions.run_and_verify_merge(short_D_COPY_path, '9', '10',
6093 sbox.repo_url + '/A/D',
6094 expected_output, expected_disk_D,
6095 expected_status_D, expected_skip_D,
6096 None, None, None, None, None, 1)
6097 os.chdir(saved_cwd)
6100 # Test for issue 2047: Merge from parent dir fails while it succeeds from
6101 # the direct dir
6102 def merge_with_implicit_target_file(sbox):
6103 "merge a change to a file, using relative path"
6105 sbox.build()
6106 wc_dir = sbox.wc_dir
6108 # Make a change to A/mu, then revert it using 'svn merge -r 2:1 A/mu'
6110 # change A/mu and commit
6111 A_path = os.path.join(wc_dir, 'A')
6112 mu_path = os.path.join(A_path, 'mu')
6114 svntest.main.file_append(mu_path, "A whole new line.\n")
6116 expected_output = svntest.wc.State(wc_dir, {
6117 'A/mu' : Item(verb='Sending'),
6119 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
6120 expected_status.tweak('A/mu', wc_rev=2)
6121 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
6122 expected_status, None, wc_dir)
6124 # Update to revision 2.
6125 svntest.actions.run_and_verify_svn(None, None, [], 'update', wc_dir)
6127 # Revert the change committed in r2
6128 os.chdir(wc_dir)
6130 # run_and_verify_merge doesn't accept file paths.
6131 svntest.actions.run_and_verify_svn(None, None, [], 'merge', '-r', '2:1',
6132 'A/mu')
6134 # Test practical application of issue #2769 fix, empty rev range elision,
6135 # and elision to the repos.
6136 def empty_mergeinfo(sbox):
6137 "mergeinfo can explicitly be empty"
6139 # A bit o' history: The fix for issue #2769 originally permitted mergeinfo
6140 # with empty range lists and as a result we permitted partial elision and
6141 # had a whole slew of tests here for that. But the fix of issue #3029 now
6142 # prevents svn ps or svn merge from creating mergeinfo with paths mapped to
6143 # empty ranges, only empty mergeinfo is allowed. As a result this test now
6144 # covers the following areas:
6146 # A) Merging a set of revisions into a path, then reverse merging the
6147 # same set out of a subtree of path results in empty mergeinfo
6148 # (i.e. "") on the subtree.
6150 # B) Empty mergeinfo elides to empty mergeinfo.
6152 # C) If a merge sets empty mergeinfo on its target and that target has
6153 # no ancestor in either the WC or the repository with explict
6154 # mergeinfo, then the target's mergeinfo is removed (a.k.a. elides
6155 # to nothing).
6156 sbox.build()
6157 wc_dir = sbox.wc_dir
6158 wc_disk, wc_status = set_up_branch(sbox)
6160 # Some paths we'll care about
6161 A_COPY_path = os.path.join(wc_dir, "A_COPY")
6162 H_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "H")
6163 psi_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "H", "psi")
6164 rho_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "G", "rho")
6166 # Test area A -- Merge r2:4 into A_COPY then reverse merge 4:2 to
6167 # A_COPY/D/G. A_COPY/D/G should end up with empty mergeinfo to
6168 # override that of A_COPY.
6170 # Search for the comment entitled "The Merge Kluge" elsewhere in
6171 # this file, to understand why we shorten and chdir() below.
6172 short_A_COPY_path = shorten_path_kludge(A_COPY_path)
6173 expected_output = wc.State(short_A_COPY_path, {
6174 'D/H/psi' : Item(status='U '),
6175 'D/G/rho' : Item(status='U '),
6177 expected_status = wc.State(short_A_COPY_path, {
6178 '' : Item(status=' M', wc_rev=2),
6179 'B' : Item(status=' ', wc_rev=2),
6180 'mu' : Item(status=' ', wc_rev=2),
6181 'B/E' : Item(status=' ', wc_rev=2),
6182 'B/E/alpha' : Item(status=' ', wc_rev=2),
6183 'B/E/beta' : Item(status=' ', wc_rev=2),
6184 'B/lambda' : Item(status=' ', wc_rev=2),
6185 'B/F' : Item(status=' ', wc_rev=2),
6186 'C' : Item(status=' ', wc_rev=2),
6187 'D' : Item(status=' ', wc_rev=2),
6188 'D/G' : Item(status=' ', wc_rev=2),
6189 'D/G/pi' : Item(status=' ', wc_rev=2),
6190 'D/G/rho' : Item(status='M ', wc_rev=2),
6191 'D/G/tau' : Item(status=' ', wc_rev=2),
6192 'D/gamma' : Item(status=' ', wc_rev=2),
6193 'D/H' : Item(status=' ', wc_rev=2),
6194 'D/H/chi' : Item(status=' ', wc_rev=2),
6195 'D/H/psi' : Item(status='M ', wc_rev=2),
6196 'D/H/omega' : Item(status=' ', wc_rev=2),
6198 expected_disk = wc.State('', {
6199 '' : Item(props={SVN_PROP_MERGEINFO : '/A:3-4'}),
6200 'B' : Item(),
6201 'mu' : Item("This is the file 'mu'.\n"),
6202 'B/E' : Item(),
6203 'B/E/alpha' : Item("This is the file 'alpha'.\n"),
6204 'B/E/beta' : Item("This is the file 'beta'.\n"),
6205 'B/lambda' : Item("This is the file 'lambda'.\n"),
6206 'B/F' : Item(),
6207 'C' : Item(),
6208 'D' : Item(),
6209 'D/G' : Item(),
6210 'D/G/pi' : Item("This is the file 'pi'.\n"),
6211 'D/G/rho' : Item("New content"),
6212 'D/G/tau' : Item("This is the file 'tau'.\n"),
6213 'D/gamma' : Item("This is the file 'gamma'.\n"),
6214 'D/H' : Item(),
6215 'D/H/chi' : Item("This is the file 'chi'.\n"),
6216 'D/H/psi' : Item("New content"),
6217 'D/H/omega' : Item("This is the file 'omega'.\n"),
6219 expected_skip = wc.State(short_A_COPY_path, { })
6220 saved_cwd = os.getcwd()
6221 os.chdir(svntest.main.work_dir)
6222 svntest.actions.run_and_verify_merge(short_A_COPY_path, '2', '4',
6223 sbox.repo_url + \
6224 '/A',
6225 expected_output,
6226 expected_disk,
6227 expected_status,
6228 expected_skip,
6229 None, None, None, None,
6230 None, 1)
6231 # Now do the reverse merge into the subtree.
6232 short_H_COPY_path = shorten_path_kludge(H_COPY_path)
6233 expected_output = wc.State(short_H_COPY_path, {
6234 'psi' : Item(status='G '),
6236 expected_status = wc.State(short_H_COPY_path, {
6237 '' : Item(status=' M', wc_rev=2),
6238 'chi' : Item(status=' ', wc_rev=2),
6239 'psi' : Item(status=' ', wc_rev=2),
6240 'omega' : Item(status=' ', wc_rev=2),
6242 expected_disk = wc.State('', {
6243 '' : Item(props={SVN_PROP_MERGEINFO : ''}),
6244 'chi' : Item("This is the file 'chi'.\n"),
6245 'psi' : Item("This is the file 'psi'.\n"),
6246 'omega' : Item("This is the file 'omega'.\n"),
6248 expected_skip = wc.State(short_H_COPY_path, { })
6249 svntest.actions.run_and_verify_merge(short_H_COPY_path, '4', '2',
6250 sbox.repo_url + \
6251 '/A/D/H',
6252 expected_output,
6253 expected_disk,
6254 expected_status,
6255 expected_skip,
6256 None, None, None, None,
6257 None, 1)
6259 # Test areas B and C -- Reverse merge r3 into A_COPY, this would result in
6260 # empty mergeinfo on A_COPY and A_COPY/D/H, but the empty mergeinfo on the
6261 # latter elides to the former. And then the empty mergeinfo on A_COPY,
6262 # which has no parent with explicit mergeinfo to override (in either the WC
6263 # or the repos) itself elides. This leaves the WC in the same unmodified
6264 # state as after the call to set_up_branch().
6265 short_rho_COPY_path = shorten_path_kludge(rho_COPY_path)
6266 expected_output = expected_merge_output(
6267 [[4,3]], 'G ' + short_rho_COPY_path + '\n')
6268 svntest.actions.run_and_verify_svn(None, expected_output,
6269 [], 'merge', '-r4:2',
6270 sbox.repo_url + '/A',
6271 short_A_COPY_path)
6272 os.chdir(saved_cwd)
6273 svntest.actions.run_and_verify_status(wc_dir, wc_status)
6274 # Check that A_COPY's mergeinfo is gone.
6275 svntest.actions.run_and_verify_svn(None, [], [], 'pg', 'svn:mergeinfo',
6276 A_COPY_path)
6278 def prop_add_to_child_with_mergeinfo(sbox):
6279 "merge adding prop to child of merge target works"
6281 # Test for Issue #2781 Prop add to child of merge target corrupts WC if
6282 # child has mergeinfo.
6284 sbox.build()
6285 wc_dir = sbox.wc_dir
6286 expected_disk, expected_status = set_up_branch(sbox)
6288 # Some paths we'll care about
6289 beta_path = os.path.join(wc_dir, "A", "B", "E", "beta")
6290 beta_COPY_path = os.path.join(wc_dir, "A_COPY", "B", "E", "beta")
6291 B_COPY_path = os.path.join(wc_dir, "A_COPY", "B")
6293 # Set a non-mergeinfo prop on a file.
6294 svntest.actions.run_and_verify_svn(None,
6295 ["property 'prop:name' set on '" +
6296 beta_path + "'\n"], [], 'ps',
6297 'prop:name', 'propval', beta_path)
6298 expected_disk.tweak('A/B/E/beta', props={'prop:name' : 'propval'})
6299 expected_status.tweak('A/B/E/beta', wc_rev=7)
6300 expected_output = wc.State(wc_dir,
6301 {'A/B/E/beta' : Item(verb='Sending')})
6302 svntest.actions.run_and_verify_commit(wc_dir,
6303 expected_output,
6304 expected_status,
6305 None,
6306 wc_dir)
6308 # Merge r4:5 from A/B/E/beta into A_COPY/B/E/beta.
6309 short_beta_COPY_path = shorten_path_kludge(beta_COPY_path)
6310 saved_cwd = os.getcwd()
6311 os.chdir(svntest.main.work_dir)
6312 svntest.actions.run_and_verify_svn(None,
6313 expected_merge_output([[5]],
6314 'U ' + short_beta_COPY_path +'\n'),
6315 [], 'merge', '-c5',
6316 sbox.repo_url + '/A/B/E/beta',
6317 short_beta_COPY_path)
6318 os.chdir(saved_cwd)
6320 # Merge r6:7 into A_COPY/B. In issue #2781 this adds a bogus
6321 # and incomplete entry in A_COPY/B/.svn/entries for 'beta'.
6322 short_B_COPY_path = shorten_path_kludge(B_COPY_path)
6323 expected_output = wc.State(short_B_COPY_path, {
6324 'E/beta' : Item(status=' U'),
6326 expected_status = wc.State(short_B_COPY_path, {
6327 '' : Item(status=' M', wc_rev=2),
6328 'E' : Item(status=' ', wc_rev=2),
6329 'E/alpha' : Item(status=' ', wc_rev=2),
6330 'E/beta' : Item(status='MM', wc_rev=2),
6331 'lambda' : Item(status=' ', wc_rev=2),
6332 'F' : Item(status=' ', wc_rev=2),
6334 expected_disk = wc.State('', {
6335 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B:7'}),
6336 'E' : Item(),
6337 'E/alpha' : Item("This is the file 'alpha'.\n"),
6338 'E/beta' : Item(contents="New content",
6339 props={SVN_PROP_MERGEINFO : '/A/B/E/beta:5,7',
6340 'prop:name' : 'propval'}),
6341 'F' : Item(),
6342 'lambda' : Item("This is the file 'lambda'.\n")
6344 expected_skip = wc.State(short_B_COPY_path, { })
6345 os.chdir(svntest.main.work_dir)
6346 svntest.actions.run_and_verify_merge(short_B_COPY_path, '6', '7',
6347 sbox.repo_url + \
6348 '/A/B',
6349 expected_output,
6350 expected_disk,
6351 expected_status,
6352 expected_skip,
6353 None, None, None, None,
6354 None, 1)
6356 def diff_repos_does_not_update_mergeinfo(sbox):
6357 "don't set mergeinfo when merging from another repo"
6359 # Test for issue #2788.
6361 sbox.build()
6362 wc_dir = sbox.wc_dir
6363 expected_disk, expected_status = set_up_branch(sbox)
6365 # Create a second repository with the same greek tree
6366 repo_dir = sbox.repo_dir
6367 other_repo_dir, other_repo_url = sbox.add_repo_path("other")
6368 svntest.main.copy_repos(repo_dir, other_repo_dir, 6, 1)
6370 # Merge r3:4 (using implied peg revisions) from 'other' repos into
6371 # A_COPY/D/G. Merge should succeed, but no mergeinfo should be set.
6373 # Search for the comment entitled "The Merge Kluge" elsewhere in
6374 # this file, to understand why we shorten and chdir() below.
6375 short_G_COPY_path = shorten_path_kludge(os.path.join(wc_dir,
6376 "A_COPY", "D", "G"))
6377 saved_cwd = os.getcwd()
6379 os.chdir(svntest.main.work_dir)
6380 svntest.actions.run_and_verify_svn(None,
6381 expected_merge_output([[4]],
6382 'U ' +
6383 os.path.join(short_G_COPY_path,
6384 "rho") + '\n'),
6385 [], 'merge', '-c4',
6386 other_repo_url + '/A/D/G',
6387 short_G_COPY_path)
6388 os.chdir(saved_cwd)
6390 # Merge r4:5 (using explicit peg revisions) from 'other' repos into
6391 # A_COPY/B/E. Merge should succeed, but no mergeinfo should be set.
6392 short_E_COPY_path = shorten_path_kludge(os.path.join(wc_dir,
6393 "A_COPY", "B", "E"))
6395 os.chdir(svntest.main.work_dir)
6396 svntest.actions.run_and_verify_svn(None,
6397 expected_merge_output([[5]],
6398 'U ' +
6399 os.path.join(short_E_COPY_path,
6400 "beta") +'\n'),
6401 [], 'merge',
6402 other_repo_url + '/A/B/E@4',
6403 other_repo_url + '/A/B/E@5',
6404 short_E_COPY_path)
6405 os.chdir(saved_cwd)
6407 expected_status.tweak('A_COPY/D/G/rho', 'A_COPY/B/E/beta', status='M ')
6408 svntest.actions.run_and_verify_status(wc_dir, expected_status)
6410 def avoid_reflected_revs(sbox):
6411 "avoid repeated merges for cyclic merging"
6413 ## See http://subversion.tigris.org/issues/show_bug.cgi?id=2897. ##
6415 # Create a WC with a single branch
6416 sbox.build()
6417 wc_dir = sbox.wc_dir
6418 wc_disk, wc_status = set_up_branch(sbox, True, 1)
6420 # Some paths we'll care about
6421 A_path = os.path.join(wc_dir, 'A')
6422 A_COPY_path = os.path.join(wc_dir, 'A_COPY')
6423 tfile1_path = os.path.join(wc_dir, 'A', 'tfile1')
6424 tfile2_path = os.path.join(wc_dir, 'A', 'tfile2')
6425 bfile1_path = os.path.join(A_COPY_path, 'bfile1')
6426 bfile2_path = os.path.join(A_COPY_path, 'bfile2')
6428 # Contents to be added to files
6429 tfile1_content = "This is tfile1\n"
6430 tfile2_content = "This is tfile2\n"
6431 bfile1_content = "This is bfile1\n"
6432 bfile2_content = "This is bfile2\n"
6434 # We'll consider A as the trunk and A_COPY as the feature branch
6435 # Create a tfile1 in A
6436 svntest.main.file_write(tfile1_path, tfile1_content)
6437 svntest.actions.run_and_verify_svn(None, None, [], 'add', tfile1_path)
6438 expected_output = wc.State(wc_dir, {'A/tfile1' : Item(verb='Adding')})
6439 wc_status.add({'A/tfile1' : Item(status=' ', wc_rev=3)})
6440 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
6441 wc_status, None, wc_dir)
6443 # Create a bfile1 in A_COPY
6444 svntest.main.file_write(bfile1_path, bfile1_content)
6445 svntest.actions.run_and_verify_svn(None, None, [], 'add', bfile1_path)
6446 expected_output = wc.State(wc_dir, {'A_COPY/bfile1' : Item(verb='Adding')})
6447 wc_status.add({'A_COPY/bfile1' : Item(status=' ', wc_rev=4)})
6448 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
6449 wc_status, None, wc_dir)
6451 # Create one more file in A
6452 svntest.main.file_write(tfile2_path, tfile2_content)
6453 svntest.actions.run_and_verify_svn(None, None, [], 'add', tfile2_path)
6454 expected_output = wc.State(wc_dir, {'A/tfile2' : Item(verb='Adding')})
6455 wc_status.add({'A/tfile2' : Item(status=' ', wc_rev=5)})
6456 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
6457 wc_status, None, wc_dir)
6459 short_A_COPY = shorten_path_kludge(A_COPY_path)
6460 saved_cwd = os.getcwd()
6461 os.chdir(svntest.main.work_dir)
6463 # Merge r5 from /A to /A_COPY, creating r6
6464 expected_output = wc.State(short_A_COPY, {
6465 'tfile2' : Item(status='A '),
6467 expected_status = wc.State(short_A_COPY, {
6468 '' : Item(status=' M', wc_rev=2),
6469 'tfile2' : Item(status='A ', wc_rev='-', copied='+'),
6470 'bfile1' : Item(status=' ', wc_rev=4),
6471 'mu' : Item(status=' ', wc_rev=2),
6472 'C' : Item(status=' ', wc_rev=2),
6473 'D' : Item(status=' ', wc_rev=2),
6474 'B' : Item(status=' ', wc_rev=2),
6475 'B/lambda' : Item(status=' ', wc_rev=2),
6476 'B/E' : Item(status=' ', wc_rev=2),
6477 'B/E/alpha': Item(status=' ', wc_rev=2),
6478 'B/E/beta' : Item(status=' ', wc_rev=2),
6479 'B/F' : Item(status=' ', wc_rev=2),
6480 'D/gamma' : Item(status=' ', wc_rev=2),
6481 'D/G' : Item(status=' ', wc_rev=2),
6482 'D/G/pi' : Item(status=' ', wc_rev=2),
6483 'D/G/rho' : Item(status=' ', wc_rev=2),
6484 'D/G/tau' : Item(status=' ', wc_rev=2),
6485 'D/H' : Item(status=' ', wc_rev=2),
6486 'D/H/chi' : Item(status=' ', wc_rev=2),
6487 'D/H/omega': Item(status=' ', wc_rev=2),
6488 'D/H/psi' : Item(status=' ', wc_rev=2),
6490 expected_disk = wc.State('', {
6491 '' : Item(props={SVN_PROP_MERGEINFO : '/A:5'}),
6492 'tfile2' : Item(tfile2_content),
6493 'bfile1' : Item(bfile1_content),
6494 'mu' : Item("This is the file 'mu'.\n"),
6495 'C' : Item(),
6496 'D' : Item(),
6497 'B' : Item(),
6498 'B/lambda' : Item("This is the file 'lambda'.\n"),
6499 'B/E' : Item(),
6500 'B/E/alpha': Item("This is the file 'alpha'.\n"),
6501 'B/E/beta' : Item("This is the file 'beta'.\n"),
6502 'B/F' : Item(),
6503 'D/gamma' : Item("This is the file 'gamma'.\n"),
6504 'D/G' : Item(),
6505 'D/G/pi' : Item("This is the file 'pi'.\n"),
6506 'D/G/rho' : Item("This is the file 'rho'.\n"),
6507 'D/G/tau' : Item("This is the file 'tau'.\n"),
6508 'D/H' : Item(),
6509 'D/H/chi' : Item("This is the file 'chi'.\n"),
6510 'D/H/omega': Item("This is the file 'omega'.\n"),
6511 'D/H/psi' : Item("This is the file 'psi'.\n"),
6513 expected_skip = wc.State(short_A_COPY, {})
6515 svntest.actions.run_and_verify_merge(short_A_COPY, '4', '5',
6516 sbox.repo_url + '/A',
6517 expected_output,
6518 expected_disk,
6519 expected_status,
6520 expected_skip,
6521 None, None, None, None, None, 1)
6522 os.chdir(saved_cwd)
6524 # Sync up with the trunk ie., A
6525 svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
6526 expected_output = wc.State(wc_dir, {
6527 'A_COPY' : Item(verb='Sending'),
6528 'A_COPY/tfile2' : Item(verb='Adding'),
6530 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
6531 None, None, wc_dir)
6532 os.chdir(svntest.main.work_dir)
6534 # Merge r3 from /A to /A_COPY, creating r7
6535 expected_output = wc.State(short_A_COPY, {
6536 'tfile1' : Item(status='A '),
6538 expected_status.tweak(wc_rev=5)
6539 expected_status.tweak('', wc_rev=6)
6540 expected_status.tweak('tfile2', status=' ', copied=None, wc_rev=6)
6541 expected_status.add({
6542 'tfile1' : Item(status='A ', wc_rev='-', copied='+'),
6544 expected_disk.tweak('', props={SVN_PROP_MERGEINFO : '/A:3,5'})
6545 expected_disk.add({
6546 'tfile1' : Item(tfile1_content),
6549 svntest.actions.run_and_verify_merge(short_A_COPY, '2', '3',
6550 sbox.repo_url + '/A',
6551 expected_output,
6552 expected_disk,
6553 expected_status,
6554 expected_skip,
6555 None, None, None, None, None, 1)
6557 os.chdir(saved_cwd)
6559 svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
6560 expected_output = wc.State(wc_dir, {
6561 'A_COPY' : Item(verb='Sending'),
6562 'A_COPY/tfile1' : Item(verb='Adding'),
6564 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
6565 None, None, wc_dir)
6567 # Add bfile2 to A_COPY
6568 svntest.main.file_write(bfile2_path, bfile2_content)
6569 svntest.actions.run_and_verify_svn(None, None, [], 'add', bfile2_path)
6570 expected_output = wc.State(wc_dir, {'A_COPY/bfile2' : Item(verb='Adding')})
6571 wc_status.tweak(wc_rev=6)
6572 wc_status.add({
6573 'A_COPY/bfile2' : Item(status=' ', wc_rev=8),
6574 'A_COPY' : Item(status=' ', wc_rev=7),
6575 'A_COPY/tfile2' : Item(status=' ', wc_rev=6),
6576 'A_COPY/tfile1' : Item(status=' ', wc_rev=7),
6578 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
6579 wc_status, None, wc_dir)
6581 # Merge 2:8 from A_COPY(feature branch) to A(trunk).
6582 short_A_path = shorten_path_kludge(A_path)
6583 expected_output = wc.State(short_A_path, {
6584 'bfile2' : Item(status='A '),
6585 'bfile1' : Item(status='A '),
6587 expected_status = wc.State(short_A_path, {
6588 '' : Item(status=' M', wc_rev=6),
6589 'bfile2' : Item(status='A ', wc_rev='-', copied='+'),
6590 'bfile1' : Item(status='A ', wc_rev='-', copied='+'),
6591 'tfile2' : Item(status=' ', wc_rev=6),
6592 'tfile1' : Item(status=' ', wc_rev=6),
6593 'mu' : Item(status=' ', wc_rev=6),
6594 'C' : Item(status=' ', wc_rev=6),
6595 'D' : Item(status=' ', wc_rev=6),
6596 'B' : Item(status=' ', wc_rev=6),
6597 'B/lambda' : Item(status=' ', wc_rev=6),
6598 'B/E' : Item(status=' ', wc_rev=6),
6599 'B/E/alpha' : Item(status=' ', wc_rev=6),
6600 'B/E/beta' : Item(status=' ', wc_rev=6),
6601 'B/F' : Item(status=' ', wc_rev=6),
6602 'D/gamma' : Item(status=' ', wc_rev=6),
6603 'D/G' : Item(status=' ', wc_rev=6),
6604 'D/G/pi' : Item(status=' ', wc_rev=6),
6605 'D/G/rho' : Item(status=' ', wc_rev=6),
6606 'D/G/tau' : Item(status=' ', wc_rev=6),
6607 'D/H' : Item(status=' ', wc_rev=6),
6608 'D/H/chi' : Item(status=' ', wc_rev=6),
6609 'D/H/omega' : Item(status=' ', wc_rev=6),
6610 'D/H/psi' : Item(status=' ', wc_rev=6),
6612 expected_disk = wc.State('', {
6613 '' : Item(props={SVN_PROP_MERGEINFO : '/A_COPY:3-8'}),
6614 'bfile2' : Item(bfile2_content),
6615 'bfile1' : Item(bfile1_content),
6616 'tfile2' : Item(tfile2_content),
6617 'tfile1' : Item(tfile1_content),
6618 'mu' : Item("This is the file 'mu'.\n"),
6619 'C' : Item(),
6620 'D' : Item(),
6621 'B' : Item(),
6622 'B/lambda' : Item("This is the file 'lambda'.\n"),
6623 'B/E' : Item(),
6624 'B/E/alpha' : Item("This is the file 'alpha'.\n"),
6625 'B/E/beta' : Item("This is the file 'beta'.\n"),
6626 'B/F' : Item(),
6627 'D/gamma' : Item("This is the file 'gamma'.\n"),
6628 'D/G' : Item(),
6629 'D/G/pi' : Item("This is the file 'pi'.\n"),
6630 'D/G/rho' : Item("This is the file 'rho'.\n"),
6631 'D/G/tau' : Item("This is the file 'tau'.\n"),
6632 'D/H' : Item(),
6633 'D/H/chi' : Item("This is the file 'chi'.\n"),
6634 'D/H/omega' : Item("This is the file 'omega'.\n"),
6635 'D/H/psi' : Item("This is the file 'psi'.\n"),
6638 expected_skip = wc.State(short_A_path, {})
6639 saved_cwd = os.getcwd()
6640 os.chdir(svntest.main.work_dir)
6642 svntest.actions.run_and_verify_merge(short_A_path, '2', '8',
6643 sbox.repo_url + '/A_COPY',
6644 expected_output,
6645 expected_disk,
6646 expected_status,
6647 expected_skip,
6648 None, None, None, None, None, 1)
6649 os.chdir(saved_cwd)
6652 def update_loses_mergeinfo(sbox):
6653 "update does not merge mergeinfo"
6656 When a working copy receives a fresh svn:mergeinfo property due to update,
6657 and working copy has some local mergeinfo(yet to be added), local mergeinfo
6658 is left unchanged.
6661 sbox.build()
6662 wc_dir = sbox.wc_dir
6663 A_C_wc_dir = wc_dir + '/A/C'
6664 A_B_url = sbox.repo_url + '/A/B'
6665 A_B_J_url = sbox.repo_url + '/A/B/J'
6666 A_B_K_url = sbox.repo_url + '/A/B/K'
6667 svntest.actions.run_and_verify_svn(None, ['\n', 'Committed revision 2.\n'],
6669 'mkdir', '-m', 'rev 2', A_B_J_url)
6670 svntest.actions.run_and_verify_svn(None, ['\n', 'Committed revision 3.\n'],
6672 'mkdir', '-m', 'rev 3', A_B_K_url)
6674 other_wc = sbox.add_wc_path('other')
6675 svntest.actions.duplicate_dir(wc_dir, other_wc)
6677 short_A_C_wc_dir = shorten_path_kludge(A_C_wc_dir)
6678 expected_output = wc.State(short_A_C_wc_dir, {'J' : Item(status='A ')})
6679 expected_disk = wc.State('', {
6680 'J' : Item(),
6681 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B:2'}),
6683 expected_status = wc.State(short_A_C_wc_dir,
6684 { '' : Item(wc_rev=1, status=' M'),
6685 'J' : Item(status='A ',
6686 wc_rev='-', copied='+')
6689 expected_skip = wc.State('', { })
6690 saved_cwd = os.getcwd()
6691 os.chdir(svntest.main.work_dir)
6692 svntest.actions.run_and_verify_merge(short_A_C_wc_dir, '1', '2',
6693 A_B_url,
6694 expected_output,
6695 expected_disk,
6696 expected_status,
6697 expected_skip,
6698 check_props=1)
6699 os.chdir(saved_cwd)
6700 expected_output = wc.State(A_C_wc_dir, {
6701 '' : Item(verb='Sending'),
6702 'J' : Item(verb='Adding')
6704 expected_status = wc.State(A_C_wc_dir,
6705 { '' : Item(status=' ', wc_rev=4),
6706 'J' : Item(status=' ', wc_rev=4)
6709 svntest.actions.run_and_verify_commit(A_C_wc_dir,
6710 expected_output,
6711 expected_status,
6712 None,
6713 A_C_wc_dir)
6715 other_A_C_wc_dir = other_wc + '/A/C'
6716 short_other_A_C_wc_dir = shorten_path_kludge(other_A_C_wc_dir)
6717 expected_output = wc.State(short_other_A_C_wc_dir, {'K' : Item(status='A ')})
6718 expected_disk = wc.State('', {
6719 'K' : Item(),
6720 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B:3'}),
6722 expected_status = wc.State(short_other_A_C_wc_dir,
6723 { '' : Item(wc_rev=1, status=' M'),
6724 'K' : Item(status='A ',
6725 wc_rev='-', copied='+')
6728 expected_skip = wc.State('', { })
6729 saved_cwd = os.getcwd()
6730 os.chdir(svntest.main.work_dir)
6731 svntest.actions.run_and_verify_merge(short_other_A_C_wc_dir, '2', '3',
6732 A_B_url,
6733 expected_output,
6734 expected_disk,
6735 expected_status,
6736 expected_skip,
6737 check_props=1)
6738 os.chdir(saved_cwd)
6739 expected_output = wc.State(other_A_C_wc_dir,
6740 {'J' : Item(status='A '),
6741 '' : Item(status=' G')
6744 expected_disk = wc.State('', {
6745 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B:2-3'}),
6746 'J' : Item(),
6747 'K' : Item(),
6749 expected_status = wc.State(other_A_C_wc_dir,
6750 { '' : Item(wc_rev=4, status=' M'),
6751 'J' : Item(status=' ', wc_rev='4'),
6752 'K' : Item(status='A ',
6753 wc_rev='-', copied='+')
6756 svntest.actions.run_and_verify_update(other_A_C_wc_dir,
6757 expected_output,
6758 expected_disk,
6759 expected_status,
6760 check_props=1)
6762 # Tests part of issue# 2829, marked as XFail until that issue is fixed.
6763 def merge_loses_mergeinfo(sbox):
6764 "merge should merge mergeinfo"
6767 When a working copy has no mergeinfo(due to local full revert of all merges),
6768 and merge is attempted for someother revision rX, The new mergeinfo should be
6769 /merge/src: rX not all the reverted ones reappearing along with rX.
6772 sbox.build()
6773 wc_dir = sbox.wc_dir
6774 A_C_wc_dir = wc_dir + '/A/C'
6775 A_B_url = sbox.repo_url + '/A/B'
6776 A_B_J_url = sbox.repo_url + '/A/B/J'
6777 A_B_K_url = sbox.repo_url + '/A/B/K'
6778 svntest.actions.run_and_verify_svn(None, ['\n', 'Committed revision 2.\n'],
6780 'mkdir', '-m', 'rev 2', A_B_J_url)
6781 svntest.actions.run_and_verify_svn(None, ['\n', 'Committed revision 3.\n'],
6783 'mkdir', '-m', 'rev 3', A_B_K_url)
6785 short_A_C_wc_dir = shorten_path_kludge(A_C_wc_dir)
6786 expected_output = wc.State(short_A_C_wc_dir, {'J' : Item(status='A ')})
6787 expected_disk = wc.State('', {
6788 'J' : Item(),
6789 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B:2'}),
6791 expected_status = wc.State(short_A_C_wc_dir,
6792 { '' : Item(wc_rev=1, status=' M'),
6793 'J' : Item(status='A ',
6794 wc_rev='-', copied='+')
6797 expected_skip = wc.State('', { })
6798 saved_cwd = os.getcwd()
6799 os.chdir(svntest.main.work_dir)
6800 svntest.actions.run_and_verify_merge(short_A_C_wc_dir, '1', '2',
6801 A_B_url,
6802 expected_output,
6803 expected_disk,
6804 expected_status,
6805 expected_skip,
6806 check_props=1)
6807 os.chdir(saved_cwd)
6808 expected_output = wc.State(A_C_wc_dir, {
6809 '' : Item(verb='Sending'),
6810 'J' : Item(verb='Adding')
6812 expected_status = wc.State(A_C_wc_dir,
6813 { '' : Item(status=' ', wc_rev=4),
6814 'J' : Item(status=' ', wc_rev=4)
6817 svntest.actions.run_and_verify_commit(A_C_wc_dir,
6818 expected_output,
6819 expected_status,
6820 None,
6821 A_C_wc_dir)
6822 expected_output = wc.State(short_A_C_wc_dir, {'J' : Item(status='D ')})
6823 expected_disk = wc.State('', {'J': Item()})
6824 expected_status = wc.State(short_A_C_wc_dir,
6825 { '' : Item(wc_rev=4, status=' M'),
6826 'J' : Item(wc_rev=4, status='D ')
6829 saved_cwd = os.getcwd()
6830 os.chdir(svntest.main.work_dir)
6831 svntest.actions.run_and_verify_merge(short_A_C_wc_dir, '2', '1',
6832 A_B_url,
6833 expected_output,
6834 expected_disk,
6835 expected_status,
6836 expected_skip,
6837 check_props=1)
6838 os.chdir(saved_cwd)
6840 expected_output = wc.State(short_A_C_wc_dir, {'K' : Item(status='A ')})
6841 expected_disk = wc.State('', {
6842 'K' : Item(),
6843 'J' : Item(),
6844 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B:3'}),
6846 expected_status = wc.State(short_A_C_wc_dir,
6847 { '' : Item(wc_rev=4, status=' M'),
6848 'K' : Item(status='A ',
6849 wc_rev='-', copied='+'),
6850 'J' : Item(wc_rev=4, status='D ')
6853 saved_cwd = os.getcwd()
6854 os.chdir(svntest.main.work_dir)
6855 svntest.actions.run_and_verify_merge(short_A_C_wc_dir, '2', '3',
6856 A_B_url,
6857 expected_output,
6858 expected_disk,
6859 expected_status,
6860 expected_skip,
6861 check_props=1)
6863 def single_file_replace_style_merge_capability(sbox):
6864 "replace-style merge capability for a single file"
6866 # Test for issue #2853, do_single_file_merge() lacks "Replace-style
6867 # merge" capability
6869 sbox.build()
6870 wc_dir = sbox.wc_dir
6871 iota_path = os.path.join(wc_dir, 'iota')
6872 mu_path = os.path.join(wc_dir, 'A', 'mu')
6874 # delete mu and replace it with a copy of iota
6875 svntest.main.run_svn(None, 'rm', mu_path)
6876 svntest.main.run_svn(None, 'mv', iota_path, mu_path)
6878 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
6879 expected_status.tweak('A/mu', status=' ', wc_rev=2)
6880 expected_status.remove('iota')
6881 expected_output = svntest.wc.State(wc_dir, {
6882 'iota': Item(verb='Deleting'),
6883 'A/mu': Item(verb='Replacing'),
6885 svntest.actions.run_and_verify_commit(wc_dir,
6886 expected_output,
6887 expected_status,
6888 None, wc_dir)
6890 # Merge the file mu alone to rev1
6891 svntest.actions.run_and_verify_svn(None,
6892 expected_merge_output(None,
6893 ['D ' + mu_path + '\n',
6894 'A ' + mu_path + '\n']),
6896 'merge',
6897 mu_path + '@2',
6898 mu_path + '@1',
6899 mu_path)
6901 # Test for issue 2786 fix.
6902 def merge_to_out_of_date_target(sbox):
6903 "merge to ood path can lead to inaccurate mergeinfo"
6905 # Create a WC with a branch.
6906 sbox.build()
6907 wc_dir = sbox.wc_dir
6908 wc_disk, wc_status = set_up_branch(sbox, False, 1)
6910 # Make second working copy
6911 other_wc = sbox.add_wc_path('other')
6912 svntest.actions.duplicate_dir(wc_dir, other_wc)
6914 # Some paths we'll care about
6915 A_COPY_H_path = os.path.join(wc_dir, "A_COPY", "D", "H")
6916 other_A_COPY_H_path = os.path.join(other_wc, "A_COPY", "D", "H")
6918 # Merge -c3 into A_COPY/D/H of first WC.
6920 # Search for the comment entitled "The Merge Kluge" elsewhere in
6921 # this file, to understand why we shorten and chdir() below.
6922 short_H_COPY_path = shorten_path_kludge(A_COPY_H_path)
6923 expected_output = wc.State(short_H_COPY_path, {
6924 'psi' : Item(status='U ')
6926 expected_status = wc.State(short_H_COPY_path, {
6927 '' : Item(status=' M', wc_rev=2),
6928 'psi' : Item(status='M ', wc_rev=2),
6929 'omega' : Item(status=' ', wc_rev=2),
6930 'chi' : Item(status=' ', wc_rev=2),
6932 expected_disk = wc.State('', {
6933 '' : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:3'}),
6934 'psi' : Item("New content"),
6935 'omega' : Item("This is the file 'omega'.\n"),
6936 'chi' : Item("This is the file 'chi'.\n"),
6938 expected_skip = wc.State(short_H_COPY_path, { })
6939 saved_cwd = os.getcwd()
6940 os.chdir(svntest.main.work_dir)
6941 svntest.actions.run_and_verify_merge(short_H_COPY_path, '2', '3',
6942 sbox.repo_url + '/A/D/H',
6943 expected_output, expected_disk,
6944 expected_status, expected_skip,
6945 None, None, None, None, None, 1)
6946 os.chdir(saved_cwd)
6948 # Commit merge to first WC.
6949 wc_status.tweak('A_COPY/D/H/psi', 'A_COPY/D/H', wc_rev=7)
6950 expected_output = svntest.wc.State(wc_dir, {
6951 'A_COPY/D/H' : Item(verb='Sending'),
6952 'A_COPY/D/H/psi': Item(verb='Sending'),
6954 svntest.actions.run_and_verify_commit(wc_dir,
6955 expected_output,
6956 wc_status,
6957 None, wc_dir)
6959 # Merge -c6 into A_COPY/D/H of other WC.
6960 short_H_COPY_path = shorten_path_kludge(other_A_COPY_H_path)
6961 expected_output = wc.State(short_H_COPY_path, {
6962 'omega' : Item(status='U ')
6964 expected_status = wc.State(short_H_COPY_path, {
6965 '' : Item(status=' M', wc_rev=2),
6966 'psi' : Item(status=' ', wc_rev=2),
6967 'omega' : Item(status='M ', wc_rev=2),
6968 'chi' : Item(status=' ', wc_rev=2),
6970 expected_disk = wc.State('', {
6971 '' : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:6'}),
6972 'psi' : Item("This is the file 'psi'.\n"),
6973 'omega' : Item("New content"),
6974 'chi' : Item("This is the file 'chi'.\n"),
6976 expected_skip = wc.State(short_H_COPY_path, { })
6977 os.chdir(svntest.main.work_dir)
6978 svntest.actions.run_and_verify_merge(short_H_COPY_path, '5', '6',
6979 sbox.repo_url + '/A/D/H',
6980 expected_output, expected_disk,
6981 expected_status, expected_skip,
6982 None, None, None, None, None, 1)
6983 os.chdir(saved_cwd)
6985 # Update A_COPY/D/H in other WC. Local mergeinfo for r6 on A_COPY/D/H
6986 # should be *merged* with r3 from first WC.
6987 expected_output = svntest.wc.State(other_A_COPY_H_path, {
6988 '' : Item(status=' G'),
6989 'psi' : Item(status='U ')
6991 other_disk = wc.State('', {
6992 '' : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:3,6'}),
6993 'psi' : Item(contents="New content"),
6994 'chi' : Item("This is the file 'chi'.\n"),
6995 'omega' : Item(contents="New content"),
6997 other_status = wc.State(other_A_COPY_H_path,{
6998 '' : Item(wc_rev=7, status=' M'),
6999 'chi' : Item(wc_rev=7, status=' '),
7000 'psi' : Item(wc_rev=7, status=' '),
7001 'omega' : Item(wc_rev=7, status='M ')
7003 svntest.actions.run_and_verify_update(other_A_COPY_H_path,
7004 expected_output,
7005 other_disk,
7006 other_status,
7007 check_props=1)
7009 def merge_with_depth_files(sbox):
7010 "merge test for --depth files"
7012 sbox.build()
7013 wc_dir = sbox.wc_dir
7015 # Some paths we'll care about
7016 mu_path = os.path.join(wc_dir, 'A', 'mu')
7017 gamma_path = os.path.join(wc_dir, 'A', 'D', 'gamma')
7018 Acopy_path = os.path.join(wc_dir, 'A_copy')
7019 Acopy_mu_path = os.path.join(wc_dir, 'A_copy', 'mu')
7020 A_url = sbox.repo_url + '/A'
7021 Acopy_url = sbox.repo_url + '/A_copy'
7023 # Copy A_url to A_copy_url
7024 svntest.actions.run_and_verify_svn(None, None, [], 'cp',
7025 A_url, Acopy_url,
7026 '-m', 'create a new copy of A')
7028 svntest.main.file_write(mu_path, "this is file 'mu' modified.\n")
7029 svntest.main.file_write(gamma_path, "this is file 'gamma' modified.\n")
7031 # Create expected output tree for commit
7032 expected_output = wc.State(wc_dir, {
7033 'A/mu' : Item(verb='Sending'),
7034 'A/D/gamma' : Item(verb='Sending'),
7037 # Create expected status tree for commit
7038 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
7039 expected_status.add({
7040 'A/mu' : Item(status=' ', wc_rev=3),
7041 'A/D/gamma' : Item(status=' ', wc_rev=3),
7044 # Commit the modified contents
7045 svntest.actions.run_and_verify_commit(wc_dir,
7046 expected_output,
7047 expected_status,
7048 None,
7049 wc_dir)
7051 # Update working copy
7052 svntest.actions.run_and_verify_svn(None, None, [],
7053 'up', Acopy_path)
7055 # Merge r1:3 into A_copy with --depth files. The merge only affects
7056 # 'A_copy' and its one file child 'mu', so 'A_copy' gets non-inheritable
7057 # mergeinfo for -r1:3 and 'mu' gets its own complete set of mergeinfo:
7058 # r1 from its parent, and r1:3 from the merge itself.
7060 # Search for the comment entitled "The Merge Kluge" elsewhere in
7061 # this file, to understand why we shorten and chdir() below.
7062 short_A_COPY_path = shorten_path_kludge(Acopy_path)
7063 expected_output = wc.State(short_A_COPY_path, {
7064 'mu' : Item(status='U '),
7066 expected_status = wc.State(short_A_COPY_path, {
7067 '' : Item(status=' M'),
7068 'B' : Item(status=' '),
7069 'mu' : Item(status='MM'),
7070 'B/E' : Item(status=' '),
7071 'B/E/alpha' : Item(status=' '),
7072 'B/E/beta' : Item(status=' '),
7073 'B/lambda' : Item(status=' '),
7074 'B/F' : Item(status=' '),
7075 'C' : Item(status=' '),
7076 'D' : Item(status=' '),
7077 'D/G' : Item(status=' '),
7078 'D/G/pi' : Item(status=' '),
7079 'D/G/rho' : Item(status=' '),
7080 'D/G/tau' : Item(status=' '),
7081 'D/gamma' : Item(status=' '),
7082 'D/H' : Item(status=' '),
7083 'D/H/chi' : Item(status=' '),
7084 'D/H/psi' : Item(status=' '),
7085 'D/H/omega' : Item(status=' '),
7087 expected_status.tweak(wc_rev=3)
7088 expected_disk = wc.State('', {
7089 '' : Item(props={SVN_PROP_MERGEINFO : '/A:2-3*'}),
7090 'B' : Item(),
7091 'mu' : Item("this is file 'mu' modified.\n",
7092 props={SVN_PROP_MERGEINFO : '/A/mu:2-3'}),
7093 'B/E' : Item(),
7094 'B/E/alpha' : Item("This is the file 'alpha'.\n"),
7095 'B/E/beta' : Item("This is the file 'beta'.\n"),
7096 'B/lambda' : Item("This is the file 'lambda'.\n"),
7097 'B/F' : Item(),
7098 'C' : Item(),
7099 'D' : Item(),
7100 'D/G' : Item(),
7101 'D/G/pi' : Item("This is the file 'pi'.\n"),
7102 'D/G/rho' : Item("This is the file 'rho'.\n"),
7103 'D/G/tau' : Item("This is the file 'tau'.\n"),
7104 'D/gamma' : Item("This is the file 'gamma'.\n"),
7105 'D/H' : Item(),
7106 'D/H/chi' : Item("This is the file 'chi'.\n"),
7107 'D/H/psi' : Item("This is the file 'psi'.\n"),
7108 'D/H/omega' : Item("This is the file 'omega'.\n"),
7110 expected_skip = wc.State(short_A_COPY_path, { })
7111 saved_cwd = os.getcwd()
7112 os.chdir(svntest.main.work_dir)
7113 svntest.actions.run_and_verify_merge(short_A_COPY_path, '1', '3',
7114 sbox.repo_url + '/A',
7115 expected_output, expected_disk,
7116 expected_status, expected_skip,
7117 None, None, None, None, None, 1, 1,
7118 '--depth', 'files')
7119 os.chdir(saved_cwd)
7121 def merge_fails_if_subtree_is_deleted_on_src(sbox):
7122 "merge fails if subtree is deleted on src"
7124 ## See http://subversion.tigris.org/issues/show_bug.cgi?id=2876. ##
7126 # Create a WC
7127 sbox.build()
7128 wc_dir = sbox.wc_dir
7130 # Some paths we'll care about
7131 Acopy_path = os.path.join(wc_dir, 'A_copy')
7132 gamma_path = os.path.join(wc_dir, 'A', 'D', 'gamma')
7133 Acopy_gamma_path = os.path.join(wc_dir, 'A_copy', 'D', 'gamma')
7134 A_url = sbox.repo_url + '/A'
7135 Acopy_url = sbox.repo_url + '/A_copy'
7137 # Contents to be added to 'gamma'
7138 new_content = "line1\nline2\nline3\nline4\nline5\n"
7140 svntest.main.file_write(gamma_path, new_content)
7142 # Create expected output tree for commit
7143 expected_output = wc.State(wc_dir, {
7144 'A/D/gamma' : Item(verb='Sending'),
7147 # Create expected status tree for commit
7148 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
7149 expected_status.tweak('A/D/gamma', wc_rev=2)
7151 # Commit the new content
7152 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
7153 expected_status, None, wc_dir)
7155 svntest.actions.run_and_verify_svn(None, None, [], 'cp', A_url, Acopy_url,
7156 '-m', 'create a new copy of A')
7158 # Update working copy
7159 svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
7161 svntest.main.file_substitute(gamma_path, "line1", "this is line1")
7162 # Create expected output tree for commit
7163 expected_output = wc.State(wc_dir, {
7164 'A/D/gamma' : Item(verb='Sending'),
7167 # Create expected status tree for commit
7168 expected_status.tweak(wc_rev=3)
7169 expected_status.tweak('A/D/gamma', wc_rev=4)
7170 expected_status.add({
7171 'A_copy' : Item(status=' ', wc_rev=3),
7172 'A_copy/B' : Item(status=' ', wc_rev=3),
7173 'A_copy/B/lambda' : Item(status=' ', wc_rev=3),
7174 'A_copy/B/E' : Item(status=' ', wc_rev=3),
7175 'A_copy/B/E/alpha': Item(status=' ', wc_rev=3),
7176 'A_copy/B/E/beta' : Item(status=' ', wc_rev=3),
7177 'A_copy/B/F' : Item(status=' ', wc_rev=3),
7178 'A_copy/mu' : Item(status=' ', wc_rev=3),
7179 'A_copy/C' : Item(status=' ', wc_rev=3),
7180 'A_copy/D' : Item(status=' ', wc_rev=3),
7181 'A_copy/D/gamma' : Item(status=' ', wc_rev=3),
7182 'A_copy/D/G' : Item(status=' ', wc_rev=3),
7183 'A_copy/D/G/pi' : Item(status=' ', wc_rev=3),
7184 'A_copy/D/G/rho' : Item(status=' ', wc_rev=3),
7185 'A_copy/D/G/tau' : Item(status=' ', wc_rev=3),
7186 'A_copy/D/H' : Item(status=' ', wc_rev=3),
7187 'A_copy/D/H/chi' : Item(status=' ', wc_rev=3),
7188 'A_copy/D/H/omega': Item(status=' ', wc_rev=3),
7189 'A_copy/D/H/psi' : Item(status=' ', wc_rev=3),
7192 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
7193 expected_status, None, wc_dir)
7195 # Delete A/D/gamma from working copy
7196 svntest.actions.run_and_verify_svn(None, None, [], 'delete', gamma_path)
7197 # Create expected output tree for commit
7198 expected_output = wc.State(wc_dir, {
7199 'A/D/gamma' : Item(verb='Deleting'),
7202 expected_status.remove('A/D/gamma')
7204 svntest.actions.run_and_verify_commit(wc_dir,
7205 expected_output,
7206 expected_status,
7207 None,
7208 wc_dir, wc_dir)
7210 svntest.actions.run_and_verify_svn(None, expected_merge_output([[3,4]],
7211 'U ' + Acopy_gamma_path + '\n'),
7212 [], 'merge', '-r1:4',
7213 A_url + '/D/gamma' + '@4',
7214 Acopy_gamma_path)
7216 svntest.actions.run_and_verify_svn(None, expected_merge_output([[5]],
7217 'D ' + Acopy_gamma_path + '\n'),
7218 [], 'merge', '-r1:5', '--force',
7219 A_url, Acopy_path)
7221 # Test for issue #2976 Subtrees can lose non-inheritable ranges
7222 def merge_away_subtrees_noninheritable_ranges(sbox):
7223 "subtrees can lose non-inheritable ranges"
7225 sbox.build()
7226 wc_dir = sbox.wc_dir
7227 wc_disk, wc_status = set_up_branch(sbox)
7229 # Some paths we'll care about
7230 H_path = os.path.join(wc_dir, "A", "D", "H")
7231 D_COPY_path = os.path.join(wc_dir, "A_COPY", "D")
7233 # Make a change to directory A/D/H and commit as r7.
7234 svntest.actions.run_and_verify_svn(None, ['At revision 6.\n'], [],
7235 'update', wc_dir)
7237 svntest.actions.run_and_verify_svn(
7238 None, ["property 'prop:name' set on '" + H_path + "'\n"], [],
7239 'ps', 'prop:name', 'propval', H_path)
7240 expected_output = svntest.wc.State(wc_dir, {
7241 'A/D/H' : Item(verb='Sending'),})
7242 wc_status.tweak(wc_rev=6)
7243 wc_status.tweak('A/D/H', wc_rev=7)
7244 svntest.actions.run_and_verify_commit(wc_dir, expected_output, wc_status,
7245 None, wc_dir)
7247 # Merge r5:7 --depth immediates to A_COPY/D. This should merge the
7248 # prop change from r7 to A_COPY/H but not the change to A_COPY/D/H/omega
7249 # from r6 since that is below the depth we are merging to. Instead,
7250 # non-inheritable mergeinfo should be set on the immediate directory
7251 # children of A_COPY/D: A_COPY/D/G and A_COPY/D/H.
7253 # Search for the comment entitled "The Merge Kluge" elsewhere in
7254 # this file, to understand why we shorten and chdir() below.
7255 short_D_COPY_path = shorten_path_kludge(D_COPY_path)
7256 expected_output = wc.State(short_D_COPY_path, {
7257 'H' : Item(status=' U'),
7259 expected_status = wc.State(short_D_COPY_path, {
7260 '' : Item(status=' M', wc_rev=6),
7261 'H' : Item(status=' M', wc_rev=6),
7262 'H/chi' : Item(status=' ', wc_rev=6),
7263 'H/omega' : Item(status=' ', wc_rev=6),
7264 'H/psi' : Item(status=' ', wc_rev=6),
7265 'G' : Item(status=' M', wc_rev=6),
7266 'G/pi' : Item(status=' ', wc_rev=6),
7267 'G/rho' : Item(status=' ', wc_rev=6),
7268 'G/tau' : Item(status=' ', wc_rev=6),
7269 'gamma' : Item(status=' ', wc_rev=6),
7271 expected_disk = wc.State('', {
7272 '' : Item(props={SVN_PROP_MERGEINFO : '/A/D:6-7'}),
7273 'H' : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:6-7*',
7274 'prop:name' : 'propval'}),
7275 'H/chi' : Item("This is the file 'chi'.\n"),
7276 'H/omega' : Item("This is the file 'omega'.\n"),
7277 'H/psi' : Item("This is the file 'psi'.\n"),
7278 'G' : Item(props={SVN_PROP_MERGEINFO : '/A/D/G:6-7*'}),
7279 'G/pi' : Item("This is the file 'pi'.\n"),
7280 'G/rho' : Item("This is the file 'rho'.\n"),
7281 'G/tau' : Item("This is the file 'tau'.\n"),
7282 'gamma' : Item("This is the file 'gamma'.\n"),
7284 expected_skip = wc.State(short_D_COPY_path, { })
7285 saved_cwd = os.getcwd()
7286 os.chdir(svntest.main.work_dir)
7287 svntest.actions.run_and_verify_merge(short_D_COPY_path, '5', '7',
7288 sbox.repo_url + '/A/D',
7289 expected_output, expected_disk,
7290 expected_status, expected_skip,
7291 None, None, None, None, None, 1, 1,
7292 '--depth', 'immediates')
7293 os.chdir(saved_cwd)
7295 # Repeat the previous merge but at default depth of infinity. The change
7296 # to A_COPY/D/H/omega should now happen and the non-inheritable ranges on
7297 # A_COPY/D/G and A_COPY/D/H be changed to inheritable and then elide to
7298 # A_COPY/D.
7299 expected_output = wc.State(short_D_COPY_path, {
7300 'H/omega' : Item(status='U '),
7302 expected_disk.tweak('', props={SVN_PROP_MERGEINFO : '/A/D:6-7'})
7303 expected_disk.tweak('H', props={'prop:name' : 'propval'})
7304 expected_disk.tweak('G', props={})
7305 expected_disk.tweak('H/omega', contents="New content")
7306 expected_status.tweak('G', status=' ')
7307 expected_status.tweak('H/omega', status='M ')
7308 os.chdir(svntest.main.work_dir)
7309 svntest.actions.run_and_verify_merge(short_D_COPY_path, '5', '7',
7310 sbox.repo_url + '/A/D',
7311 expected_output, expected_disk,
7312 expected_status, expected_skip,
7313 None, None, None, None, None, 1, 1)
7314 os.chdir(saved_cwd)
7316 # Test for issue #2827
7317 # Handle merge info for sparsely-populated directories
7318 def merge_to_sparse_directories(sbox):
7319 "merge to sparse directories"
7321 # Merges into sparse working copies should set non-inheritable mergeinfo
7322 # on the deepest directories present in the WC.
7324 sbox.build()
7325 wc_dir = sbox.wc_dir
7326 wc_disk, wc_status = set_up_branch(sbox, False, 1)
7328 # Some paths we'll care about
7329 A_path = os.path.join(wc_dir, "A")
7330 D_path = os.path.join(wc_dir, "A", "D")
7331 I_path = os.path.join(wc_dir, "A", "C", "I")
7332 G_path = os.path.join(wc_dir, "A", "D", "G")
7333 A_COPY_path = os.path.join(wc_dir, "A_COPY")
7335 # Make a few more changes to the merge source...
7337 # r7 - modify and commit A/mu
7338 svntest.main.file_write(os.path.join(wc_dir, "A", "mu"),
7339 "New content")
7340 expected_output = wc.State(wc_dir, {'A/mu' : Item(verb='Sending')})
7341 wc_status.tweak('A/mu', wc_rev=7)
7342 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
7343 wc_status, None, wc_dir)
7344 wc_disk.tweak('A/mu', contents="New content")
7346 # r8 - Add a prop to A/D and commit.
7347 svntest.actions.run_and_verify_svn(None, ["At revision 7.\n"], [],
7348 'up', wc_dir)
7349 svntest.actions.run_and_verify_svn(None,
7350 ["property 'prop:name' set on '" +
7351 D_path + "'\n"], [], 'ps',
7352 'prop:name', 'propval', D_path)
7353 expected_output = svntest.wc.State(wc_dir, {
7354 'A/D' : Item(verb='Sending'),
7356 wc_status.tweak(wc_rev=7)
7357 wc_status.tweak('A/D', wc_rev=8)
7358 svntest.actions.run_and_verify_commit(wc_dir, expected_output, wc_status,
7359 None, wc_dir)
7361 # r9 - Add a prop to A and commit.
7362 svntest.actions.run_and_verify_svn(None, ["At revision 8.\n"], [],
7363 'up', wc_dir)
7364 svntest.actions.run_and_verify_svn(None,
7365 ["property 'prop:name' set on '" +
7366 A_path + "'\n"], [], 'ps',
7367 'prop:name', 'propval', A_path)
7368 expected_output = svntest.wc.State(wc_dir, {
7369 'A' : Item(verb='Sending'),
7371 wc_status.tweak(wc_rev=8)
7372 wc_status.tweak('A', wc_rev=9)
7373 svntest.actions.run_and_verify_commit(wc_dir, expected_output, wc_status,
7374 None, wc_dir)
7376 # Do an --immediates checkout of A_COPY
7377 immediates_dir = sbox.add_wc_path('immediates')
7378 expected_output = wc.State(immediates_dir, {
7379 'B' : Item(status='A '),
7380 'mu' : Item(status='A '),
7381 'C' : Item(status='A '),
7382 'D' : Item(status='A '),
7384 expected_disk = wc.State('', {
7385 'B' : Item(),
7386 'mu' : Item("This is the file 'mu'.\n"),
7387 'C' : Item(),
7388 'D' : Item(),
7390 svntest.actions.run_and_verify_checkout(sbox.repo_url + "/A_COPY",
7391 immediates_dir,
7392 expected_output, expected_disk,
7393 None, None, None, None,
7394 "--depth", "immediates")
7396 # Merge r4:9 into the immediates WC.
7397 # The root of the immediates WC should get inheritable r4:9 as should
7398 # the one file present 'mu'. The three directory children present, 'B',
7399 # 'C', and 'D' are checked out at depth empty, so get non-inheritable
7400 # mergeinfo for r4:9. The root and 'D' do should also get the changes
7401 # that affect them directly (the prop adds from r8 and r9). Any changes
7402 # deeper than the immediates should be skipped.
7404 # Search for the comment entitled "The Merge Kluge" elsewhere in
7405 # this file, to understand why we shorten and chdir() below.
7406 short_immediates_dir = shorten_path_kludge(immediates_dir)
7407 expected_output = wc.State(short_immediates_dir, {
7408 'D' : Item(status=' U'),
7409 'mu' : Item(status='U '),
7410 '' : Item(status=' U'),
7412 expected_status = wc.State(short_immediates_dir, {
7413 '' : Item(status=' M', wc_rev=9),
7414 'B' : Item(status=' M', wc_rev=9),
7415 'mu' : Item(status='M ', wc_rev=9),
7416 'C' : Item(status=' M', wc_rev=9),
7417 'D' : Item(status=' M', wc_rev=9),
7419 expected_disk = wc.State('', {
7420 '' : Item(props={SVN_PROP_MERGEINFO : '/A:5-9',
7421 "prop:name" : "propval"}),
7422 'B' : Item(props={SVN_PROP_MERGEINFO : '/A/B:5-9*'}),
7423 'mu' : Item("New content"),
7424 'C' : Item(props={SVN_PROP_MERGEINFO : '/A/C:5-9*'}),
7425 'D' : Item(props={SVN_PROP_MERGEINFO : '/A/D:5-9*',
7426 "prop:name" : "propval"}),
7428 expected_skip = wc.State(short_immediates_dir, {})
7429 saved_cwd = os.getcwd()
7430 os.chdir(svntest.main.work_dir)
7431 svntest.actions.run_and_verify_merge(short_immediates_dir, '4', '9',
7432 sbox.repo_url + \
7433 '/A',
7434 expected_output,
7435 expected_disk,
7436 expected_status,
7437 expected_skip,
7438 None, None, None, None,
7439 None, 1)
7440 os.chdir(saved_cwd)
7442 # Do a --files checkout of A_COPY
7443 files_dir = sbox.add_wc_path('files')
7444 expected_output = wc.State(files_dir, {
7445 'mu' : Item(status='A '),
7447 expected_disk = wc.State('', {
7448 'mu' : Item("This is the file 'mu'.\n"),
7450 svntest.actions.run_and_verify_checkout(sbox.repo_url + "/A_COPY",
7451 files_dir,
7452 expected_output, expected_disk,
7453 None, None, None, None,
7454 "--depth", "files")
7456 # Merge r4:9 into the files WC.
7457 # The root of the files WC should get non-inheritable r4:9 and its one
7458 # present child 'mu' should get the same but inheritable. The root
7459 # should also get the change that affects it directly (the prop add
7460 # from r9).
7461 short_files_dir = shorten_path_kludge(files_dir)
7462 expected_output = wc.State(short_files_dir, {
7463 'mu' : Item(status='U '),
7464 '' : Item(status=' U'),
7466 expected_status = wc.State(short_files_dir, {
7467 '' : Item(status=' M', wc_rev=9),
7468 'mu' : Item(status='MM', wc_rev=9),
7470 expected_disk = wc.State('', {
7471 '' : Item(props={SVN_PROP_MERGEINFO : '/A:5-9*',
7472 "prop:name" : "propval"}),
7473 'mu' : Item("New content",
7474 props={SVN_PROP_MERGEINFO : '/A/mu:5-9'}),
7476 expected_skip = wc.State(short_files_dir, {})
7477 os.chdir(svntest.main.work_dir)
7478 svntest.actions.run_and_verify_merge(short_files_dir, '4', '9',
7479 sbox.repo_url + \
7480 '/A',
7481 expected_output,
7482 expected_disk,
7483 expected_status,
7484 expected_skip,
7485 None, None, None, None,
7486 None, 1)
7487 os.chdir(saved_cwd)
7489 # Do an --empty checkout of A_COPY
7490 empty_dir = sbox.add_wc_path('empty')
7491 expected_output = wc.State(empty_dir, {})
7492 expected_disk = wc.State('', {})
7493 svntest.actions.run_and_verify_checkout(sbox.repo_url + "/A_COPY",
7494 empty_dir,
7495 expected_output, expected_disk,
7496 None, None, None, None,
7497 "--depth", "empty")
7499 # Merge r4:9 into the empty WC.
7500 # The root of the files WC should get non-inheritable r4:9 and also get
7501 # the one change that affects it directly (the prop add from r9).
7502 short_empty_dir = shorten_path_kludge(empty_dir)
7503 expected_output = wc.State(short_empty_dir, {
7504 '' : Item(status=' U'),
7506 expected_status = wc.State(short_empty_dir, {
7507 '' : Item(status=' M', wc_rev=9),
7509 expected_disk = wc.State('', {
7510 '' : Item(props={SVN_PROP_MERGEINFO : '/A:5-9*',
7511 "prop:name" : "propval"}),
7513 expected_skip = wc.State(short_empty_dir, {})
7514 os.chdir(svntest.main.work_dir)
7515 svntest.actions.run_and_verify_merge(short_empty_dir, '4', '9',
7516 sbox.repo_url + \
7517 '/A',
7518 expected_output,
7519 expected_disk,
7520 expected_status,
7521 expected_skip,
7522 None, None, None, None,
7523 None, 1)
7524 os.chdir(saved_cwd)
7526 def merge_old_and_new_revs_from_renamed_dir(sbox):
7527 "merge -rold(before rename):head renamed dir"
7529 ## See http://svn.haxx.se/dev/archive-2007-09/0706.shtml ##
7531 # Create a WC with a single branch
7532 sbox.build()
7533 wc_dir = sbox.wc_dir
7534 wc_disk, wc_status = set_up_branch(sbox, True, 1)
7536 # Some paths we'll care about
7537 A_url = sbox.repo_url + '/A'
7538 A_MOVED_url = sbox.repo_url + '/A_MOVED'
7539 A_COPY_path = os.path.join(wc_dir, 'A_COPY')
7540 mu_path = os.path.join(wc_dir, 'A', 'mu')
7541 A_MOVED_mu_path = os.path.join(wc_dir, 'A_MOVED', 'mu')
7543 # Make a modification to A/mu
7544 svntest.main.file_write(mu_path, "This is the file 'mu' modified.\n")
7545 expected_output = wc.State(wc_dir, {'A/mu' : Item(verb='Sending')})
7546 wc_status.add({'A/mu' : Item(status=' ', wc_rev=3)})
7547 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
7548 wc_status, None, wc_dir)
7550 # Move A to A_MOVED
7551 svntest.actions.run_and_verify_svn(None, ['\n', 'Committed revision 4.\n'],
7552 [], 'mv', '-m', 'mv A to A_MOVED',
7553 A_url, A_MOVED_url)
7555 # Update the working copy to get A_MOVED
7556 svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
7558 # Make a modification to A_MOVED/mu
7559 svntest.main.file_write(A_MOVED_mu_path, "This is 'mu' in A_MOVED.\n")
7560 expected_output = wc.State(wc_dir, {'A_MOVED/mu' : Item(verb='Sending')})
7561 expected_status = svntest.actions.get_virginal_state(wc_dir, 4)
7562 expected_status.remove('A', 'A/mu', 'A/C', 'A/D', 'A/B', 'A/B/lambda',
7563 'A/B/E', 'A/B/E/alpha', 'A/B/E/beta', 'A/B/F',
7564 'A/D/gamma', 'A/D/G', 'A/D/G/pi', 'A/D/G/rho',
7565 'A/D/G/tau', 'A/D/H', 'A/D/H/chi', 'A/D/H/omega',
7566 'A/D/H/psi')
7567 expected_status.add({
7568 '' : Item(status=' ', wc_rev=4),
7569 'iota' : Item(status=' ', wc_rev=4),
7570 'A_MOVED' : Item(status=' ', wc_rev=4),
7571 'A_MOVED/mu' : Item(status=' ', wc_rev=5),
7572 'A_MOVED/C' : Item(status=' ', wc_rev=4),
7573 'A_MOVED/D' : Item(status=' ', wc_rev=4),
7574 'A_MOVED/B' : Item(status=' ', wc_rev=4),
7575 'A_MOVED/B/lambda' : Item(status=' ', wc_rev=4),
7576 'A_MOVED/B/E' : Item(status=' ', wc_rev=4),
7577 'A_MOVED/B/E/alpha': Item(status=' ', wc_rev=4),
7578 'A_MOVED/B/E/beta' : Item(status=' ', wc_rev=4),
7579 'A_MOVED/B/F' : Item(status=' ', wc_rev=4),
7580 'A_MOVED/D/gamma' : Item(status=' ', wc_rev=4),
7581 'A_MOVED/D/G' : Item(status=' ', wc_rev=4),
7582 'A_MOVED/D/G/pi' : Item(status=' ', wc_rev=4),
7583 'A_MOVED/D/G/rho' : Item(status=' ', wc_rev=4),
7584 'A_MOVED/D/G/tau' : Item(status=' ', wc_rev=4),
7585 'A_MOVED/D/H' : Item(status=' ', wc_rev=4),
7586 'A_MOVED/D/H/chi' : Item(status=' ', wc_rev=4),
7587 'A_MOVED/D/H/omega': Item(status=' ', wc_rev=4),
7588 'A_MOVED/D/H/psi' : Item(status=' ', wc_rev=4),
7589 'A_COPY' : Item(status=' ', wc_rev=4),
7590 'A_COPY/mu' : Item(status=' ', wc_rev=4),
7591 'A_COPY/C' : Item(status=' ', wc_rev=4),
7592 'A_COPY/D' : Item(status=' ', wc_rev=4),
7593 'A_COPY/B' : Item(status=' ', wc_rev=4),
7594 'A_COPY/B/lambda' : Item(status=' ', wc_rev=4),
7595 'A_COPY/B/E' : Item(status=' ', wc_rev=4),
7596 'A_COPY/B/E/alpha' : Item(status=' ', wc_rev=4),
7597 'A_COPY/B/E/beta' : Item(status=' ', wc_rev=4),
7598 'A_COPY/B/F' : Item(status=' ', wc_rev=4),
7599 'A_COPY/D/gamma' : Item(status=' ', wc_rev=4),
7600 'A_COPY/D/G' : Item(status=' ', wc_rev=4),
7601 'A_COPY/D/G/pi' : Item(status=' ', wc_rev=4),
7602 'A_COPY/D/G/rho' : Item(status=' ', wc_rev=4),
7603 'A_COPY/D/G/tau' : Item(status=' ', wc_rev=4),
7604 'A_COPY/D/H' : Item(status=' ', wc_rev=4),
7605 'A_COPY/D/H/chi' : Item(status=' ', wc_rev=4),
7606 'A_COPY/D/H/omega' : Item(status=' ', wc_rev=4),
7607 'A_COPY/D/H/psi' : Item(status=' ', wc_rev=4),
7609 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
7610 expected_status, None, wc_dir)
7612 short_A_COPY = shorten_path_kludge(A_COPY_path)
7613 saved_cwd = os.getcwd()
7614 os.chdir(svntest.main.work_dir)
7616 # Merge /A_MOVED to /A_COPY - this happens in multiple passes
7617 # because /A_MOVED has renames in its history between the boundaries
7618 # of the requested merge range.
7619 expected_output = wc.State(short_A_COPY, {
7620 'mu' : Item(status='G '), # mu gets touched twice
7622 expected_status = wc.State(short_A_COPY, {
7623 '' : Item(status=' M', wc_rev=4),
7624 'mu' : Item(status='M ', wc_rev=4),
7625 'C' : Item(status=' ', wc_rev=4),
7626 'D' : Item(status=' ', wc_rev=4),
7627 'B' : Item(status=' ', wc_rev=4),
7628 'B/lambda' : Item(status=' ', wc_rev=4),
7629 'B/E' : Item(status=' ', wc_rev=4),
7630 'B/E/alpha': Item(status=' ', wc_rev=4),
7631 'B/E/beta' : Item(status=' ', wc_rev=4),
7632 'B/F' : Item(status=' ', wc_rev=4),
7633 'D/gamma' : Item(status=' ', wc_rev=4),
7634 'D/G' : Item(status=' ', wc_rev=4),
7635 'D/G/pi' : Item(status=' ', wc_rev=4),
7636 'D/G/rho' : Item(status=' ', wc_rev=4),
7637 'D/G/tau' : Item(status=' ', wc_rev=4),
7638 'D/H' : Item(status=' ', wc_rev=4),
7639 'D/H/chi' : Item(status=' ', wc_rev=4),
7640 'D/H/omega': Item(status=' ', wc_rev=4),
7641 'D/H/psi' : Item(status=' ', wc_rev=4),
7643 expected_disk = wc.State('', {
7644 '' : Item(props={SVN_PROP_MERGEINFO : '/A:3\n/A_MOVED:4-5\n'}),
7645 'mu' : Item("This is 'mu' in A_MOVED.\n"),
7646 'C' : Item(),
7647 'D' : Item(),
7648 'B' : Item(),
7649 'B/lambda' : Item("This is the file 'lambda'.\n"),
7650 'B/E' : Item(),
7651 'B/E/alpha': Item("This is the file 'alpha'.\n"),
7652 'B/E/beta' : Item("This is the file 'beta'.\n"),
7653 'B/F' : Item(),
7654 'D/gamma' : Item("This is the file 'gamma'.\n"),
7655 'D/G' : Item(),
7656 'D/G/pi' : Item("This is the file 'pi'.\n"),
7657 'D/G/rho' : Item("This is the file 'rho'.\n"),
7658 'D/G/tau' : Item("This is the file 'tau'.\n"),
7659 'D/H' : Item(),
7660 'D/H/chi' : Item("This is the file 'chi'.\n"),
7661 'D/H/omega': Item("This is the file 'omega'.\n"),
7662 'D/H/psi' : Item("This is the file 'psi'.\n"),
7664 expected_skip = wc.State(short_A_COPY, {})
7666 ### Disabling dry_run mode because currently it can't handle the way
7667 ### 'mu' gets textually modified in multiple passes.
7668 svntest.actions.run_and_verify_merge(short_A_COPY, '2', '5',
7669 A_MOVED_url,
7670 expected_output,
7671 expected_disk,
7672 expected_status,
7673 expected_skip,
7674 None, None, None, None, None,
7675 True, False)
7676 os.chdir(saved_cwd)
7678 def merge_with_child_having_different_rev_ranges_to_merge(sbox):
7679 "child having different rev ranges to merge"
7680 #Modify A/mu to 30 lines with a content 'line1'...'line30' commit it at r2.
7681 #Create a branch A_COPY from A, commit it at r3.
7682 #Modify A/mu line number 7 to 'LINE7' modify and commit at r4.
7683 #Modify A/mu line number 17 to 'LINE17' modify, set prop 'prop1' on 'A'
7684 #with a value 'val1' and commit at r5.
7685 #Modify A/mu line number 27 to 'LINE27' modify and commit at r6.
7686 #Merge r5 to 'A/mu' as a single file merge explicitly to 'A_COPY/mu'.
7687 #Merge r3:6 from 'A' to 'A_COPY
7688 #This should merge r4 and then r5 through r6.
7689 #Revert r5 and r6 via single file merge on A_COPY/mu.
7690 #Revert r6 through r4 on A_COPY this should get back us the pristine copy.
7691 #Merge r3:6 from 'A' to 'A_COPY
7692 #Revert r5 on A_COPY/mu
7693 #Modify line number 17 with 'some other line17' of A_COPY/mu
7694 #Merge r6:3 from 'A' to 'A_COPY, This should leave line number 17
7695 #undisturbed in A_COPY/mu, rest should be reverted.
7697 # Create a WC
7698 sbox.build()
7699 wc_dir = sbox.wc_dir
7700 A_path = os.path.join(wc_dir, 'A')
7701 mu_path = os.path.join(wc_dir, 'A', 'mu')
7702 A_url = sbox.repo_url + '/A'
7703 A_mu_url = sbox.repo_url + '/A/mu'
7704 A_COPY_url = sbox.repo_url + '/A_COPY'
7705 A_COPY_path = os.path.join(wc_dir, 'A_COPY')
7706 A_COPY_mu_path = os.path.join(wc_dir, 'A_COPY', 'mu')
7707 thirty_line_dummy_text = 'line1\n'
7708 for i in range(2, 31):
7709 thirty_line_dummy_text += 'line' + str(i) + '\n'
7711 svntest.main.file_write(mu_path, thirty_line_dummy_text)
7712 expected_output = wc.State(wc_dir, {'A/mu' : Item(verb='Sending')})
7713 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
7714 expected_status.tweak('A/mu', wc_rev=2)
7715 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
7716 expected_status, None, wc_dir)
7717 svntest.actions.run_and_verify_svn(None, None, [],
7718 'cp', A_url, A_COPY_url, '-m', 'rev 3')
7719 # Update the working copy to get A_COPY
7720 svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
7721 expected_status.add({'A_COPY' : Item(status=' '),
7722 'A_COPY/mu' : Item(status=' '),
7723 'A_COPY/C' : Item(status=' '),
7724 'A_COPY/D' : Item(status=' '),
7725 'A_COPY/B' : Item(status=' '),
7726 'A_COPY/B/lambda' : Item(status=' '),
7727 'A_COPY/B/E' : Item(status=' '),
7728 'A_COPY/B/E/alpha' : Item(status=' '),
7729 'A_COPY/B/E/beta' : Item(status=' '),
7730 'A_COPY/B/F' : Item(status=' '),
7731 'A_COPY/D/gamma' : Item(status=' '),
7732 'A_COPY/D/G' : Item(status=' '),
7733 'A_COPY/D/G/pi' : Item(status=' '),
7734 'A_COPY/D/G/rho' : Item(status=' '),
7735 'A_COPY/D/G/tau' : Item(status=' '),
7736 'A_COPY/D/H' : Item(status=' '),
7737 'A_COPY/D/H/chi' : Item(status=' '),
7738 'A_COPY/D/H/omega' : Item(status=' '),
7739 'A_COPY/D/H/psi' : Item(status=' ')})
7740 expected_status.tweak(wc_rev=3)
7741 tweaked_7th_line = thirty_line_dummy_text.replace('line7', 'LINE 7')
7742 svntest.main.file_write(mu_path, tweaked_7th_line)
7743 expected_status.tweak('A/mu', wc_rev=4)
7744 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
7745 expected_status, None, wc_dir)
7746 svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
7747 expected_status.tweak(wc_rev=4)
7748 tweaked_17th_line = tweaked_7th_line.replace('line17', 'LINE 17')
7749 svntest.main.file_write(mu_path, tweaked_17th_line)
7750 svntest.main.run_svn(None, 'propset', 'prop1', 'val1', A_path)
7751 expected_output = wc.State(wc_dir,
7753 'A' : Item(verb='Sending'),
7754 'A/mu' : Item(verb='Sending')
7757 expected_status.tweak('A', wc_rev=5)
7758 expected_status.tweak('A/mu', wc_rev=5)
7759 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
7760 expected_status, None, wc_dir)
7761 tweaked_27th_line = tweaked_17th_line.replace('line27', 'LINE 27')
7762 svntest.main.file_write(mu_path, tweaked_27th_line)
7763 expected_status.tweak('A/mu', wc_rev=6)
7764 expected_output = wc.State(wc_dir, {'A/mu' : Item(verb='Sending')})
7765 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
7766 expected_status, None, wc_dir)
7767 # Merge r5 to A_COPY/mu
7768 svntest.actions.run_and_verify_svn(None,
7769 expected_merge_output([[5]],
7770 ['U ' + A_COPY_mu_path + '\n']),
7772 'merge', '-r4:5',
7773 A_mu_url,
7774 A_COPY_mu_path)
7776 short_A_COPY = shorten_path_kludge(A_COPY_path)
7777 saved_cwd = os.getcwd()
7778 os.chdir(svntest.main.work_dir)
7779 expected_skip = wc.State(short_A_COPY, {})
7780 expected_output = wc.State(short_A_COPY, {
7781 '' : Item(status=' U'),
7782 'mu' : Item(status='G '),
7784 expected_status = wc.State(short_A_COPY, {
7785 '' : Item(status=' M', wc_rev=4),
7786 'mu' : Item(status='M ', wc_rev=4),
7787 'C' : Item(status=' ', wc_rev=4),
7788 'D' : Item(status=' ', wc_rev=4),
7789 'B' : Item(status=' ', wc_rev=4),
7790 'B/lambda' : Item(status=' ', wc_rev=4),
7791 'B/E' : Item(status=' ', wc_rev=4),
7792 'B/E/alpha': Item(status=' ', wc_rev=4),
7793 'B/E/beta' : Item(status=' ', wc_rev=4),
7794 'B/F' : Item(status=' ', wc_rev=4),
7795 'D/gamma' : Item(status=' ', wc_rev=4),
7796 'D/G' : Item(status=' ', wc_rev=4),
7797 'D/G/pi' : Item(status=' ', wc_rev=4),
7798 'D/G/rho' : Item(status=' ', wc_rev=4),
7799 'D/G/tau' : Item(status=' ', wc_rev=4),
7800 'D/H' : Item(status=' ', wc_rev=4),
7801 'D/H/chi' : Item(status=' ', wc_rev=4),
7802 'D/H/omega': Item(status=' ', wc_rev=4),
7803 'D/H/psi' : Item(status=' ', wc_rev=4),
7805 expected_disk = wc.State('', {
7806 '' : Item(props={SVN_PROP_MERGEINFO : '/A:4-6',
7807 'prop1' : 'val1'}),
7808 'mu' : Item(tweaked_27th_line),
7809 'C' : Item(),
7810 'D' : Item(),
7811 'B' : Item(),
7812 'B/lambda' : Item("This is the file 'lambda'.\n"),
7813 'B/E' : Item(),
7814 'B/E/alpha': Item("This is the file 'alpha'.\n"),
7815 'B/E/beta' : Item("This is the file 'beta'.\n"),
7816 'B/F' : Item(),
7817 'D/gamma' : Item("This is the file 'gamma'.\n"),
7818 'D/G' : Item(),
7819 'D/G/pi' : Item("This is the file 'pi'.\n"),
7820 'D/G/rho' : Item("This is the file 'rho'.\n"),
7821 'D/G/tau' : Item("This is the file 'tau'.\n"),
7822 'D/H' : Item(),
7823 'D/H/chi' : Item("This is the file 'chi'.\n"),
7824 'D/H/omega': Item("This is the file 'omega'.\n"),
7825 'D/H/psi' : Item("This is the file 'psi'.\n"),
7827 svntest.actions.run_and_verify_merge(short_A_COPY, '3', '6',
7828 A_url,
7829 expected_output,
7830 expected_disk,
7831 expected_status,
7832 expected_skip,
7833 None, None, None, None, None, 1)
7834 os.chdir(saved_cwd)
7835 # Revert r5 and r6 on A_COPY/mu
7836 svntest.actions.run_and_verify_svn(None,
7837 expected_merge_output([[6,5]],
7838 ['G ' + A_COPY_mu_path + '\n']),
7840 'merge', '-r6:4',
7841 A_mu_url,
7842 A_COPY_mu_path)
7844 expected_output = wc.State(short_A_COPY, {
7845 '' : Item(status=' G'), # merged removal of prop1 property
7846 'mu' : Item(status='G '), # merged reversion of text changes
7849 expected_status.tweak('', status=' ')
7850 expected_status.tweak('mu', status=' ')
7851 expected_disk.tweak('', props={})
7852 expected_disk.remove('')
7853 expected_disk.tweak('mu', contents=thirty_line_dummy_text)
7854 os.chdir(svntest.main.work_dir)
7855 svntest.actions.run_and_verify_merge(short_A_COPY, '6', '3',
7856 A_url,
7857 expected_output,
7858 expected_disk,
7859 expected_status,
7860 expected_skip,
7861 None, None, None, None, None, 1)
7862 os.chdir(saved_cwd)
7864 expected_disk.add({'' : Item(props={SVN_PROP_MERGEINFO : '/A:4-6',
7865 'prop1' : 'val1'})})
7866 expected_disk.tweak('mu', contents=tweaked_27th_line)
7867 expected_output = wc.State(short_A_COPY, {
7868 '' : Item(status=' U'), # new mergeinfo and prop1 property
7869 'mu' : Item(status='U '), # text changes
7871 expected_status.tweak('', status=' M')
7872 expected_status.tweak('mu', status='M ')
7873 os.chdir(svntest.main.work_dir)
7874 svntest.actions.run_and_verify_merge(short_A_COPY, '3', '6',
7875 A_url,
7876 expected_output,
7877 expected_disk,
7878 expected_status,
7879 expected_skip,
7880 None, None, None, None, None, 1)
7881 os.chdir(saved_cwd)
7882 #Revert r5 on A_COPY/mu
7883 svntest.actions.run_and_verify_svn(None,
7884 expected_merge_output([[-5]],
7885 ['G ' + A_COPY_mu_path + '\n']),
7887 'merge', '-r5:4',
7888 A_mu_url,
7889 A_COPY_mu_path)
7890 tweaked_17th_line_1 = tweaked_27th_line.replace('LINE 17',
7891 'some other line17')
7892 tweaked_17th_line_2 = thirty_line_dummy_text.replace('line17',
7893 'some other line17')
7894 svntest.main.file_write(A_COPY_mu_path, tweaked_17th_line_1)
7895 expected_output = wc.State(short_A_COPY, {
7896 '' : Item(status=' G'),
7897 'mu' : Item(status='G '),
7899 expected_status.tweak('', status=' ')
7900 expected_status.tweak('mu', status='M ')
7901 expected_disk.remove('')
7902 expected_disk.tweak('mu', contents=tweaked_17th_line_2)
7903 os.chdir(svntest.main.work_dir)
7904 svntest.actions.run_and_verify_merge(short_A_COPY, '6', '3',
7905 A_url,
7906 expected_output,
7907 expected_disk,
7908 expected_status,
7909 expected_skip,
7910 None, None, None, None, None, 1)
7911 os.chdir(saved_cwd)
7913 def merge_old_and_new_revs_from_renamed_file(sbox):
7914 "merge -rold(before rename):head renamed file"
7916 ## See http://svn.haxx.se/dev/archive-2007-09/0706.shtml ##
7918 # Create a WC
7919 sbox.build()
7920 wc_dir = sbox.wc_dir
7922 # Some paths we'll care about
7923 mu_url = sbox.repo_url + '/A/mu'
7924 mu_MOVED_url = sbox.repo_url + '/A/mu_MOVED'
7925 mu_COPY_url = sbox.repo_url + '/A/mu_COPY'
7926 mu_COPY_path = os.path.join(wc_dir, 'A', 'mu_COPY')
7927 mu_path = os.path.join(wc_dir, 'A', 'mu')
7928 mu_MOVED_path = os.path.join(wc_dir, 'A', 'mu_MOVED')
7930 # Copy mu to mu_COPY
7931 svntest.actions.run_and_verify_svn(None, ['\n', 'Committed revision 2.\n'],
7932 [], 'cp', '-m', 'cp mu to mu_COPY',
7933 mu_url, mu_COPY_url)
7935 # Make a modification to A/mu
7936 svntest.main.file_write(mu_path, "This is the file 'mu' modified.\n")
7937 expected_output = wc.State(wc_dir, {'A/mu' : Item(verb='Sending')})
7938 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
7939 expected_status.tweak('A/mu', wc_rev=3)
7940 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
7941 expected_status, None, wc_dir)
7943 # Move mu to mu_MOVED
7944 svntest.actions.run_and_verify_svn(None, ['\n', 'Committed revision 4.\n'],
7945 [], 'mv', '-m', 'mv mu to mu_MOVED',
7946 mu_url, mu_MOVED_url)
7948 # Update the working copy to get mu_MOVED
7949 svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
7951 # Make a modification to mu_MOVED
7952 svntest.main.file_write(mu_MOVED_path, "This is 'mu' in mu_MOVED.\n")
7953 expected_output = wc.State(wc_dir, {'A/mu_MOVED' : Item(verb='Sending')})
7954 expected_status = svntest.actions.get_virginal_state(wc_dir, 4)
7955 expected_status.remove('A/mu')
7956 expected_status.add({
7957 'A/mu_MOVED' : Item(status=' ', wc_rev=5),
7958 'A/mu_COPY' : Item(status=' ', wc_rev=4),
7960 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
7961 expected_status, None, wc_dir)
7963 # Merge A/mu_MOVED to A/mu_COPY - this happens in multiple passes
7964 # because A/mu_MOVED has renames in its history between the
7965 # boundaries of the requested merge range.
7966 expected_output = expected_merge_output([[2,3],[4,5]],
7967 ['U %s\n' % (mu_COPY_path),
7968 'G %s\n' % (mu_COPY_path)])
7969 svntest.actions.run_and_verify_svn(None, expected_output,
7970 [], 'merge', '-r', '1:5',
7971 mu_MOVED_url,
7972 mu_COPY_path)
7973 svntest.actions.run_and_verify_svn(None, ['/A/mu:2-3\n',
7974 '/A/mu_MOVED:4-5\n'],
7975 [], 'propget', SVN_PROP_MERGEINFO,
7976 mu_COPY_path)
7979 def merge_with_auto_rev_range_detection(sbox):
7980 "merge with auto detection of revision ranges"
7982 ## See http://svn.haxx.se/dev/archive-2007-09/0735.shtml ##
7984 # Create a WC
7985 sbox.build()
7986 wc_dir = sbox.wc_dir
7988 # Some paths we'll care about
7989 A_url = sbox.repo_url + '/A'
7990 A_COPY_url = sbox.repo_url + '/A_COPY'
7991 B1_path = os.path.join(wc_dir, 'A', 'B1')
7992 B1_mu_path = os.path.join(wc_dir, 'A', 'B1', 'mu')
7993 A_COPY_path = os.path.join(wc_dir, 'A_COPY')
7995 # Create B1 inside A
7996 svntest.actions.run_and_verify_svn(None, ["A " + B1_path + "\n"],
7997 [], 'mkdir',
7998 B1_path)
8000 # Add a file mu inside B1
8001 svntest.main.file_write(B1_mu_path, "This is the file 'mu'.\n")
8002 svntest.actions.run_and_verify_svn(None, ["A " + B1_mu_path + "\n"],
8003 [], 'add', B1_mu_path)
8005 # Commit B1 and B1/mu
8006 expected_output = wc.State(wc_dir, {
8007 'A/B1' : Item(verb='Adding'),
8008 'A/B1/mu' : Item(verb='Adding'),
8010 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
8011 expected_status.add({
8012 'A/B1' : Item(status=' ', wc_rev=2),
8013 'A/B1/mu' : Item(status=' ', wc_rev=2),
8015 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
8016 expected_status, None, wc_dir)
8018 # Copy A to A_COPY
8019 svntest.actions.run_and_verify_svn(None, ['\n', 'Committed revision 3.\n'],
8020 [], 'cp', '-m', 'cp A to A_COPY',
8021 A_url, A_COPY_url)
8023 # Make a modification to A/B1/mu
8024 svntest.main.file_write(B1_mu_path, "This is the file 'mu' modified.\n")
8025 expected_output = wc.State(wc_dir, {'A/B1/mu' : Item(verb='Sending')})
8026 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
8027 expected_status.add({
8028 'A/B1' : Item(status=' ', wc_rev=2),
8029 'A/B1/mu' : Item(status=' ', wc_rev=4),
8031 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
8032 expected_status, None, wc_dir)
8034 # Update the working copy to get A_COPY
8035 svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
8037 short_A_COPY = shorten_path_kludge(A_COPY_path)
8038 saved_cwd = os.getcwd()
8039 os.chdir(svntest.main.work_dir)
8041 # Merge /A to /A_COPY
8042 expected_output = wc.State(short_A_COPY, {
8043 'B1/mu' : Item(status='U '),
8045 expected_status = wc.State(short_A_COPY, {
8046 '' : Item(status=' M', wc_rev=4),
8047 'mu' : Item(status=' ', wc_rev=4),
8048 'C' : Item(status=' ', wc_rev=4),
8049 'D' : Item(status=' ', wc_rev=4),
8050 'B' : Item(status=' ', wc_rev=4),
8051 'B/lambda' : Item(status=' ', wc_rev=4),
8052 'B/E' : Item(status=' ', wc_rev=4),
8053 'B/E/alpha': Item(status=' ', wc_rev=4),
8054 'B/E/beta' : Item(status=' ', wc_rev=4),
8055 'B/F' : Item(status=' ', wc_rev=4),
8056 'B1' : Item(status=' ', wc_rev=4),
8057 'B1/mu' : Item(status='M ', wc_rev=4),
8058 'D/gamma' : Item(status=' ', wc_rev=4),
8059 'D/G' : Item(status=' ', wc_rev=4),
8060 'D/G/pi' : Item(status=' ', wc_rev=4),
8061 'D/G/rho' : Item(status=' ', wc_rev=4),
8062 'D/G/tau' : Item(status=' ', wc_rev=4),
8063 'D/H' : Item(status=' ', wc_rev=4),
8064 'D/H/chi' : Item(status=' ', wc_rev=4),
8065 'D/H/omega': Item(status=' ', wc_rev=4),
8066 'D/H/psi' : Item(status=' ', wc_rev=4),
8068 expected_disk = wc.State('', {
8069 '' : Item(props={SVN_PROP_MERGEINFO : '/A:3-4'}),
8070 'mu' : Item("This is the file 'mu'.\n"),
8071 'C' : Item(),
8072 'D' : Item(),
8073 'B' : Item(),
8074 'B/lambda' : Item("This is the file 'lambda'.\n"),
8075 'B/E' : Item(),
8076 'B/E/alpha': Item("This is the file 'alpha'.\n"),
8077 'B/E/beta' : Item("This is the file 'beta'.\n"),
8078 'B/F' : Item(),
8079 'B1' : Item(),
8080 'B1/mu' : Item("This is the file 'mu' modified.\n"),
8081 'D/gamma' : Item("This is the file 'gamma'.\n"),
8082 'D/G' : Item(),
8083 'D/G/pi' : Item("This is the file 'pi'.\n"),
8084 'D/G/rho' : Item("This is the file 'rho'.\n"),
8085 'D/G/tau' : Item("This is the file 'tau'.\n"),
8086 'D/H' : Item(),
8087 'D/H/chi' : Item("This is the file 'chi'.\n"),
8088 'D/H/omega': Item("This is the file 'omega'.\n"),
8089 'D/H/psi' : Item("This is the file 'psi'.\n"),
8091 expected_skip = wc.State(short_A_COPY, {})
8092 svntest.actions.run_and_verify_merge(short_A_COPY, None, None,
8093 A_url,
8094 expected_output,
8095 expected_disk,
8096 expected_status,
8097 expected_skip,
8098 None, None, None, None, None,
8099 1, 1)
8100 os.chdir(saved_cwd)
8102 def mergeinfo_recording_in_skipped_merge(sbox):
8103 "mergeinfo recording in skipped merge"
8105 ## See http://subversion.tigris.org/issues/show_bug.cgi?id=2829. ##
8107 # Create a WC with a single branch
8108 sbox.build()
8109 wc_dir = sbox.wc_dir
8110 wc_disk, wc_status = set_up_branch(sbox, True, 1)
8112 # Some paths we'll care about
8113 A_url = sbox.repo_url + '/A'
8114 A_COPY_path = os.path.join(wc_dir, 'A_COPY')
8115 mu_path = os.path.join(wc_dir, 'A', 'mu')
8116 alpha_path = os.path.join(wc_dir, 'A', 'B', 'E', 'alpha')
8117 A_COPY_B_E_path = os.path.join(wc_dir, 'A_COPY', 'B', 'E')
8118 A_COPY_alpha_path = os.path.join(wc_dir, 'A_COPY', 'B', 'E', 'alpha')
8119 A_COPY_beta_path = os.path.join(wc_dir, 'A_COPY', 'B', 'E', 'beta')
8121 # Make a modification to A/mu
8122 svntest.main.file_write(mu_path, "This is the file 'mu' modified.\n")
8123 expected_output = wc.State(wc_dir, {'A/mu' : Item(verb='Sending')})
8124 wc_status.add({'A/mu' : Item(status=' ', wc_rev=3)})
8125 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
8126 wc_status, None, wc_dir)
8128 # Make a modification to A/B/E/alpha
8129 svntest.main.file_write(alpha_path, "This is the file 'alpha' modified.\n")
8130 expected_output = wc.State(wc_dir, {'A/B/E/alpha' : Item(verb='Sending')})
8131 wc_status.add({'A/B/E/alpha' : Item(status=' ', wc_rev=4)})
8132 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
8133 wc_status, None, wc_dir)
8135 # Delete A_COPY/B/E
8136 svntest.actions.run_and_verify_svn(None, None, [], 'rm',
8137 A_COPY_B_E_path)
8139 short_A_COPY = shorten_path_kludge(A_COPY_path)
8140 saved_cwd = os.getcwd()
8141 os.chdir(svntest.main.work_dir)
8143 # Merge /A to /A_COPY ie., r1 to r4
8144 expected_output = wc.State(short_A_COPY, {
8145 'mu' : Item(status='U '),
8147 expected_status = wc.State(short_A_COPY, {
8148 '' : Item(status=' M', wc_rev=2),
8149 'mu' : Item(status='M ', wc_rev=2),
8150 'B' : Item(status=' ', wc_rev=2),
8151 'B/lambda' : Item(status=' ', wc_rev=2),
8152 'B/F' : Item(status=' ', wc_rev=2),
8153 'B/E' : Item(status='D ', wc_rev=2),
8154 'B/E/alpha': Item(status='D ', wc_rev=2),
8155 'B/E/beta' : Item(status='D ', wc_rev=2),
8156 'C' : Item(status=' ', wc_rev=2),
8157 'D' : Item(status=' ', wc_rev=2),
8158 'D/gamma' : Item(status=' ', wc_rev=2),
8159 'D/G' : Item(status=' ', wc_rev=2),
8160 'D/G/pi' : Item(status=' ', wc_rev=2),
8161 'D/G/rho' : Item(status=' ', wc_rev=2),
8162 'D/G/tau' : Item(status=' ', wc_rev=2),
8163 'D/H' : Item(status=' ', wc_rev=2),
8164 'D/H/chi' : Item(status=' ', wc_rev=2),
8165 'D/H/omega': Item(status=' ', wc_rev=2),
8166 'D/H/psi' : Item(status=' ', wc_rev=2),
8168 expected_disk = wc.State('', {
8169 '' : Item(props={SVN_PROP_MERGEINFO : '/A:2-4'}),
8170 'mu' : Item("This is the file 'mu' modified.\n"),
8171 'C' : Item(),
8172 'D' : Item(),
8173 'B' : Item(),
8174 'B/lambda' : Item(contents="This is the file 'lambda'.\n"),
8175 'B/F' : Item(),
8176 'B/E' : Item(),
8177 'D/gamma' : Item("This is the file 'gamma'.\n"),
8178 'D/G' : Item(),
8179 'D/G/pi' : Item("This is the file 'pi'.\n"),
8180 'D/G/rho' : Item("This is the file 'rho'.\n"),
8181 'D/G/tau' : Item("This is the file 'tau'.\n"),
8182 'D/H' : Item(),
8183 'D/H/chi' : Item("This is the file 'chi'.\n"),
8184 'D/H/omega': Item("This is the file 'omega'.\n"),
8185 'D/H/psi' : Item("This is the file 'psi'.\n"),
8187 expected_skip = wc.State(short_A_COPY, {
8188 'B/E/alpha' : Item(),
8190 svntest.actions.run_and_verify_merge(short_A_COPY, None, None,
8191 A_url,
8192 expected_output,
8193 expected_disk,
8194 expected_status,
8195 expected_skip,
8196 None, None, None, None, None,
8197 1, 1)
8199 os.chdir(saved_cwd)
8201 # Test for issue 2818: Provide a 'merge' API which allows for merging of
8202 # arbitrary revision ranges (e.g. '-c 3,5,7')
8203 def cherry_picking(sbox):
8204 "command line supports cherry picked merge ranges"
8206 sbox.build()
8207 wc_dir = sbox.wc_dir
8208 wc_disk, wc_status = set_up_branch(sbox)
8210 # Some paths we'll care about
8211 H_path = os.path.join(wc_dir, "A", "D", "H")
8212 G_path = os.path.join(wc_dir, "A", "D", "G")
8213 A_COPY_path = os.path.join(wc_dir, "A_COPY")
8214 D_COPY_path = os.path.join(wc_dir, "A_COPY", "D")
8215 G_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "G")
8216 H_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "H")
8217 rho_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "G", "rho")
8218 omega_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "H", "omega")
8219 psi_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "H", "psi")
8221 # Update working copy
8222 expected_output = svntest.wc.State(wc_dir, {})
8223 wc_status.tweak(wc_rev='6')
8224 svntest.actions.run_and_verify_update(wc_dir, expected_output,
8225 wc_disk, wc_status,
8226 None, None, None, None, None, True)
8228 # Make some prop changes to some dirs.
8229 svntest.actions.run_and_verify_svn(None,
8230 ["property 'prop:name' set on '" +
8231 G_path + "'\n"], [], 'ps',
8232 'prop:name', 'propval', G_path)
8233 expected_output = svntest.wc.State(wc_dir, {'A/D/G': Item(verb='Sending'),})
8234 wc_status.tweak('A/D/G', wc_rev=7)
8235 wc_disk.tweak('A/D/G', props={'prop:name' : 'propval'})
8237 svntest.actions.run_and_verify_commit(wc_dir, expected_output, wc_status,
8238 None, wc_dir)
8239 svntest.actions.run_and_verify_svn(None,
8240 ["property 'prop:name' set on '" +
8241 H_path + "'\n"], [], 'ps',
8242 'prop:name', 'propval', H_path)
8243 expected_output = svntest.wc.State(wc_dir, {'A/D/H': Item(verb='Sending'),})
8244 wc_status.tweak('A/D/H', wc_rev=8)
8245 wc_disk.tweak('A/D/H', props={'prop:name' : 'propval'})
8246 svntest.actions.run_and_verify_commit(wc_dir, expected_output, wc_status,
8247 None, wc_dir)
8249 # Do multiple additive merges to a file"
8250 # Merge -r2:4 -c6 into A_COPY/D/G/rho.
8251 short_rho_COPY_path = shorten_path_kludge(rho_COPY_path)
8252 expected_skip = wc.State(short_rho_COPY_path, { })
8253 saved_cwd = os.getcwd()
8254 os.chdir(svntest.main.work_dir)
8255 # run_and_verify_merge doesn't support merging to a file WCPATH
8256 # so use run_and_verify_svn.
8257 svntest.actions.run_and_verify_svn(None,
8258 expected_merge_output(
8259 [[3,4],[6]],
8260 'U ' + short_rho_COPY_path + '\n'),
8261 [], 'merge', '-r2:4', '-c6',
8262 sbox.repo_url + '/A/D/G/rho',
8263 short_rho_COPY_path)
8264 os.chdir(saved_cwd)
8266 # Check rho's status and props.
8267 expected_status = wc.State(rho_COPY_path,
8268 {'' : Item(status='MM', wc_rev=6)})
8269 svntest.actions.run_and_verify_status(rho_COPY_path, expected_status)
8270 svntest.actions.run_and_verify_svn(None, ["/A/D/G/rho:3-4,6\n"], [],
8271 'propget', SVN_PROP_MERGEINFO,
8272 rho_COPY_path)
8274 #Do multiple additive merges to a directory:
8275 # Merge -c6 -c8 into A_COPY/D/H
8276 short_H_COPY_path = shorten_path_kludge(H_COPY_path)
8277 short_omega_COPY_path = shorten_path_kludge(omega_COPY_path)
8278 expected_output = expected_merge_output(
8279 [[6],[8]],
8280 ['U ' + short_omega_COPY_path + '\n',
8281 ' U ' + short_H_COPY_path + '\n'])
8282 os.chdir(svntest.main.work_dir)
8283 svntest.actions.run_and_verify_svn(None, expected_output,
8284 [], 'merge', '-c6', '-c8',
8285 sbox.repo_url + '/A/D/H',
8286 short_H_COPY_path)
8287 os.chdir(saved_cwd)
8289 # Check A_COPY/D/H's status and props.
8290 expected_status = wc.State(H_COPY_path,
8291 {'' : Item(status=' M', wc_rev=6),
8292 'psi' : Item(status=' ', wc_rev=6),
8293 'chi' : Item(status=' ', wc_rev=6),
8294 'omega': Item(status='M ', wc_rev=6),})
8295 svntest.actions.run_and_verify_status(H_COPY_path, expected_status)
8296 svntest.actions.run_and_verify_svn(None,
8297 [H_COPY_path + " - /A/D/H:6,8\n"],
8298 [], 'propget', '-R', SVN_PROP_MERGEINFO,
8299 H_COPY_path)
8301 # Do multiple reverse merges to a directory:
8302 # Merge -c-6 -c-3 into A_COPY
8303 short_A_COPY_path = shorten_path_kludge(A_COPY_path)
8304 short_omega_COPY_path = shorten_path_kludge(omega_COPY_path)
8305 expected_output = expected_merge_output(
8306 [[-3],[-6]], 'G ' + short_omega_COPY_path + '\n')
8307 os.chdir(svntest.main.work_dir)
8308 svntest.actions.run_and_verify_svn(None, expected_output,
8309 [], 'merge', '-c-3', '-c-6',
8310 sbox.repo_url + '/A',
8311 short_A_COPY_path)
8312 os.chdir(saved_cwd)
8313 expected_status = wc.State(A_COPY_path,
8314 {'' : Item(status=' ', wc_rev=6),
8315 'B' : Item(status=' ', wc_rev=6),
8316 'B/lambda' : Item(status=' ', wc_rev=6),
8317 'B/E' : Item(status=' ', wc_rev=6),
8318 'B/E/alpha' : Item(status=' ', wc_rev=6),
8319 'B/E/beta' : Item(status=' ', wc_rev=6),
8320 'B/F' : Item(status=' ', wc_rev=6),
8321 'mu' : Item(status=' ', wc_rev=6),
8322 'C' : Item(status=' ', wc_rev=6),
8323 'D' : Item(status=' ', wc_rev=6),
8324 'D/gamma' : Item(status=' ', wc_rev=6),
8325 'D/G' : Item(status=' ', wc_rev=6),
8326 'D/G/pi' : Item(status=' ', wc_rev=6),
8327 'D/G/rho' : Item(status='MM', wc_rev=6),
8328 'D/G/tau' : Item(status=' ', wc_rev=6),
8329 'D/H' : Item(status=' M', wc_rev=6),
8330 'D/H/chi' : Item(status=' ', wc_rev=6),
8331 'D/H/psi' : Item(status=' ', wc_rev=6),
8332 'D/H/omega' : Item(status=' ', wc_rev=6),})
8333 svntest.actions.run_and_verify_status(A_COPY_path, expected_status)
8334 expected_out = H_COPY_path + " - /A/D/H:8\n|" + \
8335 rho_COPY_path + " - /A/D/G/rho:4\n"
8336 # Construct proper regex for '\' infested Windows paths.
8337 if sys.platform == 'win32':
8338 expected_out = expected_out.replace("\\", "\\\\")
8339 svntest.actions.run_and_verify_svn(None, expected_out, [],
8340 'propget', '-R', SVN_PROP_MERGEINFO,
8341 A_COPY_path)
8343 # Do both additive and reverse merges to a directory:
8344 # Merge -r2:3 -c-4 -r4:7 to A_COPY/D
8345 short_D_COPY_path = shorten_path_kludge(D_COPY_path)
8346 short_psi_COPY_path = shorten_path_kludge(psi_COPY_path)
8347 short_G_COPY_path = shorten_path_kludge(G_COPY_path)
8348 expected_output = expected_merge_output(
8349 [[3],[-4], [5,7]],
8350 [' U ' + short_G_COPY_path + '\n',
8351 'U ' + short_omega_COPY_path + '\n',
8352 'U ' + short_psi_COPY_path + '\n',
8353 'G ' + short_rho_COPY_path + '\n'])
8355 os.chdir(svntest.main.work_dir)
8356 svntest.actions.run_and_verify_svn(None, expected_output, [], 'merge',
8357 '-r2:3', '-c-4', '-r4:7',
8358 sbox.repo_url + '/A/D',
8359 short_D_COPY_path)
8360 os.chdir(saved_cwd)
8361 expected_status = wc.State(D_COPY_path,
8362 {'' : Item(status=' M', wc_rev=6),
8363 'gamma' : Item(status=' ', wc_rev=6),
8364 'G' : Item(status=' M', wc_rev=6),
8365 'G/pi' : Item(status=' ', wc_rev=6),
8366 'G/rho' : Item(status=' ', wc_rev=6),
8367 'G/tau' : Item(status=' ', wc_rev=6),
8368 'H' : Item(status=' M', wc_rev=6),
8369 'H/chi' : Item(status=' ', wc_rev=6),
8370 'H/psi' : Item(status='M ', wc_rev=6),
8371 'H/omega' : Item(status='M ', wc_rev=6),})
8372 svntest.actions.run_and_verify_status(D_COPY_path, expected_status)
8373 expected_out = D_COPY_path + " - /A/D:3,5-7\n|" + \
8374 H_COPY_path + " - /A/D/H:3,5-8\n"
8375 # Construct proper regex for '\' infested Windows paths.
8376 if sys.platform == 'win32':
8377 expected_out = expected_out.replace("\\", "\\\\")
8378 svntest.actions.run_and_verify_svn(None, expected_out, [],
8379 'propget', '-R', SVN_PROP_MERGEINFO,
8380 D_COPY_path)
8382 def propchange_of_subdir_raises_conflict(sbox):
8383 "merge of propchange on subdir raises conflict"
8385 ## See http://subversion.tigris.org/issues/show_bug.cgi?id=2969. ##
8387 # Create a WC with a single branch
8388 sbox.build()
8389 wc_dir = sbox.wc_dir
8390 wc_disk, wc_status = set_up_branch(sbox, True, 1)
8392 # Some paths we'll care about
8393 B_url = sbox.repo_url + '/A/B'
8394 E_path = os.path.join(wc_dir, 'A', 'B', 'E')
8395 lambda_path = os.path.join(wc_dir, 'A', 'B', 'lambda')
8396 A_COPY_B_path = os.path.join(wc_dir, 'A_COPY', 'B')
8397 A_COPY_B_E_path = os.path.join(wc_dir, 'A_COPY', 'B', 'E')
8398 A_COPY_lambda_path = os.path.join(wc_dir, 'A_COPY', 'B', 'E', 'lambda')
8400 # Set a property on A/B/E and Make a modification to A/B/lambda
8401 svntest.main.run_svn(None, 'propset', 'x', 'x', E_path)
8403 svntest.main.file_write(lambda_path, "This is the file 'lambda' modified.\n")
8404 expected_output = wc.State(wc_dir, {
8405 'A/B/lambda' : Item(verb='Sending'),
8406 'A/B/E' : Item(verb='Sending'),
8408 wc_status.add({
8409 'A/B/lambda' : Item(status=' ', wc_rev=3),
8410 'A/B/E' : Item(status=' ', wc_rev=3),
8412 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
8413 wc_status, None, wc_dir)
8415 short_A_COPY_B = shorten_path_kludge(A_COPY_B_path)
8416 saved_cwd = os.getcwd()
8417 os.chdir(svntest.main.work_dir)
8419 # Merge /A/B to /A_COPY/B ie., r1 to r3 with depth files
8420 expected_output = wc.State(short_A_COPY_B, {
8421 'lambda' : Item(status='U '),
8423 expected_disk = wc.State('', {
8424 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B:2-3*'}),
8425 'lambda' : Item(contents="This is the file 'lambda' modified.\n",
8426 props={SVN_PROP_MERGEINFO : '/A/B/lambda:2-3'}),
8427 'F' : Item(),
8428 'E' : Item(),
8429 'E/alpha' : Item(contents="This is the file 'alpha'.\n"),
8430 'E/beta' : Item(contents="This is the file 'beta'.\n"),
8432 expected_status = wc.State(short_A_COPY_B, {
8433 '' : Item(status=' M', wc_rev=2),
8434 'lambda' : Item(status='MM', wc_rev=2),
8435 'F' : Item(status=' ', wc_rev=2),
8436 'E' : Item(status=' ', wc_rev=2),
8437 'E/alpha' : Item(status=' ', wc_rev=2),
8438 'E/beta' : Item(status=' ', wc_rev=2),
8440 expected_skip = wc.State(short_A_COPY_B, {})
8442 svntest.actions.run_and_verify_merge(short_A_COPY_B, None, None,
8443 B_url,
8444 expected_output,
8445 expected_disk,
8446 expected_status,
8447 expected_skip,
8448 None, None, None, None, None,
8449 1, 1, '--depth', 'files')
8451 # Merge /A/B to /A_COPY/B ie., r1 to r3 with infinite depth
8452 expected_output = wc.State(short_A_COPY_B, {
8453 'E' : Item(status=' U'),
8455 expected_disk = wc.State('', {
8456 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B:2-3'}),
8457 'lambda' : Item(contents="This is the file 'lambda' modified.\n"),
8458 'F' : Item(),
8459 'E' : Item(props={'x': 'x'}),
8460 'E/alpha' : Item(contents="This is the file 'alpha'.\n"),
8461 'E/beta' : Item(contents="This is the file 'beta'.\n"),
8463 expected_status = wc.State(short_A_COPY_B, {
8464 '' : Item(status=' M', wc_rev=2),
8465 'lambda' : Item(status='M ', wc_rev=2),
8466 'F' : Item(status=' ', wc_rev=2),
8467 'E' : Item(status=' M', wc_rev=2),
8468 'E/alpha' : Item(status=' ', wc_rev=2),
8469 'E/beta' : Item(status=' ', wc_rev=2),
8472 svntest.actions.run_and_verify_merge(short_A_COPY_B, None, None,
8473 B_url,
8474 expected_output,
8475 expected_disk,
8476 expected_status,
8477 expected_skip,
8478 None, None, None, None, None,
8479 1, 1)
8481 os.chdir(saved_cwd)
8483 # Test for issue #2971: Reverse merge of prop add segfaults if
8484 # merging to parent of first merge
8485 def reverse_merge_prop_add_on_child(sbox):
8486 "reverse merge of prop add on child"
8488 sbox.build()
8489 wc_dir = sbox.wc_dir
8490 wc_disk, wc_status = set_up_branch(sbox, True, 1)
8492 # Some paths we'll care about
8493 G_path = os.path.join(wc_dir, "A", "D", "G")
8494 D_COPY_path = os.path.join(wc_dir, "A_COPY", "D")
8495 G_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "G")
8497 # Make some prop changes to some dirs.
8498 svntest.actions.run_and_verify_svn(None,
8499 ["property 'prop:name' set on '" +
8500 G_path + "'\n"], [], 'ps',
8501 'prop:name', 'propval', G_path)
8502 expected_output = svntest.wc.State(wc_dir, {'A/D/G': Item(verb='Sending'),})
8503 wc_status.tweak('A/D/G', wc_rev=3)
8504 wc_disk.tweak('A/D/G', props={'prop:name' : 'propval'})
8506 svntest.actions.run_and_verify_commit(wc_dir, expected_output, wc_status,
8507 None, wc_dir)
8509 # Merge -c3's prop add to A_COPY/D/G
8510 saved_cwd = os.getcwd()
8511 short_G_COPY_path = shorten_path_kludge(G_COPY_path)
8512 expected_output = wc.State(short_G_COPY_path, {
8513 '' : Item(status=' U')
8515 expected_status = wc.State(short_G_COPY_path, {
8516 '' : Item(status=' M', wc_rev=2),
8517 'pi' : Item(status=' ', wc_rev=2),
8518 'rho' : Item(status=' ', wc_rev=2),
8519 'tau' : Item(status=' ', wc_rev=2),
8521 expected_disk = wc.State('', {
8522 '' : Item(props={SVN_PROP_MERGEINFO : '/A/D/G:3',
8523 'prop:name' : 'propval'}),
8524 'pi' : Item("This is the file 'pi'.\n"),
8525 'rho' : Item("This is the file 'rho'.\n"),
8526 'tau' : Item("This is the file 'tau'.\n"),
8528 expected_skip = wc.State(short_G_COPY_path, { })
8529 os.chdir(svntest.main.work_dir)
8530 svntest.actions.run_and_verify_merge(short_G_COPY_path, '2', '3',
8531 sbox.repo_url + \
8532 '/A/D/G',
8533 expected_output,
8534 expected_disk,
8535 expected_status,
8536 expected_skip,
8537 None, None, None, None,
8538 None, 1)
8539 os.chdir(saved_cwd)
8541 # Now merge -c-3 but target the previous target's parent instead.
8542 short_D_COPY_path = shorten_path_kludge(D_COPY_path)
8543 expected_output = wc.State(short_D_COPY_path, {
8544 'G' : Item(status=' G'),
8546 expected_status = wc.State(short_D_COPY_path, {
8547 '' : Item(status=' ', wc_rev=2),
8548 'G' : Item(status=' ', wc_rev=2),
8549 'G/pi' : Item(status=' ', wc_rev=2),
8550 'G/rho' : Item(status=' ', wc_rev=2),
8551 'G/tau' : Item(status=' ', wc_rev=2),
8552 'H' : Item(status=' ', wc_rev=2),
8553 'H/chi' : Item(status=' ', wc_rev=2),
8554 'H/psi' : Item(status=' ', wc_rev=2),
8555 'H/omega' : Item(status=' ', wc_rev=2),
8556 'gamma' : Item(status=' ', wc_rev=2),
8558 expected_disk = wc.State('', {
8559 'G' : Item(),
8560 'G/pi' : Item("This is the file 'pi'.\n"),
8561 'G/rho' : Item("This is the file 'rho'.\n"),
8562 'G/tau' : Item("This is the file 'tau'.\n"),
8563 'H' : Item(),
8564 'H/chi' : Item("This is the file 'chi'.\n"),
8565 'H/psi' : Item("This is the file 'psi'.\n"),
8566 'H/omega' : Item("This is the file 'omega'.\n"),
8567 'gamma' : Item("This is the file 'gamma'.\n")
8569 expected_skip = wc.State(short_D_COPY_path, { })
8570 os.chdir(svntest.main.work_dir)
8571 svntest.actions.run_and_verify_merge(short_D_COPY_path, '3', '2',
8572 sbox.repo_url + \
8573 '/A/D',
8574 expected_output,
8575 expected_disk,
8576 expected_status,
8577 expected_skip,
8578 None, None, None, None,
8579 None, 1)
8580 os.chdir(saved_cwd)
8582 def merge_target_with_non_inheritable_mergeinfo(sbox):
8583 "merge target with non inheritable mergeinfo"
8585 ## See http://subversion.tigris.org/issues/show_bug.cgi?id=2970. ##
8587 # Create a WC with a single branch
8588 sbox.build()
8589 wc_dir = sbox.wc_dir
8590 wc_disk, wc_status = set_up_branch(sbox, True, 1)
8592 # Some paths we'll care about
8593 B_url = sbox.repo_url + '/A/B'
8594 lambda_path = os.path.join(wc_dir, 'A', 'B', 'lambda')
8595 newfile_path = os.path.join(wc_dir, 'A', 'B', 'E', 'newfile')
8596 A_COPY_B_path = os.path.join(wc_dir, 'A_COPY', 'B')
8598 # Make a modifications to A/B/lambda and add A/B/E/newfile
8599 svntest.main.file_write(lambda_path, "This is the file 'lambda' modified.\n")
8600 svntest.main.file_write(newfile_path, "This is the file 'newfile'.\n")
8601 svntest.actions.run_and_verify_svn(None, None, [], 'add', newfile_path)
8602 expected_output = wc.State(wc_dir, {
8603 'A/B/lambda' : Item(verb='Sending'),
8604 'A/B/E/newfile' : Item(verb='Adding'),
8606 wc_status.add({
8607 'A/B/lambda' : Item(status=' ', wc_rev=3),
8608 'A/B/E/newfile' : Item(status=' ', wc_rev=3),
8610 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
8611 wc_status, None, wc_dir)
8613 short_A_COPY_B = shorten_path_kludge(A_COPY_B_path)
8614 saved_cwd = os.getcwd()
8615 os.chdir(svntest.main.work_dir)
8617 # Merge /A/B to /A_COPY/B ie., r1 to r3 with depth immediates
8618 expected_output = wc.State(short_A_COPY_B, {
8619 'lambda' : Item(status='U '),
8621 expected_disk = wc.State('', {
8622 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B:1-3'}),
8623 'lambda' : Item(contents="This is the file 'lambda' modified.\n"),
8624 'F' : Item(props={SVN_PROP_MERGEINFO : '/A/B/F:1,2-3*'}),
8625 'E' : Item(props={SVN_PROP_MERGEINFO : '/A/B/E:1,2-3*'}),
8626 'E/alpha' : Item(contents="This is the file 'alpha'.\n"),
8627 'E/beta' : Item(contents="This is the file 'beta'.\n"),
8629 expected_status = wc.State(short_A_COPY_B, {
8630 '' : Item(status=' M', wc_rev=2),
8631 'lambda' : Item(status='M ', wc_rev=2),
8632 'F' : Item(status=' M', wc_rev=2),
8633 'E' : Item(status=' M', wc_rev=2),
8634 'E/alpha' : Item(status=' ', wc_rev=2),
8635 'E/beta' : Item(status=' ', wc_rev=2),
8637 expected_skip = wc.State(short_A_COPY_B, {})
8639 svntest.actions.run_and_verify_merge(short_A_COPY_B, None, None,
8640 B_url,
8641 expected_output,
8642 expected_disk,
8643 expected_status,
8644 expected_skip,
8645 None, None, None, None, None,
8646 1, 1, '--depth', 'immediates')
8648 # Merge /A/B to /A_COPY/B ie., r1 to r3 with infinite depth
8649 expected_output = wc.State(short_A_COPY_B, {
8650 'E/newfile' : Item(status='A '),
8652 expected_disk = wc.State('', {
8653 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B:1-3'}),
8654 'lambda' : Item(contents="This is the file 'lambda' modified.\n"),
8655 'F' : Item(),
8656 'E' : Item(),
8657 'E/alpha' : Item(contents="This is the file 'alpha'.\n"),
8658 'E/beta' : Item(contents="This is the file 'beta'.\n"),
8659 'E/newfile' : Item(contents="This is the file 'newfile'.\n"),
8661 expected_status = wc.State(short_A_COPY_B, {
8662 '' : Item(status=' M', wc_rev=2),
8663 'lambda' : Item(status='M ', wc_rev=2),
8664 'F' : Item(status=' ', wc_rev=2),
8665 'E' : Item(status=' ', wc_rev=2),
8666 'E/alpha' : Item(status=' ', wc_rev=2),
8667 'E/beta' : Item(status=' ', wc_rev=2),
8668 'E/newfile' : Item(status='A ', wc_rev=2),
8671 svntest.actions.run_and_verify_merge(short_A_COPY_B, None, None,
8672 B_url,
8673 expected_output,
8674 expected_disk,
8675 expected_status,
8676 expected_skip,
8677 None, None, None, None, None,
8678 1, 1)
8680 os.chdir(saved_cwd)
8682 def self_reverse_merge(sbox):
8683 "revert a commit on a target"
8685 sbox.build()
8686 wc_dir = sbox.wc_dir
8688 # Make changes to the working copy
8689 mu_path = os.path.join(wc_dir, 'A', 'mu')
8690 svntest.main.file_append(mu_path, 'appended mu text')
8692 # Created expected output tree for 'svn ci'
8693 expected_output = wc.State(wc_dir, {
8694 'A/mu' : Item(verb='Sending'),
8697 # Create expected status tree; all local revisions should be at 1,
8698 # but mu should be at revision 2.
8699 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
8700 expected_status.tweak('A/mu', wc_rev=2)
8702 svntest.actions.run_and_verify_commit(wc_dir,
8703 expected_output,
8704 expected_status,
8705 None,
8706 wc_dir)
8708 # update to HEAD so that the to-be-undone revision is found in the
8709 # implicit mergeinfo (the natural history) of the target.
8710 svntest.actions.run_and_verify_svn(None, None, [], 'update', wc_dir)
8712 expected_output = wc.State(wc_dir, {
8713 'A/mu' : Item(status='U ')
8715 expected_skip = wc.State(wc_dir, { })
8716 expected_disk = svntest.main.greek_state.copy()
8717 expected_status = svntest.actions.get_virginal_state(wc_dir, 2)
8718 expected_status.tweak('A/mu', status='M ')
8719 svntest.actions.run_and_verify_merge(wc_dir, '2', '1', sbox.repo_url,
8720 expected_output, expected_disk,
8721 expected_status, expected_skip,
8722 None, None, None, None, None, 1, 1)
8723 svntest.actions.run_and_verify_svn(None, None, [], 'revert', '-R', wc_dir)
8725 # record dummy self mergeinfo to test the fact that self-reversal should work
8726 # irrespective of mergeinfo.
8727 svntest.actions.run_and_verify_svn(None, None, [], 'merge', '-c', '1',
8728 '--record-only', sbox.repo_url, wc_dir)
8730 # Bad svntest.main.greek_state does not have '', so adding it explicitly.
8731 expected_disk.add({'' : Item(props={SVN_PROP_MERGEINFO : '/:1'})})
8732 expected_status = svntest.actions.get_virginal_state(wc_dir, 2)
8733 expected_status.tweak('', status = ' M')
8734 expected_status.tweak('A/mu', status = 'M ')
8735 svntest.actions.run_and_verify_merge(wc_dir, '2', '1', sbox.repo_url,
8736 expected_output, expected_disk,
8737 expected_status, expected_skip,
8738 None, None, None, None, None, 1, 1)
8740 def ignore_ancestry_and_mergeinfo(sbox):
8741 "--ignore-ancestry also ignores mergeinfo"
8743 # Create a WC with a single branch
8744 sbox.build()
8745 wc_dir = sbox.wc_dir
8746 wc_disk, wc_status = set_up_branch(sbox, True, 1)
8748 # Some paths we'll care about
8749 A_B_url = sbox.repo_url + '/A/B'
8750 A_COPY_B_path = os.path.join(wc_dir, 'A_COPY', 'B')
8751 lambda_path = os.path.join(wc_dir, 'A', 'B', 'lambda')
8752 A_COPY_lambda_path = os.path.join(wc_dir, 'A_COPY', 'B', 'lambda')
8754 # Make modifications to A/B/lambda
8755 svntest.main.file_write(lambda_path, "This is the file 'lambda' modified.\n")
8756 expected_output = wc.State(wc_dir, {
8757 'A/B/lambda' : Item(verb='Sending'),
8759 wc_status.add({
8760 'A/B/lambda' : Item(status=' ', wc_rev=3),
8762 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
8763 wc_status, None, wc_dir)
8765 svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
8767 short_A_COPY_B = shorten_path_kludge(A_COPY_B_path)
8768 short_A_COPY_lambda = shorten_path_kludge(A_COPY_lambda_path)
8769 saved_cwd = os.getcwd()
8770 os.chdir(svntest.main.work_dir)
8772 # Merge /A/B to /A_COPY/B ie., r1 to r3 with depth immediates
8773 expected_output = wc.State(short_A_COPY_B, {
8774 'lambda' : Item(status='U '),
8776 expected_disk = wc.State('', {
8777 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B:2-3'}),
8778 'lambda' : Item(contents="This is the file 'lambda' modified.\n"),
8779 'F' : Item(props={}),
8780 'E' : Item(props={}),
8781 'E/alpha' : Item(contents="This is the file 'alpha'.\n"),
8782 'E/beta' : Item(contents="This is the file 'beta'.\n"),
8784 expected_status = wc.State(short_A_COPY_B, {
8785 '' : Item(status=' M', wc_rev=3),
8786 'lambda' : Item(status='M ', wc_rev=3),
8787 'F' : Item(status=' ', wc_rev=3),
8788 'E' : Item(status=' ', wc_rev=3),
8789 'E/alpha' : Item(status=' ', wc_rev=3),
8790 'E/beta' : Item(status=' ', wc_rev=3),
8792 expected_skip = wc.State(short_A_COPY_B, {})
8794 svntest.actions.run_and_verify_merge(short_A_COPY_B, 1, 3,
8795 A_B_url,
8796 expected_output,
8797 expected_disk,
8798 expected_status,
8799 expected_skip,
8800 None, None, None, None, None, 1, 1)
8802 # Now, revert lambda and repeat the merge. Nothing should happen.
8803 svntest.actions.run_and_verify_svn(None, None, [], 'revert', '-R',
8804 short_A_COPY_lambda)
8805 expected_output.remove('lambda')
8806 expected_disk.tweak('lambda', contents="This is the file 'lambda'.\n")
8807 expected_status.tweak('lambda', status=' ')
8808 svntest.actions.run_and_verify_merge(short_A_COPY_B, 1, 3,
8809 A_B_url,
8810 expected_output,
8811 expected_disk,
8812 expected_status,
8813 expected_skip,
8814 None, None, None, None, None, 1, 1)
8816 # Now, try the merge again with --ignore-ancestry. We should get
8817 # lambda re-modified. */
8818 expected_output = wc.State(short_A_COPY_B, {
8819 'lambda' : Item(status='U '),
8821 expected_disk.tweak('lambda',
8822 contents="This is the file 'lambda' modified.\n")
8823 expected_status.tweak('lambda', status='M ')
8824 svntest.actions.run_and_verify_merge(short_A_COPY_B, 1, 3,
8825 A_B_url,
8826 expected_output,
8827 expected_disk,
8828 expected_status,
8829 expected_skip,
8830 None, None, None, None, None, 1, 1,
8831 '--ignore-ancestry')
8833 os.chdir(saved_cwd)
8835 def merge_from_renamed_branch_fails_while_avoiding_repeat_merge(sbox):
8836 "merge from renamed branch"
8837 #Copy A/C to A/COPY_C results in r2.
8838 #Rename A/COPY_C to A/RENAMED_C results in r3.
8839 #Add A/RENAMED_C/file1 and commit, results in r4.
8840 #Change A/RENAMED_C/file1 and commit, results in r5.
8841 #Merge r4 from A/RENAMED_C to A/C
8842 #Merge r2:5 from A/RENAMED_C to A/C <-- This fails tracked via #3032.
8844 ## See http://subversion.tigris.org/issues/show_bug.cgi?id=3032. ##
8846 # Create a WC with a single branch
8847 sbox.build()
8848 wc_dir = sbox.wc_dir
8849 # Some paths we'll care about
8850 A_C_url = sbox.repo_url + '/A/C'
8851 A_COPY_C_url = sbox.repo_url + '/A/COPY_C'
8852 A_RENAMED_C_url = sbox.repo_url + '/A/RENAMED_C'
8853 A_C_path = os.path.join(wc_dir, 'A', 'C')
8854 A_RENAMED_C_path = os.path.join(wc_dir, 'A', 'RENAMED_C')
8855 A_RENAMED_C_file1_path = os.path.join(wc_dir, 'A', 'RENAMED_C', 'file1')
8857 svntest.main.run_svn(None, 'cp', A_C_url, A_COPY_C_url, '-m', 'copy...')
8858 svntest.main.run_svn(None, 'mv', A_COPY_C_url, A_RENAMED_C_url, '-m',
8859 'rename...')
8860 svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
8862 svntest.main.file_write(A_RENAMED_C_file1_path, "This is the file1.\n")
8863 svntest.main.run_svn(None, 'add', A_RENAMED_C_file1_path)
8864 expected_output = wc.State(A_RENAMED_C_path, {
8865 'file1' : Item(verb='Adding'),
8867 expected_status = wc.State(A_RENAMED_C_path, {
8868 '' : Item(status=' ', wc_rev=3),
8869 'file1' : Item(status=' ', wc_rev=4),
8871 svntest.actions.run_and_verify_commit(A_RENAMED_C_path, expected_output,
8872 expected_status, None,
8873 A_RENAMED_C_path)
8874 svntest.main.file_write(A_RENAMED_C_file1_path,
8875 "This is the file1 modified.\n")
8876 expected_output = wc.State(A_RENAMED_C_path, {
8877 'file1' : Item(verb='Sending'),
8879 expected_status.tweak('file1', wc_rev=5)
8880 svntest.actions.run_and_verify_commit(A_RENAMED_C_path, expected_output,
8881 expected_status, None,
8882 A_RENAMED_C_path)
8884 short_A_C = shorten_path_kludge(A_C_path)
8885 expected_skip = wc.State(short_A_C, {})
8886 expected_output = wc.State(short_A_C, {
8887 'file1' : Item(status='A '),
8889 expected_disk = wc.State('', {
8890 '' : Item(props={SVN_PROP_MERGEINFO : '/A/RENAMED_C:4'}),
8891 'file1' : Item("This is the file1.\n"),
8893 expected_status = wc.State(short_A_C, {
8894 '' : Item(status=' M', wc_rev=3),
8895 'file1' : Item(status='A ', wc_rev='-', copied='+'),
8897 saved_cwd = os.getcwd()
8898 os.chdir(svntest.main.work_dir)
8899 svntest.actions.run_and_verify_merge(short_A_C, 3, 4,
8900 A_RENAMED_C_url,
8901 expected_output,
8902 expected_disk,
8903 expected_status,
8904 expected_skip,
8905 None, None, None, None, None, 1, 1)
8907 expected_output = wc.State(short_A_C, {
8908 'file1' : Item(status='U '),
8910 expected_disk = wc.State('', {
8911 '' : Item(props={SVN_PROP_MERGEINFO : '/A/RENAMED_C:3-5'}),
8912 'file1' : Item("This is the file1 modified.\n"),
8914 expected_status = wc.State(short_A_C, {
8915 '' : Item(status=' M', wc_rev=3),
8916 'file1' : Item(status='A ', wc_rev='-', copied='+'),
8918 svntest.actions.run_and_verify_merge(short_A_C, 2, 5,
8919 A_RENAMED_C_url,
8920 expected_output,
8921 expected_disk,
8922 expected_status,
8923 expected_skip,
8924 None, None, None, None, None, 1, 1)
8925 os.chdir(saved_cwd)
8927 # Test for part of issue #2877: 'do subtree merge only if subtree has
8928 # explicit mergeinfo set and exists in the merge source'
8929 def merge_source_normalization_and_subtree_merges(sbox):
8930 "normalized mergeinfo is recorded on subtrees"
8932 sbox.build()
8933 wc_dir = sbox.wc_dir
8935 # Some paths we'll care about
8936 D_COPY_path = os.path.join(wc_dir, "A_COPY", "D")
8937 G_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "G")
8939 # Use our helper to copy 'A' to 'A_COPY' then make some changes under 'A'
8940 wc_disk, wc_status = set_up_branch(sbox)
8942 # r7 - Move A to A_MOVED
8943 svntest.actions.run_and_verify_svn(None, ['\n', 'Committed revision 7.\n'],
8944 [], 'mv', '-m', 'mv A to A_MOVED',
8945 sbox.repo_url + '/A',
8946 sbox.repo_url + '/A_MOVED')
8947 wc_status.add({
8948 'A_MOVED/B' : Item(),
8949 'A_MOVED/B/lambda' : Item(),
8950 'A_MOVED/B/E' : Item(),
8951 'A_MOVED/B/E/alpha' : Item(),
8952 'A_MOVED/B/E/beta' : Item(),
8953 'A_MOVED/B/F' : Item(),
8954 'A_MOVED/mu' : Item(),
8955 'A_MOVED/C' : Item(),
8956 'A_MOVED/D' : Item(),
8957 'A_MOVED/D/gamma' : Item(),
8958 'A_MOVED/D/G' : Item(),
8959 'A_MOVED/D/G/pi' : Item(),
8960 'A_MOVED/D/G/rho' : Item(),
8961 'A_MOVED/D/G/tau' : Item(),
8962 'A_MOVED/D/H' : Item(),
8963 'A_MOVED/D/H/chi' : Item(),
8964 'A_MOVED/D/H/omega' : Item(),
8965 'A_MOVED/D/H/psi' : Item(),
8966 'A_MOVED' : Item()})
8967 wc_status.remove('A', 'A/B', 'A/B/lambda', 'A/B/E', 'A/B/E/alpha',
8968 'A/B/E/beta', 'A/B/F', 'A/mu', 'A/C', 'A/D',
8969 'A/D/gamma', 'A/D/G', 'A/D/G/pi', 'A/D/G/rho',
8970 'A/D/G/tau' , 'A/D/H', 'A/D/H/chi', 'A/D/H/omega',
8971 'A/D/H/psi')
8972 wc_status.tweak(status=' ', wc_rev=7)
8974 # Update the WC
8975 svntest.actions.run_and_verify_svn(None, None, [],
8976 'update', wc_dir)
8978 # r8 - Make a text mod to 'A_MOVED/D/G/tau'
8979 svntest.main.file_write(os.path.join(wc_dir, "A_MOVED", "D", "G", "tau"),
8980 "New content")
8981 expected_output = wc.State(wc_dir,
8982 {'A_MOVED/D/G/tau' : Item(verb='Sending')})
8983 wc_status.tweak('A_MOVED/D/G/tau', status=' ', wc_rev=8)
8984 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
8985 wc_status, None, wc_dir)
8987 # Merge -c4 URL/A_MOVED/D/G A_COPY/D/G.
8989 # Search for the comment entitled "The Merge Kluge" elsewhere in
8990 # this file, to understand why we shorten and chdir() below.
8992 # A_MOVED/D/G doesn't exist at r3:4, it's still A/D/G,
8993 # so the merge source normalization logic should set
8994 # mergeinfo of '/A/D/G:4' on A_COPY/D/G, *not* 'A_MOVED/D/G:4',
8995 # see issue #2953.
8996 short_G_COPY_path = shorten_path_kludge(G_COPY_path)
8997 expected_output = wc.State(short_G_COPY_path, {
8998 'rho' : Item(status='U ')
9000 expected_status = wc.State(short_G_COPY_path, {
9001 '' : Item(status=' M', wc_rev=7),
9002 'pi' : Item(status=' ', wc_rev=7),
9003 'rho' : Item(status='M ', wc_rev=7),
9004 'tau' : Item(status=' ', wc_rev=7),
9006 expected_disk = wc.State('', {
9007 '' : Item(props={SVN_PROP_MERGEINFO : '/A/D/G:4'}),
9008 'pi' : Item("This is the file 'pi'.\n"),
9009 'rho' : Item("New content"),
9010 'tau' : Item("This is the file 'tau'.\n"),
9012 expected_skip = wc.State(short_G_COPY_path, { })
9013 saved_cwd = os.getcwd()
9014 os.chdir(svntest.main.work_dir)
9015 svntest.actions.run_and_verify_merge(short_G_COPY_path, '3', '4',
9016 sbox.repo_url + '/A_MOVED/D/G',
9017 expected_output,
9018 expected_disk,
9019 expected_status,
9020 expected_skip,
9021 None, None, None, None,
9022 None, 1)
9023 os.chdir(saved_cwd)
9025 # Merge -c8 URL/A_MOVED/D A_COPY/D.
9027 # The merge target A_COPY/D and the subtree at A_COPY/D/G
9028 # should both have their mergeinfo updated with r8
9029 # from A_MOVED_D, see reopened issue #2877.
9030 short_D_COPY_path = shorten_path_kludge(D_COPY_path)
9031 expected_output = wc.State(short_D_COPY_path, {
9032 'G/tau' : Item(status='U '),
9034 expected_status = wc.State(short_D_COPY_path, {
9035 '' : Item(status=' M', wc_rev=7),
9036 'G' : Item(status=' M', wc_rev=7),
9037 'G/pi' : Item(status=' ', wc_rev=7),
9038 'G/rho' : Item(status='M ', wc_rev=7),
9039 'G/tau' : Item(status='M ', wc_rev=7),
9040 'H' : Item(status=' ', wc_rev=7),
9041 'H/chi' : Item(status=' ', wc_rev=7),
9042 'H/psi' : Item(status=' ', wc_rev=7),
9043 'H/omega' : Item(status=' ', wc_rev=7),
9044 'gamma' : Item(status=' ', wc_rev=7),
9046 expected_disk = wc.State('', {
9047 '' : Item(props={SVN_PROP_MERGEINFO : '/A_MOVED/D:8'}),
9048 'G' : Item(props={SVN_PROP_MERGEINFO :
9049 '/A/D/G:4\n/A_MOVED/D/G:8\n'}),
9050 'G/pi' : Item("This is the file 'pi'.\n"),
9051 'G/rho' : Item("New content"),
9052 'G/tau' : Item("New content"),
9053 'H' : Item(),
9054 'H/chi' : Item("This is the file 'chi'.\n"),
9055 'H/psi' : Item("This is the file 'psi'.\n"),
9056 'H/omega' : Item("This is the file 'omega'.\n"),
9057 'gamma' : Item("This is the file 'gamma'.\n")
9059 expected_skip = wc.State(short_D_COPY_path, { })
9060 os.chdir(svntest.main.work_dir)
9061 svntest.actions.run_and_verify_merge(short_D_COPY_path, '7', '8',
9062 sbox.repo_url + \
9063 '/A_MOVED/D',
9064 expected_output,
9065 expected_disk,
9066 expected_status,
9067 expected_skip,
9068 None, None, None, None,
9069 None, 1)
9070 os.chdir(saved_cwd)
9072 # Test for issue #3067: 'subtrees with intersecting mergeinfo, that don't
9073 # exist at the start of a merge range shouldn't break the merge'
9074 def new_subtrees_should_not_break_merge(sbox):
9075 "subtrees added after start of merge range are ok"
9077 sbox.build()
9078 wc_dir = sbox.wc_dir
9079 wc_disk, wc_status = set_up_branch(sbox)
9081 # Some paths we'll care about
9082 A_COPY_path = os.path.join(wc_dir, "A_COPY")
9083 D_COPY_path = os.path.join(wc_dir, "A_COPY", "D")
9084 nu_path = os.path.join(wc_dir, "A", "D", "H", "nu")
9085 nu_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "H", "nu")
9086 H_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "H")
9088 # Create 'A/D/H/nu', commit it as r7, make a text mod to it in r8.
9089 svntest.main.file_write(nu_path, "This is the file 'nu'.\n")
9090 svntest.actions.run_and_verify_svn(None, None, [], 'add', nu_path)
9091 expected_output = wc.State(wc_dir, {'A/D/H/nu' : Item(verb='Adding')})
9092 wc_status.add({'A/D/H/nu' : Item(status=' ', wc_rev=7)})
9093 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
9094 wc_status, None, wc_dir)
9095 svntest.main.file_write(nu_path, "New content")
9096 expected_output = wc.State(wc_dir, {'A/D/H/nu' : Item(verb='Sending')})
9097 wc_status.tweak('A/D/H/nu', wc_rev=8)
9098 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
9099 wc_status, None, wc_dir)
9101 # Merge r7 to A_COPY/D/H, then, so it has it's own explicit mergeinfo,
9102 # then merge r8 to A_COPY/D/H/nu so it too has explicit mergeinfo.
9103 short_H_COPY_path = shorten_path_kludge(H_COPY_path)
9104 expected_output = wc.State(short_H_COPY_path, {
9105 'nu' : Item(status='A '),
9107 expected_status = wc.State(short_H_COPY_path, {
9108 '' : Item(status=' M', wc_rev=2),
9109 'psi' : Item(status=' ', wc_rev=2),
9110 'omega' : Item(status=' ', wc_rev=2),
9111 'chi' : Item(status=' ', wc_rev=2),
9112 'nu' : Item(status='A ', copied='+', wc_rev='-'),
9114 expected_disk = wc.State('', {
9115 '' : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:7'}),
9116 'psi' : Item("This is the file 'psi'.\n"),
9117 'omega' : Item("This is the file 'omega'.\n"),
9118 'chi' : Item("This is the file 'chi'.\n"),
9119 'nu' : Item("This is the file 'nu'.\n"),
9121 expected_skip = wc.State(short_H_COPY_path, {})
9122 saved_cwd = os.getcwd()
9123 os.chdir(svntest.main.work_dir)
9124 svntest.actions.run_and_verify_merge(short_H_COPY_path, '6', '7',
9125 sbox.repo_url + '/A/D/H',
9126 expected_output, expected_disk,
9127 expected_status, expected_skip,
9128 None, None, None, None, None, 1)
9129 # run_and_verify_merge doesn't support merging to a file WCPATH
9130 # so use run_and_verify_svn.
9131 short_nu_COPY_path = shorten_path_kludge(nu_COPY_path)
9132 svntest.actions.run_and_verify_svn(None,
9133 expected_merge_output([[8]],
9134 'U ' + short_nu_COPY_path + '\n'),
9135 [], 'merge', '-c8',
9136 sbox.repo_url + '/A/D/H/nu',
9137 short_nu_COPY_path)
9138 # Merge -r4:6 to A_COPY, then reverse merge r6 from A_COPY/D.
9139 short_A_COPY_path = shorten_path_kludge(A_COPY_path)
9140 expected_output = wc.State(short_A_COPY_path, {
9141 'B/E/beta' : Item(status='U '),
9142 'D/H/omega': Item(status='U '),
9144 expected_status = wc.State(short_A_COPY_path, {
9145 '' : Item(status=' M', wc_rev=2),
9146 'B' : Item(status=' ', wc_rev=2),
9147 'mu' : Item(status=' ', wc_rev=2),
9148 'B/E' : Item(status=' ', wc_rev=2),
9149 'B/E/alpha' : Item(status=' ', wc_rev=2),
9150 'B/E/beta' : Item(status='M ', wc_rev=2),
9151 'B/lambda' : Item(status=' ', wc_rev=2),
9152 'B/F' : Item(status=' ', wc_rev=2),
9153 'C' : Item(status=' ', wc_rev=2),
9154 'D' : Item(status=' ', wc_rev=2),
9155 'D/G' : Item(status=' ', wc_rev=2),
9156 'D/G/pi' : Item(status=' ', wc_rev=2),
9157 'D/G/rho' : Item(status=' ', wc_rev=2),
9158 'D/G/tau' : Item(status=' ', wc_rev=2),
9159 'D/gamma' : Item(status=' ', wc_rev=2),
9160 'D/H' : Item(status=' M', wc_rev=2),
9161 'D/H/chi' : Item(status=' ', wc_rev=2),
9162 'D/H/psi' : Item(status=' ', wc_rev=2),
9163 'D/H/omega' : Item(status='M ', wc_rev=2),
9164 'D/H/nu' : Item(status='A ', copied='+', wc_rev='-'),
9166 expected_disk = wc.State('', {
9167 '' : Item(props={SVN_PROP_MERGEINFO : '/A:5-6'}),
9168 'B' : Item(),
9169 'mu' : Item("This is the file 'mu'.\n"),
9170 'B/E' : Item(),
9171 'B/E/alpha' : Item("This is the file 'alpha'.\n"),
9172 'B/E/beta' : Item("New content"),
9173 'B/lambda' : Item("This is the file 'lambda'.\n"),
9174 'B/F' : Item(),
9175 'C' : Item(),
9176 'D' : Item(),
9177 'D/G' : Item(),
9178 'D/G/pi' : Item("This is the file 'pi'.\n"),
9179 'D/G/rho' : Item("This is the file 'rho'.\n"),
9180 'D/G/tau' : Item("This is the file 'tau'.\n"),
9181 'D/gamma' : Item("This is the file 'gamma'.\n"),
9182 'D/H' : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:5-7'}),
9183 'D/H/chi' : Item("This is the file 'chi'.\n"),
9184 'D/H/psi' : Item("This is the file 'psi'.\n"),
9185 'D/H/omega' : Item("New content"),
9186 'D/H/nu' : Item("New content",
9187 props={SVN_PROP_MERGEINFO : '/A/D/H/nu:5-8'}),
9189 expected_skip = wc.State(short_A_COPY_path, { })
9190 svntest.actions.run_and_verify_merge(short_A_COPY_path, '4', '6',
9191 sbox.repo_url + \
9192 '/A',
9193 expected_output,
9194 expected_disk,
9195 expected_status,
9196 expected_skip,
9197 None, None, None, None,
9198 None, 1)
9199 short_D_COPY_path = shorten_path_kludge(D_COPY_path)
9200 expected_output = wc.State(short_D_COPY_path, {
9201 'H/omega': Item(status='G '),
9203 expected_status = wc.State(short_D_COPY_path, {
9204 '' : Item(status=' M', wc_rev=2),
9205 'G' : Item(status=' ', wc_rev=2),
9206 'G/pi' : Item(status=' ', wc_rev=2),
9207 'G/rho' : Item(status=' ', wc_rev=2),
9208 'G/tau' : Item(status=' ', wc_rev=2),
9209 'gamma' : Item(status=' ', wc_rev=2),
9210 'H' : Item(status=' M', wc_rev=2),
9211 'H/chi' : Item(status=' ', wc_rev=2),
9212 'H/psi' : Item(status=' ', wc_rev=2),
9213 'H/omega' : Item(status=' ', wc_rev=2),
9214 'H/nu' : Item(status='A ', copied='+', wc_rev='-'),
9216 expected_disk = wc.State('', {
9217 '' : Item(props={SVN_PROP_MERGEINFO : '/A/D:5'}),
9218 'G/pi' : Item("This is the file 'pi'.\n"),
9219 'G/rho' : Item("This is the file 'rho'.\n"),
9220 'G/tau' : Item("This is the file 'tau'.\n"),
9221 'gamma' : Item("This is the file 'gamma'.\n"),
9222 'H' : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:5,7'}),
9223 'H/chi' : Item("This is the file 'chi'.\n"),
9224 'H/psi' : Item("This is the file 'psi'.\n"),
9225 'H/omega' : Item("This is the file 'omega'.\n"),
9226 'H/nu' : Item("New content",
9227 props={SVN_PROP_MERGEINFO : '/A/D/H/nu:5,7-8'}),
9229 expected_skip = wc.State(short_D_COPY_path, { })
9230 svntest.actions.run_and_verify_merge(short_D_COPY_path, '6', '5',
9231 sbox.repo_url + \
9232 '/A/D',
9233 expected_output,
9234 expected_disk,
9235 expected_status,
9236 expected_skip,
9237 None, None, None, None,
9238 None, 1)
9239 # Now once again merge r6 to A_COPY. A_COPY already has r6 in its mergeinfo
9240 # so we expect only subtree merges on A_COPY/D, A_COPY_D_H, and
9241 # A_COPY/D/H/nu. The fact that A/D/H/nu doesn't exist at r6 should not cause
9242 # the merge to fail.
9243 short_A_COPY_path = shorten_path_kludge(A_COPY_path)
9244 expected_output = wc.State(short_A_COPY_path, {
9245 'D/H/omega': Item(status='U '),
9247 expected_status = wc.State(short_A_COPY_path, {
9248 '' : Item(status=' M', wc_rev=2),
9249 'B' : Item(status=' ', wc_rev=2),
9250 'mu' : Item(status=' ', wc_rev=2),
9251 'B/E' : Item(status=' ', wc_rev=2),
9252 'B/E/alpha' : Item(status=' ', wc_rev=2),
9253 'B/E/beta' : Item(status='M ', wc_rev=2),
9254 'B/lambda' : Item(status=' ', wc_rev=2),
9255 'B/F' : Item(status=' ', wc_rev=2),
9256 'C' : Item(status=' ', wc_rev=2),
9257 'D' : Item(status=' ', wc_rev=2),
9258 'D/G' : Item(status=' ', wc_rev=2),
9259 'D/G/pi' : Item(status=' ', wc_rev=2),
9260 'D/G/rho' : Item(status=' ', wc_rev=2),
9261 'D/G/tau' : Item(status=' ', wc_rev=2),
9262 'D/gamma' : Item(status=' ', wc_rev=2),
9263 'D/H' : Item(status=' M', wc_rev=2),
9264 'D/H/chi' : Item(status=' ', wc_rev=2),
9265 'D/H/psi' : Item(status=' ', wc_rev=2),
9266 'D/H/omega' : Item(status='M ', wc_rev=2),
9267 'D/H/nu' : Item(status='A ', copied='+', wc_rev='-'),
9269 expected_disk = wc.State('', {
9270 '' : Item(props={SVN_PROP_MERGEINFO : '/A:5-6'}),
9271 'B' : Item(),
9272 'mu' : Item("This is the file 'mu'.\n"),
9273 'B/E' : Item(),
9274 'B/E/alpha' : Item("This is the file 'alpha'.\n"),
9275 'B/E/beta' : Item("New content"),
9276 'B/lambda' : Item("This is the file 'lambda'.\n"),
9277 'B/F' : Item(),
9278 'C' : Item(),
9279 'D' : Item(), # Mergeinfo elides to 'A_COPY'
9280 'D/G' : Item(),
9281 'D/G/pi' : Item("This is the file 'pi'.\n"),
9282 'D/G/rho' : Item("This is the file 'rho'.\n"),
9283 'D/G/tau' : Item("This is the file 'tau'.\n"),
9284 'D/gamma' : Item("This is the file 'gamma'.\n"),
9285 'D/H' : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:5-7'}),
9286 'D/H/chi' : Item("This is the file 'chi'.\n"),
9287 'D/H/psi' : Item("This is the file 'psi'.\n"),
9288 'D/H/omega' : Item("New content"),
9289 'D/H/nu' : Item("New content",
9290 props={SVN_PROP_MERGEINFO : '/A/D/H/nu:5-8'}),
9292 expected_skip = wc.State(short_A_COPY_path, { })
9293 svntest.actions.run_and_verify_merge(short_A_COPY_path, '5', '6',
9294 sbox.repo_url + \
9295 '/A',
9296 expected_output,
9297 expected_disk,
9298 expected_status,
9299 expected_skip,
9300 None, None, None, None,
9301 None, 1)
9302 os.chdir(saved_cwd)
9304 def basic_reintegrate(sbox):
9305 "basic merge --reintegrate support"
9307 # Make A_COPY branch in r2, and do a few more commits to A in r3-6.
9308 sbox.build()
9309 wc_dir = sbox.wc_dir
9310 expected_disk, expected_status = set_up_branch(sbox)
9312 # Make a change on the branch, to A/mu. Commit in r7.
9313 svntest.main.file_write(os.path.join(wc_dir, "A_COPY", "mu"),
9314 "Changed on the branch.")
9315 expected_output = wc.State(wc_dir, {'A_COPY/mu' : Item(verb='Sending')})
9316 expected_status.tweak('A_COPY/mu', wc_rev=7)
9317 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
9318 expected_status, None, wc_dir)
9319 expected_disk.tweak('A_COPY/mu', contents='Changed on the branch.')
9321 # Update the wcs.
9322 expected_output = wc.State(wc_dir, {})
9323 expected_status.tweak(wc_rev='7')
9324 svntest.actions.run_and_verify_update(wc_dir, expected_output,
9325 expected_disk, expected_status,
9326 None, None, None, None, None, True)
9328 # Merge from trunk to branch (ie, r3-6), using normal cherry-harvest.
9329 A_COPY_path = os.path.join(wc_dir, "A_COPY")
9330 short_A_COPY_path = shorten_path_kludge(A_COPY_path)
9331 expected_output = wc.State(short_A_COPY_path, {
9332 'D/H/psi' : Item(status='U '),
9333 'D/G/rho' : Item(status='U '),
9334 'B/E/beta' : Item(status='U '),
9335 'D/H/omega' : Item(status='U '),
9337 k_expected_status = wc.State(short_A_COPY_path, {
9338 "B" : Item(status=' ', wc_rev=7),
9339 "B/lambda" : Item(status=' ', wc_rev=7),
9340 "B/E" : Item(status=' ', wc_rev=7),
9341 "B/E/alpha" : Item(status=' ', wc_rev=7),
9342 "B/E/beta" : Item(status='M ', wc_rev=7),
9343 "B/F" : Item(status=' ', wc_rev=7),
9344 "mu" : Item(status=' ', wc_rev=7),
9345 "C" : Item(status=' ', wc_rev=7),
9346 "D" : Item(status=' ', wc_rev=7),
9347 "D/gamma" : Item(status=' ', wc_rev=7),
9348 "D/G" : Item(status=' ', wc_rev=7),
9349 "D/G/pi" : Item(status=' ', wc_rev=7),
9350 "D/G/rho" : Item(status='M ', wc_rev=7),
9351 "D/G/tau" : Item(status=' ', wc_rev=7),
9352 "D/H" : Item(status=' ', wc_rev=7),
9353 "D/H/chi" : Item(status=' ', wc_rev=7),
9354 "D/H/omega" : Item(status='M ', wc_rev=7),
9355 "D/H/psi" : Item(status='M ', wc_rev=7),
9356 "" : Item(status=' M', wc_rev=7),
9358 k_expected_disk = wc.State('', {
9359 '' : Item(props={SVN_PROP_MERGEINFO : '/A:2-7'}),
9360 'B' : Item(),
9361 'B/lambda' : Item("This is the file 'lambda'.\n"),
9362 'B/E' : Item(),
9363 'B/E/alpha' : Item("This is the file 'alpha'.\n"),
9364 'B/E/beta' : Item("New content"),
9365 'B/F' : Item(),
9366 'mu' : Item("Changed on the branch."),
9367 'C' : Item(),
9368 'D' : Item(),
9369 'D/gamma' : Item("This is the file 'gamma'.\n"),
9370 'D/G' : Item(),
9371 'D/G/pi' : Item("This is the file 'pi'.\n"),
9372 'D/G/rho' : Item("New content"),
9373 'D/G/tau' : Item("This is the file 'tau'.\n"),
9374 'D/H' : Item(),
9375 'D/H/chi' : Item("This is the file 'chi'.\n"),
9376 'D/H/omega' : Item("New content"),
9377 'D/H/psi' : Item("New content"),
9379 expected_skip = wc.State(short_A_COPY_path, {})
9380 saved_cwd = os.getcwd()
9381 os.chdir(svntest.main.work_dir)
9382 svntest.actions.run_and_verify_merge(short_A_COPY_path, None, None,
9383 sbox.repo_url + '/A',
9384 expected_output,
9385 k_expected_disk,
9386 k_expected_status,
9387 expected_skip,
9388 None, None, None, None,
9389 None, True)
9390 os.chdir(saved_cwd)
9391 expected_disk.tweak('A_COPY', props={SVN_PROP_MERGEINFO: '/A:2-7'})
9392 expected_disk.tweak('A_COPY/B/E/beta', contents="New content")
9393 expected_disk.tweak('A_COPY/D/G/rho', contents="New content")
9394 expected_disk.tweak('A_COPY/D/H/omega', contents="New content")
9395 expected_disk.tweak('A_COPY/D/H/psi', contents="New content")
9397 # Commit the merge to branch (r8).
9398 expected_output = wc.State(wc_dir, {
9399 'A_COPY/D/H/psi' : Item(verb='Sending'),
9400 'A_COPY/D/G/rho' : Item(verb='Sending'),
9401 'A_COPY/B/E/beta' : Item(verb='Sending'),
9402 'A_COPY/D/H/omega' : Item(verb='Sending'),
9403 'A_COPY' : Item(verb='Sending'),
9405 expected_status.tweak('A_COPY', 'A_COPY/D/H/psi', 'A_COPY/D/G/rho',
9406 'A_COPY/B/E/beta', 'A_COPY/D/H/omega', wc_rev=8)
9407 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
9408 expected_status, None, wc_dir)
9410 # Update the wcs again.
9411 expected_output = wc.State(wc_dir, {})
9412 expected_status.tweak(wc_rev='8')
9413 svntest.actions.run_and_verify_update(wc_dir, expected_output,
9414 expected_disk, expected_status,
9415 None, None, None, None, None, True)
9418 # *finally*, actually run merge --reintegrate in trunk with the
9419 # branch URL. This should bring in the mu change and the tauprime
9420 # change.
9421 A_path = os.path.join(wc_dir, "A")
9422 short_A_path = shorten_path_kludge(A_path)
9423 expected_output = wc.State(short_A_path, {
9424 '' : Item(status=' U'),
9425 'mu' : Item(status='U '),
9427 k_expected_status = wc.State(short_A_path, {
9428 "B" : Item(status=' ', wc_rev=8),
9429 "B/lambda" : Item(status=' ', wc_rev=8),
9430 "B/E" : Item(status=' ', wc_rev=8),
9431 "B/E/alpha" : Item(status=' ', wc_rev=8),
9432 "B/E/beta" : Item(status=' ', wc_rev=8),
9433 "B/F" : Item(status=' ', wc_rev=8),
9434 "mu" : Item(status='M ', wc_rev=8),
9435 "C" : Item(status=' ', wc_rev=8),
9436 "D" : Item(status=' ', wc_rev=8),
9437 "D/gamma" : Item(status=' ', wc_rev=8),
9438 "D/G" : Item(status=' ', wc_rev=8),
9439 "D/G/pi" : Item(status=' ', wc_rev=8),
9440 "D/G/rho" : Item(status=' ', wc_rev=8),
9441 "D/G/tau" : Item(status=' ', wc_rev=8),
9442 "D/H" : Item(status=' ', wc_rev=8),
9443 "D/H/chi" : Item(status=' ', wc_rev=8),
9444 "D/H/omega" : Item(status=' ', wc_rev=8),
9445 "D/H/psi" : Item(status=' ', wc_rev=8),
9446 "" : Item(status=' M', wc_rev=8),
9448 k_expected_disk.tweak('', props={SVN_PROP_MERGEINFO : '/A_COPY:2-8'})
9449 expected_skip = wc.State(short_A_path, {})
9450 saved_cwd = os.getcwd()
9451 os.chdir(svntest.main.work_dir)
9452 svntest.actions.run_and_verify_merge(short_A_path, None, None,
9453 sbox.repo_url + '/A_COPY',
9454 expected_output,
9455 k_expected_disk,
9456 k_expected_status,
9457 expected_skip,
9458 None, None, None, None,
9459 None, True, True,
9460 '--reintegrate')
9461 os.chdir(saved_cwd)
9463 # Finally, commit the result of the merge (r9).
9464 expected_output = wc.State(wc_dir, {
9465 'A/mu' : Item(verb='Sending'),
9466 'A' : Item(verb='Sending'),
9468 expected_status.tweak('A', 'A/mu', wc_rev=9)
9469 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
9470 expected_status, None, wc_dir)
9472 def reintegrate_with_rename(sbox):
9473 "merge --reintegrate with renamed file on branch"
9475 # Make A_COPY branch in r2, and do a few more commits to A in r3-6.
9476 sbox.build()
9477 wc_dir = sbox.wc_dir
9478 expected_disk, expected_status = set_up_branch(sbox)
9480 # Make a change on the branch, to A/mu. Commit in r7.
9481 svntest.main.file_write(os.path.join(wc_dir, "A_COPY", "mu"),
9482 "Changed on the branch.")
9483 expected_output = wc.State(wc_dir, {'A_COPY/mu' : Item(verb='Sending')})
9484 expected_status.tweak('A_COPY/mu', wc_rev=7)
9485 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
9486 expected_status, None, wc_dir)
9487 expected_disk.tweak('A_COPY/mu', contents='Changed on the branch.')
9489 # Update the wcs.
9490 expected_output = wc.State(wc_dir, {})
9491 expected_status.tweak(wc_rev='7')
9492 svntest.actions.run_and_verify_update(wc_dir, expected_output,
9493 expected_disk, expected_status,
9494 None, None, None, None, None, True)
9496 # Merge from trunk to branch (ie, r3-6), using normal cherry-harvest.
9497 A_COPY_path = os.path.join(wc_dir, "A_COPY")
9498 short_A_COPY_path = shorten_path_kludge(A_COPY_path)
9499 expected_output = wc.State(short_A_COPY_path, {
9500 'D/H/psi' : Item(status='U '),
9501 'D/G/rho' : Item(status='U '),
9502 'B/E/beta' : Item(status='U '),
9503 'D/H/omega' : Item(status='U '),
9505 k_expected_status = wc.State(short_A_COPY_path, {
9506 "B" : Item(status=' ', wc_rev=7),
9507 "B/lambda" : Item(status=' ', wc_rev=7),
9508 "B/E" : Item(status=' ', wc_rev=7),
9509 "B/E/alpha" : Item(status=' ', wc_rev=7),
9510 "B/E/beta" : Item(status='M ', wc_rev=7),
9511 "B/F" : Item(status=' ', wc_rev=7),
9512 "mu" : Item(status=' ', wc_rev=7),
9513 "C" : Item(status=' ', wc_rev=7),
9514 "D" : Item(status=' ', wc_rev=7),
9515 "D/gamma" : Item(status=' ', wc_rev=7),
9516 "D/G" : Item(status=' ', wc_rev=7),
9517 "D/G/pi" : Item(status=' ', wc_rev=7),
9518 "D/G/rho" : Item(status='M ', wc_rev=7),
9519 "D/G/tau" : Item(status=' ', wc_rev=7),
9520 "D/H" : Item(status=' ', wc_rev=7),
9521 "D/H/chi" : Item(status=' ', wc_rev=7),
9522 "D/H/omega" : Item(status='M ', wc_rev=7),
9523 "D/H/psi" : Item(status='M ', wc_rev=7),
9524 "" : Item(status=' M', wc_rev=7),
9526 k_expected_disk = wc.State('', {
9527 '' : Item(props={SVN_PROP_MERGEINFO : '/A:2-7'}),
9528 'B' : Item(),
9529 'B/lambda' : Item("This is the file 'lambda'.\n"),
9530 'B/E' : Item(),
9531 'B/E/alpha' : Item("This is the file 'alpha'.\n"),
9532 'B/E/beta' : Item("New content"),
9533 'B/F' : Item(),
9534 'mu' : Item("Changed on the branch."),
9535 'C' : Item(),
9536 'D' : Item(),
9537 'D/gamma' : Item("This is the file 'gamma'.\n"),
9538 'D/G' : Item(),
9539 'D/G/pi' : Item("This is the file 'pi'.\n"),
9540 'D/G/rho' : Item("New content"),
9541 'D/G/tau' : Item("This is the file 'tau'.\n"),
9542 'D/H' : Item(),
9543 'D/H/chi' : Item("This is the file 'chi'.\n"),
9544 'D/H/omega' : Item("New content"),
9545 'D/H/psi' : Item("New content"),
9547 expected_skip = wc.State(short_A_COPY_path, {})
9548 saved_cwd = os.getcwd()
9549 os.chdir(svntest.main.work_dir)
9550 svntest.actions.run_and_verify_merge(short_A_COPY_path, None, None,
9551 sbox.repo_url + '/A',
9552 expected_output,
9553 k_expected_disk,
9554 k_expected_status,
9555 expected_skip,
9556 None, None, None, None,
9557 None, True)
9558 os.chdir(saved_cwd)
9559 expected_disk.tweak('A_COPY', props={SVN_PROP_MERGEINFO: '/A:2-7'})
9560 expected_disk.tweak('A_COPY/B/E/beta', contents="New content")
9561 expected_disk.tweak('A_COPY/D/G/rho', contents="New content")
9562 expected_disk.tweak('A_COPY/D/H/omega', contents="New content")
9563 expected_disk.tweak('A_COPY/D/H/psi', contents="New content")
9565 # Commit the merge to branch (r8).
9566 expected_output = wc.State(wc_dir, {
9567 'A_COPY/D/H/psi' : Item(verb='Sending'),
9568 'A_COPY/D/G/rho' : Item(verb='Sending'),
9569 'A_COPY/B/E/beta' : Item(verb='Sending'),
9570 'A_COPY/D/H/omega' : Item(verb='Sending'),
9571 'A_COPY' : Item(verb='Sending'),
9573 expected_status.tweak('A_COPY', 'A_COPY/D/H/psi', 'A_COPY/D/G/rho',
9574 'A_COPY/B/E/beta', 'A_COPY/D/H/omega', wc_rev=8)
9575 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
9576 expected_status, None, wc_dir)
9579 # Update the wcs again.
9581 # Note: this update had to be added because of r28942 (which was
9582 # merged into the reintegrate branch in r28947). Without this
9583 # update, the mergeinfo will not be inherited properly as part of
9584 # the 'svn cp tau tauprime' step, and later (during the post-commit
9585 # update, with the new expected_disk) we'll get an error like this:
9587 # =============================================================
9588 # Expected 'tauprime' and actual 'tauprime' in disk tree are different!
9589 # =============================================================
9590 # EXPECTED NODE TO BE:
9591 # =============================================================
9592 # * Node name: tauprime
9593 # Path: A_COPY/D/G/tauprime
9594 # Contents: This is the file 'tau'.
9596 # Properties: {'svn:mergeinfo': '/A/D/G/tau:2-7'}
9597 # Attributes: {}
9598 # Children: N/A (node is a file)
9599 # =============================================================
9600 # ACTUAL NODE FOUND:
9601 # =============================================================
9602 # * Node name: tauprime
9603 # Path: G/tauprime
9604 # Contents: This is the file 'tau'.
9606 # Properties: {'svn:mergeinfo': ''}
9607 # Attributes: {}
9608 # Children: N/A (node is a file)
9610 expected_output = wc.State(wc_dir, {})
9611 expected_status.tweak(wc_rev='8')
9612 svntest.actions.run_and_verify_update(wc_dir, expected_output,
9613 expected_disk, expected_status,
9614 None, None, None, None, None, True)
9616 # Make another change on the branch: copy tau to tauprime. Commit
9617 # in r9.
9618 svntest.actions.run_and_verify_svn(None, None, [], 'cp',
9619 os.path.join(wc_dir, 'A_COPY', 'D', 'G',
9620 'tau'),
9621 os.path.join(wc_dir, 'A_COPY', 'D', 'G',
9622 'tauprime'))
9624 expected_output = wc.State(wc_dir, {
9625 'A_COPY/D/G/tauprime' : Item(verb='Adding')
9627 expected_status.add({'A_COPY/D/G/tauprime': Item(status=' ', wc_rev=9)})
9628 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
9629 expected_status, None, wc_dir)
9631 expected_disk.add({
9632 'A_COPY/D/G/tauprime' : Item(props={SVN_PROP_MERGEINFO: '/A/D/G/tau:2-7'},
9633 contents="This is the file 'tau'.\n")
9636 # Update the trunk (well, the whole wc) (since reintegrate really
9637 # wants a clean wc).
9638 expected_output = wc.State(wc_dir, {})
9639 expected_status.tweak(wc_rev='9')
9640 svntest.actions.run_and_verify_update(wc_dir, expected_output,
9641 expected_disk, expected_status,
9642 None, None, None, None, None, True)
9644 # *finally*, actually run merge --reintegrate in trunk with the
9645 # branch URL. This should bring in the mu change and the tauprime
9646 # change.
9647 A_path = os.path.join(wc_dir, "A")
9648 short_A_path = shorten_path_kludge(A_path)
9649 expected_output = wc.State(short_A_path, {
9650 '' : Item(status=' U'),
9651 'mu' : Item(status='U '),
9652 'D/G/tauprime' : Item(status='A '),
9654 k_expected_status = wc.State(short_A_path, {
9655 "B" : Item(status=' ', wc_rev=9),
9656 "B/lambda" : Item(status=' ', wc_rev=9),
9657 "B/E" : Item(status=' ', wc_rev=9),
9658 "B/E/alpha" : Item(status=' ', wc_rev=9),
9659 "B/E/beta" : Item(status=' ', wc_rev=9),
9660 "B/F" : Item(status=' ', wc_rev=9),
9661 "mu" : Item(status='M ', wc_rev=9),
9662 "C" : Item(status=' ', wc_rev=9),
9663 "D" : Item(status=' ', wc_rev=9),
9664 "D/gamma" : Item(status=' ', wc_rev=9),
9665 "D/G" : Item(status=' ', wc_rev=9),
9666 "D/G/pi" : Item(status=' ', wc_rev=9),
9667 "D/G/rho" : Item(status=' ', wc_rev=9),
9668 "D/G/tau" : Item(status=' ', wc_rev=9),
9669 "D/G/tauprime" : Item(status='A ', wc_rev='-', copied='+'),
9670 "D/H" : Item(status=' ', wc_rev=9),
9671 "D/H/chi" : Item(status=' ', wc_rev=9),
9672 "D/H/omega" : Item(status=' ', wc_rev=9),
9673 "D/H/psi" : Item(status=' ', wc_rev=9),
9674 "" : Item(status=' M', wc_rev=9),
9676 k_expected_disk.tweak('', props={SVN_PROP_MERGEINFO : '/A_COPY:2-9'})
9677 k_expected_disk.add({
9678 'D/G/tauprime' : Item(props={SVN_PROP_MERGEINFO: '/A/D/G/tau:2-7'},
9679 contents="This is the file 'tau'.\n")
9681 expected_skip = wc.State(short_A_path, {})
9682 saved_cwd = os.getcwd()
9683 os.chdir(svntest.main.work_dir)
9684 svntest.actions.run_and_verify_merge(short_A_path, None, None,
9685 sbox.repo_url + '/A_COPY',
9686 expected_output,
9687 k_expected_disk,
9688 k_expected_status,
9689 expected_skip,
9690 None, None, None, None,
9691 None, True, True,
9692 '--reintegrate')
9693 os.chdir(saved_cwd)
9695 # Finally, commit the result of the merge (r10).
9696 expected_output = wc.State(wc_dir, {
9697 'A/D/G/tauprime' : Item(verb='Adding'),
9698 'A/mu' : Item(verb='Sending'),
9699 'A' : Item(verb='Sending'),
9701 expected_status.add({
9702 'A/D/G/tauprime' : Item(status=' ', wc_rev=10),
9704 expected_status.tweak('A', 'A/mu', wc_rev=10)
9705 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
9706 expected_status, None, wc_dir)
9708 def reintegrate_branch_never_merged_to(sbox):
9709 "merge --reintegrate on a never-updated branch"
9711 # Make A_COPY branch in r2, and do a few more commits to A in r3-6.
9712 sbox.build()
9713 wc_dir = sbox.wc_dir
9714 expected_disk, expected_status = set_up_branch(sbox)
9716 # Make a change on the branch, to A/mu. Commit in r7.
9717 svntest.main.file_write(os.path.join(wc_dir, "A_COPY", "mu"),
9718 "Changed on the branch.")
9719 expected_output = wc.State(wc_dir, {'A_COPY/mu' : Item(verb='Sending')})
9720 expected_status.tweak('A_COPY/mu', wc_rev=7)
9721 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
9722 expected_status, None, wc_dir)
9723 expected_disk.tweak('A_COPY/mu', contents='Changed on the branch.')
9725 # Update the wcs.
9726 expected_output = wc.State(wc_dir, {})
9727 expected_status.tweak(wc_rev='7')
9728 svntest.actions.run_and_verify_update(wc_dir, expected_output,
9729 expected_disk, expected_status,
9730 None, None, None, None, None, True)
9732 # Make another change on the branch: copy tau to tauprime. Commit
9733 # in r8.
9734 svntest.actions.run_and_verify_svn(None, None, [], 'cp',
9735 os.path.join(wc_dir, 'A_COPY', 'D', 'G',
9736 'tau'),
9737 os.path.join(wc_dir, 'A_COPY', 'D', 'G',
9738 'tauprime'))
9739 expected_output = wc.State(wc_dir, {
9740 'A_COPY/D/G/tauprime' : Item(verb='Adding')
9742 expected_status.add({'A_COPY/D/G/tauprime': Item(status=' ', wc_rev=8)})
9743 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
9744 expected_status, None, wc_dir)
9745 expected_disk.add({
9746 'A_COPY/D/G/tauprime' : Item(props={SVN_PROP_MERGEINFO: ''},
9747 ### TODO(reint): why empty?
9748 contents="This is the file 'tau'.\n")
9751 # Update the trunk (well, the whole wc) (since reintegrate really
9752 # wants a clean wc).
9753 expected_output = wc.State(wc_dir, {})
9754 expected_status.tweak(wc_rev='8')
9755 svntest.actions.run_and_verify_update(wc_dir, expected_output,
9756 expected_disk, expected_status,
9757 None, None, None, None, None, True)
9759 # *finally*, actually run merge --reintegrate in trunk with the
9760 # branch URL. This should bring in the mu change and the tauprime
9761 # change.
9762 A_path = os.path.join(wc_dir, "A")
9763 short_A_path = shorten_path_kludge(A_path)
9764 expected_output = wc.State(short_A_path, {
9765 'mu' : Item(status='U '),
9766 'D/G/tauprime' : Item(status='A '),
9768 k_expected_status = wc.State(short_A_path, {
9769 "B" : Item(status=' ', wc_rev=8),
9770 "B/lambda" : Item(status=' ', wc_rev=8),
9771 "B/E" : Item(status=' ', wc_rev=8),
9772 "B/E/alpha" : Item(status=' ', wc_rev=8),
9773 "B/E/beta" : Item(status=' ', wc_rev=8),
9774 "B/F" : Item(status=' ', wc_rev=8),
9775 "mu" : Item(status='M ', wc_rev=8),
9776 "C" : Item(status=' ', wc_rev=8),
9777 "D" : Item(status=' ', wc_rev=8),
9778 "D/gamma" : Item(status=' ', wc_rev=8),
9779 "D/G" : Item(status=' ', wc_rev=8),
9780 "D/G/pi" : Item(status=' ', wc_rev=8),
9781 "D/G/rho" : Item(status=' ', wc_rev=8),
9782 "D/G/tau" : Item(status=' ', wc_rev=8),
9783 "D/G/tauprime" : Item(status='A ', wc_rev='-', copied='+'),
9784 "D/H" : Item(status=' ', wc_rev=8),
9785 "D/H/chi" : Item(status=' ', wc_rev=8),
9786 "D/H/omega" : Item(status=' ', wc_rev=8),
9787 "D/H/psi" : Item(status=' ', wc_rev=8),
9788 "" : Item(status=' M', wc_rev=8),
9790 k_expected_disk = wc.State('', {
9791 '' : Item(props={SVN_PROP_MERGEINFO : '/A_COPY:2-8'}),
9792 'B' : Item(),
9793 'B/lambda' : Item("This is the file 'lambda'.\n"),
9794 'B/E' : Item(),
9795 'B/E/alpha' : Item("This is the file 'alpha'.\n"),
9796 'B/E/beta' : Item("New content"),
9797 'B/F' : Item(),
9798 'mu' : Item("Changed on the branch."),
9799 'C' : Item(),
9800 'D' : Item(),
9801 'D/gamma' : Item("This is the file 'gamma'.\n"),
9802 'D/G' : Item(),
9803 'D/G/pi' : Item("This is the file 'pi'.\n"),
9804 'D/G/rho' : Item("New content"),
9805 'D/G/tau' : Item("This is the file 'tau'.\n"),
9806 'D/G/tauprime' : Item("This is the file 'tau'.\n",
9807 ### TODO(reint): why empty?
9808 props={SVN_PROP_MERGEINFO: ''}),
9809 'D/H' : Item(),
9810 'D/H/chi' : Item("This is the file 'chi'.\n"),
9811 'D/H/omega' : Item("New content"),
9812 'D/H/psi' : Item("New content"),
9814 expected_skip = wc.State(short_A_path, {})
9815 saved_cwd = os.getcwd()
9816 os.chdir(svntest.main.work_dir)
9817 svntest.actions.run_and_verify_merge(short_A_path, None, None,
9818 sbox.repo_url + '/A_COPY',
9819 expected_output,
9820 k_expected_disk,
9821 k_expected_status,
9822 expected_skip,
9823 None, None, None, None,
9824 None, True, True,
9825 '--reintegrate')
9826 os.chdir(saved_cwd)
9828 # Finally, commit the result of the merge (r9).
9829 expected_output = wc.State(wc_dir, {
9830 'A/D/G/tauprime' : Item(verb='Adding'),
9831 'A/mu' : Item(verb='Sending'),
9832 'A' : Item(verb='Sending'),
9834 expected_status.add({
9835 'A/D/G/tauprime' : Item(status=' ', wc_rev=9),
9837 expected_status.tweak('A', 'A/mu', wc_rev=9)
9838 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
9839 expected_status, None, wc_dir)
9841 def reintegrate_fail_on_modified_wc(sbox):
9842 "merge --reintegrate should fail in modified wc"
9843 sbox.build()
9844 wc_dir = sbox.wc_dir
9845 A_path = os.path.join(wc_dir, "A")
9846 mu_path = os.path.join(A_path, "mu")
9847 ignored_expected_disk, ignored_expected_status = set_up_branch(sbox)
9848 svntest.main.file_write(mu_path, "Changed on 'trunk' (the merge target).")
9849 svntest.actions.run_and_verify_merge(
9850 A_path, None, None, sbox.repo_url + '/A_COPY', None, None, None, None,
9851 ".*Cannot reintegrate into a working copy that has local modifications.*",
9852 None, None, None, None, True, False, '--reintegrate')
9854 def reintegrate_fail_on_mixed_rev_wc(sbox):
9855 "merge --reintegrate should fail in mixed-rev wc"
9856 sbox.build()
9857 wc_dir = sbox.wc_dir
9858 A_path = os.path.join(wc_dir, "A")
9859 mu_path = os.path.join(A_path, "mu")
9860 ignored_expected_disk, expected_status = set_up_branch(sbox)
9861 # Make and commit a change, in order to get a mixed-rev wc.
9862 svntest.main.file_write(mu_path, "Changed on 'trunk' (the merge target).")
9863 expected_output = wc.State(wc_dir, {
9864 'A/mu' : Item(verb='Sending'),
9866 expected_status.tweak('A/mu', wc_rev=7)
9867 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
9868 expected_status, None, wc_dir)
9869 # Try merging into that same wc, expecting failure.
9870 svntest.actions.run_and_verify_merge(
9871 A_path, None, None, sbox.repo_url + '/A_COPY', None, None, None, None,
9872 ".*Cannot reintegrate into mixed-revision working copy.*",
9873 None, None, None, None, True, False, '--reintegrate')
9875 def reintegrate_fail_on_switched_wc(sbox):
9876 "merge --reintegrate should fail in switched wc"
9877 sbox.build()
9878 wc_dir = sbox.wc_dir
9879 A_path = os.path.join(wc_dir, "A")
9880 G_path = os.path.join(A_path, "D", "G")
9881 switch_url = sbox.repo_url + "/A/D/H"
9882 expected_disk, expected_status = set_up_branch(sbox)
9884 # Switch a subdir of the target.
9885 expected_output = svntest.wc.State(wc_dir, {
9886 'A/D/G/pi' : Item(status='D '),
9887 'A/D/G/rho' : Item(status='D '),
9888 'A/D/G/tau' : Item(status='D '),
9889 'A/D/G/chi' : Item(status='A '),
9890 'A/D/G/psi' : Item(status='A '),
9891 'A/D/G/omega' : Item(status='A '),
9893 expected_disk.remove('A/D/G/pi', 'A/D/G/rho', 'A/D/G/tau')
9894 expected_disk.add({
9895 'A/D/G/chi' : Item(contents="This is the file 'chi'.\n"),
9896 'A/D/G/psi' : Item(contents="New content"),
9897 'A/D/G/omega' : Item(contents="New content"),
9899 expected_status.remove('A/D/G/pi', 'A/D/G/rho', 'A/D/G/tau')
9900 expected_status.add({
9901 'A/D/G' : Item(status=' ', wc_rev=6, switched='S'),
9902 'A/D/G/chi' : Item(status=' ', wc_rev=6),
9903 'A/D/G/psi' : Item(status=' ', wc_rev=6),
9904 'A/D/G/omega' : Item(status=' ', wc_rev=6),
9906 svntest.actions.run_and_verify_switch(wc_dir,
9907 G_path,
9908 switch_url,
9909 expected_output,
9910 expected_disk,
9911 expected_status,
9912 None, None, None, None, False);
9913 svntest.actions.run_and_verify_merge(
9914 A_path, None, None, sbox.repo_url + '/A_COPY', None, None, None, None,
9915 ".*Cannot reintegrate into a working copy with a switched subtree.*",
9916 None, None, None, None, True, False, '--reintegrate')
9918 def reintegrate_fail_on_shallow_wc(sbox):
9919 "merge --reintegrate should fail in shallow wc"
9920 sbox.build()
9921 wc_dir = sbox.wc_dir
9922 expected_disk, expected_status = set_up_branch(sbox)
9923 A_path = os.path.join(wc_dir, "A")
9924 G_path = os.path.join(A_path, "D", "G")
9925 # Our default checkout doesn't have any subdirs at non-infinite
9926 # depth, so we'll have to create one the old-fashioned way: remove a
9927 # tree, then "update" it back into existence at a shallower depth.
9928 svntest.main.safe_rmtree(G_path)
9929 svntest.actions.run_and_verify_svn(None, None, [], 'update', G_path,
9930 '--depth=files')
9931 # Even though everything is actually present (as G has no subdirs
9932 # anyway), the reintegration should fail, because G's depth is other
9933 # than infinity.
9934 svntest.actions.run_and_verify_merge(
9935 A_path, None, None, sbox.repo_url + '/A_COPY', None, None, None, None,
9936 ".*Cannot reintegrate into a working copy not.*at infinite depth.*",
9937 None, None, None, None, True, False, '--reintegrate')
9939 def reintegrate_fail_on_stale_source(sbox):
9940 "merge --reintegrate should fail on stale source"
9941 sbox.build()
9942 wc_dir = sbox.wc_dir
9943 expected_disk, expected_status = set_up_branch(sbox)
9944 A_path = os.path.join(wc_dir, "A")
9945 mu_path = os.path.join(A_path, "mu")
9946 svntest.main.file_append(mu_path, 'some text appended to mu\n')
9947 svntest.actions.run_and_verify_svn(None, None, [], 'commit',
9948 '-m', 'a change to mu', mu_path);
9949 # Unmix the revisions in the working copy.
9950 svntest.actions.run_and_verify_svn(None, None, [], 'update', wc_dir);
9951 # The merge --reintegrate should fail because target has changes not
9952 # present in source.
9953 svntest.actions.run_and_verify_merge(
9954 A_path, None, None, sbox.repo_url + '/A_COPY', None, None, None, None,
9955 ".*", ###TODO(reint): need a more specific check here
9956 None, None, None, None, True, False, '--reintegrate')
9958 def dont_add_mergeinfo_from_own_history(sbox):
9959 "cyclic merges don't add mergeinfo from own history"
9961 sbox.build()
9962 wc_dir = sbox.wc_dir
9963 wc_disk, wc_status = set_up_branch(sbox)
9965 # Some paths we'll care about
9966 A_path = os.path.join(wc_dir, "A")
9967 A_MOVED_path = os.path.join(wc_dir, "A_MOVED")
9968 mu_path = os.path.join(wc_dir, "A", "mu")
9969 mu_MOVED_path = os.path.join(wc_dir, "A_MOVED", "mu")
9970 A_COPY_path = os.path.join(wc_dir, "A_COPY")
9971 mu_COPY_path = os.path.join(wc_dir, "A_COPY", "mu")
9973 # Merge r3 from 'A' to 'A_COPY', make a text mod to 'A_COPY/mu' and
9974 # commit both as r7. This results in mergeinfo of '/A:3' on 'A_COPY'.
9975 # Then merge r7 from 'A_COPY' to 'A'. This attempts to add the mergeinfo
9976 # '/A:3' to 'A', but that is self-referrential and should be filtered out,
9977 # leaving only the mergeinfo '/A_COPY:7' on 'A'.
9979 # Search for the comment entitled "The Merge Kluge" elsewhere in
9980 # this file, to understand why we shorten and chdir() below.
9981 short_A_COPY_path = shorten_path_kludge(A_COPY_path)
9982 expected_output = wc.State(short_A_COPY_path, {
9983 'D/H/psi' : Item(status='U '),
9985 expected_A_COPY_status = wc.State(short_A_COPY_path, {
9986 '' : Item(status=' M', wc_rev=2),
9987 'B' : Item(status=' ', wc_rev=2),
9988 'mu' : Item(status=' ', wc_rev=2),
9989 'B/E' : Item(status=' ', wc_rev=2),
9990 'B/E/alpha' : Item(status=' ', wc_rev=2),
9991 'B/E/beta' : Item(status=' ', wc_rev=2),
9992 'B/lambda' : Item(status=' ', wc_rev=2),
9993 'B/F' : Item(status=' ', wc_rev=2),
9994 'C' : Item(status=' ', wc_rev=2),
9995 'D' : Item(status=' ', wc_rev=2),
9996 'D/G' : Item(status=' ', wc_rev=2),
9997 'D/G/pi' : Item(status=' ', wc_rev=2),
9998 'D/G/rho' : Item(status=' ', wc_rev=2),
9999 'D/G/tau' : Item(status=' ', wc_rev=2),
10000 'D/gamma' : Item(status=' ', wc_rev=2),
10001 'D/H' : Item(status=' ', wc_rev=2),
10002 'D/H/chi' : Item(status=' ', wc_rev=2),
10003 'D/H/psi' : Item(status='M ', wc_rev=2),
10004 'D/H/omega' : Item(status=' ', wc_rev=2),
10006 expected_A_COPY_disk = wc.State('', {
10007 '' : Item(props={SVN_PROP_MERGEINFO : '/A:3'}),
10008 'B' : Item(),
10009 'mu' : Item("This is the file 'mu'.\n"),
10010 'B/E' : Item(),
10011 'B/E/alpha' : Item("This is the file 'alpha'.\n"),
10012 'B/E/beta' : Item("This is the file 'beta'.\n"),
10013 'B/lambda' : Item("This is the file 'lambda'.\n"),
10014 'B/F' : Item(),
10015 'C' : Item(),
10016 'D' : Item(),
10017 'D/G' : Item(),
10018 'D/G/pi' : Item("This is the file 'pi'.\n"),
10019 'D/G/rho' : Item("This is the file 'rho'.\n"),
10020 'D/G/tau' : Item("This is the file 'tau'.\n"),
10021 'D/gamma' : Item("This is the file 'gamma'.\n"),
10022 'D/H' : Item(),
10023 'D/H/chi' : Item("This is the file 'chi'.\n"),
10024 'D/H/psi' : Item("New content"),
10025 'D/H/omega' : Item("This is the file 'omega'.\n"),
10027 expected_A_COPY_skip = wc.State(short_A_COPY_path, { })
10028 saved_cwd = os.getcwd()
10029 os.chdir(svntest.main.work_dir)
10030 svntest.actions.run_and_verify_merge(short_A_COPY_path, '2', '3',
10031 sbox.repo_url + \
10032 '/A',
10033 expected_output,
10034 expected_A_COPY_disk,
10035 expected_A_COPY_status,
10036 expected_A_COPY_skip,
10037 None, None, None, None,
10038 None, 1)
10039 os.chdir(saved_cwd)
10041 # Change 'A_COPY/mu'
10042 svntest.main.file_write(mu_COPY_path, "New content")
10044 # Commit r7
10045 expected_output = wc.State(wc_dir, {
10046 'A_COPY' : Item(verb='Sending'),
10047 'A_COPY/D/H/psi' : Item(verb='Sending'),
10048 'A_COPY/mu' : Item(verb='Sending'),
10050 wc_status.tweak('A_COPY', 'A_COPY/D/H/psi', 'A_COPY/mu', wc_rev=7)
10051 svntest.actions.run_and_verify_commit(wc_dir,
10052 expected_output,
10053 wc_status,
10054 None,
10055 wc_dir)
10057 # Merge r7 back to the 'A'
10058 short_A_path = shorten_path_kludge(A_path)
10059 expected_output = wc.State(short_A_path, {
10060 'mu' : Item(status='U '),
10062 expected_A_status = wc.State(short_A_path, {
10063 '' : Item(status=' M', wc_rev=1),
10064 'B' : Item(status=' ', wc_rev=1),
10065 'mu' : Item(status='M ', wc_rev=1),
10066 'B/E' : Item(status=' ', wc_rev=1),
10067 'B/E/alpha' : Item(status=' ', wc_rev=1),
10068 'B/E/beta' : Item(status=' ', wc_rev=5),
10069 'B/lambda' : Item(status=' ', wc_rev=1),
10070 'B/F' : Item(status=' ', wc_rev=1),
10071 'C' : Item(status=' ', wc_rev=1),
10072 'D' : Item(status=' ', wc_rev=1),
10073 'D/G' : Item(status=' ', wc_rev=1),
10074 'D/G/pi' : Item(status=' ', wc_rev=1),
10075 'D/G/rho' : Item(status=' ', wc_rev=4),
10076 'D/G/tau' : Item(status=' ', wc_rev=1),
10077 'D/gamma' : Item(status=' ', wc_rev=1),
10078 'D/H' : Item(status=' ', wc_rev=1),
10079 'D/H/chi' : Item(status=' ', wc_rev=1),
10080 'D/H/psi' : Item(status=' ', wc_rev=3),
10081 'D/H/omega' : Item(status=' ', wc_rev=6),
10083 expected_A_disk = wc.State('', {
10084 '' : Item(props={SVN_PROP_MERGEINFO : '/A_COPY:7'}),
10085 'B' : Item(),
10086 'mu' : Item("New content"),
10087 'B/E' : Item(),
10088 'B/E/alpha' : Item("This is the file 'alpha'.\n"),
10089 'B/E/beta' : Item("New content"),
10090 'B/lambda' : Item("This is the file 'lambda'.\n"),
10091 'B/F' : Item(),
10092 'C' : Item(),
10093 'D' : Item(),
10094 'D/G' : Item(),
10095 'D/G/pi' : Item("This is the file 'pi'.\n"),
10096 'D/G/rho' : Item("New content"),
10097 'D/G/tau' : Item("This is the file 'tau'.\n"),
10098 'D/gamma' : Item("This is the file 'gamma'.\n"),
10099 'D/H' : Item(),
10100 'D/H/chi' : Item("This is the file 'chi'.\n"),
10101 'D/H/psi' : Item("New content"),
10102 'D/H/omega' : Item("New content"),
10104 expected_A_skip = wc.State(short_A_path, {})
10105 os.chdir(svntest.main.work_dir)
10106 svntest.actions.run_and_verify_merge(short_A_path, '6', '7',
10107 sbox.repo_url + \
10108 '/A_COPY',
10109 expected_output,
10110 expected_A_disk,
10111 expected_A_status,
10112 expected_A_skip,
10113 None, None, None, None,
10114 None, 1)
10115 os.chdir(saved_cwd)
10117 # Revert all local mods
10118 svntest.actions.run_and_verify_svn(None,
10119 ["Reverted '" + A_path + "'\n",
10120 "Reverted '" + mu_path + "'\n"],
10121 [], 'revert', '-R', wc_dir)
10123 # Move 'A' to 'A_MOVED' and once again merge r7 from 'A_COPY', this time
10124 # to 'A_MOVED'. This attempts to add the mergeinfo '/A:3' to
10125 # 'A_MOVED', but 'A_MOVED@3' is 'A', so again this mergeinfo is filtered
10126 # out, leaving the only the mergeinfo created from the merge itself:
10127 # '/A_COPY:7'.
10128 svntest.actions.run_and_verify_svn(None,
10129 ['\n', 'Committed revision 8.\n'],
10130 [], 'move',
10131 sbox.repo_url + '/A',
10132 sbox.repo_url + '/A_MOVED',
10133 '-m', 'Copy A to A_MOVED')
10134 wc_status.remove('A', 'A/B', 'A/B/lambda', 'A/B/E', 'A/B/E/alpha',
10135 'A/B/E/beta', 'A/B/F', 'A/mu', 'A/C', 'A/D', 'A/D/gamma', 'A/D/G',
10136 'A/D/G/pi', 'A/D/G/rho', 'A/D/G/tau', 'A/D/H', 'A/D/H/chi',
10137 'A/D/H/omega', 'A/D/H/psi')
10138 wc_status.add({
10139 'A_MOVED' : Item(),
10140 'A_MOVED/B' : Item(),
10141 'A_MOVED/B/lambda' : Item(),
10142 'A_MOVED/B/E' : Item(),
10143 'A_MOVED/B/E/alpha' : Item(),
10144 'A_MOVED/B/E/beta' : Item(),
10145 'A_MOVED/B/F' : Item(),
10146 'A_MOVED/mu' : Item(),
10147 'A_MOVED/C' : Item(),
10148 'A_MOVED/D' : Item(),
10149 'A_MOVED/D/gamma' : Item(),
10150 'A_MOVED/D/G' : Item(),
10151 'A_MOVED/D/G/pi' : Item(),
10152 'A_MOVED/D/G/rho' : Item(),
10153 'A_MOVED/D/G/tau' : Item(),
10154 'A_MOVED/D/H' : Item(),
10155 'A_MOVED/D/H/chi' : Item(),
10156 'A_MOVED/D/H/omega' : Item(),
10157 'A_MOVED/D/H/psi' : Item(),
10159 wc_status.tweak(wc_rev=8, status=' ')
10160 wc_disk.remove('A', 'A/B', 'A/B/lambda', 'A/B/E', 'A/B/E/alpha',
10161 'A/B/E/beta', 'A/B/F', 'A/mu', 'A/C', 'A/D', 'A/D/gamma',
10162 'A/D/G', 'A/D/G/pi', 'A/D/G/rho', 'A/D/G/tau', 'A/D/H',
10163 'A/D/H/chi', 'A/D/H/omega', 'A/D/H/psi' )
10164 wc_disk.add({
10165 'A_MOVED' : Item(),
10166 'A_MOVED/B' : Item(),
10167 'A_MOVED/B/lambda' : Item("This is the file 'lambda'.\n"),
10168 'A_MOVED/B/E' : Item(),
10169 'A_MOVED/B/E/alpha' : Item("This is the file 'alpha'.\n"),
10170 'A_MOVED/B/E/beta' : Item("New content"),
10171 'A_MOVED/B/F' : Item(),
10172 'A_MOVED/mu' : Item("This is the file 'mu'.\n"),
10173 'A_MOVED/C' : Item(),
10174 'A_MOVED/D' : Item(),
10175 'A_MOVED/D/gamma' : Item("This is the file 'gamma'.\n"),
10176 'A_MOVED/D/G' : Item(),
10177 'A_MOVED/D/G/pi' : Item("This is the file 'pi'.\n"),
10178 'A_MOVED/D/G/rho' : Item("New content"),
10179 'A_MOVED/D/G/tau' : Item("This is the file 'tau'.\n"),
10180 'A_MOVED/D/H' : Item(),
10181 'A_MOVED/D/H/chi' : Item("This is the file 'chi'.\n"),
10182 'A_MOVED/D/H/omega' : Item("New content"),
10183 'A_MOVED/D/H/psi' : Item("New content"),
10185 wc_disk.tweak('A_COPY/D/H/psi', 'A_COPY/mu', contents='New content')
10186 wc_disk.tweak('A_COPY', props={SVN_PROP_MERGEINFO : '/A:3'})
10187 expected_output = wc.State(wc_dir, {
10188 'A' : Item(status='D '),
10189 'A_MOVED' : Item(status='A '),
10190 'A_MOVED/B' : Item(status='A '),
10191 'A_MOVED/B/lambda' : Item(status='A '),
10192 'A_MOVED/B/E' : Item(status='A '),
10193 'A_MOVED/B/E/alpha' : Item(status='A '),
10194 'A_MOVED/B/E/beta' : Item(status='A '),
10195 'A_MOVED/B/F' : Item(status='A '),
10196 'A_MOVED/mu' : Item(status='A '),
10197 'A_MOVED/C' : Item(status='A '),
10198 'A_MOVED/D' : Item(status='A '),
10199 'A_MOVED/D/gamma' : Item(status='A '),
10200 'A_MOVED/D/G' : Item(status='A '),
10201 'A_MOVED/D/G/pi' : Item(status='A '),
10202 'A_MOVED/D/G/rho' : Item(status='A '),
10203 'A_MOVED/D/G/tau' : Item(status='A '),
10204 'A_MOVED/D/H' : Item(status='A '),
10205 'A_MOVED/D/H/chi' : Item(status='A '),
10206 'A_MOVED/D/H/omega' : Item(status='A '),
10207 'A_MOVED/D/H/psi' : Item(status='A ')
10209 svntest.actions.run_and_verify_update(wc_dir,
10210 expected_output,
10211 wc_disk,
10212 wc_status,
10213 None, None, None, None, None,
10214 True)
10216 short_A_MOVED_path = shorten_path_kludge(A_MOVED_path)
10217 expected_output = wc.State(short_A_MOVED_path, {
10218 'mu' : Item(status='U '),
10220 expected_A_status = wc.State(short_A_MOVED_path, {
10221 '' : Item(status=' M', wc_rev=8),
10222 'B' : Item(status=' ', wc_rev=8),
10223 'mu' : Item(status='M ', wc_rev=8),
10224 'B/E' : Item(status=' ', wc_rev=8),
10225 'B/E/alpha' : Item(status=' ', wc_rev=8),
10226 'B/E/beta' : Item(status=' ', wc_rev=8),
10227 'B/lambda' : Item(status=' ', wc_rev=8),
10228 'B/F' : Item(status=' ', wc_rev=8),
10229 'C' : Item(status=' ', wc_rev=8),
10230 'D' : Item(status=' ', wc_rev=8),
10231 'D/G' : Item(status=' ', wc_rev=8),
10232 'D/G/pi' : Item(status=' ', wc_rev=8),
10233 'D/G/rho' : Item(status=' ', wc_rev=8),
10234 'D/G/tau' : Item(status=' ', wc_rev=8),
10235 'D/gamma' : Item(status=' ', wc_rev=8),
10236 'D/H' : Item(status=' ', wc_rev=8),
10237 'D/H/chi' : Item(status=' ', wc_rev=8),
10238 'D/H/psi' : Item(status=' ', wc_rev=8),
10239 'D/H/omega' : Item(status=' ', wc_rev=8),
10241 # We can reuse expected_A_disk from above without change.
10242 os.chdir(svntest.main.work_dir)
10243 svntest.actions.run_and_verify_merge(short_A_MOVED_path, '6', '7',
10244 sbox.repo_url + \
10245 '/A_COPY',
10246 expected_output,
10247 expected_A_disk,
10248 expected_A_status,
10249 expected_A_skip,
10250 None, None, None, None,
10251 None, 1)
10252 os.chdir(saved_cwd)
10254 # Revert all local mods
10255 svntest.actions.run_and_verify_svn(None,
10256 ["Reverted '" + A_MOVED_path + "'\n",
10257 "Reverted '" + mu_MOVED_path + "'\n"],
10258 [], 'revert', '-R', wc_dir)
10260 # Create a new 'A' unrelated to the old 'A' which was moved. Then merge
10261 # r7 from 'A_COPY' to this new 'A'. Since the new 'A' shares no history
10262 # with the mergeinfo 'A@3', the mergeinfo '/A:3' is added and when combined
10263 # with the mergeinfo created from the merge should result in
10264 # '/A:3\n/A_COPY:7'
10266 # Create the new 'A' by exporting the old 'A@1'.
10267 expected_output = svntest.verify.UnorderedOutput(
10268 ["A " + os.path.join(wc_dir, "A") + "\n",
10269 "A " + os.path.join(wc_dir, "A", "B") + "\n",
10270 "A " + os.path.join(wc_dir, "A", "B", "lambda") + "\n",
10271 "A " + os.path.join(wc_dir, "A", "B", "E") + "\n",
10272 "A " + os.path.join(wc_dir, "A", "B", "E", "alpha") + "\n",
10273 "A " + os.path.join(wc_dir, "A", "B", "E", "beta") + "\n",
10274 "A " + os.path.join(wc_dir, "A", "B", "F") + "\n",
10275 "A " + os.path.join(wc_dir, "A", "mu") + "\n",
10276 "A " + os.path.join(wc_dir, "A", "C") + "\n",
10277 "A " + os.path.join(wc_dir, "A", "D") + "\n",
10278 "A " + os.path.join(wc_dir, "A", "D", "gamma") + "\n",
10279 "A " + os.path.join(wc_dir, "A", "D", "G") + "\n",
10280 "A " + os.path.join(wc_dir, "A", "D", "G", "pi") + "\n",
10281 "A " + os.path.join(wc_dir, "A", "D", "G", "rho") + "\n",
10282 "A " + os.path.join(wc_dir, "A", "D", "G", "tau") + "\n",
10283 "A " + os.path.join(wc_dir, "A", "D", "H") + "\n",
10284 "A " + os.path.join(wc_dir, "A", "D", "H", "chi") + "\n",
10285 "A " + os.path.join(wc_dir, "A", "D", "H", "omega") + "\n",
10286 "A " + os.path.join(wc_dir, "A", "D", "H", "psi") + "\n",
10287 "Exported revision 1.\n",]
10289 svntest.actions.run_and_verify_svn(None, expected_output, [],
10290 'export', sbox.repo_url + '/A@1',
10291 A_path)
10292 expected_output = svntest.verify.UnorderedOutput(
10293 ["A " + os.path.join(wc_dir, "A") + "\n",
10294 "A " + os.path.join(wc_dir, "A", "B") + "\n",
10295 "A " + os.path.join(wc_dir, "A", "B", "lambda") + "\n",
10296 "A " + os.path.join(wc_dir, "A", "B", "E") + "\n",
10297 "A " + os.path.join(wc_dir, "A", "B", "E", "alpha") + "\n",
10298 "A " + os.path.join(wc_dir, "A", "B", "E", "beta") + "\n",
10299 "A " + os.path.join(wc_dir, "A", "B", "F") + "\n",
10300 "A " + os.path.join(wc_dir, "A", "mu") + "\n",
10301 "A " + os.path.join(wc_dir, "A", "C") + "\n",
10302 "A " + os.path.join(wc_dir, "A", "D") + "\n",
10303 "A " + os.path.join(wc_dir, "A", "D", "gamma") + "\n",
10304 "A " + os.path.join(wc_dir, "A", "D", "G") + "\n",
10305 "A " + os.path.join(wc_dir, "A", "D", "G", "pi") + "\n",
10306 "A " + os.path.join(wc_dir, "A", "D", "G", "rho") + "\n",
10307 "A " + os.path.join(wc_dir, "A", "D", "G", "tau") + "\n",
10308 "A " + os.path.join(wc_dir, "A", "D", "H") + "\n",
10309 "A " + os.path.join(wc_dir, "A", "D", "H", "chi") + "\n",
10310 "A " + os.path.join(wc_dir, "A", "D", "H", "omega") + "\n",
10311 "A " + os.path.join(wc_dir, "A", "D", "H", "psi") + "\n",]
10313 svntest.actions.run_and_verify_svn(None, expected_output, [],
10314 'add', A_path)
10315 # Commit the new 'A' as r9
10316 expected_output = wc.State(wc_dir, {
10317 'A' : Item(verb='Adding'),
10318 'A/B' : Item(verb='Adding'),
10319 'A/mu' : Item(verb='Adding'),
10320 'A/B/E' : Item(verb='Adding'),
10321 'A/B/E/alpha' : Item(verb='Adding'),
10322 'A/B/E/beta' : Item(verb='Adding'),
10323 'A/B/lambda' : Item(verb='Adding'),
10324 'A/B/F' : Item(verb='Adding'),
10325 'A/C' : Item(verb='Adding'),
10326 'A/D' : Item(verb='Adding'),
10327 'A/D/G' : Item(verb='Adding'),
10328 'A/D/G/pi' : Item(verb='Adding'),
10329 'A/D/G/rho' : Item(verb='Adding'),
10330 'A/D/G/tau' : Item(verb='Adding'),
10331 'A/D/gamma' : Item(verb='Adding'),
10332 'A/D/H' : Item(verb='Adding'),
10333 'A/D/H/chi' : Item(verb='Adding'),
10334 'A/D/H/psi' : Item(verb='Adding'),
10335 'A/D/H/omega' : Item(verb='Adding'),
10337 wc_status.tweak(wc_rev=8)
10338 wc_status.add({
10339 'A' : Item(wc_rev=9),
10340 'A/B' : Item(wc_rev=9),
10341 'A/B/lambda' : Item(wc_rev=9),
10342 'A/B/E' : Item(wc_rev=9),
10343 'A/B/E/alpha' : Item(wc_rev=9),
10344 'A/B/E/beta' : Item(wc_rev=9),
10345 'A/B/F' : Item(wc_rev=9),
10346 'A/mu' : Item(wc_rev=9),
10347 'A/C' : Item(wc_rev=9),
10348 'A/D' : Item(wc_rev=9),
10349 'A/D/gamma' : Item(wc_rev=9),
10350 'A/D/G' : Item(wc_rev=9),
10351 'A/D/G/pi' : Item(wc_rev=9),
10352 'A/D/G/rho' : Item(wc_rev=9),
10353 'A/D/G/tau' : Item(wc_rev=9),
10354 'A/D/H' : Item(wc_rev=9),
10355 'A/D/H/chi' : Item(wc_rev=9),
10356 'A/D/H/omega' : Item(wc_rev=9),
10357 'A/D/H/psi' : Item(wc_rev=9),
10359 wc_status.tweak(status=' ')
10360 svntest.actions.run_and_verify_commit(wc_dir,
10361 expected_output,
10362 wc_status,
10363 None,
10364 wc_dir)
10366 expected_output = wc.State(short_A_path, {
10367 'mu' : Item(status='U '),
10368 'D/H/psi' : Item(status='U '),
10369 '' : Item(status=' U'),
10371 expected_A_status = wc.State(short_A_path, {
10372 '' : Item(status=' M', wc_rev=9),
10373 'B' : Item(status=' ', wc_rev=9),
10374 'mu' : Item(status='M ', wc_rev=9),
10375 'B/E' : Item(status=' ', wc_rev=9),
10376 'B/E/alpha' : Item(status=' ', wc_rev=9),
10377 'B/E/beta' : Item(status=' ', wc_rev=9),
10378 'B/lambda' : Item(status=' ', wc_rev=9),
10379 'B/F' : Item(status=' ', wc_rev=9),
10380 'C' : Item(status=' ', wc_rev=9),
10381 'D' : Item(status=' ', wc_rev=9),
10382 'D/G' : Item(status=' ', wc_rev=9),
10383 'D/G/pi' : Item(status=' ', wc_rev=9),
10384 'D/G/rho' : Item(status=' ', wc_rev=9),
10385 'D/G/tau' : Item(status=' ', wc_rev=9),
10386 'D/gamma' : Item(status=' ', wc_rev=9),
10387 'D/H' : Item(status=' ', wc_rev=9),
10388 'D/H/chi' : Item(status=' ', wc_rev=9),
10389 'D/H/psi' : Item(status='M ', wc_rev=9),
10390 'D/H/omega' : Item(status=' ', wc_rev=9),
10392 expected_A_disk = wc.State('', {
10393 '' : Item(props={SVN_PROP_MERGEINFO : '/A:3\n/A_COPY:7\n'}),
10394 'B' : Item(),
10395 'mu' : Item("New content"),
10396 'B/E' : Item(),
10397 'B/E/alpha' : Item("This is the file 'alpha'.\n"),
10398 'B/E/beta' : Item("This is the file 'beta'.\n"),
10399 'B/lambda' : Item("This is the file 'lambda'.\n"),
10400 'B/F' : Item(),
10401 'C' : Item(),
10402 'D' : Item(),
10403 'D/G' : Item(),
10404 'D/G/pi' : Item("This is the file 'pi'.\n"),
10405 'D/G/rho' : Item("This is the file 'rho'.\n"),
10406 'D/G/tau' : Item("This is the file 'tau'.\n"),
10407 'D/gamma' : Item("This is the file 'gamma'.\n"),
10408 'D/H' : Item(),
10409 'D/H/chi' : Item("This is the file 'chi'.\n"),
10410 'D/H/psi' : Item("New content"),
10411 'D/H/omega' : Item("This is the file 'omega'.\n"),
10413 expected_A_skip = wc.State(short_A_path, {})
10414 os.chdir(svntest.main.work_dir)
10415 svntest.actions.run_and_verify_merge(short_A_path, '6', '7',
10416 sbox.repo_url + \
10417 '/A_COPY',
10418 expected_output,
10419 expected_A_disk,
10420 expected_A_status,
10421 expected_A_skip,
10422 None, None, None, None,
10423 None, 1)
10425 def merge_range_predates_history(sbox):
10426 "merge range predates history (issue #3094)"
10428 sbox.build()
10429 wc_dir = sbox.wc_dir
10431 iota_path = os.path.join(wc_dir, "iota")
10432 trunk_file_path = os.path.join(wc_dir, "trunk", "file")
10433 trunk_url = sbox.repo_url + "/trunk"
10434 branches_url = sbox.repo_url + "/branches"
10435 branch_path = os.path.join(wc_dir, "branches", "branch")
10436 branch_file_path = os.path.join(wc_dir, "branches", "branch", "file")
10437 branch_url = sbox.repo_url + "/branches/branch"
10439 # Tweak a file and commit. (r2)
10440 svntest.main.file_append(iota_path, "More data.\n")
10441 svntest.main.run_svn(None, 'ci', '-m', 'tweak iota', wc_dir)
10443 # Create our trunk and branches directory, and update working copy. (r3)
10444 svntest.main.run_svn(None, 'mkdir', trunk_url, branches_url,
10445 '-m', 'add trunk and branches dirs')
10446 svntest.main.run_svn(None, 'up', wc_dir)
10448 # Add a file to the trunk and commit. (r4)
10449 svntest.main.file_append(trunk_file_path, "This is the file 'file'.\n")
10450 svntest.main.run_svn(None, 'add', trunk_file_path)
10451 svntest.main.run_svn(None, 'ci', '-m', 'add trunk file', wc_dir)
10453 # Branch trunk from r3, and update working copy. (r5)
10454 svntest.main.run_svn(None, 'cp', trunk_url, branch_url, '-r3',
10455 '-m', 'branch trunk@2')
10456 svntest.main.run_svn(None, 'up', wc_dir)
10458 # Now, try to merge trunk into the branch. There should be one
10459 # outstanding change -- the addition of the file.
10460 expected_output = expected_merge_output([[4,5]],
10461 'A ' + branch_file_path + '\n')
10462 svntest.actions.run_and_verify_svn(None, expected_output, [], 'merge',
10463 trunk_url, branch_path)
10466 def foreign_repos(sbox):
10467 "merge from a foreign repository"
10469 sbox.build()
10470 wc_dir = sbox.wc_dir
10472 # Make a copy of this repository and associated working copy. Both
10473 # should have nothing but a Greek tree in them, and the two
10474 # repository UUIDs should differ.
10475 sbox2 = sbox.clone_dependent(True)
10476 sbox2.build()
10477 wc_dir2 = sbox2.wc_dir
10479 # Convenience variables for working copy paths.
10480 Z_path = os.path.join(wc_dir, 'A', 'D', 'G', 'Z')
10481 Q_path = os.path.join(wc_dir, 'Q')
10482 H_path = os.path.join(wc_dir, 'A', 'D', 'H')
10483 iota_path = os.path.join(wc_dir, 'iota')
10484 beta_path = os.path.join(wc_dir, 'A', 'B', 'E', 'beta')
10485 alpha_path = os.path.join(wc_dir, 'A', 'B', 'E', 'alpha')
10486 zeta_path = os.path.join(wc_dir, 'A', 'D', 'G', 'Z', 'zeta')
10487 fred_path = os.path.join(wc_dir, 'A', 'C', 'fred')
10489 # Add new directories
10490 svntest.main.run_svn(None, 'mkdir', Q_path, Z_path)
10492 # Add new files
10493 zeta_contents = "This is the file 'zeta'.\n"
10494 fred_contents = "This is the file 'fred'.\n"
10495 svntest.main.file_append(zeta_path, zeta_contents)
10496 svntest.main.file_append(fred_path, fred_contents)
10497 svntest.main.run_svn(None, 'add', zeta_path, fred_path)
10499 # Modify existing files
10500 added_contents = "This is another line of text.\n"
10501 svntest.main.file_append(iota_path, added_contents)
10502 svntest.main.file_append(beta_path, added_contents)
10504 # Delete some stuff
10505 svntest.main.run_svn(None, 'delete', alpha_path, H_path)
10507 # Commit up these changes.
10508 expected_output = wc.State(wc_dir, {
10509 'Q' : Item(verb='Adding'),
10510 'A/D/G/Z' : Item(verb='Adding'),
10511 'A/D/G/Z/zeta' : Item(verb='Adding'),
10512 'A/C/fred' : Item(verb='Adding'),
10513 'iota' : Item(verb='Sending'),
10514 'A/B/E/beta' : Item(verb='Sending'),
10515 'A/B/E/alpha' : Item(verb='Deleting'),
10516 'A/D/H' : Item(verb='Deleting'),
10518 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
10519 expected_status.add({
10520 'Q' : Item(status=' ', wc_rev=2),
10521 'A/D/G/Z' : Item(status=' ', wc_rev=2),
10522 'A/D/G/Z/zeta' : Item(status=' ', wc_rev=2),
10523 'A/C/fred' : Item(status=' ', wc_rev=2),
10525 expected_status.tweak('iota', 'A/B/E/beta', wc_rev=2)
10526 expected_status.remove('A/B/E/alpha', 'A/D/H', 'A/D/H/chi',
10527 'A/D/H/psi', 'A/D/H/omega')
10528 expected_disk = svntest.main.greek_state.copy()
10529 expected_disk.add({
10530 'Q' : Item(),
10531 'A/D/G/Z' : Item(),
10532 'A/D/G/Z/zeta' : Item(contents=zeta_contents),
10533 'A/C/fred' : Item(contents=fred_contents),
10535 expected_disk.remove('A/B/E/alpha', 'A/D/H', 'A/D/H/chi',
10536 'A/D/H/psi', 'A/D/H/omega')
10537 expected_disk.tweak('iota',
10538 contents=expected_disk.desc['iota'].contents
10539 + added_contents)
10540 expected_disk.tweak('A/B/E/beta',
10541 contents=expected_disk.desc['A/B/E/beta'].contents
10542 + added_contents)
10543 svntest.actions.run_and_verify_commit(wc_dir,
10544 expected_output,
10545 expected_status,
10546 None,
10547 wc_dir)
10548 svntest.actions.verify_disk(wc_dir, expected_disk,
10549 None, None, None, None, 1)
10551 # Now, merge our committed revision into a working copy of another
10552 # repository. Not only should the merge succeed, but the results on
10553 # disk should match those in our first working copy.
10555 ### TODO: Use run_and_verify_merge() ###
10556 svntest.main.run_svn(None, 'merge', '-c2', sbox.repo_url, wc_dir2)
10557 svntest.main.run_svn(None, 'ci', '-m', 'Merge from foreign repos', wc_dir2)
10558 svntest.actions.verify_disk(wc_dir2, expected_disk,
10559 None, None, None, None, 1)
10562 def foreign_repos_2_url(sbox):
10563 "2-url merge from a foreign repository"
10565 sbox.build()
10566 wc_dir = sbox.wc_dir
10568 # Make a copy of this repository and associated working copy. Both
10569 # should have nothing but a Greek tree in them, and the two
10570 # repository UUIDs should differ.
10571 sbox2 = sbox.clone_dependent(True)
10572 sbox2.build()
10573 wc_dir2 = sbox2.wc_dir
10575 # Convenience variables for working copy paths.
10576 Z_path = os.path.join(wc_dir, 'A', 'D', 'G', 'Z')
10577 Q_path = os.path.join(wc_dir, 'A', 'Q')
10578 H_path = os.path.join(wc_dir, 'A', 'D', 'H')
10579 beta_path = os.path.join(wc_dir, 'A', 'B', 'E', 'beta')
10580 alpha_path = os.path.join(wc_dir, 'A', 'B', 'E', 'alpha')
10581 zeta_path = os.path.join(wc_dir, 'A', 'D', 'G', 'Z', 'zeta')
10582 fred_path = os.path.join(wc_dir, 'A', 'C', 'fred')
10584 # First, "tag" the current state of the repository.
10585 svntest.main.run_svn(None, 'copy', sbox.repo_url + '/A',
10586 sbox.repo_url + '/A-tag1', '-m', 'tag1')
10588 # Add new directories
10589 svntest.main.run_svn(None, 'mkdir', Q_path, Z_path)
10591 # Add new files
10592 zeta_contents = "This is the file 'zeta'.\n"
10593 fred_contents = "This is the file 'fred'.\n"
10594 svntest.main.file_append(zeta_path, zeta_contents)
10595 svntest.main.file_append(fred_path, fred_contents)
10596 svntest.main.run_svn(None, 'add', zeta_path, fred_path)
10598 # Modify existing files
10599 added_contents = "This is another line of text.\n"
10600 svntest.main.file_append(beta_path, added_contents)
10602 # Delete some stuff
10603 svntest.main.run_svn(None, 'delete', alpha_path, H_path)
10605 # Commit up these changes.
10606 expected_output = wc.State(wc_dir, {
10607 'A/Q' : Item(verb='Adding'),
10608 'A/D/G/Z' : Item(verb='Adding'),
10609 'A/D/G/Z/zeta' : Item(verb='Adding'),
10610 'A/C/fred' : Item(verb='Adding'),
10611 'A/B/E/beta' : Item(verb='Sending'),
10612 'A/B/E/alpha' : Item(verb='Deleting'),
10613 'A/D/H' : Item(verb='Deleting'),
10615 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
10616 expected_status.add({
10617 'A/Q' : Item(status=' ', wc_rev=3),
10618 'A/D/G/Z' : Item(status=' ', wc_rev=3),
10619 'A/D/G/Z/zeta' : Item(status=' ', wc_rev=3),
10620 'A/C/fred' : Item(status=' ', wc_rev=3),
10622 expected_status.tweak('A/B/E/beta', wc_rev=3)
10623 expected_status.remove('A/B/E/alpha', 'A/D/H', 'A/D/H/chi',
10624 'A/D/H/psi', 'A/D/H/omega')
10625 expected_disk = svntest.main.greek_state.copy()
10626 expected_disk.add({
10627 'A/Q' : Item(),
10628 'A/D/G/Z' : Item(),
10629 'A/D/G/Z/zeta' : Item(contents=zeta_contents),
10630 'A/C/fred' : Item(contents=fred_contents),
10632 expected_disk.remove('A/B/E/alpha', 'A/D/H', 'A/D/H/chi',
10633 'A/D/H/psi', 'A/D/H/omega')
10634 expected_disk.tweak('A/B/E/beta',
10635 contents=expected_disk.desc['A/B/E/beta'].contents
10636 + added_contents)
10637 svntest.actions.run_and_verify_commit(wc_dir,
10638 expected_output,
10639 expected_status,
10640 None,
10641 wc_dir)
10642 svntest.actions.verify_disk(wc_dir, expected_disk,
10643 None, None, None, None, 1)
10645 # Now, "tag" the new state of the repository.
10646 svntest.main.run_svn(None, 'copy', sbox.repo_url + '/A',
10647 sbox.repo_url + '/A-tag2', '-m', 'tag2')
10649 # Now, merge across our "tags" (copies of /A) into the /A of a
10650 # working copy of another repository. Not only should the merge
10651 # succeed, but the results on disk should match those in our first
10652 # working copy.
10654 ### TODO: Use run_and_verify_merge() ###
10655 svntest.main.run_svn(None, 'merge', sbox.repo_url + '/A-tag1',
10656 sbox.repo_url + '/A-tag2',
10657 os.path.join(wc_dir2, 'A'))
10658 svntest.main.run_svn(None, 'ci', '-m', 'Merge from foreign repos', wc_dir2)
10659 svntest.actions.verify_disk(wc_dir2, expected_disk,
10660 None, None, None, None, 1)
10663 def merge_added_subtree(sbox):
10664 "merge added subtree"
10666 # The result of a subtree added by copying
10667 # or merging an added subtree, should be the same on disk
10668 ### with the exception of mergeinfo?!
10670 # test for issue 1962
10671 sbox.build()
10672 wc_dir = sbox.wc_dir
10673 url = sbox.repo_url
10675 # make a branch of A
10676 # svn cp A A_COPY
10677 A_url = url + "/A"
10678 A_COPY_url = url + "/A_COPY"
10679 A_path = os.path.join(wc_dir, "A")
10681 svntest.actions.run_and_verify_svn("",["\n", "Committed revision 2.\n"], [],
10682 "cp", "-m", "", A_url, A_COPY_url)
10683 svntest.actions.run_and_verify_svn("",["\n", "Committed revision 3.\n"], [],
10684 "cp", "-m", "",
10685 A_COPY_url + '/D',
10686 A_COPY_url + '/D2')
10687 expected_output = wc.State(A_path, {
10688 'D2' : Item(status='A '),
10689 'D2/gamma' : Item(status='A '),
10690 'D2/H/' : Item(status='A '),
10691 'D2/H/chi' : Item(status='A '),
10692 'D2/H/psi' : Item(status='A '),
10693 'D2/H/omega': Item(status='A '),
10694 'D2/G/' : Item(status='A '),
10695 'D2/G/pi' : Item(status='A '),
10696 'D2/G/rho' : Item(status='A '),
10697 'D2/G/tau' : Item(status='A ')
10700 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
10701 expected_status.add({
10702 'A/D2' : Item(status='A ', copied='+', wc_rev='-'),
10703 'A/D2/gamma' : Item(status=' ', copied='+', wc_rev='-'),
10704 'A/D2/H/' : Item(status=' ', copied='+', wc_rev='-'),
10705 'A/D2/H/chi' : Item(status=' ', copied='+', wc_rev='-'),
10706 'A/D2/H/psi' : Item(status=' ', copied='+', wc_rev='-'),
10707 'A/D2/H/omega': Item(status=' ', copied='+', wc_rev='-'),
10708 'A/D2/G/' : Item(status=' ', copied='+', wc_rev='-'),
10709 'A/D2/G/pi' : Item(status=' ', copied='+', wc_rev='-'),
10710 'A/D2/G/rho' : Item(status=' ', copied='+', wc_rev='-'),
10711 'A/D2/G/tau' : Item(status=' ', copied='+', wc_rev='-')
10713 expected_status.remove('', 'iota')
10715 expected_skip = wc.State('', {})
10716 expected_disk = svntest.main.greek_state.subtree("A")
10717 dest_name = ''
10718 expected_disk.add({
10719 dest_name + 'D2' : Item(),
10720 dest_name + 'D2/gamma' : Item("This is the file 'gamma'.\n"),
10721 dest_name + 'D2/G' : Item(),
10722 dest_name + 'D2/G/pi' : Item("This is the file 'pi'.\n"),
10723 dest_name + 'D2/G/rho' : Item("This is the file 'rho'.\n"),
10724 dest_name + 'D2/G/tau' : Item("This is the file 'tau'.\n"),
10725 dest_name + 'D2/H' : Item(),
10726 dest_name + 'D2/H/chi' : Item("This is the file 'chi'.\n"),
10727 dest_name + 'D2/H/omega' : Item("This is the file 'omega'.\n"),
10728 dest_name + 'D2/H/psi' : Item("This is the file 'psi'.\n")
10731 # Using the above information, verify a REPO->WC copy
10732 svntest.actions.run_and_verify_svn("", None, [],
10733 "cp", A_COPY_url + '/D2',
10734 os.path.join(A_path, "D2"))
10735 actual_tree = svntest.tree.build_tree_from_wc (A_path, 0)
10736 svntest.tree.compare_trees (actual_tree, expected_disk.old_tree(),
10737 None, None, None, None)
10738 svntest.actions.run_and_verify_status(A_path, expected_status)
10740 # Remove the copy artifacts
10741 svntest.actions.run_and_verify_svn("", None, [],
10742 "revert", "-R", A_path)
10743 svntest.main.safe_rmtree(os.path.join(A_path, "D2"))
10745 # Add merge-tracking differences between copying and merging
10746 # Verify a merge using the otherwise unchanged disk and status trees
10747 expected_status.tweak('A',status=' M')
10748 svntest.actions.run_and_verify_merge(A_path, 2, 3, A_COPY_url,
10749 expected_output, expected_disk,
10750 expected_status, expected_skip)
10752 #----------------------------------------------------------------------
10753 # Issue #3138
10754 def merge_unknown_url(sbox):
10755 "merging an unknown url should return error"
10757 sbox.build()
10758 wc_dir = sbox.wc_dir
10760 # remove a path from the repo and commit.
10761 iota_path = os.path.join(wc_dir, 'iota')
10762 svntest.actions.run_and_verify_svn(None, None, [], 'rm', iota_path)
10763 svntest.actions.run_and_verify_svn("", None, [],
10764 "ci", wc_dir, "-m", "log message")
10767 url = sbox.repo_url + "/iota"
10768 expected_err = ".*File not found.*iota.*|.*iota.*path not found.*"
10769 svntest.actions.run_and_verify_svn("", None, expected_err,
10770 "merge", url, wc_dir)
10772 ########################################################################
10773 # Run the tests
10776 # list all tests here, starting with None:
10777 test_list = [ None,
10778 textual_merges_galore,
10779 add_with_history,
10780 delete_file_and_dir,
10781 simple_property_merges,
10782 merge_with_implicit_target_using_r,
10783 merge_with_implicit_target_using_c,
10784 merge_with_implicit_target_and_revs,
10785 merge_catches_nonexistent_target,
10786 merge_tree_deleted_in_target,
10787 merge_similar_unrelated_trees,
10788 merge_with_prev,
10789 merge_binary_file,
10790 three_way_merge_add_of_existing_binary_file,
10791 merge_one_file_using_r,
10792 merge_one_file_using_c,
10793 merge_one_file_using_implicit_revs,
10794 merge_record_only,
10795 merge_in_new_file_and_diff,
10796 merge_skips_obstructions,
10797 merge_into_missing,
10798 dry_run_adds_file_with_prop,
10799 merge_binary_with_common_ancestry,
10800 merge_funny_chars_on_path,
10801 merge_keyword_expansions,
10802 merge_prop_change_to_deleted_target,
10803 merge_file_with_space_in_its_name,
10804 merge_dir_branches,
10805 safe_property_merge,
10806 property_merge_from_branch,
10807 property_merge_undo_redo,
10808 cherry_pick_text_conflict,
10809 merge_file_replace,
10810 merge_dir_replace,
10811 XFail(merge_dir_and_file_replace),
10812 merge_file_replace_to_mixed_rev_wc,
10813 merge_added_dir_to_deleted_in_target,
10814 merge_ignore_whitespace,
10815 merge_ignore_eolstyle,
10816 merge_add_over_versioned_file_conflicts,
10817 merge_conflict_markers_matching_eol,
10818 merge_eolstyle_handling,
10819 avoid_repeated_merge_using_inherited_merge_info,
10820 avoid_repeated_merge_on_subtree_with_merge_info,
10821 obey_reporter_api_semantics_while_doing_subtree_merges,
10822 SkipUnless(mergeinfo_inheritance,
10823 server_has_mergeinfo),
10824 mergeinfo_elision,
10825 mergeinfo_inheritance_and_discontinuous_ranges,
10826 SkipUnless(merge_to_target_with_copied_children,
10827 server_has_mergeinfo),
10828 merge_to_switched_path,
10829 SkipUnless(merge_to_path_with_switched_children,
10830 server_has_mergeinfo),
10831 merge_with_implicit_target_file,
10832 SkipUnless(empty_mergeinfo,
10833 server_has_mergeinfo),
10834 prop_add_to_child_with_mergeinfo,
10835 diff_repos_does_not_update_mergeinfo,
10836 XFail(avoid_reflected_revs),
10837 update_loses_mergeinfo,
10838 merge_loses_mergeinfo,
10839 single_file_replace_style_merge_capability,
10840 merge_to_out_of_date_target,
10841 merge_with_depth_files,
10842 merge_fails_if_subtree_is_deleted_on_src,
10843 merge_away_subtrees_noninheritable_ranges,
10844 merge_to_sparse_directories,
10845 merge_old_and_new_revs_from_renamed_dir,
10846 merge_with_child_having_different_rev_ranges_to_merge,
10847 merge_old_and_new_revs_from_renamed_file,
10848 merge_with_auto_rev_range_detection,
10849 mergeinfo_recording_in_skipped_merge,
10850 cherry_picking,
10851 propchange_of_subdir_raises_conflict,
10852 reverse_merge_prop_add_on_child,
10853 XFail(merge_target_with_non_inheritable_mergeinfo),
10854 self_reverse_merge,
10855 ignore_ancestry_and_mergeinfo,
10856 merge_from_renamed_branch_fails_while_avoiding_repeat_merge,
10857 merge_source_normalization_and_subtree_merges,
10858 new_subtrees_should_not_break_merge,
10859 basic_reintegrate,
10860 XFail(reintegrate_with_rename),
10861 XFail(reintegrate_branch_never_merged_to),
10862 reintegrate_fail_on_modified_wc,
10863 reintegrate_fail_on_mixed_rev_wc,
10864 reintegrate_fail_on_switched_wc,
10865 reintegrate_fail_on_shallow_wc,
10866 XFail(reintegrate_fail_on_stale_source),
10867 dont_add_mergeinfo_from_own_history,
10868 merge_range_predates_history,
10869 foreign_repos,
10870 foreign_repos_2_url,
10871 XFail(merge_added_subtree),
10872 merge_unknown_url,
10875 if __name__ == '__main__':
10876 svntest.main.run_tests(test_list)
10877 # NOTREACHED
10880 ### End of file.