Add a little more to the svn_rangelist_intersect test to test the
[svn.git] / subversion / tests / cmdline / merge_tests.py
blob99ea6b02826af0dd523ac527c4c470c67457ba27
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 # Local mods in B2
578 B2_E_path = os.path.join(B2_path, 'E')
579 B2_lambda_path = os.path.join(B2_path, 'lambda')
580 svntest.actions.run_and_verify_svn(None, None, [],
581 'propset', 'foo', 'foo_val',
582 B2_E_path, B2_lambda_path)
583 expected_status.tweak(
584 'A/B2/E', 'A/B2/lambda', status=' M'
586 svntest.actions.run_and_verify_status(wc_dir, expected_status)
588 # Merge rev 3 into B2
590 # The local mods to the paths modified in r3 cause the paths to be
591 # skipped (without --force), resulting in no changes to the WC.
592 expected_output = wc.State(B2_path, { })
593 expected_disk = wc.State('', {
594 'E' : Item(),
595 'E/alpha' : Item("This is the file 'alpha'.\n"),
596 'E/beta' : Item("This is the file 'beta'.\n"),
597 'F' : Item(),
598 'lambda' : Item("This is the file 'lambda'.\n"),
600 expected_status = wc.State(B2_path, {
601 '' : Item(status=' '),
602 'E' : Item(status=' M'),
603 'E/alpha' : Item(status=' '),
604 'E/beta' : Item(status=' '),
605 'F' : Item(status=' '),
606 'lambda' : Item(status=' M'),
608 expected_status.tweak(wc_rev=2)
609 expected_skip = wc.State(B2_path, {
610 'lambda' : Item(),
611 'E' : Item(),
613 svntest.actions.run_and_verify_merge(B2_path, '2', '3', B_url,
614 expected_output,
615 expected_disk,
616 expected_status,
617 expected_skip)
619 expected_output = wc.State(B2_path, {
620 'E' : Item(status='D '),
621 'lambda' : Item(status='D '),
623 expected_disk.remove('E/alpha', 'E/beta', 'lambda')
624 expected_status.tweak('E', 'E/alpha', 'E/beta', 'lambda', status='D ')
625 expected_status.tweak('', status=' M')
626 expected_skip.remove('lambda', 'E')
628 ### Full-to-dry-run automatic comparison disabled because a) dry-run
629 ### doesn't descend into deleted directories, and b) the full merge
630 ### notifies deleted directories twice.
631 svntest.actions.run_and_verify_merge(B2_path, '2', '3', B_url,
632 expected_output,
633 expected_disk,
634 expected_status,
635 expected_skip,
636 None, None, None, None, None,
637 0, 0, '--force')
641 #----------------------------------------------------------------------
643 # Issue 953
644 def simple_property_merges(sbox):
645 "some simple property merges"
647 sbox.build()
648 wc_dir = sbox.wc_dir
650 # Add a property to a file and a directory
651 alpha_path = os.path.join(wc_dir, 'A', 'B', 'E', 'alpha')
652 beta_path = os.path.join(wc_dir, 'A', 'B', 'E', 'beta')
653 E_path = os.path.join(wc_dir, 'A', 'B', 'E')
655 svntest.actions.run_and_verify_svn(None, None, [],
656 'propset', 'foo', 'foo_val',
657 alpha_path)
658 # A binary, non-UTF8 property value
659 svntest.actions.run_and_verify_svn(None, None, [],
660 'propset', 'foo', 'foo\201val',
661 beta_path)
662 svntest.actions.run_and_verify_svn(None, None, [],
663 'propset', 'foo', 'foo_val',
664 E_path)
666 # Commit change as rev 2
667 expected_output = svntest.wc.State(wc_dir, {
668 'A/B/E' : Item(verb='Sending'),
669 'A/B/E/alpha' : Item(verb='Sending'),
670 'A/B/E/beta' : Item(verb='Sending'),
672 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
673 expected_status.tweak('A/B/E', 'A/B/E/alpha', 'A/B/E/beta',
674 wc_rev=2, status=' ')
675 svntest.actions.run_and_verify_commit(wc_dir,
676 expected_output, expected_status,
677 None, wc_dir)
678 svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
680 # Copy B to B2 as rev 3
681 B_url = sbox.repo_url + '/A/B'
682 B2_url = sbox.repo_url + '/A/B2'
684 svntest.actions.run_and_verify_svn(None, None, [],
685 'copy', '-m', 'copy B to B2',
686 B_url, B2_url)
687 svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
689 # Modify a property and add a property for the file and directory
690 svntest.actions.run_and_verify_svn(None, None, [],
691 'propset', 'foo', 'mod_foo', alpha_path)
692 svntest.actions.run_and_verify_svn(None, None, [],
693 'propset', 'bar', 'bar_val', alpha_path)
694 svntest.actions.run_and_verify_svn(None, None, [],
695 'propset', 'foo', 'mod\201foo', beta_path)
696 svntest.actions.run_and_verify_svn(None, None, [],
697 'propset', 'bar', 'bar\201val', beta_path)
698 svntest.actions.run_and_verify_svn(None, None, [],
699 'propset', 'foo', 'mod_foo', E_path)
700 svntest.actions.run_and_verify_svn(None, None, [],
701 'propset', 'bar', 'bar_val', E_path)
703 # Commit change as rev 4
704 expected_status = svntest.actions.get_virginal_state(wc_dir, 3)
705 expected_status.tweak('A/B/E', 'A/B/E/alpha', 'A/B/E/beta',
706 wc_rev=4, status=' ')
707 expected_status.add({
708 'A/B2' : Item(status=' ', wc_rev=3),
709 'A/B2/E' : Item(status=' ', wc_rev=3),
710 'A/B2/E/alpha' : Item(status=' ', wc_rev=3),
711 'A/B2/E/beta' : Item(status=' ', wc_rev=3),
712 'A/B2/F' : Item(status=' ', wc_rev=3),
713 'A/B2/lambda' : Item(status=' ', wc_rev=3),
715 svntest.actions.run_and_verify_commit(wc_dir,
716 expected_output, expected_status,
717 None, wc_dir)
718 svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
720 pristine_status = expected_status
721 pristine_status.tweak(wc_rev=4)
723 # Merge B 3:4 into B2
724 B2_path = os.path.join(wc_dir, 'A', 'B2')
725 expected_output = wc.State(B2_path, {
726 'E' : Item(status=' U'),
727 'E/alpha' : Item(status=' U'),
728 'E/beta' : Item(status=' U'),
730 expected_disk = wc.State('', {
731 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B:4'}),
732 'E' : Item(),
733 'E/alpha' : Item("This is the file 'alpha'.\n"),
734 'E/beta' : Item("This is the file 'beta'.\n"),
735 'F' : Item(),
736 'lambda' : Item("This is the file 'lambda'.\n"),
738 expected_disk.tweak('E', 'E/alpha',
739 props={'foo' : 'mod_foo', 'bar' : 'bar_val'})
740 expected_disk.tweak('E/beta',
741 props={'foo' : 'mod\201foo', 'bar' : 'bar\201val'})
742 expected_status = wc.State(B2_path, {
743 '' : Item(status=' M'),
744 'E' : Item(status=' M'),
745 'E/alpha' : Item(status=' M'),
746 'E/beta' : Item(status=' M'),
747 'F' : Item(status=' '),
748 'lambda' : Item(status=' '),
750 expected_status.tweak(wc_rev=4)
751 expected_skip = wc.State('', { })
752 svntest.actions.run_and_verify_merge(B2_path, '3', '4', B_url,
753 expected_output,
754 expected_disk,
755 expected_status,
756 expected_skip,
757 None, None, None, None, None, 1)
759 # Revert merge
760 svntest.actions.run_and_verify_svn(None, None, [],
761 'revert', '--recursive', wc_dir)
762 svntest.actions.run_and_verify_status(wc_dir, pristine_status)
764 # Merge B 2:1 into B2 (B2's mergeinfo should get elided away)
765 expected_status.tweak('', status=' ')
766 expected_disk.remove('')
767 expected_disk.tweak('E', 'E/alpha', 'E/beta', props={})
768 svntest.actions.run_and_verify_merge(B2_path, '2', '1', B_url,
769 expected_output,
770 expected_disk,
771 expected_status,
772 expected_skip,
773 None, None, None, None, None, 1)
775 # Merge B 3:4 into B2 now causes a conflict
776 expected_disk.add({
777 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B:4'}),
778 'E/dir_conflicts.prej'
779 : Item("Trying to change property 'foo' from 'foo_val' to 'mod_foo',\n"
780 + "but it has been locally deleted.\n"),
781 'E/alpha.prej'
782 : Item("Trying to change property 'foo' from 'foo_val' to 'mod_foo',\n"
783 + "but it has been locally deleted.\n"),
784 'E/beta.prej'
785 : Item("Trying to change property 'foo' from 'foo?\\129val' to"
786 + " 'mod?\\129foo',\n"
787 + "but it has been locally deleted.\n"),
789 expected_disk.tweak('E', 'E/alpha', props={'bar' : 'bar_val'})
790 expected_disk.tweak('E/beta', props={'bar' : 'bar\201val'})
791 expected_status.tweak('', status=' M')
792 expected_status.tweak('E', 'E/alpha', 'E/beta', status=' C')
793 expected_output.tweak('E', 'E/alpha', 'E/beta', status=' C')
794 svntest.actions.run_and_verify_merge(B2_path, '3', '4', B_url,
795 expected_output,
796 expected_disk,
797 expected_status,
798 expected_skip,
799 None, None, None, None, None, 1)
801 # issue 1109 : single file property merge. This test performs a merge
802 # that should be a no-op (adding properties that are already present).
803 svntest.actions.run_and_verify_svn(None, None, [],
804 'revert', '--recursive', wc_dir)
805 svntest.actions.run_and_verify_status(wc_dir, pristine_status)
807 # Copy A at rev 4 to A2 to make revision 5.
808 A_url = sbox.repo_url + '/A'
809 A2_url = sbox.repo_url + '/A2'
810 svntest.actions.run_and_verify_svn(None,
811 ['\n', 'Committed revision 5.\n'], [],
812 'copy', '-m', 'copy A to A2',
813 A_url, A2_url)
815 # Re-root the WC at A2.
816 svntest.actions.run_and_verify_svn(None, None, [], 'switch', A2_url, wc_dir)
818 # Attempt to re-merge rev 4 of the original A's alpha. Mergeinfo
819 # inherited from A2 (created by its copy from A) allows us to avoid
820 # a repeated merge.
821 alpha_url = sbox.repo_url + '/A/B/E/alpha'
822 alpha_path = os.path.join(wc_dir, 'B', 'E', 'alpha')
824 # Cannot use run_and_verify_merge with a file target
825 svntest.actions.run_and_verify_svn(None, [], [], 'merge', '-r', '3:4',
826 alpha_url, alpha_path)
828 output, err = svntest.actions.run_and_verify_svn(None, None, [],
829 'pl', alpha_path)
831 saw_foo = 0
832 saw_bar = 0
833 for line in output:
834 if re.match("\\s*foo\\s*$", line):
835 saw_foo = 1
836 if re.match("\\s*bar\\s*$", line):
837 saw_bar = 1
839 if not saw_foo or not saw_bar:
840 raise svntest.Failure("Expected properties not found")
843 #----------------------------------------------------------------------
844 # This is a regression for issue #1176.
846 def merge_catches_nonexistent_target(sbox):
847 "merge should not die if a target file is absent"
849 sbox.build()
850 wc_dir = sbox.wc_dir
852 # Copy G to a new directory, Q. Create Q/newfile. Commit a change
853 # to Q/newfile. Now merge that change... into G. Merge should not
854 # error, but should do nothing.
856 G_path = os.path.join(wc_dir, 'A', 'D', 'G')
857 Q_path = os.path.join(wc_dir, 'A', 'D', 'Q')
858 newfile_path = os.path.join(Q_path, 'newfile')
859 Q_url = sbox.repo_url + '/A/D/Q'
861 # Copy dir A/D/G to A/D/Q
862 svntest.actions.run_and_verify_svn(None, None, [], 'cp', G_path, Q_path)
864 svntest.main.file_append(newfile_path, 'This is newfile.\n')
865 svntest.actions.run_and_verify_svn(None, None, [], 'add', newfile_path)
867 # Add newfile to dir G, creating r2.
868 expected_output = wc.State(wc_dir, {
869 'A/D/Q' : Item(verb='Adding'),
870 'A/D/Q/newfile' : Item(verb='Adding'),
872 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
873 expected_status.add({
874 'A/D/Q' : Item(status=' ', wc_rev=2),
875 'A/D/Q/pi' : Item(status=' ', wc_rev=2),
876 'A/D/Q/rho' : Item(status=' ', wc_rev=2),
877 'A/D/Q/tau' : Item(status=' ', wc_rev=2),
878 'A/D/Q/newfile' : Item(status=' ', wc_rev=2),
880 svntest.actions.run_and_verify_commit(wc_dir,
881 expected_output,
882 expected_status,
883 None, wc_dir)
885 # Change newfile, creating r3.
886 svntest.main.file_append(newfile_path, 'A change to newfile.\n')
887 expected_output = wc.State(wc_dir, {
888 'A/D/Q/newfile' : Item(verb='Sending'),
890 expected_status.tweak('A/D/Q/newfile', wc_rev=3)
891 svntest.actions.run_and_verify_commit(wc_dir,
892 expected_output,
893 expected_status,
894 None, wc_dir)
896 # Merge the change to newfile (from r3) into G, where newfile
897 # doesn't exist.
898 os.chdir(G_path)
899 expected_output = wc.State('', { })
900 expected_status = wc.State('', {
901 '' : Item(),
902 'pi' : Item(),
903 'rho' : Item(),
904 'tau' : Item(),
906 expected_status.tweak(status=' ', wc_rev=1)
907 expected_disk = wc.State('', {
908 'pi' : Item("This is the file 'pi'.\n"),
909 'rho' : Item("This is the file 'rho'.\n"),
910 'tau' : Item("This is the file 'tau'.\n"),
912 expected_skip = wc.State('', {
913 'newfile' :Item(),
915 svntest.actions.run_and_verify_merge('', '2', '3', Q_url,
916 expected_output,
917 expected_disk,
918 expected_status,
919 expected_skip)
921 #----------------------------------------------------------------------
923 def merge_tree_deleted_in_target(sbox):
924 "merge on deleted directory in target"
926 sbox.build()
927 wc_dir = sbox.wc_dir
929 # Copy B to a new directory, I. Modify B/E/alpha, Remove I/E. Now
930 # merge that change... into I. Merge should not error
932 B_path = os.path.join(wc_dir, 'A', 'B')
933 I_path = os.path.join(wc_dir, 'A', 'I')
934 alpha_path = os.path.join(B_path, 'E', 'alpha')
935 B_url = sbox.repo_url + '/A/B'
936 I_url = sbox.repo_url + '/A/I'
939 # Copy B to I, creating r1.
940 svntest.actions.run_and_verify_svn(None, None, [],
941 'cp', B_url, I_url, '-m', 'rev 2')
943 # Change some files, creating r2.
944 svntest.main.file_append(alpha_path, 'A change to alpha.\n')
945 svntest.main.file_append(os.path.join(B_path, 'lambda'), 'change lambda.\n')
946 svntest.actions.run_and_verify_svn(None, None, [],
947 'ci', '-m', 'rev 3', B_path)
949 # Remove E, creating r3.
950 E_url = sbox.repo_url + '/A/I/E'
951 svntest.actions.run_and_verify_svn(None, None, [],
952 'rm', E_url, '-m', 'rev 4')
954 svntest.actions.run_and_verify_svn(None, None, [],
955 'up', os.path.join(wc_dir,'A'))
957 expected_output = wc.State(I_path, {
958 'lambda' : Item(status='U '),
960 expected_disk = wc.State('', {
961 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B:3'}),
962 'F' : Item(),
963 'lambda' : Item("This is the file 'lambda'.\nchange lambda.\n"),
965 expected_status = wc.State(I_path, {
966 '' : Item(status=' M'),
967 'F' : Item(status=' '),
968 'lambda' : Item(status='M '),
970 expected_status.tweak(wc_rev=4)
971 expected_skip = wc.State(I_path, {
972 'E' : Item(),
973 'E/alpha' : Item(),
975 svntest.actions.run_and_verify_merge(I_path, '2', '3', B_url,
976 expected_output,
977 expected_disk,
978 expected_status,
979 expected_skip,
980 None, None, None, None, None,
981 1, 0)
983 #----------------------------------------------------------------------
984 # Issue #2515
986 def merge_added_dir_to_deleted_in_target(sbox):
987 "merge an added dir on a deleted dir in target"
989 sbox.build()
990 wc_dir = sbox.wc_dir
992 # copy B to a new directory, I.
993 # delete F in I.
994 # add J to B/F.
995 # merge add to I.
997 B_url = sbox.repo_url + '/A/B'
998 I_url = sbox.repo_url + '/A/I'
999 F_url = sbox.repo_url + '/A/I/F'
1000 J_url = sbox.repo_url + '/A/B/F/J'
1001 I_path = os.path.join(wc_dir, 'A', 'I')
1004 svntest.actions.run_and_verify_svn(None, None, [],
1005 'cp', B_url, I_url, '-m', 'rev 2')
1007 svntest.actions.run_and_verify_svn(None, None, [],
1008 'rm', F_url, '-m', 'rev 3')
1010 svntest.actions.run_and_verify_svn(None, None, [],
1011 'mkdir', '-m', 'rev 4', J_url)
1013 svntest.actions.run_and_verify_svn(None, None, [],
1014 'up', os.path.join(wc_dir,'A'))
1016 expected_output = wc.State(I_path, {})
1017 expected_disk = wc.State('', {
1018 'E' : Item(),
1019 'E/alpha' : Item("This is the file 'alpha'.\n"),
1020 'E/beta' : Item("This is the file 'beta'.\n"),
1021 'lambda' : Item("This is the file 'lambda'.\n"),
1023 expected_skip = wc.State(I_path, {
1024 'F/J' : Item(),
1025 'F' : Item(),
1028 svntest.actions.run_and_verify_merge(I_path, '2', '4', B_url,
1029 expected_output,
1030 expected_disk,
1031 None,
1032 expected_skip,
1033 None, None, None, None, None,
1034 0, 0)
1036 #----------------------------------------------------------------------
1037 # This is a regression for issue #1176.
1039 def merge_similar_unrelated_trees(sbox):
1040 "merging similar trees ancestrally unrelated"
1042 ## See http://subversion.tigris.org/issues/show_bug.cgi?id=1249. ##
1044 sbox.build()
1045 wc_dir = sbox.wc_dir
1047 # Simple test. Make three directories with the same content.
1048 # Modify some stuff in the second one. Now merge
1049 # (firstdir:seconddir->thirddir).
1051 base1_path = os.path.join(wc_dir, 'base1')
1052 base2_path = os.path.join(wc_dir, 'base2')
1053 apply_path = os.path.join(wc_dir, 'apply')
1055 base1_url = os.path.join(sbox.repo_url + '/base1')
1056 base2_url = os.path.join(sbox.repo_url + '/base2')
1058 # Make a tree of stuff ...
1059 os.mkdir(base1_path)
1060 svntest.main.file_append(os.path.join(base1_path, 'iota'),
1061 "This is the file iota\n")
1062 os.mkdir(os.path.join(base1_path, 'A'))
1063 svntest.main.file_append(os.path.join(base1_path, 'A', 'mu'),
1064 "This is the file mu\n")
1065 os.mkdir(os.path.join(base1_path, 'A', 'B'))
1066 svntest.main.file_append(os.path.join(base1_path, 'A', 'B', 'alpha'),
1067 "This is the file alpha\n")
1068 svntest.main.file_append(os.path.join(base1_path, 'A', 'B', 'beta'),
1069 "This is the file beta\n")
1071 # ... Copy it twice ...
1072 shutil.copytree(base1_path, base2_path)
1073 shutil.copytree(base1_path, apply_path)
1075 # ... Gonna see if merge is naughty or nice!
1076 svntest.main.file_append(os.path.join(base2_path, 'A', 'mu'),
1077 "A new line in mu.\n")
1078 os.rename(os.path.join(base2_path, 'A', 'B', 'beta'),
1079 os.path.join(base2_path, 'A', 'B', 'zeta'))
1081 svntest.actions.run_and_verify_svn(None, None, [],
1082 'add', base1_path, base2_path, apply_path)
1084 svntest.actions.run_and_verify_svn(None, None, [],
1085 'ci', '-m', 'rev 2', wc_dir)
1087 expected_output = wc.State(apply_path, {
1088 'A/mu' : Item(status='U '),
1089 'A/B/zeta' : Item(status='A '),
1090 'A/B/beta' : Item(status='D '),
1093 # Search for the comment entitled "The Merge Kluge" elsewhere in
1094 # this file, to understand why we shorten and chdir() below.
1095 saved_cwd = os.getcwd()
1096 os.chdir(svntest.main.work_dir)
1097 # run_and_verify_merge doesn't support 'svn merge URL URL path'
1098 svntest.actions.run_and_verify_svn(None, None, [],
1099 'merge',
1100 '--ignore-ancestry',
1101 base1_url, base2_url,
1102 shorten_path_kludge(apply_path))
1103 os.chdir(saved_cwd)
1105 expected_status = wc.State(apply_path, {
1106 '' : Item(status=' '),
1107 'A' : Item(status=' '),
1108 'A/mu' : Item(status='M '),
1109 'A/B' : Item(status=' '),
1110 'A/B/zeta' : Item(status='A ', copied='+'),
1111 'A/B/alpha' : Item(status=' '),
1112 'A/B/beta' : Item(status='D '),
1113 'iota' : Item(status=' '),
1115 expected_status.tweak(wc_rev=2)
1116 expected_status.tweak('A/B/zeta', wc_rev='-')
1117 svntest.actions.run_and_verify_status(apply_path, expected_status)
1119 #----------------------------------------------------------------------
1120 def merge_one_file_helper(sbox, arg_flav, record_only = 0):
1121 "ARG_FLAV is one of 'r' (revision range) or 'c' (single change)."
1123 if arg_flav not in ('r', 'c', '*'):
1124 raise svntest.Failure("Unrecognized flavor of merge argument")
1126 sbox.build()
1127 wc_dir = sbox.wc_dir
1129 rho_rel_path = os.path.join('A', 'D', 'G', 'rho')
1130 rho_path = os.path.join(wc_dir, rho_rel_path)
1131 G_path = os.path.join(wc_dir, 'A', 'D', 'G')
1132 rho_url = sbox.repo_url + '/A/D/G/rho'
1134 # Change rho for revision 2
1135 svntest.main.file_append(rho_path, 'A new line in rho.\n')
1137 expected_output = wc.State(wc_dir, { rho_rel_path : Item(verb='Sending'), })
1138 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
1139 expected_status.tweak('A/D/G/rho', wc_rev=2)
1140 svntest.actions.run_and_verify_commit(wc_dir,
1141 expected_output,
1142 expected_status,
1143 None,
1144 wc_dir)
1146 # Backdate rho to revision 1, so we can merge in the rev 2 changes.
1147 svntest.actions.run_and_verify_svn(None, None, [],
1148 'up', '-r', '1', rho_path)
1150 # Try one merge with an explicit target; it should succeed.
1151 # ### Yes, it would be nice to use run_and_verify_merge(), but it
1152 # appears to be impossible to get the expected_foo trees working
1153 # right. I think something is still assuming a directory target.
1154 if arg_flav == 'r':
1155 svntest.actions.run_and_verify_svn(None ,
1156 expected_merge_output([[2]], 'U ' +
1157 rho_path + '\n'),
1159 'merge', '-r', '1:2',
1160 rho_url, rho_path)
1161 elif arg_flav == 'c':
1162 svntest.actions.run_and_verify_svn(None ,
1163 expected_merge_output([[2]], 'U ' +
1164 rho_path + '\n'),
1166 'merge', '-c', '2',
1167 rho_url, rho_path)
1168 elif arg_flav == '*':
1169 svntest.actions.run_and_verify_svn(None ,
1170 expected_merge_output([[2]], 'U ' +
1171 rho_path + '\n'),
1173 'merge', rho_url, rho_path)
1175 expected_status.tweak(wc_rev=1)
1176 expected_status.tweak('A/D/G/rho', status='MM')
1177 svntest.actions.run_and_verify_status(wc_dir, expected_status)
1179 # Inspect rho, make sure it's right.
1180 rho_text = svntest.tree.get_text(rho_path)
1181 if rho_text != "This is the file 'rho'.\nA new line in rho.\n":
1182 raise svntest.Failure("Unexpected text in merged '" + rho_path + "'")
1184 # Restore rho to pristine revision 1, for another merge.
1185 svntest.actions.run_and_verify_svn(None, None, [], 'revert', rho_path)
1186 expected_status.tweak('A/D/G/rho', status=' ')
1187 svntest.actions.run_and_verify_status(wc_dir, expected_status)
1189 # Cd into the directory and run merge with no targets.
1190 # It should still merge into rho.
1191 saved_cwd = os.getcwd()
1192 os.chdir(G_path)
1194 # Cannot use run_and_verify_merge with a file target
1195 merge_cmd = ['merge']
1196 if arg_flav == 'r':
1197 merge_cmd += ['-r', '1:2']
1198 elif arg_flav == 'c':
1199 merge_cmd += ['-c', '2']
1201 if record_only:
1202 expected_output = []
1203 merge_cmd.append('--record-only')
1204 rho_expected_status = ' M'
1205 else:
1206 expected_output = expected_merge_output([[2]], 'U rho\n')
1207 rho_expected_status = 'MM'
1208 merge_cmd.append(rho_url)
1210 svntest.actions.run_and_verify_svn(None, expected_output, [], *merge_cmd)
1212 # Inspect rho, make sure it's right.
1213 rho_text = svntest.tree.get_text('rho')
1214 if record_only:
1215 expected_text = "This is the file 'rho'.\n"
1216 else:
1217 expected_text = "This is the file 'rho'.\nA new line in rho.\n"
1218 if rho_text != expected_text:
1219 print
1220 raise svntest.Failure("Unexpected text merged to 'rho' in '" +
1221 G_path + "'")
1222 os.chdir(saved_cwd)
1224 expected_status.tweak('A/D/G/rho', status=rho_expected_status)
1225 svntest.actions.run_and_verify_status(wc_dir, expected_status)
1227 def merge_one_file_using_r(sbox):
1228 "merge one file (issue #1150) using the -r option"
1229 merge_one_file_helper(sbox, 'r')
1231 def merge_one_file_using_c(sbox):
1232 "merge one file (issue #1150) using the -c option"
1233 merge_one_file_helper(sbox, 'c')
1235 def merge_one_file_using_implicit_revs(sbox):
1236 "merge one file without explicit revisions"
1237 merge_one_file_helper(sbox, '*')
1239 def merge_record_only(sbox):
1240 "mark a revision range as merged"
1241 merge_one_file_helper(sbox, 'r', 1)
1243 #----------------------------------------------------------------------
1244 # This is a regression for the enhancement added in issue #785.
1246 def merge_with_implicit_target_helper(sbox, arg_flav):
1247 "ARG_FLAV is one of 'r' (revision range) or 'c' (single change)."
1249 if arg_flav not in ('r', 'c', '*'):
1250 raise svntest.Failure("Unrecognized flavor of merge argument")
1252 sbox.build()
1253 wc_dir = sbox.wc_dir
1255 # Change mu for revision 2
1256 mu_path = os.path.join(wc_dir, 'A', 'mu')
1257 orig_mu_text = svntest.tree.get_text(mu_path)
1258 added_mu_text = ""
1259 for x in range(2,11):
1260 added_mu_text = added_mu_text + 'This is line ' + `x` + ' in mu\n'
1261 svntest.main.file_append(mu_path, added_mu_text)
1263 # Create expected output tree for initial commit
1264 expected_output = wc.State(wc_dir, {
1265 'A/mu' : Item(verb='Sending'),
1268 # Create expected status tree; all local revisions should be at 1,
1269 # but mu should be at revision 2.
1270 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
1271 expected_status.tweak('A/mu', wc_rev=2)
1273 # Initial commit.
1274 svntest.actions.run_and_verify_commit(wc_dir,
1275 expected_output,
1276 expected_status,
1277 None,
1278 wc_dir)
1280 # Make the "other" working copy, at r1
1281 other_wc = sbox.add_wc_path('other')
1282 svntest.actions.duplicate_dir(wc_dir, other_wc)
1283 svntest.main.run_svn(None, 'up', '-r', 1, other_wc)
1285 # Try the merge without an explicit target; it should succeed.
1286 # Can't use run_and_verify_merge cuz it expects a directory argument.
1287 mu_url = sbox.repo_url + '/A/mu'
1289 os.chdir(os.path.join(other_wc, 'A'))
1291 # merge using filename for sourcepath
1292 # Cannot use run_and_verify_merge with a file target
1293 if arg_flav == 'r':
1294 svntest.actions.run_and_verify_svn(None,
1295 expected_merge_output([[2]],
1296 'U mu\n'),
1298 'merge', '-r', '1:2', 'mu')
1299 elif arg_flav == 'c':
1300 svntest.actions.run_and_verify_svn(None,
1301 expected_merge_output([[2]],
1302 'U mu\n'),
1304 'merge', '-c', '2', 'mu')
1306 elif arg_flav == '*':
1307 svntest.actions.run_and_verify_svn(None,
1308 expected_merge_output([[2]],
1309 'U mu\n'),
1311 'merge', 'mu')
1313 # sanity-check resulting file
1314 if (svntest.tree.get_text('mu') != orig_mu_text + added_mu_text):
1315 raise svntest.Failure("Unexpected text in 'mu'")
1317 # merge using URL for sourcepath
1318 if arg_flav == 'r':
1319 svntest.actions.run_and_verify_svn(None,
1320 expected_merge_output([[-2]],
1321 'G mu\n'),
1323 'merge', '-r', '2:1', mu_url)
1324 elif arg_flav == 'c':
1325 svntest.actions.run_and_verify_svn(None,
1326 expected_merge_output([[-2]],
1327 'G mu\n'),
1329 'merge', '-c', '-2', mu_url)
1330 elif arg_flav == '*':
1331 # Implicit merge source URL and revision range detection is for
1332 # forward merges only (e.g. non-reverts). Undo application of
1333 # r2 to enable continuation of the test case.
1334 svntest.actions.run_and_verify_svn(None,
1335 expected_merge_output([[-2]],
1336 'G mu\n'),
1338 'merge', '-c', '-2', mu_url)
1340 # sanity-check resulting file
1341 if (svntest.tree.get_text('mu') != orig_mu_text):
1342 raise svntest.Failure("Unexpected text '%s' in 'mu', expected '%s'" %
1343 (svntest.tree.get_text('mu'), orig_mu_text))
1347 def merge_with_implicit_target_using_r(sbox):
1348 "merging a file w/no explicit target path using -r"
1349 merge_with_implicit_target_helper(sbox, 'r')
1351 def merge_with_implicit_target_using_c(sbox):
1352 "merging a file w/no explicit target path using -c"
1353 merge_with_implicit_target_helper(sbox, 'c')
1355 def merge_with_implicit_target_and_revs(sbox):
1356 "merging a file w/no explicit target path or revs"
1357 merge_with_implicit_target_helper(sbox, '*')
1360 #----------------------------------------------------------------------
1362 def merge_with_prev (sbox):
1363 "merge operations using PREV revision"
1365 sbox.build()
1366 wc_dir = sbox.wc_dir
1368 # Change mu for revision 2
1369 mu_path = os.path.join(wc_dir, 'A', 'mu')
1370 orig_mu_text = svntest.tree.get_text(mu_path)
1371 added_mu_text = ""
1372 for x in range(2,11):
1373 added_mu_text = added_mu_text + '\nThis is line ' + `x` + ' in mu'
1374 added_mu_text += "\n"
1375 svntest.main.file_append(mu_path, added_mu_text)
1377 zot_path = os.path.join(wc_dir, 'A', 'zot')
1379 svntest.main.file_append(zot_path, "bar")
1380 svntest.main.run_svn(None, 'add', zot_path)
1382 # Create expected output tree for initial commit
1383 expected_output = wc.State(wc_dir, {
1384 'A/mu' : Item(verb='Sending'),
1385 'A/zot' : Item(verb='Adding'),
1388 # Create expected status tree; all local revisions should be at 1,
1389 # but mu should be at revision 2.
1390 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
1391 expected_status.tweak('A/mu', wc_rev=2)
1392 expected_status.add({'A/zot' : Item(status=' ', wc_rev=2)})
1394 # Initial commit.
1395 svntest.actions.run_and_verify_commit(wc_dir,
1396 expected_output,
1397 expected_status,
1398 None,
1399 wc_dir)
1401 # Make some other working copies
1402 other_wc = sbox.add_wc_path('other')
1403 svntest.actions.duplicate_dir(wc_dir, other_wc)
1405 another_wc = sbox.add_wc_path('another')
1406 svntest.actions.duplicate_dir(wc_dir, another_wc)
1408 was_cwd = os.getcwd()
1410 os.chdir(os.path.join(other_wc, 'A'))
1412 # Try to revert the last change to mu via svn merge
1413 # Cannot use run_and_verify_merge with a file target
1414 svntest.actions.run_and_verify_svn(None,
1415 expected_merge_output([[-2]],
1416 'U mu\n'),
1418 'merge', '-r', 'HEAD:PREV', 'mu')
1420 # sanity-check resulting file
1421 if (svntest.tree.get_text('mu') != orig_mu_text):
1422 raise svntest.Failure("Unexpected text in 'mu'")
1424 os.chdir(was_cwd)
1426 other_status = expected_status
1427 other_status.wc_dir = other_wc
1428 other_status.tweak('A/mu', status='M ', wc_rev=2)
1429 other_status.tweak('A/zot', wc_rev=2)
1430 svntest.actions.run_and_verify_status(other_wc, other_status)
1432 os.chdir(another_wc)
1434 # ensure 'A' will be at revision 2
1435 svntest.actions.run_and_verify_svn(None, None, [], 'up')
1437 # now try a revert on a directory, and verify that it removed the zot
1438 # file we had added previously
1439 svntest.actions.run_and_verify_svn(None, None, [],
1440 'merge', '-r', 'COMMITTED:PREV',
1441 'A', 'A')
1443 if (svntest.tree.get_text('A/zot') != None):
1444 raise svntest.Failure("Unexpected text in 'A/zot'")
1446 os.chdir(was_cwd)
1448 another_status = expected_status
1449 another_status.wc_dir = another_wc
1450 another_status.tweak(wc_rev=2)
1451 another_status.tweak('A/mu', status='M ')
1452 another_status.tweak('A/zot', status='D ')
1453 svntest.actions.run_and_verify_status(another_wc, another_status)
1455 #----------------------------------------------------------------------
1456 # Regression test for issue #1319: 'svn merge' should *not* 'C' when
1457 # merging a change into a binary file, unless it has local mods, or has
1458 # different contents from the left side of the merge.
1460 def merge_binary_file (sbox):
1461 "merge change into unchanged binary file"
1463 sbox.build()
1464 wc_dir = sbox.wc_dir
1466 # Add a binary file to the project
1467 theta_contents = svntest.main.file_read(
1468 os.path.join(sys.path[0], "theta.bin"), 'rb')
1469 # Write PNG file data into 'A/theta'.
1470 theta_path = os.path.join(wc_dir, 'A', 'theta')
1471 svntest.main.file_write(theta_path, theta_contents, 'wb')
1473 svntest.main.run_svn(None, 'add', theta_path)
1475 # Commit the new binary file, creating revision 2.
1476 expected_output = svntest.wc.State(wc_dir, {
1477 'A/theta' : Item(verb='Adding (bin)'),
1479 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
1480 expected_status.add({
1481 'A/theta' : Item(status=' ', wc_rev=2),
1483 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
1484 expected_status, None,
1485 wc_dir)
1487 # Make the "other" working copy
1488 other_wc = sbox.add_wc_path('other')
1489 svntest.actions.duplicate_dir(wc_dir, other_wc)
1491 # Change the binary file in first working copy, commit revision 3.
1492 svntest.main.file_append(theta_path, "some extra junk")
1493 expected_output = wc.State(wc_dir, {
1494 'A/theta' : Item(verb='Sending'),
1496 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
1497 expected_status.add({
1498 'A/theta' : Item(status=' ', wc_rev=3),
1500 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
1501 expected_status, None,
1502 wc_dir)
1504 # Search for the comment entitled "The Merge Kluge" elsewhere in
1505 # this file, to understand why we shorten and chdir() below.
1506 short_other_wc = shorten_path_kludge(other_wc)
1508 # In second working copy, attempt to 'svn merge -r 2:3'.
1509 # We should *not* see a conflict during the update, but a 'U'.
1510 # And after the merge, the status should be 'M'.
1511 expected_output = wc.State(short_other_wc, {
1512 'A/theta' : Item(status='U '),
1514 expected_disk = svntest.main.greek_state.copy()
1515 expected_disk.add({
1516 '' : Item(props={SVN_PROP_MERGEINFO : '/:3'}),
1517 'A/theta' : Item(theta_contents + "some extra junk",
1518 props={'svn:mime-type' : 'application/octet-stream'}),
1520 expected_status = svntest.actions.get_virginal_state(short_other_wc, 1)
1521 expected_status.add({
1522 '' : Item(status=' M', wc_rev=1),
1523 'A/theta' : Item(status='M ', wc_rev=2),
1525 expected_skip = wc.State('', { })
1527 os.chdir(svntest.main.work_dir)
1528 svntest.actions.run_and_verify_merge(short_other_wc, '2', '3',
1529 sbox.repo_url,
1530 expected_output,
1531 expected_disk,
1532 expected_status,
1533 expected_skip,
1534 None, None, None, None, None,
1537 #----------------------------------------------------------------------
1538 # Regression test for issue #2403: Incorrect 3-way merge of "added"
1539 # binary file which already exists (unmodified) in the WC
1541 def three_way_merge_add_of_existing_binary_file(sbox):
1542 "3-way merge of 'file add' into existing binary"
1544 sbox.build()
1545 wc_dir = sbox.wc_dir
1547 # Create a branch of A, creating revision 2.
1548 A_url = sbox.repo_url + "/A"
1549 branch_A_url = sbox.repo_url + "/copy-of-A"
1550 svntest.actions.run_and_verify_svn(None, None, [],
1551 "cp",
1552 A_url, branch_A_url,
1553 "-m", "Creating copy-of-A")
1555 # Add a binary file to the WC.
1556 theta_contents = svntest.main.file_read(
1557 os.path.join(sys.path[0], "theta.bin"), 'rb')
1558 # Write PNG file data into 'A/theta'.
1559 theta_path = os.path.join(wc_dir, 'A', 'theta')
1560 svntest.main.file_write(theta_path, theta_contents, 'wb')
1562 svntest.main.run_svn(None, "add", theta_path)
1564 # Commit the new binary file to the repos, creating revision 3.
1565 expected_output = svntest.wc.State(wc_dir, {
1566 "A/theta" : Item(verb="Adding (bin)"),
1568 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
1569 expected_status.add({
1570 "A/theta" : Item(status=" ", wc_rev=3),
1572 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
1573 expected_status, None,
1574 wc_dir)
1576 # Search for the comment entitled "The Merge Kluge" elsewhere in
1577 # this file, to understand why we shorten and chdir() below.
1578 short_wc = shorten_path_kludge(wc_dir)
1580 # In the working copy, attempt to 'svn merge branch_A_url@2 A_url@3 A'.
1581 # We should *not* see a conflict during the merge, but an 'A'.
1582 # And after the merge, the status should not report any differences.
1584 expected_output = wc.State(short_wc, {
1585 "A/theta" : Item(status="A "),
1588 # As greek_state is rooted at / instead of /A (our merge target), we
1589 # need a sub-tree of it rather than straight copy.
1590 expected_disk = svntest.main.greek_state.subtree("A")
1591 expected_disk.add({
1592 "" : Item(props={SVN_PROP_MERGEINFO : '/A:2-3'}),
1593 "theta" : Item(theta_contents,
1594 props={"svn:mime-type" : "application/octet-stream"}),
1596 expected_status = svntest.actions.get_virginal_state(short_wc, 1)
1597 expected_status.add({
1598 "A/theta" : Item(status=" ", wc_rev=3),
1600 expected_status.tweak("A", status=" M")
1601 expected_status.remove("") # top-level of the WC
1602 expected_status.remove("iota")
1603 expected_skip = wc.State("", { })
1605 os.chdir(svntest.main.work_dir)
1606 # If we merge into short_wc alone, theta appears at the WC root,
1607 # which is in the wrong location -- append "/A" to stay on target.
1608 svntest.actions.run_and_verify_merge2(short_wc + "/A", "2", "3",
1609 branch_A_url, A_url,
1610 expected_output,
1611 expected_disk,
1612 expected_status,
1613 expected_skip,
1614 None, None, None, None, None,
1617 #----------------------------------------------------------------------
1618 # Regression test for Issue #1297:
1619 # A merge that creates a new file followed by an immediate diff
1620 # The diff should succeed.
1622 def merge_in_new_file_and_diff(sbox):
1623 "diff after merge that creates a new file"
1625 sbox.build()
1626 wc_dir = sbox.wc_dir
1628 trunk_url = sbox.repo_url + '/A/B/E'
1630 # Create a branch
1631 svntest.actions.run_and_verify_svn(None, None, [], 'cp',
1632 trunk_url,
1633 sbox.repo_url + '/branch',
1634 '-m', "Creating the Branch")
1636 # Update to revision 2.
1637 svntest.actions.run_and_verify_svn(None, None, [],
1638 'update', wc_dir)
1640 new_file_path = os.path.join(wc_dir, 'A', 'B', 'E', 'newfile')
1641 svntest.main.file_write(new_file_path, "newfile\n")
1643 # Add the new file, and commit revision 3.
1644 svntest.actions.run_and_verify_svn(None, None, [], "add", new_file_path)
1645 svntest.actions.run_and_verify_svn(None, None, [],
1646 'ci', '-m',
1647 "Changing the trunk.", wc_dir)
1649 # Search for the comment entitled "The Merge Kluge" elsewhere in
1650 # this file, to understand why we shorten and chdir() below.
1651 branch_path = os.path.join(wc_dir, "branch")
1652 short_branch_path = shorten_path_kludge(branch_path)
1654 # Merge our addition into the branch.
1655 expected_output = svntest.wc.State(short_branch_path, {
1656 'newfile' : Item(status='A '),
1658 expected_disk = wc.State('', {
1659 'alpha' : Item("This is the file 'alpha'.\n"),
1660 'beta' : Item("This is the file 'beta'.\n"),
1661 'newfile' : Item("newfile\n"),
1663 expected_status = wc.State(short_branch_path, {
1664 '' : Item(status=' M', wc_rev=2),
1665 'alpha' : Item(status=' ', wc_rev=2),
1666 'beta' : Item(status=' ', wc_rev=2),
1667 'newfile' : Item(status='A ', wc_rev='-', copied='+')
1669 expected_skip = wc.State('', { })
1671 saved_cwd = os.getcwd()
1673 os.chdir(svntest.main.work_dir)
1674 svntest.actions.run_and_verify_merge(short_branch_path,
1675 '1', 'HEAD', trunk_url,
1676 expected_output,
1677 expected_disk,
1678 expected_status,
1679 expected_skip)
1681 os.chdir(saved_cwd)
1683 # Finally, run diff. This diff produces no output!
1684 expected_output = [
1685 "\n",
1686 "Property changes on: " + branch_path + "\n",
1687 "___________________________________________________________________\n",
1688 "Added: " + SVN_PROP_MERGEINFO + "\n",
1689 " Merged /A/B/E:r2-3\n",
1690 "\n", ]
1691 svntest.actions.run_and_verify_svn(None, expected_output, [], 'diff',
1692 branch_path)
1695 #----------------------------------------------------------------------
1697 # Issue #1425: 'svn merge' should skip over any unversioned obstructions.
1699 def merge_skips_obstructions(sbox):
1700 "merge should skip over unversioned obstructions"
1702 sbox.build()
1703 wc_dir = sbox.wc_dir
1705 C_path = os.path.join(wc_dir, 'A', 'C')
1706 F_path = os.path.join(wc_dir, 'A', 'B', 'F')
1707 F_url = sbox.repo_url + '/A/B/F'
1709 Q_path = os.path.join(F_path, 'Q')
1710 foo_path = os.path.join(F_path, 'foo')
1711 bar_path = os.path.join(F_path, 'Q', 'bar')
1713 svntest.main.run_svn(None, 'mkdir', Q_path)
1714 svntest.main.file_append(foo_path, "foo")
1715 svntest.main.file_append(bar_path, "bar")
1716 svntest.main.run_svn(None, 'add', foo_path, bar_path)
1718 expected_output = wc.State(wc_dir, {
1719 'A/B/F/Q' : Item(verb='Adding'),
1720 'A/B/F/Q/bar' : Item(verb='Adding'),
1721 'A/B/F/foo' : Item(verb='Adding'),
1723 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
1724 expected_status.add({
1725 'A/B/F/Q' : Item(status=' ', wc_rev=2),
1726 'A/B/F/Q/bar' : Item(status=' ', wc_rev=2),
1727 'A/B/F/foo' : Item(status=' ', wc_rev=2),
1729 svntest.actions.run_and_verify_commit(wc_dir,
1730 expected_output,
1731 expected_status,
1732 None,
1733 wc_dir)
1735 pre_merge_status = expected_status
1737 # Revision 2 now has A/B/F/foo, A/B/F/Q, A/B/F/Q/bar. Let's merge
1738 # those 'F' changes into empty dir 'C'. But first, create an
1739 # unversioned 'foo' within C, and make sure 'svn merge' doesn't
1740 # error when the addition of foo is obstructed.
1742 # Search for the comment entitled "The Merge Kluge" elsewhere in
1743 # this file, to understand why we shorten and chdir() below.
1744 short_C_path = shorten_path_kludge(C_path)
1746 expected_output = wc.State(short_C_path, {
1747 'Q' : Item(status='A '),
1748 'Q/bar' : Item(status='A '),
1750 expected_disk = wc.State('', {
1751 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B/F:2'}),
1752 'Q' : Item(),
1753 'Q/bar' : Item("bar"),
1754 'foo' : Item("foo"),
1756 expected_status = wc.State(short_C_path, {
1757 '' : Item(status=' M', wc_rev=1),
1758 'Q' : Item(status='A ', wc_rev='-', copied='+'),
1759 'Q/bar' : Item(status='A ', wc_rev='-', copied='+'),
1761 expected_skip = wc.State(short_C_path, {
1762 'foo' : Item(),
1764 # Unversioned:
1765 svntest.main.file_append(os.path.join(C_path, "foo"), "foo")
1767 saved_cwd = os.getcwd()
1769 os.chdir(svntest.main.work_dir)
1770 svntest.actions.run_and_verify_merge(short_C_path, '1', '2', F_url,
1771 expected_output,
1772 expected_disk,
1773 expected_status,
1774 expected_skip,
1775 None, None, None, None, None,
1776 1, 0)
1778 os.chdir(saved_cwd)
1780 # Revert the local mods, and this time make "Q" obstructed. An
1781 # unversioned file called "Q" will obstruct the adding of the
1782 # directory of the same name.
1784 svntest.actions.run_and_verify_svn(None, None, [],
1785 'revert', '-R', wc_dir)
1786 os.unlink(os.path.join(C_path, "foo"))
1787 svntest.main.safe_rmtree(os.path.join(C_path, "Q"))
1788 svntest.main.file_append(os.path.join(C_path, "Q"), "foo") # unversioned
1789 svntest.actions.run_and_verify_status(wc_dir, pre_merge_status)
1791 # Search for the comment entitled "The Merge Kluge" elsewhere in
1792 # this file, to understand why we use short_C_path and chdir() below.
1793 expected_output = wc.State(short_C_path, {
1794 'foo' : Item(status='A '),
1796 expected_disk = wc.State('', {
1797 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B/F:2'}),
1798 'Q' : Item("foo"),
1799 'foo' : Item("foo"),
1801 expected_status = wc.State(short_C_path, {
1802 '' : Item(status=' M', wc_rev=1),
1803 'foo' : Item(status='A ', wc_rev='-', copied='+'),
1805 expected_skip = wc.State(short_C_path, {
1806 'Q' : Item(),
1807 'Q/bar' : Item(),
1810 saved_cwd = os.getcwd()
1812 os.chdir(svntest.main.work_dir)
1813 svntest.actions.run_and_verify_merge(short_C_path, '1', '2', F_url,
1814 expected_output,
1815 expected_disk,
1816 expected_status,
1817 expected_skip,
1818 None, None, None, None, None,
1819 1, 0)
1821 os.chdir(saved_cwd)
1823 # Revert the local mods, and commit the deletion of iota and A/D/G. (r3)
1824 os.unlink(os.path.join(C_path, "foo"))
1825 svntest.actions.run_and_verify_svn(None, None, [], 'revert', '-R', wc_dir)
1826 svntest.actions.run_and_verify_status(wc_dir, pre_merge_status)
1828 iota_path = os.path.join(wc_dir, 'iota')
1829 G_path = os.path.join(wc_dir, 'A', 'D', 'G')
1830 svntest.actions.run_and_verify_svn(None, None, [], 'rm', iota_path, G_path)
1832 expected_output = wc.State(wc_dir, {
1833 'A/D/G' : Item(verb='Deleting'),
1834 'iota' : Item(verb='Deleting'),
1836 expected_status = pre_merge_status
1837 expected_status.remove('iota', 'A/D/G', 'A/D/G/pi', 'A/D/G/rho', 'A/D/G/tau')
1838 svntest.actions.run_and_verify_commit(wc_dir,
1839 expected_output,
1840 expected_status,
1841 None, wc_dir)
1843 # Now create unversioned iota and A/D/G, try running a merge -r2:3.
1844 # The merge process should skip over these targets, since they're
1845 # unversioned.
1847 # Note: This merge, and all subsequent merges within this test,
1848 # skip *all* targets, so no mergeinfo is set.
1850 # Search for the comment entitled "The Merge Kluge" elsewhere in
1851 # this file, to understand why we shorten and chdir() below.
1852 short_wc_dir = shorten_path_kludge(wc_dir)
1854 svntest.main.file_append(iota_path, "foo") # unversioned
1855 os.mkdir(G_path) # unversioned
1857 expected_output = wc.State(short_wc_dir, { })
1858 expected_disk = svntest.main.greek_state.copy()
1859 expected_disk.remove('A/D/G/pi', 'A/D/G/rho', 'A/D/G/tau')
1860 expected_disk.add({
1861 'A/B/F/Q' : Item(),
1862 'A/B/F/Q/bar' : Item("bar"),
1863 'A/B/F/foo' : Item("foo"),
1864 'iota' : Item("foo"),
1865 'A/C/Q' : Item("foo"),
1867 expected_skip = wc.State(short_wc_dir, {
1868 'A/D/G' : Item(),
1869 'iota' : Item(),
1872 saved_cwd = os.getcwd()
1874 os.chdir(svntest.main.work_dir)
1875 svntest.actions.run_and_verify_merge(short_wc_dir, '2', '3',
1876 sbox.repo_url,
1877 expected_output,
1878 expected_disk,
1879 expected_status.copy(short_wc_dir),
1880 expected_skip,
1881 None, None, None, None, None,
1882 1, 0)
1884 os.chdir(saved_cwd)
1886 # Revert the local mods, and commit a change to A/B/lambda (r4), and then
1887 # commit the deletion of the same file. (r5)
1888 os.unlink(iota_path)
1889 svntest.main.safe_rmtree(G_path)
1890 svntest.actions.run_and_verify_svn(None, None, [], 'revert', '-R', wc_dir)
1891 svntest.actions.run_and_verify_status(wc_dir, expected_status)
1893 lambda_path = os.path.join(wc_dir, 'A', 'B', 'lambda')
1894 svntest.main.file_append(lambda_path, "more text")
1895 expected_output = wc.State(wc_dir, {
1896 'A/B/lambda' : Item(verb='Sending'),
1898 expected_status.tweak('A/B/lambda', wc_rev=4)
1899 svntest.actions.run_and_verify_commit(wc_dir,
1900 expected_output,
1901 expected_status,
1902 None, wc_dir)
1904 svntest.actions.run_and_verify_svn(None, None, [], 'rm', lambda_path)
1906 expected_output = wc.State(wc_dir, {
1907 'A/B/lambda' : Item(verb='Deleting'),
1909 expected_status.remove('A/B/lambda')
1910 svntest.actions.run_and_verify_commit(wc_dir,
1911 expected_output,
1912 expected_status,
1913 None, wc_dir)
1915 # lambda is gone, so create an unversioned lambda in its place.
1916 # Then attempt to merge -r3:4, which is a change to lambda. The merge
1917 # should simply skip the unversioned file.
1919 svntest.main.file_append(lambda_path, "foo") # unversioned
1921 # Search for the comment entitled "The Merge Kluge" elsewhere in
1922 # this file, to understand why we use short_wc_dir and chdir() below.
1923 expected_output = wc.State(short_wc_dir, { })
1924 expected_disk.add({
1925 'A/B/lambda' : Item("foo"),
1927 expected_disk.remove('A/D/G', 'iota')
1928 expected_skip = wc.State(short_wc_dir, {
1929 'A/B/lambda' : Item(),
1932 saved_cwd = os.getcwd()
1934 os.chdir(svntest.main.work_dir)
1935 svntest.actions.run_and_verify_merge(short_wc_dir, '3', '4',
1936 sbox.repo_url,
1937 expected_output,
1938 expected_disk,
1939 expected_status.copy(short_wc_dir),
1940 expected_skip,
1941 None, None, None, None, None,
1942 1, 0)
1944 os.chdir(saved_cwd)
1946 # OK, so let's commit the new lambda (r6), and then delete the
1947 # working file. Then re-run the -r3:4 merge, and see how svn deals
1948 # with a file being under version control, but missing.
1950 svntest.actions.run_and_verify_svn(None, None, [], 'add', lambda_path)
1952 expected_output = wc.State(wc_dir, {
1953 'A/B/lambda' : Item(verb='Adding'),
1955 expected_status.add({
1956 'A/B/lambda' : Item(wc_rev=6, status=' '),
1958 svntest.actions.run_and_verify_commit(wc_dir,
1959 expected_output,
1960 expected_status,
1961 None, wc_dir)
1963 os.unlink(lambda_path)
1965 # Search for the comment entitled "The Merge Kluge" elsewhere in
1966 # this file, to understand why we use short_wc_dir and chdir() below.
1967 expected_output = wc.State(short_wc_dir, { })
1968 expected_disk.remove('A/B/lambda')
1969 expected_status.tweak('A/B/lambda', status='! ')
1971 os.chdir(svntest.main.work_dir)
1972 svntest.actions.run_and_verify_merge(short_wc_dir, '3', '4',
1973 sbox.repo_url,
1974 expected_output,
1975 expected_disk,
1976 expected_status.copy(short_wc_dir),
1977 expected_skip,
1978 None, None, None, None, None,
1979 1, 0)
1981 #----------------------------------------------------------------------
1982 # At one time, a merge that added items with the same name as missing
1983 # items would attempt to add the items and fail, leaving the working
1984 # copy locked and broken.
1986 def merge_into_missing(sbox):
1987 "merge into missing must not break working copy"
1989 sbox.build()
1990 wc_dir = sbox.wc_dir
1992 F_path = os.path.join(wc_dir, 'A', 'B', 'F')
1993 F_url = sbox.repo_url + '/A/B/F'
1994 Q_path = os.path.join(F_path, 'Q')
1995 foo_path = os.path.join(F_path, 'foo')
1997 svntest.actions.run_and_verify_svn(None, None, [], 'mkdir', Q_path)
1998 svntest.main.file_append(foo_path, "foo")
1999 svntest.actions.run_and_verify_svn(None, None, [], 'add', foo_path)
2001 expected_output = wc.State(wc_dir, {
2002 'A/B/F/Q' : Item(verb='Adding'),
2003 'A/B/F/foo' : Item(verb='Adding'),
2005 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
2006 expected_status.add({
2007 'A/B/F/Q' : Item(status=' ', wc_rev=2),
2008 'A/B/F/foo' : Item(status=' ', wc_rev=2),
2010 svntest.actions.run_and_verify_commit(wc_dir,
2011 expected_output,
2012 expected_status,
2013 None, wc_dir)
2015 R_path = os.path.join(Q_path, 'R')
2016 bar_path = os.path.join(R_path, 'bar')
2017 baz_path = os.path.join(Q_path, 'baz')
2018 svntest.actions.run_and_verify_svn(None, None, [], 'mkdir', R_path)
2019 svntest.main.file_append(bar_path, "bar")
2020 svntest.actions.run_and_verify_svn(None, None, [], 'add', bar_path)
2021 svntest.main.file_append(baz_path, "baz")
2022 svntest.actions.run_and_verify_svn(None, None, [], 'add', baz_path)
2024 expected_output = wc.State(wc_dir, {
2025 'A/B/F/Q/R' : Item(verb='Adding'),
2026 'A/B/F/Q/R/bar' : Item(verb='Adding'),
2027 'A/B/F/Q/baz' : Item(verb='Adding'),
2029 expected_status.add({
2030 'A/B/F/Q/R' : Item(status=' ', wc_rev=3),
2031 'A/B/F/Q/R/bar' : Item(status=' ', wc_rev=3),
2032 'A/B/F/Q/baz' : Item(status=' ', wc_rev=3),
2034 svntest.actions.run_and_verify_commit(wc_dir,
2035 expected_output,
2036 expected_status,
2037 None, wc_dir)
2039 os.unlink(foo_path)
2040 svntest.main.safe_rmtree(Q_path)
2042 expected_output = wc.State(F_path, {
2044 expected_disk = wc.State('', {
2046 expected_status = wc.State(F_path, {
2047 '' : Item(status=' ', wc_rev=1),
2048 'foo' : Item(status='! ', wc_rev=2),
2049 'Q' : Item(status='! ', wc_rev='?'),
2051 expected_skip = wc.State(F_path, {
2052 'Q' : Item(),
2053 'foo' : Item(),
2056 ### Need to real and dry-run separately since real merge notifies Q
2057 ### twice!
2058 svntest.actions.run_and_verify_merge(F_path, '1', '2', F_url,
2059 expected_output,
2060 expected_disk,
2061 expected_status,
2062 expected_skip,
2063 None, None, None, None, None,
2064 0, 0, '--dry-run')
2066 svntest.actions.run_and_verify_merge(F_path, '1', '2', F_url,
2067 expected_output,
2068 expected_disk,
2069 expected_status,
2070 expected_skip,
2071 None, None, None, None, None,
2072 0, 0)
2074 # This merge fails when it attempts to descend into the missing
2075 # directory. That's OK, there is no real need to support merge into
2076 # an incomplete working copy, so long as when it fails it doesn't
2077 # break the working copy.
2078 svntest.main.run_svn('Working copy not locked',
2079 'merge', '-r1:3', '--dry-run', F_url, F_path)
2081 svntest.main.run_svn('Working copy not locked',
2082 'merge', '-r1:3', F_url, F_path)
2084 # Check working copy is not locked.
2085 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
2086 expected_status.add({
2087 'A/B/F/foo' : Item(status='! ', wc_rev=2),
2088 'A/B/F/Q' : Item(status='! ', wc_rev='?'),
2090 svntest.actions.run_and_verify_status(wc_dir, expected_status)
2092 #----------------------------------------------------------------------
2093 # A test for issue 1738
2095 def dry_run_adds_file_with_prop(sbox):
2096 "merge --dry-run adding a new file with props"
2098 sbox.build()
2099 wc_dir = sbox.wc_dir
2101 # Commit a new file which has a property.
2102 zig_path = os.path.join(wc_dir, 'A', 'B', 'E', 'zig')
2103 svntest.main.file_append(zig_path, "zig contents")
2104 svntest.actions.run_and_verify_svn(None, None, [], 'add', zig_path)
2105 svntest.actions.run_and_verify_svn(None, None, [],
2106 'propset', 'foo', 'foo_val',
2107 zig_path)
2109 expected_output = wc.State(wc_dir, {
2110 'A/B/E/zig' : Item(verb='Adding'),
2112 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
2113 expected_status.add({
2114 'A/B/E/zig' : Item(status=' ', wc_rev=2),
2116 svntest.actions.run_and_verify_commit(wc_dir,
2117 expected_output,
2118 expected_status,
2119 None, wc_dir)
2121 # Do a regular merge of that change into a different dir.
2122 F_path = os.path.join(wc_dir, 'A', 'B', 'F')
2123 E_url = sbox.repo_url + '/A/B/E'
2125 # Search for the comment entitled "The Merge Kluge" elsewhere in
2126 # this file, to understand why we shorten and chdir() below.
2127 short_F_path = shorten_path_kludge(F_path)
2129 expected_output = wc.State(short_F_path, {
2130 'zig' : Item(status='A '),
2132 expected_disk = wc.State('', {
2133 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B/E:2'}),
2134 'zig' : Item("zig contents", {'foo':'foo_val'}),
2136 expected_skip = wc.State('', { })
2137 expected_status = None # status is optional
2139 os.chdir(svntest.main.work_dir)
2140 svntest.actions.run_and_verify_merge(short_F_path, '1', '2', E_url,
2141 expected_output,
2142 expected_disk,
2143 expected_status,
2144 expected_skip,
2145 None, None, None, None, None,
2146 1, # please check props
2147 1) # and do a dry-run also)
2149 #----------------------------------------------------------------------
2151 # Regression test for issue #1673
2152 # Merge a binary file from two URL with a common ancestry
2154 def merge_binary_with_common_ancestry(sbox):
2155 "merge binary files with common ancestry"
2157 sbox.build()
2158 wc_dir = sbox.wc_dir
2160 # Create the common ancestry path
2161 I_path = os.path.join(wc_dir, 'I')
2162 svntest.main.run_svn(None, 'mkdir', I_path)
2164 # Add a binary file to the common ancestry path
2165 theta_contents = svntest.main.file_read(
2166 os.path.join(sys.path[0], "theta.bin"), 'rb')
2167 theta_I_path = os.path.join(I_path, 'theta')
2168 svntest.main.file_write(theta_I_path, theta_contents)
2169 svntest.main.run_svn(None, 'add', theta_I_path)
2170 svntest.main.run_svn(None, 'propset', 'svn:mime-type',
2171 'application/octet-stream', theta_I_path)
2173 # Commit the ancestry
2174 expected_output = wc.State(wc_dir, {
2175 'I' : Item(verb='Adding'),
2176 'I/theta' : Item(verb='Adding (bin)'),
2179 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
2180 expected_status.add({
2181 'I' : Item(status=' ', wc_rev=2),
2182 'I/theta' : Item(status=' ', wc_rev=2),
2185 svntest.actions.run_and_verify_commit(wc_dir,
2186 expected_output, expected_status,
2187 None,
2188 wc_dir)
2190 # Create the first branch
2191 J_path = os.path.join(wc_dir, 'J')
2192 svntest.main.run_svn(None, 'copy', I_path, J_path)
2194 # Commit the first branch
2195 expected_output = wc.State(wc_dir, {
2196 'J' : Item(verb='Adding'),
2199 expected_status.add({
2200 'J' : Item(status=' ', wc_rev=3),
2201 'J/theta' : Item(status=' ', wc_rev=3),
2204 svntest.actions.run_and_verify_commit(wc_dir,
2205 expected_output, expected_status,
2206 None,
2207 wc_dir)
2209 # Create the path where the files will be merged
2210 K_path = os.path.join(wc_dir, 'K')
2211 svntest.main.run_svn(None, 'mkdir', K_path)
2213 # Commit the new path
2214 expected_output = wc.State(wc_dir, {
2215 'K' : Item(verb='Adding'),
2218 expected_status.add({
2219 'K' : Item(status=' ', wc_rev=4),
2222 svntest.actions.run_and_verify_commit(wc_dir,
2223 expected_output, expected_status,
2224 None,
2225 wc_dir)
2227 # Copy 'I/theta' to 'K/'. This file will be merged later.
2228 theta_K_path = os.path.join(K_path, 'theta')
2229 svntest.main.run_svn(None, 'copy', theta_I_path, theta_K_path)
2231 # Commit the new file
2232 expected_output = wc.State(wc_dir, {
2233 'K/theta' : Item(verb='Adding (bin)'),
2236 expected_status.add({
2237 'K/theta' : Item(status=' ', wc_rev=5),
2240 svntest.actions.run_and_verify_commit(wc_dir,
2241 expected_output, expected_status,
2242 None,
2243 wc_dir)
2245 # Modify the original ancestry 'I/theta'
2246 svntest.main.file_append(theta_I_path, "some extra junk")
2248 # Commit the modification
2249 expected_output = wc.State(wc_dir, {
2250 'I/theta' : Item(verb='Sending'),
2253 expected_status.tweak('I/theta', wc_rev=6)
2255 svntest.actions.run_and_verify_commit(wc_dir,
2256 expected_output, expected_status,
2257 None,
2258 wc_dir)
2260 # Create the second branch from the modified ancestry
2261 L_path = os.path.join(wc_dir, 'L')
2262 svntest.main.run_svn(None, 'copy', I_path, L_path)
2264 # Commit the second branch
2265 expected_output = wc.State(wc_dir, {
2266 'L' : Item(verb='Adding'),
2267 'L/theta' : Item(verb='Adding (bin)'),
2270 expected_status.add({
2271 'L' : Item(status=' ', wc_rev=7),
2272 'L/theta' : Item(status=' ', wc_rev=7),
2275 svntest.actions.run_and_verify_commit(wc_dir,
2276 expected_output, expected_status,
2277 None,
2278 wc_dir)
2280 # Now merge first ('J/') and second ('L/') branches into 'K/'
2281 saved_cwd = os.getcwd()
2283 os.chdir(K_path)
2284 theta_J_url = sbox.repo_url + '/J/theta'
2285 theta_L_url = sbox.repo_url + '/L/theta'
2286 svntest.actions.run_and_verify_svn(None,
2287 expected_merge_output(None,
2288 'U theta\n'),
2290 'merge', theta_J_url, theta_L_url)
2291 os.chdir(saved_cwd)
2293 expected_status.tweak('K/theta', status='MM')
2294 svntest.actions.run_and_verify_status(wc_dir, expected_status)
2296 #----------------------------------------------------------------------
2297 # A test for issue 1905
2298 def merge_funny_chars_on_path(sbox):
2299 "merge with funny characters (issue #1905)"
2301 sbox.build()
2302 wc_dir = sbox.wc_dir
2304 # In following lists: 'd' stands for directory, 'f' for file
2305 # targets to be added by recursive add
2306 add_by_add = [
2307 ('d', 'dir_10', 'F%lename'),
2308 ('d', 'dir%20', 'F lename'),
2309 ('d', 'dir 30', 'Filename'),
2310 ('d', 'dir 40', None),
2311 ('f', 'F lename', None),
2314 # targets to be added by 'svn mkdir' + add
2315 add_by_mkdir = [
2316 ('d', 'dir_11', 'F%lename'),
2317 ('d', 'dir%21', 'Filename'),
2318 ('d', 'dir 31', 'F lename'),
2319 ('d', 'dir 41', None),
2322 for target in add_by_add:
2323 if target[0] == 'd':
2324 target_dir = os.path.join(wc_dir, 'A', 'B', 'E', target[1])
2325 os.mkdir(target_dir)
2326 if target[2]:
2327 target_path = os.path.join(wc_dir, 'A', 'B', 'E', '%s' % target[1],
2328 target[2])
2329 svntest.main.file_append(target_path, "%s/%s" % (target[1], target[2]))
2330 svntest.actions.run_and_verify_svn(None, None, [], 'add', target_dir)
2331 elif target[0] == 'f':
2332 target_path = os.path.join(wc_dir, 'A', 'B', 'E', '%s' % target[1])
2333 svntest.main.file_append(target_path, "%s" % target[1])
2334 svntest.actions.run_and_verify_svn(None, None, [], 'add', target_path)
2335 else:
2336 raise svntest.Failure
2339 for target in add_by_mkdir:
2340 if target[0] == 'd':
2341 target_dir = os.path.join(wc_dir, 'A', 'B', 'E', target[1])
2342 svntest.actions.run_and_verify_svn(None, None, [], 'mkdir', target_dir)
2343 if target[2]:
2344 target_path = os.path.join(wc_dir, 'A', 'B', 'E', '%s' % target[1],
2345 target[2])
2346 svntest.main.file_append(target_path, "%s/%s" % (target[1], target[2]))
2347 svntest.actions.run_and_verify_svn(None, None, [], 'add', target_path)
2349 expected_output_dic = {}
2350 expected_status_dic = {}
2352 for targets in add_by_add,add_by_mkdir:
2353 for target in targets:
2354 key = 'A/B/E/%s' % target[1]
2355 expected_output_dic[key] = Item(verb='Adding')
2356 expected_status_dic[key] = Item(status=' ', wc_rev=2)
2358 if target[2]:
2359 key = 'A/B/E/%s/%s' % (target[1], target[2])
2360 expected_output_dic[key] = Item(verb='Adding')
2361 expected_status_dic[key] = Item(status=' ', wc_rev=2)
2364 expected_output = wc.State(wc_dir, expected_output_dic)
2366 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
2367 expected_status.add(expected_status_dic)
2369 svntest.actions.run_and_verify_commit(wc_dir,
2370 expected_output,
2371 expected_status,
2372 None, wc_dir)
2374 # Do a regular merge of that change into a different dir.
2375 F_path = os.path.join(wc_dir, 'A', 'B', 'F')
2376 E_url = sbox.repo_url + '/A/B/E'
2378 expected_output_dic = {}
2379 expected_disk_dic = {}
2381 for targets in add_by_add,add_by_mkdir:
2382 for target in targets:
2383 key = '%s' % target[1]
2384 expected_output_dic[key] = Item(status='A ')
2385 if target[0] == 'd':
2386 expected_disk_dic[key] = Item(None, {})
2387 elif target[0] == 'f':
2388 expected_disk_dic[key] = Item("%s" % target[1], {})
2389 else:
2390 raise svntest.Failure
2391 if target[2]:
2392 key = '%s/%s' % (target[1], target[2])
2393 expected_output_dic[key] = Item(status='A ')
2394 expected_disk_dic[key] = Item('%s/%s' % (target[1], target[2]), {})
2397 # Search for the comment entitled "The Merge Kluge" elsewhere in
2398 # this file, to understand why we shorten and chdir() below.
2399 short_F_path = shorten_path_kludge(F_path)
2401 expected_output = wc.State(short_F_path, expected_output_dic)
2403 expected_disk = wc.State('', expected_disk_dic)
2404 expected_skip = wc.State('', { })
2405 expected_status = None # status is optional
2407 saved_cwd = os.getcwd()
2409 os.chdir(svntest.main.work_dir)
2410 svntest.actions.run_and_verify_merge(short_F_path, '1', '2', E_url,
2411 expected_output,
2412 expected_disk,
2413 expected_status,
2414 expected_skip,
2415 None, None, None, None, None,
2416 0, # don't check props
2417 1) # but do a dry-run
2418 os.chdir(saved_cwd)
2420 expected_output_dic = {}
2422 for targets in add_by_add,add_by_mkdir:
2423 for target in targets:
2424 key = '%s' % target[1]
2425 expected_output_dic[key] = Item(verb='Adding')
2426 if target[2]:
2427 key = '%s/%s' % (target[1], target[2])
2428 expected_output_dic[key] = Item(verb='Adding')
2430 expected_output = wc.State(F_path, expected_output_dic)
2431 expected_output.add({
2432 '' : Item(verb='Sending'),
2435 svntest.actions.run_and_verify_commit(F_path,
2436 expected_output,
2437 None,
2438 None, wc_dir)
2440 #-----------------------------------------------------------------------
2441 # Regression test for issue #2064
2443 def merge_keyword_expansions(sbox):
2444 "merge changes to keyword expansion property"
2446 sbox.build()
2448 wcpath = sbox.wc_dir
2449 tpath = os.path.join(wcpath, "t")
2450 bpath = os.path.join(wcpath, "b")
2451 t_fpath = os.path.join(tpath, 'f')
2452 b_fpath = os.path.join(bpath, 'f')
2454 os.mkdir(tpath)
2455 svntest.main.run_svn(None, "add", tpath)
2456 # Commit r2.
2457 svntest.actions.run_and_verify_svn(None, None, [],
2458 "ci", "-m", "r2", wcpath)
2460 # Copy t to b.
2461 svntest.main.run_svn(None, "cp", tpath, bpath)
2462 # Commit r3
2463 svntest.actions.run_and_verify_svn(None, None, [],
2464 "ci", "-m", "r3", wcpath)
2466 # Add a file to t.
2467 svntest.main.file_append(t_fpath, "$Revision$")
2468 svntest.actions.run_and_verify_svn(None, None, [],
2469 'add', t_fpath)
2470 # Ask for keyword expansion in the file.
2471 svntest.actions.run_and_verify_svn(None, None, [],
2472 'propset', 'svn:keywords', 'Revision',
2473 t_fpath)
2474 # Commit r4
2475 svntest.actions.run_and_verify_svn(None, None, [],
2476 'ci', '-m', 'r4', wcpath)
2478 # Update the wc before the merge.
2479 svntest.actions.run_and_verify_svn(None, None, [],
2480 'update', wcpath)
2482 expected_status = svntest.actions.get_virginal_state(wcpath, 4)
2483 expected_status.add({
2484 't' : Item(status=' ', wc_rev=4),
2485 't/f' : Item(status=' ', wc_rev=4),
2486 'b' : Item(status=' ', wc_rev=4),
2488 svntest.actions.run_and_verify_status(wcpath, expected_status)
2490 # Do the merge.
2492 # Search for the comment entitled "The Merge Kluge" elsewhere in
2493 # this file, to understand why we shorten and chdir() below.
2494 short_bpath = shorten_path_kludge(bpath)
2496 expected_output = wc.State(short_bpath, {
2497 'f' : Item(status='A '),
2499 expected_disk = wc.State('', {
2500 'f' : Item("$Revision: 4 $"),
2502 expected_status = wc.State(short_bpath, {
2503 '' : Item(status=' M', wc_rev=4),
2504 'f' : Item(status='A ', wc_rev='-', copied='+'),
2506 expected_skip = wc.State(short_bpath, { })
2508 os.chdir(svntest.main.work_dir)
2509 svntest.actions.run_and_verify_merge(short_bpath, '2', 'HEAD',
2510 sbox.repo_url + '/t',
2511 expected_output,
2512 expected_disk,
2513 expected_status,
2514 expected_skip)
2516 #----------------------------------------------------------------------
2517 def merge_prop_change_to_deleted_target(sbox):
2518 "merge prop change into deleted target"
2519 # For issue #2132.
2520 sbox.build()
2521 wc_dir = sbox.wc_dir
2523 # Add a property to alpha.
2524 alpha_path = os.path.join(wc_dir, 'A', 'B', 'E', 'alpha')
2525 svntest.actions.run_and_verify_svn(None, None, [],
2526 'propset', 'foo', 'foo_val',
2527 alpha_path)
2529 # Commit the property add as r2.
2530 expected_output = svntest.wc.State(wc_dir, {
2531 'A/B/E/alpha' : Item(verb='Sending'),
2533 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
2534 expected_status.tweak('A/B/E/alpha', wc_rev=2, status=' ')
2535 svntest.actions.run_and_verify_commit(wc_dir,
2536 expected_output, expected_status,
2537 None, wc_dir)
2538 svntest.actions.run_and_verify_svn(None, None, [],
2539 'up', wc_dir)
2541 # Remove alpha entirely.
2542 svntest.actions.run_and_verify_svn(None, None, [], 'rm', alpha_path)
2543 expected_output = wc.State(wc_dir, {
2544 'A/B/E/alpha' : Item(verb='Deleting'),
2546 expected_status.tweak(wc_rev=2)
2547 expected_status.remove('A/B/E/alpha')
2548 svntest.actions.run_and_verify_commit(wc_dir,
2549 expected_output,
2550 expected_status,
2551 None, alpha_path)
2553 # Try merging the original propset, which applies to a target that
2554 # no longer exists. The bug would only reproduce when run from
2555 # inside the wc, so we cd in there. We have to use
2556 # --ignore-ancestry here because our merge logic will otherwise
2557 # prevent a merge of changes we already have.
2558 os.chdir(wc_dir)
2559 svntest.actions.run_and_verify_svn("Merge errored unexpectedly",
2560 svntest.verify.AnyOutput, [], 'merge',
2561 '-r1:2', '--ignore-ancestry', '.')
2564 def set_up_dir_replace(sbox):
2565 """Set up the working copy for directory replace tests, creating
2566 directory 'A/B/F/foo' with files 'new file' and 'new file2' within
2567 it (r2), and merging 'foo' onto 'C' (r3), then deleting 'A/B/F/foo'
2568 (r4)."""
2570 sbox.build()
2571 wc_dir = sbox.wc_dir
2573 C_path = os.path.join(wc_dir, 'A', 'C')
2574 F_path = os.path.join(wc_dir, 'A', 'B', 'F')
2575 F_url = sbox.repo_url + '/A/B/F'
2577 foo_path = os.path.join(F_path, 'foo')
2578 new_file = os.path.join(foo_path, "new file")
2579 new_file2 = os.path.join(foo_path, "new file 2")
2581 # Make directory foo in F, and add some files within it.
2582 svntest.actions.run_and_verify_svn(None, None, [], 'mkdir', foo_path)
2583 svntest.main.file_append(new_file, "Initial text in new file.\n")
2584 svntest.main.file_append(new_file2, "Initial text in new file 2.\n")
2585 svntest.main.run_svn(None, "add", new_file)
2586 svntest.main.run_svn(None, "add", new_file2)
2588 # Commit all the new content, creating r2.
2589 expected_output = wc.State(wc_dir, {
2590 'A/B/F/foo' : Item(verb='Adding'),
2591 'A/B/F/foo/new file' : Item(verb='Adding'),
2592 'A/B/F/foo/new file 2' : Item(verb='Adding'),
2594 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
2595 expected_status.add({
2596 'A/B/F/foo' : Item(status=' ', wc_rev=2),
2597 'A/B/F/foo/new file' : Item(status=' ', wc_rev=2),
2598 'A/B/F/foo/new file 2' : Item(status=' ', wc_rev=2),
2600 svntest.actions.run_and_verify_commit(wc_dir,
2601 expected_output,
2602 expected_status,
2603 None, wc_dir)
2605 # Merge foo onto C
2606 expected_output = wc.State(C_path, {
2607 'foo' : Item(status='A '),
2608 'foo/new file' : Item(status='A '),
2609 'foo/new file 2' : Item(status='A '),
2611 expected_disk = wc.State('', {
2612 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B/F:2'}),
2613 'foo' : Item(),
2614 'foo/new file' : Item("Initial text in new file.\n"),
2615 'foo/new file 2' : Item("Initial text in new file 2.\n"),
2617 expected_status = wc.State(C_path, {
2618 '' : Item(status=' M', wc_rev=1),
2619 'foo' : Item(status='A ', wc_rev='-', copied='+'),
2620 'foo/new file' : Item(status='A ', wc_rev='-', copied='+'),
2621 'foo/new file 2' : Item(status='A ', wc_rev='-', copied='+'),
2623 expected_skip = wc.State(C_path, { })
2624 svntest.actions.run_and_verify_merge(C_path, '1', '2', F_url,
2625 expected_output,
2626 expected_disk,
2627 expected_status,
2628 expected_skip,
2629 None, None, None, None, None, 1)
2630 # Commit merge of foo onto C, creating r3.
2631 expected_output = svntest.wc.State(wc_dir, {
2632 'A/C' : Item(verb='Sending'),
2633 'A/C/foo' : Item(verb='Adding'),
2634 'A/C/foo/new file' : Item(verb='Adding'),
2635 'A/C/foo/new file 2' : Item(verb='Adding'),
2637 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
2638 expected_status.add({
2639 'A/B/F/foo' : Item(status=' ', wc_rev=2),
2640 'A/C' : Item(status=' ', wc_rev=3),
2641 'A/B/F/foo/new file' : Item(status=' ', wc_rev=2),
2642 'A/B/F/foo/new file 2' : Item(status=' ', wc_rev=2),
2643 'A/C/foo' : Item(status=' ', wc_rev=3),
2644 'A/C/foo/new file' : Item(status=' ', wc_rev=3),
2645 'A/C/foo/new file 2' : Item(status=' ', wc_rev=3),
2648 svntest.actions.run_and_verify_commit(wc_dir,
2649 expected_output,
2650 expected_status,
2651 None, wc_dir)
2653 # Delete foo on F, creating r4.
2654 svntest.actions.run_and_verify_svn(None, None, [], 'rm', foo_path)
2655 expected_output = svntest.wc.State(wc_dir, {
2656 'A/B/F/foo' : Item(verb='Deleting'),
2658 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
2659 expected_status.add({
2660 'A/C' : Item(status=' ', wc_rev=3),
2661 'A/C/foo' : Item(status=' ', wc_rev=3),
2662 'A/C/foo/new file' : Item(status=' ', wc_rev=3),
2663 'A/C/foo/new file 2' : Item(status=' ', wc_rev=3),
2665 svntest.actions.run_and_verify_commit(wc_dir,
2666 expected_output,
2667 expected_status,
2668 None, wc_dir)
2670 #----------------------------------------------------------------------
2671 # A merge that replaces a directory
2672 # Tests for Issue #2144 and Issue #2607
2674 def merge_dir_replace(sbox):
2675 "merge a replacement of a directory"
2677 set_up_dir_replace(sbox)
2678 wc_dir = sbox.wc_dir
2680 C_path = os.path.join(wc_dir, 'A', 'C')
2681 F_path = os.path.join(wc_dir, 'A', 'B', 'F')
2682 F_url = sbox.repo_url + '/A/B/F'
2683 foo_path = os.path.join(F_path, 'foo')
2684 new_file2 = os.path.join(foo_path, "new file 2")
2686 # Recreate foo in F and add a new folder and two files
2687 bar_path = os.path.join(foo_path, 'bar')
2688 foo_file = os.path.join(foo_path, "file foo")
2689 new_file3 = os.path.join(bar_path, "new file 3")
2691 # Make a couple of directories, and add some files within them.
2692 svntest.actions.run_and_verify_svn(None, None, [], 'mkdir', foo_path)
2693 svntest.actions.run_and_verify_svn(None, None, [], 'mkdir', bar_path)
2694 svntest.main.file_append(new_file3, "Initial text in new file 3.\n")
2695 svntest.main.run_svn(None, "add", new_file3)
2696 svntest.main.file_append(foo_file, "Initial text in file foo.\n")
2697 svntest.main.run_svn(None, "add", foo_file)
2699 # Commit the new content, creating r5.
2700 expected_output = wc.State(wc_dir, {
2701 'A/B/F/foo' : Item(verb='Adding'),
2702 'A/B/F/foo/file foo' : Item(verb='Adding'),
2703 'A/B/F/foo/bar' : Item(verb='Adding'),
2704 'A/B/F/foo/bar/new file 3' : Item(verb='Adding'),
2706 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
2707 expected_status.add({
2708 'A/B/F/foo' : Item(status=' ', wc_rev=5),
2709 'A/B/F/foo/file foo' : Item(status=' ', wc_rev=5),
2710 'A/B/F/foo/bar' : Item(status=' ', wc_rev=5),
2711 'A/B/F/foo/bar/new file 3' : Item(status=' ', wc_rev=5),
2712 'A/C' : Item(status=' ', wc_rev=3),
2713 'A/C/foo' : Item(status=' ', wc_rev=3),
2714 'A/C/foo/new file' : Item(status=' ', wc_rev=3),
2715 'A/C/foo/new file 2' : Item(status=' ', wc_rev=3),
2717 svntest.actions.run_and_verify_commit(wc_dir,
2718 expected_output,
2719 expected_status,
2720 None, wc_dir)
2721 # Merge replacement of foo onto C
2722 expected_output = wc.State(C_path, {
2723 'foo' : Item(status='R '),
2724 'foo/file foo' : Item(status='A '),
2725 'foo/bar' : Item(status='A '),
2726 'foo/bar/new file 3' : Item(status='A '),
2728 expected_disk = wc.State('', {
2729 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B/F:2-5'}),
2730 'foo' : Item(),
2731 'foo/file foo' : Item("Initial text in file foo.\n"),
2732 'foo/bar' : Item(),
2733 'foo/bar/new file 3' : Item("Initial text in new file 3.\n"),
2735 expected_status = wc.State(C_path, {
2736 '' : Item(status=' M', wc_rev=3),
2737 'foo' : Item(status='R ', wc_rev='-', copied='+'),
2738 'foo/new file 2' : Item(status='D ', wc_rev='-', copied='+'),
2739 'foo/file foo' : Item(status='A ', wc_rev='-', copied='+'),
2740 'foo/bar' : Item(status='A ', wc_rev='-', copied='+'),
2741 'foo/bar/new file 3' : Item(status='A ', wc_rev='-', copied='+'),
2742 'foo/new file' : Item(status='D ', wc_rev='-', copied='+'),
2744 expected_skip = wc.State(C_path, { })
2745 svntest.actions.run_and_verify_merge(C_path, '2', '5', F_url,
2746 expected_output,
2747 expected_disk,
2748 expected_status,
2749 expected_skip,
2750 None, None, None, None, None,
2752 0) # don't do a dry-run
2753 # the output differs
2755 # Commit merge of foo onto C
2756 expected_output = svntest.wc.State(wc_dir, {
2757 'A/C' : Item(verb='Sending'),
2758 'A/C/foo' : Item(verb='Replacing'),
2759 'A/C/foo/file foo' : Item(verb='Adding'),
2760 'A/C/foo/bar' : Item(verb='Adding'),
2761 'A/C/foo/bar/new file 3' : Item(verb='Adding'),
2762 'A/C/foo/new file' : Item(verb='Deleting'),
2763 'A/C/foo/new file 2' : Item(verb='Deleting'),
2765 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
2766 expected_status.add({
2767 'A/B/F/foo' : Item(status=' ', wc_rev=5),
2768 'A/B/F/foo/file foo' : Item(status=' ', wc_rev=5),
2769 'A/B/F/foo/bar' : Item(status=' ', wc_rev=5),
2770 'A/B/F/foo/bar/new file 3' : Item(status=' ', wc_rev=5),
2771 'A/C' : Item(status=' ', wc_rev=6),
2772 'A/C/foo' : Item(status=' ', wc_rev=6),
2773 'A/C/foo/file foo' : Item(status=' ', wc_rev=6),
2774 'A/C/foo/bar' : Item(status=' ', wc_rev=6),
2775 'A/C/foo/bar/new file 3' : Item(status=' ', wc_rev=6),
2777 svntest.actions.run_and_verify_commit(wc_dir,
2778 expected_output,
2779 expected_status,
2780 None, wc_dir)
2782 #----------------------------------------------------------------------
2783 # A merge that replaces a directory and one of its children
2784 # Tests for Issue #2690
2786 def merge_dir_and_file_replace(sbox):
2787 "replace both dir and one of its children"
2789 set_up_dir_replace(sbox)
2790 wc_dir = sbox.wc_dir
2792 C_path = os.path.join(wc_dir, 'A', 'C')
2793 F_path = os.path.join(wc_dir, 'A', 'B', 'F')
2794 F_url = sbox.repo_url + '/A/B/F'
2795 foo_path = os.path.join(F_path, 'foo')
2796 new_file2 = os.path.join(foo_path, "new file 2")
2798 # Recreate foo and 'new file 2' in F and add a new folder with a file
2799 bar_path = os.path.join(foo_path, 'bar')
2800 new_file3 = os.path.join(bar_path, "new file 3")
2801 svntest.actions.run_and_verify_svn(None, None, [], 'mkdir', foo_path)
2802 svntest.actions.run_and_verify_svn(None, None, [], 'mkdir', bar_path)
2803 svntest.main.file_append(new_file3, "Initial text in new file 3.\n")
2804 svntest.main.run_svn(None, "add", new_file3)
2805 svntest.main.file_append(new_file2, "New text in new file 2.\n")
2806 svntest.main.run_svn(None, "add", new_file2)
2808 expected_output = wc.State(wc_dir, {
2809 'A/B/F/foo' : Item(verb='Adding'),
2810 'A/B/F/foo/new file 2' : Item(verb='Adding'),
2811 'A/B/F/foo/bar' : Item(verb='Adding'),
2812 'A/B/F/foo/bar/new file 3' : Item(verb='Adding'),
2814 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
2815 expected_status.add({
2816 'A/B/F/foo' : Item(status=' ', wc_rev=5),
2817 'A/B/F/foo/new file 2' : Item(status=' ', wc_rev=5),
2818 'A/B/F/foo/bar' : Item(status=' ', wc_rev=5),
2819 'A/B/F/foo/bar/new file 3' : Item(status=' ', wc_rev=5),
2820 'A/C/foo' : Item(status=' ', wc_rev=3),
2821 'A/C/foo/new file' : Item(status=' ', wc_rev=3),
2822 'A/C/foo/new file 2' : Item(status=' ', wc_rev=3),
2824 svntest.actions.run_and_verify_commit(wc_dir,
2825 expected_output,
2826 expected_status,
2827 None, wc_dir)
2828 # Merge replacement of foo onto C
2829 expected_output = wc.State(C_path, {
2830 'foo' : Item(status='D '),
2831 'foo' : Item(status='A '),
2832 'foo/new file 2' : Item(status='D '),
2833 'foo/new file 2' : Item(status='A '),
2834 'foo/bar' : Item(status='A '),
2835 'foo/bar/new file 3' : Item(status='A '),
2836 'foo/new file' : Item(status='D '),
2838 expected_disk = wc.State('', {
2839 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B/F:2-5'}),
2840 'foo' : Item(),
2841 'foo/new file 2' : Item("New text in new file 2.\n"),
2842 'foo/bar' : Item(),
2843 'foo/bar/new file 3' : Item("Initial text in new file 3.\n"),
2845 expected_status = wc.State(C_path, {
2846 '' : Item(status=' ', wc_rev=1),
2847 'foo' : Item(status='R ', wc_rev='-', copied='+'),
2848 'foo/new file 2' : Item(status='R ', wc_rev='-', copied='+'),
2849 'foo/bar' : Item(status='A ', wc_rev='-', copied='+'),
2850 'foo/bar/new file 3' : Item(status='A ', wc_rev='-', copied='+'),
2851 'foo/new file' : Item(status='D ', wc_rev='-', copied='+'),
2853 expected_skip = wc.State(C_path, { })
2854 svntest.actions.run_and_verify_merge(C_path, '2', '5', F_url,
2855 expected_output,
2856 expected_disk,
2857 expected_status,
2858 expected_skip,
2859 None, None, None, None, None,
2861 0) # don't do a dry-run
2862 # the output differs
2864 # Commit merge of foo onto C
2865 expected_output = svntest.wc.State(wc_dir, {
2866 'A/C/foo' : Item(verb='Replacing'),
2867 'A/C/foo/new file 2' : Item(verb='Replacing'),
2868 'A/C/foo/new file' : Item(verb='Deleting'),
2869 'A/C/foo/bar' : Item(verb='Adding'),
2870 'A/C/foo/bar/new file 3' : Item(verb='Adding'),
2873 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
2874 expected_status.add({
2875 'A/B/F/foo' : Item(status=' ', wc_rev=5),
2876 'A/B/F/foo/new file 2' : Item(status=' ', wc_rev=5),
2877 'A/B/F/foo/bar' : Item(status=' ', wc_rev=5),
2878 'A/B/F/foo/bar/new file 3' : Item(status=' ', wc_rev=5),
2879 'A/C/foo' : Item(status=' ', wc_rev=6),
2880 'A/C/foo/new file 2' : Item(status=' ', wc_rev=6),
2881 'A/C/foo/bar' : Item(status=' ', wc_rev=6),
2882 'A/C/foo/bar/new file 3' : Item(status=' ', wc_rev=6),
2884 svntest.actions.run_and_verify_commit(wc_dir,
2885 expected_output,
2886 expected_status,
2887 None, wc_dir)
2889 #----------------------------------------------------------------------
2890 def merge_file_with_space_in_its_name(sbox):
2891 "merge a file whose name contains a space"
2892 # For issue #2144
2893 sbox.build()
2894 wc_dir = sbox.wc_dir
2895 new_file = os.path.join(wc_dir, "new file")
2897 # Make r2.
2898 svntest.main.file_append(new_file, "Initial text in the file.\n")
2899 svntest.main.run_svn(None, "add", new_file)
2900 svntest.actions.run_and_verify_svn(None, None, [],
2901 "ci", "-m", "r2", wc_dir)
2903 # Make r3.
2904 svntest.main.file_append(new_file, "Next line of text in the file.\n")
2905 svntest.actions.run_and_verify_svn(None, None, [],
2906 "ci", "-m", "r3", wc_dir)
2908 # Try to reverse merge.
2910 # The reproduction recipe requires that no explicit merge target be
2911 # passed, so we run merge from inside the wc dir where the target
2912 # file (i.e., the URL basename) lives.
2913 os.chdir(wc_dir)
2914 target_url = sbox.repo_url + '/new%20file'
2915 svntest.actions.run_and_verify_svn(None, None, [],
2916 "merge", "-r3:2", target_url)
2918 #----------------------------------------------------------------------
2919 # A merge between two branches using no revision number with the dir being
2920 # created already existing as an unversioned directory.
2921 # Tests for Issue #2222
2923 def merge_dir_branches(sbox):
2924 "merge between branches (Issue #2222)"
2926 sbox.build()
2927 wc_dir = sbox.wc_dir
2929 F_path = os.path.join(wc_dir, 'A', 'B', 'F')
2930 F_url = sbox.repo_url + '/A/B/F'
2931 C_url = sbox.repo_url + '/A/C'
2933 # Create foo in F
2934 foo_path = os.path.join(F_path, 'foo')
2935 svntest.actions.run_and_verify_svn(None, None, [], 'mkdir', foo_path)
2937 expected_output = wc.State(wc_dir, {
2938 'A/B/F/foo' : Item(verb='Adding'),
2940 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
2941 expected_status.add({
2942 'A/B/F/foo' : Item(status=' ', wc_rev=2),
2944 svntest.actions.run_and_verify_commit(wc_dir,
2945 expected_output,
2946 expected_status,
2947 None, wc_dir)
2949 # Create an unversioned foo
2950 foo_path = os.path.join(wc_dir, 'foo')
2951 os.mkdir(foo_path)
2953 # Merge from C to F onto the wc_dir
2954 # We can't use run_and_verify_merge because it doesn't support this
2955 # syntax of the merge command.
2956 ### TODO: We can use run_and_verify_merge2() here now.
2957 expected_output = expected_merge_output(None, "A " + foo_path + "\n")
2958 svntest.actions.run_and_verify_svn(None, expected_output, [],
2959 'merge', C_url, F_url, wc_dir)
2961 # Run info to check the copied rev to make sure it's right
2962 expected_output = ["Path: " + foo_path + "\n",
2963 "URL: " + sbox.repo_url + "/foo\n",
2964 "Repository Root: " + sbox.repo_url + "\n",
2965 "Revision: 2\n",
2966 "Node Kind: directory\n",
2967 "Schedule: add\n",
2968 "Copied From URL: " + F_url + "/foo\n",
2969 "Copied From Rev: 2\n", "\n"]
2970 svntest.actions.run_and_verify_svn(None, expected_output, [],
2971 'info', foo_path)
2974 #----------------------------------------------------------------------
2976 def safe_property_merge(sbox):
2977 "property merges don't overwrite existing prop-mods"
2979 sbox.build()
2980 wc_dir = sbox.wc_dir
2982 # Add a property to two files and a directory, commit as r2.
2983 alpha_path = os.path.join(wc_dir, 'A', 'B', 'E', 'alpha')
2984 beta_path = os.path.join(wc_dir, 'A', 'B', 'E', 'beta')
2985 E_path = os.path.join(wc_dir, 'A', 'B', 'E')
2987 svntest.actions.run_and_verify_svn(None, None, [],
2988 'propset', 'foo', 'foo_val',
2989 alpha_path, beta_path)
2990 svntest.actions.run_and_verify_svn(None, None, [],
2991 'propset', 'foo', 'foo_val',
2992 E_path)
2994 expected_output = svntest.wc.State(wc_dir, {
2995 'A/B/E' : Item(verb='Sending'),
2996 'A/B/E/alpha' : Item(verb='Sending'),
2997 'A/B/E/beta' : Item(verb='Sending'),
2999 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
3000 expected_status.tweak('A/B/E', 'A/B/E/alpha', 'A/B/E/beta',
3001 wc_rev=2, status=' ')
3002 svntest.actions.run_and_verify_commit(wc_dir,
3003 expected_output, expected_status,
3004 None, wc_dir)
3005 svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
3007 # Copy B to B2 as rev 3 (making a branch)
3008 B_url = sbox.repo_url + '/A/B'
3009 B2_url = sbox.repo_url + '/A/B2'
3011 svntest.actions.run_and_verify_svn(None, None, [],
3012 'copy', '-m', 'copy B to B2',
3013 B_url, B2_url)
3014 svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
3016 # Change the properties underneath B again, and commit as r4
3017 svntest.actions.run_and_verify_svn(None, None, [],
3018 'propset', 'foo', 'foo_val2',
3019 alpha_path)
3020 svntest.actions.run_and_verify_svn(None, None, [],
3021 'propdel', 'foo',
3022 beta_path)
3023 svntest.actions.run_and_verify_svn(None, None, [],
3024 'propset', 'foo', 'foo_val2',
3025 E_path)
3026 expected_output = svntest.wc.State(wc_dir, {
3027 'A/B/E' : Item(verb='Sending'),
3028 'A/B/E/alpha' : Item(verb='Sending'),
3029 'A/B/E/beta' : Item(verb='Sending'),
3031 svntest.actions.run_and_verify_commit(wc_dir,
3032 expected_output, None,
3033 None, wc_dir)
3034 svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
3036 # Make local propchanges to E, alpha and beta in the branch.
3037 alpha_path2 = os.path.join(wc_dir, 'A', 'B2', 'E', 'alpha')
3038 beta_path2 = os.path.join(wc_dir, 'A', 'B2', 'E', 'beta')
3039 E_path2 = os.path.join(wc_dir, 'A', 'B2', 'E')
3041 svntest.actions.run_and_verify_svn(None, None, [],
3042 'propset', 'foo', 'branchval',
3043 alpha_path2, beta_path2)
3044 svntest.actions.run_and_verify_svn(None, None, [],
3045 'propset', 'foo', 'branchval',
3046 E_path2)
3048 # Now merge the recent B change to the branch. Because we already
3049 # have local propmods, we should get property conflicts.
3050 B2_path = os.path.join(wc_dir, 'A', 'B2')
3052 expected_output = wc.State(B2_path, {
3053 'E' : Item(status=' C'),
3054 'E/alpha' : Item(status=' C'),
3055 'E/beta' : Item(status=' C'),
3058 expected_disk = wc.State('', {
3059 '' : Item(props={SVN_PROP_MERGEINFO : "/A/B:4"}),
3060 'E' : Item(),
3061 'E/alpha' : Item("This is the file 'alpha'.\n"),
3062 'E/beta' : Item("This is the file 'beta'.\n"),
3063 'F' : Item(),
3064 'lambda' : Item("This is the file 'lambda'.\n"),
3066 expected_disk.tweak('E', 'E/alpha', 'E/beta',
3067 props={'foo' : 'branchval'}) # local mods still present
3069 expected_status = wc.State(B2_path, {
3070 '' : Item(status=' M'),
3071 'E' : Item(status=' C'),
3072 'E/alpha' : Item(status=' C'),
3073 'E/beta' : Item(status=' C'),
3074 'F' : Item(status=' '),
3075 'lambda' : Item(status=' '),
3077 expected_status.tweak(wc_rev=4)
3079 expected_skip = wc.State('', { })
3081 # should have 3 'prej' files left behind, describing prop conflicts:
3082 extra_files = ['alpha.*\.prej', 'beta.*\.prej', 'dir_conflicts.*\.prej']
3084 svntest.actions.run_and_verify_merge(B2_path, '3', '4', B_url,
3085 expected_output,
3086 expected_disk,
3087 expected_status,
3088 expected_skip,
3089 None, # expected error string
3090 svntest.tree.detect_conflict_files,
3091 extra_files,
3092 None, None, # no B singleton handler
3093 1, # check props
3094 0) # dry_run
3096 #----------------------------------------------------------------------
3098 # Test for issue 2035, whereby 'svn merge' wouldn't always mark
3099 # property conflicts when it should.
3101 def property_merge_from_branch(sbox):
3102 "property merge conflict even without local mods"
3104 sbox.build()
3105 wc_dir = sbox.wc_dir
3107 # Add a property to a file and a directory, commit as r2.
3108 alpha_path = os.path.join(wc_dir, 'A', 'B', 'E', 'alpha')
3109 E_path = os.path.join(wc_dir, 'A', 'B', 'E')
3111 svntest.actions.run_and_verify_svn(None, None, [],
3112 'propset', 'foo', 'foo_val',
3113 alpha_path)
3114 svntest.actions.run_and_verify_svn(None, None, [],
3115 'propset', 'foo', 'foo_val',
3116 E_path)
3118 expected_output = svntest.wc.State(wc_dir, {
3119 'A/B/E' : Item(verb='Sending'),
3120 'A/B/E/alpha' : Item(verb='Sending'),
3122 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
3123 expected_status.tweak('A/B/E', 'A/B/E/alpha', wc_rev=2, status=' ')
3124 svntest.actions.run_and_verify_commit(wc_dir,
3125 expected_output, expected_status,
3126 None, wc_dir)
3127 svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
3129 # Copy B to B2 as rev 3 (making a branch)
3130 B_url = sbox.repo_url + '/A/B'
3131 B2_url = sbox.repo_url + '/A/B2'
3133 svntest.actions.run_and_verify_svn(None, None, [],
3134 'copy', '-m', 'copy B to B2',
3135 B_url, B2_url)
3136 svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
3138 # Change the properties underneath B again, and commit as r4
3139 svntest.actions.run_and_verify_svn(None, None, [],
3140 'propset', 'foo', 'foo_val2',
3141 alpha_path)
3142 svntest.actions.run_and_verify_svn(None, None, [],
3143 'propset', 'foo', 'foo_val2',
3144 E_path)
3145 expected_output = svntest.wc.State(wc_dir, {
3146 'A/B/E' : Item(verb='Sending'),
3147 'A/B/E/alpha' : Item(verb='Sending'),
3149 svntest.actions.run_and_verify_commit(wc_dir,
3150 expected_output, None,
3151 None, wc_dir)
3152 svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
3154 # Make different propchanges changes to the B2 branch and commit as r5.
3155 alpha_path2 = os.path.join(wc_dir, 'A', 'B2', 'E', 'alpha')
3156 E_path2 = os.path.join(wc_dir, 'A', 'B2', 'E')
3158 svntest.actions.run_and_verify_svn(None, None, [],
3159 'propset', 'foo', 'branchval',
3160 alpha_path2)
3161 svntest.actions.run_and_verify_svn(None, None, [],
3162 'propset', 'foo', 'branchval',
3163 E_path2)
3164 expected_output = svntest.wc.State(wc_dir, {
3165 'A/B2/E' : Item(verb='Sending'),
3166 'A/B2/E/alpha' : Item(verb='Sending'),
3168 svntest.actions.run_and_verify_commit(wc_dir,
3169 expected_output, None,
3170 None, wc_dir)
3171 svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
3173 # Now merge the recent B change to the branch. There are no local
3174 # mods anywhere, but we should still get property conflicts anyway!
3175 B2_path = os.path.join(wc_dir, 'A', 'B2')
3177 expected_output = wc.State(B2_path, {
3178 'E' : Item(status=' C'),
3179 'E/alpha' : Item(status=' C'),
3182 expected_disk = wc.State('', {
3183 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B:4'}),
3184 'E' : Item(),
3185 'E/alpha' : Item("This is the file 'alpha'.\n"),
3186 'E/beta' : Item("This is the file 'beta'.\n"),
3187 'F' : Item(),
3188 'lambda' : Item("This is the file 'lambda'.\n"),
3190 expected_disk.tweak('E', 'E/alpha',
3191 props={'foo' : 'branchval'})
3193 expected_status = wc.State(B2_path, {
3194 '' : Item(status=' M'),
3195 'E' : Item(status=' C'),
3196 'E/alpha' : Item(status=' C'),
3197 'E/beta' : Item(status=' '),
3198 'F' : Item(status=' '),
3199 'lambda' : Item(status=' '),
3201 expected_status.tweak(wc_rev=5)
3203 expected_skip = wc.State('', { })
3205 # should have 2 'prej' files left behind, describing prop conflicts:
3206 extra_files = ['alpha.*\.prej', 'dir_conflicts.*\.prej']
3208 svntest.actions.run_and_verify_merge(B2_path, '3', '4', B_url,
3209 expected_output,
3210 expected_disk,
3211 expected_status,
3212 expected_skip,
3213 None, # expected error string
3214 svntest.tree.detect_conflict_files,
3215 extra_files,
3216 None, None, # no B singleton handler
3217 1, # check props
3218 0) # dry_run
3220 #----------------------------------------------------------------------
3222 # Another test for issue 2035, whereby sometimes 'svn merge' marked
3223 # property conflicts when it shouldn't!
3225 def property_merge_undo_redo(sbox):
3226 "undo, then redo a property merge"
3228 sbox.build()
3229 wc_dir = sbox.wc_dir
3231 # Add a property to a file, commit as r2.
3232 alpha_path = os.path.join(wc_dir, 'A', 'B', 'E', 'alpha')
3233 svntest.actions.run_and_verify_svn(None, None, [],
3234 'propset', 'foo', 'foo_val',
3235 alpha_path)
3237 expected_output = svntest.wc.State(wc_dir, {
3238 'A/B/E/alpha' : Item(verb='Sending'),
3240 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
3241 expected_status.tweak('A/B/E/alpha', wc_rev=2, status=' ')
3242 svntest.actions.run_and_verify_commit(wc_dir,
3243 expected_output, expected_status,
3244 None, wc_dir)
3245 svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
3247 # Use 'svn merge' to undo the commit. ('svn merge -r2:1')
3248 # Result should be a single local-prop-mod.
3249 expected_output = wc.State(wc_dir, {'A/B/E/alpha' : Item(status=' U'), })
3251 expected_disk = svntest.main.greek_state.copy()
3253 expected_status = svntest.actions.get_virginal_state(wc_dir, 2)
3254 expected_status.tweak('A/B/E/alpha', status=' M')
3256 expected_skip = wc.State('', { })
3258 svntest.actions.run_and_verify_merge(wc_dir, '2', '1',
3259 sbox.repo_url,
3260 expected_output,
3261 expected_disk,
3262 expected_status,
3263 expected_skip,
3264 None, # expected error string
3265 None, None, # no A singleton handler
3266 None, None, # no B singleton handler
3267 1, # check props
3268 0) # dry_run
3270 # Change mind, re-apply the change ('svn merge -r1:2').
3271 # This should merge cleanly into existing prop-mod, status shows nothing.
3272 expected_output = wc.State(wc_dir, {'A/B/E/alpha' : Item(status=' C'), })
3274 expected_disk = svntest.main.greek_state.copy()
3275 expected_disk.add({'' : Item(props={SVN_PROP_MERGEINFO : '/:2'}), })
3276 expected_disk.add({'A/B/E/alpha.prej'
3277 : Item("Trying to create property 'foo' with value 'foo_val',\n"
3278 + "but it has been locally deleted.\n")})
3280 expected_status = svntest.actions.get_virginal_state(wc_dir, 2)
3281 expected_status.tweak('', status=' M')
3282 expected_status.tweak('A/B/E/alpha', status=' C')
3284 expected_skip = wc.State('', { })
3286 # Re-merge r1. We have to use --ignore-ancestry here. Otherwise
3287 # the merge logic will claim we already have this change (because it
3288 # was unable to record the previous undoing merge).
3289 svntest.actions.run_and_verify_merge(wc_dir, '1', '2',
3290 sbox.repo_url,
3291 expected_output,
3292 expected_disk,
3293 expected_status,
3294 expected_skip,
3295 None, # expected error string
3296 None, None, # no A singleton handler
3297 None, None, # no B singleton handler
3298 1, # check props
3299 0, # dry_run
3300 '--ignore-ancestry')
3304 #----------------------------------------------------------------------
3305 def cherry_pick_text_conflict(sbox):
3306 "cherry-pick a dependent change, get conflict"
3308 sbox.build()
3309 wc_dir = sbox.wc_dir
3311 A_path = os.path.join(wc_dir, 'A')
3312 A_url = sbox.repo_url + '/A'
3313 mu_path = os.path.join(A_path, 'mu')
3314 branch_A_url = sbox.repo_url + '/copy-of-A'
3315 branch_mu_path = os.path.join(wc_dir, 'copy-of-A', 'mu')
3317 # Create a branch of A.
3318 svntest.actions.run_and_verify_svn(None, None, [], 'cp',
3319 A_url, branch_A_url,
3320 '-m', "Creating copy-of-A")
3322 # Update to get the branch.
3323 svntest.actions.run_and_verify_svn(None, None, [],
3324 'update', wc_dir)
3326 # Change mu's text on the branch, producing r3 through r6.
3327 for rev in range(3, 7):
3328 svntest.main.file_append(branch_mu_path, ("r%d\n" % rev) * 3)
3329 svntest.actions.run_and_verify_svn(None, None, [],
3330 'ci', '-m',
3331 'Add lines to mu in r%d.' % rev, wc_dir)
3333 # Mark r5 as merged into trunk, to create disparate revision ranges
3334 # which need to be merged.
3335 svntest.actions.run_and_verify_svn(None, [], [],
3336 'merge', '-c5', '--record-only',
3337 branch_A_url, A_path)
3340 # Try to merge r4:6 into trunk, without r3. It should fail.
3341 expected_output = wc.State(A_path, {
3342 'mu' : Item(status='C '),
3344 expected_disk = wc.State('', {
3345 'mu' : Item("This is the file 'mu'.\n"
3346 + make_conflict_marker_text("r3\n" * 3, "r4\n" * 3, 4)),
3347 'B' : Item(),
3348 'B/lambda' : Item("This is the file 'lambda'.\n"),
3349 'B/E' : Item(),
3350 'B/E/alpha' : Item("This is the file 'alpha'.\n"),
3351 'B/E/beta' : Item("This is the file 'beta'.\n"),
3352 'B/F' : Item(),
3353 'C' : Item(),
3354 'D' : Item(),
3355 'D/gamma' : Item("This is the file 'gamma'.\n"),
3356 'D/H' : Item(),
3357 'D/H/chi' : Item("This is the file 'chi'.\n"),
3358 'D/H/psi' : Item("This is the file 'psi'.\n"),
3359 'D/H/omega' : Item("This is the file 'omega'.\n"),
3360 'D/G' : Item(),
3361 'D/G/pi' : Item("This is the file 'pi'.\n"),
3362 'D/G/rho' : Item("This is the file 'rho'.\n"),
3363 'D/G/tau' : Item("This is the file 'tau'.\n"),
3365 expected_status = wc.State(A_path, {
3366 '' : Item(status=' M'),
3367 'mu' : Item(status='C '),
3368 'B' : Item(status=' '),
3369 'B/lambda' : Item(status=' '),
3370 'B/E' : Item(status=' '),
3371 'B/E/alpha' : Item(status=' '),
3372 'B/E/beta' : Item(status=' '),
3373 'B/F' : Item(status=' '),
3374 'C' : Item(status=' '),
3375 'D' : Item(status=' '),
3376 'D/gamma' : Item(status=' '),
3377 'D/H' : Item(status=' '),
3378 'D/H/chi' : Item(status=' '),
3379 'D/H/psi' : Item(status=' '),
3380 'D/H/omega' : Item(status=' '),
3381 'D/G' : Item(status=' '),
3382 'D/G/pi' : Item(status=' '),
3383 'D/G/rho' : Item(status=' '),
3384 'D/G/tau' : Item(status=' '),
3386 expected_status.tweak(wc_rev=2)
3387 expected_skip = wc.State('', { })
3388 expected_error = "conflicts were produced while merging r3:4"
3389 svntest.actions.run_and_verify_merge(A_path, '3', '6', branch_A_url,
3390 expected_output,
3391 expected_disk,
3392 expected_status,
3393 expected_skip,
3394 expected_error,
3395 svntest.tree.detect_conflict_files,
3396 ["mu\.working",
3397 "mu\.merge-right\.r4",
3398 "mu\.merge-left\.r3"],
3399 None, None, # no singleton handler
3400 0, # don't check props
3401 0) # not a dry_run
3405 # Test for issue 2135
3406 def merge_file_replace(sbox):
3407 "merge a replacement of a file"
3409 sbox.build()
3410 wc_dir = sbox.wc_dir
3412 # File scheduled for deletion
3413 rho_path = os.path.join(wc_dir, 'A', 'D', 'G', 'rho')
3414 svntest.actions.run_and_verify_svn(None, None, [], 'rm', rho_path)
3416 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
3417 expected_status.tweak('A/D/G/rho', status='D ')
3418 svntest.actions.run_and_verify_status(wc_dir, expected_status)
3420 expected_output = svntest.wc.State(wc_dir, {
3421 'A/D/G/rho': Item(verb='Deleting'),
3424 expected_status.remove('A/D/G/rho')
3426 # Commit rev 2
3427 svntest.actions.run_and_verify_commit(wc_dir,
3428 expected_output,
3429 expected_status,
3430 None, wc_dir)
3431 # Create and add a new file.
3432 svntest.main.file_write(rho_path, "new rho\n")
3433 svntest.actions.run_and_verify_svn(None, None, [], 'add', rho_path)
3435 # Commit revsion 3
3436 expected_status.add({
3437 'A/D/G/rho' : Item(status='A ', wc_rev='0')
3439 svntest.actions.run_and_verify_status(wc_dir, expected_status)
3440 expected_output = svntest.wc.State(wc_dir, {
3441 'A/D/G/rho': Item(verb='Adding'),
3444 svntest.actions.run_and_verify_commit(wc_dir,
3445 expected_output,
3446 None,
3447 None, wc_dir)
3449 # Update working copy
3450 expected_output = svntest.wc.State(wc_dir, {})
3451 expected_disk = svntest.main.greek_state.copy()
3452 expected_disk.tweak('A/D/G/rho', contents='new rho\n' )
3453 expected_status.tweak(wc_rev='3')
3454 expected_status.tweak('A/D/G/rho', status=' ')
3456 svntest.actions.run_and_verify_update(wc_dir,
3457 expected_output,
3458 expected_disk,
3459 expected_status)
3461 # merge changes from r3:1
3462 expected_output = svntest.wc.State(wc_dir, {
3463 'A/D/G/rho': Item(status='R ')
3465 expected_status.tweak('A/D/G/rho', status='R ', copied='+', wc_rev='-')
3466 expected_skip = wc.State(wc_dir, { })
3467 expected_disk.tweak('A/D/G/rho', contents="This is the file 'rho'.\n")
3468 svntest.actions.run_and_verify_merge(wc_dir, '3', '1',
3469 sbox.repo_url,
3470 expected_output,
3471 expected_disk,
3472 expected_status,
3473 expected_skip)
3475 # Now commit merged wc
3476 expected_output = svntest.wc.State(wc_dir, {
3477 'A/D/G/rho': Item(verb='Replacing'),
3479 expected_status.tweak('A/D/G/rho', status=' ', copied=None, wc_rev='4')
3480 svntest.actions.run_and_verify_commit(wc_dir,
3481 expected_output,
3482 expected_status,
3483 None,
3484 wc_dir)
3485 # Test for issue 2522
3486 # Same as merge_file_replace, but without update before merge.
3487 def merge_file_replace_to_mixed_rev_wc(sbox):
3488 "merge a replacement of a file to mixed rev wc"
3490 sbox.build()
3491 wc_dir = sbox.wc_dir
3493 # File scheduled for deletion
3494 rho_path = os.path.join(wc_dir, 'A', 'D', 'G', 'rho')
3495 svntest.actions.run_and_verify_svn(None, None, [], 'rm', rho_path)
3497 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
3498 expected_status.tweak('A/D/G/rho', status='D ')
3499 svntest.actions.run_and_verify_status(wc_dir, expected_status)
3501 expected_output = svntest.wc.State(wc_dir, {
3502 'A/D/G/rho': Item(verb='Deleting'),
3505 expected_status.remove('A/D/G/rho')
3507 # Commit rev 2
3508 svntest.actions.run_and_verify_commit(wc_dir,
3509 expected_output,
3510 expected_status,
3511 None, wc_dir)
3513 # Update working copy
3514 expected_disk = svntest.main.greek_state.copy()
3515 expected_disk.remove('A/D/G/rho' )
3516 expected_output = svntest.wc.State(wc_dir, {})
3517 expected_status.tweak(wc_rev='2')
3519 svntest.actions.run_and_verify_update(wc_dir,
3520 expected_output,
3521 expected_disk,
3522 expected_status)
3524 # Create and add a new file.
3525 svntest.main.file_write(rho_path, "new rho\n")
3526 svntest.actions.run_and_verify_svn(None, None, [], 'add', rho_path)
3528 # Commit revsion 3
3529 expected_status.add({
3530 'A/D/G/rho' : Item(status='A ', wc_rev='0')
3532 svntest.actions.run_and_verify_status(wc_dir, expected_status)
3533 expected_output = svntest.wc.State(wc_dir, {
3534 'A/D/G/rho': Item(verb='Adding'),
3537 expected_disk.add({'A/D/G/rho' : Item(contents='new rho\n')} )
3538 expected_status.tweak(wc_rev='2')
3539 expected_status.tweak('A/D/G/rho', status=' ', wc_rev='3')
3541 svntest.actions.run_and_verify_commit(wc_dir,
3542 expected_output,
3543 expected_status,
3544 None, wc_dir)
3547 # merge changes from r3:1
3548 expected_output = svntest.wc.State(wc_dir, {
3549 'A/D/G/rho': Item(status='R ')
3551 expected_status.tweak('A/D/G/rho', status='R ', copied='+', wc_rev='-')
3552 expected_skip = wc.State(wc_dir, { })
3553 expected_disk.tweak('A/D/G/rho', contents="This is the file 'rho'.\n")
3554 svntest.actions.run_and_verify_merge(wc_dir, '3', '1',
3555 sbox.repo_url,
3556 expected_output,
3557 expected_disk,
3558 expected_status,
3559 expected_skip)
3561 # At this point WC is broken, because file rho has invalid revision
3562 # Try to update
3563 expected_output = svntest.wc.State(wc_dir, {})
3564 expected_status.tweak(wc_rev='3')
3565 expected_status.tweak('A/D/G/rho', status='R ', copied='+', wc_rev='-')
3566 svntest.actions.run_and_verify_update(wc_dir,
3567 expected_output,
3568 expected_disk,
3569 expected_status)
3571 # Now commit merged wc
3572 expected_output = svntest.wc.State(wc_dir, {
3573 'A/D/G/rho': Item(verb='Replacing'),
3575 expected_status.tweak('A/D/G/rho', status=' ', copied=None, wc_rev='4')
3576 svntest.actions.run_and_verify_commit(wc_dir,
3577 expected_output,
3578 expected_status,
3579 None,
3580 wc_dir)
3582 # use -x -w option for ignoring whitespace during merge
3583 def merge_ignore_whitespace(sbox):
3584 "ignore whitespace when merging"
3586 sbox.build()
3587 wc_dir = sbox.wc_dir
3589 # commit base version of iota
3590 file_name = "iota"
3591 file_path = os.path.join(wc_dir, file_name)
3592 file_url = sbox.repo_url + '/iota'
3594 svntest.main.file_write(file_path,
3595 "Aa\n"
3596 "Bb\n"
3597 "Cc\n")
3598 expected_output = svntest.wc.State(wc_dir, {
3599 'iota' : Item(verb='Sending'),
3601 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
3602 None, None, wc_dir)
3604 # change the file, mostly whitespace changes + an extra line
3605 svntest.main.file_write(file_path, "A a\nBb \n Cc\nNew line in iota\n")
3606 expected_output = wc.State(wc_dir, { file_name : Item(verb='Sending'), })
3607 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
3608 expected_status.tweak(file_name, wc_rev=3)
3609 svntest.actions.run_and_verify_commit(wc_dir,
3610 expected_output,
3611 expected_status,
3612 None,
3613 wc_dir)
3615 # Backdate iota to revision 2, so we can merge in the rev 3 changes.
3616 svntest.actions.run_and_verify_svn(None, None, [],
3617 'up', '-r', '2', file_path)
3618 # Make some local whitespace changes, these should not conflict
3619 # with the remote whitespace changes as both will be ignored.
3620 svntest.main.file_write(file_path, " Aa\nB b\nC c\n")
3622 # Lines changed only by whitespaces - both in local or remote -
3623 # should be ignored
3624 expected_output = wc.State(sbox.wc_dir, { file_name : Item(status='G ') })
3625 expected_disk = svntest.main.greek_state.copy()
3626 expected_disk.tweak(file_name,
3627 contents=" Aa\n"
3628 "B b\n"
3629 "C c\n"
3630 "New line in iota\n")
3631 expected_status = svntest.actions.get_virginal_state(sbox.wc_dir, 1)
3632 expected_status.tweak('', status=' M', wc_rev=1)
3633 expected_status.tweak(file_name, status='M ', wc_rev=2)
3634 expected_skip = wc.State('', { })
3636 svntest.actions.run_and_verify_merge(sbox.wc_dir, '2', '3',
3637 sbox.repo_url,
3638 expected_output,
3639 expected_disk,
3640 expected_status,
3641 expected_skip,
3642 None, None, None, None, None,
3643 0, 0,
3644 '-x', '-w')
3646 # use -x --ignore-eol-style option for ignoring eolstyle during merge
3647 def merge_ignore_eolstyle(sbox):
3648 "ignore eolstyle when merging"
3650 sbox.build()
3651 wc_dir = sbox.wc_dir
3653 # commit base version of iota
3654 file_name = "iota"
3655 file_path = os.path.join(wc_dir, file_name)
3656 file_url = sbox.repo_url + '/iota'
3658 svntest.main.file_write(file_path,
3659 "Aa\r\n"
3660 "Bb\r\n"
3661 "Cc\r\n",
3662 "wb")
3663 expected_output = svntest.wc.State(wc_dir, {
3664 'iota' : Item(verb='Sending'),
3666 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
3667 None, None, wc_dir)
3669 # change the file, mostly eol changes + an extra line
3670 svntest.main.file_write(file_path,
3671 "Aa\r"
3672 "Bb\n"
3673 "Cc\r"
3674 "New line in iota\n",
3675 "wb")
3676 expected_output = wc.State(wc_dir, { file_name : Item(verb='Sending'), })
3677 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
3678 expected_status.tweak(file_name, wc_rev=3)
3679 svntest.actions.run_and_verify_commit(wc_dir,
3680 expected_output,
3681 expected_status,
3682 None,
3683 wc_dir)
3685 # Backdate iota to revision 2, so we can merge in the rev 3 changes.
3686 svntest.actions.run_and_verify_svn(None, None, [],
3687 'up', '-r', '2', file_path)
3688 # Make some local eol changes, these should not conflict
3689 # with the remote eol changes as both will be ignored.
3690 svntest.main.file_write(file_path,
3691 "Aa\n"
3692 "Bb\r"
3693 "Cc\n",
3694 "wb")
3696 # Lines changed only by eolstyle - both in local or remote -
3697 # should be ignored
3698 expected_output = wc.State(sbox.wc_dir, { file_name : Item(status='G ') })
3699 expected_disk = svntest.main.greek_state.copy()
3700 expected_disk.tweak(file_name,
3701 contents="Aa\n"
3702 "Bb\r"
3703 "Cc\n"
3704 "New line in iota\n")
3705 expected_status = svntest.actions.get_virginal_state(sbox.wc_dir, 1)
3706 expected_status.tweak('', status=' M')
3707 expected_status.tweak(file_name, status='M ', wc_rev=2)
3708 expected_skip = wc.State('', { })
3710 svntest.actions.run_and_verify_merge(sbox.wc_dir, '2', '3',
3711 sbox.repo_url,
3712 expected_output,
3713 expected_disk,
3714 expected_status,
3715 expected_skip,
3716 None, None, None, None, None,
3717 0, 0,
3718 '-x', '--ignore-eol-style')
3720 #----------------------------------------------------------------------
3721 # Issue 2584
3722 def merge_add_over_versioned_file_conflicts(sbox):
3723 "conflict from merge of add over versioned file"
3725 sbox.build()
3726 wc_dir = sbox.wc_dir
3728 E_path = os.path.join(wc_dir, 'A', 'B', 'E')
3729 alpha_path = os.path.join(E_path, 'alpha')
3730 new_alpha_path = os.path.join(wc_dir, 'A', 'C', 'alpha')
3732 # Create a new "alpha" file, with enough differences to cause a conflict.
3733 ### TODO: Leverage inject_conflict_into_wc() here.
3734 svntest.main.file_write(new_alpha_path, 'new alpha content\n')
3736 # Add and commit the new "alpha" file, creating revision 2.
3737 svntest.main.run_svn(None, "add", new_alpha_path)
3739 expected_output = svntest.wc.State(wc_dir, {
3740 'A/C/alpha' : Item(verb='Adding'),
3742 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
3743 expected_status.add({
3744 'A/C/alpha' : Item(status=' ', wc_rev=2),
3746 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
3747 expected_status, None,
3748 wc_dir)
3750 # Search for the comment entitled "The Merge Kluge" elsewhere in
3751 # this file, to understand why we shorten and chdir() below.
3752 short_E_path = shorten_path_kludge(E_path)
3754 # Merge changes from r1:2 into our pre-existing "alpha" file,
3755 # causing a conflict.
3756 expected_output = wc.State(short_E_path, {
3757 'alpha' : Item(status='C '),
3759 expected_disk = wc.State('', {
3760 'alpha' : Item(""), # state filled in below
3761 'beta' : Item("This is the file 'beta'.\n"),
3763 expected_status = wc.State(short_E_path, {
3764 '' : Item(status=' M', wc_rev=1),
3765 'alpha' : Item(status='C ', wc_rev=1),
3766 'beta' : Item(status=' ', wc_rev=1),
3769 inject_conflict_into_expected_state('alpha', expected_disk, expected_status,
3770 "This is the file 'alpha'.\n",
3771 "new alpha content\n", 2)
3772 expected_skip = wc.State(short_E_path, { })
3774 os.chdir(svntest.main.work_dir)
3775 svntest.actions.run_and_verify_merge(short_E_path, '1', '2',
3776 sbox.repo_url + \
3777 '/A/C',
3778 expected_output,
3779 expected_disk,
3780 expected_status,
3781 expected_skip,
3782 None,
3783 svntest.tree.detect_conflict_files,
3784 ["alpha\.working",
3785 "alpha\.merge-right\.r2",
3786 "alpha\.merge-left\.r0"])
3788 #----------------------------------------------------------------------
3789 # eol-style handling during merge with conflicts, scenario 1:
3790 # when a merge creates a conflict on a file, make sure the file and files
3791 # r<left>, r<right> and .mine are in the eol-style defined for that file.
3793 # This test for 'svn update' can be found in update_tests.py as
3794 # conflict_markers_matching_eol.
3795 def merge_conflict_markers_matching_eol(sbox):
3796 "conflict markers should match the file's eol style"
3798 sbox.build()
3799 wc_dir = sbox.wc_dir
3800 filecount = 1
3802 mu_path = os.path.join(wc_dir, 'A', 'mu')
3804 if os.name == 'nt':
3805 crlf = '\n'
3806 else:
3807 crlf = '\r\n'
3809 # Checkout a second working copy
3810 wc_backup = sbox.add_wc_path('backup')
3811 svntest.actions.run_and_verify_svn(None, None, [], 'checkout',
3812 sbox.repo_url, wc_backup)
3814 # set starting revision
3815 cur_rev = 1
3817 expected_disk = svntest.main.greek_state.copy()
3818 expected_status = svntest.actions.get_virginal_state(wc_dir, cur_rev)
3819 expected_backup_status = svntest.actions.get_virginal_state(wc_backup,
3820 cur_rev)
3822 path_backup = os.path.join(wc_backup, 'A', 'mu')
3824 # do the test for each eol-style
3825 for eol, eolchar in zip(['CRLF', 'CR', 'native', 'LF'],
3826 [crlf, '\015', '\n', '\012']):
3827 # rewrite file mu and set the eol-style property.
3828 svntest.main.file_write(mu_path, "This is the file 'mu'."+ eolchar, 'wb')
3829 svntest.main.run_svn(None, 'propset', 'svn:eol-style', eol, mu_path)
3831 expected_disk.add({
3832 'A/mu' : Item("This is the file 'mu'." + eolchar)
3834 expected_output = svntest.wc.State(wc_dir, {
3835 'A/mu' : Item(verb='Sending'),
3837 expected_status.tweak(wc_rev = cur_rev)
3838 expected_status.add({
3839 'A/mu' : Item(status=' ', wc_rev = cur_rev + 1),
3842 # Commit the original change and note the 'base' revision number
3843 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
3844 expected_status, None,
3845 wc_dir)
3846 cur_rev = cur_rev + 1
3847 base_rev = cur_rev
3849 svntest.main.run_svn(None, 'update', wc_backup)
3851 # Make a local mod to mu
3852 svntest.main.file_append(mu_path,
3853 'Original appended text for mu' + eolchar)
3855 # Commit the original change and note the 'theirs' revision number
3856 svntest.main.run_svn(None, 'commit', '-m', 'test log', wc_dir)
3857 cur_rev = cur_rev + 1
3858 theirs_rev = cur_rev
3860 # Make a local mod to mu, will conflict with the previous change
3861 svntest.main.file_append(path_backup,
3862 'Conflicting appended text for mu' + eolchar)
3864 # Create expected output tree for an update of the wc_backup.
3865 expected_backup_output = svntest.wc.State(wc_backup, {
3866 'A/mu' : Item(status='C '),
3869 # Create expected disk tree for the update.
3870 expected_backup_disk = expected_disk.copy()
3872 # verify content of resulting conflicted file
3873 expected_backup_disk.add({
3874 'A/mu' : Item(contents= "This is the file 'mu'." + eolchar +
3875 "<<<<<<< .working" + eolchar +
3876 "Conflicting appended text for mu" + eolchar +
3877 "=======" + eolchar +
3878 "Original appended text for mu" + eolchar +
3879 ">>>>>>> .merge-right.r" + str(cur_rev) + eolchar),
3881 # verify content of base(left) file
3882 expected_backup_disk.add({
3883 'A/mu.merge-left.r' + str(base_rev) :
3884 Item(contents= "This is the file 'mu'." + eolchar)
3886 # verify content of theirs(right) file
3887 expected_backup_disk.add({
3888 'A/mu.merge-right.r' + str(theirs_rev) :
3889 Item(contents= "This is the file 'mu'." + eolchar +
3890 "Original appended text for mu" + eolchar)
3892 # verify content of mine file
3893 expected_backup_disk.add({
3894 'A/mu.working' : Item(contents= "This is the file 'mu'." +
3895 eolchar +
3896 "Conflicting appended text for mu" + eolchar)
3899 # Create expected status tree for the update.
3900 expected_backup_status.add({
3901 'A/mu' : Item(status=' ', wc_rev=cur_rev),
3903 expected_backup_status.tweak('A/mu', status='C ')
3904 expected_backup_status.tweak(wc_rev = cur_rev - 1)
3905 expected_backup_status.tweak('', status= ' M')
3907 expected_backup_skip = wc.State('', { })
3909 svntest.actions.run_and_verify_merge(wc_backup, cur_rev - 1, cur_rev,
3910 sbox.repo_url,
3911 expected_backup_output,
3912 expected_backup_disk,
3913 expected_backup_status,
3914 expected_backup_skip)
3916 # cleanup for next run
3917 svntest.main.run_svn(None, 'revert', '-R', wc_backup)
3918 svntest.main.run_svn(None, 'update', wc_dir)
3920 # eol-style handling during merge, scenario 2:
3921 # if part of that merge is a propchange (add, change, delete) of
3922 # svn:eol-style, make sure the correct eol-style is applied before
3923 # calculating the merge (and conflicts if any)
3925 # This test for 'svn update' can be found in update_tests.py as
3926 # update_eolstyle_handling.
3927 def merge_eolstyle_handling(sbox):
3928 "handle eol-style propchange during merge"
3930 sbox.build()
3931 wc_dir = sbox.wc_dir
3933 mu_path = os.path.join(wc_dir, 'A', 'mu')
3935 if os.name == 'nt':
3936 crlf = '\n'
3937 else:
3938 crlf = '\r\n'
3940 # Checkout a second working copy
3941 wc_backup = sbox.add_wc_path('backup')
3942 svntest.actions.run_and_verify_svn(None, None, [], 'checkout',
3943 sbox.repo_url, wc_backup)
3944 path_backup = os.path.join(wc_backup, 'A', 'mu')
3946 # Test 1: add the eol-style property and commit, change mu in the second
3947 # working copy and merge the last revision; there should be no conflict!
3948 svntest.main.run_svn(None, 'propset', 'svn:eol-style', "CRLF", mu_path)
3949 svntest.main.run_svn(None,
3950 'commit', '-m', 'set eol-style property', wc_dir)
3952 svntest.main.file_append_binary(path_backup, 'Added new line of text.\012')
3954 expected_backup_disk = svntest.main.greek_state.copy()
3955 expected_backup_disk.tweak(
3956 'A/mu', contents= "This is the file 'mu'." + crlf +
3957 "Added new line of text." + crlf)
3958 expected_backup_output = svntest.wc.State(wc_backup, {
3959 'A/mu' : Item(status='GU'),
3961 expected_backup_status = svntest.actions.get_virginal_state(wc_backup, 1)
3962 expected_backup_status.tweak('', status=' M')
3963 expected_backup_status.tweak('A/mu', status='MM')
3965 expected_backup_skip = wc.State('', { })
3967 svntest.actions.run_and_verify_merge(wc_backup, '1', '2', sbox.repo_url,
3968 expected_backup_output,
3969 expected_backup_disk,
3970 expected_backup_status,
3971 expected_backup_skip)
3973 # Test 2: now change the eol-style property to another value and commit,
3974 # merge this revision in the still changed mu in the second working copy;
3975 # there should be a property conflict! (Since this will overwrite a
3976 # local change to a versioned resource.)
3977 svntest.main.run_svn(None, 'propset', 'svn:eol-style', "CR", mu_path)
3978 svntest.main.run_svn(None,
3979 'commit', '-m', 'set eol-style property', wc_dir)
3981 expected_backup_disk = svntest.main.greek_state.copy()
3982 expected_backup_disk.add({
3983 'A/mu' : Item(contents= "This is the file 'mu'." + crlf +
3984 "Added new line of text." + crlf)
3986 expected_backup_disk.add({
3987 'A/mu.prej' : Item("Trying to change property 'svn:eol-style' from 'CRLF'"
3988 + " to 'CR',\nbut property has been locally added with"
3989 + " value 'CRLF'\n")})
3990 expected_backup_output = svntest.wc.State(wc_backup, {
3991 'A/mu' : Item(status='GC'),
3993 expected_backup_status = svntest.actions.get_virginal_state(wc_backup, 1)
3994 expected_backup_status.tweak('', status=' M')
3995 expected_backup_status.tweak('A/mu', status='MC')
3996 svntest.actions.run_and_verify_merge(wc_backup, '2', '3', sbox.repo_url,
3997 expected_backup_output,
3998 expected_backup_disk,
3999 expected_backup_status,
4000 expected_backup_skip)
4002 # Test 3: now delete the eol-style property and commit, merge this revision
4003 # in the still changed mu in the second working copy; there should be no
4004 # conflict! (after marking mu resolved from Test 2)
4005 # EOL of mu should be unchanged (=CRLF).
4006 svntest.main.run_svn(None, 'propdel', 'svn:eol-style', mu_path)
4007 svntest.main.run_svn(None,
4008 'commit', '-m', 'del eol-style property', wc_dir)
4010 expected_backup_disk = svntest.main.greek_state.copy()
4011 expected_backup_disk.add({
4012 'A/mu' : Item(contents= "This is the file 'mu'." + crlf +
4013 "Added new line of text." + crlf)
4015 expected_backup_output = svntest.wc.State(wc_backup, {
4016 'A/mu' : Item(status=' G'),
4018 expected_backup_status = svntest.actions.get_virginal_state(wc_backup, 1)
4019 expected_backup_status.tweak('', status=' M')
4020 expected_backup_status.tweak('A/mu', status='M ')
4021 svntest.main.run_svn(None, 'resolved', path_backup)
4022 svntest.actions.run_and_verify_merge(wc_backup, '3', '4', sbox.repo_url,
4023 expected_backup_output,
4024 expected_backup_disk,
4025 expected_backup_status,
4026 expected_backup_skip)
4028 def create_deep_trees(wc_dir):
4029 """Create A/B/F/E by moving A/B/E to A/B/F/E.
4030 Copy A/B/F/E to A/B/F/E1.
4031 Copy A/B to A/copy-of-B, and return the expected status.
4032 At the end of this function WC would be at r4"""
4034 A_path = os.path.join(wc_dir, 'A')
4035 A_B_path = os.path.join(A_path, 'B')
4036 A_B_E_path = os.path.join(A_B_path, 'E')
4037 A_B_F_path = os.path.join(A_B_path, 'F')
4038 A_B_F_E_path = os.path.join(A_B_F_path, 'E')
4039 A_B_F_E1_path = os.path.join(A_B_F_path, 'E1')
4041 # Deepen the directory structure we're working with by moving E to
4042 # underneath F and committing, creating revision 2.
4043 svntest.main.run_svn(None, 'mv', A_B_E_path, A_B_F_path)
4045 # A/B/F/E now has empty mergeinfo
4047 expected_output = wc.State(wc_dir, {
4048 'A/B/E' : Item(verb='Deleting'),
4049 'A/B/F/E' : Item(verb='Adding')
4051 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
4052 expected_status.remove('A/B/E', 'A/B/E/alpha', 'A/B/E/beta')
4053 expected_status.add({
4054 'A/B/F/E' : Item(status=' ', wc_rev=2),
4055 'A/B/F/E/alpha' : Item(status=' ', wc_rev=2),
4056 'A/B/F/E/beta' : Item(status=' ', wc_rev=2),
4058 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
4059 expected_status, None,
4060 wc_dir)
4062 svntest.main.run_svn(None, 'cp', A_B_F_E_path, A_B_F_E1_path)
4064 # A/B/F/E1 now has empty mergeinfo
4066 expected_output = wc.State(wc_dir, {
4067 'A/B/F/E1' : Item(verb='Adding')
4069 expected_status.add({
4070 'A/B/F/E1' : Item(status=' ', wc_rev=3),
4071 'A/B/F/E1/alpha' : Item(status=' ', wc_rev=3),
4072 'A/B/F/E1/beta' : Item(status=' ', wc_rev=3),
4074 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
4075 expected_status, None,
4076 wc_dir)
4078 # Bring the entire WC up to date with rev 3.
4079 svntest.actions.run_and_verify_svn(None, None, [], 'update', wc_dir)
4080 expected_status.tweak(wc_rev=3)
4082 # Copy B and commit, creating revision 4.
4083 copy_of_B_path = os.path.join(A_path, 'copy-of-B')
4084 svntest.main.run_svn(None, "cp", A_B_path, copy_of_B_path)
4086 # A/copy-of-B, A/copy-of-B/F/E, and A/copy-of-B/F/E1 now have empty mergeinfo
4088 expected_output = svntest.wc.State(wc_dir, {
4089 'A/copy-of-B' : Item(verb='Adding'),
4091 expected_status.add({
4092 'A/copy-of-B' : Item(status=' ', wc_rev=4),
4093 'A/copy-of-B/F' : Item(status=' ', wc_rev=4),
4094 'A/copy-of-B/F/E' : Item(status=' ', wc_rev=4),
4095 'A/copy-of-B/F/E/alpha' : Item(status=' ', wc_rev=4),
4096 'A/copy-of-B/F/E/beta' : Item(status=' ', wc_rev=4),
4097 'A/copy-of-B/F/E1' : Item(status=' ', wc_rev=4),
4098 'A/copy-of-B/F/E1/alpha' : Item(status=' ', wc_rev=4),
4099 'A/copy-of-B/F/E1/beta' : Item(status=' ', wc_rev=4),
4100 'A/copy-of-B/lambda' : Item(status=' ', wc_rev=4),
4102 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
4103 expected_status, None,
4104 wc_dir)
4106 # pre-update, empty mergeinfo can be found on:
4108 # /A/B/F/E
4109 # /A/B/F/E1
4110 # /A/copy-of-B
4111 # /A/copy-of-B/F/E
4112 # /A/copy-of-B/F/E1
4114 expected_disk = svntest.main.greek_state.copy()
4115 expected_disk.remove('A/B/E', 'A/B/E/alpha', 'A/B/E/beta')
4116 expected_disk.add({
4117 'A/B/F/E' : Item(props={SVN_PROP_MERGEINFO : ''}),
4118 'A/B/F/E/alpha' : Item(contents="This is the file 'alpha'.\n"),
4119 'A/B/F/E/beta' : Item(contents="This is the file 'beta'.\n"),
4120 'A/B/F/E1' : Item(props={SVN_PROP_MERGEINFO : ''}),
4121 'A/B/F/E1/alpha' : Item(contents="This is the file 'alpha'.\n"),
4122 'A/B/F/E1/beta' : Item(contents="This is the file 'beta'.\n"),
4123 'A/copy-of-B' : Item(props={SVN_PROP_MERGEINFO : ''}),
4124 'A/copy-of-B/F' : Item(props={}),
4125 'A/copy-of-B/F/E' : Item(props={SVN_PROP_MERGEINFO : ''}),
4126 'A/copy-of-B/F/E/alpha' : Item(contents="This is the file 'alpha'.\n"),
4127 'A/copy-of-B/F/E/beta' : Item(contents="This is the file 'beta'.\n"),
4128 'A/copy-of-B/F/E1' : Item(props={SVN_PROP_MERGEINFO : ''}),
4129 'A/copy-of-B/F/E1/alpha' : Item(contents="This is the file 'alpha'.\n"),
4130 'A/copy-of-B/F/E1/beta' : Item(contents="This is the file 'beta'.\n"),
4131 'A/copy-of-B/lambda' : Item(contents="This is the file 'lambda'.\n"),
4133 svntest.actions.verify_disk(wc_dir, expected_disk,
4134 None, None, None, None, 1)
4136 # Bring the entire WC up to date with rev 4.
4137 svntest.actions.run_and_verify_svn(None, None, [], 'update', wc_dir)
4139 svntest.actions.verify_disk(wc_dir, expected_disk,
4140 None, None, None, None, 1)
4142 expected_status.tweak(wc_rev=4)
4143 expected_disk.tweak('A/copy-of-B/F/E', 'A/copy-of-B/F/E1', status=' M')
4144 return expected_status
4146 def avoid_repeated_merge_using_inherited_merge_info(sbox):
4147 "use inherited mergeinfo to avoid repeated merge"
4149 sbox.build()
4150 wc_dir = sbox.wc_dir
4152 A_path = os.path.join(wc_dir, 'A')
4153 A_B_path = os.path.join(A_path, 'B')
4154 A_B_E_path = os.path.join(A_B_path, 'E')
4155 A_B_F_path = os.path.join(A_B_path, 'F')
4156 copy_of_B_path = os.path.join(A_path, 'copy-of-B')
4158 # Create a deeper directory structure.
4159 expected_status = create_deep_trees(wc_dir)
4161 # Edit alpha and commit it, creating revision 5.
4162 alpha_path = os.path.join(A_B_F_path, 'E', 'alpha')
4163 new_content_for_alpha = 'new content to alpha\n'
4164 svntest.main.file_write(alpha_path, new_content_for_alpha)
4165 expected_output = svntest.wc.State(wc_dir, {
4166 'A/B/F/E/alpha' : Item(verb='Sending'),
4168 expected_status.tweak('A/B/F/E/alpha', wc_rev=5)
4169 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
4170 expected_status, None,
4171 wc_dir)
4173 # Search for the comment entitled "The Merge Kluge" elsewhere in
4174 # this file, to understand why we shorten and chdir() below.
4175 short_copy_of_B_path = shorten_path_kludge(copy_of_B_path)
4177 # Bring the entire WC up to date with rev 5.
4178 svntest.actions.run_and_verify_svn(None, None, [], 'update', wc_dir)
4180 # Merge changes from rev 5 of B (to alpha) into copy_of_B.
4181 # A_COPY/copy_of_B/F/E and A_COPY/copy_of_B/F/E1 both exist in the merge
4182 # source at r5, so their empty mergeinfo should be updted with r5, which
4183 # then should elide to A_COPY/copy_of_B leaving no mergeinfo on either.
4184 expected_output = wc.State(short_copy_of_B_path, {
4185 'F/E/alpha' : Item(status='U '),
4187 expected_status = wc.State(short_copy_of_B_path, {
4188 '' : Item(status=' M', wc_rev=5),
4189 'F/E' : Item(status=' M', wc_rev=5),
4190 'F/E/alpha' : Item(status='M ', wc_rev=5),
4191 'F/E/beta' : Item(status=' ', wc_rev=5),
4192 'F/E1' : Item(status=' M', wc_rev=5),
4193 'F/E1/alpha' : Item(status=' ', wc_rev=5),
4194 'F/E1/beta' : Item(status=' ', wc_rev=5),
4195 'lambda' : Item(status=' ', wc_rev=5),
4196 'F' : Item(status=' ', wc_rev=5),
4198 expected_disk = wc.State('', {
4199 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B:5'}),
4200 'F/E' : Item(),
4201 'F/E/alpha' : Item(new_content_for_alpha),
4202 'F/E/beta' : Item("This is the file 'beta'.\n"),
4203 'F/E1' : Item(),
4204 'F/E1/alpha' : Item("This is the file 'alpha'.\n"),
4205 'F/E1/beta' : Item("This is the file 'beta'.\n"),
4206 'F' : Item(),
4207 'lambda' : Item("This is the file 'lambda'.\n")
4209 expected_skip = wc.State(short_copy_of_B_path, { })
4210 saved_cwd = os.getcwd()
4212 os.chdir(svntest.main.work_dir)
4213 svntest.actions.run_and_verify_merge(short_copy_of_B_path, '4', '5',
4214 sbox.repo_url + \
4215 '/A/B',
4216 expected_output,
4217 expected_disk,
4218 expected_status,
4219 expected_skip,
4220 None,
4221 None,
4222 None,
4223 None,
4224 None, 1)
4225 os.chdir(saved_cwd)
4227 # Commit the result of the merge, creating revision 6.
4228 expected_output = svntest.wc.State(copy_of_B_path, {
4229 '' : Item(verb='Sending'),
4230 'F/E' : Item(verb='Sending'),
4231 'F/E/alpha' : Item(verb='Sending'),
4232 'F/E1' : Item(verb='Sending'),
4234 svntest.actions.run_and_verify_commit(short_copy_of_B_path, expected_output,
4235 None, None, wc_dir)
4237 # Update the WC to bring /A/copy_of_B/F from rev 4 to rev 6.
4238 # Without this update, a subsequent merge will not find any merge
4239 # info for /A/copy_of_B/F -- nor its parent dir in the repos -- at
4240 # rev 4. Mergeinfo wasn't introduced until rev 6.
4241 copy_of_B_F_E_path = os.path.join(copy_of_B_path, 'F', 'E')
4242 svntest.actions.run_and_verify_svn(None, None, [], 'update', wc_dir)
4244 # Search for the comment entitled "The Merge Kluge" elsewhere in
4245 # this file, to understand why we shorten and chdir() below.
4246 short_copy_of_B_F_E_path = shorten_path_kludge(copy_of_B_F_E_path)
4248 # Attempt to re-merge changes to alpha from rev 4. Use the merge
4249 # info inherited from the grandparent (copy-of-B) of our merge
4250 # target (/A/copy-of-B/F/E) to avoid a repeated merge.
4251 expected_status = wc.State(short_copy_of_B_F_E_path, {
4252 '' : Item(status=' ', wc_rev=6),
4253 'alpha' : Item(status=' ', wc_rev=6),
4254 'beta' : Item(status=' ', wc_rev=6),
4256 os.chdir(svntest.main.work_dir)
4257 svntest.actions.run_and_verify_svn(None, [], [], 'merge', '-r4:5',
4258 sbox.repo_url + '/A/B/F/E',
4259 short_copy_of_B_F_E_path)
4260 svntest.actions.run_and_verify_status(short_copy_of_B_F_E_path,
4261 expected_status)
4262 os.chdir(saved_cwd)
4264 def avoid_repeated_merge_on_subtree_with_merge_info(sbox):
4265 "use subtree's mergeinfo to avoid repeated merge"
4266 # Create deep trees A/B/F/E and A/B/F/E1 and copy A/B to A/copy-of-B
4267 # with the help of 'create_deep_trees'
4268 # As /A/copy-of-B/F/E1 is not a child of /A/copy-of-B/F/E,
4269 # set_path should not be called on /A/copy-of-B/F/E1 while
4270 # doing a implicit subtree merge on /A/copy-of-B/F/E.
4271 sbox.build()
4272 wc_dir = sbox.wc_dir
4274 A_path = os.path.join(wc_dir, 'A')
4275 A_B_path = os.path.join(A_path, 'B')
4276 A_B_E_path = os.path.join(A_B_path, 'E')
4277 A_B_F_path = os.path.join(A_B_path, 'F')
4278 A_B_F_E_path = os.path.join(A_B_F_path, 'E')
4279 copy_of_B_path = os.path.join(A_path, 'copy-of-B')
4281 # Create a deeper directory structure.
4282 expected_status = create_deep_trees(wc_dir)
4284 # Edit alpha and commit it, creating revision 5.
4285 alpha_path = os.path.join(A_B_F_E_path, 'alpha')
4286 new_content_for_alpha1 = 'new content to alpha\n'
4287 svntest.main.file_write(alpha_path, new_content_for_alpha1)
4289 expected_output = svntest.wc.State(wc_dir, {
4290 'A/B/F/E/alpha' : Item(verb='Sending'),
4292 expected_status.tweak('A/B/F/E/alpha', wc_rev=5)
4293 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
4294 expected_status, None, wc_dir)
4296 for path_and_mergeinfo in (('E', '/A/B/F/E:5'),
4297 ('E1', '/A/B/F/E:5')):
4298 path_name = os.path.join(copy_of_B_path, 'F', path_and_mergeinfo[0])
4299 # Search for the comment entitled "The Merge Kluge" elsewhere in
4300 # this file, to understand why we shorten and chdir() below.
4301 short_path_name = shorten_path_kludge(path_name)
4303 # Merge r5 to path_name.
4304 expected_output = wc.State(short_path_name, {
4305 'alpha' : Item(status='U '),
4307 expected_status = wc.State(short_path_name, {
4308 '' : Item(status=' M', wc_rev=4),
4309 'alpha' : Item(status='M ', wc_rev=4),
4310 'beta' : Item(status=' ', wc_rev=4),
4312 expected_disk = wc.State('', {
4313 '' : Item(props={SVN_PROP_MERGEINFO : path_and_mergeinfo[1]}),
4314 'alpha' : Item(new_content_for_alpha1),
4315 'beta' : Item("This is the file 'beta'.\n"),
4317 expected_skip = wc.State(short_path_name, { })
4318 saved_cwd = os.getcwd()
4320 os.chdir(svntest.main.work_dir)
4321 svntest.actions.run_and_verify_merge(short_path_name, '4', '5',
4322 sbox.repo_url + '/A/B/F/E',
4323 expected_output,
4324 expected_disk,
4325 expected_status,
4326 expected_skip,
4327 None,
4328 None,
4329 None,
4330 None,
4331 None, 1)
4332 os.chdir(saved_cwd)
4334 # Commit the result of the merge, creating new revision.
4335 expected_output = svntest.wc.State(path_name, {
4336 '' : Item(verb='Sending'),
4337 'alpha' : Item(verb='Sending'),
4339 svntest.actions.run_and_verify_commit(short_path_name,
4340 expected_output, None, None, wc_dir)
4342 # Edit A/B/F/E/alpha and commit it, creating revision 8.
4343 new_content_for_alpha = 'new content to alpha\none more line\n'
4344 svntest.main.file_write(alpha_path, new_content_for_alpha)
4346 expected_output = svntest.wc.State(A_B_F_E_path, {
4347 'alpha' : Item(verb='Sending'),
4349 expected_status = wc.State(A_B_F_E_path, {
4350 '' : Item(status=' ', wc_rev=4),
4351 'alpha' : Item(status=' ', wc_rev=8),
4352 'beta' : Item(status=' ', wc_rev=4),
4354 svntest.actions.run_and_verify_commit(A_B_F_E_path, expected_output,
4355 expected_status, None, wc_dir)
4357 # Search for the comment entitled "The Merge Kluge" elsewhere in
4358 # this file, to understand why we shorten and chdir() below.
4359 short_copy_of_B_path = shorten_path_kludge(copy_of_B_path)
4361 # Update the WC to bring /A/copy_of_B to rev 8.
4362 # Without this update expected_status tree would be cumbersome to
4363 # understand.
4364 svntest.actions.run_and_verify_svn(None, None, [], 'update', wc_dir)
4366 # Merge changes from rev 4:8 of A/B into A/copy_of_B. A/copy_of_B/F/E1
4367 # has explicit mergeinfo and exists at r4 in the merge source, so it
4368 # should be treated as a subtree with intersecting mergeinfo and its
4369 # mergeinfo updated.
4370 expected_output = wc.State(short_copy_of_B_path, {
4371 'F/E/alpha' : Item(status='U ')
4373 expected_status = wc.State(short_copy_of_B_path, {
4374 # When we merge multiple sub-targets, we record mergeinfo on each
4375 # child.
4376 '' : Item(status=' M', wc_rev=8),
4377 'F/E' : Item(status=' M', wc_rev=8),
4378 'F/E/alpha' : Item(status='M ', wc_rev=8),
4379 'F/E/beta' : Item(status=' ', wc_rev=8),
4380 'F/E1' : Item(status=' M', wc_rev=8),
4381 'F/E1/alpha' : Item(status=' ', wc_rev=8),
4382 'F/E1/beta' : Item(status=' ', wc_rev=8),
4383 'lambda' : Item(status=' ', wc_rev=8),
4384 'F' : Item(status=' ', wc_rev=8)
4386 expected_disk = wc.State('', {
4387 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B:5-8'}),
4388 'F/E' : Item(props={}), # elision!
4389 'F/E/alpha' : Item(new_content_for_alpha),
4390 'F/E/beta' : Item("This is the file 'beta'.\n"),
4391 'F' : Item(),
4392 'F/E1' : Item(props={SVN_PROP_MERGEINFO :
4393 '/A/B/F/E:5\n/A/B/F/E1:5-8\n'}),
4394 'F/E1/alpha' : Item(new_content_for_alpha1),
4395 'F/E1/beta' : Item("This is the file 'beta'.\n"),
4396 'lambda' : Item("This is the file 'lambda'.\n")
4398 expected_skip = wc.State(short_copy_of_B_path, { })
4399 os.chdir(svntest.main.work_dir)
4400 svntest.actions.run_and_verify_merge(short_copy_of_B_path, '4', '8',
4401 sbox.repo_url + '/A/B',
4402 expected_output,
4403 expected_disk,
4404 expected_status,
4405 expected_skip,
4406 None,
4407 None,
4408 None,
4409 None,
4410 None, 1)
4412 def tweak_src_then_merge_to_dest(sbox, src_path, dst_path,
4413 canon_src_path, contents, cur_rev):
4414 """Edit src and commit it. This results in new_rev.
4415 Merge new_rev to dst_path. Return new_rev."""
4417 wc_dir = sbox.wc_dir
4418 new_rev = cur_rev + 1
4419 svntest.main.file_write(src_path, contents)
4421 expected_output = svntest.wc.State(src_path, {
4422 '': Item(verb='Sending'),
4425 expected_status = wc.State(src_path,
4426 { '': Item(wc_rev=new_rev, status=' ')})
4428 svntest.actions.run_and_verify_commit(src_path, expected_output,
4429 expected_status, None, src_path)
4431 # Update the WC to new_rev so that it would be easier to expect everyone
4432 # to be at new_rev.
4433 svntest.actions.run_and_verify_svn(None, None, [], 'update', wc_dir)
4435 # Merge new_rev of src_path to dst_path.
4437 # Search for the comment entitled "The Merge Kluge" elsewhere in
4438 # this file, to understand why we shorten and chdir() below.
4439 short_dst_path = shorten_path_kludge(dst_path)
4440 expected_status = wc.State(dst_path,
4441 { '': Item(wc_rev=new_rev, status='MM')})
4442 saved_cwd = os.getcwd()
4444 os.chdir(svntest.main.work_dir)
4446 merge_url = sbox.repo_url + '/' + canon_src_path
4447 if sys.platform == 'win32':
4448 merge_url = merge_url.replace('\\', '/')
4450 svntest.actions.run_and_verify_svn(None,
4451 expected_merge_output([[new_rev]],
4452 'U ' +
4453 short_dst_path +
4454 '\n'),
4456 'merge', '-c', str(new_rev),
4457 merge_url,
4458 short_dst_path)
4459 os.chdir(saved_cwd)
4461 svntest.actions.run_and_verify_status(dst_path, expected_status)
4463 return new_rev
4465 def obey_reporter_api_semantics_while_doing_subtree_merges(sbox):
4466 "drive reporter api in depth first order"
4468 # Copy /A/D to /A/copy-of-D it results in rONE.
4469 # Create children at different hierarchies having some merge-info
4470 # to test the set_path calls on a reporter in a depth-first order.
4471 # On all 'file' descendants of /A/copy-of-D/ we run merges.
4472 # We create /A/D/umlaut directly over URL it results in rev rTWO.
4473 # When we merge rONE+1:TWO of /A/D on /A/copy-of-D it should merge smoothly.
4475 sbox.build()
4476 wc_dir = sbox.wc_dir
4478 A_path = os.path.join(wc_dir, 'A')
4479 A_D_path = os.path.join(wc_dir, 'A', 'D')
4480 copy_of_A_D_path = os.path.join(wc_dir, 'A', 'copy-of-D')
4482 svntest.main.run_svn(None, "cp", A_D_path, copy_of_A_D_path)
4484 expected_output = svntest.wc.State(wc_dir, {
4485 'A/copy-of-D' : Item(verb='Adding'),
4487 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
4488 expected_status.add({
4489 'A/copy-of-D' : Item(status=' ', wc_rev=2),
4490 'A/copy-of-D/G' : Item(status=' ', wc_rev=2),
4491 'A/copy-of-D/G/pi' : Item(status=' ', wc_rev=2),
4492 'A/copy-of-D/G/rho' : Item(status=' ', wc_rev=2),
4493 'A/copy-of-D/G/tau' : Item(status=' ', wc_rev=2),
4494 'A/copy-of-D/H' : Item(status=' ', wc_rev=2),
4495 'A/copy-of-D/H/chi' : Item(status=' ', wc_rev=2),
4496 'A/copy-of-D/H/omega' : Item(status=' ', wc_rev=2),
4497 'A/copy-of-D/H/psi' : Item(status=' ', wc_rev=2),
4498 'A/copy-of-D/gamma' : Item(status=' ', wc_rev=2),
4500 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
4501 expected_status, None, wc_dir)
4504 cur_rev = 2
4505 for path in (["A", "D", "G", "pi"],
4506 ["A", "D", "G", "rho"],
4507 ["A", "D", "G", "tau"],
4508 ["A", "D", "H", "chi"],
4509 ["A", "D", "H", "omega"],
4510 ["A", "D", "H", "psi"],
4511 ["A", "D", "gamma"]):
4512 path_name = os.path.join(wc_dir, *path)
4513 canon_path_name = os.path.join(*path)
4514 path[1] = "copy-of-D"
4515 copy_of_path_name = os.path.join(wc_dir, *path)
4516 var_name = 'new_content_for_' + path[len(path) - 1]
4517 file_contents = "new content to " + path[len(path) - 1] + "\n"
4518 globals()[var_name] = file_contents
4519 cur_rev = tweak_src_then_merge_to_dest(sbox, path_name,
4520 copy_of_path_name, canon_path_name,
4521 file_contents, cur_rev)
4523 copy_of_A_D_wc_rev = cur_rev
4524 svntest.actions.run_and_verify_svn(None,
4525 ['\n',
4526 'Committed revision ' + str(cur_rev+1) +
4527 '.\n'],
4529 'mkdir', sbox.repo_url + '/A/D/umlaut',
4530 '-m', "log msg")
4531 rev_to_merge_to_copy_of_D = cur_rev + 1
4533 # Search for the comment entitled "The Merge Kluge" elsewhere in
4534 # this file, to understand why we shorten and chdir() below.
4535 short_copy_of_A_D_path = shorten_path_kludge(copy_of_A_D_path)
4537 # All the file descendants of /A/copy-of-D/ have already been merged
4538 # so the only notification we expect is for the added 'umlaut'.
4539 expected_output = wc.State(short_copy_of_A_D_path, {
4540 'umlaut' : Item(status='A '),
4543 # All the local svn:mergeinfo under A/copy-of-D elides
4544 # to A/copy-of-D.
4545 expected_status = wc.State(short_copy_of_A_D_path, {
4546 '' : Item(status=' M', wc_rev=copy_of_A_D_wc_rev),
4547 'G' : Item(status=' ', wc_rev=copy_of_A_D_wc_rev),
4548 'G/pi' : Item(status='M ', wc_rev=copy_of_A_D_wc_rev),
4549 'G/rho' : Item(status='M ', wc_rev=copy_of_A_D_wc_rev),
4550 'G/tau' : Item(status='M ', wc_rev=copy_of_A_D_wc_rev),
4551 'H' : Item(status=' ', wc_rev=copy_of_A_D_wc_rev),
4552 'H/chi' : Item(status='M ', wc_rev=copy_of_A_D_wc_rev),
4553 'H/omega' : Item(status='M ', wc_rev=copy_of_A_D_wc_rev),
4554 'H/psi' : Item(status='M ', wc_rev=copy_of_A_D_wc_rev),
4555 'gamma' : Item(status='M ', wc_rev=copy_of_A_D_wc_rev),
4556 'umlaut' : Item(status='A ', copied='+', wc_rev='-'),
4559 merged_rangelist = "3-%d" % rev_to_merge_to_copy_of_D
4562 expected_disk = wc.State('', {
4563 '' : Item(props={SVN_PROP_MERGEINFO : '/A/D:' + merged_rangelist}),
4564 'G' : Item(),
4565 'G/pi' : Item(new_content_for_pi),
4566 'G/rho' : Item(new_content_for_rho),
4567 'G/tau' : Item(new_content_for_tau),
4568 'H' : Item(),
4569 'H/chi' : Item(new_content_for_chi,),
4570 'H/omega' : Item(new_content_for_omega,),
4571 'H/psi' : Item(new_content_for_psi,),
4572 'gamma' : Item(new_content_for_gamma,),
4573 'umlaut' : Item(),
4575 expected_skip = wc.State(short_copy_of_A_D_path, { })
4576 os.chdir(svntest.main.work_dir)
4577 svntest.actions.run_and_verify_merge(short_copy_of_A_D_path,
4579 str(rev_to_merge_to_copy_of_D),
4580 sbox.repo_url + '/A/D',
4581 expected_output,
4582 expected_disk,
4583 expected_status,
4584 expected_skip,
4585 None,
4586 None,
4587 None,
4588 None,
4589 None, 1)
4591 def set_up_branch(sbox, branch_only = False, nbr_of_branches = 1):
4592 '''Starting with standard greek tree, copy 'A' NBR_OF_BRANCHES times
4593 to A_COPY, A_COPY_2, A_COPY_3, and so on. Then make four modifications
4594 (setting file contents to "New content") under A:
4595 r(2 + NBR_OF_BRANCHES) - A/D/H/psi
4596 r(3 + NBR_OF_BRANCHES) - A/D/G/rho
4597 r(4 + NBR_OF_BRANCHES) - A/B/E/beta
4598 r(5 + NBR_OF_BRANCHES) - A/D/H/omega'''
4600 wc_dir = sbox.wc_dir
4602 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
4603 expected_disk = svntest.main.greek_state.copy()
4605 def copy_A(dest_name, rev):
4606 expected = svntest.verify.UnorderedOutput(
4607 ["A " + os.path.join(wc_dir, dest_name, "B") + "\n",
4608 "A " + os.path.join(wc_dir, dest_name, "B", "lambda") + "\n",
4609 "A " + os.path.join(wc_dir, dest_name, "B", "E") + "\n",
4610 "A " + os.path.join(wc_dir, dest_name, "B", "E", "alpha") + "\n",
4611 "A " + os.path.join(wc_dir, dest_name, "B", "E", "beta") + "\n",
4612 "A " + os.path.join(wc_dir, dest_name, "B", "F") + "\n",
4613 "A " + os.path.join(wc_dir, dest_name, "mu") + "\n",
4614 "A " + os.path.join(wc_dir, dest_name, "C") + "\n",
4615 "A " + os.path.join(wc_dir, dest_name, "D") + "\n",
4616 "A " + os.path.join(wc_dir, dest_name, "D", "gamma") + "\n",
4617 "A " + os.path.join(wc_dir, dest_name, "D", "G") + "\n",
4618 "A " + os.path.join(wc_dir, dest_name, "D", "G", "pi") + "\n",
4619 "A " + os.path.join(wc_dir, dest_name, "D", "G", "rho") + "\n",
4620 "A " + os.path.join(wc_dir, dest_name, "D", "G", "tau") + "\n",
4621 "A " + os.path.join(wc_dir, dest_name, "D", "H") + "\n",
4622 "A " + os.path.join(wc_dir, dest_name, "D", "H", "chi") + "\n",
4623 "A " + os.path.join(wc_dir, dest_name, "D", "H", "omega") + "\n",
4624 "A " + os.path.join(wc_dir, dest_name, "D", "H", "psi") + "\n",
4625 "Checked out revision " + str(rev - 1) + ".\n",
4626 "A " + os.path.join(wc_dir, dest_name) + "\n"])
4627 expected_status.add({
4628 dest_name + "/B" : Item(status=' ', wc_rev=rev),
4629 dest_name + "/B/lambda" : Item(status=' ', wc_rev=rev),
4630 dest_name + "/B/E" : Item(status=' ', wc_rev=rev),
4631 dest_name + "/B/E/alpha" : Item(status=' ', wc_rev=rev),
4632 dest_name + "/B/E/beta" : Item(status=' ', wc_rev=rev),
4633 dest_name + "/B/F" : Item(status=' ', wc_rev=rev),
4634 dest_name + "/mu" : Item(status=' ', wc_rev=rev),
4635 dest_name + "/C" : Item(status=' ', wc_rev=rev),
4636 dest_name + "/D" : Item(status=' ', wc_rev=rev),
4637 dest_name + "/D/gamma" : Item(status=' ', wc_rev=rev),
4638 dest_name + "/D/G" : Item(status=' ', wc_rev=rev),
4639 dest_name + "/D/G/pi" : Item(status=' ', wc_rev=rev),
4640 dest_name + "/D/G/rho" : Item(status=' ', wc_rev=rev),
4641 dest_name + "/D/G/tau" : Item(status=' ', wc_rev=rev),
4642 dest_name + "/D/H" : Item(status=' ', wc_rev=rev),
4643 dest_name + "/D/H/chi" : Item(status=' ', wc_rev=rev),
4644 dest_name + "/D/H/omega" : Item(status=' ', wc_rev=rev),
4645 dest_name + "/D/H/psi" : Item(status=' ', wc_rev=rev),
4646 dest_name : Item(status=' ', wc_rev=rev)})
4647 expected_disk.add({
4648 dest_name : Item(),
4649 dest_name + '/B' : Item(),
4650 dest_name + '/B/lambda' : Item("This is the file 'lambda'.\n"),
4651 dest_name + '/B/E' : Item(),
4652 dest_name + '/B/E/alpha' : Item("This is the file 'alpha'.\n"),
4653 dest_name + '/B/E/beta' : Item("This is the file 'beta'.\n"),
4654 dest_name + '/B/F' : Item(),
4655 dest_name + '/mu' : Item("This is the file 'mu'.\n"),
4656 dest_name + '/C' : Item(),
4657 dest_name + '/D' : Item(),
4658 dest_name + '/D/gamma' : Item("This is the file 'gamma'.\n"),
4659 dest_name + '/D/G' : Item(),
4660 dest_name + '/D/G/pi' : Item("This is the file 'pi'.\n"),
4661 dest_name + '/D/G/rho' : Item("This is the file 'rho'.\n"),
4662 dest_name + '/D/G/tau' : Item("This is the file 'tau'.\n"),
4663 dest_name + '/D/H' : Item(),
4664 dest_name + '/D/H/chi' : Item("This is the file 'chi'.\n"),
4665 dest_name + '/D/H/omega' : Item("This is the file 'omega'.\n"),
4666 dest_name + '/D/H/psi' : Item("This is the file 'psi'.\n"),
4669 # Make a branch A_COPY to merge into.
4670 svntest.actions.run_and_verify_svn(None, expected, [], 'copy',
4671 sbox.repo_url + "/A",
4672 os.path.join(wc_dir,
4673 dest_name))
4675 expected_output = wc.State(wc_dir, {dest_name : Item(verb='Adding')})
4676 svntest.actions.run_and_verify_commit(wc_dir,
4677 expected_output,
4678 expected_status,
4679 None,
4680 wc_dir)
4681 for i in range(nbr_of_branches):
4682 if i == 0:
4683 copy_A('A_COPY', i + 2)
4684 else:
4685 copy_A('A_COPY_' + str(i + 1), i + 2)
4687 if (branch_only):
4688 return expected_disk, expected_status
4690 # Make some changes under A which we'll later merge under A_COPY:
4692 # r(nbr_of_branches + 2) - modify and commit A/D/H/psi
4693 svntest.main.file_write(os.path.join(wc_dir, "A", "D", "H", "psi"),
4694 "New content")
4695 expected_output = wc.State(wc_dir, {'A/D/H/psi' : Item(verb='Sending')})
4696 expected_status.tweak('A/D/H/psi', wc_rev=nbr_of_branches + 2)
4697 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
4698 expected_status, None, wc_dir)
4699 expected_disk.tweak('A/D/H/psi', contents="New content")
4701 # r(nbr_of_branches + 3) - modify and commit A/D/G/rho
4702 svntest.main.file_write(os.path.join(wc_dir, "A", "D", "G", "rho"),
4703 "New content")
4704 expected_output = wc.State(wc_dir, {'A/D/G/rho' : Item(verb='Sending')})
4705 expected_status.tweak('A/D/G/rho', wc_rev=nbr_of_branches + 3)
4706 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
4707 expected_status, None, wc_dir)
4708 expected_disk.tweak('A/D/G/rho', contents="New content")
4710 # r(nbr_of_branches + 4) - modify and commit A/B/E/beta
4711 svntest.main.file_write(os.path.join(wc_dir, "A", "B", "E", "beta"),
4712 "New content")
4713 expected_output = wc.State(wc_dir, {'A/B/E/beta' : Item(verb='Sending')})
4714 expected_status.tweak('A/B/E/beta', wc_rev=nbr_of_branches + 4)
4715 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
4716 expected_status, None, wc_dir)
4717 expected_disk.tweak('A/B/E/beta', contents="New content")
4719 # r(nbr_of_branches + 5) - modify and commit A/D/H/omega
4720 svntest.main.file_write(os.path.join(wc_dir, "A", "D", "H", "omega"),
4721 "New content")
4722 expected_output = wc.State(wc_dir, {'A/D/H/omega' : Item(verb='Sending')})
4723 expected_status.tweak('A/D/H/omega', wc_rev=nbr_of_branches + 5)
4724 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
4725 expected_status, None, wc_dir)
4726 expected_disk.tweak('A/D/H/omega', contents="New content")
4728 return expected_disk, expected_status
4731 def mergeinfo_inheritance(sbox):
4732 "target inherits mergeinfo from nearest ancestor"
4734 # Test for Issues #2733 and #2734.
4736 # When the target of a merge has no explicit mergeinfo and the merge
4737 # would result in mergeinfo being added to the target which...
4739 # ...is a subset of the *local* mergeinfo on one of the target's
4740 # ancestors (it's nearest ancestor takes precedence), then the merge is
4741 # not repeated and no mergeinfo should be set on the target (Issue #2734).
4743 # OR
4745 # ...is not a subset it's nearest ancestor, the target should inherit the
4746 # non-inersecting mergeinfo (local or committed, the former takes
4747 # precedence) from it's nearest ancestor (Issue #2733).
4749 sbox.build()
4750 wc_dir = sbox.wc_dir
4751 wc_disk, wc_status = set_up_branch(sbox)
4753 # Some paths we'll care about
4754 A_COPY_path = os.path.join(wc_dir, "A_COPY")
4755 B_COPY_path = os.path.join(wc_dir, "A_COPY", "B")
4756 beta_COPY_path = os.path.join(wc_dir, "A_COPY", "B", "E", "beta")
4757 E_COPY_path = os.path.join(wc_dir, "A_COPY", "B", "E")
4758 omega_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "H", "omega")
4759 D_COPY_path = os.path.join(wc_dir, "A_COPY", "D")
4760 G_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "G")
4762 # Now start merging...
4764 # Merge r4 into A_COPY/D/
4765 # Search for the comment entitled "The Merge Kluge" elsewhere in
4766 # this file, to understand why we shorten and chdir() below.
4767 short_D_COPY_path = shorten_path_kludge(D_COPY_path)
4768 expected_output = wc.State(short_D_COPY_path, {
4769 'G/rho' : Item(status='U '),
4771 expected_status = wc.State(short_D_COPY_path, {
4772 '' : Item(status=' M', wc_rev=2),
4773 'G' : Item(status=' ', wc_rev=2),
4774 'G/pi' : Item(status=' ', wc_rev=2),
4775 'G/rho' : Item(status='M ', wc_rev=2),
4776 'G/tau' : Item(status=' ', wc_rev=2),
4777 'H' : Item(status=' ', wc_rev=2),
4778 'H/chi' : Item(status=' ', wc_rev=2),
4779 'H/psi' : Item(status=' ', wc_rev=2),
4780 'H/omega' : Item(status=' ', wc_rev=2),
4781 'gamma' : Item(status=' ', wc_rev=2),
4783 # We test issue #2733 here (with a directory as the merge target).
4784 # r1 should be inherited from 'A_COPY'.
4785 expected_disk = wc.State('', {
4786 '' : Item(props={SVN_PROP_MERGEINFO : '/A/D:4'}),
4787 'G' : Item(),
4788 'G/pi' : Item("This is the file 'pi'.\n"),
4789 'G/rho' : Item("New content"),
4790 'G/tau' : Item("This is the file 'tau'.\n"),
4791 'H' : Item(),
4792 'H/chi' : Item("This is the file 'chi'.\n"),
4793 'H/psi' : Item("This is the file 'psi'.\n"),
4794 'H/omega' : Item("This is the file 'omega'.\n"),
4795 'gamma' : Item("This is the file 'gamma'.\n")
4797 expected_skip = wc.State(short_D_COPY_path, { })
4798 saved_cwd = os.getcwd()
4799 os.chdir(svntest.main.work_dir)
4800 svntest.actions.run_and_verify_merge(short_D_COPY_path, '3', '4',
4801 sbox.repo_url + \
4802 '/A/D',
4803 expected_output,
4804 expected_disk,
4805 expected_status,
4806 expected_skip,
4807 None, None, None, None,
4808 None, 1)
4809 os.chdir(saved_cwd)
4811 # Merge r4 again, this time into A_COPY/D/G. An ancestor directory
4812 # (A_COPY/D) exists with identical local mergeinfo, so the merge
4813 # should not be repeated. We test issue #2734 here with (with a
4814 # directory as the merge target).
4815 short_G_COPY_path = shorten_path_kludge(G_COPY_path)
4816 expected_output = wc.State(short_G_COPY_path, { })
4817 expected_status = wc.State(short_G_COPY_path, {
4818 '' : Item(status=' ', wc_rev=2),
4819 'pi' : Item(status=' ', wc_rev=2),
4820 'rho' : Item(status='M ', wc_rev=2),
4821 'tau' : Item(status=' ', wc_rev=2),
4823 expected_disk = wc.State('', {
4824 'pi' : Item("This is the file 'pi'.\n"),
4825 'rho' : Item("New content"),
4826 'tau' : Item("This is the file 'tau'.\n"),
4828 expected_skip = wc.State(short_G_COPY_path, { })
4829 os.chdir(svntest.main.work_dir)
4830 svntest.actions.run_and_verify_merge(short_G_COPY_path, '3', '4',
4831 sbox.repo_url + \
4832 '/A/D/G',
4833 expected_output,
4834 expected_disk,
4835 expected_status,
4836 expected_skip,
4837 None, None, None, None,
4838 None, 1)
4839 os.chdir(saved_cwd)
4840 # Merge r5 into A_COPY/B. Again, r1 should be inherited from
4841 # A_COPY (Issue #2733)
4842 short_B_COPY_path = shorten_path_kludge(B_COPY_path)
4843 expected_output = wc.State(short_B_COPY_path, {
4844 'E/beta' : Item(status='U '),
4846 expected_status = wc.State(short_B_COPY_path, {
4847 '' : Item(status=' M', wc_rev=2),
4848 'E' : Item(status=' ', wc_rev=2),
4849 'E/alpha' : Item(status=' ', wc_rev=2),
4850 'E/beta' : Item(status='M ', wc_rev=2),
4851 'lambda' : Item(status=' ', wc_rev=2),
4852 'F' : Item(status=' ', wc_rev=2),
4854 expected_disk = wc.State('', {
4855 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B:5'}),
4856 'E' : Item(),
4857 'E/alpha' : Item("This is the file 'alpha'.\n"),
4858 'E/beta' : Item("New content"),
4859 'F' : Item(),
4860 'lambda' : Item("This is the file 'lambda'.\n")
4862 expected_skip = wc.State(short_B_COPY_path, { })
4864 os.chdir(svntest.main.work_dir)
4865 svntest.actions.run_and_verify_merge(short_B_COPY_path, '4', '5',
4866 sbox.repo_url + \
4867 '/A/B',
4868 expected_output,
4869 expected_disk,
4870 expected_status,
4871 expected_skip,
4872 None, None, None, None,
4873 None, 1)
4874 os.chdir(saved_cwd)
4876 # Merge r5 again, this time into A_COPY/B/E/beta. An ancestor
4877 # directory (A_COPY/B) exists with identical local mergeinfo, so
4878 # the merge should not be repeated (Issue #2734 with a file as the
4879 # merge target).
4880 short_beta_COPY_path = shorten_path_kludge(beta_COPY_path)
4881 expected_skip = wc.State(short_beta_COPY_path, { })
4882 saved_cwd = os.getcwd()
4884 os.chdir(svntest.main.work_dir)
4885 # run_and_verify_merge doesn't support merging to a file WCPATH
4886 # so use run_and_verify_svn.
4887 svntest.actions.run_and_verify_svn(None, [], [], 'merge', '-c5',
4888 sbox.repo_url + '/A/B/E/beta',
4889 short_beta_COPY_path)
4890 os.chdir(saved_cwd)
4892 # The merge wasn't repeated so beta shouldn't have any mergeinfo.
4893 # We are implicitly testing that without looking at the prop value
4894 # itself, just beta's prop modification status.
4895 expected_status = wc.State(beta_COPY_path, {
4896 '' : Item(status='M ', wc_rev=2),
4898 svntest.actions.run_and_verify_status(beta_COPY_path, expected_status)
4900 # Merge r3 into A_COPY. A_COPY's two descendants with mergeinfo,
4901 # A_COPY/B/E/beta and A_COPY/D/G/rho must have complete mergeinfo
4902 # so they both should pick up r3 too.
4903 short_A_COPY_path = shorten_path_kludge(A_COPY_path)
4904 expected_output = wc.State(short_A_COPY_path, {
4905 'D/H/psi' : Item(status='U '),
4907 expected_status = wc.State(short_A_COPY_path, {
4908 '' : Item(status=' M', wc_rev=2),
4909 'B' : Item(status=' M', wc_rev=2),
4910 'mu' : Item(status=' ', wc_rev=2),
4911 'B/E' : Item(status=' ', wc_rev=2),
4912 'B/E/alpha' : Item(status=' ', wc_rev=2),
4913 'B/E/beta' : Item(status='M ', wc_rev=2),
4914 'B/lambda' : Item(status=' ', wc_rev=2),
4915 'B/F' : Item(status=' ', wc_rev=2),
4916 'C' : Item(status=' ', wc_rev=2),
4917 'D' : Item(status=' M', wc_rev=2),
4918 'D/G' : Item(status=' ', wc_rev=2),
4919 'D/G/pi' : Item(status=' ', wc_rev=2),
4920 'D/G/rho' : Item(status='M ', wc_rev=2),
4921 'D/G/tau' : Item(status=' ', wc_rev=2),
4922 'D/gamma' : Item(status=' ', wc_rev=2),
4923 'D/H' : Item(status=' ', wc_rev=2),
4924 'D/H/chi' : Item(status=' ', wc_rev=2),
4925 'D/H/psi' : Item(status='M ', wc_rev=2),
4926 'D/H/omega' : Item(status=' ', wc_rev=2),
4928 expected_disk = wc.State('', {
4929 '' : Item(props={SVN_PROP_MERGEINFO : '/A:3'}),
4930 'B' : Item(props={SVN_PROP_MERGEINFO : '/A/B:3,5'}),
4931 'mu' : Item("This is the file 'mu'.\n"),
4932 'B/E' : Item(),
4933 'B/E/alpha' : Item("This is the file 'alpha'.\n"),
4934 'B/E/beta' : Item("New content"),
4935 'B/lambda' : Item("This is the file 'lambda'.\n"),
4936 'B/F' : Item(),
4937 'C' : Item(),
4938 'D' : Item(props={SVN_PROP_MERGEINFO : '/A/D:3-4'}),
4939 'D/G' : Item(),
4940 'D/G/pi' : Item("This is the file 'pi'.\n"),
4941 'D/G/rho' : Item("New content"),
4942 'D/G/tau' : Item("This is the file 'tau'.\n"),
4943 'D/gamma' : Item("This is the file 'gamma'.\n"),
4944 'D/H' : Item(),
4945 'D/H/chi' : Item("This is the file 'chi'.\n"),
4946 'D/H/psi' : Item("New content"),
4947 'D/H/omega' : Item("This is the file 'omega'.\n"),
4949 expected_skip = wc.State(short_A_COPY_path, { })
4950 saved_cwd = os.getcwd()
4951 os.chdir(svntest.main.work_dir)
4952 svntest.actions.run_and_verify_merge(short_A_COPY_path, '2', '3',
4953 sbox.repo_url + \
4954 '/A',
4955 expected_output,
4956 expected_disk,
4957 expected_status,
4958 expected_skip,
4959 None, None, None, None,
4960 None, 1)
4961 os.chdir(saved_cwd)
4963 # Merge r6 into A_COPY/D/H/omega, it should inherit it's nearest
4964 # ancestor's (A_COPY/D) mergeinfo (Issue #2733 with a file as the
4965 # merge target).
4966 short_omega_COPY_path = shorten_path_kludge(omega_COPY_path)
4967 expected_skip = wc.State(short_omega_COPY_path, { })
4968 saved_cwd = os.getcwd()
4969 os.chdir(svntest.main.work_dir)
4970 # run_and_verify_merge doesn't support merging to a file WCPATH
4971 # so use run_and_verify_svn.
4972 svntest.actions.run_and_verify_svn(None,
4973 expected_merge_output([[6]],
4974 'U ' + short_omega_COPY_path + '\n'),
4975 [], 'merge', '-c6',
4976 sbox.repo_url + '/A/D/H/omega',
4977 short_omega_COPY_path)
4978 os.chdir(saved_cwd)
4980 # Check that mergeinfo was properly set on A_COPY/D/H/omega
4981 svntest.actions.run_and_verify_svn(None,
4982 ["/A/D/H/omega:3-4,6\n"],
4984 'propget', SVN_PROP_MERGEINFO,
4985 omega_COPY_path)
4987 # Given a merge target *without* any of the following:
4989 # 1) Explicit mergeinfo set on itself in the WC
4990 # 2) Any WC ancestor to inherit mergeinfo from
4991 # 3) Any mergeinfo for the target in the repository
4993 # Check that the target still inherits mergeinfo from it's nearest
4994 # repository ancestor.
4996 # Commit all the merges thus far
4997 expected_output = wc.State(wc_dir, {
4998 'A_COPY' : Item(verb='Sending'),
4999 'A_COPY/B' : Item(verb='Sending'),
5000 'A_COPY/B/E/beta' : Item(verb='Sending'),
5001 'A_COPY/D' : Item(verb='Sending'),
5002 'A_COPY/D/G/rho' : Item(verb='Sending'),
5003 'A_COPY/D/H/omega' : Item(verb='Sending'),
5004 'A_COPY/D/H/psi' : Item(verb='Sending'),
5006 wc_status.tweak('A_COPY', 'A_COPY/B', 'A_COPY/B/E/beta', 'A_COPY/D',
5007 'A_COPY/D/G/rho', 'A_COPY/D/H/omega', 'A_COPY/D/H/psi',
5008 wc_rev=7)
5009 svntest.actions.run_and_verify_commit(wc_dir,
5010 expected_output,
5011 wc_status,
5012 None,
5013 wc_dir)
5015 # Copy the subtree A_COPY/B/E from the working copy, making the
5016 # disconnected WC E_only.
5017 other_wc = sbox.add_wc_path('E_only')
5018 svntest.actions.duplicate_dir(E_COPY_path, other_wc)
5020 # Update the disconnected WC it so it will get the most recent mergeinfo
5021 # from the repos when merging.
5022 svntest.actions.run_and_verify_svn(None, ["At revision 7.\n"], [], 'up',
5023 other_wc)
5025 # Merge r5:4 into the root of the disconnected WC.
5026 # E_only has no explicit mergeinfo and since it's the root of the WC
5027 # cannot inherit and mergeinfo from a working copy ancestor path. Nor
5028 # does it have any mergeinfo explicitly set on it in the repository.
5029 # An ancestor path on the repository side, A_COPY/B does have the merge
5030 # info '/A/B:1,3,5' however and E_only should inherit this, resulting in
5031 # mergeinfo of 'A/B/E:1,3' after the removal of r5.
5032 short_other_wc_path = shorten_path_kludge(other_wc)
5033 expected_output = wc.State(short_other_wc_path,
5034 {'beta' : Item(status='U ')})
5035 expected_status = wc.State(short_other_wc_path, {
5036 '' : Item(status=' M', wc_rev=7),
5037 'alpha' : Item(status=' ', wc_rev=7),
5038 'beta' : Item(status='M ', wc_rev=7),
5040 expected_disk = wc.State('', {
5041 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B/E:3'}),
5042 'alpha' : Item("This is the file 'alpha'.\n"),
5043 'beta' : Item("This is the file 'beta'.\n"),
5045 expected_skip = wc.State(short_other_wc_path, { })
5047 os.chdir(svntest.main.work_dir)
5048 svntest.actions.run_and_verify_merge(short_other_wc_path, '5', '4',
5049 sbox.repo_url + \
5050 '/A/B/E',
5051 expected_output,
5052 expected_disk,
5053 expected_status,
5054 expected_skip,
5055 None, None, None, None,
5056 None, 1)
5058 def mergeinfo_elision(sbox):
5059 "mergeinfo elides to ancestor with identical info"
5061 # When a merge would result in mergeinfo on a target which is identical
5062 # to mergeinfo (local or committed) on one of the node's ancestors (the
5063 # nearest ancestor takes precedence), then the mergeinfo elides from the
5064 # target to the nearest ancestor (e.g. no mergeinfo is set on the target
5065 # or committed mergeinfo is removed).
5067 sbox.build()
5068 wc_dir = sbox.wc_dir
5069 wc_disk, wc_status = set_up_branch(sbox)
5071 # Some paths we'll care about
5072 A_COPY_path = os.path.join(wc_dir, "A_COPY")
5073 beta_COPY_path = os.path.join(wc_dir, "A_COPY", "B", "E", "beta")
5074 G_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "G")
5076 # Now start merging...
5078 # Merge r5 into A_COPY/B/E/beta.
5079 short_beta_COPY_path = shorten_path_kludge(beta_COPY_path)
5080 expected_skip = wc.State(short_beta_COPY_path, { })
5081 saved_cwd = os.getcwd()
5083 os.chdir(svntest.main.work_dir)
5084 # run_and_verify_merge doesn't support merging to a file WCPATH
5085 # so use run_and_verify_svn.
5086 svntest.actions.run_and_verify_svn(None,
5087 expected_merge_output([[5]],
5088 'U ' + short_beta_COPY_path + '\n'),
5089 [], 'merge', '-c5',
5090 sbox.repo_url + '/A/B/E/beta',
5091 short_beta_COPY_path)
5092 os.chdir(saved_cwd)
5094 # Check beta's status and props.
5095 expected_status = wc.State(beta_COPY_path, {
5096 '' : Item(status='MM', wc_rev=2),
5098 svntest.actions.run_and_verify_status(beta_COPY_path, expected_status)
5100 svntest.actions.run_and_verify_svn(None, ["/A/B/E/beta:5\n"], [],
5101 'propget', SVN_PROP_MERGEINFO,
5102 beta_COPY_path)
5104 # Commit the merge
5105 expected_output = wc.State(wc_dir, {
5106 'A_COPY/B/E/beta' : Item(verb='Sending'),
5108 wc_status.tweak('A_COPY/B/E/beta', wc_rev=7)
5109 svntest.actions.run_and_verify_commit(wc_dir,
5110 expected_output,
5111 wc_status,
5112 None,
5113 wc_dir)
5115 # Update A_COPY to get all paths to the same working revision.
5116 svntest.actions.run_and_verify_svn(None, ["At revision 7.\n"], [],
5117 'up', wc_dir)
5118 wc_status.tweak(wc_rev=7)
5120 # Merge r4 into A_COPY/D/G.
5121 # Search for the comment entitled "The Merge Kluge" elsewhere in
5122 # this file, to understand why we shorten and chdir() below.
5123 short_G_COPY_path = shorten_path_kludge(G_COPY_path)
5124 expected_output = wc.State(short_G_COPY_path, {
5125 'rho' : Item(status='U ')
5127 expected_status = wc.State(short_G_COPY_path, {
5128 '' : Item(status=' M', wc_rev=7),
5129 'pi' : Item(status=' ', wc_rev=7),
5130 'rho' : Item(status='M ', wc_rev=7),
5131 'tau' : Item(status=' ', wc_rev=7),
5133 expected_disk = wc.State('', {
5134 '' : Item(props={SVN_PROP_MERGEINFO : '/A/D/G:4'}),
5135 'pi' : Item("This is the file 'pi'.\n"),
5136 'rho' : Item("New content"),
5137 'tau' : Item("This is the file 'tau'.\n"),
5139 expected_skip = wc.State(short_G_COPY_path, { })
5141 os.chdir(svntest.main.work_dir)
5142 svntest.actions.run_and_verify_merge(short_G_COPY_path, '3', '4',
5143 sbox.repo_url + \
5144 '/A/D/G',
5145 expected_output,
5146 expected_disk,
5147 expected_status,
5148 expected_skip,
5149 None, None, None, None,
5150 None, 1)
5151 os.chdir(saved_cwd)
5153 # Merge r3:6 into A_COPY. This would result in identical mergeinfo
5154 # (r4-6) on A_COPY and two of it's descendants, A_COPY/D/G and
5155 # A_COPY/B/E/beta, so the mergeinfo on the latter two should elide
5156 # to A_COPY. In the case of A_COPY/D/G this means its wholly uncommitted
5157 # mergeinfo is removed leaving no prop mods. In the case of
5158 # A_COPY/B/E/beta its committed mergeinfo prop is removed leaving a prop
5159 # change.
5160 short_A_COPY_path = shorten_path_kludge(A_COPY_path)
5161 expected_output = wc.State(short_A_COPY_path, {
5162 'D/H/omega' : Item(status='U ')
5164 expected_status = wc.State(short_A_COPY_path, {
5165 '' : Item(status=' M', wc_rev=7),
5166 'B' : Item(status=' ', wc_rev=7),
5167 'mu' : Item(status=' ', wc_rev=7),
5168 'B/E' : Item(status=' ', wc_rev=7),
5169 'B/E/alpha' : Item(status=' ', wc_rev=7),
5170 'B/E/beta' : Item(status=' M', wc_rev=7),
5171 'B/lambda' : Item(status=' ', wc_rev=7),
5172 'B/F' : Item(status=' ', wc_rev=7),
5173 'C' : Item(status=' ', wc_rev=7),
5174 'D' : Item(status=' ', wc_rev=7),
5175 'D/G' : Item(status=' ', wc_rev=7),
5176 'D/G/pi' : Item(status=' ', wc_rev=7),
5177 'D/G/rho' : Item(status='M ', wc_rev=7),
5178 'D/G/tau' : Item(status=' ', wc_rev=7),
5179 'D/gamma' : Item(status=' ', wc_rev=7),
5180 'D/H' : Item(status=' ', wc_rev=7),
5181 'D/H/chi' : Item(status=' ', wc_rev=7),
5182 'D/H/psi' : Item(status=' ', wc_rev=7),
5183 'D/H/omega' : Item(status='M ', wc_rev=7),
5185 expected_disk = wc.State('', {
5186 '' : Item(props={SVN_PROP_MERGEINFO : '/A:4-6'}),
5187 'B' : Item(),
5188 'mu' : Item("This is the file 'mu'.\n"),
5189 'B/E' : Item(),
5190 'B/E/alpha' : Item("This is the file 'alpha'.\n"),
5191 'B/E/beta' : Item("New content"),
5192 'B/lambda' : Item("This is the file 'lambda'.\n"),
5193 'B/F' : Item(),
5194 'C' : Item(),
5195 'D' : Item(),
5196 'D/G' : Item(),
5197 'D/G/pi' : Item("This is the file 'pi'.\n"),
5198 'D/G/rho' : Item("New content"),
5199 'D/G/tau' : Item("This is the file 'tau'.\n"),
5200 'D/gamma' : Item("This is the file 'gamma'.\n"),
5201 'D/H' : Item(),
5202 'D/H/chi' : Item("This is the file 'chi'.\n"),
5203 'D/H/psi' : Item("This is the file 'psi'.\n"),
5204 'D/H/omega' : Item("New content"),
5206 expected_skip = wc.State(short_A_COPY_path, { })
5208 os.chdir(svntest.main.work_dir)
5209 svntest.actions.run_and_verify_merge(short_A_COPY_path, '3', '6',
5210 sbox.repo_url + \
5211 '/A',
5212 expected_output,
5213 expected_disk,
5214 expected_status,
5215 expected_skip,
5216 None, None, None, None,
5217 None, 1)
5218 os.chdir(saved_cwd)
5220 # Reverse merge r5 out of A_COPY/B/E/beta. The mergeinfo on
5221 # A_COPY/B/E/beta which previously elided will now return,
5222 # minus r5 of course.
5223 expected_skip = wc.State(short_beta_COPY_path, { })
5225 os.chdir(svntest.main.work_dir)
5226 # run_and_verify_merge doesn't support merging to a file WCPATH
5227 # so use run_and_verify_svn.
5228 svntest.actions.run_and_verify_svn(None,
5229 expected_merge_output([[-5]],
5230 'U ' + short_beta_COPY_path + '\n'),
5231 [], 'merge', '-c-5',
5232 sbox.repo_url + '/A/B/E/beta',
5233 short_beta_COPY_path)
5234 os.chdir(saved_cwd)
5236 # Check beta's status and props.
5237 expected_status = wc.State(beta_COPY_path, {
5238 '' : Item(status='MM', wc_rev=7),
5240 svntest.actions.run_and_verify_status(beta_COPY_path, expected_status)
5242 svntest.actions.run_and_verify_svn(None, ["/A/B/E/beta:4,6\n"], [],
5243 'propget', SVN_PROP_MERGEINFO,
5244 beta_COPY_path)
5246 # Merge r5 back into A_COPY/B/E/beta. Now the mergeinfo on the merge
5247 # target (A_COPY/B/E/beta) is identical to it's nearest ancestor with
5248 # mergeinfo (A_COPY) and so the former should elide.
5249 os.chdir(svntest.main.work_dir)
5250 # run_and_verify_merge doesn't support merging to a file WCPATH
5251 # so use run_and_verify_svn.
5252 svntest.actions.run_and_verify_svn(None,
5253 expected_merge_output([[5]],
5254 'G ' + short_beta_COPY_path + '\n'),
5255 [], 'merge', '-c5',
5256 sbox.repo_url + '/A/B/E/beta',
5257 short_beta_COPY_path)
5258 os.chdir(saved_cwd)
5260 # Check beta's status and props.
5261 expected_status = wc.State(beta_COPY_path, {
5262 '' : Item(status=' M', wc_rev=7),
5264 svntest.actions.run_and_verify_status(beta_COPY_path, expected_status)
5266 # Once again A_COPY/B/E/beta has no mergeinfo.
5267 svntest.actions.run_and_verify_svn(None, [], [],
5268 'propget', SVN_PROP_MERGEINFO,
5269 beta_COPY_path)
5271 def mergeinfo_inheritance_and_discontinuous_ranges(sbox):
5272 "discontinuous merges produce correct mergeinfo"
5274 # When a merge target has no explicit mergeinfo and is subject
5275 # to multiple merges, the resulting mergeinfo on the target
5276 # should reflect the combination of the inherited mergeinfo
5277 # with each merge performed.
5279 # Also tests implied merge source and target when only a revision
5280 # range is specified.
5282 sbox.build()
5283 wc_dir = sbox.wc_dir
5285 # Some paths we'll care about
5286 A_url = sbox.repo_url + '/A'
5287 A_COPY_path = os.path.join(wc_dir, "A_COPY")
5288 D_COPY_path = os.path.join(wc_dir, "A_COPY", "D")
5289 A_COPY_rho_path = os.path.join(wc_dir, "A_COPY", "D", "G", "rho")
5291 expected_disk, expected_status = set_up_branch(sbox)
5293 # Merge r4 into A_COPY
5294 saved_cwd = os.getcwd()
5296 os.chdir(A_COPY_path)
5297 svntest.actions.run_and_verify_svn(None,
5298 expected_merge_output([[4]], 'U ' +
5299 os.path.join("D", "G", "rho") + '\n'),
5300 [], 'merge', '-c4', A_url)
5301 os.chdir(saved_cwd)
5303 # Check the results of the merge.
5304 expected_status.tweak("A_COPY", status=' M')
5305 expected_status.tweak("A_COPY/D/G/rho", status='M ')
5306 svntest.actions.run_and_verify_status(wc_dir, expected_status)
5307 svntest.actions.run_and_verify_svn(None, ["/A:4\n"], [],
5308 'propget', SVN_PROP_MERGEINFO,
5309 A_COPY_path)
5311 # Merge r2:6 into A_COPY/D
5313 # Search for the comment entitled "The Merge Kluge" elsewhere in
5314 # this file, to understand why we shorten and chdir() below.
5316 # A_COPY/D should inherit the mergeinfo '/A:4' from A_COPY
5317 # combine it with the discontinous merges performed directly on
5318 # it (A/D/ 2:3 and A/D 4:6) resulting in '/A/D:3-6'.
5319 short_D_COPY_path = shorten_path_kludge(D_COPY_path)
5320 expected_output = wc.State(short_D_COPY_path, {
5321 'H/psi' : Item(status='U '),
5322 'H/omega' : Item(status='U '),
5324 expected_status = wc.State(short_D_COPY_path, {
5325 '' : Item(status=' M', wc_rev=2),
5326 'G' : Item(status=' ', wc_rev=2),
5327 'G/pi' : Item(status=' ', wc_rev=2),
5328 'G/rho' : Item(status='M ', wc_rev=2),
5329 'G/tau' : Item(status=' ', wc_rev=2),
5330 'H' : Item(status=' ', wc_rev=2),
5331 'H/chi' : Item(status=' ', wc_rev=2),
5332 'H/psi' : Item(status='M ', wc_rev=2),
5333 'H/omega' : Item(status='M ', wc_rev=2),
5334 'gamma' : Item(status=' ', wc_rev=2),
5336 expected_disk = wc.State('', {
5337 '' : Item(props={SVN_PROP_MERGEINFO : '/A/D:3-6'}),
5338 'G' : Item(),
5339 'G/pi' : Item("This is the file 'pi'.\n"),
5340 'G/rho' : Item("New content"),
5341 'G/tau' : Item("This is the file 'tau'.\n"),
5342 'H' : Item(),
5343 'H/chi' : Item("This is the file 'chi'.\n"),
5344 'H/psi' : Item("New content"),
5345 'H/omega' : Item("New content"),
5346 'gamma' : Item("This is the file 'gamma'.\n")
5348 expected_skip = wc.State(short_D_COPY_path, { })
5350 os.chdir(svntest.main.work_dir)
5351 svntest.actions.run_and_verify_merge(short_D_COPY_path, '2', '6',
5352 sbox.repo_url + '/A/D',
5353 expected_output,
5354 expected_disk,
5355 expected_status,
5356 expected_skip,
5357 None, None, None, None,
5358 None, 1)
5359 os.chdir(saved_cwd)
5361 # Wipe the memory of a portion of the previous merge...
5362 ### It'd be nice to use 'merge --record-only' here, but we can't (yet)
5363 ### wipe all ranges for a file due to the bug pointed out in r24645.
5364 mu_copy_path = os.path.join(A_COPY_path, 'mu')
5365 svntest.actions.run_and_verify_svn(None,
5366 ["property '" + SVN_PROP_MERGEINFO
5367 + "' set on '" +
5368 mu_copy_path + "'\n"], [], 'propset',
5369 SVN_PROP_MERGEINFO, '', mu_copy_path)
5370 # ...and confirm that we can commit the wiped mergeinfo...
5371 expected_output = wc.State(wc_dir, {
5372 'A_COPY/mu' : Item(verb='Sending'),
5374 svntest.actions.run_and_verify_commit(wc_dir,
5375 expected_output,
5376 None,
5377 None,
5378 mu_copy_path)
5379 # ...and that the presence of the property is retained, even when
5380 # the value has been wiped.
5381 svntest.actions.run_and_verify_svn(None, ['\n'], [], 'propget',
5382 SVN_PROP_MERGEINFO, mu_copy_path)
5384 def merge_to_target_with_copied_children(sbox):
5385 "merge works when target has copied children"
5387 # Test for Issue #2754 Can't merge to target with copied/moved children
5389 sbox.build()
5390 wc_dir = sbox.wc_dir
5391 expected_disk, expected_status = set_up_branch(sbox)
5393 # Some paths we'll care about
5394 D_COPY_path = os.path.join(wc_dir, "A_COPY", "D")
5395 G_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "G")
5396 rho_COPY_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "G", "rho_copy")
5398 # URL to URL copy A_COPY/D/G/rho to A_COPY/D/G/rho_copy
5399 svntest.actions.run_and_verify_svn(None, None, [], 'copy',
5400 sbox.repo_url + '/A_COPY/D/G/rho',
5401 sbox.repo_url + '/A_COPY/D/G/rho_copy',
5402 '-m', 'copy')
5404 # Update WC.
5405 expected_output = wc.State(wc_dir,
5406 {'A_COPY/D/G/rho_copy' : Item(status='A ')})
5407 expected_disk.add({
5408 'A_COPY/D/G/rho_copy' : Item("This is the file 'rho'.\n", props={})
5410 expected_status.tweak(wc_rev=7)
5411 expected_status.add({'A_COPY/D/G/rho_copy' : Item(status=' ', wc_rev=7)})
5412 svntest.actions.run_and_verify_update(wc_dir,
5413 expected_output,
5414 expected_disk,
5415 expected_status,
5416 None, None, None,
5417 None, None, 1)
5419 # Merge r4 into A_COPY/D/G/rho_copy.
5421 # Search for the comment entitled "The Merge Kluge" elsewhere in
5422 # this file, to understand why we shorten and chdir() below.
5423 os.chdir(svntest.main.work_dir)
5424 short_rho_COPY_COPY_path = shorten_path_kludge(rho_COPY_COPY_path)
5425 svntest.actions.run_and_verify_svn(None,
5426 expected_merge_output([[4]],
5427 'U ' + short_rho_COPY_COPY_path +
5428 '\n'),
5429 [], 'merge', '-c4',
5430 sbox.repo_url + '/A/D/G/rho',
5431 short_rho_COPY_COPY_path)
5433 # Merge r3:5 into A_COPY/D/G.
5434 short_G_COPY_path = shorten_path_kludge(G_COPY_path)
5435 expected_output = wc.State(short_G_COPY_path, {
5436 'rho' : Item(status='U ')
5438 expected_status = wc.State(short_G_COPY_path, {
5439 '' : Item(status=' M', wc_rev=7),
5440 'pi' : Item(status=' ', wc_rev=7),
5441 'rho' : Item(status='M ', wc_rev=7),
5442 'rho_copy' : Item(status='MM', wc_rev=7),
5443 'tau' : Item(status=' ', wc_rev=7),
5445 expected_disk = wc.State('', {
5446 '' : Item(props={SVN_PROP_MERGEINFO : '/A/D/G:4-5'}),
5447 'pi' : Item("This is the file 'pi'.\n"),
5448 'rho' : Item("New content"),
5449 'rho_copy' : Item("New content",
5450 props={SVN_PROP_MERGEINFO : '/A/D/G/rho:4'}),
5451 'tau' : Item("This is the file 'tau'.\n"),
5453 expected_skip = wc.State(short_G_COPY_path, { })
5454 svntest.actions.run_and_verify_merge(short_G_COPY_path, '3', '5',
5455 sbox.repo_url + \
5456 '/A/D/G',
5457 expected_output,
5458 expected_disk,
5459 expected_status,
5460 expected_skip,
5461 None, None, None, None,
5462 None, 1)
5464 def merge_to_switched_path(sbox):
5465 "merge to switched path does not inherit or elide"
5467 # When the target of a merge is a switched path we don't inherit WC
5468 # mergeinfo from above the target or attempt to elide the mergeinfo
5469 # set on the target as a result of the merge.
5471 sbox.build()
5472 wc_dir = sbox.wc_dir
5473 wc_disk, wc_status = set_up_branch(sbox)
5475 # Some paths we'll care about
5476 A_COPY_D_path = os.path.join(wc_dir, "A_COPY", "D")
5477 G_COPY_path = os.path.join(wc_dir, "A", "D", "G_COPY")
5478 A_COPY_D_G_path = os.path.join(wc_dir, "A_COPY", "D", "G")
5479 A_COPY_D_G_rho_path = os.path.join(wc_dir, "A_COPY", "D", "G", "rho")
5481 expected = svntest.verify.UnorderedOutput(
5482 ["A " + os.path.join(G_COPY_path, "pi") + "\n",
5483 "A " + os.path.join(G_COPY_path, "rho") + "\n",
5484 "A " + os.path.join(G_COPY_path, "tau") + "\n",
5485 "Checked out revision 6.\n",
5486 "A " + G_COPY_path + "\n"])
5488 # r7 - Copy A/D/G to A/D/G_COPY and commit.
5489 svntest.actions.run_and_verify_svn(None, expected, [], 'copy',
5490 sbox.repo_url + "/A/D/G",
5491 G_COPY_path)
5493 expected_output = wc.State(wc_dir, {'A/D/G_COPY' : Item(verb='Adding')})
5494 wc_status.add({
5495 "A/D/G_COPY" : Item(status=' ', wc_rev=7),
5496 "A/D/G_COPY/pi" : Item(status=' ', wc_rev=7),
5497 "A/D/G_COPY/rho" : Item(status=' ', wc_rev=7),
5498 "A/D/G_COPY/tau" : Item(status=' ', wc_rev=7),
5501 svntest.actions.run_and_verify_commit(wc_dir, expected_output, wc_status,
5502 None, wc_dir)
5504 # r8 - modify and commit A/D/G_COPY/rho
5505 svntest.main.file_write(os.path.join(wc_dir, "A", "D", "G_COPY", "rho"),
5506 "New *and* improved rho content")
5507 expected_output = wc.State(wc_dir, {'A/D/G_COPY/rho' : Item(verb='Sending')})
5508 wc_status.tweak('A/D/G_COPY/rho', wc_rev=8)
5509 svntest.actions.run_and_verify_commit(wc_dir, expected_output, wc_status,
5510 None, wc_dir)
5512 # Switch A_COPY/D/G to A/D/G.
5513 wc_disk.add({
5514 "A" : Item(),
5515 "A/D/G_COPY" : Item(),
5516 "A/D/G_COPY/pi" : Item("This is the file 'pi'.\n"),
5517 "A/D/G_COPY/rho" : Item("New *and* improved rho content"),
5518 "A/D/G_COPY/tau" : Item("This is the file 'tau'.\n"),
5520 wc_disk.tweak('A_COPY/D/G/rho',contents="New content")
5521 wc_status.tweak("A_COPY/D/G", wc_rev=8, switched='S')
5522 wc_status.tweak("A_COPY/D/G/pi", wc_rev=8)
5523 wc_status.tweak("A_COPY/D/G/rho", wc_rev=8)
5524 wc_status.tweak("A_COPY/D/G/tau", wc_rev=8)
5525 expected_output = svntest.wc.State(sbox.wc_dir, {
5526 "A_COPY/D/G/rho" : Item(status='U '),
5528 svntest.actions.run_and_verify_switch(sbox.wc_dir, A_COPY_D_G_path,
5529 sbox.repo_url + "/A/D/G",
5530 expected_output, wc_disk, wc_status,
5531 None, None, None, None, None, 1)
5533 # Merge r8 from A/D/G_COPY into our switched target A_COPY/D/G.
5534 # A_COPY/D/G should get mergeinfo for r8 as a result of the merge,
5535 # but because it's switched should not inherit the mergeinfo from
5536 # its nearest WC ancestor with mergeinfo (A_COPY: svn:mergeinfo : /A:1)
5538 # Search for the comment entitled "The Merge Kluge" elsewhere in
5539 # this file, to understand why we shorten and chdir() below.
5540 short_G_COPY_path = shorten_path_kludge(A_COPY_D_G_path)
5541 expected_output = wc.State(short_G_COPY_path, {
5542 'rho' : Item(status='U ')
5544 # Note: A_COPY/D/G won't show as switched because of the Merge Kluge.
5545 expected_status = wc.State(short_G_COPY_path, {
5546 '' : Item(status=' M', wc_rev=8),
5547 'pi' : Item(status=' ', wc_rev=8),
5548 'rho' : Item(status='M ', wc_rev=8),
5549 'tau' : Item(status=' ', wc_rev=8),
5551 expected_disk = wc.State('', {
5552 '' : Item(props={SVN_PROP_MERGEINFO : '/A/D/G_COPY:8'}),
5553 'pi' : Item("This is the file 'pi'.\n"),
5554 'rho' : Item("New *and* improved rho content"),
5555 'tau' : Item("This is the file 'tau'.\n"),
5557 expected_skip = wc.State(short_G_COPY_path, { })
5558 saved_cwd = os.getcwd()
5560 os.chdir(svntest.main.work_dir)
5561 svntest.actions.run_and_verify_merge(short_G_COPY_path, '7', '8',
5562 sbox.repo_url + '/A/D/G_COPY',
5563 expected_output, expected_disk,
5564 expected_status, expected_skip,
5565 None, None, None, None, None, 1)
5566 os.chdir(saved_cwd)
5568 # Check that the mergeinfo set on a target doesn't elide when that
5569 # target is switched.
5571 # Revert the previous merge and manually set 'svn:mergeinfo : '
5572 # on 'merge_tests-1\A_COPY\D'. Now merge -c-4 from /A/D/G into A_COPY/D/G.
5573 # This should still set 'svn:mergeinfo : ' on
5574 # 'merge_tests-1\A_COPY\D\G'. This would normally elide to A_COPY/D,
5575 # but since A_COPY/D/G is switched it should not.
5576 svntest.actions.run_and_verify_svn(None,
5577 ["Reverted '" + A_COPY_D_G_path+ "'\n",
5578 "Reverted '" + A_COPY_D_G_rho_path +
5579 "'\n"],
5580 [], 'revert', '-R', wc_dir)
5581 svntest.actions.run_and_verify_svn(None,
5582 ["property '" + SVN_PROP_MERGEINFO +
5583 "' set on '" + A_COPY_D_path+ "'" +
5584 "\n"], [], 'ps', SVN_PROP_MERGEINFO,
5585 '', A_COPY_D_path)
5586 svntest.actions.run_and_verify_svn(None,
5587 expected_merge_output([[-4]],
5588 'U ' + A_COPY_D_G_rho_path + '\n'),
5589 [], 'merge', '-c-4',
5590 sbox.repo_url + '/A/D/G_COPY',
5591 A_COPY_D_G_path)
5592 wc_status.tweak("A_COPY/D", status=' M')
5593 wc_status.tweak("A_COPY/D/G", status=' M')
5594 wc_status.tweak("A_COPY/D/G/rho", status='M ')
5595 svntest.actions.run_and_verify_status(wc_dir, wc_status)
5596 expected = svntest.verify.UnorderedOutput(
5597 ["A " + os.path.join(G_COPY_path, "pi") + "\n",
5598 "A " + os.path.join(G_COPY_path, "rho") + "\n",
5599 "A " + os.path.join(G_COPY_path, "tau") + "\n",
5600 "Checked out revision 6.\n",
5601 "A " + G_COPY_path + "\n"])
5602 expected = svntest.verify.UnorderedOutput(
5603 ["Properties on '" + A_COPY_D_path + "':\n",
5604 " " + SVN_PROP_MERGEINFO + " : \n",
5605 "Properties on '" + A_COPY_D_G_path + "':\n",
5606 " " + SVN_PROP_MERGEINFO +" : \n"])
5607 svntest.actions.run_and_verify_svn(None,
5608 expected, [],
5609 'pl', '-vR', A_COPY_D_path)
5611 # Test for issues
5613 # 2823: Account for mergeinfo differences for switched
5614 # directories when gathering mergeinfo
5616 # 2839: Support non-inheritable mergeinfo revision ranges
5617 def merge_to_path_with_switched_children(sbox):
5618 "merge to path with switched children"
5620 # Merging to a target with switched children requires special handling
5621 # to keep mergeinfo correct:
5623 # 1) If the target of a merge has switched children without explicit
5624 # mergeinfo, the switched children should get mergeinfo set on
5625 # them as a result of the merge. This mergeinfo includes the
5626 # mergeinfo resulting from the merge *and* any mergeinfo inherited
5627 # from the repos for the switched path.
5629 # 2) Mergeinfo on switched children should never elide.
5631 # 3) The path the switched child overrides cannot be modified by the
5632 # merge (it isn't present in the WC) so should not inherit any
5633 # mergeinfo added as a result of the merge. To prevent this, the
5634 # immediate parent of any switched child should have non-inheritable
5635 # mergeinfo added/modified for the merge performed.
5637 # 4) Because of 3, siblings of switched children will not inherit the
5638 # mergeinfo resulting from the merge, so must get their own, full set
5639 # of mergeinfo.
5641 sbox.build()
5642 wc_dir = sbox.wc_dir
5643 wc_disk, wc_status = set_up_branch(sbox, False, 3)
5645 # Some paths we'll care about
5646 D_path = os.path.join(wc_dir, "A", "D")
5647 A_COPY_path = os.path.join(wc_dir, "A_COPY")
5648 A_COPY_beta_path = os.path.join(wc_dir, "A_COPY", "B", "E", "beta")
5649 A_COPY_chi_path = os.path.join(wc_dir, "A_COPY", "D", "H", "chi")
5650 A_COPY_omega_path = os.path.join(wc_dir, "A_COPY", "D", "H", "omega")
5651 A_COPY_psi_path = os.path.join(wc_dir, "A_COPY", "D", "H", "psi")
5652 A_COPY_G_path = os.path.join(wc_dir, "A_COPY", "D", "G")
5653 A_COPY_rho_path = os.path.join(wc_dir, "A_COPY", "D", "G", "rho")
5654 A_COPY_H_path = os.path.join(wc_dir, "A_COPY", "D", "H")
5655 A_COPY_D_path = os.path.join(wc_dir, "A_COPY", "D")
5656 A_COPY_gamma_path = os.path.join(wc_dir, "A_COPY", "D", "gamma")
5657 H_COPY_2_path = os.path.join(wc_dir, "A_COPY_2", "D", "H")
5659 svntest.actions.run_and_verify_svn(None, ["At revision 8.\n"], [], 'up',
5660 wc_dir)
5661 wc_status.tweak(wc_rev=8)
5663 # Switch a file and dir path in the branch:
5665 # Switch A_COPY/D/G to A_COPY_2/D/G.
5666 wc_status.tweak("A_COPY/D/G", switched='S')
5667 expected_output = svntest.wc.State(sbox.wc_dir, {})
5668 svntest.actions.run_and_verify_switch(sbox.wc_dir, A_COPY_G_path,
5669 sbox.repo_url + "/A_COPY_2/D/G",
5670 expected_output, wc_disk, wc_status,
5671 None, None, None, None, None, 1)
5673 # Switch A_COPY/D/G/rho to A_COPY_3/D/G/rho.
5674 wc_status.tweak("A_COPY/D/G/rho", switched='S')
5675 expected_output = svntest.wc.State(sbox.wc_dir, {})
5676 svntest.actions.run_and_verify_switch(sbox.wc_dir, A_COPY_rho_path,
5677 sbox.repo_url + "/A_COPY_3/D/G/rho",
5678 expected_output, wc_disk, wc_status,
5679 None, None, None, None, None, 1)
5681 # Switch A_COPY/D/H/psi to A_COPY_2/D/H/psi.
5682 wc_status.tweak("A_COPY/D/H/psi", switched='S')
5683 expected_output = svntest.wc.State(sbox.wc_dir, {})
5684 svntest.actions.run_and_verify_switch(sbox.wc_dir, A_COPY_psi_path,
5685 sbox.repo_url + "/A_COPY_2/D/H/psi",
5686 expected_output, wc_disk, wc_status,
5687 None, None, None, None, None, 1)
5689 # Target with switched file child:
5691 # Merge r8 from A/D/H into A_COPY/D/H. The switched child of
5692 # A_COPY/D/H, file A_COPY/D/H/psi (which has no mergeinfo prior
5693 # to the merge), should get their own mergeinfo, both r8 from the
5694 # merge itself, and r1-2 inherited from A_COPY_2 in the repository.
5696 # A_COPY/D/H/psi's parent A_COPY/D/H has no pre-exiting explicit
5697 # mergeinfo so should get its own mergeinfo, made up of r1 inherited
5698 # from A_COPY and the non-inheritable r8 resulting from the merge.
5700 # A_COPY/D/H/psi's two unswitched siblings, A_COPY/D/H/chi and
5701 # A_COPY/D/H/omega won't inherit r8 from A_COPY/D/H, so need their
5702 # own mergeinfo.
5704 # Search for the comment entitled "The Merge Kluge" elsewhere in
5705 # this file, to understand why we shorten and chdir() below.
5706 short_H_COPY_path = shorten_path_kludge(A_COPY_H_path)
5707 expected_output = wc.State(short_H_COPY_path, {
5708 'omega' : Item(status='U ')
5710 expected_status = wc.State(short_H_COPY_path, {
5711 '' : Item(status=' M', wc_rev=8),
5712 'psi' : Item(status=' M', wc_rev=8, switched='S'),
5713 'omega' : Item(status='MM', wc_rev=8),
5714 'chi' : Item(status=' M', wc_rev=8),
5716 expected_disk = wc.State('', {
5717 '' : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:8*'}),
5718 'psi' : Item("This is the file 'psi'.\n",
5719 props={SVN_PROP_MERGEINFO : '/A/D/H/psi:8'}),
5720 'omega' : Item("New content",
5721 props={SVN_PROP_MERGEINFO : '/A/D/H/omega:8'}),
5722 'chi' : Item("This is the file 'chi'.\n",
5723 props={SVN_PROP_MERGEINFO : '/A/D/H/chi:8'}),
5725 expected_skip = wc.State(short_H_COPY_path, { })
5726 saved_cwd = os.getcwd()
5728 os.chdir(svntest.main.work_dir)
5730 svntest.actions.run_and_verify_merge(short_H_COPY_path, '7', '8',
5731 sbox.repo_url + '/A/D/H',
5732 expected_output, expected_disk,
5733 expected_status, expected_skip,
5734 None, None, None, None, None, 1)
5735 os.chdir(saved_cwd)
5737 # Target with switched dir child:
5739 # Merge r6 from A/D into A_COPY/D. The switched child of A_COPY/D,
5740 # directory A_COPY/D/G (which has no mergeinfo prior to the merge),
5741 # should get its own mergeinfo, both for r6 from the merge itself and
5742 # r1-2 from the repository.
5744 # A_COPY/D/G's parent A_COPY/D has no pre-exiting explicit
5745 # mergeinfo so should get its own mergeinfo, made up of r1 inherited
5746 # from A_COPY and the non-inheritable r5 resulting from the merge.
5748 # A_COPY/D/G's two unswitched siblings, A_COPY/D/gamma and A_COPY/D/H
5749 # won't inherit r5 from A_COPY/D, so need their own mergeinfo
5750 # added/updated respectively. A_COPY/D/H itself has a switched child
5751 # so r5 is set as non-inheritable on it. All of A_COPY/D/H's children,
5752 # which have explict mergeinfo from the previous merge, get r5 in their
5753 # mergeinfo.
5754 short_D_COPY_path = shorten_path_kludge(A_COPY_D_path)
5755 expected_output = wc.State(short_D_COPY_path, {
5756 'G/rho' : Item(status='U ')
5758 expected_status_D = wc.State(short_D_COPY_path, {
5759 '' : Item(status=' M', wc_rev=8),
5760 'H' : Item(status=' M', wc_rev=8),
5761 'H/chi' : Item(status=' M', wc_rev=8),
5762 'H/omega' : Item(status='MM', wc_rev=8),
5763 'H/psi' : Item(status=' M', wc_rev=8, switched='S'),
5764 'G' : Item(status=' M', wc_rev=8, switched='S'),
5765 'G/pi' : Item(status=' M', wc_rev=8),
5766 'G/rho' : Item(status='MM', wc_rev=8, switched='S'),
5767 'G/tau' : Item(status=' M', wc_rev=8),
5768 'gamma' : Item(status=' M', wc_rev=8),
5770 expected_disk_D = wc.State('', {
5771 '' : Item(props={SVN_PROP_MERGEINFO : '/A/D:6*'}),
5772 'H' : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:6*,8*'}),
5773 'H/chi' : Item("This is the file 'chi'.\n",
5774 props={SVN_PROP_MERGEINFO : '/A/D/H/chi:6,8'}),
5775 'H/omega' : Item("New content",
5776 props={SVN_PROP_MERGEINFO : '/A/D/H/omega:6,8'}),
5777 'H/psi' : Item("This is the file 'psi'.\n",
5778 props={SVN_PROP_MERGEINFO : '/A/D/H/psi:6,8'}),
5779 'G' : Item(props={SVN_PROP_MERGEINFO : '/A/D/G:6*'}),
5780 'G/pi' : Item("This is the file 'pi'.\n",
5781 props={SVN_PROP_MERGEINFO : '/A/D/G/pi:6'}),
5782 'G/rho' : Item("New content",
5783 props={SVN_PROP_MERGEINFO : '/A/D/G/rho:6'}),
5784 'G/tau' : Item("This is the file 'tau'.\n",
5785 props={SVN_PROP_MERGEINFO : '/A/D/G/tau:6'}),
5786 'gamma' : Item("This is the file 'gamma'.\n",
5787 props={SVN_PROP_MERGEINFO : '/A/D/gamma:6'}),
5789 expected_skip_D = wc.State(short_D_COPY_path, { })
5790 os.chdir(svntest.main.work_dir)
5791 svntest.actions.run_and_verify_merge(short_D_COPY_path, '5', '6',
5792 sbox.repo_url + '/A/D',
5793 expected_output, expected_disk_D,
5794 expected_status_D, expected_skip_D,
5795 None, None, None, None, None, 1)
5797 # Merge r5 from A/D into A_COPY/D. A_COPY/D's switched child A_COPY/D/G
5798 # gets r5 as does A_COPY/D/G's unswitched sibling A_COPY/D/gamma. Since
5799 # A_COPY/D has a switched child, it gets an uninheritable r5. Every other
5800 # path with existing mergeinfo already has r8 so are unchanged.
5802 # A_COPY/D/H/chi and A_COPY/D/H/omega both have mergeinfo for r1,6,8 but
5803 # won't elide to A_COPY/D/H becuase the latter's mergeinfo, while
5804 # encompassing the same ranges, r1,6*,8*, has some non-inheritable ranges.
5805 # The same is true of A_COPY/D/gamma and A_COPY/D.
5806 expected_output = wc.State(short_D_COPY_path, {
5807 'H/psi' : Item(status='U ')})
5808 expected_disk_D.tweak('', props={SVN_PROP_MERGEINFO : '/A/D:5-6*'})
5809 expected_disk_D.tweak('G', props={SVN_PROP_MERGEINFO : '/A/D/G:5-6*'})
5810 expected_disk_D.tweak('G/pi',
5811 props={SVN_PROP_MERGEINFO : '/A/D/G/pi:5-6'})
5812 expected_disk_D.tweak('G/rho',
5813 props={SVN_PROP_MERGEINFO : '/A/D/G/rho:5-6'})
5814 expected_disk_D.tweak('G/tau',
5815 props={SVN_PROP_MERGEINFO : '/A/D/G/tau:5-6'})
5816 expected_disk_D.tweak('H', props={SVN_PROP_MERGEINFO : '/A/D/H:5-6*,8*'})
5817 expected_disk_D.tweak('gamma',
5818 props={SVN_PROP_MERGEINFO : '/A/D/gamma:5-6'})
5819 expected_disk_D.tweak('H/chi',
5820 props={SVN_PROP_MERGEINFO :'/A/D/H/chi:5-6,8'})
5821 expected_disk_D.tweak('H/psi', contents="New content",
5822 props={SVN_PROP_MERGEINFO :'/A/D/H/psi:5-6,8'})
5823 expected_disk_D.tweak('H/omega',
5824 props={SVN_PROP_MERGEINFO :'/A/D/H/omega:5-6,8'})
5825 expected_status_D.tweak('H/psi', status='MM')
5826 svntest.actions.run_and_verify_merge(short_D_COPY_path, '4', '5',
5827 sbox.repo_url + '/A/D',
5828 expected_output, expected_disk_D,
5829 expected_status_D, expected_skip_D,
5830 None, None, None, None, None, 1)
5831 os.chdir(saved_cwd)
5833 # Finally, merge r4:8 into A_COPY. A_COPY gets r5-8 added and every path
5834 # under it with with explicit mergeinfo gets r5,7 added (they all have r6,8
5835 # already). Again there is no elision, since the only possibilities, e.g.
5836 # A_COPY/D/H's '/A/D/H:1,5-8*' to A_COPY/D's '/A/D:1,5-8*', involve
5837 # non-inheritable mergeinfo one ond side or the other.
5838 short_A_COPY_path = shorten_path_kludge(A_COPY_path)
5839 expected_output = wc.State(short_A_COPY_path, {
5840 'B/E/beta' : Item(status='U ')
5842 expected_status = wc.State(short_A_COPY_path, {
5843 '' : Item(status=' M', wc_rev=8),
5844 'B' : Item(status=' ', wc_rev=8),
5845 'mu' : Item(status=' ', wc_rev=8),
5846 'B/E' : Item(status=' ', wc_rev=8),
5847 'B/E/alpha' : Item(status=' ', wc_rev=8),
5848 'B/E/beta' : Item(status='M ', wc_rev=8),
5849 'B/lambda' : Item(status=' ', wc_rev=8),
5850 'B/F' : Item(status=' ', wc_rev=8),
5851 'C' : Item(status=' ', wc_rev=8),
5852 'D' : Item(status=' M', wc_rev=8),
5853 'D/G' : Item(status=' M', wc_rev=8, switched='S'),
5854 'D/G/pi' : Item(status=' M', wc_rev=8),
5855 'D/G/rho' : Item(status='MM', wc_rev=8, switched='S'),
5856 'D/G/tau' : Item(status=' M', wc_rev=8),
5857 'D/gamma' : Item(status=' M', wc_rev=8),
5858 'D/H' : Item(status=' M', wc_rev=8),
5859 'D/H/chi' : Item(status=' M', wc_rev=8),
5860 'D/H/psi' : Item(status='MM', wc_rev=8, switched='S'),
5861 'D/H/omega' : Item(status='MM', wc_rev=8),
5863 expected_disk = wc.State('', {
5864 '' : Item(props={SVN_PROP_MERGEINFO : '/A:5-8'}),
5865 'B' : Item(),
5866 'mu' : Item("This is the file 'mu'.\n"),
5867 'B/E' : Item(),
5868 'B/E/alpha' : Item("This is the file 'alpha'.\n"),
5869 'B/E/beta' : Item("New content"),
5870 'B/lambda' : Item("This is the file 'lambda'.\n"),
5871 'B/F' : Item(),
5872 'C' : Item(),
5873 'D' : Item(props={SVN_PROP_MERGEINFO : '/A/D:5-8*'}),
5874 'D/G' : Item(props={SVN_PROP_MERGEINFO : '/A/D/G:5-8*'}),
5875 'D/G/pi' : Item("This is the file 'pi'.\n",
5876 props={SVN_PROP_MERGEINFO : '/A/D/G/pi:5-8'}),
5877 'D/G/rho' : Item("New content",
5878 props={SVN_PROP_MERGEINFO : '/A/D/G/rho:5-8'}),
5879 'D/G/tau' : Item("This is the file 'tau'.\n",
5880 props={SVN_PROP_MERGEINFO : '/A/D/G/tau:5-8'}),
5881 'D/gamma' : Item("This is the file 'gamma'.\n",
5882 props={SVN_PROP_MERGEINFO : '/A/D/gamma:5-8'}),
5883 'D/H' : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:5-8*'}),
5884 'D/H/chi' : Item("This is the file 'chi'.\n",
5885 props={SVN_PROP_MERGEINFO : '/A/D/H/chi:5-8'}),
5886 'D/H/psi' : Item("New content",
5887 props={SVN_PROP_MERGEINFO : '/A/D/H/psi:5-8'}),
5888 'D/H/omega' : Item("New content",
5889 props={SVN_PROP_MERGEINFO : '/A/D/H/omega:5-8'}),
5891 expected_skip = wc.State(short_A_COPY_path, { })
5892 os.chdir(svntest.main.work_dir)
5893 svntest.actions.run_and_verify_merge(short_A_COPY_path, '4', '8',
5894 sbox.repo_url + '/A',
5895 expected_output, expected_disk,
5896 expected_status, expected_skip,
5897 None, None, None, None, None, 1)
5899 os.chdir(saved_cwd)
5901 # Commit changes thus far.
5902 expected_output = svntest.wc.State(wc_dir, {
5903 'A_COPY' : Item(verb='Sending'),
5904 'A_COPY/B/E/beta' : Item(verb='Sending'),
5905 'A_COPY/D' : Item(verb='Sending'),
5906 'A_COPY/D/gamma' : Item(verb='Sending'),
5907 'A_COPY/D/G' : Item(verb='Sending'),
5908 'A_COPY/D/G/pi' : Item(verb='Sending'),
5909 'A_COPY/D/G/rho' : Item(verb='Sending'),
5910 'A_COPY/D/G/tau' : Item(verb='Sending'),
5911 'A_COPY/D/H' : Item(verb='Sending'),
5912 'A_COPY/D/H/chi' : Item(verb='Sending'),
5913 'A_COPY/D/H/omega' : Item(verb='Sending'),
5914 'A_COPY/D/H/psi' : Item(verb='Sending'),
5916 wc_status.tweak('A_COPY', 'A_COPY/B/E/beta', 'A_COPY/D', 'A_COPY/D/gamma',
5917 'A_COPY/D/G', 'A_COPY/D/G/pi', 'A_COPY/D/G/rho',
5918 'A_COPY/D/G/tau','A_COPY/D/H', 'A_COPY/D/H/chi',
5919 'A_COPY/D/H/omega', 'A_COPY/D/H/psi',
5920 wc_rev=9)
5921 svntest.actions.run_and_verify_commit(wc_dir, expected_output, wc_status,
5922 None, wc_dir)
5924 # Unswitch A_COPY/D/H/psi.
5925 expected_output = svntest.wc.State(wc_dir, {
5926 'A_COPY/D/H/psi' : Item(status='UU')})
5927 wc_status.tweak("A_COPY/D/H/psi", switched=None, wc_rev=9)
5928 wc_disk.tweak("A_COPY",
5929 props={SVN_PROP_MERGEINFO : '/A:5-8'})
5930 wc_disk.tweak("A_COPY/B/E/beta",
5931 contents="New content")
5932 wc_disk.tweak("A_COPY/D",
5933 props={SVN_PROP_MERGEINFO : '/A/D:5-8*'})
5934 wc_disk.tweak("A_COPY/D/gamma",
5935 props={SVN_PROP_MERGEINFO : '/A/D/gamma:5-8'})
5936 wc_disk.tweak("A_COPY/D/G",
5937 props={SVN_PROP_MERGEINFO : '/A/D/G:5-8*'})
5938 wc_disk.tweak("A_COPY/D/G/pi",
5939 props={SVN_PROP_MERGEINFO : '/A/D/G/pi:5-8'})
5940 wc_disk.tweak("A_COPY/D/G/rho",
5941 contents="New content",
5942 props={SVN_PROP_MERGEINFO : '/A/D/G/rho:5-8'})
5943 wc_disk.tweak("A_COPY/D/G/tau",
5944 props={SVN_PROP_MERGEINFO : '/A/D/G/tau:5-8'})
5945 wc_disk.tweak("A_COPY/D/H",
5946 props={SVN_PROP_MERGEINFO : '/A/D/H:5-8*'})
5947 wc_disk.tweak("A_COPY/D/H/chi",
5948 props={SVN_PROP_MERGEINFO : '/A/D/H/chi:5-8'})
5949 wc_disk.tweak("A_COPY/D/H/omega",
5950 contents="New content",
5951 props={SVN_PROP_MERGEINFO : '/A/D/H/omega:5-8'})
5952 wc_disk.tweak("A_COPY_2", props={})
5953 svntest.actions.run_and_verify_switch(sbox.wc_dir, A_COPY_psi_path,
5954 sbox.repo_url + "/A_COPY/D/H/psi",
5955 expected_output, wc_disk, wc_status,
5956 None, None, None, None, None, 1)
5958 # Non-inheritable mergeinfo ranges on a target don't prevent repeat
5959 # merges of that range on the target's children.
5961 # Non-inheritable mergeinfo ranges on a target are removed if the target
5962 # no longer has any switched children and a repeat merge is performed.
5964 # Merge r4:8 from A/D/H into A_COPY/D/H. A_COPY/D/H already has mergeinfo
5965 # for r4:8 but it is marked as uninheritable so the repeat merge is
5966 # allowed on its children, notably the now unswitched A_COPY/D/H/psi.
5967 # Since A_COPY/D/H no longer has any switched children and the merge of
5968 # r4:8 has been repeated the previously uninheritable ranges 5-8* on
5969 # A_COPY/D/H are made inheritable. This also means that the mergeinfo on
5970 # A_COPY/D/H's children will elide to it.
5971 short_H_COPY_path = shorten_path_kludge(A_COPY_H_path)
5972 expected_output = wc.State(short_H_COPY_path, {
5973 'psi' : Item(status='U ')
5975 expected_status = wc.State(short_H_COPY_path, {
5976 '' : Item(status=' M', wc_rev=9),
5977 'psi' : Item(status='M ', wc_rev=9),
5978 'omega' : Item(status=' M', wc_rev=9),
5979 'chi' : Item(status=' M', wc_rev=9),
5981 expected_disk = wc.State('', {
5982 '' : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:5-8'}),
5983 'psi' : Item("New content"),
5984 'omega' : Item("New content"),
5985 'chi' : Item("This is the file 'chi'.\n"),
5987 expected_skip = wc.State(short_H_COPY_path, { })
5988 os.chdir(svntest.main.work_dir)
5989 svntest.actions.run_and_verify_merge(short_H_COPY_path, '4', '8',
5990 sbox.repo_url + '/A/D/H',
5991 expected_output, expected_disk,
5992 expected_status, expected_skip,
5993 None, None, None, None, None, 1)
5994 os.chdir(saved_cwd)
5996 # Non-inheritable mergeinfo ranges on a target do prevent repeat
5997 # merges on the target itself.
5999 # Add a prop A/D and commit it as r10. Merge r10 into A_COPY/D. Since
6000 # A_COPY/D has a switched child it gets r10 added as a non-inheritable
6001 # range. Repeat the same merge checking that no repeat merge is
6002 # attempted on A_COPY/D.
6003 svntest.actions.run_and_verify_svn(None,
6004 ["property 'prop:name' set on '" +
6005 D_path + "'\n"], [], 'ps',
6006 'prop:name', 'propval', D_path)
6007 expected_output = svntest.wc.State(wc_dir, {
6008 'A/D' : Item(verb='Sending'),
6009 'A_COPY/D/H' : Item(verb='Sending'),
6010 'A_COPY/D/H/chi' : Item(verb='Sending'),
6011 'A_COPY/D/H/psi' : Item(verb='Sending'),
6012 'A_COPY/D/H/omega' : Item(verb='Sending'),
6014 wc_status.tweak('A_COPY/D', wc_rev=9)
6015 wc_status.tweak('A/D', 'A_COPY/D/H', 'A_COPY/D/H/chi', 'A_COPY/D/H/psi',
6016 'A_COPY/D/H/omega', wc_rev=10)
6017 svntest.actions.run_and_verify_commit(wc_dir, expected_output, wc_status,
6018 None, wc_dir)
6019 expected_output = wc.State(short_D_COPY_path, {
6020 '' : Item(status=' U')
6022 # Reuse expected status and disk from last merge to A_COPY/D
6023 expected_status_D.tweak('', 'gamma', status=' M', wc_rev=9)
6024 expected_status_D.tweak('H', status=' M', wc_rev=10)
6025 expected_status_D.tweak('H/psi', 'H/chi', 'H/omega', status=' ', wc_rev=10,
6026 switched=None)
6027 expected_status_D.tweak('G', switched='S', status=' M', wc_rev=9)
6028 expected_status_D.tweak('G/tau', 'G/pi', 'G/rho', status=' M', wc_rev=9)
6029 expected_disk_D.tweak('', props={SVN_PROP_MERGEINFO : '/A/D:5-8*,10*',
6030 "prop:name" : "propval"})
6031 expected_disk_D.tweak('G',
6032 props={SVN_PROP_MERGEINFO : '/A/D/G:5-8*,10*'})
6033 expected_disk_D.tweak('G/pi',
6034 props={SVN_PROP_MERGEINFO : '/A/D/G/pi:5-8,10'})
6035 expected_disk_D.tweak('G/rho',
6036 props={SVN_PROP_MERGEINFO : '/A/D/G/rho:5-8,10'})
6037 expected_disk_D.tweak('G/tau',
6038 props={SVN_PROP_MERGEINFO : '/A/D/G/tau:5-8,10'})
6039 expected_disk_D.tweak('H', props={SVN_PROP_MERGEINFO : '/A/D/H:5-8,10'})
6040 expected_disk_D.tweak('gamma',
6041 props={SVN_PROP_MERGEINFO : '/A/D/gamma:5-8,10'})
6042 expected_disk_D.tweak('H/chi', 'H/omega', props={})
6043 expected_disk_D.tweak('H/psi', contents="New content", props={})
6044 os.chdir(svntest.main.work_dir)
6045 svntest.actions.run_and_verify_merge(short_D_COPY_path, '9', '10',
6046 sbox.repo_url + '/A/D',
6047 expected_output, expected_disk_D,
6048 expected_status_D, expected_skip_D,
6049 None, None, None, None, None, 1)
6050 # Repeated merge is a no-op.
6051 expected_output = wc.State(short_D_COPY_path, {})
6052 svntest.actions.run_and_verify_merge(short_D_COPY_path, '9', '10',
6053 sbox.repo_url + '/A/D',
6054 expected_output, expected_disk_D,
6055 expected_status_D, expected_skip_D,
6056 None, None, None, None, None, 1)
6057 os.chdir(saved_cwd)
6060 # Test for issue 2047: Merge from parent dir fails while it succeeds from
6061 # the direct dir
6062 def merge_with_implicit_target_file(sbox):
6063 "merge a change to a file, using relative path"
6065 sbox.build()
6066 wc_dir = sbox.wc_dir
6068 # Make a change to A/mu, then revert it using 'svn merge -r 2:1 A/mu'
6070 # change A/mu and commit
6071 A_path = os.path.join(wc_dir, 'A')
6072 mu_path = os.path.join(A_path, 'mu')
6074 svntest.main.file_append(mu_path, "A whole new line.\n")
6076 expected_output = svntest.wc.State(wc_dir, {
6077 'A/mu' : Item(verb='Sending'),
6079 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
6080 expected_status.tweak('A/mu', wc_rev=2)
6081 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
6082 expected_status, None, wc_dir)
6084 # Update to revision 2.
6085 svntest.actions.run_and_verify_svn(None, None, [], 'update', wc_dir)
6087 # Revert the change committed in r2
6088 os.chdir(wc_dir)
6090 # run_and_verify_merge doesn't accept file paths.
6091 svntest.actions.run_and_verify_svn(None, None, [], 'merge', '-r', '2:1',
6092 'A/mu')
6094 # Test practical application of issue #2769 fix, empty rev range elision,
6095 # and elision to the repos.
6096 def empty_mergeinfo(sbox):
6097 "mergeinfo can explicitly be empty"
6099 # A bit o' history: The fix for issue #2769 originally permitted mergeinfo
6100 # with empty range lists and as a result we permitted partial elision and
6101 # had a whole slew of tests here for that. But the fix of issue #3029 now
6102 # prevents svn ps or svn merge from creating mergeinfo with paths mapped to
6103 # empty ranges, only empty mergeinfo is allowed. As a result this test now
6104 # covers the following areas:
6106 # A) Merging a set of revisions into a path, then reverse merging the
6107 # same set out of a subtree of path results in empty mergeinfo
6108 # (i.e. "") on the subtree.
6110 # B) Empty mergeinfo elides to empty mergeinfo.
6112 # C) If a merge sets empty mergeinfo on its target and that target has
6113 # no ancestor in either the WC or the repository with explict
6114 # mergeinfo, then the target's mergeinfo is removed (a.k.a. elides
6115 # to nothing).
6116 sbox.build()
6117 wc_dir = sbox.wc_dir
6118 wc_disk, wc_status = set_up_branch(sbox)
6120 # Some paths we'll care about
6121 A_COPY_path = os.path.join(wc_dir, "A_COPY")
6122 H_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "H")
6123 psi_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "H", "psi")
6124 rho_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "G", "rho")
6126 # Test area A -- Merge r2:4 into A_COPY then reverse merge 4:2 to
6127 # A_COPY/D/G. A_COPY/D/G should end up with empty mergeinfo to
6128 # override that of A_COPY.
6130 # Search for the comment entitled "The Merge Kluge" elsewhere in
6131 # this file, to understand why we shorten and chdir() below.
6132 short_A_COPY_path = shorten_path_kludge(A_COPY_path)
6133 expected_output = wc.State(short_A_COPY_path, {
6134 'D/H/psi' : Item(status='U '),
6135 'D/G/rho' : Item(status='U '),
6137 expected_status = wc.State(short_A_COPY_path, {
6138 '' : Item(status=' M', wc_rev=2),
6139 'B' : Item(status=' ', wc_rev=2),
6140 'mu' : Item(status=' ', wc_rev=2),
6141 'B/E' : Item(status=' ', wc_rev=2),
6142 'B/E/alpha' : Item(status=' ', wc_rev=2),
6143 'B/E/beta' : Item(status=' ', wc_rev=2),
6144 'B/lambda' : Item(status=' ', wc_rev=2),
6145 'B/F' : Item(status=' ', wc_rev=2),
6146 'C' : Item(status=' ', wc_rev=2),
6147 'D' : Item(status=' ', wc_rev=2),
6148 'D/G' : Item(status=' ', wc_rev=2),
6149 'D/G/pi' : Item(status=' ', wc_rev=2),
6150 'D/G/rho' : Item(status='M ', wc_rev=2),
6151 'D/G/tau' : Item(status=' ', wc_rev=2),
6152 'D/gamma' : Item(status=' ', wc_rev=2),
6153 'D/H' : Item(status=' ', wc_rev=2),
6154 'D/H/chi' : Item(status=' ', wc_rev=2),
6155 'D/H/psi' : Item(status='M ', wc_rev=2),
6156 'D/H/omega' : Item(status=' ', wc_rev=2),
6158 expected_disk = wc.State('', {
6159 '' : Item(props={SVN_PROP_MERGEINFO : '/A:3-4'}),
6160 'B' : Item(),
6161 'mu' : Item("This is the file 'mu'.\n"),
6162 'B/E' : Item(),
6163 'B/E/alpha' : Item("This is the file 'alpha'.\n"),
6164 'B/E/beta' : Item("This is the file 'beta'.\n"),
6165 'B/lambda' : Item("This is the file 'lambda'.\n"),
6166 'B/F' : Item(),
6167 'C' : Item(),
6168 'D' : Item(),
6169 'D/G' : Item(),
6170 'D/G/pi' : Item("This is the file 'pi'.\n"),
6171 'D/G/rho' : Item("New content"),
6172 'D/G/tau' : Item("This is the file 'tau'.\n"),
6173 'D/gamma' : Item("This is the file 'gamma'.\n"),
6174 'D/H' : Item(),
6175 'D/H/chi' : Item("This is the file 'chi'.\n"),
6176 'D/H/psi' : Item("New content"),
6177 'D/H/omega' : Item("This is the file 'omega'.\n"),
6179 expected_skip = wc.State(short_A_COPY_path, { })
6180 saved_cwd = os.getcwd()
6181 os.chdir(svntest.main.work_dir)
6182 svntest.actions.run_and_verify_merge(short_A_COPY_path, '2', '4',
6183 sbox.repo_url + \
6184 '/A',
6185 expected_output,
6186 expected_disk,
6187 expected_status,
6188 expected_skip,
6189 None, None, None, None,
6190 None, 1)
6191 # Now do the reverse merge into the subtree.
6192 short_H_COPY_path = shorten_path_kludge(H_COPY_path)
6193 expected_output = wc.State(short_H_COPY_path, {
6194 'psi' : Item(status='G '),
6196 expected_status = wc.State(short_H_COPY_path, {
6197 '' : Item(status=' M', wc_rev=2),
6198 'chi' : Item(status=' ', wc_rev=2),
6199 'psi' : Item(status=' ', wc_rev=2),
6200 'omega' : Item(status=' ', wc_rev=2),
6202 expected_disk = wc.State('', {
6203 '' : Item(props={SVN_PROP_MERGEINFO : ''}),
6204 'chi' : Item("This is the file 'chi'.\n"),
6205 'psi' : Item("This is the file 'psi'.\n"),
6206 'omega' : Item("This is the file 'omega'.\n"),
6208 expected_skip = wc.State(short_H_COPY_path, { })
6209 svntest.actions.run_and_verify_merge(short_H_COPY_path, '4', '2',
6210 sbox.repo_url + \
6211 '/A/D/H',
6212 expected_output,
6213 expected_disk,
6214 expected_status,
6215 expected_skip,
6216 None, None, None, None,
6217 None, 1)
6219 # Test areas B and C -- Reverse merge r3 into A_COPY, this would result in
6220 # empty mergeinfo on A_COPY and A_COPY/D/H, but the empty mergeinfo on the
6221 # latter elides to the former. And then the empty mergeinfo on A_COPY,
6222 # which has no parent with explicit mergeinfo to override (in either the WC
6223 # or the repos) itself elides. This leaves the WC in the same unmodified
6224 # state as after the call to set_up_branch().
6225 short_rho_COPY_path = shorten_path_kludge(rho_COPY_path)
6226 expected_output = expected_merge_output(
6227 [[4,3]], 'G ' + short_rho_COPY_path + '\n')
6228 svntest.actions.run_and_verify_svn(None, expected_output,
6229 [], 'merge', '-r4:2',
6230 sbox.repo_url + '/A',
6231 short_A_COPY_path)
6232 os.chdir(saved_cwd)
6233 svntest.actions.run_and_verify_status(wc_dir, wc_status)
6234 # Check that A_COPY's mergeinfo is gone.
6235 svntest.actions.run_and_verify_svn(None, [], [], 'pg', 'svn:mergeinfo',
6236 A_COPY_path)
6238 def prop_add_to_child_with_mergeinfo(sbox):
6239 "merge adding prop to child of merge target works"
6241 # Test for Issue #2781 Prop add to child of merge target corrupts WC if
6242 # child has mergeinfo.
6244 sbox.build()
6245 wc_dir = sbox.wc_dir
6246 expected_disk, expected_status = set_up_branch(sbox)
6248 # Some paths we'll care about
6249 beta_path = os.path.join(wc_dir, "A", "B", "E", "beta")
6250 beta_COPY_path = os.path.join(wc_dir, "A_COPY", "B", "E", "beta")
6251 B_COPY_path = os.path.join(wc_dir, "A_COPY", "B")
6253 # Set a non-mergeinfo prop on a file.
6254 svntest.actions.run_and_verify_svn(None,
6255 ["property 'prop:name' set on '" +
6256 beta_path + "'\n"], [], 'ps',
6257 'prop:name', 'propval', beta_path)
6258 expected_disk.tweak('A/B/E/beta', props={'prop:name' : 'propval'})
6259 expected_status.tweak('A/B/E/beta', wc_rev=7)
6260 expected_output = wc.State(wc_dir,
6261 {'A/B/E/beta' : Item(verb='Sending')})
6262 svntest.actions.run_and_verify_commit(wc_dir,
6263 expected_output,
6264 expected_status,
6265 None,
6266 wc_dir)
6268 # Merge r4:5 from A/B/E/beta into A_COPY/B/E/beta.
6269 short_beta_COPY_path = shorten_path_kludge(beta_COPY_path)
6270 saved_cwd = os.getcwd()
6271 os.chdir(svntest.main.work_dir)
6272 svntest.actions.run_and_verify_svn(None,
6273 expected_merge_output([[5]],
6274 'U ' + short_beta_COPY_path +'\n'),
6275 [], 'merge', '-c5',
6276 sbox.repo_url + '/A/B/E/beta',
6277 short_beta_COPY_path)
6278 os.chdir(saved_cwd)
6280 # Merge r6:7 into A_COPY/B. In issue #2781 this adds a bogus
6281 # and incomplete entry in A_COPY/B/.svn/entries for 'beta'.
6282 short_B_COPY_path = shorten_path_kludge(B_COPY_path)
6283 expected_output = wc.State(short_B_COPY_path, {
6284 'E/beta' : Item(status=' U'),
6286 expected_status = wc.State(short_B_COPY_path, {
6287 '' : Item(status=' M', wc_rev=2),
6288 'E' : Item(status=' ', wc_rev=2),
6289 'E/alpha' : Item(status=' ', wc_rev=2),
6290 'E/beta' : Item(status='MM', wc_rev=2),
6291 'lambda' : Item(status=' ', wc_rev=2),
6292 'F' : Item(status=' ', wc_rev=2),
6294 expected_disk = wc.State('', {
6295 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B:7'}),
6296 'E' : Item(),
6297 'E/alpha' : Item("This is the file 'alpha'.\n"),
6298 'E/beta' : Item(contents="New content",
6299 props={SVN_PROP_MERGEINFO : '/A/B/E/beta:5,7',
6300 'prop:name' : 'propval'}),
6301 'F' : Item(),
6302 'lambda' : Item("This is the file 'lambda'.\n")
6304 expected_skip = wc.State(short_B_COPY_path, { })
6305 os.chdir(svntest.main.work_dir)
6306 svntest.actions.run_and_verify_merge(short_B_COPY_path, '6', '7',
6307 sbox.repo_url + \
6308 '/A/B',
6309 expected_output,
6310 expected_disk,
6311 expected_status,
6312 expected_skip,
6313 None, None, None, None,
6314 None, 1)
6316 def diff_repos_does_not_update_mergeinfo(sbox):
6317 "don't set mergeinfo when merging from another repo"
6319 # Test for issue #2788.
6321 sbox.build()
6322 wc_dir = sbox.wc_dir
6323 expected_disk, expected_status = set_up_branch(sbox)
6325 # Create a second repository with the same greek tree
6326 repo_dir = sbox.repo_dir
6327 other_repo_dir, other_repo_url = sbox.add_repo_path("other")
6328 svntest.main.copy_repos(repo_dir, other_repo_dir, 6, 1)
6330 # Merge r3:4 (using implied peg revisions) from 'other' repos into
6331 # A_COPY/D/G. Merge should succeed, but no mergeinfo should be set.
6333 # Search for the comment entitled "The Merge Kluge" elsewhere in
6334 # this file, to understand why we shorten and chdir() below.
6335 short_G_COPY_path = shorten_path_kludge(os.path.join(wc_dir,
6336 "A_COPY", "D", "G"))
6337 saved_cwd = os.getcwd()
6339 os.chdir(svntest.main.work_dir)
6340 svntest.actions.run_and_verify_svn(None,
6341 expected_merge_output([[4]],
6342 'U ' +
6343 os.path.join(short_G_COPY_path,
6344 "rho") + '\n'),
6345 [], 'merge', '-c4',
6346 other_repo_url + '/A/D/G',
6347 short_G_COPY_path)
6348 os.chdir(saved_cwd)
6350 # Merge r4:5 (using explicit peg revisions) from 'other' repos into
6351 # A_COPY/B/E. Merge should succeed, but no mergeinfo should be set.
6352 short_E_COPY_path = shorten_path_kludge(os.path.join(wc_dir,
6353 "A_COPY", "B", "E"))
6355 os.chdir(svntest.main.work_dir)
6356 svntest.actions.run_and_verify_svn(None,
6357 expected_merge_output([[5]],
6358 'U ' +
6359 os.path.join(short_E_COPY_path,
6360 "beta") +'\n'),
6361 [], 'merge',
6362 other_repo_url + '/A/B/E@4',
6363 other_repo_url + '/A/B/E@5',
6364 short_E_COPY_path)
6365 os.chdir(saved_cwd)
6367 expected_status.tweak('A_COPY/D/G/rho', 'A_COPY/B/E/beta', status='M ')
6368 svntest.actions.run_and_verify_status(wc_dir, expected_status)
6370 def avoid_reflected_revs(sbox):
6371 "avoid repeated merges for cyclic merging"
6373 ## See http://subversion.tigris.org/issues/show_bug.cgi?id=2897. ##
6375 # Create a WC with a single branch
6376 sbox.build()
6377 wc_dir = sbox.wc_dir
6378 wc_disk, wc_status = set_up_branch(sbox, True, 1)
6380 # Some paths we'll care about
6381 A_path = os.path.join(wc_dir, 'A')
6382 A_COPY_path = os.path.join(wc_dir, 'A_COPY')
6383 tfile1_path = os.path.join(wc_dir, 'A', 'tfile1')
6384 tfile2_path = os.path.join(wc_dir, 'A', 'tfile2')
6385 bfile1_path = os.path.join(A_COPY_path, 'bfile1')
6386 bfile2_path = os.path.join(A_COPY_path, 'bfile2')
6388 # Contents to be added to files
6389 tfile1_content = "This is tfile1\n"
6390 tfile2_content = "This is tfile2\n"
6391 bfile1_content = "This is bfile1\n"
6392 bfile2_content = "This is bfile2\n"
6394 # We'll consider A as the trunk and A_COPY as the feature branch
6395 # Create a tfile1 in A
6396 svntest.main.file_write(tfile1_path, tfile1_content)
6397 svntest.actions.run_and_verify_svn(None, None, [], 'add', tfile1_path)
6398 expected_output = wc.State(wc_dir, {'A/tfile1' : Item(verb='Adding')})
6399 wc_status.add({'A/tfile1' : Item(status=' ', wc_rev=3)})
6400 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
6401 wc_status, None, wc_dir)
6403 # Create a bfile1 in A_COPY
6404 svntest.main.file_write(bfile1_path, bfile1_content)
6405 svntest.actions.run_and_verify_svn(None, None, [], 'add', bfile1_path)
6406 expected_output = wc.State(wc_dir, {'A_COPY/bfile1' : Item(verb='Adding')})
6407 wc_status.add({'A_COPY/bfile1' : Item(status=' ', wc_rev=4)})
6408 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
6409 wc_status, None, wc_dir)
6411 # Create one more file in A
6412 svntest.main.file_write(tfile2_path, tfile2_content)
6413 svntest.actions.run_and_verify_svn(None, None, [], 'add', tfile2_path)
6414 expected_output = wc.State(wc_dir, {'A/tfile2' : Item(verb='Adding')})
6415 wc_status.add({'A/tfile2' : Item(status=' ', wc_rev=5)})
6416 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
6417 wc_status, None, wc_dir)
6419 short_A_COPY = shorten_path_kludge(A_COPY_path)
6420 saved_cwd = os.getcwd()
6421 os.chdir(svntest.main.work_dir)
6423 # Merge r5 from /A to /A_COPY, creating r6
6424 expected_output = wc.State(short_A_COPY, {
6425 'tfile2' : Item(status='A '),
6427 expected_status = wc.State(short_A_COPY, {
6428 '' : Item(status=' M', wc_rev=2),
6429 'tfile2' : Item(status='A ', wc_rev='-', copied='+'),
6430 'bfile1' : Item(status=' ', wc_rev=4),
6431 'mu' : Item(status=' ', wc_rev=2),
6432 'C' : Item(status=' ', wc_rev=2),
6433 'D' : Item(status=' ', wc_rev=2),
6434 'B' : Item(status=' ', wc_rev=2),
6435 'B/lambda' : Item(status=' ', wc_rev=2),
6436 'B/E' : Item(status=' ', wc_rev=2),
6437 'B/E/alpha': Item(status=' ', wc_rev=2),
6438 'B/E/beta' : Item(status=' ', wc_rev=2),
6439 'B/F' : Item(status=' ', wc_rev=2),
6440 'D/gamma' : Item(status=' ', wc_rev=2),
6441 'D/G' : Item(status=' ', wc_rev=2),
6442 'D/G/pi' : Item(status=' ', wc_rev=2),
6443 'D/G/rho' : Item(status=' ', wc_rev=2),
6444 'D/G/tau' : Item(status=' ', wc_rev=2),
6445 'D/H' : Item(status=' ', wc_rev=2),
6446 'D/H/chi' : Item(status=' ', wc_rev=2),
6447 'D/H/omega': Item(status=' ', wc_rev=2),
6448 'D/H/psi' : Item(status=' ', wc_rev=2),
6450 expected_disk = wc.State('', {
6451 '' : Item(props={SVN_PROP_MERGEINFO : '/A:5'}),
6452 'tfile2' : Item(tfile2_content),
6453 'bfile1' : Item(bfile1_content),
6454 'mu' : Item("This is the file 'mu'.\n"),
6455 'C' : Item(),
6456 'D' : Item(),
6457 'B' : Item(),
6458 'B/lambda' : Item("This is the file 'lambda'.\n"),
6459 'B/E' : Item(),
6460 'B/E/alpha': Item("This is the file 'alpha'.\n"),
6461 'B/E/beta' : Item("This is the file 'beta'.\n"),
6462 'B/F' : Item(),
6463 'D/gamma' : Item("This is the file 'gamma'.\n"),
6464 'D/G' : Item(),
6465 'D/G/pi' : Item("This is the file 'pi'.\n"),
6466 'D/G/rho' : Item("This is the file 'rho'.\n"),
6467 'D/G/tau' : Item("This is the file 'tau'.\n"),
6468 'D/H' : Item(),
6469 'D/H/chi' : Item("This is the file 'chi'.\n"),
6470 'D/H/omega': Item("This is the file 'omega'.\n"),
6471 'D/H/psi' : Item("This is the file 'psi'.\n"),
6473 expected_skip = wc.State(short_A_COPY, {})
6475 svntest.actions.run_and_verify_merge(short_A_COPY, '4', '5',
6476 sbox.repo_url + '/A',
6477 expected_output,
6478 expected_disk,
6479 expected_status,
6480 expected_skip,
6481 None, None, None, None, None, 1)
6482 os.chdir(saved_cwd)
6484 # Sync up with the trunk ie., A
6485 svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
6486 expected_output = wc.State(wc_dir, {
6487 'A_COPY' : Item(verb='Sending'),
6488 'A_COPY/tfile2' : Item(verb='Adding'),
6490 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
6491 None, None, wc_dir)
6492 os.chdir(svntest.main.work_dir)
6494 # Merge r3 from /A to /A_COPY, creating r7
6495 expected_output = wc.State(short_A_COPY, {
6496 'tfile1' : Item(status='A '),
6498 expected_status.tweak(wc_rev=5)
6499 expected_status.tweak('', wc_rev=6)
6500 expected_status.tweak('tfile2', status=' ', copied=None, wc_rev=6)
6501 expected_status.add({
6502 'tfile1' : Item(status='A ', wc_rev='-', copied='+'),
6504 expected_disk.tweak('', props={SVN_PROP_MERGEINFO : '/A:3,5'})
6505 expected_disk.add({
6506 'tfile1' : Item(tfile1_content),
6509 svntest.actions.run_and_verify_merge(short_A_COPY, '2', '3',
6510 sbox.repo_url + '/A',
6511 expected_output,
6512 expected_disk,
6513 expected_status,
6514 expected_skip,
6515 None, None, None, None, None, 1)
6517 os.chdir(saved_cwd)
6519 svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
6520 expected_output = wc.State(wc_dir, {
6521 'A_COPY' : Item(verb='Sending'),
6522 'A_COPY/tfile1' : Item(verb='Adding'),
6524 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
6525 None, None, wc_dir)
6527 # Add bfile2 to A_COPY
6528 svntest.main.file_write(bfile2_path, bfile2_content)
6529 svntest.actions.run_and_verify_svn(None, None, [], 'add', bfile2_path)
6530 expected_output = wc.State(wc_dir, {'A_COPY/bfile2' : Item(verb='Adding')})
6531 wc_status.tweak(wc_rev=6)
6532 wc_status.add({
6533 'A_COPY/bfile2' : Item(status=' ', wc_rev=8),
6534 'A_COPY' : Item(status=' ', wc_rev=7),
6535 'A_COPY/tfile2' : Item(status=' ', wc_rev=6),
6536 'A_COPY/tfile1' : Item(status=' ', wc_rev=7),
6538 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
6539 wc_status, None, wc_dir)
6541 # Merge 2:8 from A_COPY(feature branch) to A(trunk).
6542 short_A_path = shorten_path_kludge(A_path)
6543 expected_output = wc.State(short_A_path, {
6544 'bfile2' : Item(status='A '),
6545 'bfile1' : Item(status='A '),
6547 expected_status = wc.State(short_A_path, {
6548 '' : Item(status=' M', wc_rev=6),
6549 'bfile2' : Item(status='A ', wc_rev='-', copied='+'),
6550 'bfile1' : Item(status='A ', wc_rev='-', copied='+'),
6551 'tfile2' : Item(status=' ', wc_rev=6),
6552 'tfile1' : Item(status=' ', wc_rev=6),
6553 'mu' : Item(status=' ', wc_rev=6),
6554 'C' : Item(status=' ', wc_rev=6),
6555 'D' : Item(status=' ', wc_rev=6),
6556 'B' : Item(status=' ', wc_rev=6),
6557 'B/lambda' : Item(status=' ', wc_rev=6),
6558 'B/E' : Item(status=' ', wc_rev=6),
6559 'B/E/alpha' : Item(status=' ', wc_rev=6),
6560 'B/E/beta' : Item(status=' ', wc_rev=6),
6561 'B/F' : Item(status=' ', wc_rev=6),
6562 'D/gamma' : Item(status=' ', wc_rev=6),
6563 'D/G' : Item(status=' ', wc_rev=6),
6564 'D/G/pi' : Item(status=' ', wc_rev=6),
6565 'D/G/rho' : Item(status=' ', wc_rev=6),
6566 'D/G/tau' : Item(status=' ', wc_rev=6),
6567 'D/H' : Item(status=' ', wc_rev=6),
6568 'D/H/chi' : Item(status=' ', wc_rev=6),
6569 'D/H/omega' : Item(status=' ', wc_rev=6),
6570 'D/H/psi' : Item(status=' ', wc_rev=6),
6572 expected_disk = wc.State('', {
6573 '' : Item(props={SVN_PROP_MERGEINFO : '/A_COPY:3-8'}),
6574 'bfile2' : Item(bfile2_content),
6575 'bfile1' : Item(bfile1_content),
6576 'tfile2' : Item(tfile2_content),
6577 'tfile1' : Item(tfile1_content),
6578 'mu' : Item("This is the file 'mu'.\n"),
6579 'C' : Item(),
6580 'D' : Item(),
6581 'B' : Item(),
6582 'B/lambda' : Item("This is the file 'lambda'.\n"),
6583 'B/E' : Item(),
6584 'B/E/alpha' : Item("This is the file 'alpha'.\n"),
6585 'B/E/beta' : Item("This is the file 'beta'.\n"),
6586 'B/F' : Item(),
6587 'D/gamma' : Item("This is the file 'gamma'.\n"),
6588 'D/G' : Item(),
6589 'D/G/pi' : Item("This is the file 'pi'.\n"),
6590 'D/G/rho' : Item("This is the file 'rho'.\n"),
6591 'D/G/tau' : Item("This is the file 'tau'.\n"),
6592 'D/H' : Item(),
6593 'D/H/chi' : Item("This is the file 'chi'.\n"),
6594 'D/H/omega' : Item("This is the file 'omega'.\n"),
6595 'D/H/psi' : Item("This is the file 'psi'.\n"),
6598 expected_skip = wc.State(short_A_path, {})
6599 saved_cwd = os.getcwd()
6600 os.chdir(svntest.main.work_dir)
6602 svntest.actions.run_and_verify_merge(short_A_path, '2', '8',
6603 sbox.repo_url + '/A_COPY',
6604 expected_output,
6605 expected_disk,
6606 expected_status,
6607 expected_skip,
6608 None, None, None, None, None, 1)
6609 os.chdir(saved_cwd)
6612 def update_loses_mergeinfo(sbox):
6613 "update does not merge mergeinfo"
6616 When a working copy receives a fresh svn:mergeinfo property due to update,
6617 and working copy has some local mergeinfo(yet to be added), local mergeinfo
6618 is left unchanged.
6621 sbox.build()
6622 wc_dir = sbox.wc_dir
6623 A_C_wc_dir = wc_dir + '/A/C'
6624 A_B_url = sbox.repo_url + '/A/B'
6625 A_B_J_url = sbox.repo_url + '/A/B/J'
6626 A_B_K_url = sbox.repo_url + '/A/B/K'
6627 svntest.actions.run_and_verify_svn(None, ['\n', 'Committed revision 2.\n'],
6629 'mkdir', '-m', 'rev 2', A_B_J_url)
6630 svntest.actions.run_and_verify_svn(None, ['\n', 'Committed revision 3.\n'],
6632 'mkdir', '-m', 'rev 3', A_B_K_url)
6634 other_wc = sbox.add_wc_path('other')
6635 svntest.actions.duplicate_dir(wc_dir, other_wc)
6637 short_A_C_wc_dir = shorten_path_kludge(A_C_wc_dir)
6638 expected_output = wc.State(short_A_C_wc_dir, {'J' : Item(status='A ')})
6639 expected_disk = wc.State('', {
6640 'J' : Item(),
6641 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B:2'}),
6643 expected_status = wc.State(short_A_C_wc_dir,
6644 { '' : Item(wc_rev=1, status=' M'),
6645 'J' : Item(status='A ',
6646 wc_rev='-', copied='+')
6649 expected_skip = wc.State('', { })
6650 saved_cwd = os.getcwd()
6651 os.chdir(svntest.main.work_dir)
6652 svntest.actions.run_and_verify_merge(short_A_C_wc_dir, '1', '2',
6653 A_B_url,
6654 expected_output,
6655 expected_disk,
6656 expected_status,
6657 expected_skip,
6658 check_props=1)
6659 os.chdir(saved_cwd)
6660 expected_output = wc.State(A_C_wc_dir, {
6661 '' : Item(verb='Sending'),
6662 'J' : Item(verb='Adding')
6664 expected_status = wc.State(A_C_wc_dir,
6665 { '' : Item(status=' ', wc_rev=4),
6666 'J' : Item(status=' ', wc_rev=4)
6669 svntest.actions.run_and_verify_commit(A_C_wc_dir,
6670 expected_output,
6671 expected_status,
6672 None,
6673 A_C_wc_dir)
6675 other_A_C_wc_dir = other_wc + '/A/C'
6676 short_other_A_C_wc_dir = shorten_path_kludge(other_A_C_wc_dir)
6677 expected_output = wc.State(short_other_A_C_wc_dir, {'K' : Item(status='A ')})
6678 expected_disk = wc.State('', {
6679 'K' : Item(),
6680 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B:3'}),
6682 expected_status = wc.State(short_other_A_C_wc_dir,
6683 { '' : Item(wc_rev=1, status=' M'),
6684 'K' : Item(status='A ',
6685 wc_rev='-', copied='+')
6688 expected_skip = wc.State('', { })
6689 saved_cwd = os.getcwd()
6690 os.chdir(svntest.main.work_dir)
6691 svntest.actions.run_and_verify_merge(short_other_A_C_wc_dir, '2', '3',
6692 A_B_url,
6693 expected_output,
6694 expected_disk,
6695 expected_status,
6696 expected_skip,
6697 check_props=1)
6698 os.chdir(saved_cwd)
6699 expected_output = wc.State(other_A_C_wc_dir,
6700 {'J' : Item(status='A '),
6701 '' : Item(status=' G')
6704 expected_disk = wc.State('', {
6705 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B:2-3'}),
6706 'J' : Item(),
6707 'K' : Item(),
6709 expected_status = wc.State(other_A_C_wc_dir,
6710 { '' : Item(wc_rev=4, status=' M'),
6711 'J' : Item(status=' ', wc_rev='4'),
6712 'K' : Item(status='A ',
6713 wc_rev='-', copied='+')
6716 svntest.actions.run_and_verify_update(other_A_C_wc_dir,
6717 expected_output,
6718 expected_disk,
6719 expected_status,
6720 check_props=1)
6722 # Tests part of issue# 2829, marked as XFail until that issue is fixed.
6723 def merge_loses_mergeinfo(sbox):
6724 "merge should merge mergeinfo"
6727 When a working copy has no mergeinfo(due to local full revert of all merges),
6728 and merge is attempted for someother revision rX, The new mergeinfo should be
6729 /merge/src: rX not all the reverted ones reappearing along with rX.
6732 sbox.build()
6733 wc_dir = sbox.wc_dir
6734 A_C_wc_dir = wc_dir + '/A/C'
6735 A_B_url = sbox.repo_url + '/A/B'
6736 A_B_J_url = sbox.repo_url + '/A/B/J'
6737 A_B_K_url = sbox.repo_url + '/A/B/K'
6738 svntest.actions.run_and_verify_svn(None, ['\n', 'Committed revision 2.\n'],
6740 'mkdir', '-m', 'rev 2', A_B_J_url)
6741 svntest.actions.run_and_verify_svn(None, ['\n', 'Committed revision 3.\n'],
6743 'mkdir', '-m', 'rev 3', A_B_K_url)
6745 short_A_C_wc_dir = shorten_path_kludge(A_C_wc_dir)
6746 expected_output = wc.State(short_A_C_wc_dir, {'J' : Item(status='A ')})
6747 expected_disk = wc.State('', {
6748 'J' : Item(),
6749 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B:2'}),
6751 expected_status = wc.State(short_A_C_wc_dir,
6752 { '' : Item(wc_rev=1, status=' M'),
6753 'J' : Item(status='A ',
6754 wc_rev='-', copied='+')
6757 expected_skip = wc.State('', { })
6758 saved_cwd = os.getcwd()
6759 os.chdir(svntest.main.work_dir)
6760 svntest.actions.run_and_verify_merge(short_A_C_wc_dir, '1', '2',
6761 A_B_url,
6762 expected_output,
6763 expected_disk,
6764 expected_status,
6765 expected_skip,
6766 check_props=1)
6767 os.chdir(saved_cwd)
6768 expected_output = wc.State(A_C_wc_dir, {
6769 '' : Item(verb='Sending'),
6770 'J' : Item(verb='Adding')
6772 expected_status = wc.State(A_C_wc_dir,
6773 { '' : Item(status=' ', wc_rev=4),
6774 'J' : Item(status=' ', wc_rev=4)
6777 svntest.actions.run_and_verify_commit(A_C_wc_dir,
6778 expected_output,
6779 expected_status,
6780 None,
6781 A_C_wc_dir)
6782 expected_output = wc.State(short_A_C_wc_dir, {'J' : Item(status='D ')})
6783 expected_disk = wc.State('', {'J': Item()})
6784 expected_status = wc.State(short_A_C_wc_dir,
6785 { '' : Item(wc_rev=4, status=' M'),
6786 'J' : Item(wc_rev=4, status='D ')
6789 saved_cwd = os.getcwd()
6790 os.chdir(svntest.main.work_dir)
6791 svntest.actions.run_and_verify_merge(short_A_C_wc_dir, '2', '1',
6792 A_B_url,
6793 expected_output,
6794 expected_disk,
6795 expected_status,
6796 expected_skip,
6797 check_props=1)
6798 os.chdir(saved_cwd)
6800 expected_output = wc.State(short_A_C_wc_dir, {'K' : Item(status='A ')})
6801 expected_disk = wc.State('', {
6802 'K' : Item(),
6803 'J' : Item(),
6804 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B:3'}),
6806 expected_status = wc.State(short_A_C_wc_dir,
6807 { '' : Item(wc_rev=4, status=' M'),
6808 'K' : Item(status='A ',
6809 wc_rev='-', copied='+'),
6810 'J' : Item(wc_rev=4, status='D ')
6813 saved_cwd = os.getcwd()
6814 os.chdir(svntest.main.work_dir)
6815 svntest.actions.run_and_verify_merge(short_A_C_wc_dir, '2', '3',
6816 A_B_url,
6817 expected_output,
6818 expected_disk,
6819 expected_status,
6820 expected_skip,
6821 check_props=1)
6823 def single_file_replace_style_merge_capability(sbox):
6824 "replace-style merge capability for a single file"
6826 # Test for issue #2853, do_single_file_merge() lacks "Replace-style
6827 # merge" capability
6829 sbox.build()
6830 wc_dir = sbox.wc_dir
6831 iota_path = os.path.join(wc_dir, 'iota')
6832 mu_path = os.path.join(wc_dir, 'A', 'mu')
6834 # delete mu and replace it with a copy of iota
6835 svntest.main.run_svn(None, 'rm', mu_path)
6836 svntest.main.run_svn(None, 'mv', iota_path, mu_path)
6838 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
6839 expected_status.tweak('A/mu', status=' ', wc_rev=2)
6840 expected_status.remove('iota')
6841 expected_output = svntest.wc.State(wc_dir, {
6842 'iota': Item(verb='Deleting'),
6843 'A/mu': Item(verb='Replacing'),
6845 svntest.actions.run_and_verify_commit(wc_dir,
6846 expected_output,
6847 expected_status,
6848 None, wc_dir)
6850 # Merge the file mu alone to rev1
6851 svntest.actions.run_and_verify_svn(None,
6852 expected_merge_output(None,
6853 ['D ' + mu_path + '\n',
6854 'A ' + mu_path + '\n']),
6856 'merge',
6857 mu_path + '@2',
6858 mu_path + '@1',
6859 mu_path)
6861 # Test for issue 2786 fix.
6862 def merge_to_out_of_date_target(sbox):
6863 "merge to ood path can lead to inaccurate mergeinfo"
6865 # Create a WC with a branch.
6866 sbox.build()
6867 wc_dir = sbox.wc_dir
6868 wc_disk, wc_status = set_up_branch(sbox, False, 1)
6870 # Make second working copy
6871 other_wc = sbox.add_wc_path('other')
6872 svntest.actions.duplicate_dir(wc_dir, other_wc)
6874 # Some paths we'll care about
6875 A_COPY_H_path = os.path.join(wc_dir, "A_COPY", "D", "H")
6876 other_A_COPY_H_path = os.path.join(other_wc, "A_COPY", "D", "H")
6878 # Merge -c3 into A_COPY/D/H of first WC.
6880 # Search for the comment entitled "The Merge Kluge" elsewhere in
6881 # this file, to understand why we shorten and chdir() below.
6882 short_H_COPY_path = shorten_path_kludge(A_COPY_H_path)
6883 expected_output = wc.State(short_H_COPY_path, {
6884 'psi' : Item(status='U ')
6886 expected_status = wc.State(short_H_COPY_path, {
6887 '' : Item(status=' M', wc_rev=2),
6888 'psi' : Item(status='M ', wc_rev=2),
6889 'omega' : Item(status=' ', wc_rev=2),
6890 'chi' : Item(status=' ', wc_rev=2),
6892 expected_disk = wc.State('', {
6893 '' : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:3'}),
6894 'psi' : Item("New content"),
6895 'omega' : Item("This is the file 'omega'.\n"),
6896 'chi' : Item("This is the file 'chi'.\n"),
6898 expected_skip = wc.State(short_H_COPY_path, { })
6899 saved_cwd = os.getcwd()
6900 os.chdir(svntest.main.work_dir)
6901 svntest.actions.run_and_verify_merge(short_H_COPY_path, '2', '3',
6902 sbox.repo_url + '/A/D/H',
6903 expected_output, expected_disk,
6904 expected_status, expected_skip,
6905 None, None, None, None, None, 1)
6906 os.chdir(saved_cwd)
6908 # Commit merge to first WC.
6909 wc_status.tweak('A_COPY/D/H/psi', 'A_COPY/D/H', wc_rev=7)
6910 expected_output = svntest.wc.State(wc_dir, {
6911 'A_COPY/D/H' : Item(verb='Sending'),
6912 'A_COPY/D/H/psi': Item(verb='Sending'),
6914 svntest.actions.run_and_verify_commit(wc_dir,
6915 expected_output,
6916 wc_status,
6917 None, wc_dir)
6919 # Merge -c6 into A_COPY/D/H of other WC.
6920 short_H_COPY_path = shorten_path_kludge(other_A_COPY_H_path)
6921 expected_output = wc.State(short_H_COPY_path, {
6922 'omega' : Item(status='U ')
6924 expected_status = wc.State(short_H_COPY_path, {
6925 '' : Item(status=' M', wc_rev=2),
6926 'psi' : Item(status=' ', wc_rev=2),
6927 'omega' : Item(status='M ', wc_rev=2),
6928 'chi' : Item(status=' ', wc_rev=2),
6930 expected_disk = wc.State('', {
6931 '' : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:6'}),
6932 'psi' : Item("This is the file 'psi'.\n"),
6933 'omega' : Item("New content"),
6934 'chi' : Item("This is the file 'chi'.\n"),
6936 expected_skip = wc.State(short_H_COPY_path, { })
6937 os.chdir(svntest.main.work_dir)
6938 svntest.actions.run_and_verify_merge(short_H_COPY_path, '5', '6',
6939 sbox.repo_url + '/A/D/H',
6940 expected_output, expected_disk,
6941 expected_status, expected_skip,
6942 None, None, None, None, None, 1)
6943 os.chdir(saved_cwd)
6945 # Update A_COPY/D/H in other WC. Local mergeinfo for r6 on A_COPY/D/H
6946 # should be *merged* with r3 from first WC.
6947 expected_output = svntest.wc.State(other_A_COPY_H_path, {
6948 '' : Item(status=' G'),
6949 'psi' : Item(status='U ')
6951 other_disk = wc.State('', {
6952 '' : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:3,6'}),
6953 'psi' : Item(contents="New content"),
6954 'chi' : Item("This is the file 'chi'.\n"),
6955 'omega' : Item(contents="New content"),
6957 other_status = wc.State(other_A_COPY_H_path,{
6958 '' : Item(wc_rev=7, status=' M'),
6959 'chi' : Item(wc_rev=7, status=' '),
6960 'psi' : Item(wc_rev=7, status=' '),
6961 'omega' : Item(wc_rev=7, status='M ')
6963 svntest.actions.run_and_verify_update(other_A_COPY_H_path,
6964 expected_output,
6965 other_disk,
6966 other_status,
6967 check_props=1)
6969 def merge_with_depth_files(sbox):
6970 "merge test for --depth files"
6972 sbox.build()
6973 wc_dir = sbox.wc_dir
6975 # Some paths we'll care about
6976 mu_path = os.path.join(wc_dir, 'A', 'mu')
6977 gamma_path = os.path.join(wc_dir, 'A', 'D', 'gamma')
6978 Acopy_path = os.path.join(wc_dir, 'A_copy')
6979 Acopy_mu_path = os.path.join(wc_dir, 'A_copy', 'mu')
6980 A_url = sbox.repo_url + '/A'
6981 Acopy_url = sbox.repo_url + '/A_copy'
6983 # Copy A_url to A_copy_url
6984 svntest.actions.run_and_verify_svn(None, None, [], 'cp',
6985 A_url, Acopy_url,
6986 '-m', 'create a new copy of A')
6988 svntest.main.file_write(mu_path, "this is file 'mu' modified.\n")
6989 svntest.main.file_write(gamma_path, "this is file 'gamma' modified.\n")
6991 # Create expected output tree for commit
6992 expected_output = wc.State(wc_dir, {
6993 'A/mu' : Item(verb='Sending'),
6994 'A/D/gamma' : Item(verb='Sending'),
6997 # Create expected status tree for commit
6998 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
6999 expected_status.add({
7000 'A/mu' : Item(status=' ', wc_rev=3),
7001 'A/D/gamma' : Item(status=' ', wc_rev=3),
7004 # Commit the modified contents
7005 svntest.actions.run_and_verify_commit(wc_dir,
7006 expected_output,
7007 expected_status,
7008 None,
7009 wc_dir)
7011 # Update working copy
7012 svntest.actions.run_and_verify_svn(None, None, [],
7013 'up', Acopy_path)
7015 # Merge r1:3 into A_copy with --depth files. The merge only affects
7016 # 'A_copy' and its one file child 'mu', so 'A_copy' gets non-inheritable
7017 # mergeinfo for -r1:3 and 'mu' gets its own complete set of mergeinfo:
7018 # r1 from its parent, and r1:3 from the merge itself.
7020 # Search for the comment entitled "The Merge Kluge" elsewhere in
7021 # this file, to understand why we shorten and chdir() below.
7022 short_A_COPY_path = shorten_path_kludge(Acopy_path)
7023 expected_output = wc.State(short_A_COPY_path, {
7024 'mu' : Item(status='U '),
7026 expected_status = wc.State(short_A_COPY_path, {
7027 '' : Item(status=' M'),
7028 'B' : Item(status=' '),
7029 'mu' : Item(status='MM'),
7030 'B/E' : Item(status=' '),
7031 'B/E/alpha' : Item(status=' '),
7032 'B/E/beta' : Item(status=' '),
7033 'B/lambda' : Item(status=' '),
7034 'B/F' : Item(status=' '),
7035 'C' : Item(status=' '),
7036 'D' : Item(status=' '),
7037 'D/G' : Item(status=' '),
7038 'D/G/pi' : Item(status=' '),
7039 'D/G/rho' : Item(status=' '),
7040 'D/G/tau' : Item(status=' '),
7041 'D/gamma' : Item(status=' '),
7042 'D/H' : Item(status=' '),
7043 'D/H/chi' : Item(status=' '),
7044 'D/H/psi' : Item(status=' '),
7045 'D/H/omega' : Item(status=' '),
7047 expected_status.tweak(wc_rev=3)
7048 expected_disk = wc.State('', {
7049 '' : Item(props={SVN_PROP_MERGEINFO : '/A:2-3*'}),
7050 'B' : Item(),
7051 'mu' : Item("this is file 'mu' modified.\n",
7052 props={SVN_PROP_MERGEINFO : '/A/mu:2-3'}),
7053 'B/E' : Item(),
7054 'B/E/alpha' : Item("This is the file 'alpha'.\n"),
7055 'B/E/beta' : Item("This is the file 'beta'.\n"),
7056 'B/lambda' : Item("This is the file 'lambda'.\n"),
7057 'B/F' : Item(),
7058 'C' : Item(),
7059 'D' : Item(),
7060 'D/G' : Item(),
7061 'D/G/pi' : Item("This is the file 'pi'.\n"),
7062 'D/G/rho' : Item("This is the file 'rho'.\n"),
7063 'D/G/tau' : Item("This is the file 'tau'.\n"),
7064 'D/gamma' : Item("This is the file 'gamma'.\n"),
7065 'D/H' : Item(),
7066 'D/H/chi' : Item("This is the file 'chi'.\n"),
7067 'D/H/psi' : Item("This is the file 'psi'.\n"),
7068 'D/H/omega' : Item("This is the file 'omega'.\n"),
7070 expected_skip = wc.State(short_A_COPY_path, { })
7071 saved_cwd = os.getcwd()
7072 os.chdir(svntest.main.work_dir)
7073 svntest.actions.run_and_verify_merge(short_A_COPY_path, '1', '3',
7074 sbox.repo_url + '/A',
7075 expected_output, expected_disk,
7076 expected_status, expected_skip,
7077 None, None, None, None, None, 1, 1,
7078 '--depth', 'files')
7079 os.chdir(saved_cwd)
7081 def merge_fails_if_subtree_is_deleted_on_src(sbox):
7082 "merge fails if subtree is deleted on src"
7084 ## See http://subversion.tigris.org/issues/show_bug.cgi?id=2876. ##
7086 # Create a WC
7087 sbox.build()
7088 wc_dir = sbox.wc_dir
7090 # Some paths we'll care about
7091 Acopy_path = os.path.join(wc_dir, 'A_copy')
7092 gamma_path = os.path.join(wc_dir, 'A', 'D', 'gamma')
7093 Acopy_gamma_path = os.path.join(wc_dir, 'A_copy', 'D', 'gamma')
7094 A_url = sbox.repo_url + '/A'
7095 Acopy_url = sbox.repo_url + '/A_copy'
7097 # Contents to be added to 'gamma'
7098 new_content = "line1\nline2\nline3\nline4\nline5\n"
7100 svntest.main.file_write(gamma_path, new_content)
7102 # Create expected output tree for commit
7103 expected_output = wc.State(wc_dir, {
7104 'A/D/gamma' : Item(verb='Sending'),
7107 # Create expected status tree for commit
7108 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
7109 expected_status.tweak('A/D/gamma', wc_rev=2)
7111 # Commit the new content
7112 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
7113 expected_status, None, wc_dir)
7115 svntest.actions.run_and_verify_svn(None, None, [], 'cp', A_url, Acopy_url,
7116 '-m', 'create a new copy of A')
7118 # Update working copy
7119 svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
7121 svntest.main.file_substitute(gamma_path, "line1", "this is line1")
7122 # Create expected output tree for commit
7123 expected_output = wc.State(wc_dir, {
7124 'A/D/gamma' : Item(verb='Sending'),
7127 # Create expected status tree for commit
7128 expected_status.tweak(wc_rev=3)
7129 expected_status.tweak('A/D/gamma', wc_rev=4)
7130 expected_status.add({
7131 'A_copy' : Item(status=' ', wc_rev=3),
7132 'A_copy/B' : Item(status=' ', wc_rev=3),
7133 'A_copy/B/lambda' : Item(status=' ', wc_rev=3),
7134 'A_copy/B/E' : Item(status=' ', wc_rev=3),
7135 'A_copy/B/E/alpha': Item(status=' ', wc_rev=3),
7136 'A_copy/B/E/beta' : Item(status=' ', wc_rev=3),
7137 'A_copy/B/F' : Item(status=' ', wc_rev=3),
7138 'A_copy/mu' : Item(status=' ', wc_rev=3),
7139 'A_copy/C' : Item(status=' ', wc_rev=3),
7140 'A_copy/D' : Item(status=' ', wc_rev=3),
7141 'A_copy/D/gamma' : Item(status=' ', wc_rev=3),
7142 'A_copy/D/G' : Item(status=' ', wc_rev=3),
7143 'A_copy/D/G/pi' : Item(status=' ', wc_rev=3),
7144 'A_copy/D/G/rho' : Item(status=' ', wc_rev=3),
7145 'A_copy/D/G/tau' : Item(status=' ', wc_rev=3),
7146 'A_copy/D/H' : Item(status=' ', wc_rev=3),
7147 'A_copy/D/H/chi' : Item(status=' ', wc_rev=3),
7148 'A_copy/D/H/omega': Item(status=' ', wc_rev=3),
7149 'A_copy/D/H/psi' : Item(status=' ', wc_rev=3),
7152 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
7153 expected_status, None, wc_dir)
7155 # Delete A/D/gamma from working copy
7156 svntest.actions.run_and_verify_svn(None, None, [], 'delete', gamma_path)
7157 # Create expected output tree for commit
7158 expected_output = wc.State(wc_dir, {
7159 'A/D/gamma' : Item(verb='Deleting'),
7162 expected_status.remove('A/D/gamma')
7164 svntest.actions.run_and_verify_commit(wc_dir,
7165 expected_output,
7166 expected_status,
7167 None,
7168 wc_dir, wc_dir)
7170 svntest.actions.run_and_verify_svn(None, expected_merge_output([[3,4]],
7171 'U ' + Acopy_gamma_path + '\n'),
7172 [], 'merge', '-r1:4',
7173 A_url + '/D/gamma' + '@4',
7174 Acopy_gamma_path)
7176 svntest.actions.run_and_verify_svn(None, expected_merge_output([[5]],
7177 'D ' + Acopy_gamma_path + '\n'),
7178 [], 'merge', '-r1:5', '--force',
7179 A_url, Acopy_path)
7181 # Test for issues:
7183 # 2883: No-op merge (without skip) should not change mergeinfo.
7184 # 2976: Subtrees can lose non-inhertiable ranges
7185 def no_mergeinfo_from_no_op_merge(sbox):
7186 "no-op merge without skips doesn't change mergeinfo"
7188 sbox.build()
7189 wc_dir = sbox.wc_dir
7190 wc_disk, wc_status = set_up_branch(sbox)
7192 # Some paths we'll care about
7193 H_path = os.path.join(wc_dir, "A", "D", "H")
7194 beta_COPY_path = os.path.join(wc_dir, "A_COPY", "B", "E", "beta")
7195 G_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "G")
7196 tau_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "G", "tau")
7197 C_COPY_path = os.path.join(wc_dir, "A_COPY", "C")
7198 D_COPY_path = os.path.join(wc_dir, "A_COPY", "D")
7200 # Part 1: Test for issue #2883
7202 # Merge r5 into A_COPY/B/E/beta and commit it.
7203 # Search for the comment entitled "The Merge Kluge" elsewhere in
7204 # this file, to understand why we shorten and chdir() below.
7205 short_beta_COPY_path = shorten_path_kludge(beta_COPY_path)
7206 expected_skip = wc.State(short_beta_COPY_path, { })
7207 saved_cwd = os.getcwd()
7208 os.chdir(svntest.main.work_dir)
7209 # run_and_verify_merge doesn't support merging to a file WCPATH
7210 # so use run_and_verify_svn.
7211 svntest.actions.run_and_verify_svn(None,
7212 expected_merge_output([[5]],
7213 'U ' + short_beta_COPY_path +
7214 '\n'), [], 'merge', '-c5',
7215 sbox.repo_url + '/A/B/E/beta',
7216 short_beta_COPY_path)
7217 os.chdir(saved_cwd)
7219 expected_output = wc.State(wc_dir,
7220 {'A_COPY/B/E/beta' : Item(verb='Sending')})
7221 wc_status.tweak('A_COPY/B/E/beta', wc_rev=7)
7222 svntest.actions.run_and_verify_commit(wc_dir, expected_output, wc_status,
7223 None, wc_dir)
7225 # Update A_COPY to get all paths to the same working revision.
7226 svntest.actions.run_and_verify_svn(None, ["At revision 7.\n"], [],
7227 'up', wc_dir)
7228 wc_status.tweak(wc_rev=7)
7230 # Merge r4 into A_COPY/D/G.
7231 short_G_COPY_path = shorten_path_kludge(G_COPY_path)
7232 expected_output = wc.State(short_G_COPY_path, {
7233 'rho' : Item(status='U ')
7235 expected_status = wc.State(short_G_COPY_path, {
7236 '' : Item(status=' M', wc_rev=7),
7237 'pi' : Item(status=' ', wc_rev=7),
7238 'rho' : Item(status='M ', wc_rev=7),
7239 'tau' : Item(status=' ', wc_rev=7),
7241 expected_disk = wc.State('', {
7242 '' : Item(props={SVN_PROP_MERGEINFO : '/A/D/G:4'}),
7243 'pi' : Item("This is the file 'pi'.\n"),
7244 'rho' : Item("New content"),
7245 'tau' : Item("This is the file 'tau'.\n"),
7247 expected_skip = wc.State(short_G_COPY_path, { })
7248 os.chdir(svntest.main.work_dir)
7249 svntest.actions.run_and_verify_merge(short_G_COPY_path, '3', '4',
7250 sbox.repo_url + '/A/D/G',
7251 expected_output, expected_disk,
7252 expected_status, expected_skip,
7253 None, None, None, None, None, 1)
7254 wc_status.tweak('A_COPY/D/G', status=' M')
7255 wc_status.tweak('A_COPY/D/G/rho', status='M ')
7257 # Now try a few no-op variants, in every case no mergeinfo should
7258 # change from the above state.
7260 # Do a no-op merge to a file with committed mergeinfo:
7261 # -r2:4 into A_COPY/B/E/beta.
7262 svntest.actions.run_and_verify_svn(None,
7263 expected_merge_output([[3,4]], None),
7264 [], 'merge', '-r2:4',
7265 sbox.repo_url + '/A/B/E/beta',
7266 short_beta_COPY_path)
7268 # Do a no-op merge to a file with inherited mergeinfo:
7269 # -c3 into A_COPY/D/G/tau.
7270 short_tau_COPY_path = shorten_path_kludge(tau_COPY_path)
7271 svntest.actions.run_and_verify_svn(None,
7272 expected_merge_output([[3]]),
7273 [], 'merge', '-c3',
7274 sbox.repo_url + '/A/D/G/tau',
7275 short_tau_COPY_path)
7277 # Do a no-op merge to a dir with local mergeinfo:
7278 # -r4:6 into A_COPY/D/G.
7279 short_tau_COPY_path = shorten_path_kludge(tau_COPY_path)
7280 svntest.actions.run_and_verify_svn(None,
7281 expected_merge_output([[5,6]], None),
7282 [], 'merge', '-r4:6',
7283 sbox.repo_url + '/A/D/G',
7284 short_G_COPY_path)
7285 os.chdir(saved_cwd)
7286 svntest.actions.run_and_verify_svn(None, ["/A/D/G:4\n"], [],
7287 'propget', SVN_PROP_MERGEINFO,
7288 G_COPY_path)
7290 # Do a no-op merge to a dir with inherited mergeinfo:
7291 # All available revs into A_COPY/C.
7292 os.chdir(svntest.main.work_dir)
7293 short_C_COPY_path = shorten_path_kludge(C_COPY_path)
7294 svntest.actions.run_and_verify_svn(None,
7295 expected_merge_output([[2,7]], None),
7296 [], 'merge',
7297 sbox.repo_url + '/A/C',
7298 short_C_COPY_path)
7299 os.chdir(saved_cwd)
7301 # A final check of the WC's status to ensure nothing unexpected occurred
7302 # (that the above merge's stdout/stderr didn't already reveal).
7303 svntest.actions.run_and_verify_status(wc_dir, wc_status)
7305 # Reopened Issue #2883 as the following sequence failed:
7306 # Commit what we have so far as r8.
7307 expected_output = svntest.wc.State(wc_dir, {
7308 'A_COPY/D/G' : Item(verb='Sending'),
7309 'A_COPY/D/G/rho' : Item(verb='Sending'),
7311 wc_status.tweak(status=' ')
7312 wc_status.tweak('A_COPY/D/G', 'A_COPY/D/G/rho', wc_rev=8)
7313 svntest.actions.run_and_verify_commit(wc_dir, expected_output, wc_status,
7314 None, wc_dir)
7315 wc_status.tweak(wc_rev=8)
7316 # Update the WC and add a prop to A/D/H and commit that as r9.
7317 svntest.actions.run_and_verify_svn(None, ['At revision 8.\n'], [],
7318 'update', wc_dir)
7320 svntest.actions.run_and_verify_svn(
7321 None, ["property 'prop:name' set on '" + H_path + "'\n"], [],
7322 'ps', 'prop:name', 'propval', H_path)
7323 expected_output = svntest.wc.State(wc_dir, {
7324 'A/D/H' : Item(verb='Sending'),})
7325 wc_status.tweak(status=' ')
7326 wc_status.tweak('A/D/H', wc_rev=9)
7327 wc_status.tweak('A_COPY/D/G', 'A_COPY/D/G/rho', wc_rev=8)
7328 svntest.actions.run_and_verify_commit(wc_dir, expected_output, wc_status,
7329 None, wc_dir)
7331 # Update A_COPY to get all paths to the same working revision.
7332 svntest.actions.run_and_verify_svn(None, ["At revision 9.\n"], [],
7333 'up', wc_dir)
7334 wc_status.tweak(wc_rev=9)
7336 # Merge r5:9 --depth immediates to A_COPY/D
7337 short_D_COPY_path = shorten_path_kludge(D_COPY_path)
7338 expected_output = wc.State(short_D_COPY_path, {
7339 'H' : Item(status=' U'),
7341 expected_status = wc.State(short_D_COPY_path, {
7342 '' : Item(status=' M', wc_rev=9),
7343 'H' : Item(status=' M', wc_rev=9),
7344 'H/chi' : Item(status=' ', wc_rev=9),
7345 'H/omega' : Item(status=' ', wc_rev=9),
7346 'H/psi' : Item(status=' ', wc_rev=9),
7347 'G' : Item(status=' M', wc_rev=9),
7348 'G/pi' : Item(status=' ', wc_rev=9),
7349 'G/rho' : Item(status=' ', wc_rev=9),
7350 'G/tau' : Item(status=' ', wc_rev=9),
7351 'gamma' : Item(status=' ', wc_rev=9),
7353 expected_disk = wc.State('', {
7354 '' : Item(props={SVN_PROP_MERGEINFO : '/A/D:6-9'}),
7355 'H' : Item(props={'prop:name' : 'propval',
7356 SVN_PROP_MERGEINFO : '/A/D/H:6-9*'}),
7357 'H/chi' : Item("This is the file 'chi'.\n"),
7358 'H/omega' : Item("This is the file 'omega'.\n"),
7359 'H/psi' : Item("This is the file 'psi'.\n"),
7360 'G' : Item(props={SVN_PROP_MERGEINFO : '/A/D/G:4,6-9*'}),
7361 'G/pi' : Item("This is the file 'pi'.\n"),
7362 'G/rho' : Item("New content"),
7363 'G/tau' : Item("This is the file 'tau'.\n"),
7364 'gamma' : Item("This is the file 'gamma'.\n"),
7366 expected_skip = wc.State(short_D_COPY_path, { })
7367 os.chdir(svntest.main.work_dir)
7368 svntest.actions.run_and_verify_merge(short_D_COPY_path, '5', '9',
7369 sbox.repo_url + '/A/D',
7370 expected_output, expected_disk,
7371 expected_status, expected_skip,
7372 None, None, None, None, None, 1, 1,
7373 '--depth', 'immediates')
7374 os.chdir(saved_cwd)
7376 # Commit everything so far.
7377 expected_output = svntest.wc.State(wc_dir, {
7378 'A_COPY/D' : Item(verb='Sending'),
7379 'A_COPY/D/G' : Item(verb='Sending'),
7380 'A_COPY/D/H' : Item(verb='Sending'),
7382 wc_status.tweak('A_COPY/D', 'A_COPY/D/G', 'A_COPY/D/H',
7383 wc_rev=10, status=' ')
7384 svntest.actions.run_and_verify_commit(wc_dir, expected_output, wc_status,
7385 None, wc_dir)
7387 # Do a no-op merge of -c9 to A_COPY/D. In issue #2883 this
7388 # looked like a no-op but modified subtree mergeinfo.
7389 expected_output = wc.State(short_D_COPY_path, {})
7390 expected_status.tweak('', 'G', 'H', wc_rev=10, status=' ')
7391 os.chdir(svntest.main.work_dir)
7392 svntest.actions.run_and_verify_merge(short_D_COPY_path, '8', '9',
7393 sbox.repo_url + '/A/D',
7394 expected_output, expected_disk,
7395 expected_status, expected_skip,
7396 None, None, None, None, None, 1, 1)
7398 os.chdir(saved_cwd)
7400 # Part 2: Test for issue #2976
7402 # Merge r3:8 A_COPY/D/H and A_COPY/D/G should
7403 # both retain mergeinfo for r9*
7405 # Update A_COPY to get all paths to the same working revision.
7406 svntest.actions.run_and_verify_svn(None, ["At revision 10.\n"], [],
7407 'up', wc_dir)
7408 expected_status.tweak(wc_rev=10)
7410 short_D_COPY_path = shorten_path_kludge(D_COPY_path)
7411 expected_output = wc.State(short_D_COPY_path, {
7412 'H/omega' : Item(status='U '),
7414 expected_status.tweak('', 'G', 'H', status=' M')
7415 expected_status.tweak('H/omega', status='M ')
7416 expected_disk = wc.State('', {
7417 '' : Item(props={SVN_PROP_MERGEINFO : '/A/D:4-9'}),
7418 'H' : Item(props={'prop:name' : 'propval',
7419 SVN_PROP_MERGEINFO : '/A/D/H:4-8,9*'}),
7420 'H/chi' : Item("This is the file 'chi'.\n"),
7421 'H/omega' : Item("New content"),
7422 'H/psi' : Item("This is the file 'psi'.\n"),
7423 'G' : Item(props={SVN_PROP_MERGEINFO : '/A/D/G:4-8,9*'}),
7424 'G/pi' : Item("This is the file 'pi'.\n"),
7425 'G/rho' : Item("New content"),
7426 'G/tau' : Item("This is the file 'tau'.\n"),
7427 'gamma' : Item("This is the file 'gamma'.\n"),
7429 expected_skip = wc.State(short_D_COPY_path, { })
7430 os.chdir(svntest.main.work_dir)
7431 svntest.actions.run_and_verify_merge(short_D_COPY_path, '3', '8',
7432 sbox.repo_url + '/A/D',
7433 expected_output, expected_disk,
7434 expected_status, expected_skip,
7435 None, None, None, None, None, 1, 1)
7436 os.chdir(saved_cwd)
7438 # Test for issue #2827
7439 # Handle merge info for sparsely-populated directories
7440 def merge_to_sparse_directories(sbox):
7441 "merge to sparse directories"
7443 # Merges into sparse working copies should set non-inheritable mergeinfo
7444 # on the deepest directories present in the WC.
7446 sbox.build()
7447 wc_dir = sbox.wc_dir
7448 wc_disk, wc_status = set_up_branch(sbox, False, 1)
7450 # Some paths we'll care about
7451 A_path = os.path.join(wc_dir, "A")
7452 D_path = os.path.join(wc_dir, "A", "D")
7453 I_path = os.path.join(wc_dir, "A", "C", "I")
7454 G_path = os.path.join(wc_dir, "A", "D", "G")
7455 A_COPY_path = os.path.join(wc_dir, "A_COPY")
7457 # Make a few more changes to the merge source...
7459 # r7 - modify and commit A/mu
7460 svntest.main.file_write(os.path.join(wc_dir, "A", "mu"),
7461 "New content")
7462 expected_output = wc.State(wc_dir, {'A/mu' : Item(verb='Sending')})
7463 wc_status.tweak('A/mu', wc_rev=7)
7464 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
7465 wc_status, None, wc_dir)
7466 wc_disk.tweak('A/mu', contents="New content")
7468 # r8 - Add a prop to A/D and commit.
7469 svntest.actions.run_and_verify_svn(None, ["At revision 7.\n"], [],
7470 'up', wc_dir)
7471 svntest.actions.run_and_verify_svn(None,
7472 ["property 'prop:name' set on '" +
7473 D_path + "'\n"], [], 'ps',
7474 'prop:name', 'propval', D_path)
7475 expected_output = svntest.wc.State(wc_dir, {
7476 'A/D' : Item(verb='Sending'),
7478 wc_status.tweak(wc_rev=7)
7479 wc_status.tweak('A/D', wc_rev=8)
7480 svntest.actions.run_and_verify_commit(wc_dir, expected_output, wc_status,
7481 None, wc_dir)
7483 # r9 - Add a prop to A and commit.
7484 svntest.actions.run_and_verify_svn(None, ["At revision 8.\n"], [],
7485 'up', wc_dir)
7486 svntest.actions.run_and_verify_svn(None,
7487 ["property 'prop:name' set on '" +
7488 A_path + "'\n"], [], 'ps',
7489 'prop:name', 'propval', A_path)
7490 expected_output = svntest.wc.State(wc_dir, {
7491 'A' : Item(verb='Sending'),
7493 wc_status.tweak(wc_rev=8)
7494 wc_status.tweak('A', wc_rev=9)
7495 svntest.actions.run_and_verify_commit(wc_dir, expected_output, wc_status,
7496 None, wc_dir)
7498 # Do an --immediates checkout of A_COPY
7499 immediates_dir = sbox.add_wc_path('immediates')
7500 expected_output = wc.State(immediates_dir, {
7501 'B' : Item(status='A '),
7502 'mu' : Item(status='A '),
7503 'C' : Item(status='A '),
7504 'D' : Item(status='A '),
7506 expected_disk = wc.State('', {
7507 'B' : Item(),
7508 'mu' : Item("This is the file 'mu'.\n"),
7509 'C' : Item(),
7510 'D' : Item(),
7512 svntest.actions.run_and_verify_checkout(sbox.repo_url + "/A_COPY",
7513 immediates_dir,
7514 expected_output, expected_disk,
7515 None, None, None, None,
7516 "--depth", "immediates")
7518 # Merge r4:9 into the immediates WC.
7519 # The root of the immediates WC should get inheritable r4:9 as should
7520 # the one file present 'mu'. The three directory children present, 'B',
7521 # 'C', and 'D' are checked out at depth empty, so get non-inheritable
7522 # mergeinfo for r4:9. The root and 'D' do should also get the changes
7523 # that affect them directly (the prop adds from r8 and r9). Any changes
7524 # deeper than the immediates should be skipped.
7526 # Search for the comment entitled "The Merge Kluge" elsewhere in
7527 # this file, to understand why we shorten and chdir() below.
7528 short_immediates_dir = shorten_path_kludge(immediates_dir)
7529 expected_output = wc.State(short_immediates_dir, {
7530 'D' : Item(status=' U'),
7531 'mu' : Item(status='U '),
7532 '' : Item(status=' U'),
7534 expected_status = wc.State(short_immediates_dir, {
7535 '' : Item(status=' M', wc_rev=9),
7536 'B' : Item(status=' M', wc_rev=9),
7537 'mu' : Item(status='M ', wc_rev=9),
7538 'C' : Item(status=' M', wc_rev=9),
7539 'D' : Item(status=' M', wc_rev=9),
7541 expected_disk = wc.State('', {
7542 '' : Item(props={SVN_PROP_MERGEINFO : '/A:5-9',
7543 "prop:name" : "propval"}),
7544 'B' : Item(props={SVN_PROP_MERGEINFO : '/A/B:5-9*'}),
7545 'mu' : Item("New content"),
7546 'C' : Item(props={SVN_PROP_MERGEINFO : '/A/C:5-9*'}),
7547 'D' : Item(props={SVN_PROP_MERGEINFO : '/A/D:5-9*',
7548 "prop:name" : "propval"}),
7550 expected_skip = wc.State(short_immediates_dir, {})
7551 saved_cwd = os.getcwd()
7552 os.chdir(svntest.main.work_dir)
7553 svntest.actions.run_and_verify_merge(short_immediates_dir, '4', '9',
7554 sbox.repo_url + \
7555 '/A',
7556 expected_output,
7557 expected_disk,
7558 expected_status,
7559 expected_skip,
7560 None, None, None, None,
7561 None, 1)
7562 os.chdir(saved_cwd)
7564 # Do a --files checkout of A_COPY
7565 files_dir = sbox.add_wc_path('files')
7566 expected_output = wc.State(files_dir, {
7567 'mu' : Item(status='A '),
7569 expected_disk = wc.State('', {
7570 'mu' : Item("This is the file 'mu'.\n"),
7572 svntest.actions.run_and_verify_checkout(sbox.repo_url + "/A_COPY",
7573 files_dir,
7574 expected_output, expected_disk,
7575 None, None, None, None,
7576 "--depth", "files")
7578 # Merge r4:9 into the files WC.
7579 # The root of the files WC should get non-inheritable r4:9 and its one
7580 # present child 'mu' should get the same but inheritable. The root
7581 # should also get the change that affects it directly (the prop add
7582 # from r9).
7583 short_files_dir = shorten_path_kludge(files_dir)
7584 expected_output = wc.State(short_files_dir, {
7585 'mu' : Item(status='U '),
7586 '' : Item(status=' U'),
7588 expected_status = wc.State(short_files_dir, {
7589 '' : Item(status=' M', wc_rev=9),
7590 'mu' : Item(status='MM', wc_rev=9),
7592 expected_disk = wc.State('', {
7593 '' : Item(props={SVN_PROP_MERGEINFO : '/A:5-9*',
7594 "prop:name" : "propval"}),
7595 'mu' : Item("New content",
7596 props={SVN_PROP_MERGEINFO : '/A/mu:5-9'}),
7598 expected_skip = wc.State(short_files_dir, {})
7599 os.chdir(svntest.main.work_dir)
7600 svntest.actions.run_and_verify_merge(short_files_dir, '4', '9',
7601 sbox.repo_url + \
7602 '/A',
7603 expected_output,
7604 expected_disk,
7605 expected_status,
7606 expected_skip,
7607 None, None, None, None,
7608 None, 1)
7609 os.chdir(saved_cwd)
7611 # Do an --empty checkout of A_COPY
7612 empty_dir = sbox.add_wc_path('empty')
7613 expected_output = wc.State(empty_dir, {})
7614 expected_disk = wc.State('', {})
7615 svntest.actions.run_and_verify_checkout(sbox.repo_url + "/A_COPY",
7616 empty_dir,
7617 expected_output, expected_disk,
7618 None, None, None, None,
7619 "--depth", "empty")
7621 # Merge r4:9 into the empty WC.
7622 # The root of the files WC should get non-inheritable r4:9 and also get
7623 # the one change that affects it directly (the prop add from r9).
7624 short_empty_dir = shorten_path_kludge(empty_dir)
7625 expected_output = wc.State(short_empty_dir, {
7626 '' : Item(status=' U'),
7628 expected_status = wc.State(short_empty_dir, {
7629 '' : Item(status=' M', wc_rev=9),
7631 expected_disk = wc.State('', {
7632 '' : Item(props={SVN_PROP_MERGEINFO : '/A:5-9*',
7633 "prop:name" : "propval"}),
7635 expected_skip = wc.State(short_empty_dir, {})
7636 os.chdir(svntest.main.work_dir)
7637 svntest.actions.run_and_verify_merge(short_empty_dir, '4', '9',
7638 sbox.repo_url + \
7639 '/A',
7640 expected_output,
7641 expected_disk,
7642 expected_status,
7643 expected_skip,
7644 None, None, None, None,
7645 None, 1)
7646 os.chdir(saved_cwd)
7648 def merge_old_and_new_revs_from_renamed_dir(sbox):
7649 "merge -rold(before rename):head renamed dir"
7651 ## See http://svn.haxx.se/dev/archive-2007-09/0706.shtml ##
7653 # Create a WC with a single branch
7654 sbox.build()
7655 wc_dir = sbox.wc_dir
7656 wc_disk, wc_status = set_up_branch(sbox, True, 1)
7658 # Some paths we'll care about
7659 A_url = sbox.repo_url + '/A'
7660 A_MOVED_url = sbox.repo_url + '/A_MOVED'
7661 A_COPY_path = os.path.join(wc_dir, 'A_COPY')
7662 mu_path = os.path.join(wc_dir, 'A', 'mu')
7663 A_MOVED_mu_path = os.path.join(wc_dir, 'A_MOVED', 'mu')
7665 # Make a modification to A/mu
7666 svntest.main.file_write(mu_path, "This is the file 'mu' modified.\n")
7667 expected_output = wc.State(wc_dir, {'A/mu' : Item(verb='Sending')})
7668 wc_status.add({'A/mu' : Item(status=' ', wc_rev=3)})
7669 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
7670 wc_status, None, wc_dir)
7672 # Move A to A_MOVED
7673 svntest.actions.run_and_verify_svn(None, ['\n', 'Committed revision 4.\n'],
7674 [], 'mv', '-m', 'mv A to A_MOVED',
7675 A_url, A_MOVED_url)
7677 # Update the working copy to get A_MOVED
7678 svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
7680 # Make a modification to A_MOVED/mu
7681 svntest.main.file_write(A_MOVED_mu_path, "This is 'mu' in A_MOVED.\n")
7682 expected_output = wc.State(wc_dir, {'A_MOVED/mu' : Item(verb='Sending')})
7683 expected_status = svntest.actions.get_virginal_state(wc_dir, 4)
7684 expected_status.remove('A', 'A/mu', 'A/C', 'A/D', 'A/B', 'A/B/lambda',
7685 'A/B/E', 'A/B/E/alpha', 'A/B/E/beta', 'A/B/F',
7686 'A/D/gamma', 'A/D/G', 'A/D/G/pi', 'A/D/G/rho',
7687 'A/D/G/tau', 'A/D/H', 'A/D/H/chi', 'A/D/H/omega',
7688 'A/D/H/psi')
7689 expected_status.add({
7690 '' : Item(status=' ', wc_rev=4),
7691 'iota' : Item(status=' ', wc_rev=4),
7692 'A_MOVED' : Item(status=' ', wc_rev=4),
7693 'A_MOVED/mu' : Item(status=' ', wc_rev=5),
7694 'A_MOVED/C' : Item(status=' ', wc_rev=4),
7695 'A_MOVED/D' : Item(status=' ', wc_rev=4),
7696 'A_MOVED/B' : Item(status=' ', wc_rev=4),
7697 'A_MOVED/B/lambda' : Item(status=' ', wc_rev=4),
7698 'A_MOVED/B/E' : Item(status=' ', wc_rev=4),
7699 'A_MOVED/B/E/alpha': Item(status=' ', wc_rev=4),
7700 'A_MOVED/B/E/beta' : Item(status=' ', wc_rev=4),
7701 'A_MOVED/B/F' : Item(status=' ', wc_rev=4),
7702 'A_MOVED/D/gamma' : Item(status=' ', wc_rev=4),
7703 'A_MOVED/D/G' : Item(status=' ', wc_rev=4),
7704 'A_MOVED/D/G/pi' : Item(status=' ', wc_rev=4),
7705 'A_MOVED/D/G/rho' : Item(status=' ', wc_rev=4),
7706 'A_MOVED/D/G/tau' : Item(status=' ', wc_rev=4),
7707 'A_MOVED/D/H' : Item(status=' ', wc_rev=4),
7708 'A_MOVED/D/H/chi' : Item(status=' ', wc_rev=4),
7709 'A_MOVED/D/H/omega': Item(status=' ', wc_rev=4),
7710 'A_MOVED/D/H/psi' : Item(status=' ', wc_rev=4),
7711 'A_COPY' : Item(status=' ', wc_rev=4),
7712 'A_COPY/mu' : Item(status=' ', wc_rev=4),
7713 'A_COPY/C' : Item(status=' ', wc_rev=4),
7714 'A_COPY/D' : Item(status=' ', wc_rev=4),
7715 'A_COPY/B' : Item(status=' ', wc_rev=4),
7716 'A_COPY/B/lambda' : Item(status=' ', wc_rev=4),
7717 'A_COPY/B/E' : Item(status=' ', wc_rev=4),
7718 'A_COPY/B/E/alpha' : Item(status=' ', wc_rev=4),
7719 'A_COPY/B/E/beta' : Item(status=' ', wc_rev=4),
7720 'A_COPY/B/F' : Item(status=' ', wc_rev=4),
7721 'A_COPY/D/gamma' : Item(status=' ', wc_rev=4),
7722 'A_COPY/D/G' : Item(status=' ', wc_rev=4),
7723 'A_COPY/D/G/pi' : Item(status=' ', wc_rev=4),
7724 'A_COPY/D/G/rho' : Item(status=' ', wc_rev=4),
7725 'A_COPY/D/G/tau' : Item(status=' ', wc_rev=4),
7726 'A_COPY/D/H' : Item(status=' ', wc_rev=4),
7727 'A_COPY/D/H/chi' : Item(status=' ', wc_rev=4),
7728 'A_COPY/D/H/omega' : Item(status=' ', wc_rev=4),
7729 'A_COPY/D/H/psi' : Item(status=' ', wc_rev=4),
7731 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
7732 expected_status, None, wc_dir)
7734 short_A_COPY = shorten_path_kludge(A_COPY_path)
7735 saved_cwd = os.getcwd()
7736 os.chdir(svntest.main.work_dir)
7738 # Merge /A_MOVED to /A_COPY - this happens in multiple passes
7739 # because /A_MOVED has renames in its history between the boundaries
7740 # of the requested merge range.
7741 expected_output = wc.State(short_A_COPY, {
7742 'mu' : Item(status='G '), # mu gets touched twice
7744 expected_status = wc.State(short_A_COPY, {
7745 '' : Item(status=' M', wc_rev=4),
7746 'mu' : Item(status='M ', wc_rev=4),
7747 'C' : Item(status=' ', wc_rev=4),
7748 'D' : Item(status=' ', wc_rev=4),
7749 'B' : Item(status=' ', wc_rev=4),
7750 'B/lambda' : Item(status=' ', wc_rev=4),
7751 'B/E' : Item(status=' ', wc_rev=4),
7752 'B/E/alpha': Item(status=' ', wc_rev=4),
7753 'B/E/beta' : Item(status=' ', wc_rev=4),
7754 'B/F' : Item(status=' ', wc_rev=4),
7755 'D/gamma' : Item(status=' ', wc_rev=4),
7756 'D/G' : Item(status=' ', wc_rev=4),
7757 'D/G/pi' : Item(status=' ', wc_rev=4),
7758 'D/G/rho' : Item(status=' ', wc_rev=4),
7759 'D/G/tau' : Item(status=' ', wc_rev=4),
7760 'D/H' : Item(status=' ', wc_rev=4),
7761 'D/H/chi' : Item(status=' ', wc_rev=4),
7762 'D/H/omega': Item(status=' ', wc_rev=4),
7763 'D/H/psi' : Item(status=' ', wc_rev=4),
7765 expected_disk = wc.State('', {
7766 '' : Item(props={SVN_PROP_MERGEINFO : '/A:3\n/A_MOVED:4-5\n'}),
7767 'mu' : Item("This is 'mu' in A_MOVED.\n"),
7768 'C' : Item(),
7769 'D' : Item(),
7770 'B' : Item(),
7771 'B/lambda' : Item("This is the file 'lambda'.\n"),
7772 'B/E' : Item(),
7773 'B/E/alpha': Item("This is the file 'alpha'.\n"),
7774 'B/E/beta' : Item("This is the file 'beta'.\n"),
7775 'B/F' : Item(),
7776 'D/gamma' : Item("This is the file 'gamma'.\n"),
7777 'D/G' : Item(),
7778 'D/G/pi' : Item("This is the file 'pi'.\n"),
7779 'D/G/rho' : Item("This is the file 'rho'.\n"),
7780 'D/G/tau' : Item("This is the file 'tau'.\n"),
7781 'D/H' : Item(),
7782 'D/H/chi' : Item("This is the file 'chi'.\n"),
7783 'D/H/omega': Item("This is the file 'omega'.\n"),
7784 'D/H/psi' : Item("This is the file 'psi'.\n"),
7786 expected_skip = wc.State(short_A_COPY, {})
7788 ### Disabling dry_run mode because currently it can't handle the way
7789 ### 'mu' gets textually modified in multiple passes.
7790 svntest.actions.run_and_verify_merge(short_A_COPY, '2', '5',
7791 A_MOVED_url,
7792 expected_output,
7793 expected_disk,
7794 expected_status,
7795 expected_skip,
7796 None, None, None, None, None,
7797 True, False)
7798 os.chdir(saved_cwd)
7800 def merge_with_child_having_different_rev_ranges_to_merge(sbox):
7801 "child having different rev ranges to merge"
7802 #Modify A/mu to 30 lines with a content 'line1'...'line30' commit it at r2.
7803 #Create a branch A_COPY from A, commit it at r3.
7804 #Modify A/mu line number 7 to 'LINE7' modify and commit at r4.
7805 #Modify A/mu line number 17 to 'LINE17' modify, set prop 'prop1' on 'A'
7806 #with a value 'val1' and commit at r5.
7807 #Modify A/mu line number 27 to 'LINE27' modify and commit at r6.
7808 #Merge r5 to 'A/mu' as a single file merge explicitly to 'A_COPY/mu'.
7809 #Merge r3:6 from 'A' to 'A_COPY
7810 #This should merge r4 and then r5 through r6.
7811 #Revert r5 and r6 via single file merge on A_COPY/mu.
7812 #Revert r6 through r4 on A_COPY this should get back us the pristine copy.
7813 #Merge r3:6 from 'A' to 'A_COPY
7814 #Revert r5 on A_COPY/mu
7815 #Modify line number 17 with 'some other line17' of A_COPY/mu
7816 #Merge r6:3 from 'A' to 'A_COPY, This should leave line number 17
7817 #undisturbed in A_COPY/mu, rest should be reverted.
7819 # Create a WC
7820 sbox.build()
7821 wc_dir = sbox.wc_dir
7822 A_path = os.path.join(wc_dir, 'A')
7823 mu_path = os.path.join(wc_dir, 'A', 'mu')
7824 A_url = sbox.repo_url + '/A'
7825 A_mu_url = sbox.repo_url + '/A/mu'
7826 A_COPY_url = sbox.repo_url + '/A_COPY'
7827 A_COPY_path = os.path.join(wc_dir, 'A_COPY')
7828 A_COPY_mu_path = os.path.join(wc_dir, 'A_COPY', 'mu')
7829 thirty_line_dummy_text = 'line1\n'
7830 for i in range(2, 31):
7831 thirty_line_dummy_text += 'line' + str(i) + '\n'
7833 svntest.main.file_write(mu_path, thirty_line_dummy_text)
7834 expected_output = wc.State(wc_dir, {'A/mu' : Item(verb='Sending')})
7835 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
7836 expected_status.tweak('A/mu', wc_rev=2)
7837 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
7838 expected_status, None, wc_dir)
7839 svntest.actions.run_and_verify_svn(None, None, [],
7840 'cp', A_url, A_COPY_url, '-m', 'rev 3')
7841 # Update the working copy to get A_COPY
7842 svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
7843 expected_status.add({'A_COPY' : Item(status=' '),
7844 'A_COPY/mu' : Item(status=' '),
7845 'A_COPY/C' : Item(status=' '),
7846 'A_COPY/D' : Item(status=' '),
7847 'A_COPY/B' : Item(status=' '),
7848 'A_COPY/B/lambda' : Item(status=' '),
7849 'A_COPY/B/E' : Item(status=' '),
7850 'A_COPY/B/E/alpha' : Item(status=' '),
7851 'A_COPY/B/E/beta' : Item(status=' '),
7852 'A_COPY/B/F' : Item(status=' '),
7853 'A_COPY/D/gamma' : Item(status=' '),
7854 'A_COPY/D/G' : Item(status=' '),
7855 'A_COPY/D/G/pi' : Item(status=' '),
7856 'A_COPY/D/G/rho' : Item(status=' '),
7857 'A_COPY/D/G/tau' : Item(status=' '),
7858 'A_COPY/D/H' : Item(status=' '),
7859 'A_COPY/D/H/chi' : Item(status=' '),
7860 'A_COPY/D/H/omega' : Item(status=' '),
7861 'A_COPY/D/H/psi' : Item(status=' ')})
7862 expected_status.tweak(wc_rev=3)
7863 tweaked_7th_line = thirty_line_dummy_text.replace('line7', 'LINE 7')
7864 svntest.main.file_write(mu_path, tweaked_7th_line)
7865 expected_status.tweak('A/mu', wc_rev=4)
7866 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
7867 expected_status, None, wc_dir)
7868 svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
7869 expected_status.tweak(wc_rev=4)
7870 tweaked_17th_line = tweaked_7th_line.replace('line17', 'LINE 17')
7871 svntest.main.file_write(mu_path, tweaked_17th_line)
7872 svntest.main.run_svn(None, 'propset', 'prop1', 'val1', A_path)
7873 expected_output = wc.State(wc_dir,
7875 'A' : Item(verb='Sending'),
7876 'A/mu' : Item(verb='Sending')
7879 expected_status.tweak('A', wc_rev=5)
7880 expected_status.tweak('A/mu', wc_rev=5)
7881 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
7882 expected_status, None, wc_dir)
7883 tweaked_27th_line = tweaked_17th_line.replace('line27', 'LINE 27')
7884 svntest.main.file_write(mu_path, tweaked_27th_line)
7885 expected_status.tweak('A/mu', wc_rev=6)
7886 expected_output = wc.State(wc_dir, {'A/mu' : Item(verb='Sending')})
7887 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
7888 expected_status, None, wc_dir)
7889 # Merge r5 to A_COPY/mu
7890 svntest.actions.run_and_verify_svn(None,
7891 expected_merge_output([[5]],
7892 ['U ' + A_COPY_mu_path + '\n']),
7894 'merge', '-r4:5',
7895 A_mu_url,
7896 A_COPY_mu_path)
7898 short_A_COPY = shorten_path_kludge(A_COPY_path)
7899 saved_cwd = os.getcwd()
7900 os.chdir(svntest.main.work_dir)
7901 expected_skip = wc.State(short_A_COPY, {})
7902 expected_output = wc.State(short_A_COPY, {
7903 '' : Item(status=' U'),
7904 'mu' : Item(status='G '),
7906 expected_status = wc.State(short_A_COPY, {
7907 '' : Item(status=' M', wc_rev=4),
7908 'mu' : Item(status='M ', wc_rev=4),
7909 'C' : Item(status=' ', wc_rev=4),
7910 'D' : Item(status=' ', wc_rev=4),
7911 'B' : Item(status=' ', wc_rev=4),
7912 'B/lambda' : Item(status=' ', wc_rev=4),
7913 'B/E' : Item(status=' ', wc_rev=4),
7914 'B/E/alpha': Item(status=' ', wc_rev=4),
7915 'B/E/beta' : Item(status=' ', wc_rev=4),
7916 'B/F' : Item(status=' ', wc_rev=4),
7917 'D/gamma' : Item(status=' ', wc_rev=4),
7918 'D/G' : Item(status=' ', wc_rev=4),
7919 'D/G/pi' : Item(status=' ', wc_rev=4),
7920 'D/G/rho' : Item(status=' ', wc_rev=4),
7921 'D/G/tau' : Item(status=' ', wc_rev=4),
7922 'D/H' : Item(status=' ', wc_rev=4),
7923 'D/H/chi' : Item(status=' ', wc_rev=4),
7924 'D/H/omega': Item(status=' ', wc_rev=4),
7925 'D/H/psi' : Item(status=' ', wc_rev=4),
7927 expected_disk = wc.State('', {
7928 '' : Item(props={SVN_PROP_MERGEINFO : '/A:4-6',
7929 'prop1' : 'val1'}),
7930 'mu' : Item(tweaked_27th_line),
7931 'C' : Item(),
7932 'D' : Item(),
7933 'B' : Item(),
7934 'B/lambda' : Item("This is the file 'lambda'.\n"),
7935 'B/E' : Item(),
7936 'B/E/alpha': Item("This is the file 'alpha'.\n"),
7937 'B/E/beta' : Item("This is the file 'beta'.\n"),
7938 'B/F' : Item(),
7939 'D/gamma' : Item("This is the file 'gamma'.\n"),
7940 'D/G' : Item(),
7941 'D/G/pi' : Item("This is the file 'pi'.\n"),
7942 'D/G/rho' : Item("This is the file 'rho'.\n"),
7943 'D/G/tau' : Item("This is the file 'tau'.\n"),
7944 'D/H' : Item(),
7945 'D/H/chi' : Item("This is the file 'chi'.\n"),
7946 'D/H/omega': Item("This is the file 'omega'.\n"),
7947 'D/H/psi' : Item("This is the file 'psi'.\n"),
7949 svntest.actions.run_and_verify_merge(short_A_COPY, '3', '6',
7950 A_url,
7951 expected_output,
7952 expected_disk,
7953 expected_status,
7954 expected_skip,
7955 None, None, None, None, None, 1)
7956 os.chdir(saved_cwd)
7957 # Revert r5 and r6 on A_COPY/mu
7958 svntest.actions.run_and_verify_svn(None,
7959 expected_merge_output([[6,5]],
7960 ['G ' + A_COPY_mu_path + '\n']),
7962 'merge', '-r6:4',
7963 A_mu_url,
7964 A_COPY_mu_path)
7966 expected_output = wc.State(short_A_COPY, {
7967 '' : Item(status=' G'), # merged removal of prop1 property
7968 'mu' : Item(status='G '), # merged reversion of text changes
7971 expected_status.tweak('', status=' ')
7972 expected_status.tweak('mu', status=' ')
7973 expected_disk.tweak('', props={})
7974 expected_disk.remove('')
7975 expected_disk.tweak('mu', contents=thirty_line_dummy_text)
7976 os.chdir(svntest.main.work_dir)
7977 svntest.actions.run_and_verify_merge(short_A_COPY, '6', '3',
7978 A_url,
7979 expected_output,
7980 expected_disk,
7981 expected_status,
7982 expected_skip,
7983 None, None, None, None, None, 1)
7984 os.chdir(saved_cwd)
7986 expected_disk.add({'' : Item(props={SVN_PROP_MERGEINFO : '/A:4-6',
7987 'prop1' : 'val1'})})
7988 expected_disk.tweak('mu', contents=tweaked_27th_line)
7989 expected_output = wc.State(short_A_COPY, {
7990 '' : Item(status=' U'), # new mergeinfo and prop1 property
7991 'mu' : Item(status='U '), # text changes
7993 expected_status.tweak('', status=' M')
7994 expected_status.tweak('mu', status='M ')
7995 os.chdir(svntest.main.work_dir)
7996 svntest.actions.run_and_verify_merge(short_A_COPY, '3', '6',
7997 A_url,
7998 expected_output,
7999 expected_disk,
8000 expected_status,
8001 expected_skip,
8002 None, None, None, None, None, 1)
8003 os.chdir(saved_cwd)
8004 #Revert r5 on A_COPY/mu
8005 svntest.actions.run_and_verify_svn(None,
8006 expected_merge_output([[-5]],
8007 ['G ' + A_COPY_mu_path + '\n']),
8009 'merge', '-r5:4',
8010 A_mu_url,
8011 A_COPY_mu_path)
8012 tweaked_17th_line_1 = tweaked_27th_line.replace('LINE 17',
8013 'some other line17')
8014 tweaked_17th_line_2 = thirty_line_dummy_text.replace('line17',
8015 'some other line17')
8016 svntest.main.file_write(A_COPY_mu_path, tweaked_17th_line_1)
8017 expected_output = wc.State(short_A_COPY, {
8018 '' : Item(status=' G'),
8019 'mu' : Item(status='G '),
8021 expected_status.tweak('', status=' ')
8022 expected_status.tweak('mu', status='M ')
8023 expected_disk.remove('')
8024 expected_disk.tweak('mu', contents=tweaked_17th_line_2)
8025 os.chdir(svntest.main.work_dir)
8026 svntest.actions.run_and_verify_merge(short_A_COPY, '6', '3',
8027 A_url,
8028 expected_output,
8029 expected_disk,
8030 expected_status,
8031 expected_skip,
8032 None, None, None, None, None, 1)
8033 os.chdir(saved_cwd)
8035 def merge_old_and_new_revs_from_renamed_file(sbox):
8036 "merge -rold(before rename):head renamed file"
8038 ## See http://svn.haxx.se/dev/archive-2007-09/0706.shtml ##
8040 # Create a WC
8041 sbox.build()
8042 wc_dir = sbox.wc_dir
8044 # Some paths we'll care about
8045 mu_url = sbox.repo_url + '/A/mu'
8046 mu_MOVED_url = sbox.repo_url + '/A/mu_MOVED'
8047 mu_COPY_url = sbox.repo_url + '/A/mu_COPY'
8048 mu_COPY_path = os.path.join(wc_dir, 'A', 'mu_COPY')
8049 mu_path = os.path.join(wc_dir, 'A', 'mu')
8050 mu_MOVED_path = os.path.join(wc_dir, 'A', 'mu_MOVED')
8052 # Copy mu to mu_COPY
8053 svntest.actions.run_and_verify_svn(None, ['\n', 'Committed revision 2.\n'],
8054 [], 'cp', '-m', 'cp mu to mu_COPY',
8055 mu_url, mu_COPY_url)
8057 # Make a modification to A/mu
8058 svntest.main.file_write(mu_path, "This is the file 'mu' modified.\n")
8059 expected_output = wc.State(wc_dir, {'A/mu' : Item(verb='Sending')})
8060 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
8061 expected_status.tweak('A/mu', wc_rev=3)
8062 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
8063 expected_status, None, wc_dir)
8065 # Move mu to mu_MOVED
8066 svntest.actions.run_and_verify_svn(None, ['\n', 'Committed revision 4.\n'],
8067 [], 'mv', '-m', 'mv mu to mu_MOVED',
8068 mu_url, mu_MOVED_url)
8070 # Update the working copy to get mu_MOVED
8071 svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
8073 # Make a modification to mu_MOVED
8074 svntest.main.file_write(mu_MOVED_path, "This is 'mu' in mu_MOVED.\n")
8075 expected_output = wc.State(wc_dir, {'A/mu_MOVED' : Item(verb='Sending')})
8076 expected_status = svntest.actions.get_virginal_state(wc_dir, 4)
8077 expected_status.remove('A/mu')
8078 expected_status.add({
8079 'A/mu_MOVED' : Item(status=' ', wc_rev=5),
8080 'A/mu_COPY' : Item(status=' ', wc_rev=4),
8082 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
8083 expected_status, None, wc_dir)
8085 # Merge A/mu_MOVED to A/mu_COPY - this happens in multiple passes
8086 # because A/mu_MOVED has renames in its history between the
8087 # boundaries of the requested merge range.
8088 expected_output = expected_merge_output([[2,3],[4,5]],
8089 ['U %s\n' % (mu_COPY_path),
8090 'G %s\n' % (mu_COPY_path)])
8091 svntest.actions.run_and_verify_svn(None, expected_output,
8092 [], 'merge', '-r', '1:5',
8093 mu_MOVED_url,
8094 mu_COPY_path)
8095 svntest.actions.run_and_verify_svn(None, ['/A/mu:2-3\n',
8096 '/A/mu_MOVED:4-5\n'],
8097 [], 'propget', SVN_PROP_MERGEINFO,
8098 mu_COPY_path)
8101 def merge_with_auto_rev_range_detection(sbox):
8102 "merge with auto detection of revision ranges"
8104 ## See http://svn.haxx.se/dev/archive-2007-09/0735.shtml ##
8106 # Create a WC
8107 sbox.build()
8108 wc_dir = sbox.wc_dir
8110 # Some paths we'll care about
8111 A_url = sbox.repo_url + '/A'
8112 A_COPY_url = sbox.repo_url + '/A_COPY'
8113 B1_path = os.path.join(wc_dir, 'A', 'B1')
8114 B1_mu_path = os.path.join(wc_dir, 'A', 'B1', 'mu')
8115 A_COPY_path = os.path.join(wc_dir, 'A_COPY')
8117 # Create B1 inside A
8118 svntest.actions.run_and_verify_svn(None, ["A " + B1_path + "\n"],
8119 [], 'mkdir',
8120 B1_path)
8122 # Add a file mu inside B1
8123 svntest.main.file_write(B1_mu_path, "This is the file 'mu'.\n")
8124 svntest.actions.run_and_verify_svn(None, ["A " + B1_mu_path + "\n"],
8125 [], 'add', B1_mu_path)
8127 # Commit B1 and B1/mu
8128 expected_output = wc.State(wc_dir, {
8129 'A/B1' : Item(verb='Adding'),
8130 'A/B1/mu' : Item(verb='Adding'),
8132 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
8133 expected_status.add({
8134 'A/B1' : Item(status=' ', wc_rev=2),
8135 'A/B1/mu' : Item(status=' ', wc_rev=2),
8137 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
8138 expected_status, None, wc_dir)
8140 # Copy A to A_COPY
8141 svntest.actions.run_and_verify_svn(None, ['\n', 'Committed revision 3.\n'],
8142 [], 'cp', '-m', 'cp A to A_COPY',
8143 A_url, A_COPY_url)
8145 # Make a modification to A/B1/mu
8146 svntest.main.file_write(B1_mu_path, "This is the file 'mu' modified.\n")
8147 expected_output = wc.State(wc_dir, {'A/B1/mu' : Item(verb='Sending')})
8148 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
8149 expected_status.add({
8150 'A/B1' : Item(status=' ', wc_rev=2),
8151 'A/B1/mu' : Item(status=' ', wc_rev=4),
8153 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
8154 expected_status, None, wc_dir)
8156 # Update the working copy to get A_COPY
8157 svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
8159 short_A_COPY = shorten_path_kludge(A_COPY_path)
8160 saved_cwd = os.getcwd()
8161 os.chdir(svntest.main.work_dir)
8163 # Merge /A to /A_COPY
8164 expected_output = wc.State(short_A_COPY, {
8165 'B1/mu' : Item(status='U '),
8167 expected_status = wc.State(short_A_COPY, {
8168 '' : Item(status=' M', wc_rev=4),
8169 'mu' : Item(status=' ', wc_rev=4),
8170 'C' : Item(status=' ', wc_rev=4),
8171 'D' : Item(status=' ', wc_rev=4),
8172 'B' : Item(status=' ', wc_rev=4),
8173 'B/lambda' : Item(status=' ', wc_rev=4),
8174 'B/E' : Item(status=' ', wc_rev=4),
8175 'B/E/alpha': Item(status=' ', wc_rev=4),
8176 'B/E/beta' : Item(status=' ', wc_rev=4),
8177 'B/F' : Item(status=' ', wc_rev=4),
8178 'B1' : Item(status=' ', wc_rev=4),
8179 'B1/mu' : Item(status='M ', wc_rev=4),
8180 'D/gamma' : Item(status=' ', wc_rev=4),
8181 'D/G' : Item(status=' ', wc_rev=4),
8182 'D/G/pi' : Item(status=' ', wc_rev=4),
8183 'D/G/rho' : Item(status=' ', wc_rev=4),
8184 'D/G/tau' : Item(status=' ', wc_rev=4),
8185 'D/H' : Item(status=' ', wc_rev=4),
8186 'D/H/chi' : Item(status=' ', wc_rev=4),
8187 'D/H/omega': Item(status=' ', wc_rev=4),
8188 'D/H/psi' : Item(status=' ', wc_rev=4),
8190 expected_disk = wc.State('', {
8191 '' : Item(props={SVN_PROP_MERGEINFO : '/A:3-4'}),
8192 'mu' : Item("This is the file 'mu'.\n"),
8193 'C' : Item(),
8194 'D' : Item(),
8195 'B' : Item(),
8196 'B/lambda' : Item("This is the file 'lambda'.\n"),
8197 'B/E' : Item(),
8198 'B/E/alpha': Item("This is the file 'alpha'.\n"),
8199 'B/E/beta' : Item("This is the file 'beta'.\n"),
8200 'B/F' : Item(),
8201 'B1' : Item(),
8202 'B1/mu' : Item("This is the file 'mu' modified.\n"),
8203 'D/gamma' : Item("This is the file 'gamma'.\n"),
8204 'D/G' : Item(),
8205 'D/G/pi' : Item("This is the file 'pi'.\n"),
8206 'D/G/rho' : Item("This is the file 'rho'.\n"),
8207 'D/G/tau' : Item("This is the file 'tau'.\n"),
8208 'D/H' : Item(),
8209 'D/H/chi' : Item("This is the file 'chi'.\n"),
8210 'D/H/omega': Item("This is the file 'omega'.\n"),
8211 'D/H/psi' : Item("This is the file 'psi'.\n"),
8213 expected_skip = wc.State(short_A_COPY, {})
8214 svntest.actions.run_and_verify_merge(short_A_COPY, None, None,
8215 A_url,
8216 expected_output,
8217 expected_disk,
8218 expected_status,
8219 expected_skip,
8220 None, None, None, None, None,
8221 1, 1)
8222 os.chdir(saved_cwd)
8224 def mergeinfo_recording_in_skipped_merge(sbox):
8225 "mergeinfo recording in skipped merge"
8227 ## See http://subversion.tigris.org/issues/show_bug.cgi?id=2829. ##
8229 # Create a WC with a single branch
8230 sbox.build()
8231 wc_dir = sbox.wc_dir
8232 wc_disk, wc_status = set_up_branch(sbox, True, 1)
8234 # Some paths we'll care about
8235 A_url = sbox.repo_url + '/A'
8236 A_COPY_path = os.path.join(wc_dir, 'A_COPY')
8237 mu_path = os.path.join(wc_dir, 'A', 'mu')
8238 alpha_path = os.path.join(wc_dir, 'A', 'B', 'E', 'alpha')
8239 A_COPY_B_E_path = os.path.join(wc_dir, 'A_COPY', 'B', 'E')
8240 A_COPY_alpha_path = os.path.join(wc_dir, 'A_COPY', 'B', 'E', 'alpha')
8241 A_COPY_beta_path = os.path.join(wc_dir, 'A_COPY', 'B', 'E', 'beta')
8243 # Make a modification to A/mu
8244 svntest.main.file_write(mu_path, "This is the file 'mu' modified.\n")
8245 expected_output = wc.State(wc_dir, {'A/mu' : Item(verb='Sending')})
8246 wc_status.add({'A/mu' : Item(status=' ', wc_rev=3)})
8247 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
8248 wc_status, None, wc_dir)
8250 # Make a modification to A/B/E/alpha
8251 svntest.main.file_write(alpha_path, "This is the file 'alpha' modified.\n")
8252 expected_output = wc.State(wc_dir, {'A/B/E/alpha' : Item(verb='Sending')})
8253 wc_status.add({'A/B/E/alpha' : Item(status=' ', wc_rev=4)})
8254 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
8255 wc_status, None, wc_dir)
8257 # Delete A_COPY/B/E
8258 svntest.actions.run_and_verify_svn(None, None, [], 'rm',
8259 A_COPY_B_E_path)
8261 short_A_COPY = shorten_path_kludge(A_COPY_path)
8262 saved_cwd = os.getcwd()
8263 os.chdir(svntest.main.work_dir)
8265 # Merge /A to /A_COPY ie., r1 to r4
8266 expected_output = wc.State(short_A_COPY, {
8267 'mu' : Item(status='U '),
8269 expected_status = wc.State(short_A_COPY, {
8270 '' : Item(status=' M', wc_rev=2),
8271 'mu' : Item(status='M ', wc_rev=2),
8272 'B' : Item(status=' M', wc_rev=2),
8273 'B/lambda' : Item(status=' M', wc_rev=2),
8274 'B/F' : Item(status=' M', wc_rev=2),
8275 'B/E' : Item(status='D ', wc_rev=2),
8276 'B/E/alpha': Item(status='D ', wc_rev=2),
8277 'B/E/beta' : Item(status='D ', wc_rev=2),
8278 'C' : Item(status=' ', wc_rev=2),
8279 'D' : Item(status=' ', wc_rev=2),
8280 'D/gamma' : Item(status=' ', wc_rev=2),
8281 'D/G' : Item(status=' ', wc_rev=2),
8282 'D/G/pi' : Item(status=' ', wc_rev=2),
8283 'D/G/rho' : Item(status=' ', wc_rev=2),
8284 'D/G/tau' : Item(status=' ', wc_rev=2),
8285 'D/H' : Item(status=' ', wc_rev=2),
8286 'D/H/chi' : Item(status=' ', wc_rev=2),
8287 'D/H/omega': Item(status=' ', wc_rev=2),
8288 'D/H/psi' : Item(status=' ', wc_rev=2),
8290 expected_disk = wc.State('', {
8291 '' : Item(props={SVN_PROP_MERGEINFO : '/A:2-4'}),
8292 'mu' : Item("This is the file 'mu' modified.\n"),
8293 'C' : Item(),
8294 'D' : Item(),
8295 'B' : Item(props={SVN_PROP_MERGEINFO : '/A/B:2-4*'}),
8296 'B/lambda' : Item(contents="This is the file 'lambda'.\n",
8297 props={SVN_PROP_MERGEINFO : '/A/B/lambda:2-4'}),
8298 'B/F' : Item(props={SVN_PROP_MERGEINFO : '/A/B/F:2-4'}),
8299 'B/E' : Item(),
8300 'D/gamma' : Item("This is the file 'gamma'.\n"),
8301 'D/G' : Item(),
8302 'D/G/pi' : Item("This is the file 'pi'.\n"),
8303 'D/G/rho' : Item("This is the file 'rho'.\n"),
8304 'D/G/tau' : Item("This is the file 'tau'.\n"),
8305 'D/H' : Item(),
8306 'D/H/chi' : Item("This is the file 'chi'.\n"),
8307 'D/H/omega': Item("This is the file 'omega'.\n"),
8308 'D/H/psi' : Item("This is the file 'psi'.\n"),
8310 expected_skip = wc.State(short_A_COPY, {
8311 'B/E/alpha' : Item(),
8313 svntest.actions.run_and_verify_merge(short_A_COPY, None, None,
8314 A_url,
8315 expected_output,
8316 expected_disk,
8317 expected_status,
8318 expected_skip,
8319 None, None, None, None, None,
8320 1, 1)
8322 os.chdir(saved_cwd)
8324 # Test for issue 2818: Provide a 'merge' API which allows for merging of
8325 # arbitrary revision ranges (e.g. '-c 3,5,7')
8326 def cherry_picking(sbox):
8327 "command line supports cherry picked merge ranges"
8329 sbox.build()
8330 wc_dir = sbox.wc_dir
8331 wc_disk, wc_status = set_up_branch(sbox)
8333 # Some paths we'll care about
8334 H_path = os.path.join(wc_dir, "A", "D", "H")
8335 G_path = os.path.join(wc_dir, "A", "D", "G")
8336 A_COPY_path = os.path.join(wc_dir, "A_COPY")
8337 D_COPY_path = os.path.join(wc_dir, "A_COPY", "D")
8338 G_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "G")
8339 H_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "H")
8340 rho_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "G", "rho")
8341 omega_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "H", "omega")
8342 psi_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "H", "psi")
8344 # Update working copy
8345 expected_output = svntest.wc.State(wc_dir, {})
8346 wc_status.tweak(wc_rev='6')
8347 svntest.actions.run_and_verify_update(wc_dir, expected_output,
8348 wc_disk, wc_status,
8349 None, None, None, None, None, True)
8351 # Make some prop changes to some dirs.
8352 svntest.actions.run_and_verify_svn(None,
8353 ["property 'prop:name' set on '" +
8354 G_path + "'\n"], [], 'ps',
8355 'prop:name', 'propval', G_path)
8356 expected_output = svntest.wc.State(wc_dir, {'A/D/G': Item(verb='Sending'),})
8357 wc_status.tweak('A/D/G', wc_rev=7)
8358 wc_disk.tweak('A/D/G', props={'prop:name' : 'propval'})
8360 svntest.actions.run_and_verify_commit(wc_dir, expected_output, wc_status,
8361 None, wc_dir)
8362 svntest.actions.run_and_verify_svn(None,
8363 ["property 'prop:name' set on '" +
8364 H_path + "'\n"], [], 'ps',
8365 'prop:name', 'propval', H_path)
8366 expected_output = svntest.wc.State(wc_dir, {'A/D/H': Item(verb='Sending'),})
8367 wc_status.tweak('A/D/H', wc_rev=8)
8368 wc_disk.tweak('A/D/H', props={'prop:name' : 'propval'})
8369 svntest.actions.run_and_verify_commit(wc_dir, expected_output, wc_status,
8370 None, wc_dir)
8372 # Do multiple additive merges to a file"
8373 # Merge -r2:4 -c6 into A_COPY/D/G/rho.
8374 short_rho_COPY_path = shorten_path_kludge(rho_COPY_path)
8375 expected_skip = wc.State(short_rho_COPY_path, { })
8376 saved_cwd = os.getcwd()
8377 os.chdir(svntest.main.work_dir)
8378 # run_and_verify_merge doesn't support merging to a file WCPATH
8379 # so use run_and_verify_svn.
8380 svntest.actions.run_and_verify_svn(None,
8381 expected_merge_output(
8382 [[3,4],[6]],
8383 'U ' + short_rho_COPY_path + '\n'),
8384 [], 'merge', '-r2:4', '-c6',
8385 sbox.repo_url + '/A/D/G/rho',
8386 short_rho_COPY_path)
8387 os.chdir(saved_cwd)
8389 # Check rho's status and props.
8390 expected_status = wc.State(rho_COPY_path,
8391 {'' : Item(status='MM', wc_rev=6)})
8392 svntest.actions.run_and_verify_status(rho_COPY_path, expected_status)
8393 svntest.actions.run_and_verify_svn(None, ["/A/D/G/rho:3-4,6\n"], [],
8394 'propget', SVN_PROP_MERGEINFO,
8395 rho_COPY_path)
8397 #Do multiple additive merges to a directory:
8398 # Merge -c6 -c8 into A_COPY/D/H
8399 short_H_COPY_path = shorten_path_kludge(H_COPY_path)
8400 short_omega_COPY_path = shorten_path_kludge(omega_COPY_path)
8401 expected_output = expected_merge_output(
8402 [[6],[8]],
8403 ['U ' + short_omega_COPY_path + '\n',
8404 ' U ' + short_H_COPY_path + '\n'])
8405 os.chdir(svntest.main.work_dir)
8406 svntest.actions.run_and_verify_svn(None, expected_output,
8407 [], 'merge', '-c6', '-c8',
8408 sbox.repo_url + '/A/D/H',
8409 short_H_COPY_path)
8410 os.chdir(saved_cwd)
8412 # Check A_COPY/D/H's status and props.
8413 expected_status = wc.State(H_COPY_path,
8414 {'' : Item(status=' M', wc_rev=6),
8415 'psi' : Item(status=' ', wc_rev=6),
8416 'chi' : Item(status=' ', wc_rev=6),
8417 'omega': Item(status='M ', wc_rev=6),})
8418 svntest.actions.run_and_verify_status(H_COPY_path, expected_status)
8419 svntest.actions.run_and_verify_svn(None,
8420 [H_COPY_path + " - /A/D/H:6,8\n"],
8421 [], 'propget', '-R', SVN_PROP_MERGEINFO,
8422 H_COPY_path)
8424 # Do multiple reverse merges to a directory:
8425 # Merge -c-6 -c-3 into A_COPY
8426 short_A_COPY_path = shorten_path_kludge(A_COPY_path)
8427 short_omega_COPY_path = shorten_path_kludge(omega_COPY_path)
8428 expected_output = expected_merge_output(
8429 [[-3],[-6]], 'G ' + short_omega_COPY_path + '\n')
8430 os.chdir(svntest.main.work_dir)
8431 svntest.actions.run_and_verify_svn(None, expected_output,
8432 [], 'merge', '-c-3', '-c-6',
8433 sbox.repo_url + '/A',
8434 short_A_COPY_path)
8435 os.chdir(saved_cwd)
8436 expected_status = wc.State(A_COPY_path,
8437 {'' : Item(status=' ', wc_rev=6),
8438 'B' : Item(status=' ', wc_rev=6),
8439 'B/lambda' : Item(status=' ', wc_rev=6),
8440 'B/E' : Item(status=' ', wc_rev=6),
8441 'B/E/alpha' : Item(status=' ', wc_rev=6),
8442 'B/E/beta' : Item(status=' ', wc_rev=6),
8443 'B/F' : Item(status=' ', wc_rev=6),
8444 'mu' : Item(status=' ', wc_rev=6),
8445 'C' : Item(status=' ', wc_rev=6),
8446 'D' : Item(status=' ', wc_rev=6),
8447 'D/gamma' : Item(status=' ', wc_rev=6),
8448 'D/G' : Item(status=' ', wc_rev=6),
8449 'D/G/pi' : Item(status=' ', wc_rev=6),
8450 'D/G/rho' : Item(status='MM', wc_rev=6),
8451 'D/G/tau' : Item(status=' ', wc_rev=6),
8452 'D/H' : Item(status=' M', wc_rev=6),
8453 'D/H/chi' : Item(status=' ', wc_rev=6),
8454 'D/H/psi' : Item(status=' ', wc_rev=6),
8455 'D/H/omega' : Item(status=' ', wc_rev=6),})
8456 svntest.actions.run_and_verify_status(A_COPY_path, expected_status)
8457 expected_out = H_COPY_path + " - /A/D/H:8\n|" + \
8458 rho_COPY_path + " - /A/D/G/rho:4\n"
8459 # Construct proper regex for '\' infested Windows paths.
8460 if sys.platform == 'win32':
8461 expected_out = expected_out.replace("\\", "\\\\")
8462 svntest.actions.run_and_verify_svn(None, expected_out, [],
8463 'propget', '-R', SVN_PROP_MERGEINFO,
8464 A_COPY_path)
8466 # Do both additive and reverse merges to a directory:
8467 # Merge -r2:3 -c-4 -r4:7 to A_COPY/D
8468 short_D_COPY_path = shorten_path_kludge(D_COPY_path)
8469 short_psi_COPY_path = shorten_path_kludge(psi_COPY_path)
8470 short_G_COPY_path = shorten_path_kludge(G_COPY_path)
8471 expected_output = expected_merge_output(
8472 [[3],[-4], [5,7]],
8473 [' U ' + short_G_COPY_path + '\n',
8474 'U ' + short_omega_COPY_path + '\n',
8475 'U ' + short_psi_COPY_path + '\n',
8476 'G ' + short_rho_COPY_path + '\n'])
8478 os.chdir(svntest.main.work_dir)
8479 svntest.actions.run_and_verify_svn(None, expected_output, [], 'merge',
8480 '-r2:3', '-c-4', '-r4:7',
8481 sbox.repo_url + '/A/D',
8482 short_D_COPY_path)
8483 os.chdir(saved_cwd)
8484 expected_status = wc.State(D_COPY_path,
8485 {'' : Item(status=' M', wc_rev=6),
8486 'gamma' : Item(status=' ', wc_rev=6),
8487 'G' : Item(status=' M', wc_rev=6),
8488 'G/pi' : Item(status=' ', wc_rev=6),
8489 'G/rho' : Item(status=' ', wc_rev=6),
8490 'G/tau' : Item(status=' ', wc_rev=6),
8491 'H' : Item(status=' M', wc_rev=6),
8492 'H/chi' : Item(status=' ', wc_rev=6),
8493 'H/psi' : Item(status='M ', wc_rev=6),
8494 'H/omega' : Item(status='M ', wc_rev=6),})
8495 svntest.actions.run_and_verify_status(D_COPY_path, expected_status)
8496 expected_out = D_COPY_path + " - /A/D:3,5-7\n|" + \
8497 H_COPY_path + " - /A/D/H:3,5-8\n"
8498 # Construct proper regex for '\' infested Windows paths.
8499 if sys.platform == 'win32':
8500 expected_out = expected_out.replace("\\", "\\\\")
8501 svntest.actions.run_and_verify_svn(None, expected_out, [],
8502 'propget', '-R', SVN_PROP_MERGEINFO,
8503 D_COPY_path)
8505 def propchange_of_subdir_raises_conflict(sbox):
8506 "merge of propchange on subdir raises conflict"
8508 ## See http://subversion.tigris.org/issues/show_bug.cgi?id=2969. ##
8510 # Create a WC with a single branch
8511 sbox.build()
8512 wc_dir = sbox.wc_dir
8513 wc_disk, wc_status = set_up_branch(sbox, True, 1)
8515 # Some paths we'll care about
8516 B_url = sbox.repo_url + '/A/B'
8517 E_path = os.path.join(wc_dir, 'A', 'B', 'E')
8518 lambda_path = os.path.join(wc_dir, 'A', 'B', 'lambda')
8519 A_COPY_B_path = os.path.join(wc_dir, 'A_COPY', 'B')
8520 A_COPY_B_E_path = os.path.join(wc_dir, 'A_COPY', 'B', 'E')
8521 A_COPY_lambda_path = os.path.join(wc_dir, 'A_COPY', 'B', 'E', 'lambda')
8523 # Set a property on A/B/E and Make a modification to A/B/lambda
8524 svntest.main.run_svn(None, 'propset', 'x', 'x', E_path)
8526 svntest.main.file_write(lambda_path, "This is the file 'lambda' modified.\n")
8527 expected_output = wc.State(wc_dir, {
8528 'A/B/lambda' : Item(verb='Sending'),
8529 'A/B/E' : Item(verb='Sending'),
8531 wc_status.add({
8532 'A/B/lambda' : Item(status=' ', wc_rev=3),
8533 'A/B/E' : Item(status=' ', wc_rev=3),
8535 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
8536 wc_status, None, wc_dir)
8538 short_A_COPY_B = shorten_path_kludge(A_COPY_B_path)
8539 saved_cwd = os.getcwd()
8540 os.chdir(svntest.main.work_dir)
8542 # Merge /A/B to /A_COPY/B ie., r1 to r3 with depth files
8543 expected_output = wc.State(short_A_COPY_B, {
8544 'lambda' : Item(status='U '),
8546 expected_disk = wc.State('', {
8547 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B:2-3*'}),
8548 'lambda' : Item(contents="This is the file 'lambda' modified.\n",
8549 props={SVN_PROP_MERGEINFO : '/A/B/lambda:2-3'}),
8550 'F' : Item(),
8551 'E' : Item(),
8552 'E/alpha' : Item(contents="This is the file 'alpha'.\n"),
8553 'E/beta' : Item(contents="This is the file 'beta'.\n"),
8555 expected_status = wc.State(short_A_COPY_B, {
8556 '' : Item(status=' M', wc_rev=2),
8557 'lambda' : Item(status='MM', wc_rev=2),
8558 'F' : Item(status=' ', wc_rev=2),
8559 'E' : Item(status=' ', wc_rev=2),
8560 'E/alpha' : Item(status=' ', wc_rev=2),
8561 'E/beta' : Item(status=' ', wc_rev=2),
8563 expected_skip = wc.State(short_A_COPY_B, {})
8565 svntest.actions.run_and_verify_merge(short_A_COPY_B, None, None,
8566 B_url,
8567 expected_output,
8568 expected_disk,
8569 expected_status,
8570 expected_skip,
8571 None, None, None, None, None,
8572 1, 1, '--depth', 'files')
8574 # Merge /A/B to /A_COPY/B ie., r1 to r3 with infinite depth
8575 expected_output = wc.State(short_A_COPY_B, {
8576 'E' : Item(status=' U'),
8578 expected_disk = wc.State('', {
8579 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B:2-3'}),
8580 'lambda' : Item(contents="This is the file 'lambda' modified.\n"),
8581 'F' : Item(),
8582 'E' : Item(props={'x': 'x'}),
8583 'E/alpha' : Item(contents="This is the file 'alpha'.\n"),
8584 'E/beta' : Item(contents="This is the file 'beta'.\n"),
8586 expected_status = wc.State(short_A_COPY_B, {
8587 '' : Item(status=' M', wc_rev=2),
8588 'lambda' : Item(status='M ', wc_rev=2),
8589 'F' : Item(status=' ', wc_rev=2),
8590 'E' : Item(status=' M', wc_rev=2),
8591 'E/alpha' : Item(status=' ', wc_rev=2),
8592 'E/beta' : Item(status=' ', wc_rev=2),
8595 svntest.actions.run_and_verify_merge(short_A_COPY_B, None, None,
8596 B_url,
8597 expected_output,
8598 expected_disk,
8599 expected_status,
8600 expected_skip,
8601 None, None, None, None, None,
8602 1, 1)
8604 os.chdir(saved_cwd)
8606 # Test for issue #2971: Reverse merge of prop add segfaults if
8607 # merging to parent of first merge
8608 def reverse_merge_prop_add_on_child(sbox):
8609 "reverse merge of prop add on child"
8611 sbox.build()
8612 wc_dir = sbox.wc_dir
8613 wc_disk, wc_status = set_up_branch(sbox, True, 1)
8615 # Some paths we'll care about
8616 G_path = os.path.join(wc_dir, "A", "D", "G")
8617 D_COPY_path = os.path.join(wc_dir, "A_COPY", "D")
8618 G_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "G")
8620 # Make some prop changes to some dirs.
8621 svntest.actions.run_and_verify_svn(None,
8622 ["property 'prop:name' set on '" +
8623 G_path + "'\n"], [], 'ps',
8624 'prop:name', 'propval', G_path)
8625 expected_output = svntest.wc.State(wc_dir, {'A/D/G': Item(verb='Sending'),})
8626 wc_status.tweak('A/D/G', wc_rev=3)
8627 wc_disk.tweak('A/D/G', props={'prop:name' : 'propval'})
8629 svntest.actions.run_and_verify_commit(wc_dir, expected_output, wc_status,
8630 None, wc_dir)
8632 # Merge -c3's prop add to A_COPY/D/G
8633 saved_cwd = os.getcwd()
8634 short_G_COPY_path = shorten_path_kludge(G_COPY_path)
8635 expected_output = wc.State(short_G_COPY_path, {
8636 '' : Item(status=' U')
8638 expected_status = wc.State(short_G_COPY_path, {
8639 '' : Item(status=' M', wc_rev=2),
8640 'pi' : Item(status=' ', wc_rev=2),
8641 'rho' : Item(status=' ', wc_rev=2),
8642 'tau' : Item(status=' ', wc_rev=2),
8644 expected_disk = wc.State('', {
8645 '' : Item(props={SVN_PROP_MERGEINFO : '/A/D/G:3',
8646 'prop:name' : 'propval'}),
8647 'pi' : Item("This is the file 'pi'.\n"),
8648 'rho' : Item("This is the file 'rho'.\n"),
8649 'tau' : Item("This is the file 'tau'.\n"),
8651 expected_skip = wc.State(short_G_COPY_path, { })
8652 os.chdir(svntest.main.work_dir)
8653 svntest.actions.run_and_verify_merge(short_G_COPY_path, '2', '3',
8654 sbox.repo_url + \
8655 '/A/D/G',
8656 expected_output,
8657 expected_disk,
8658 expected_status,
8659 expected_skip,
8660 None, None, None, None,
8661 None, 1)
8662 os.chdir(saved_cwd)
8664 # Now merge -c-3 but target the previous target's parent instead.
8665 short_D_COPY_path = shorten_path_kludge(D_COPY_path)
8666 expected_output = wc.State(short_D_COPY_path, {
8667 'G' : Item(status=' G'),
8669 expected_status = wc.State(short_D_COPY_path, {
8670 '' : Item(status=' ', wc_rev=2),
8671 'G' : Item(status=' ', wc_rev=2),
8672 'G/pi' : Item(status=' ', wc_rev=2),
8673 'G/rho' : Item(status=' ', wc_rev=2),
8674 'G/tau' : Item(status=' ', wc_rev=2),
8675 'H' : Item(status=' ', wc_rev=2),
8676 'H/chi' : Item(status=' ', wc_rev=2),
8677 'H/psi' : Item(status=' ', wc_rev=2),
8678 'H/omega' : Item(status=' ', wc_rev=2),
8679 'gamma' : Item(status=' ', wc_rev=2),
8681 expected_disk = wc.State('', {
8682 'G' : Item(),
8683 'G/pi' : Item("This is the file 'pi'.\n"),
8684 'G/rho' : Item("This is the file 'rho'.\n"),
8685 'G/tau' : Item("This is the file 'tau'.\n"),
8686 'H' : Item(),
8687 'H/chi' : Item("This is the file 'chi'.\n"),
8688 'H/psi' : Item("This is the file 'psi'.\n"),
8689 'H/omega' : Item("This is the file 'omega'.\n"),
8690 'gamma' : Item("This is the file 'gamma'.\n")
8692 expected_skip = wc.State(short_D_COPY_path, { })
8693 os.chdir(svntest.main.work_dir)
8694 svntest.actions.run_and_verify_merge(short_D_COPY_path, '3', '2',
8695 sbox.repo_url + \
8696 '/A/D',
8697 expected_output,
8698 expected_disk,
8699 expected_status,
8700 expected_skip,
8701 None, None, None, None,
8702 None, 1)
8703 os.chdir(saved_cwd)
8705 def merge_target_with_non_inheritable_mergeinfo(sbox):
8706 "merge target with non inheritable mergeinfo"
8708 ## See http://subversion.tigris.org/issues/show_bug.cgi?id=2970. ##
8710 # Create a WC with a single branch
8711 sbox.build()
8712 wc_dir = sbox.wc_dir
8713 wc_disk, wc_status = set_up_branch(sbox, True, 1)
8715 # Some paths we'll care about
8716 B_url = sbox.repo_url + '/A/B'
8717 lambda_path = os.path.join(wc_dir, 'A', 'B', 'lambda')
8718 newfile_path = os.path.join(wc_dir, 'A', 'B', 'E', 'newfile')
8719 A_COPY_B_path = os.path.join(wc_dir, 'A_COPY', 'B')
8721 # Make a modifications to A/B/lambda and add A/B/E/newfile
8722 svntest.main.file_write(lambda_path, "This is the file 'lambda' modified.\n")
8723 svntest.main.file_write(newfile_path, "This is the file 'newfile'.\n")
8724 svntest.actions.run_and_verify_svn(None, None, [], 'add', newfile_path)
8725 expected_output = wc.State(wc_dir, {
8726 'A/B/lambda' : Item(verb='Sending'),
8727 'A/B/E/newfile' : Item(verb='Adding'),
8729 wc_status.add({
8730 'A/B/lambda' : Item(status=' ', wc_rev=3),
8731 'A/B/E/newfile' : Item(status=' ', wc_rev=3),
8733 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
8734 wc_status, None, wc_dir)
8736 short_A_COPY_B = shorten_path_kludge(A_COPY_B_path)
8737 saved_cwd = os.getcwd()
8738 os.chdir(svntest.main.work_dir)
8740 # Merge /A/B to /A_COPY/B ie., r1 to r3 with depth immediates
8741 expected_output = wc.State(short_A_COPY_B, {
8742 'lambda' : Item(status='U '),
8744 expected_disk = wc.State('', {
8745 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B:1-3'}),
8746 'lambda' : Item(contents="This is the file 'lambda' modified.\n"),
8747 'F' : Item(props={SVN_PROP_MERGEINFO : '/A/B/F:1,2-3*'}),
8748 'E' : Item(props={SVN_PROP_MERGEINFO : '/A/B/E:1,2-3*'}),
8749 'E/alpha' : Item(contents="This is the file 'alpha'.\n"),
8750 'E/beta' : Item(contents="This is the file 'beta'.\n"),
8752 expected_status = wc.State(short_A_COPY_B, {
8753 '' : Item(status=' M', wc_rev=2),
8754 'lambda' : Item(status='M ', wc_rev=2),
8755 'F' : Item(status=' M', wc_rev=2),
8756 'E' : Item(status=' M', wc_rev=2),
8757 'E/alpha' : Item(status=' ', wc_rev=2),
8758 'E/beta' : Item(status=' ', wc_rev=2),
8760 expected_skip = wc.State(short_A_COPY_B, {})
8762 svntest.actions.run_and_verify_merge(short_A_COPY_B, None, None,
8763 B_url,
8764 expected_output,
8765 expected_disk,
8766 expected_status,
8767 expected_skip,
8768 None, None, None, None, None,
8769 1, 1, '--depth', 'immediates')
8771 # Merge /A/B to /A_COPY/B ie., r1 to r3 with infinite depth
8772 expected_output = wc.State(short_A_COPY_B, {
8773 'E/newfile' : Item(status='A '),
8775 expected_disk = wc.State('', {
8776 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B:1-3'}),
8777 'lambda' : Item(contents="This is the file 'lambda' modified.\n"),
8778 'F' : Item(),
8779 'E' : Item(),
8780 'E/alpha' : Item(contents="This is the file 'alpha'.\n"),
8781 'E/beta' : Item(contents="This is the file 'beta'.\n"),
8782 'E/newfile' : Item(contents="This is the file 'newfile'.\n"),
8784 expected_status = wc.State(short_A_COPY_B, {
8785 '' : Item(status=' M', wc_rev=2),
8786 'lambda' : Item(status='M ', wc_rev=2),
8787 'F' : Item(status=' ', wc_rev=2),
8788 'E' : Item(status=' ', wc_rev=2),
8789 'E/alpha' : Item(status=' ', wc_rev=2),
8790 'E/beta' : Item(status=' ', wc_rev=2),
8791 'E/newfile' : Item(status='A ', wc_rev=2),
8794 svntest.actions.run_and_verify_merge(short_A_COPY_B, None, None,
8795 B_url,
8796 expected_output,
8797 expected_disk,
8798 expected_status,
8799 expected_skip,
8800 None, None, None, None, None,
8801 1, 1)
8803 os.chdir(saved_cwd)
8805 def self_reverse_merge(sbox):
8806 "revert a commit on a target"
8808 sbox.build()
8809 wc_dir = sbox.wc_dir
8811 # Make changes to the working copy
8812 mu_path = os.path.join(wc_dir, 'A', 'mu')
8813 svntest.main.file_append(mu_path, 'appended mu text')
8815 # Created expected output tree for 'svn ci'
8816 expected_output = wc.State(wc_dir, {
8817 'A/mu' : Item(verb='Sending'),
8820 # Create expected status tree; all local revisions should be at 1,
8821 # but mu should be at revision 2.
8822 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
8823 expected_status.tweak('A/mu', wc_rev=2)
8825 svntest.actions.run_and_verify_commit(wc_dir,
8826 expected_output,
8827 expected_status,
8828 None,
8829 wc_dir)
8831 # update to HEAD so that the to-be-undone revision is found in the
8832 # implicit mergeinfo (the natural history) of the target.
8833 svntest.actions.run_and_verify_svn(None, None, [], 'update', wc_dir)
8835 expected_output = wc.State(wc_dir, {
8836 'A/mu' : Item(status='U ')
8838 expected_skip = wc.State(wc_dir, { })
8839 expected_disk = svntest.main.greek_state.copy()
8840 expected_status = svntest.actions.get_virginal_state(wc_dir, 2)
8841 expected_status.tweak('A/mu', status='M ')
8842 svntest.actions.run_and_verify_merge(wc_dir, '2', '1', sbox.repo_url,
8843 expected_output, expected_disk,
8844 expected_status, expected_skip,
8845 None, None, None, None, None, 1, 1)
8846 svntest.actions.run_and_verify_svn(None, None, [], 'revert', '-R', wc_dir)
8848 # record dummy self mergeinfo to test the fact that self-reversal should work
8849 # irrespective of mergeinfo.
8850 svntest.actions.run_and_verify_svn(None, None, [], 'merge', '-c', '1',
8851 '--record-only', sbox.repo_url, wc_dir)
8853 # Bad svntest.main.greek_state does not have '', so adding it explicitly.
8854 expected_disk.add({'' : Item(props={SVN_PROP_MERGEINFO : '/:1'})})
8855 expected_status = svntest.actions.get_virginal_state(wc_dir, 2)
8856 expected_status.tweak('', status = ' M')
8857 expected_status.tweak('A/mu', status = 'M ')
8858 svntest.actions.run_and_verify_merge(wc_dir, '2', '1', sbox.repo_url,
8859 expected_output, expected_disk,
8860 expected_status, expected_skip,
8861 None, None, None, None, None, 1, 1)
8863 def ignore_ancestry_and_mergeinfo(sbox):
8864 "--ignore-ancestry also ignores mergeinfo"
8866 # Create a WC with a single branch
8867 sbox.build()
8868 wc_dir = sbox.wc_dir
8869 wc_disk, wc_status = set_up_branch(sbox, True, 1)
8871 # Some paths we'll care about
8872 A_B_url = sbox.repo_url + '/A/B'
8873 A_COPY_B_path = os.path.join(wc_dir, 'A_COPY', 'B')
8874 lambda_path = os.path.join(wc_dir, 'A', 'B', 'lambda')
8875 A_COPY_lambda_path = os.path.join(wc_dir, 'A_COPY', 'B', 'lambda')
8877 # Make modifications to A/B/lambda
8878 svntest.main.file_write(lambda_path, "This is the file 'lambda' modified.\n")
8879 expected_output = wc.State(wc_dir, {
8880 'A/B/lambda' : Item(verb='Sending'),
8882 wc_status.add({
8883 'A/B/lambda' : Item(status=' ', wc_rev=3),
8885 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
8886 wc_status, None, wc_dir)
8888 svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
8890 short_A_COPY_B = shorten_path_kludge(A_COPY_B_path)
8891 short_A_COPY_lambda = shorten_path_kludge(A_COPY_lambda_path)
8892 saved_cwd = os.getcwd()
8893 os.chdir(svntest.main.work_dir)
8895 # Merge /A/B to /A_COPY/B ie., r1 to r3 with depth immediates
8896 expected_output = wc.State(short_A_COPY_B, {
8897 'lambda' : Item(status='U '),
8899 expected_disk = wc.State('', {
8900 '' : Item(props={SVN_PROP_MERGEINFO : '/A/B:2-3'}),
8901 'lambda' : Item(contents="This is the file 'lambda' modified.\n"),
8902 'F' : Item(props={}),
8903 'E' : Item(props={}),
8904 'E/alpha' : Item(contents="This is the file 'alpha'.\n"),
8905 'E/beta' : Item(contents="This is the file 'beta'.\n"),
8907 expected_status = wc.State(short_A_COPY_B, {
8908 '' : Item(status=' M', wc_rev=3),
8909 'lambda' : Item(status='M ', wc_rev=3),
8910 'F' : Item(status=' ', wc_rev=3),
8911 'E' : Item(status=' ', wc_rev=3),
8912 'E/alpha' : Item(status=' ', wc_rev=3),
8913 'E/beta' : Item(status=' ', wc_rev=3),
8915 expected_skip = wc.State(short_A_COPY_B, {})
8917 svntest.actions.run_and_verify_merge(short_A_COPY_B, 1, 3,
8918 A_B_url,
8919 expected_output,
8920 expected_disk,
8921 expected_status,
8922 expected_skip,
8923 None, None, None, None, None, 1, 1)
8925 # Now, revert lambda and repeat the merge. Nothing should happen.
8926 svntest.actions.run_and_verify_svn(None, None, [], 'revert', '-R',
8927 short_A_COPY_lambda)
8928 expected_output.remove('lambda')
8929 expected_disk.tweak('lambda', contents="This is the file 'lambda'.\n")
8930 expected_status.tweak('lambda', status=' ')
8931 svntest.actions.run_and_verify_merge(short_A_COPY_B, 1, 3,
8932 A_B_url,
8933 expected_output,
8934 expected_disk,
8935 expected_status,
8936 expected_skip,
8937 None, None, None, None, None, 1, 1)
8939 # Now, try the merge again with --ignore-ancestry. We should get
8940 # lambda re-modified. */
8941 expected_output = wc.State(short_A_COPY_B, {
8942 'lambda' : Item(status='U '),
8944 expected_disk.tweak('lambda',
8945 contents="This is the file 'lambda' modified.\n")
8946 expected_status.tweak('lambda', status='M ')
8947 svntest.actions.run_and_verify_merge(short_A_COPY_B, 1, 3,
8948 A_B_url,
8949 expected_output,
8950 expected_disk,
8951 expected_status,
8952 expected_skip,
8953 None, None, None, None, None, 1, 1,
8954 '--ignore-ancestry')
8956 os.chdir(saved_cwd)
8958 def merge_from_renamed_branch_fails_while_avoiding_repeat_merge(sbox):
8959 "merge from renamed branch"
8960 #Copy A/C to A/COPY_C results in r2.
8961 #Rename A/COPY_C to A/RENAMED_C results in r3.
8962 #Add A/RENAMED_C/file1 and commit, results in r4.
8963 #Change A/RENAMED_C/file1 and commit, results in r5.
8964 #Merge r4 from A/RENAMED_C to A/C
8965 #Merge r2:5 from A/RENAMED_C to A/C <-- This fails tracked via #3032.
8967 ## See http://subversion.tigris.org/issues/show_bug.cgi?id=3032. ##
8969 # Create a WC with a single branch
8970 sbox.build()
8971 wc_dir = sbox.wc_dir
8972 # Some paths we'll care about
8973 A_C_url = sbox.repo_url + '/A/C'
8974 A_COPY_C_url = sbox.repo_url + '/A/COPY_C'
8975 A_RENAMED_C_url = sbox.repo_url + '/A/RENAMED_C'
8976 A_C_path = os.path.join(wc_dir, 'A', 'C')
8977 A_RENAMED_C_path = os.path.join(wc_dir, 'A', 'RENAMED_C')
8978 A_RENAMED_C_file1_path = os.path.join(wc_dir, 'A', 'RENAMED_C', 'file1')
8980 svntest.main.run_svn(None, 'cp', A_C_url, A_COPY_C_url, '-m', 'copy...')
8981 svntest.main.run_svn(None, 'mv', A_COPY_C_url, A_RENAMED_C_url, '-m',
8982 'rename...')
8983 svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
8985 svntest.main.file_write(A_RENAMED_C_file1_path, "This is the file1.\n")
8986 svntest.main.run_svn(None, 'add', A_RENAMED_C_file1_path)
8987 expected_output = wc.State(A_RENAMED_C_path, {
8988 'file1' : Item(verb='Adding'),
8990 expected_status = wc.State(A_RENAMED_C_path, {
8991 '' : Item(status=' ', wc_rev=3),
8992 'file1' : Item(status=' ', wc_rev=4),
8994 svntest.actions.run_and_verify_commit(A_RENAMED_C_path, expected_output,
8995 expected_status, None,
8996 A_RENAMED_C_path)
8997 svntest.main.file_write(A_RENAMED_C_file1_path,
8998 "This is the file1 modified.\n")
8999 expected_output = wc.State(A_RENAMED_C_path, {
9000 'file1' : Item(verb='Sending'),
9002 expected_status.tweak('file1', wc_rev=5)
9003 svntest.actions.run_and_verify_commit(A_RENAMED_C_path, expected_output,
9004 expected_status, None,
9005 A_RENAMED_C_path)
9007 short_A_C = shorten_path_kludge(A_C_path)
9008 expected_skip = wc.State(short_A_C, {})
9009 expected_output = wc.State(short_A_C, {
9010 'file1' : Item(status='A '),
9012 expected_disk = wc.State('', {
9013 '' : Item(props={SVN_PROP_MERGEINFO : '/A/RENAMED_C:4'}),
9014 'file1' : Item("This is the file1.\n"),
9016 expected_status = wc.State(short_A_C, {
9017 '' : Item(status=' M', wc_rev=3),
9018 'file1' : Item(status='A ', wc_rev='-', copied='+'),
9020 saved_cwd = os.getcwd()
9021 os.chdir(svntest.main.work_dir)
9022 svntest.actions.run_and_verify_merge(short_A_C, 3, 4,
9023 A_RENAMED_C_url,
9024 expected_output,
9025 expected_disk,
9026 expected_status,
9027 expected_skip,
9028 None, None, None, None, None, 1, 1)
9030 expected_output = wc.State(short_A_C, {
9031 'file1' : Item(status='U '),
9033 expected_disk = wc.State('', {
9034 '' : Item(props={SVN_PROP_MERGEINFO : '/A/RENAMED_C:3-5'}),
9035 'file1' : Item("This is the file1 modified.\n"),
9037 expected_status = wc.State(short_A_C, {
9038 '' : Item(status=' M', wc_rev=3),
9039 'file1' : Item(status='A ', wc_rev='-', copied='+'),
9041 svntest.actions.run_and_verify_merge(short_A_C, 2, 5,
9042 A_RENAMED_C_url,
9043 expected_output,
9044 expected_disk,
9045 expected_status,
9046 expected_skip,
9047 None, None, None, None, None, 1, 1)
9048 os.chdir(saved_cwd)
9050 # Test for part of issue #2877: 'do subtree merge only if subtree has
9051 # explicit mergeinfo set and exists in the merge source'
9052 def merge_source_normalization_and_subtree_merges(sbox):
9053 "normalized mergeinfo is recorded on subtrees"
9055 sbox.build()
9056 wc_dir = sbox.wc_dir
9058 # Some paths we'll care about
9059 D_COPY_path = os.path.join(wc_dir, "A_COPY", "D")
9060 G_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "G")
9062 # Use our helper to copy 'A' to 'A_COPY' then make some changes under 'A'
9063 wc_disk, wc_status = set_up_branch(sbox)
9065 # r7 - Move A to A_MOVED
9066 svntest.actions.run_and_verify_svn(None, ['\n', 'Committed revision 7.\n'],
9067 [], 'mv', '-m', 'mv A to A_MOVED',
9068 sbox.repo_url + '/A',
9069 sbox.repo_url + '/A_MOVED')
9070 wc_status.add({
9071 'A_MOVED/B' : Item(),
9072 'A_MOVED/B/lambda' : Item(),
9073 'A_MOVED/B/E' : Item(),
9074 'A_MOVED/B/E/alpha' : Item(),
9075 'A_MOVED/B/E/beta' : Item(),
9076 'A_MOVED/B/F' : Item(),
9077 'A_MOVED/mu' : Item(),
9078 'A_MOVED/C' : Item(),
9079 'A_MOVED/D' : Item(),
9080 'A_MOVED/D/gamma' : Item(),
9081 'A_MOVED/D/G' : Item(),
9082 'A_MOVED/D/G/pi' : Item(),
9083 'A_MOVED/D/G/rho' : Item(),
9084 'A_MOVED/D/G/tau' : Item(),
9085 'A_MOVED/D/H' : Item(),
9086 'A_MOVED/D/H/chi' : Item(),
9087 'A_MOVED/D/H/omega' : Item(),
9088 'A_MOVED/D/H/psi' : Item(),
9089 'A_MOVED' : Item()})
9090 wc_status.remove('A', 'A/B', 'A/B/lambda', 'A/B/E', 'A/B/E/alpha',
9091 'A/B/E/beta', 'A/B/F', 'A/mu', 'A/C', 'A/D',
9092 'A/D/gamma', 'A/D/G', 'A/D/G/pi', 'A/D/G/rho',
9093 'A/D/G/tau' , 'A/D/H', 'A/D/H/chi', 'A/D/H/omega',
9094 'A/D/H/psi')
9095 wc_status.tweak(status=' ', wc_rev=7)
9097 # Update the WC
9098 svntest.actions.run_and_verify_svn(None, None, [],
9099 'update', wc_dir)
9101 # r8 - Make a text mod to 'A_MOVED/D/G/tau'
9102 svntest.main.file_write(os.path.join(wc_dir, "A_MOVED", "D", "G", "tau"),
9103 "New content")
9104 expected_output = wc.State(wc_dir,
9105 {'A_MOVED/D/G/tau' : Item(verb='Sending')})
9106 wc_status.tweak('A_MOVED/D/G/tau', status=' ', wc_rev=8)
9107 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
9108 wc_status, None, wc_dir)
9110 # Merge -c4 URL/A_MOVED/D/G A_COPY/D/G.
9112 # Search for the comment entitled "The Merge Kluge" elsewhere in
9113 # this file, to understand why we shorten and chdir() below.
9115 # A_MOVED/D/G doesn't exist at r3:4, it's still A/D/G,
9116 # so the merge source normalization logic should set
9117 # mergeinfo of '/A/D/G:4' on A_COPY/D/G, *not* 'A_MOVED/D/G:4',
9118 # see issue #2953.
9119 short_G_COPY_path = shorten_path_kludge(G_COPY_path)
9120 expected_output = wc.State(short_G_COPY_path, {
9121 'rho' : Item(status='U ')
9123 expected_status = wc.State(short_G_COPY_path, {
9124 '' : Item(status=' M', wc_rev=7),
9125 'pi' : Item(status=' ', wc_rev=7),
9126 'rho' : Item(status='M ', wc_rev=7),
9127 'tau' : Item(status=' ', wc_rev=7),
9129 expected_disk = wc.State('', {
9130 '' : Item(props={SVN_PROP_MERGEINFO : '/A/D/G:4'}),
9131 'pi' : Item("This is the file 'pi'.\n"),
9132 'rho' : Item("New content"),
9133 'tau' : Item("This is the file 'tau'.\n"),
9135 expected_skip = wc.State(short_G_COPY_path, { })
9136 saved_cwd = os.getcwd()
9137 os.chdir(svntest.main.work_dir)
9138 svntest.actions.run_and_verify_merge(short_G_COPY_path, '3', '4',
9139 sbox.repo_url + '/A_MOVED/D/G',
9140 expected_output,
9141 expected_disk,
9142 expected_status,
9143 expected_skip,
9144 None, None, None, None,
9145 None, 1)
9146 os.chdir(saved_cwd)
9148 # Merge -c8 URL/A_MOVED/D A_COPY/D.
9150 # The merge target A_COPY/D and the subtree at A_COPY/D/G
9151 # should both have their mergeinfo updated with r8
9152 # from A_MOVED_D, see reopened issue #2877.
9153 short_D_COPY_path = shorten_path_kludge(D_COPY_path)
9154 expected_output = wc.State(short_D_COPY_path, {
9155 'G/tau' : Item(status='U '),
9157 expected_status = wc.State(short_D_COPY_path, {
9158 '' : Item(status=' M', wc_rev=7),
9159 'G' : Item(status=' M', wc_rev=7),
9160 'G/pi' : Item(status=' ', wc_rev=7),
9161 'G/rho' : Item(status='M ', wc_rev=7),
9162 'G/tau' : Item(status='M ', wc_rev=7),
9163 'H' : Item(status=' ', wc_rev=7),
9164 'H/chi' : Item(status=' ', wc_rev=7),
9165 'H/psi' : Item(status=' ', wc_rev=7),
9166 'H/omega' : Item(status=' ', wc_rev=7),
9167 'gamma' : Item(status=' ', wc_rev=7),
9169 expected_disk = wc.State('', {
9170 '' : Item(props={SVN_PROP_MERGEINFO : '/A_MOVED/D:8'}),
9171 'G' : Item(props={SVN_PROP_MERGEINFO :
9172 '/A/D/G:4\n/A_MOVED/D/G:8\n'}),
9173 'G/pi' : Item("This is the file 'pi'.\n"),
9174 'G/rho' : Item("New content"),
9175 'G/tau' : Item("New content"),
9176 'H' : Item(),
9177 'H/chi' : Item("This is the file 'chi'.\n"),
9178 'H/psi' : Item("This is the file 'psi'.\n"),
9179 'H/omega' : Item("This is the file 'omega'.\n"),
9180 'gamma' : Item("This is the file 'gamma'.\n")
9182 expected_skip = wc.State(short_D_COPY_path, { })
9183 os.chdir(svntest.main.work_dir)
9184 svntest.actions.run_and_verify_merge(short_D_COPY_path, '7', '8',
9185 sbox.repo_url + \
9186 '/A_MOVED/D',
9187 expected_output,
9188 expected_disk,
9189 expected_status,
9190 expected_skip,
9191 None, None, None, None,
9192 None, 1)
9193 os.chdir(saved_cwd)
9195 # Test for issue #3067: 'subtrees with intersecting mergeinfo, that don't
9196 # exist at the start of a merge range shouldn't break the merge'
9197 def new_subtrees_should_not_break_merge(sbox):
9198 "subtrees added after start of merge range are ok"
9200 sbox.build()
9201 wc_dir = sbox.wc_dir
9202 wc_disk, wc_status = set_up_branch(sbox)
9204 # Some paths we'll care about
9205 A_COPY_path = os.path.join(wc_dir, "A_COPY")
9206 D_COPY_path = os.path.join(wc_dir, "A_COPY", "D")
9207 nu_path = os.path.join(wc_dir, "A", "D", "H", "nu")
9208 nu_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "H", "nu")
9209 H_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "H")
9211 # Create 'A/D/H/nu', commit it as r7, make a text mod to it in r8.
9212 svntest.main.file_write(nu_path, "This is the file 'nu'.\n")
9213 svntest.actions.run_and_verify_svn(None, None, [], 'add', nu_path)
9214 expected_output = wc.State(wc_dir, {'A/D/H/nu' : Item(verb='Adding')})
9215 wc_status.add({'A/D/H/nu' : Item(status=' ', wc_rev=7)})
9216 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
9217 wc_status, None, wc_dir)
9218 svntest.main.file_write(nu_path, "New content")
9219 expected_output = wc.State(wc_dir, {'A/D/H/nu' : Item(verb='Sending')})
9220 wc_status.tweak('A/D/H/nu', wc_rev=8)
9221 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
9222 wc_status, None, wc_dir)
9224 # Merge r7 to A_COPY/D/H, then, so it has it's own explicit mergeinfo,
9225 # then merge r8 to A_COPY/D/H/nu so it too has explicit mergeinfo.
9226 short_H_COPY_path = shorten_path_kludge(H_COPY_path)
9227 expected_output = wc.State(short_H_COPY_path, {
9228 'nu' : Item(status='A '),
9230 expected_status = wc.State(short_H_COPY_path, {
9231 '' : Item(status=' M', wc_rev=2),
9232 'psi' : Item(status=' ', wc_rev=2),
9233 'omega' : Item(status=' ', wc_rev=2),
9234 'chi' : Item(status=' ', wc_rev=2),
9235 'nu' : Item(status='A ', copied='+', wc_rev='-'),
9237 expected_disk = wc.State('', {
9238 '' : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:7'}),
9239 'psi' : Item("This is the file 'psi'.\n"),
9240 'omega' : Item("This is the file 'omega'.\n"),
9241 'chi' : Item("This is the file 'chi'.\n"),
9242 'nu' : Item("This is the file 'nu'.\n"),
9244 expected_skip = wc.State(short_H_COPY_path, {})
9245 saved_cwd = os.getcwd()
9246 os.chdir(svntest.main.work_dir)
9247 svntest.actions.run_and_verify_merge(short_H_COPY_path, '6', '7',
9248 sbox.repo_url + '/A/D/H',
9249 expected_output, expected_disk,
9250 expected_status, expected_skip,
9251 None, None, None, None, None, 1)
9252 # run_and_verify_merge doesn't support merging to a file WCPATH
9253 # so use run_and_verify_svn.
9254 short_nu_COPY_path = shorten_path_kludge(nu_COPY_path)
9255 svntest.actions.run_and_verify_svn(None,
9256 expected_merge_output([[8]],
9257 'U ' + short_nu_COPY_path + '\n'),
9258 [], 'merge', '-c8',
9259 sbox.repo_url + '/A/D/H/nu',
9260 short_nu_COPY_path)
9261 # Merge -r4:6 to A_COPY, then reverse merge r6 from A_COPY/D.
9262 short_A_COPY_path = shorten_path_kludge(A_COPY_path)
9263 expected_output = wc.State(short_A_COPY_path, {
9264 'B/E/beta' : Item(status='U '),
9265 'D/H/omega': Item(status='U '),
9267 expected_status = wc.State(short_A_COPY_path, {
9268 '' : Item(status=' M', wc_rev=2),
9269 'B' : Item(status=' ', wc_rev=2),
9270 'mu' : Item(status=' ', wc_rev=2),
9271 'B/E' : Item(status=' ', wc_rev=2),
9272 'B/E/alpha' : Item(status=' ', wc_rev=2),
9273 'B/E/beta' : Item(status='M ', wc_rev=2),
9274 'B/lambda' : Item(status=' ', wc_rev=2),
9275 'B/F' : Item(status=' ', wc_rev=2),
9276 'C' : Item(status=' ', wc_rev=2),
9277 'D' : Item(status=' ', wc_rev=2),
9278 'D/G' : Item(status=' ', wc_rev=2),
9279 'D/G/pi' : Item(status=' ', wc_rev=2),
9280 'D/G/rho' : Item(status=' ', wc_rev=2),
9281 'D/G/tau' : Item(status=' ', wc_rev=2),
9282 'D/gamma' : Item(status=' ', wc_rev=2),
9283 'D/H' : Item(status=' M', wc_rev=2),
9284 'D/H/chi' : Item(status=' ', wc_rev=2),
9285 'D/H/psi' : Item(status=' ', wc_rev=2),
9286 'D/H/omega' : Item(status='M ', wc_rev=2),
9287 'D/H/nu' : Item(status='A ', copied='+', wc_rev='-'),
9289 expected_disk = wc.State('', {
9290 '' : Item(props={SVN_PROP_MERGEINFO : '/A:5-6'}),
9291 'B' : Item(),
9292 'mu' : Item("This is the file 'mu'.\n"),
9293 'B/E' : Item(),
9294 'B/E/alpha' : Item("This is the file 'alpha'.\n"),
9295 'B/E/beta' : Item("New content"),
9296 'B/lambda' : Item("This is the file 'lambda'.\n"),
9297 'B/F' : Item(),
9298 'C' : Item(),
9299 'D' : Item(),
9300 'D/G' : Item(),
9301 'D/G/pi' : Item("This is the file 'pi'.\n"),
9302 'D/G/rho' : Item("This is the file 'rho'.\n"),
9303 'D/G/tau' : Item("This is the file 'tau'.\n"),
9304 'D/gamma' : Item("This is the file 'gamma'.\n"),
9305 'D/H' : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:5-7'}),
9306 'D/H/chi' : Item("This is the file 'chi'.\n"),
9307 'D/H/psi' : Item("This is the file 'psi'.\n"),
9308 'D/H/omega' : Item("New content"),
9309 'D/H/nu' : Item("New content",
9310 props={SVN_PROP_MERGEINFO : '/A/D/H/nu:5-8'}),
9312 expected_skip = wc.State(short_A_COPY_path, { })
9313 svntest.actions.run_and_verify_merge(short_A_COPY_path, '4', '6',
9314 sbox.repo_url + \
9315 '/A',
9316 expected_output,
9317 expected_disk,
9318 expected_status,
9319 expected_skip,
9320 None, None, None, None,
9321 None, 1)
9322 short_D_COPY_path = shorten_path_kludge(D_COPY_path)
9323 expected_output = wc.State(short_D_COPY_path, {
9324 'H/omega': Item(status='G '),
9326 expected_status = wc.State(short_D_COPY_path, {
9327 '' : Item(status=' M', wc_rev=2),
9328 'G' : Item(status=' ', wc_rev=2),
9329 'G/pi' : Item(status=' ', wc_rev=2),
9330 'G/rho' : Item(status=' ', wc_rev=2),
9331 'G/tau' : Item(status=' ', wc_rev=2),
9332 'gamma' : Item(status=' ', wc_rev=2),
9333 'H' : Item(status=' M', wc_rev=2),
9334 'H/chi' : Item(status=' ', wc_rev=2),
9335 'H/psi' : Item(status=' ', wc_rev=2),
9336 'H/omega' : Item(status=' ', wc_rev=2),
9337 'H/nu' : Item(status='A ', copied='+', wc_rev='-'),
9339 expected_disk = wc.State('', {
9340 '' : Item(props={SVN_PROP_MERGEINFO : '/A/D:5'}),
9341 'G/pi' : Item("This is the file 'pi'.\n"),
9342 'G/rho' : Item("This is the file 'rho'.\n"),
9343 'G/tau' : Item("This is the file 'tau'.\n"),
9344 'gamma' : Item("This is the file 'gamma'.\n"),
9345 'H' : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:5,7'}),
9346 'H/chi' : Item("This is the file 'chi'.\n"),
9347 'H/psi' : Item("This is the file 'psi'.\n"),
9348 'H/omega' : Item("This is the file 'omega'.\n"),
9349 'H/nu' : Item("New content",
9350 props={SVN_PROP_MERGEINFO : '/A/D/H/nu:5,7-8'}),
9352 expected_skip = wc.State(short_D_COPY_path, { })
9353 svntest.actions.run_and_verify_merge(short_D_COPY_path, '6', '5',
9354 sbox.repo_url + \
9355 '/A/D',
9356 expected_output,
9357 expected_disk,
9358 expected_status,
9359 expected_skip,
9360 None, None, None, None,
9361 None, 1)
9362 # Now once again merge r6 to A_COPY. A_COPY already has r6 in its mergeinfo
9363 # so we expect only subtree merges on A_COPY/D, A_COPY_D_H, and
9364 # A_COPY/D/H/nu. The fact that A/D/H/nu doesn't exist at r6 should not cause
9365 # the merge to fail.
9366 short_A_COPY_path = shorten_path_kludge(A_COPY_path)
9367 expected_output = wc.State(short_A_COPY_path, {
9368 'D/H/omega': Item(status='U '),
9370 expected_status = wc.State(short_A_COPY_path, {
9371 '' : Item(status=' M', wc_rev=2),
9372 'B' : Item(status=' ', wc_rev=2),
9373 'mu' : Item(status=' ', wc_rev=2),
9374 'B/E' : Item(status=' ', wc_rev=2),
9375 'B/E/alpha' : Item(status=' ', wc_rev=2),
9376 'B/E/beta' : Item(status='M ', wc_rev=2),
9377 'B/lambda' : Item(status=' ', wc_rev=2),
9378 'B/F' : Item(status=' ', wc_rev=2),
9379 'C' : Item(status=' ', wc_rev=2),
9380 'D' : Item(status=' ', wc_rev=2),
9381 'D/G' : Item(status=' ', wc_rev=2),
9382 'D/G/pi' : Item(status=' ', wc_rev=2),
9383 'D/G/rho' : Item(status=' ', wc_rev=2),
9384 'D/G/tau' : Item(status=' ', wc_rev=2),
9385 'D/gamma' : Item(status=' ', wc_rev=2),
9386 'D/H' : Item(status=' M', wc_rev=2),
9387 'D/H/chi' : Item(status=' ', wc_rev=2),
9388 'D/H/psi' : Item(status=' ', wc_rev=2),
9389 'D/H/omega' : Item(status='M ', wc_rev=2),
9390 'D/H/nu' : Item(status='A ', copied='+', wc_rev='-'),
9392 expected_disk = wc.State('', {
9393 '' : Item(props={SVN_PROP_MERGEINFO : '/A:5-6'}),
9394 'B' : Item(),
9395 'mu' : Item("This is the file 'mu'.\n"),
9396 'B/E' : Item(),
9397 'B/E/alpha' : Item("This is the file 'alpha'.\n"),
9398 'B/E/beta' : Item("New content"),
9399 'B/lambda' : Item("This is the file 'lambda'.\n"),
9400 'B/F' : Item(),
9401 'C' : Item(),
9402 'D' : Item(), # Mergeinfo elides to 'A_COPY'
9403 'D/G' : Item(),
9404 'D/G/pi' : Item("This is the file 'pi'.\n"),
9405 'D/G/rho' : Item("This is the file 'rho'.\n"),
9406 'D/G/tau' : Item("This is the file 'tau'.\n"),
9407 'D/gamma' : Item("This is the file 'gamma'.\n"),
9408 'D/H' : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:5-7'}),
9409 'D/H/chi' : Item("This is the file 'chi'.\n"),
9410 'D/H/psi' : Item("This is the file 'psi'.\n"),
9411 'D/H/omega' : Item("New content"),
9412 'D/H/nu' : Item("New content",
9413 props={SVN_PROP_MERGEINFO : '/A/D/H/nu:5-8'}),
9415 expected_skip = wc.State(short_A_COPY_path, { })
9416 svntest.actions.run_and_verify_merge(short_A_COPY_path, '5', '6',
9417 sbox.repo_url + \
9418 '/A',
9419 expected_output,
9420 expected_disk,
9421 expected_status,
9422 expected_skip,
9423 None, None, None, None,
9424 None, 1)
9425 os.chdir(saved_cwd)
9427 def basic_reintegrate(sbox):
9428 "basic merge --reintegrate support"
9430 # Make A_COPY branch in r2, and do a few more commits to A in r3-6.
9431 sbox.build()
9432 wc_dir = sbox.wc_dir
9433 expected_disk, expected_status = set_up_branch(sbox)
9435 # Make a change on the branch, to A/mu. Commit in r7.
9436 svntest.main.file_write(os.path.join(wc_dir, "A_COPY", "mu"),
9437 "Changed on the branch.")
9438 expected_output = wc.State(wc_dir, {'A_COPY/mu' : Item(verb='Sending')})
9439 expected_status.tweak('A_COPY/mu', wc_rev=7)
9440 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
9441 expected_status, None, wc_dir)
9442 expected_disk.tweak('A_COPY/mu', contents='Changed on the branch.')
9444 # Update the wcs.
9445 expected_output = wc.State(wc_dir, {})
9446 expected_status.tweak(wc_rev='7')
9447 svntest.actions.run_and_verify_update(wc_dir, expected_output,
9448 expected_disk, expected_status,
9449 None, None, None, None, None, True)
9451 # Merge from trunk to branch (ie, r3-6), using normal cherry-harvest.
9452 A_COPY_path = os.path.join(wc_dir, "A_COPY")
9453 short_A_COPY_path = shorten_path_kludge(A_COPY_path)
9454 expected_output = wc.State(short_A_COPY_path, {
9455 'D/H/psi' : Item(status='U '),
9456 'D/G/rho' : Item(status='U '),
9457 'B/E/beta' : Item(status='U '),
9458 'D/H/omega' : Item(status='U '),
9460 k_expected_status = wc.State(short_A_COPY_path, {
9461 "B" : Item(status=' ', wc_rev=7),
9462 "B/lambda" : Item(status=' ', wc_rev=7),
9463 "B/E" : Item(status=' ', wc_rev=7),
9464 "B/E/alpha" : Item(status=' ', wc_rev=7),
9465 "B/E/beta" : Item(status='M ', wc_rev=7),
9466 "B/F" : Item(status=' ', wc_rev=7),
9467 "mu" : Item(status=' ', wc_rev=7),
9468 "C" : Item(status=' ', wc_rev=7),
9469 "D" : Item(status=' ', wc_rev=7),
9470 "D/gamma" : Item(status=' ', wc_rev=7),
9471 "D/G" : Item(status=' ', wc_rev=7),
9472 "D/G/pi" : Item(status=' ', wc_rev=7),
9473 "D/G/rho" : Item(status='M ', wc_rev=7),
9474 "D/G/tau" : Item(status=' ', wc_rev=7),
9475 "D/H" : Item(status=' ', wc_rev=7),
9476 "D/H/chi" : Item(status=' ', wc_rev=7),
9477 "D/H/omega" : Item(status='M ', wc_rev=7),
9478 "D/H/psi" : Item(status='M ', wc_rev=7),
9479 "" : Item(status=' M', wc_rev=7),
9481 k_expected_disk = wc.State('', {
9482 '' : Item(props={SVN_PROP_MERGEINFO : '/A:2-7'}),
9483 'B' : Item(),
9484 'B/lambda' : Item("This is the file 'lambda'.\n"),
9485 'B/E' : Item(),
9486 'B/E/alpha' : Item("This is the file 'alpha'.\n"),
9487 'B/E/beta' : Item("New content"),
9488 'B/F' : Item(),
9489 'mu' : Item("Changed on the branch."),
9490 'C' : Item(),
9491 'D' : Item(),
9492 'D/gamma' : Item("This is the file 'gamma'.\n"),
9493 'D/G' : Item(),
9494 'D/G/pi' : Item("This is the file 'pi'.\n"),
9495 'D/G/rho' : Item("New content"),
9496 'D/G/tau' : Item("This is the file 'tau'.\n"),
9497 'D/H' : Item(),
9498 'D/H/chi' : Item("This is the file 'chi'.\n"),
9499 'D/H/omega' : Item("New content"),
9500 'D/H/psi' : Item("New content"),
9502 expected_skip = wc.State(short_A_COPY_path, {})
9503 saved_cwd = os.getcwd()
9504 os.chdir(svntest.main.work_dir)
9505 svntest.actions.run_and_verify_merge(short_A_COPY_path, None, None,
9506 sbox.repo_url + '/A',
9507 expected_output,
9508 k_expected_disk,
9509 k_expected_status,
9510 expected_skip,
9511 None, None, None, None,
9512 None, True)
9513 os.chdir(saved_cwd)
9514 expected_disk.tweak('A_COPY', props={SVN_PROP_MERGEINFO: '/A:2-7'})
9515 expected_disk.tweak('A_COPY/B/E/beta', contents="New content")
9516 expected_disk.tweak('A_COPY/D/G/rho', contents="New content")
9517 expected_disk.tweak('A_COPY/D/H/omega', contents="New content")
9518 expected_disk.tweak('A_COPY/D/H/psi', contents="New content")
9520 # Commit the merge to branch (r8).
9521 expected_output = wc.State(wc_dir, {
9522 'A_COPY/D/H/psi' : Item(verb='Sending'),
9523 'A_COPY/D/G/rho' : Item(verb='Sending'),
9524 'A_COPY/B/E/beta' : Item(verb='Sending'),
9525 'A_COPY/D/H/omega' : Item(verb='Sending'),
9526 'A_COPY' : Item(verb='Sending'),
9528 expected_status.tweak('A_COPY', 'A_COPY/D/H/psi', 'A_COPY/D/G/rho',
9529 'A_COPY/B/E/beta', 'A_COPY/D/H/omega', wc_rev=8)
9530 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
9531 expected_status, None, wc_dir)
9534 # Update the wcs again.
9536 # Note: this update had to be added because of r28942 (which was
9537 # merged into the reintegrate branch in r28947). Without this
9538 # update, the mergeinfo will not be inherited properly as part of
9539 # the 'svn cp tau tauprime' step, and later (during the post-commit
9540 # update, with the new expected_disk) we'll get an error like this:
9542 # =============================================================
9543 # Expected 'tauprime' and actual 'tauprime' in disk tree are different!
9544 # =============================================================
9545 # EXPECTED NODE TO BE:
9546 # =============================================================
9547 # * Node name: tauprime
9548 # Path: A_COPY/D/G/tauprime
9549 # Contents: This is the file 'tau'.
9551 # Properties: {'svn:mergeinfo': '/A/D/G/tau:2-7'}
9552 # Attributes: {}
9553 # Children: N/A (node is a file)
9554 # =============================================================
9555 # ACTUAL NODE FOUND:
9556 # =============================================================
9557 # * Node name: tauprime
9558 # Path: G/tauprime
9559 # Contents: This is the file 'tau'.
9561 # Properties: {'svn:mergeinfo': ''}
9562 # Attributes: {}
9563 # Children: N/A (node is a file)
9565 expected_output = wc.State(wc_dir, {})
9566 expected_status.tweak(wc_rev='8')
9567 svntest.actions.run_and_verify_update(wc_dir, expected_output,
9568 expected_disk, expected_status,
9569 None, None, None, None, None, True)
9571 # Make another change on the branch: copy tau to tauprime. Commit
9572 # in r9.
9573 svntest.actions.run_and_verify_svn(None, None, [], 'cp',
9574 os.path.join(wc_dir, 'A_COPY', 'D', 'G',
9575 'tau'),
9576 os.path.join(wc_dir, 'A_COPY', 'D', 'G',
9577 'tauprime'))
9579 expected_output = wc.State(wc_dir, {
9580 'A_COPY/D/G/tauprime' : Item(verb='Adding')
9582 expected_status.add({'A_COPY/D/G/tauprime': Item(status=' ', wc_rev=9)})
9583 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
9584 expected_status, None, wc_dir)
9586 expected_disk.add({
9587 'A_COPY/D/G/tauprime' : Item(props={SVN_PROP_MERGEINFO: '/A/D/G/tau:2-7'},
9588 contents="This is the file 'tau'.\n")
9591 # Update the trunk (well, the whole wc) (since reintegrate really
9592 # wants a clean wc).
9593 expected_output = wc.State(wc_dir, {})
9594 expected_status.tweak(wc_rev='9')
9595 svntest.actions.run_and_verify_update(wc_dir, expected_output,
9596 expected_disk, expected_status,
9597 None, None, None, None, None, True)
9599 # *finally*, actually run merge --reintegrate in trunk with the
9600 # branch URL. This should bring in the mu change and the tauprime
9601 # change.
9602 A_path = os.path.join(wc_dir, "A")
9603 short_A_path = shorten_path_kludge(A_path)
9604 expected_output = wc.State(short_A_path, {
9605 '' : Item(status=' U'),
9606 'mu' : Item(status='U '),
9607 'D/G/tauprime' : Item(status='A '),
9609 k_expected_status = wc.State(short_A_path, {
9610 "B" : Item(status=' ', wc_rev=9),
9611 "B/lambda" : Item(status=' ', wc_rev=9),
9612 "B/E" : Item(status=' ', wc_rev=9),
9613 "B/E/alpha" : Item(status=' ', wc_rev=9),
9614 "B/E/beta" : Item(status=' ', wc_rev=9),
9615 "B/F" : Item(status=' ', wc_rev=9),
9616 "mu" : Item(status='M ', wc_rev=9),
9617 "C" : Item(status=' ', wc_rev=9),
9618 "D" : Item(status=' ', wc_rev=9),
9619 "D/gamma" : Item(status=' ', wc_rev=9),
9620 "D/G" : Item(status=' ', wc_rev=9),
9621 "D/G/pi" : Item(status=' ', wc_rev=9),
9622 "D/G/rho" : Item(status=' ', wc_rev=9),
9623 "D/G/tau" : Item(status=' ', wc_rev=9),
9624 "D/G/tauprime" : Item(status='A ', wc_rev='-', copied='+'),
9625 "D/H" : Item(status=' ', wc_rev=9),
9626 "D/H/chi" : Item(status=' ', wc_rev=9),
9627 "D/H/omega" : Item(status=' ', wc_rev=9),
9628 "D/H/psi" : Item(status=' ', wc_rev=9),
9629 "" : Item(status=' M', wc_rev=9),
9631 k_expected_disk.tweak('', props={SVN_PROP_MERGEINFO : '/A_COPY:2-9'})
9632 k_expected_disk.add({
9633 'D/G/tauprime' : Item(props={SVN_PROP_MERGEINFO: '/A/D/G/tau:2-7'},
9634 contents="This is the file 'tau'.\n")
9636 expected_skip = wc.State(short_A_path, {})
9637 saved_cwd = os.getcwd()
9638 os.chdir(svntest.main.work_dir)
9639 svntest.actions.run_and_verify_merge(short_A_path, None, None,
9640 sbox.repo_url + '/A_COPY',
9641 expected_output,
9642 k_expected_disk,
9643 k_expected_status,
9644 expected_skip,
9645 None, None, None, None,
9646 None, True, True,
9647 '--reintegrate')
9648 os.chdir(saved_cwd)
9650 # Finally, commit the result of the merge (r10).
9651 expected_output = wc.State(wc_dir, {
9652 'A/D/G/tauprime' : Item(verb='Adding'),
9653 'A/mu' : Item(verb='Sending'),
9654 'A' : Item(verb='Sending'),
9656 expected_status.add({
9657 'A/D/G/tauprime' : Item(status=' ', wc_rev=10),
9659 expected_status.tweak('A', 'A/mu', wc_rev=10)
9660 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
9661 expected_status, None, wc_dir)
9663 def reintegrate_branch_never_merged_to(sbox):
9664 "merge --reintegrate on a never-updated branch"
9666 # Make A_COPY branch in r2, and do a few more commits to A in r3-6.
9667 sbox.build()
9668 wc_dir = sbox.wc_dir
9669 expected_disk, expected_status = set_up_branch(sbox)
9671 # Make a change on the branch, to A/mu. Commit in r7.
9672 svntest.main.file_write(os.path.join(wc_dir, "A_COPY", "mu"),
9673 "Changed on the branch.")
9674 expected_output = wc.State(wc_dir, {'A_COPY/mu' : Item(verb='Sending')})
9675 expected_status.tweak('A_COPY/mu', wc_rev=7)
9676 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
9677 expected_status, None, wc_dir)
9678 expected_disk.tweak('A_COPY/mu', contents='Changed on the branch.')
9680 # Update the wcs.
9681 expected_output = wc.State(wc_dir, {})
9682 expected_status.tweak(wc_rev='7')
9683 svntest.actions.run_and_verify_update(wc_dir, expected_output,
9684 expected_disk, expected_status,
9685 None, None, None, None, None, True)
9687 # Make another change on the branch: copy tau to tauprime. Commit
9688 # in r8.
9689 svntest.actions.run_and_verify_svn(None, None, [], 'cp',
9690 os.path.join(wc_dir, 'A_COPY', 'D', 'G',
9691 'tau'),
9692 os.path.join(wc_dir, 'A_COPY', 'D', 'G',
9693 'tauprime'))
9694 expected_output = wc.State(wc_dir, {
9695 'A_COPY/D/G/tauprime' : Item(verb='Adding')
9697 expected_status.add({'A_COPY/D/G/tauprime': Item(status=' ', wc_rev=8)})
9698 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
9699 expected_status, None, wc_dir)
9700 expected_disk.add({
9701 'A_COPY/D/G/tauprime' : Item(props={SVN_PROP_MERGEINFO: ''},
9702 ### TODO(reint): why empty?
9703 contents="This is the file 'tau'.\n")
9706 # Update the trunk (well, the whole wc) (since reintegrate really
9707 # wants a clean wc).
9708 expected_output = wc.State(wc_dir, {})
9709 expected_status.tweak(wc_rev='8')
9710 svntest.actions.run_and_verify_update(wc_dir, expected_output,
9711 expected_disk, expected_status,
9712 None, None, None, None, None, True)
9714 # *finally*, actually run merge --reintegrate in trunk with the
9715 # branch URL. This should bring in the mu change and the tauprime
9716 # change.
9717 A_path = os.path.join(wc_dir, "A")
9718 short_A_path = shorten_path_kludge(A_path)
9719 expected_output = wc.State(short_A_path, {
9720 'mu' : Item(status='U '),
9721 'D/G/tauprime' : Item(status='A '),
9723 k_expected_status = wc.State(short_A_path, {
9724 "B" : Item(status=' ', wc_rev=8),
9725 "B/lambda" : Item(status=' ', wc_rev=8),
9726 "B/E" : Item(status=' ', wc_rev=8),
9727 "B/E/alpha" : Item(status=' ', wc_rev=8),
9728 "B/E/beta" : Item(status=' ', wc_rev=8),
9729 "B/F" : Item(status=' ', wc_rev=8),
9730 "mu" : Item(status='M ', wc_rev=8),
9731 "C" : Item(status=' ', wc_rev=8),
9732 "D" : Item(status=' ', wc_rev=8),
9733 "D/gamma" : Item(status=' ', wc_rev=8),
9734 "D/G" : Item(status=' ', wc_rev=8),
9735 "D/G/pi" : Item(status=' ', wc_rev=8),
9736 "D/G/rho" : Item(status=' ', wc_rev=8),
9737 "D/G/tau" : Item(status=' ', wc_rev=8),
9738 "D/G/tauprime" : Item(status='A ', wc_rev='-', copied='+'),
9739 "D/H" : Item(status=' ', wc_rev=8),
9740 "D/H/chi" : Item(status=' ', wc_rev=8),
9741 "D/H/omega" : Item(status=' ', wc_rev=8),
9742 "D/H/psi" : Item(status=' ', wc_rev=8),
9743 "" : Item(status=' M', wc_rev=8),
9745 k_expected_disk = wc.State('', {
9746 '' : Item(props={SVN_PROP_MERGEINFO : '/A_COPY:2-8'}),
9747 'B' : Item(),
9748 'B/lambda' : Item("This is the file 'lambda'.\n"),
9749 'B/E' : Item(),
9750 'B/E/alpha' : Item("This is the file 'alpha'.\n"),
9751 'B/E/beta' : Item("New content"),
9752 'B/F' : Item(),
9753 'mu' : Item("Changed on the branch."),
9754 'C' : Item(),
9755 'D' : Item(),
9756 'D/gamma' : Item("This is the file 'gamma'.\n"),
9757 'D/G' : Item(),
9758 'D/G/pi' : Item("This is the file 'pi'.\n"),
9759 'D/G/rho' : Item("New content"),
9760 'D/G/tau' : Item("This is the file 'tau'.\n"),
9761 'D/G/tauprime' : Item("This is the file 'tau'.\n",
9762 ### TODO(reint): why empty?
9763 props={SVN_PROP_MERGEINFO: ''}),
9764 'D/H' : Item(),
9765 'D/H/chi' : Item("This is the file 'chi'.\n"),
9766 'D/H/omega' : Item("New content"),
9767 'D/H/psi' : Item("New content"),
9769 expected_skip = wc.State(short_A_path, {})
9770 saved_cwd = os.getcwd()
9771 os.chdir(svntest.main.work_dir)
9772 svntest.actions.run_and_verify_merge(short_A_path, None, None,
9773 sbox.repo_url + '/A_COPY',
9774 expected_output,
9775 k_expected_disk,
9776 k_expected_status,
9777 expected_skip,
9778 None, None, None, None,
9779 None, True, True,
9780 '--reintegrate')
9781 os.chdir(saved_cwd)
9783 # Finally, commit the result of the merge (r9).
9784 expected_output = wc.State(wc_dir, {
9785 'A/D/G/tauprime' : Item(verb='Adding'),
9786 'A/mu' : Item(verb='Sending'),
9787 'A' : Item(verb='Sending'),
9789 expected_status.add({
9790 'A/D/G/tauprime' : Item(status=' ', wc_rev=9),
9792 expected_status.tweak('A', 'A/mu', wc_rev=9)
9793 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
9794 expected_status, None, wc_dir)
9796 def reintegrate_fail_on_modified_wc(sbox):
9797 "merge --reintegrate should fail in modified wc"
9798 sbox.build()
9799 wc_dir = sbox.wc_dir
9800 A_path = os.path.join(wc_dir, "A")
9801 mu_path = os.path.join(A_path, "mu")
9802 ignored_expected_disk, ignored_expected_status = set_up_branch(sbox)
9803 svntest.main.file_write(mu_path, "Changed on 'trunk' (the merge target).")
9804 svntest.actions.run_and_verify_merge(
9805 A_path, None, None, sbox.repo_url + '/A_COPY', None, None, None, None,
9806 ".*Cannot reintegrate into a working copy that has local modifications.*",
9807 None, None, None, None, True, False, '--reintegrate')
9809 def reintegrate_fail_on_mixed_rev_wc(sbox):
9810 "merge --reintegrate should fail in mixed-rev wc"
9811 sbox.build()
9812 wc_dir = sbox.wc_dir
9813 A_path = os.path.join(wc_dir, "A")
9814 mu_path = os.path.join(A_path, "mu")
9815 ignored_expected_disk, expected_status = set_up_branch(sbox)
9816 # Make and commit a change, in order to get a mixed-rev wc.
9817 svntest.main.file_write(mu_path, "Changed on 'trunk' (the merge target).")
9818 expected_output = wc.State(wc_dir, {
9819 'A/mu' : Item(verb='Sending'),
9821 expected_status.tweak('A/mu', wc_rev=7)
9822 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
9823 expected_status, None, wc_dir)
9824 # Try merging into that same wc, expecting failure.
9825 svntest.actions.run_and_verify_merge(
9826 A_path, None, None, sbox.repo_url + '/A_COPY', None, None, None, None,
9827 ".*Cannot reintegrate into mixed-revision working copy.*",
9828 None, None, None, None, True, False, '--reintegrate')
9830 def reintegrate_fail_on_switched_wc(sbox):
9831 "merge --reintegrate should fail in switched wc"
9832 sbox.build()
9833 wc_dir = sbox.wc_dir
9834 A_path = os.path.join(wc_dir, "A")
9835 G_path = os.path.join(A_path, "D", "G")
9836 switch_url = sbox.repo_url + "/A/D/H"
9837 expected_disk, expected_status = set_up_branch(sbox)
9839 # Switch a subdir of the target.
9840 expected_output = svntest.wc.State(wc_dir, {
9841 'A/D/G/pi' : Item(status='D '),
9842 'A/D/G/rho' : Item(status='D '),
9843 'A/D/G/tau' : Item(status='D '),
9844 'A/D/G/chi' : Item(status='A '),
9845 'A/D/G/psi' : Item(status='A '),
9846 'A/D/G/omega' : Item(status='A '),
9848 expected_disk.remove('A/D/G/pi', 'A/D/G/rho', 'A/D/G/tau')
9849 expected_disk.add({
9850 'A/D/G/chi' : Item(contents="This is the file 'chi'.\n"),
9851 'A/D/G/psi' : Item(contents="New content"),
9852 'A/D/G/omega' : Item(contents="New content"),
9854 expected_status.remove('A/D/G/pi', 'A/D/G/rho', 'A/D/G/tau')
9855 expected_status.add({
9856 'A/D/G' : Item(status=' ', wc_rev=6, switched='S'),
9857 'A/D/G/chi' : Item(status=' ', wc_rev=6),
9858 'A/D/G/psi' : Item(status=' ', wc_rev=6),
9859 'A/D/G/omega' : Item(status=' ', wc_rev=6),
9861 svntest.actions.run_and_verify_switch(wc_dir,
9862 G_path,
9863 switch_url,
9864 expected_output,
9865 expected_disk,
9866 expected_status,
9867 None, None, None, None, False);
9868 svntest.actions.run_and_verify_merge(
9869 A_path, None, None, sbox.repo_url + '/A_COPY', None, None, None, None,
9870 ".*Cannot reintegrate into a working copy with a switched subtree.*",
9871 None, None, None, None, True, False, '--reintegrate')
9873 def reintegrate_fail_on_shallow_wc(sbox):
9874 "merge --reintegrate should fail in shallow wc"
9875 sbox.build()
9876 wc_dir = sbox.wc_dir
9877 expected_disk, expected_status = set_up_branch(sbox)
9878 A_path = os.path.join(wc_dir, "A")
9879 G_path = os.path.join(A_path, "D", "G")
9880 # Our default checkout doesn't have any subdirs at non-infinite
9881 # depth, so we'll have to create one the old-fashioned way: remove a
9882 # tree, then "update" it back into existence at a shallower depth.
9883 svntest.main.safe_rmtree(G_path)
9884 svntest.actions.run_and_verify_svn(None, None, [], 'update', G_path,
9885 '--depth=files')
9886 # Even though everything is actually present (as G has no subdirs
9887 # anyway), the reintegration should fail, because G's depth is other
9888 # than infinity.
9889 svntest.actions.run_and_verify_merge(
9890 A_path, None, None, sbox.repo_url + '/A_COPY', None, None, None, None,
9891 ".*Cannot reintegrate into a working copy not.*at infinite depth.*",
9892 None, None, None, None, True, False, '--reintegrate')
9894 def reintegrate_fail_on_stale_source(sbox):
9895 "merge --reintegrate should fail on stale source"
9896 sbox.build()
9897 wc_dir = sbox.wc_dir
9898 expected_disk, expected_status = set_up_branch(sbox)
9899 A_path = os.path.join(wc_dir, "A")
9900 mu_path = os.path.join(A_path, "mu")
9901 svntest.main.file_append(mu_path, 'some text appended to mu\n')
9902 svntest.actions.run_and_verify_svn(None, None, [], 'commit',
9903 '-m', 'a change to mu', mu_path);
9904 # Unmix the revisions in the working copy.
9905 svntest.actions.run_and_verify_svn(None, None, [], 'update', wc_dir);
9906 # The merge --reintegrate should fail because target has changes not
9907 # present in source.
9908 svntest.actions.run_and_verify_merge(
9909 A_path, None, None, sbox.repo_url + '/A_COPY', None, None, None, None,
9910 ".*", ###TODO(reint): need a more specific check here
9911 None, None, None, None, True, False, '--reintegrate')
9913 def dont_add_mergeinfo_from_own_history(sbox):
9914 "cyclic merges dont add mergeinfo from own history"
9916 sbox.build()
9917 wc_dir = sbox.wc_dir
9918 wc_disk, wc_status = set_up_branch(sbox)
9920 # Some paths we'll care about
9921 A_path = os.path.join(wc_dir, "A")
9922 A_MOVED_path = os.path.join(wc_dir, "A_MOVED")
9923 mu_path = os.path.join(wc_dir, "A", "mu")
9924 mu_MOVED_path = os.path.join(wc_dir, "A_MOVED", "mu")
9925 A_COPY_path = os.path.join(wc_dir, "A_COPY")
9926 mu_COPY_path = os.path.join(wc_dir, "A_COPY", "mu")
9928 # Merge r5 from 'A' to 'A_COPY' and commit as r7. This creates mergeinfo
9929 # of '/A:5' on 'A_COPY'. Then merge r7 from 'A_COPY' to 'A'. This attempts
9930 # to add the mergeinfo '/A:5' to 'A', but since this revision already exists
9931 # in 'A's history it should be filtered out. In addition, as there is no
9932 # other operative change to 'A', the merge should be a no-op.
9934 # Search for the comment entitled "The Merge Kluge" elsewhere in
9935 # this file, to understand why we shorten and chdir() below.
9936 short_A_COPY_path = shorten_path_kludge(A_COPY_path)
9937 expected_output = wc.State(short_A_COPY_path, {
9938 'B/E/beta' : Item(status='U '),
9940 expected_A_COPY_status = wc.State(short_A_COPY_path, {
9941 '' : Item(status=' M', wc_rev=2),
9942 'B' : Item(status=' ', wc_rev=2),
9943 'mu' : Item(status=' ', wc_rev=2),
9944 'B/E' : Item(status=' ', wc_rev=2),
9945 'B/E/alpha' : Item(status=' ', wc_rev=2),
9946 'B/E/beta' : Item(status='M ', wc_rev=2),
9947 'B/lambda' : Item(status=' ', wc_rev=2),
9948 'B/F' : Item(status=' ', wc_rev=2),
9949 'C' : Item(status=' ', wc_rev=2),
9950 'D' : Item(status=' ', wc_rev=2),
9951 'D/G' : Item(status=' ', wc_rev=2),
9952 'D/G/pi' : Item(status=' ', wc_rev=2),
9953 'D/G/rho' : Item(status=' ', wc_rev=2),
9954 'D/G/tau' : Item(status=' ', wc_rev=2),
9955 'D/gamma' : Item(status=' ', wc_rev=2),
9956 'D/H' : Item(status=' ', wc_rev=2),
9957 'D/H/chi' : Item(status=' ', wc_rev=2),
9958 'D/H/psi' : Item(status=' ', wc_rev=2),
9959 'D/H/omega' : Item(status=' ', wc_rev=2),
9961 expected_A_COPY_disk = wc.State('', {
9962 '' : Item(props={SVN_PROP_MERGEINFO : '/A:5'}),
9963 'B' : Item(),
9964 'mu' : Item("This is the file 'mu'.\n"),
9965 'B/E' : Item(),
9966 'B/E/alpha' : Item("This is the file 'alpha'.\n"),
9967 'B/E/beta' : Item("New content"),
9968 'B/lambda' : Item("This is the file 'lambda'.\n"),
9969 'B/F' : Item(),
9970 'C' : Item(),
9971 'D' : Item(),
9972 'D/G' : Item(),
9973 'D/G/pi' : Item("This is the file 'pi'.\n"),
9974 'D/G/rho' : Item("This is the file 'rho'.\n"),
9975 'D/G/tau' : Item("This is the file 'tau'.\n"),
9976 'D/gamma' : Item("This is the file 'gamma'.\n"),
9977 'D/H' : Item(),
9978 'D/H/chi' : Item("This is the file 'chi'.\n"),
9979 'D/H/psi' : Item("This is the file 'psi'.\n"),
9980 'D/H/omega' : Item("This is the file 'omega'.\n"),
9982 expected_A_COPY_skip = wc.State(short_A_COPY_path, { })
9983 saved_cwd = os.getcwd()
9984 os.chdir(svntest.main.work_dir)
9985 svntest.actions.run_and_verify_merge(short_A_COPY_path, '4', '5',
9986 sbox.repo_url + \
9987 '/A',
9988 expected_output,
9989 expected_A_COPY_disk,
9990 expected_A_COPY_status,
9991 expected_A_COPY_skip,
9992 None, None, None, None,
9993 None, 1)
9994 os.chdir(saved_cwd)
9996 # Commit the merge
9997 expected_output = wc.State(wc_dir, {
9998 'A_COPY' : Item(verb='Sending'),
9999 'A_COPY/B/E/beta' : Item(verb='Sending'),
10001 wc_status.tweak('A_COPY', 'A_COPY/B/E/beta', wc_rev=7)
10002 svntest.actions.run_and_verify_commit(wc_dir,
10003 expected_output,
10004 wc_status,
10005 None,
10006 wc_dir)
10008 # Merge r7 back to the original source.
10009 os.chdir(svntest.main.work_dir)
10010 short_A_path = shorten_path_kludge(A_path)
10011 svntest.actions.run_and_verify_svn(None, [], [], 'merge', '-c', '7',
10012 sbox.repo_url + '/A_COPY',
10013 short_A_path)
10014 os.chdir(saved_cwd)
10016 # Now merge r3 from 'A' to 'A_COPY', make a text mod to 'A_COPY/mu' and
10017 # commit both as r8. This results in mergeinfo of '/A:3,5' on 'A_COPY'.
10018 # Then merge r8 from 'A_COPY' to 'A'. This attempts to add the mergeinfo
10019 # '/A:3' to 'A', which is again filtered out, but the change to 'A/mu'
10020 # makes the merge operative, so 'A' should get the mergeinfo '/A_COPY:8'.
10021 os.chdir(svntest.main.work_dir)
10022 expected_output = wc.State(short_A_COPY_path, {
10023 'D/H/psi' : Item(status='U '),
10025 expected_A_COPY_status.tweak('B/E/beta', status=' ', wc_rev=7)
10026 expected_A_COPY_status.tweak('', status=' M', wc_rev=7)
10027 expected_A_COPY_status.tweak('D/H/psi', status='M ')
10028 expected_A_COPY_disk.tweak('D/H/psi', contents='New content')
10029 expected_A_COPY_disk.tweak('', props={SVN_PROP_MERGEINFO : '/A:3,5'})
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 r8
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=8)
10051 svntest.actions.run_and_verify_commit(wc_dir,
10052 expected_output,
10053 wc_status,
10054 None,
10055 wc_dir)
10057 # Merge r8 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:8'}),
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, '7', '8',
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 r8 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:8'.
10128 svntest.actions.run_and_verify_svn(None,
10129 ['\n', 'Committed revision 9.\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=9, 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', 'A_COPY/B/E/beta',
10186 contents='New content')
10187 wc_disk.tweak('A_COPY', props={SVN_PROP_MERGEINFO : '/A:3,5'})
10188 expected_output = wc.State(wc_dir, {
10189 'A' : Item(status='D '),
10190 'A_MOVED' : Item(status='A '),
10191 'A_MOVED/B' : Item(status='A '),
10192 'A_MOVED/B/lambda' : Item(status='A '),
10193 'A_MOVED/B/E' : Item(status='A '),
10194 'A_MOVED/B/E/alpha' : Item(status='A '),
10195 'A_MOVED/B/E/beta' : Item(status='A '),
10196 'A_MOVED/B/F' : Item(status='A '),
10197 'A_MOVED/mu' : Item(status='A '),
10198 'A_MOVED/C' : Item(status='A '),
10199 'A_MOVED/D' : Item(status='A '),
10200 'A_MOVED/D/gamma' : Item(status='A '),
10201 'A_MOVED/D/G' : Item(status='A '),
10202 'A_MOVED/D/G/pi' : Item(status='A '),
10203 'A_MOVED/D/G/rho' : Item(status='A '),
10204 'A_MOVED/D/G/tau' : Item(status='A '),
10205 'A_MOVED/D/H' : Item(status='A '),
10206 'A_MOVED/D/H/chi' : Item(status='A '),
10207 'A_MOVED/D/H/omega' : Item(status='A '),
10208 'A_MOVED/D/H/psi' : Item(status='A ')
10210 svntest.actions.run_and_verify_update(wc_dir,
10211 expected_output,
10212 wc_disk,
10213 wc_status,
10214 None, None, None, None, None,
10215 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=9),
10222 'B' : Item(status=' ', wc_rev=9),
10223 'mu' : Item(status='M ', wc_rev=9),
10224 'B/E' : Item(status=' ', wc_rev=9),
10225 'B/E/alpha' : Item(status=' ', wc_rev=9),
10226 'B/E/beta' : Item(status=' ', wc_rev=9),
10227 'B/lambda' : Item(status=' ', wc_rev=9),
10228 'B/F' : Item(status=' ', wc_rev=9),
10229 'C' : Item(status=' ', wc_rev=9),
10230 'D' : Item(status=' ', wc_rev=9),
10231 'D/G' : Item(status=' ', wc_rev=9),
10232 'D/G/pi' : Item(status=' ', wc_rev=9),
10233 'D/G/rho' : Item(status=' ', wc_rev=9),
10234 'D/G/tau' : Item(status=' ', wc_rev=9),
10235 'D/gamma' : Item(status=' ', wc_rev=9),
10236 'D/H' : Item(status=' ', wc_rev=9),
10237 'D/H/chi' : Item(status=' ', wc_rev=9),
10238 'D/H/psi' : Item(status=' ', wc_rev=9),
10239 'D/H/omega' : Item(status=' ', wc_rev=9),
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, '7', '8',
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 # r8 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:8'
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 r10
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=9)
10338 wc_status.add({
10339 'A' : Item(wc_rev=10),
10340 'A/B' : Item(wc_rev=10),
10341 'A/B/lambda' : Item(wc_rev=10),
10342 'A/B/E' : Item(wc_rev=10),
10343 'A/B/E/alpha' : Item(wc_rev=10),
10344 'A/B/E/beta' : Item(wc_rev=10),
10345 'A/B/F' : Item(wc_rev=10),
10346 'A/mu' : Item(wc_rev=10),
10347 'A/C' : Item(wc_rev=10),
10348 'A/D' : Item(wc_rev=10),
10349 'A/D/gamma' : Item(wc_rev=10),
10350 'A/D/G' : Item(wc_rev=10),
10351 'A/D/G/pi' : Item(wc_rev=10),
10352 'A/D/G/rho' : Item(wc_rev=10),
10353 'A/D/G/tau' : Item(wc_rev=10),
10354 'A/D/H' : Item(wc_rev=10),
10355 'A/D/H/chi' : Item(wc_rev=10),
10356 'A/D/H/omega' : Item(wc_rev=10),
10357 'A/D/H/psi' : Item(wc_rev=10),
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=10),
10373 'B' : Item(status=' ', wc_rev=10),
10374 'mu' : Item(status='M ', wc_rev=10),
10375 'B/E' : Item(status=' ', wc_rev=10),
10376 'B/E/alpha' : Item(status=' ', wc_rev=10),
10377 'B/E/beta' : Item(status=' ', wc_rev=10),
10378 'B/lambda' : Item(status=' ', wc_rev=10),
10379 'B/F' : Item(status=' ', wc_rev=10),
10380 'C' : Item(status=' ', wc_rev=10),
10381 'D' : Item(status=' ', wc_rev=10),
10382 'D/G' : Item(status=' ', wc_rev=10),
10383 'D/G/pi' : Item(status=' ', wc_rev=10),
10384 'D/G/rho' : Item(status=' ', wc_rev=10),
10385 'D/G/tau' : Item(status=' ', wc_rev=10),
10386 'D/gamma' : Item(status=' ', wc_rev=10),
10387 'D/H' : Item(status=' ', wc_rev=10),
10388 'D/H/chi' : Item(status=' ', wc_rev=10),
10389 'D/H/psi' : Item(status='M ', wc_rev=10),
10390 'D/H/omega' : Item(status=' ', wc_rev=10),
10392 expected_A_disk = wc.State('', {
10393 '' : Item(props={SVN_PROP_MERGEINFO : '/A:3\n/A_COPY:8\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, '7', '8',
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)
10424 os.chdir(saved_cwd)
10426 ########################################################################
10427 # Run the tests
10430 # list all tests here, starting with None:
10431 test_list = [ None,
10432 textual_merges_galore,
10433 add_with_history,
10434 delete_file_and_dir,
10435 simple_property_merges,
10436 merge_with_implicit_target_using_r,
10437 merge_with_implicit_target_using_c,
10438 merge_with_implicit_target_and_revs,
10439 merge_catches_nonexistent_target,
10440 merge_tree_deleted_in_target,
10441 merge_similar_unrelated_trees,
10442 merge_with_prev,
10443 merge_binary_file,
10444 three_way_merge_add_of_existing_binary_file,
10445 merge_one_file_using_r,
10446 merge_one_file_using_c,
10447 merge_one_file_using_implicit_revs,
10448 merge_record_only,
10449 merge_in_new_file_and_diff,
10450 merge_skips_obstructions,
10451 merge_into_missing,
10452 dry_run_adds_file_with_prop,
10453 merge_binary_with_common_ancestry,
10454 merge_funny_chars_on_path,
10455 merge_keyword_expansions,
10456 merge_prop_change_to_deleted_target,
10457 merge_file_with_space_in_its_name,
10458 merge_dir_branches,
10459 safe_property_merge,
10460 property_merge_from_branch,
10461 property_merge_undo_redo,
10462 cherry_pick_text_conflict,
10463 merge_file_replace,
10464 merge_dir_replace,
10465 XFail(merge_dir_and_file_replace),
10466 merge_file_replace_to_mixed_rev_wc,
10467 merge_added_dir_to_deleted_in_target,
10468 merge_ignore_whitespace,
10469 merge_ignore_eolstyle,
10470 merge_add_over_versioned_file_conflicts,
10471 merge_conflict_markers_matching_eol,
10472 merge_eolstyle_handling,
10473 avoid_repeated_merge_using_inherited_merge_info,
10474 avoid_repeated_merge_on_subtree_with_merge_info,
10475 obey_reporter_api_semantics_while_doing_subtree_merges,
10476 SkipUnless(mergeinfo_inheritance,
10477 server_has_mergeinfo),
10478 mergeinfo_elision,
10479 mergeinfo_inheritance_and_discontinuous_ranges,
10480 SkipUnless(merge_to_target_with_copied_children,
10481 server_has_mergeinfo),
10482 merge_to_switched_path,
10483 SkipUnless(merge_to_path_with_switched_children,
10484 server_has_mergeinfo),
10485 merge_with_implicit_target_file,
10486 SkipUnless(empty_mergeinfo,
10487 server_has_mergeinfo),
10488 prop_add_to_child_with_mergeinfo,
10489 diff_repos_does_not_update_mergeinfo,
10490 XFail(avoid_reflected_revs),
10491 update_loses_mergeinfo,
10492 XFail(merge_loses_mergeinfo),
10493 single_file_replace_style_merge_capability,
10494 merge_to_out_of_date_target,
10495 merge_with_depth_files,
10496 merge_fails_if_subtree_is_deleted_on_src,
10497 no_mergeinfo_from_no_op_merge,
10498 merge_to_sparse_directories,
10499 merge_old_and_new_revs_from_renamed_dir,
10500 merge_with_child_having_different_rev_ranges_to_merge,
10501 merge_old_and_new_revs_from_renamed_file,
10502 merge_with_auto_rev_range_detection,
10503 mergeinfo_recording_in_skipped_merge,
10504 cherry_picking,
10505 propchange_of_subdir_raises_conflict,
10506 reverse_merge_prop_add_on_child,
10507 XFail(merge_target_with_non_inheritable_mergeinfo),
10508 self_reverse_merge,
10509 ignore_ancestry_and_mergeinfo,
10510 merge_from_renamed_branch_fails_while_avoiding_repeat_merge,
10511 merge_source_normalization_and_subtree_merges,
10512 new_subtrees_should_not_break_merge,
10513 basic_reintegrate,
10514 reintegrate_branch_never_merged_to,
10515 reintegrate_fail_on_modified_wc,
10516 reintegrate_fail_on_mixed_rev_wc,
10517 reintegrate_fail_on_switched_wc,
10518 reintegrate_fail_on_shallow_wc,
10519 XFail(reintegrate_fail_on_stale_source),
10520 dont_add_mergeinfo_from_own_history,
10523 if __name__ == '__main__':
10524 svntest.main.run_tests(test_list)
10525 # NOTREACHED
10528 ### End of file.