1#!/usr/bin/env python
2#
3#  merge_tests.py:  testing merge
4#
5#  Subversion is a tool for revision control.
6#  See http://subversion.apache.org for more information.
7#
8# ====================================================================
9#    Licensed to the Apache Software Foundation (ASF) under one
10#    or more contributor license agreements.  See the NOTICE file
11#    distributed with this work for additional information
12#    regarding copyright ownership.  The ASF licenses this file
13#    to you under the Apache License, Version 2.0 (the
14#    "License"); you may not use this file except in compliance
15#    with the License.  You may obtain a copy of the License at
16#
17#      http://www.apache.org/licenses/LICENSE-2.0
18#
19#    Unless required by applicable law or agreed to in writing,
20#    software distributed under the License is distributed on an
21#    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
22#    KIND, either express or implied.  See the License for the
23#    specific language governing permissions and limitations
24#    under the License.
25######################################################################
26
27# General modules
28import shutil, sys, re, os
29import time
30
31# Our testing module
32import svntest
33from svntest import main, wc, verify, actions
34
35from prop_tests import binary_mime_type_on_text_file_warning
36
37# (abbreviation)
38Item = wc.StateItem
39Skip = svntest.testcase.Skip_deco
40SkipUnless = svntest.testcase.SkipUnless_deco
41XFail = svntest.testcase.XFail_deco
42Issues = svntest.testcase.Issues_deco
43Issue = svntest.testcase.Issue_deco
44Wimp = svntest.testcase.Wimp_deco
45exp_noop_up_out = svntest.actions.expected_noop_update_output
46
47from svntest.main import SVN_PROP_MERGEINFO
48from svntest.main import server_has_mergeinfo
49from svntest.actions import fill_file_with_lines
50from svntest.actions import make_conflict_marker_text
51from svntest.actions import inject_conflict_into_expected_state
52from svntest.verify import RegexListOutput
53
54from svntest.mergetrees import expected_merge_output, \
55                               check_mergeinfo_recursively, \
56                               set_up_dir_replace, \
57                               set_up_branch, \
58                               local_path, \
59                               svn_mkfile, \
60                               svn_modfile, \
61                               svn_copy, \
62                               svn_merge, \
63                               noninheritable_mergeinfo_test_set_up
64
65######################################################################
66# Tests
67#
68#   Each test must return on success or raise on failure.
69
70#----------------------------------------------------------------------
71@SkipUnless(server_has_mergeinfo)
72def textual_merges_galore(sbox):
73  "performing a merge, with mixed results"
74
75  ## The Plan:
76  ##
77  ## The goal is to test that "svn merge" does the right thing in the
78  ## following cases:
79  ##
80  ##   1 : _ :  Received changes already present in unmodified local file
81  ##   2 : U :  No local mods, received changes folded in without trouble
82  ##   3 : G :  Received changes already exist as local mods
83  ##   4 : G :  Received changes do not conflict with local mods
84  ##   5 : C :  Received changes conflict with local mods
85  ##
86  ## So first modify these files and commit:
87  ##
88  ##    Revision 2:
89  ##    -----------
90  ##    A/mu ............... add ten or so lines
91  ##    A/D/G/rho .......... add ten or so lines
92  ##
93  ## Now check out an "other" working copy, from revision 2.
94  ##
95  ## Next further modify and commit some files from the original
96  ## working copy:
97  ##
98  ##    Revision 3:
99  ##    -----------
100  ##    A/B/lambda ......... add ten or so lines
101  ##    A/D/G/pi ........... add ten or so lines
102  ##    A/D/G/tau .......... add ten or so lines
103  ##    A/D/G/rho .......... add an additional ten or so lines
104  ##
105  ## In the other working copy (which is at rev 2), update rho back
106  ## to revision 1, while giving other files local mods.  This sets
107  ## things up so that "svn merge -r 1:3" will test all of the above
108  ## cases except case 4:
109  ##
110  ##    case 1: A/mu .......... do nothing, the only change was in rev 2
111  ##    case 2: A/B/lambda .... do nothing, so we accept the merge easily
112  ##    case 3: A/D/G/pi ...... add same ten lines as committed in rev 3
113  ##    case 5: A/D/G/tau ..... add ten or so lines at the end
114  ##    [none]: A/D/G/rho ..... ignore what happens to this file for now
115  ##
116  ## Now run
117  ##
118  ##    $ cd wc.other
119  ##    $ svn merge -r 1:3 url-to-repo
120  ##
121  ## ...and expect the right output.
122  ##
123  ## Now revert rho, then update it to revision 2, then *prepend* a
124  ## bunch of lines, which will be separated by enough distance from
125  ## the changes about to be received that the merge will be clean.
126  ##
127  ##    $ cd wc.other/A/D/G
128  ##    $ svn merge -r 2:3 url-to-repo/A/D/G
129  ##
130  ## Which tests case 4.  (Ignore the changes to the other files,
131  ## we're only interested in rho here.)
132
133  sbox.build()
134  wc_dir = sbox.wc_dir
135  #  url = os.path.join(svntest.main.test_area_url, sbox.repo_dir)
136
137  # Change mu and rho for revision 2
138  mu_path = sbox.ospath('A/mu')
139  rho_path = sbox.ospath('A/D/G/rho')
140  mu_text = fill_file_with_lines(mu_path, 2)
141  rho_text = fill_file_with_lines(rho_path, 2)
142
143  # Create expected output tree for initial commit
144  expected_output = wc.State(wc_dir, {
145    'A/mu' : Item(verb='Sending'),
146    'A/D/G/rho' : Item(verb='Sending'),
147    })
148
149  # Create expected status tree; all local revisions should be at 1,
150  # but mu and rho should be at revision 2.
151  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
152  expected_status.tweak('A/mu', 'A/D/G/rho', wc_rev=2)
153
154  # Initial commit.
155  svntest.actions.run_and_verify_commit(wc_dir,
156                                        expected_output,
157                                        expected_status)
158
159  # Make the "other" working copy
160  other_wc = sbox.add_wc_path('other')
161  svntest.actions.duplicate_dir(wc_dir, other_wc)
162
163  # Now commit some more mods from the original working copy, to
164  # produce revision 3.
165  lambda_path = sbox.ospath('A/B/lambda')
166  pi_path = sbox.ospath('A/D/G/pi')
167  tau_path = sbox.ospath('A/D/G/tau')
168
169  lambda_text = fill_file_with_lines(lambda_path, 2)
170  pi_text = fill_file_with_lines(pi_path, 2)
171  tau_text = fill_file_with_lines(tau_path, 2)
172  additional_rho_text = fill_file_with_lines(rho_path, 2)
173
174  # Created expected output tree for 'svn ci'
175  expected_output = wc.State(wc_dir, {
176    'A/B/lambda' : Item(verb='Sending'),
177    'A/D/G/pi' : Item(verb='Sending'),
178    'A/D/G/tau' : Item(verb='Sending'),
179    'A/D/G/rho' : Item(verb='Sending'),
180    })
181
182  # Create expected status tree.
183  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
184  expected_status.tweak('A/mu', wc_rev=2)
185  expected_status.tweak('A/B/lambda', 'A/D/G/pi', 'A/D/G/tau', 'A/D/G/rho',
186                        wc_rev=3)
187
188  # Commit revision 3.
189  svntest.actions.run_and_verify_commit(wc_dir,
190                                        expected_output,
191                                        expected_status)
192
193  # Make local mods in wc.other
194  other_pi_path = os.path.join(other_wc, 'A', 'D', 'G', 'pi')
195  other_rho_path = os.path.join(other_wc, 'A', 'D', 'G', 'rho')
196  other_tau_path = os.path.join(other_wc, 'A', 'D', 'G', 'tau')
197
198  # For A/mu and A/B/lambda, we do nothing.  For A/D/G/pi, we add the
199  # same ten lines as were already committed in revision 3.
200  # (Remember, wc.other is only at revision 2, so it doesn't have
201  # these changes.)
202  svntest.main.file_append(other_pi_path, pi_text)
203
204  # We skip A/D/G/rho in this merge; it will be tested with a separate
205  # merge command.  Temporarily put it back to revision 1, so this
206  # merge succeeds cleanly.
207  svntest.actions.run_and_verify_svn(None, [],
208                                     'up', '-r', '1', other_rho_path)
209
210  # For A/D/G/tau, we append few different lines, to conflict with the
211  # few lines appended in revision 3.
212  other_tau_text = fill_file_with_lines(other_tau_path, 2,
213                                        line_descrip="Conflicting line")
214
215  # Do the first merge, revs 1:3.  This tests all the cases except
216  # case 4, which we'll handle in a second pass.
217  expected_output = wc.State(other_wc, {'A/B/lambda' : Item(status='U '),
218                                        'A/D/G/rho'  : Item(status='U '),
219                                        'A/D/G/tau'  : Item(status='C '),
220                                        })
221  expected_mergeinfo_output = wc.State(other_wc, {''  : Item(status=' U')})
222  expected_elision_output = wc.State(other_wc, {})
223  expected_disk = svntest.main.greek_state.copy()
224  expected_disk.tweak('A/mu',
225                      contents=expected_disk.desc['A/mu'].contents
226                      + mu_text)
227  expected_disk.tweak('A/B/lambda',
228                      contents=expected_disk.desc['A/B/lambda'].contents
229                      + lambda_text)
230  expected_disk.tweak('A/D/G/rho',
231                      contents=expected_disk.desc['A/D/G/rho'].contents
232                      + rho_text + additional_rho_text)
233  expected_disk.tweak('A/D/G/pi',
234                      contents=expected_disk.desc['A/D/G/pi'].contents
235                      + pi_text)
236
237  expected_status = svntest.actions.get_virginal_state(other_wc, 1)
238  expected_status.tweak('', status=' M')
239  expected_status.tweak('A/mu', wc_rev=2)
240  expected_status.tweak('A/B/lambda', status='M ')
241  expected_status.tweak('A/D/G/pi', status='M ')
242  expected_status.tweak('A/D/G/rho', status='M ')
243
244  inject_conflict_into_expected_state('A/D/G/tau', expected_disk,
245                                      expected_status, other_tau_text, tau_text,
246                                      1, 3)
247
248  expected_skip = wc.State('', { })
249
250  tau_conflict_support_files = ["tau\.working",
251                                "tau\.merge-right\.r3",
252                                "tau\.merge-left\.r1"]
253
254  svntest.actions.run_and_verify_merge(other_wc, '1', '3',
255                                       sbox.repo_url, None,
256                                       expected_output,
257                                       expected_mergeinfo_output,
258                                       expected_elision_output,
259                                       expected_disk,
260                                       expected_status,
261                                       expected_skip,
262                                       [], False, True,
263                                       '--allow-mixed-revisions', other_wc,
264                                       extra_files=list(tau_conflict_support_files))
265
266  # Now reverse merge r3 into A/D/G/rho, give it non-conflicting local
267  # mods, then merge in the 2:3 change.  ### Not bothering to do the
268  # whole expected_foo routine for these intermediate operations;
269  # they're not what we're here to test, after all, so it's enough to
270  # know that they worked.  Is this a bad practice? ###
271  #
272  # run_and_verify_merge doesn't support merging to a file WCPATH
273  # so use run_and_verify_svn.
274  ### TODO: We can use run_and_verify_merge() here now.
275  svntest.actions.run_and_verify_svn(
276    expected_merge_output([[-3]],
277                          ['G    ' + other_rho_path + '\n',
278                           ' G   ' + other_rho_path + '\n',]),
279    [], 'merge', '-c-3',
280    sbox.repo_url + '/A/D/G/rho',
281    other_rho_path)
282
283  # Now *prepend* ten or so lines to A/D/G/rho.  Since rho had ten
284  # lines appended in revision 2, and then another ten in revision 3,
285  # these new local mods will be separated from the rev 3 changes by
286  # enough distance that they won't conflict, so the merge should be
287  # clean.
288  other_rho_text = ""
289  for x in range(1,10):
290    other_rho_text = other_rho_text + 'Unobtrusive line ' + repr(x) + ' in rho\n'
291  current_other_rho_text = open(other_rho_path).read()
292  svntest.main.file_write(other_rho_path,
293                          other_rho_text + current_other_rho_text)
294
295  # We expect no merge attempt for pi and tau because they inherit
296  # mergeinfo from the WC root.  There is explicit mergeinfo on rho
297  # ('/A/D/G/rho:2') so expect it to be merged (cleanly).
298  G_path = os.path.join(other_wc, 'A', 'D', 'G')
299  expected_output = wc.State(os.path.join(other_wc, 'A', 'D', 'G'),
300                             {'rho' : Item(status='G ')})
301  expected_mergeinfo_output = wc.State(G_path, {
302    ''    : Item(status=' G'),
303    'rho' : Item(status=' G')
304    })
305  expected_elision_output = wc.State(G_path, {
306    ''    : Item(status=' U'),
307    'rho' : Item(status=' U')
308    })
309  expected_disk = wc.State("", {
310    'pi'    : Item("This is the file 'pi'.\n"),
311    'rho'   : Item("This is the file 'rho'.\n"),
312    'tau'   : Item("This is the file 'tau'.\n"),
313    })
314  expected_disk.tweak('rho',
315                      contents=other_rho_text
316                      + expected_disk.desc['rho'].contents
317                      + rho_text
318                      + additional_rho_text)
319  expected_disk.tweak('pi',
320                      contents=expected_disk.desc['pi'].contents
321                      + pi_text)
322
323  expected_status = wc.State(os.path.join(other_wc, 'A', 'D', 'G'),
324                             { ''     : Item(wc_rev=1, status='  '),
325                               'rho'  : Item(wc_rev=1, status='M '),
326                               'pi'   : Item(wc_rev=1, status='M '),
327                               'tau'  : Item(wc_rev=1, status='C '),
328                               })
329
330  inject_conflict_into_expected_state('tau', expected_disk, expected_status,
331                                      other_tau_text, tau_text, 1, 3)
332
333  # Do the merge, but check svn:mergeinfo props separately since
334  # run_and_verify_merge would attempt to proplist tau's conflict
335  # files if we asked it to check props.
336  svntest.actions.run_and_verify_merge(
337    os.path.join(other_wc, 'A', 'D', 'G'),
338    '2', '3',
339    sbox.repo_url + '/A/D/G', None,
340    expected_output,
341    expected_mergeinfo_output,
342    expected_elision_output,
343    expected_disk,
344    expected_status,
345    expected_skip,
346    extra_files=list(tau_conflict_support_files))
347
348
349  svntest.actions.run_and_verify_svn([], '.*W200017: Property.*not found',
350                                     'propget', SVN_PROP_MERGEINFO,
351                                     os.path.join(other_wc,
352                                                  "A", "D", "G", "rho"))
353
354
355#----------------------------------------------------------------------
356# Merge should copy-with-history when adding files or directories
357@SkipUnless(server_has_mergeinfo)
358def add_with_history(sbox):
359  "merge and add new files/dirs with history"
360
361  sbox.build()
362  wc_dir = sbox.wc_dir
363
364  C_path = sbox.ospath('A/C')
365  F_path = sbox.ospath('A/B/F')
366  F_url = sbox.repo_url + '/A/B/F'
367
368  Q_path = os.path.join(F_path, 'Q')
369  Q2_path = os.path.join(F_path, 'Q2')
370  foo_path = os.path.join(F_path, 'foo')
371  foo2_path = os.path.join(F_path, 'foo2')
372  bar_path = os.path.join(F_path, 'Q', 'bar')
373  bar2_path = os.path.join(F_path, 'Q', 'bar2')
374
375  svntest.main.run_svn(None, 'mkdir', Q_path)
376  svntest.main.run_svn(None, 'mkdir', Q2_path)
377  svntest.main.file_append(foo_path, "foo")
378  svntest.main.file_append(foo2_path, "foo2")
379  svntest.main.file_append(bar_path, "bar")
380  svntest.main.file_append(bar2_path, "bar2")
381  svntest.main.run_svn(None, 'add', foo_path, foo2_path, bar_path, bar2_path)
382  svntest.main.run_svn(None, 'propset', 'x', 'x', Q2_path)
383  svntest.main.run_svn(None, 'propset', 'y', 'y', foo2_path)
384  svntest.main.run_svn(None, 'propset', 'z', 'z', bar2_path)
385
386  expected_output = wc.State(wc_dir, {
387    'A/B/F/Q'     : Item(verb='Adding'),
388    'A/B/F/Q2'    : Item(verb='Adding'),
389    'A/B/F/Q/bar' : Item(verb='Adding'),
390    'A/B/F/Q/bar2': Item(verb='Adding'),
391    'A/B/F/foo'   : Item(verb='Adding'),
392    'A/B/F/foo2'  : Item(verb='Adding'),
393    })
394  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
395  expected_status.add({
396    'A/B/F/Q'     : Item(status='  ', wc_rev=2),
397    'A/B/F/Q2'    : Item(status='  ', wc_rev=2),
398    'A/B/F/Q/bar' : Item(status='  ', wc_rev=2),
399    'A/B/F/Q/bar2': Item(status='  ', wc_rev=2),
400    'A/B/F/foo'   : Item(status='  ', wc_rev=2),
401    'A/B/F/foo2'  : Item(status='  ', wc_rev=2),
402    })
403  svntest.actions.run_and_verify_commit(wc_dir,
404                                        expected_output,
405                                        expected_status)
406
407  expected_output = wc.State(C_path, {
408    'Q'      : Item(status='A '),
409    'Q2'     : Item(status='A '),
410    'Q/bar'  : Item(status='A '),
411    'Q/bar2' : Item(status='A '),
412    'foo'    : Item(status='A '),
413    'foo2'   : Item(status='A '),
414    })
415  expected_mergeinfo_output = wc.State(C_path, {
416    '' : Item(status=' U'),
417    })
418  expected_elision_output = wc.State(C_path, {
419    })
420  expected_disk = wc.State('', {
421    ''       : Item(props={SVN_PROP_MERGEINFO : '/A/B/F:2'}),
422    'Q'      : Item(),
423    'Q2'     : Item(props={'x' : 'x'}),
424    'Q/bar'  : Item("bar"),
425    'Q/bar2' : Item("bar2", props={'z' : 'z'}),
426    'foo'    : Item("foo"),
427    'foo2'   : Item("foo2", props={'y' : 'y'}),
428    })
429  expected_status = wc.State(C_path, {
430    ''       : Item(status=' M', wc_rev=1),
431    'Q'      : Item(status='A ', wc_rev='-', copied='+'),
432    'Q2'     : Item(status='A ', wc_rev='-', copied='+'),
433    'Q/bar'  : Item(status='  ', wc_rev='-', copied='+'),
434    'Q/bar2' : Item(status='  ', wc_rev='-', copied='+'),
435    'foo'    : Item(status='A ', wc_rev='-', copied='+'),
436    'foo2'   : Item(status='A ', wc_rev='-', copied='+'),
437    })
438
439  expected_skip = wc.State(C_path, { })
440
441  # Add some unversioned directory obstructions to the incoming
442  # additions.  This should be tolerated and *not* result in any
443  # difference between the --dry-run and actual merge.
444  # See http://svn.haxx.se/dev/archive-2012-11/0696.shtml
445  os.mkdir(sbox.ospath('A/C/Q'))
446  os.mkdir(sbox.ospath('A/C/Q2'))
447
448  svntest.actions.run_and_verify_merge(C_path, '1', '2', F_url, None,
449                                       expected_output,
450                                       expected_mergeinfo_output,
451                                       expected_elision_output,
452                                       expected_disk,
453                                       expected_status,
454                                       expected_skip,
455                                       check_props=True)
456
457  expected_output = svntest.wc.State(wc_dir, {
458    'A/C'       : Item(verb='Sending'),
459    'A/C/Q'     : Item(verb='Adding'),
460    'A/C/Q2'    : Item(verb='Adding'),
461    'A/C/foo'   : Item(verb='Adding'),
462    'A/C/foo2'  : Item(verb='Adding'),
463    })
464  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
465  expected_status.add({
466    'A/C'         : Item(status='  ', wc_rev=3),
467    'A/B/F/Q'     : Item(status='  ', wc_rev=2),
468    'A/B/F/Q2'    : Item(status='  ', wc_rev=2),
469    'A/B/F/Q/bar' : Item(status='  ', wc_rev=2),
470    'A/B/F/Q/bar2': Item(status='  ', wc_rev=2),
471    'A/B/F/foo'   : Item(status='  ', wc_rev=2),
472    'A/B/F/foo2'  : Item(status='  ', wc_rev=2),
473    'A/C/Q'       : Item(status='  ', wc_rev=3),
474    'A/C/Q2'      : Item(status='  ', wc_rev=3),
475    'A/C/Q/bar'   : Item(status='  ', wc_rev=3),
476    'A/C/Q/bar2'  : Item(status='  ', wc_rev=3),
477    'A/C/foo'     : Item(status='  ', wc_rev=3),
478    'A/C/foo2'    : Item(status='  ', wc_rev=3),
479    })
480  svntest.actions.run_and_verify_commit(wc_dir,
481                                        expected_output,
482                                        expected_status)
483
484#----------------------------------------------------------------------
485# Issue 953
486@SkipUnless(server_has_mergeinfo)
487@Issue(953)
488def simple_property_merges(sbox):
489  "some simple property merges"
490
491  sbox.build()
492  wc_dir = sbox.wc_dir
493
494  # Add a property to a file and a directory
495  alpha_path = sbox.ospath('A/B/E/alpha')
496  beta_path = sbox.ospath('A/B/E/beta')
497  E_path = sbox.ospath('A/B/E')
498
499  svntest.actions.set_prop('foo', 'foo_val', alpha_path)
500  # A binary, non-UTF8 property value
501  svntest.actions.set_prop('foo', b'foo\201val', beta_path)
502  svntest.actions.set_prop('foo', 'foo_val', E_path)
503
504  # Commit change as rev 2
505  expected_output = svntest.wc.State(wc_dir, {
506    'A/B/E'       : Item(verb='Sending'),
507    'A/B/E/alpha' : Item(verb='Sending'),
508    'A/B/E/beta'  : Item(verb='Sending'),
509    })
510  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
511  expected_status.tweak('A/B/E', 'A/B/E/alpha', 'A/B/E/beta',
512                        wc_rev=2, status='  ')
513  svntest.actions.run_and_verify_commit(wc_dir,
514                                        expected_output, expected_status)
515  svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir)
516
517  # Copy B to B2 as rev 3
518  B_url = sbox.repo_url + '/A/B'
519  B2_url = sbox.repo_url + '/A/B2'
520
521  svntest.actions.run_and_verify_svn(None, [],
522                                     'copy', '-m', 'copy B to B2',
523                                     B_url, B2_url)
524  svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir)
525
526  # Modify a property and add a property for the file and directory
527  svntest.actions.set_prop('foo', 'mod_foo', alpha_path)
528  svntest.actions.set_prop('bar', 'bar_val', alpha_path)
529  svntest.actions.set_prop('foo', b'mod\201foo', beta_path)
530  svntest.actions.set_prop('bar', b'bar\201val', beta_path)
531  svntest.actions.set_prop('foo', 'mod_foo', E_path)
532  svntest.actions.set_prop('bar', 'bar_val', E_path)
533
534  # Commit change as rev 4
535  expected_status = svntest.actions.get_virginal_state(wc_dir, 3)
536  expected_status.tweak('A/B/E', 'A/B/E/alpha', 'A/B/E/beta',
537                        wc_rev=4, status='  ')
538  expected_status.add({
539    'A/B2'         : Item(status='  ', wc_rev=3),
540    'A/B2/E'       : Item(status='  ', wc_rev=3),
541    'A/B2/E/alpha' : Item(status='  ', wc_rev=3),
542    'A/B2/E/beta'  : Item(status='  ', wc_rev=3),
543    'A/B2/F'       : Item(status='  ', wc_rev=3),
544    'A/B2/lambda'  : Item(status='  ', wc_rev=3),
545    })
546  svntest.actions.run_and_verify_commit(wc_dir,
547                                        expected_output, expected_status)
548  svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir)
549
550  pristine_status = expected_status
551  pristine_status.tweak(wc_rev=4)
552
553  # Merge B 3:4 into B2
554  B2_path = sbox.ospath('A/B2')
555  expected_output = wc.State(B2_path, {
556    'E'        : Item(status=' U'),
557    'E/alpha'  : Item(status=' U'),
558    'E/beta'   : Item(status=' U'),
559    })
560  expected_mergeinfo_output = wc.State(B2_path, {
561    '' : Item(status=' U'),
562    })
563  expected_elision_output = wc.State(B2_path, {
564    })
565  expected_disk = wc.State('', {
566    ''         : Item(props={SVN_PROP_MERGEINFO : '/A/B:4'}),
567    'E'        : Item(),
568    'E/alpha'  : Item("This is the file 'alpha'.\n"),
569    'E/beta'   : Item("This is the file 'beta'.\n"),
570    'F'        : Item(),
571    'lambda'   : Item("This is the file 'lambda'.\n"),
572    })
573  expected_disk.tweak('E', 'E/alpha',
574                      props={'foo' : 'mod_foo', 'bar' : 'bar_val'})
575  expected_disk.tweak('E/beta',
576                      props={'foo' : b'mod\201foo', 'bar' : b'bar\201val'})
577  expected_status = wc.State(B2_path, {
578    ''        : Item(status=' M'),
579    'E'       : Item(status=' M'),
580    'E/alpha' : Item(status=' M'),
581    'E/beta'  : Item(status=' M'),
582    'F'       : Item(status='  '),
583    'lambda'  : Item(status='  '),
584    })
585  expected_status.tweak(wc_rev=4)
586  expected_skip = wc.State('', { })
587  svntest.actions.run_and_verify_merge(B2_path, '3', '4', B_url, None,
588                                       expected_output,
589                                       expected_mergeinfo_output,
590                                       expected_elision_output,
591                                       expected_disk,
592                                       expected_status,
593                                       expected_skip,
594                                       check_props=True)
595
596  # Revert merge
597  svntest.actions.run_and_verify_svn(None, [],
598                                     'revert', '--recursive', wc_dir)
599  svntest.actions.run_and_verify_status(wc_dir, pristine_status)
600
601  # Merge B 2:1 into B2 (B2's mergeinfo should get elided away)
602  expected_status.tweak('', status='  ')
603  expected_disk.remove('')
604  expected_disk.tweak('E', 'E/alpha', 'E/beta', props={})
605  expected_elision_output = wc.State(B2_path, {
606    '' : Item(status=' U'),
607    })
608  svntest.actions.run_and_verify_merge(B2_path, '2', '1', B_url, None,
609                                       expected_output,
610                                       expected_mergeinfo_output,
611                                       expected_elision_output,
612                                       expected_disk,
613                                       expected_status,
614                                       expected_skip,
615                                       check_props=True)
616
617  def error_message(property, old_value, new_value):
618    return "Trying to change property '%s'\n" \
619           "but the property has been locally deleted.\n" \
620           "<<<<<<< (local property value)\n" \
621           "||||||| (incoming 'changed from' value)\n" \
622           "%s=======\n" \
623           "%s>>>>>>> (incoming 'changed to' value)\n" % (property, old_value, new_value)
624
625  # Merge B 3:4 into B2 now causes a conflict
626  expected_disk.add({
627    '' : Item(props={SVN_PROP_MERGEINFO : '/A/B:4'}),
628    'E/dir_conflicts.prej'
629    : Item(error_message('foo', 'foo_val', 'mod_foo')),
630    'E/alpha.prej'
631    : Item(error_message('foo', 'foo_val', 'mod_foo')),
632    'E/beta.prej'
633    : Item(error_message('foo', 'foo?\\81val', 'mod?\\81foo')),
634    })
635  expected_disk.tweak('E', 'E/alpha', props={'bar' : 'bar_val'})
636  expected_disk.tweak('E/beta', props={'bar' : b'bar\201val'})
637  expected_status.tweak('', status=' M')
638  expected_status.tweak('E', 'E/alpha', 'E/beta', status=' C')
639  expected_output.tweak('E', 'E/alpha', 'E/beta', status=' C')
640  expected_elision_output = wc.State(B2_path, {
641    })
642  svntest.actions.run_and_verify_merge(B2_path, '3', '4', B_url, None,
643                                       expected_output,
644                                       expected_mergeinfo_output,
645                                       expected_elision_output,
646                                       expected_disk,
647                                       expected_status,
648                                       expected_skip,
649                                       check_props=True)
650
651  # issue 1109 : single file property merge.  This test performs a merge
652  # that should be a no-op (adding properties that are already present).
653  svntest.actions.run_and_verify_svn(None, [],
654                                     'revert', '--recursive', wc_dir)
655  svntest.actions.run_and_verify_status(wc_dir, pristine_status)
656
657  # Copy A at rev 4 to A2 to make revision 5.
658  A_url = sbox.repo_url + '/A'
659  A2_url = sbox.repo_url + '/A2'
660  svntest.actions.run_and_verify_svn(['Committing transaction...\n',
661                                      'Committed revision 5.\n'], [],
662                                     'copy', '-m', 'copy A to A2',
663                                     A_url, A2_url)
664
665  # Re-root the WC at A2.
666  svntest.main.safe_rmtree(wc_dir)
667  svntest.actions.run_and_verify_svn(None, [], 'checkout',
668                                     A2_url, wc_dir)
669
670  # Attempt to re-merge rev 4 of the original A's alpha.  Mergeinfo
671  # inherited from A2 (created by its copy from A) allows us to avoid
672  # a repeated merge.
673  alpha_url = sbox.repo_url + '/A/B/E/alpha'
674  alpha_path = sbox.ospath('B/E/alpha')
675
676  # Cannot use run_and_verify_merge with a file target
677  svntest.actions.run_and_verify_svn([], [], 'merge', '-r', '3:4',
678                                     alpha_url, alpha_path)
679
680  exit_code, output, err = svntest.actions.run_and_verify_svn(None, [],
681                                                              'pl', alpha_path)
682
683  saw_foo = 0
684  saw_bar = 0
685  for line in output:
686    if re.match("\\s*foo\\s*$", line):
687      saw_foo = 1
688    if re.match("\\s*bar\\s*$", line):
689      saw_bar = 1
690
691  if not saw_foo or not saw_bar:
692    raise svntest.Failure("Expected properties not found")
693
694#----------------------------------------------------------------------
695# This is a regression for issue #1176.
696@Issue(1176)
697def merge_similar_unrelated_trees(sbox):
698  "merging similar trees ancestrally unrelated"
699
700  ## See https://issues.apache.org/jira/browse/SVN-1249. ##
701
702  sbox.build()
703  wc_dir = sbox.wc_dir
704
705  # Simple test.  Make three directories with the same content.
706  # Modify some stuff in the second one.  Now merge
707  # (firstdir:seconddir->thirddir).
708
709  base1_path = sbox.ospath('base1')
710  base2_path = sbox.ospath('base2')
711  apply_path = sbox.ospath('apply')
712
713  base1_url = os.path.join(sbox.repo_url + '/base1')
714  base2_url = os.path.join(sbox.repo_url + '/base2')
715
716  # Make a tree of stuff ...
717  os.mkdir(base1_path)
718  svntest.main.file_append(os.path.join(base1_path, 'iota'),
719                           "This is the file iota\n")
720  os.mkdir(os.path.join(base1_path, 'A'))
721  svntest.main.file_append(os.path.join(base1_path, 'A', 'mu'),
722                           "This is the file mu\n")
723  os.mkdir(os.path.join(base1_path, 'A', 'B'))
724  svntest.main.file_append(os.path.join(base1_path, 'A', 'B', 'alpha'),
725                           "This is the file alpha\n")
726  svntest.main.file_append(os.path.join(base1_path, 'A', 'B', 'beta'),
727                           "This is the file beta\n")
728
729  # ... Copy it twice ...
730  shutil.copytree(base1_path, base2_path)
731  shutil.copytree(base1_path, apply_path)
732
733  # ... Gonna see if merge is naughty or nice!
734  svntest.main.file_append(os.path.join(base2_path, 'A', 'mu'),
735                           "A new line in mu.\n")
736  os.rename(os.path.join(base2_path, 'A', 'B', 'beta'),
737            os.path.join(base2_path, 'A', 'B', 'zeta'))
738
739  svntest.actions.run_and_verify_svn(None, [],
740                                  'add', base1_path, base2_path, apply_path)
741
742  svntest.actions.run_and_verify_svn(None, [],
743                                     'ci', '-m', 'rev 2', wc_dir)
744
745  expected_output = wc.State(apply_path, {
746    'A/mu'     : Item(status='U '),
747    'A/B/zeta' : Item(status='A '),
748    'A/B/beta' : Item(status='D '),
749    })
750
751  # run_and_verify_merge doesn't support 'svn merge URL URL path'
752  ### TODO: We can use run_and_verify_merge() here now.
753  svntest.actions.run_and_verify_svn(None, [],
754                                     'merge',
755                                     '--ignore-ancestry',
756                                     base1_url, base2_url,
757                                     apply_path)
758
759  expected_status = wc.State(apply_path, {
760    ''            : Item(status='  '),
761    'A'           : Item(status='  '),
762    'A/mu'        : Item(status='M '),
763    'A/B'         : Item(status='  '),
764    'A/B/zeta'    : Item(status='A ', copied='+'),
765    'A/B/alpha'   : Item(status='  '),
766    'A/B/beta'    : Item(status='D '),
767    'iota'        : Item(status='  '),
768    })
769  expected_status.tweak(wc_rev=2)
770  expected_status.tweak('A/B/zeta', wc_rev='-')
771  svntest.actions.run_and_verify_status(apply_path, expected_status)
772
773#----------------------------------------------------------------------
774def merge_one_file_helper(sbox, arg_flav, record_only = 0):
775  """ARG_FLAV is one of 'r' (revision range) or 'c' (single change) or
776  '*' (no revision specified)."""
777
778  if arg_flav not in ('r', 'c', '*'):
779    raise svntest.Failure("Unrecognized flavor of merge argument")
780
781  sbox.build()
782  wc_dir = sbox.wc_dir
783
784  rho_rel_path = os.path.join('A', 'D', 'G', 'rho')
785  rho_path = os.path.join(wc_dir, rho_rel_path)
786  G_path = sbox.ospath('A/D/G')
787  rho_url = sbox.repo_url + '/A/D/G/rho'
788
789  # Change rho for revision 2
790  svntest.main.file_append(rho_path, 'A new line in rho.\n')
791
792  expected_output = wc.State(wc_dir, { rho_rel_path : Item(verb='Sending'), })
793  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
794  expected_status.tweak('A/D/G/rho', wc_rev=2)
795  svntest.actions.run_and_verify_commit(wc_dir,
796                                        expected_output,
797                                        expected_status)
798
799  # Backdate rho to revision 1, so we can merge in the rev 2 changes.
800  svntest.actions.run_and_verify_svn(None, [],
801                                     'up', '-r', '1', rho_path)
802
803  # Try one merge with an explicit target; it should succeed.
804  ### Yes, it would be nice to use run_and_verify_merge(), but it
805  # appears to be impossible to get the expected_foo trees working
806  # right.  I think something is still assuming a directory target.
807  if arg_flav == 'r':
808    svntest.actions.run_and_verify_svn(
809      expected_merge_output([[2]],
810                            ['U    ' + rho_path + '\n',
811                             ' U   ' + rho_path + '\n']),
812      [], 'merge', '-r', '1:2', rho_url, rho_path)
813  elif arg_flav == 'c':
814    svntest.actions.run_and_verify_svn(
815      expected_merge_output([[2]],
816                            ['U    ' + rho_path + '\n',
817                             ' U   ' + rho_path + '\n']),
818      [], 'merge', '-c', '2', rho_url, rho_path)
819  elif arg_flav == '*':
820    svntest.actions.run_and_verify_svn(
821      expected_merge_output([[2]],
822                            ['U    ' + rho_path + '\n',
823                             ' U   ' + rho_path + '\n']),
824      [], 'merge', rho_url, rho_path)
825
826  expected_status.tweak(wc_rev=1)
827  expected_status.tweak('A/D/G/rho', status='MM')
828  svntest.actions.run_and_verify_status(wc_dir, expected_status)
829
830  # Inspect rho, make sure it's right.
831  rho_text = svntest.tree.get_text(rho_path)
832  if rho_text != "This is the file 'rho'.\nA new line in rho.\n":
833    raise svntest.Failure("Unexpected text in merged '" + rho_path + "'")
834
835  # Restore rho to pristine revision 1, for another merge.
836  svntest.actions.run_and_verify_svn(None, [], 'revert', rho_path)
837  expected_status.tweak('A/D/G/rho', status='  ')
838  svntest.actions.run_and_verify_status(wc_dir, expected_status)
839
840  # Cd into the directory and run merge with no targets.
841  # It should still merge into rho.
842  saved_cwd = os.getcwd()
843  os.chdir(G_path)
844
845  # Cannot use run_and_verify_merge with a file target
846  merge_cmd = ['merge']
847  if arg_flav == 'r':
848    merge_cmd += ['-r', '1:2']
849  elif arg_flav == 'c':
850    merge_cmd += ['-c', '2']
851
852  if record_only:
853    expected_output = expected_merge_output([[2]],
854                                            [' U   rho\n'])
855    merge_cmd.append('--record-only')
856    rho_expected_status = ' M'
857  else:
858    expected_output = expected_merge_output([[2]],
859                                            ['U    rho\n',
860                                             ' U   rho\n'])
861    rho_expected_status = 'MM'
862  merge_cmd.append(rho_url)
863
864  svntest.actions.run_and_verify_svn(expected_output, [], *merge_cmd)
865
866  # Inspect rho, make sure it's right.
867  rho_text = svntest.tree.get_text('rho')
868  if record_only:
869    expected_text = "This is the file 'rho'.\n"
870  else:
871    expected_text = "This is the file 'rho'.\nA new line in rho.\n"
872  if rho_text != expected_text:
873    print("")
874    raise svntest.Failure("Unexpected text merged to 'rho' in '" +
875                          G_path + "'")
876  os.chdir(saved_cwd)
877
878  expected_status.tweak('A/D/G/rho', status=rho_expected_status)
879  svntest.actions.run_and_verify_status(wc_dir, expected_status)
880
881#----------------------------------------------------------------------
882@SkipUnless(server_has_mergeinfo)
883@Issue(1150)
884def merge_one_file_using_r(sbox):
885  "merge one file using the -r option"
886  merge_one_file_helper(sbox, 'r')
887
888#----------------------------------------------------------------------
889@SkipUnless(server_has_mergeinfo)
890@Issue(1150)
891def merge_one_file_using_c(sbox):
892  "merge one file using the -c option"
893  merge_one_file_helper(sbox, 'c')
894
895#----------------------------------------------------------------------
896@SkipUnless(server_has_mergeinfo)
897def merge_one_file_using_implicit_revs(sbox):
898  "merge one file without explicit revisions"
899  merge_one_file_helper(sbox, '*')
900
901#----------------------------------------------------------------------
902@SkipUnless(server_has_mergeinfo)
903def merge_record_only(sbox):
904  "mark a revision range as merged"
905  merge_one_file_helper(sbox, 'r', 1)
906
907#----------------------------------------------------------------------
908# This is a regression test for the enhancement added in issue #785 "add
909# friendly enhancement to 'svn merge'", which is about inferring that
910# the default target of "svn merge [-r...] FILE" should not be "." but
911# rather should be "FILE".
912def merge_with_implicit_target_helper(sbox, arg_flav):
913  """ARG_FLAV is one of 'r' (revision range) or 'c' (single change) or
914  '*' (no revision specified)."""
915
916  if arg_flav not in ('r', 'c', '*'):
917    raise svntest.Failure("Unrecognized flavor of merge argument")
918
919  sbox.build()
920  wc_dir = sbox.wc_dir
921
922  # Change mu for revision 2
923  mu_path = sbox.ospath('A/mu')
924  orig_mu_text = svntest.tree.get_text(mu_path)
925  added_mu_text = ""
926  for x in range(2,11):
927    added_mu_text = added_mu_text + 'This is line ' + repr(x) + ' in mu\n'
928  svntest.main.file_append(mu_path, added_mu_text)
929
930  # Create expected output tree for initial commit
931  expected_output = wc.State(wc_dir, {
932    'A/mu' : Item(verb='Sending'),
933    })
934
935  # Create expected status tree; all local revisions should be at 1,
936  # but mu should be at revision 2.
937  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
938  expected_status.tweak('A/mu', wc_rev=2)
939
940  # Initial commit.
941  svntest.actions.run_and_verify_commit(wc_dir,
942                                        expected_output,
943                                        expected_status)
944
945  # Make the "other" working copy, at r1
946  other_wc = sbox.add_wc_path('other')
947  svntest.actions.duplicate_dir(wc_dir, other_wc)
948  svntest.main.run_svn(None, 'up', '-r', 1, other_wc)
949
950  # Try the merge without an explicit target; it should succeed.
951  # Can't use run_and_verify_merge cuz it expects a directory argument.
952  mu_url = sbox.repo_url + '/A/mu'
953
954  os.chdir(os.path.join(other_wc, 'A'))
955
956  # merge using filename for sourcepath
957  # Cannot use run_and_verify_merge with a file target
958  if arg_flav == 'r':
959    svntest.actions.run_and_verify_svn(expected_merge_output([[2]],
960                                                             ['U    mu\n',
961                                                              ' U   mu\n']),
962                                       [],
963                                       'merge', '-r', '1:2', 'mu')
964  elif arg_flav == 'c':
965    svntest.actions.run_and_verify_svn(expected_merge_output([[2]],
966                                                             ['U    mu\n',
967                                                              ' U   mu\n']),
968                                       [],
969                                       'merge', '-c', '2', 'mu')
970
971  elif arg_flav == '*':
972    # Without a peg revision, the default merge range of BASE:1 (which
973    # is a no-op) will be chosen.  Let's do it both ways (no-op first,
974    # of course).
975    svntest.actions.run_and_verify_svn(None, [], 'merge', 'mu')
976    svntest.actions.run_and_verify_svn(expected_merge_output([[2]],
977                                                             ['U    mu\n',
978                                                              ' U   mu\n']),
979                                       [],
980                                       'merge', 'mu@2')
981
982  # sanity-check resulting file
983  if svntest.tree.get_text('mu') != orig_mu_text + added_mu_text:
984    raise svntest.Failure("Unexpected text in 'mu'")
985
986  # merge using URL for sourcepath
987  if arg_flav == 'r':
988    svntest.actions.run_and_verify_svn(expected_merge_output([[-2]],
989                                                             ['G    mu\n',
990                                                              ' U   mu\n',
991                                                              ' G   mu\n',],
992                                                             elides=True),
993                                       [],
994                                       'merge', '-r', '2:1', mu_url)
995  elif arg_flav == 'c':
996    svntest.actions.run_and_verify_svn(expected_merge_output([[-2]],
997                                                             ['G    mu\n',
998                                                              ' U   mu\n',
999                                                              ' G   mu\n'],
1000                                                             elides=True),
1001                                       [],
1002                                       'merge', '-c', '-2', mu_url)
1003  elif arg_flav == '*':
1004    # Implicit merge source URL and revision range detection is for
1005    # forward merges only (e.g. non-reverts).  Undo application of
1006    # r2 to enable continuation of the test case.
1007    svntest.actions.run_and_verify_svn(expected_merge_output([[-2]],
1008                                                             ['G    mu\n',
1009                                                              ' U   mu\n',
1010                                                              ' G   mu\n'],
1011                                                             elides=True),
1012                                       [],
1013                                       'merge', '-c', '-2', mu_url)
1014
1015  # sanity-check resulting file
1016  if svntest.tree.get_text('mu') != orig_mu_text:
1017    raise svntest.Failure("Unexpected text '%s' in 'mu', expected '%s'" %
1018                          (svntest.tree.get_text('mu'), orig_mu_text))
1019
1020#----------------------------------------------------------------------
1021@SkipUnless(server_has_mergeinfo)
1022@Issue(785)
1023def merge_with_implicit_target_using_r(sbox):
1024  "merging a file w/no explicit target path using -r"
1025  merge_with_implicit_target_helper(sbox, 'r')
1026
1027#----------------------------------------------------------------------
1028@Issue(785)
1029def merge_with_implicit_target_using_c(sbox):
1030  "merging a file w/no explicit target path using -c"
1031  merge_with_implicit_target_helper(sbox, 'c')
1032
1033#----------------------------------------------------------------------
1034@Issue(785)
1035def merge_with_implicit_target_and_revs(sbox):
1036  "merging a file w/no explicit target path or revs"
1037  merge_with_implicit_target_helper(sbox, '*')
1038
1039#----------------------------------------------------------------------
1040def merge_with_prev(sbox):
1041  "merge operations using PREV revision"
1042
1043  sbox.build()
1044  wc_dir = sbox.wc_dir
1045
1046  # Change mu for revision 2
1047  mu_path = sbox.ospath('A/mu')
1048  orig_mu_text = svntest.tree.get_text(mu_path)
1049  added_mu_text = ""
1050  for x in range(2,11):
1051    added_mu_text = added_mu_text + '\nThis is line ' + repr(x) + ' in mu'
1052  added_mu_text += "\n"
1053  svntest.main.file_append(mu_path, added_mu_text)
1054
1055  zot_path = sbox.ospath('A/zot')
1056
1057  svntest.main.file_append(zot_path, "bar")
1058  svntest.main.run_svn(None, 'add', zot_path)
1059
1060  # Create expected output tree for initial commit
1061  expected_output = wc.State(wc_dir, {
1062    'A/mu' : Item(verb='Sending'),
1063    'A/zot' : Item(verb='Adding'),
1064    })
1065
1066  # Create expected status tree; all local revisions should be at 1,
1067  # but mu should be at revision 2.
1068  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
1069  expected_status.tweak('A/mu', wc_rev=2)
1070  expected_status.add({'A/zot' : Item(status='  ', wc_rev=2)})
1071
1072  # Initial commit.
1073  svntest.actions.run_and_verify_commit(wc_dir,
1074                                        expected_output,
1075                                        expected_status)
1076
1077  # Make some other working copies
1078  other_wc = sbox.add_wc_path('other')
1079  svntest.actions.duplicate_dir(wc_dir, other_wc)
1080
1081  another_wc = sbox.add_wc_path('another')
1082  svntest.actions.duplicate_dir(wc_dir, another_wc)
1083
1084  was_cwd = os.getcwd()
1085
1086  os.chdir(os.path.join(other_wc, 'A'))
1087
1088  # Try to revert the last change to mu via svn merge
1089  # Cannot use run_and_verify_merge with a file target
1090  svntest.actions.run_and_verify_svn(expected_merge_output([[-2]],
1091                                                           ['U    mu\n',
1092                                                            ' U   mu\n'],
1093                                                           elides=True),
1094                                     [],
1095                                     'merge', '-r', 'HEAD:PREV', 'mu')
1096
1097  # sanity-check resulting file
1098  if svntest.tree.get_text('mu') != orig_mu_text:
1099    raise svntest.Failure("Unexpected text in 'mu'")
1100
1101  os.chdir(was_cwd)
1102
1103  other_status = expected_status
1104  other_status.wc_dir = other_wc
1105  other_status.tweak('A/mu', status='M ', wc_rev=2)
1106  other_status.tweak('A/zot', wc_rev=2)
1107  svntest.actions.run_and_verify_status(other_wc, other_status)
1108
1109  os.chdir(another_wc)
1110
1111  # ensure 'A' will be at revision 2
1112  svntest.actions.run_and_verify_svn(None, [], 'up')
1113
1114  # now try a revert on a directory, and verify that it removed the zot
1115  # file we had added previously
1116  svntest.actions.run_and_verify_svn(None, [],
1117                                     'merge', '-r', 'COMMITTED:PREV',
1118                                     'A', 'A')
1119
1120  if svntest.tree.get_text('A/zot') != None:
1121    raise svntest.Failure("Unexpected text in 'A/zot'")
1122
1123  os.chdir(was_cwd)
1124
1125  another_status = expected_status
1126  another_status.wc_dir = another_wc
1127  another_status.tweak(wc_rev=2)
1128  another_status.tweak('A/mu', status='M ')
1129  another_status.tweak('A/zot', status='D ')
1130  svntest.actions.run_and_verify_status(another_wc, another_status)
1131
1132#----------------------------------------------------------------------
1133# Regression test for issue #1319: 'svn merge' should *not* 'C' when
1134# merging a change into a binary file, unless it has local mods, or has
1135# different contents from the left side of the merge.
1136@SkipUnless(server_has_mergeinfo)
1137@Issue(1319)
1138def merge_binary_file(sbox):
1139  "merge change into unchanged binary file"
1140
1141  sbox.build()
1142  wc_dir = sbox.wc_dir
1143
1144  # Add a binary file to the project
1145  theta_contents = open(os.path.join(sys.path[0], "theta.bin"), 'rb').read()
1146  # Write PNG file data into 'A/theta'.
1147  theta_path = sbox.ospath('A/theta')
1148  svntest.main.file_write(theta_path, theta_contents, 'wb')
1149
1150  svntest.main.run_svn(None, 'add', theta_path)
1151
1152  # Commit the new binary file, creating revision 2.
1153  expected_output = svntest.wc.State(wc_dir, {
1154    'A/theta' : Item(verb='Adding  (bin)'),
1155    })
1156  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
1157  expected_status.add({
1158    'A/theta' : Item(status='  ', wc_rev=2),
1159    })
1160  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
1161                                        expected_status)
1162
1163  # Make the "other" working copy
1164  other_wc = sbox.add_wc_path('other')
1165  svntest.actions.duplicate_dir(wc_dir, other_wc)
1166
1167  # Change the binary file in first working copy, commit revision 3.
1168  svntest.main.file_append(theta_path, "some extra junk")
1169  expected_output = wc.State(wc_dir, {
1170    'A/theta' : Item(verb='Sending'),
1171    })
1172  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
1173  expected_status.add({
1174    'A/theta' : Item(status='  ', wc_rev=3),
1175    })
1176  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
1177                                        expected_status)
1178
1179  # In second working copy, attempt to 'svn merge -r 2:3'.
1180  # We should *not* see a conflict during the update, but a 'U'.
1181  # And after the merge, the status should be 'M'.
1182  expected_output = wc.State(other_wc, {
1183    'A/theta' : Item(status='U '),
1184    })
1185  expected_mergeinfo_output = wc.State(other_wc, {
1186    '' : Item(status=' U'),
1187    })
1188  expected_elision_output = wc.State(other_wc, {
1189    })
1190  expected_disk = svntest.main.greek_state.copy()
1191  expected_disk.add({
1192    ''        : Item(props={SVN_PROP_MERGEINFO : '/:3'}),
1193    'A/theta' : Item(theta_contents + b"some extra junk",
1194                     props={'svn:mime-type' : 'application/octet-stream'}),
1195    })
1196  expected_status = svntest.actions.get_virginal_state(other_wc, 1)
1197  expected_status.add({
1198    ''        : Item(status=' M', wc_rev=1),
1199    'A/theta' : Item(status='M ', wc_rev=2),
1200    })
1201  expected_skip = wc.State('', { })
1202
1203  svntest.actions.run_and_verify_merge(other_wc, '2', '3',
1204                                       sbox.repo_url, None,
1205                                       expected_output,
1206                                       expected_mergeinfo_output,
1207                                       expected_elision_output,
1208                                       expected_disk,
1209                                       expected_status,
1210                                       expected_skip,
1211                                       [],
1212                                       True, True, '--allow-mixed-revisions',
1213                                       other_wc)
1214
1215#----------------------------------------------------------------------
1216# Regression test for Issue #1297:
1217# A merge that creates a new file followed by an immediate diff
1218# The diff should succeed.
1219@SkipUnless(server_has_mergeinfo)
1220@Issue(1297)
1221def merge_in_new_file_and_diff(sbox):
1222  "diff after merge that creates a new file"
1223
1224  sbox.build()
1225  wc_dir = sbox.wc_dir
1226
1227  trunk_url = sbox.repo_url + '/A/B/E'
1228
1229  # Create a branch
1230  svntest.actions.run_and_verify_svn(None, [], 'cp',
1231                                     trunk_url,
1232                                     sbox.repo_url + '/branch',
1233                                     '-m', "Creating the Branch")
1234
1235  # Update to revision 2.
1236  svntest.actions.run_and_verify_svn(None, [],
1237                                     'update', wc_dir)
1238
1239  new_file_path = sbox.ospath('A/B/E/newfile')
1240  svntest.main.file_write(new_file_path, "newfile\n")
1241
1242  # Add the new file, and commit revision 3.
1243  svntest.actions.run_and_verify_svn(None, [], "add", new_file_path)
1244  svntest.actions.run_and_verify_svn(None, [],
1245                                     'ci', '-m',
1246                                     "Changing the trunk.", wc_dir)
1247
1248  branch_path = sbox.ospath('branch')
1249  url_branch_path = branch_path.replace(os.path.sep, '/')
1250
1251  # Merge our addition into the branch.
1252  expected_output = svntest.wc.State(branch_path, {
1253    'newfile' : Item(status='A '),
1254    })
1255  expected_mergeinfo_output = svntest.wc.State(branch_path, {
1256    '' : Item(status=' U'),
1257    })
1258  expected_elision_output = wc.State(branch_path, {
1259    })
1260  expected_disk = wc.State('', {
1261    'alpha'   : Item("This is the file 'alpha'.\n"),
1262    'beta'    : Item("This is the file 'beta'.\n"),
1263    'newfile' : Item("newfile\n"),
1264    })
1265  expected_status = wc.State(branch_path, {
1266    ''        : Item(status=' M', wc_rev=2),
1267    'alpha'   : Item(status='  ', wc_rev=2),
1268    'beta'    : Item(status='  ', wc_rev=2),
1269    'newfile' : Item(status='A ', wc_rev='-', copied='+')
1270    })
1271  expected_skip = wc.State('', { })
1272
1273  svntest.actions.run_and_verify_merge(branch_path,
1274                                       '1', 'HEAD', trunk_url, None,
1275                                       expected_output,
1276                                       expected_mergeinfo_output,
1277                                       expected_elision_output,
1278                                       expected_disk,
1279                                       expected_status,
1280                                       expected_skip)
1281
1282  # Finally, run diff.
1283  expected_output = [
1284    "Index: " + url_branch_path + "/newfile\n",
1285    "===================================================================\n",
1286    "--- "+ url_branch_path + "/newfile	(nonexistent)\n",
1287    "+++ "+ url_branch_path + "/newfile	(working copy)\n",
1288    "@@ -0,0 +1 @@\n",
1289    "+newfile\n",
1290
1291    "Index: " + url_branch_path + "\n",
1292    "===================================================================\n",
1293    "--- "+ url_branch_path + "\t(revision 2)\n",
1294    "+++ "+ url_branch_path + "\t(working copy)\n",
1295    "\n",
1296    "Property changes on: " + url_branch_path + "\n",
1297    "___________________________________________________________________\n",
1298    "Added: " + SVN_PROP_MERGEINFO + "\n",
1299    "## -0,0 +0,1 ##\n",
1300    "   Merged /A/B/E:r2-3\n",
1301  ]
1302  svntest.actions.run_and_verify_svn(expected_output, [], 'diff',
1303                                     '--show-copies-as-adds', branch_path)
1304
1305
1306#----------------------------------------------------------------------
1307# Issue #1425:  'svn merge' should skip over any unversioned obstructions.
1308# This test involves tree conflicts. - but attempting to test for
1309# pre-tree-conflict behaviour
1310@SkipUnless(server_has_mergeinfo)
1311@Issues(1425, 2898)
1312def merge_skips_obstructions(sbox):
1313  "merge should skip over unversioned obstructions"
1314
1315  sbox.build()
1316  wc_dir = sbox.wc_dir
1317
1318  C_path = sbox.ospath('A/C')
1319  F_path = sbox.ospath('A/B/F')
1320  F_url = sbox.repo_url + '/A/B/F'
1321
1322  Q_path = os.path.join(F_path, 'Q')
1323  foo_path = os.path.join(F_path, 'foo')
1324  bar_path = os.path.join(F_path, 'Q', 'bar')
1325
1326  svntest.main.run_svn(None, 'mkdir', Q_path)
1327  svntest.main.file_append(foo_path, "foo")
1328  svntest.main.file_append(bar_path, "bar")
1329  svntest.main.run_svn(None, 'add', foo_path, bar_path)
1330
1331  expected_output = wc.State(wc_dir, {
1332    'A/B/F/Q'     : Item(verb='Adding'),
1333    'A/B/F/Q/bar' : Item(verb='Adding'),
1334    'A/B/F/foo'   : Item(verb='Adding'),
1335    })
1336  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
1337  expected_status.add({
1338    'A/B/F/Q'     : Item(status='  ', wc_rev=2),
1339    'A/B/F/Q/bar' : Item(status='  ', wc_rev=2),
1340    'A/B/F/foo'   : Item(status='  ', wc_rev=2),
1341    })
1342  svntest.actions.run_and_verify_commit(wc_dir,
1343                                        expected_output,
1344                                        expected_status)
1345
1346  pre_merge_status = expected_status
1347
1348  # Revision 2 now has A/B/F/foo, A/B/F/Q, A/B/F/Q/bar.  Let's merge
1349  # those 'F' changes into empty dir 'C'.  But first, create an
1350  # unversioned 'foo' within C, and make sure 'svn merge' doesn't
1351  # error when the addition of foo is obstructed.
1352
1353  expected_output = wc.State(C_path, {
1354    'Q'      : Item(status='A '),
1355    'Q/bar'  : Item(status='A '),
1356    })
1357  expected_mergeinfo_output = wc.State(C_path, {
1358    '' : Item(status=' U'),
1359    })
1360  expected_elision_output = wc.State(C_path, {
1361    })
1362  expected_disk = wc.State('', {
1363    ''       : Item(props={SVN_PROP_MERGEINFO : '/A/B/F:2'}),
1364    'Q'      : Item(),
1365    'Q/bar'  : Item("bar"),
1366    'foo'    : Item("foo"),
1367    })
1368  expected_status = wc.State(C_path, {
1369    ''       : Item(status=' M', wc_rev=1),
1370    'Q'      : Item(status='A ', wc_rev='-', copied='+'),
1371    'Q/bar'  : Item(status='  ', wc_rev='-', copied='+'),
1372    })
1373  expected_skip = wc.State(C_path, {
1374    'foo' : Item(verb='Skipped'),
1375    })
1376  # Unversioned:
1377  svntest.main.file_append(os.path.join(C_path, "foo"), "foo")
1378
1379  svntest.actions.run_and_verify_merge(C_path, '1', '2', F_url, None,
1380                                       expected_output,
1381                                       expected_mergeinfo_output,
1382                                       expected_elision_output,
1383                                       expected_disk,
1384                                       expected_status,
1385                                       expected_skip,
1386                                       [], True)
1387
1388  # Revert the local mods, and this time make "Q" obstructed.  An
1389  # unversioned file called "Q" will obstruct the adding of the
1390  # directory of the same name.
1391
1392  svntest.actions.run_and_verify_svn(None, [],
1393                                     'revert', '-R', wc_dir)
1394  os.unlink(os.path.join(C_path, "foo"))
1395  svntest.main.safe_rmtree(os.path.join(C_path, "Q"))
1396  svntest.main.file_append(os.path.join(C_path, "Q"), "foo") # unversioned
1397  svntest.actions.run_and_verify_status(wc_dir, pre_merge_status)
1398
1399  expected_output = wc.State(C_path, {
1400    'foo'    : Item(status='A '),
1401    'Q/bar'  : Item(status='  ', treeconflict='A'), # Skipped
1402    })
1403  expected_mergeinfo_output = wc.State(C_path, {
1404    '' : Item(status=' U'),
1405    })
1406  expected_elision_output = wc.State(C_path, {
1407    })
1408  expected_disk = wc.State('', {
1409    ''       : Item(props={SVN_PROP_MERGEINFO : '/A/B/F:2'}),
1410    'Q'      : Item("foo"),
1411    'foo'    : Item("foo"),
1412    })
1413  expected_status = wc.State(C_path, {
1414    ''     : Item(status=' M', wc_rev=1),
1415    'foo'  : Item(status='A ', wc_rev='-', copied='+'),
1416    })
1417  expected_skip = wc.State(C_path, {
1418    'Q'     : Item(verb='Skipped'),
1419    })
1420
1421  svntest.actions.run_and_verify_merge(C_path, '1', '2', F_url, None,
1422                                       expected_output,
1423                                       expected_mergeinfo_output,
1424                                       expected_elision_output,
1425                                       expected_disk,
1426                                       expected_status,
1427                                       expected_skip,
1428                                       [], True)
1429
1430  # Revert the local mods, and commit the deletion of iota and A/D/G. (r3)
1431  os.unlink(os.path.join(C_path, "foo"))
1432  svntest.actions.run_and_verify_svn(None, [], 'revert', '-R', wc_dir)
1433  svntest.actions.run_and_verify_status(wc_dir, pre_merge_status)
1434
1435  iota_path = sbox.ospath('iota')
1436  G_path = sbox.ospath('A/D/G')
1437  svntest.actions.run_and_verify_svn(None, [], 'rm', iota_path, G_path)
1438
1439  expected_output = wc.State(wc_dir, {
1440    'A/D/G'  : Item(verb='Deleting'),
1441    'iota'   : Item(verb='Deleting'),
1442    })
1443  expected_status = pre_merge_status
1444  expected_status.remove('iota', 'A/D/G', 'A/D/G/pi', 'A/D/G/rho', 'A/D/G/tau')
1445  svntest.actions.run_and_verify_commit(wc_dir,
1446                                        expected_output,
1447                                        expected_status)
1448
1449  # Now create unversioned iota and A/D/G, try running a merge -r2:3.
1450  # The merge process should skip over these targets, since they're
1451  # unversioned.
1452
1453  svntest.main.file_append(iota_path, "foo") # unversioned
1454  os.mkdir(G_path) # unversioned
1455
1456  expected_output = wc.State(wc_dir, {
1457    })
1458  expected_mergeinfo_output = wc.State(wc_dir, {
1459    '' : Item(status=' U'),
1460    })
1461  expected_elision_output = wc.State(wc_dir, {
1462    })
1463  expected_disk = svntest.main.greek_state.copy()
1464  expected_disk.remove('A/D/G/pi', 'A/D/G/rho', 'A/D/G/tau')
1465  expected_disk.add({
1466    ''             : Item(props={SVN_PROP_MERGEINFO : '/:3'}),
1467    'A/B/F/Q'      : Item(),
1468    'A/B/F/Q/bar'  : Item("bar"),
1469    'A/B/F/foo'    : Item("foo"),
1470    'A/C/Q'        : Item("foo"),
1471    })
1472  expected_disk.tweak('iota', contents="foo")
1473  # No-op merge still sets mergeinfo
1474  expected_status.tweak('', status=' M')
1475  expected_skip = wc.State(wc_dir, {
1476    'iota'  : Item(verb='Skipped'),
1477    'A/D/G' : Item(verb='Skipped'),
1478    })
1479  svntest.actions.run_and_verify_merge(wc_dir, '2', '3',
1480                                       sbox.repo_url, None,
1481                                       expected_output,
1482                                       expected_mergeinfo_output,
1483                                       expected_elision_output,
1484                                       expected_disk,
1485                                       expected_status.copy(wc_dir),
1486                                       expected_skip,
1487                                       [],
1488                                       True, False, '--allow-mixed-revisions',
1489                                       wc_dir)
1490
1491  # Revert the local mods, and commit a change to A/B/lambda (r4), and then
1492  # commit the deletion of the same file. (r5)
1493  svntest.main.safe_rmtree(G_path)
1494  svntest.actions.run_and_verify_svn(None, [], 'revert', '-R', wc_dir)
1495  expected_status.tweak('', status='  ')
1496  svntest.actions.run_and_verify_status(wc_dir, expected_status)
1497
1498  lambda_path = sbox.ospath('A/B/lambda')
1499  svntest.main.file_append(lambda_path, "more text")
1500  expected_output = wc.State(wc_dir, {
1501    'A/B/lambda'  : Item(verb='Sending'),
1502    })
1503  expected_status.tweak('A/B/lambda', wc_rev=4)
1504  svntest.actions.run_and_verify_commit(wc_dir,
1505                                        expected_output,
1506                                        expected_status)
1507
1508  svntest.actions.run_and_verify_svn(None, [], 'rm', lambda_path)
1509
1510  expected_output = wc.State(wc_dir, {
1511    'A/B/lambda'  : Item(verb='Deleting'),
1512    })
1513  expected_status.remove('A/B/lambda')
1514  svntest.actions.run_and_verify_commit(wc_dir,
1515                                        expected_output,
1516                                        expected_status)
1517
1518  # lambda is gone, so create an unversioned lambda in its place.
1519  # Then attempt to merge -r3:4, which is a change to lambda.  The merge
1520  # should simply skip the unversioned file.
1521
1522  svntest.main.file_append(lambda_path, "foo") # unversioned
1523
1524  expected_output = wc.State(wc_dir, { })
1525  expected_mergeinfo_output = wc.State(wc_dir, {
1526    '' : Item(status=' U'),
1527    })
1528  expected_elision_output = wc.State(wc_dir, {
1529    })
1530  expected_disk.add({
1531    'A/B/lambda'      : Item("foo"),
1532    })
1533  expected_disk.remove('A/D/G')
1534  expected_disk.tweak('', props={SVN_PROP_MERGEINFO : '/:4'})
1535  expected_skip = wc.State(wc_dir, {
1536    'A/B/lambda' : Item(verb='Skipped'),
1537    })
1538  # No-op merge still sets mergeinfo.
1539  expected_status_short = expected_status.copy(wc_dir)
1540  expected_status_short.tweak('', status=' M')
1541
1542  svntest.actions.run_and_verify_merge(wc_dir, '3', '4',
1543                                       sbox.repo_url, None,
1544                                       expected_output,
1545                                       expected_mergeinfo_output,
1546                                       expected_elision_output,
1547                                       expected_disk,
1548                                       expected_status_short,
1549                                       expected_skip,
1550                                       [],
1551                                       True, False, '--allow-mixed-revisions',
1552                                       wc_dir)
1553
1554  # OK, so let's commit the new lambda (r6), and then delete the
1555  # working file.  Then re-run the -r3:4 merge, and see how svn deals
1556  # with a file being under version control, but missing.
1557
1558  svntest.actions.run_and_verify_svn(None, [], 'add', lambda_path)
1559
1560  # Mergeinfo prop changed so update to avoid out of date error.
1561  svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir)
1562
1563  expected_output = wc.State(wc_dir, {
1564    ''            : Item(verb='Sending'),
1565    'A/B/lambda'  : Item(verb='Adding'),
1566    })
1567  expected_mergeinfo_output = wc.State(wc_dir, {})
1568  expected_elision_output = wc.State(wc_dir, {})
1569  expected_status.tweak(wc_rev=5)
1570  expected_status.add({
1571    'A/B/lambda'  : Item(wc_rev=6, status='  '),
1572    })
1573  expected_status.tweak('', status='  ', wc_rev=6)
1574  svntest.actions.run_and_verify_commit(wc_dir,
1575                                        expected_output,
1576                                        expected_status)
1577  os.unlink(lambda_path)
1578
1579  expected_output = wc.State(wc_dir, { })
1580  expected_disk.remove('A/B/lambda')
1581  expected_status.tweak('A/B/lambda', status='! ')
1582  expected_status.tweak('', status='  ')
1583  expected_skip = wc.State(wc_dir, {
1584    'A/B/lambda' : Item(verb='Skipped missing target'),
1585    })
1586  # Why do we need to --ignore-ancestry?  Because the previous merge of r4,
1587  # despite being inoperative, set mergeinfo for r4 on the WC.  With the
1588  # advent of merge tracking this repeat merge attempt would not be attempted.
1589  # By using --ignore-ancestry we disregard the mergeinfo and *really* try to
1590  # merge into a missing path.  This is another facet of issue #2898.
1591  svntest.actions.run_and_verify_merge(wc_dir, '3', '4',
1592                                       sbox.repo_url, None,
1593                                       expected_output,
1594                                       expected_mergeinfo_output,
1595                                       expected_elision_output,
1596                                       expected_disk,
1597                                       expected_status.copy(wc_dir),
1598                                       expected_skip,
1599                                       [],
1600                                       1, 0, '--ignore-ancestry',
1601                                       '--allow-mixed-revisions', wc_dir)
1602
1603#----------------------------------------------------------------------
1604# At one time, a merge that added items with the same name as missing
1605# items would attempt to add the items and fail, leaving the working
1606# copy locked and broken.
1607
1608# This test involves tree conflicts.
1609@SkipUnless(server_has_mergeinfo)
1610def merge_into_missing(sbox):
1611  "merge into missing must not break working copy"
1612
1613  sbox.build()
1614  wc_dir = sbox.wc_dir
1615
1616  F_path = sbox.ospath('A/B/F')
1617  F_url = sbox.repo_url + '/A/B/F'
1618  Q_path = os.path.join(F_path, 'Q')
1619  foo_path = os.path.join(F_path, 'foo')
1620
1621  svntest.actions.run_and_verify_svn(None, [], 'mkdir', Q_path)
1622  svntest.main.file_append(foo_path, "foo")
1623  svntest.actions.run_and_verify_svn(None, [], 'add', foo_path)
1624
1625  expected_output = wc.State(wc_dir, {
1626    'A/B/F/Q'       : Item(verb='Adding'),
1627    'A/B/F/foo'     : Item(verb='Adding'),
1628    })
1629  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
1630  expected_status.add({
1631    'A/B/F/Q'       : Item(status='  ', wc_rev=2),
1632    'A/B/F/foo'     : Item(status='  ', wc_rev=2),
1633    })
1634  svntest.actions.run_and_verify_commit(wc_dir,
1635                                        expected_output,
1636                                        expected_status)
1637
1638  R_path = os.path.join(Q_path, 'R')
1639  bar_path = os.path.join(R_path, 'bar')
1640  baz_path = os.path.join(Q_path, 'baz')
1641  svntest.actions.run_and_verify_svn(None, [], 'mkdir', R_path)
1642  svntest.main.file_append(bar_path, "bar")
1643  svntest.actions.run_and_verify_svn(None, [], 'add', bar_path)
1644  svntest.main.file_append(baz_path, "baz")
1645  svntest.actions.run_and_verify_svn(None, [], 'add', baz_path)
1646
1647  expected_output = wc.State(wc_dir, {
1648    'A/B/F/Q/R'     : Item(verb='Adding'),
1649    'A/B/F/Q/R/bar' : Item(verb='Adding'),
1650    'A/B/F/Q/baz'   : Item(verb='Adding'),
1651    })
1652  expected_status.add({
1653    'A/B/F/Q/R'     : Item(status='  ', wc_rev=3),
1654    'A/B/F/Q/R/bar' : Item(status='  ', wc_rev=3),
1655    'A/B/F/Q/baz'   : Item(status='  ', wc_rev=3),
1656    })
1657  svntest.actions.run_and_verify_commit(wc_dir,
1658                                        expected_output,
1659                                        expected_status)
1660
1661  os.unlink(foo_path)
1662  svntest.main.safe_rmtree(Q_path)
1663
1664  expected_output = wc.State(F_path, {
1665    })
1666  expected_mergeinfo_output = wc.State(F_path, {
1667    })
1668  expected_elision_output = wc.State(F_path, {
1669    })
1670  expected_disk = wc.State('', {
1671    })
1672  expected_status = wc.State(F_path, {
1673    ''      : Item(status='  ', wc_rev=1),
1674    'foo'   : Item(status='! ', wc_rev=2),
1675    'Q'     : Item(status='! ', wc_rev=2),
1676    # Missing data still available
1677    'Q/R'      : Item(status='! ', wc_rev=3),
1678    'Q/R/bar'  : Item(status='! ', wc_rev=3),
1679    'Q/baz'    : Item(status='! ', wc_rev=3),
1680  })
1681  expected_skip = wc.State(F_path, {
1682    'Q'   : Item(verb='Skipped missing target'),
1683    'foo' : Item(verb='Skipped missing target'),
1684    })
1685  # Use --ignore-ancestry because merge tracking aware merges raise an
1686  # error when the merge target is missing subtrees due to OS-level
1687  # deletes.
1688
1689  ### Need to real and dry-run separately since real merge notifies Q
1690  ### twice!
1691  svntest.actions.run_and_verify_merge(F_path, '1', '2', F_url, None,
1692                                       expected_output,
1693                                       expected_mergeinfo_output,
1694                                       expected_elision_output,
1695                                       expected_disk,
1696                                       expected_status,
1697                                       expected_skip,
1698                                       [], False, False,
1699                                       '--dry-run',
1700                                       '--ignore-ancestry',
1701                                       '--allow-mixed-revisions',
1702                                       F_path)
1703
1704  expected_status = wc.State(F_path, {
1705    ''      : Item(status='  ', wc_rev=1),
1706    'foo'   : Item(status='! ', wc_rev=2),
1707    'Q'     : Item(status='! ', wc_rev='2'),
1708    # Revision is known and we can record mergeinfo
1709    'Q/R'      : Item(status='! ', wc_rev='3'),
1710    'Q/R/bar'  : Item(status='! ', wc_rev='3'),
1711    'Q/baz'    : Item(status='! ', wc_rev='3'),
1712  })
1713  expected_mergeinfo_output = wc.State(F_path, {
1714    })
1715
1716  svntest.actions.run_and_verify_merge(F_path, '1', '2', F_url, None,
1717                                       expected_output,
1718                                       expected_mergeinfo_output,
1719                                       expected_elision_output,
1720                                       expected_disk,
1721                                       expected_status,
1722                                       expected_skip,
1723                                       [], False, False,
1724                                       '--ignore-ancestry',
1725                                       '--allow-mixed-revisions',
1726                                       F_path)
1727
1728  # This merge fails when it attempts to descend into the missing
1729  # directory.  That's OK, there is no real need to support merge into
1730  # an incomplete working copy, so long as when it fails it doesn't
1731  # break the working copy.
1732  svntest.main.run_svn('Working copy not locked',
1733                       'merge', '-r1:3', '--dry-run', F_url, F_path)
1734
1735  svntest.main.run_svn('Working copy not locked',
1736                       'merge', '-r1:3', F_url, F_path)
1737
1738  # Check working copy is not locked.
1739  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
1740  expected_status.add({
1741    'A/B/F'     : Item(status='  ', wc_rev=1),
1742    'A/B/F/foo' : Item(status='! ', wc_rev=2),
1743    'A/B/F/Q'   : Item(status='! ', wc_rev=2),
1744    'A/B/F/Q/baz'    : Item(status='! ', wc_rev='3'),
1745    'A/B/F/Q/R'      : Item(status='! ', wc_rev='3'),
1746    'A/B/F/Q/R/bar'  : Item(status='! ', wc_rev='3'),
1747  })
1748
1749  svntest.actions.run_and_verify_status(wc_dir, expected_status)
1750
1751#----------------------------------------------------------------------
1752# A test for issue 1738
1753@Issue(1738)
1754@SkipUnless(server_has_mergeinfo)
1755def dry_run_adds_file_with_prop(sbox):
1756  "merge --dry-run adding a new file with props"
1757
1758  sbox.build()
1759  wc_dir = sbox.wc_dir
1760
1761  # Commit a new file which has a property.
1762  zig_path = sbox.ospath('A/B/E/zig')
1763  svntest.main.file_append(zig_path, "zig contents")
1764  svntest.actions.run_and_verify_svn(None, [], 'add', zig_path)
1765  svntest.actions.run_and_verify_svn(None, [],
1766                                     'propset', 'foo', 'foo_val',
1767                                     zig_path)
1768
1769  expected_output = wc.State(wc_dir, {
1770    'A/B/E/zig'     : Item(verb='Adding'),
1771    })
1772  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
1773  expected_status.add({
1774    'A/B/E/zig'   : Item(status='  ', wc_rev=2),
1775    })
1776  svntest.actions.run_and_verify_commit(wc_dir,
1777                                        expected_output,
1778                                        expected_status)
1779
1780  # Do a regular merge of that change into a different dir.
1781  F_path = sbox.ospath('A/B/F')
1782  E_url = sbox.repo_url + '/A/B/E'
1783
1784  expected_output = wc.State(F_path, {
1785    'zig'  : Item(status='A '),
1786    })
1787  expected_mergeinfo_output = wc.State(F_path, {
1788    '' : Item(status=' U'),
1789    })
1790  expected_elision_output = wc.State(F_path, {
1791    })
1792  expected_disk = wc.State('', {
1793    ''         : Item(props={SVN_PROP_MERGEINFO : '/A/B/E:2'}),
1794    'zig'      : Item("zig contents", {'foo':'foo_val'}),
1795    })
1796  expected_skip = wc.State('', { })
1797  expected_status = None  # status is optional
1798
1799  svntest.actions.run_and_verify_merge(F_path, '1', '2', E_url, None,
1800                                       expected_output,
1801                                       expected_mergeinfo_output,
1802                                       expected_elision_output,
1803                                       expected_disk,
1804                                       expected_status,
1805                                       expected_skip,
1806                                       [], True, True)
1807
1808#----------------------------------------------------------------------
1809# Regression test for issue #1673
1810# Merge a binary file from two URL with a common ancestry
1811@Issue(1673)
1812def merge_binary_with_common_ancestry(sbox):
1813  "merge binary files with common ancestry"
1814
1815  sbox.build()
1816  wc_dir = sbox.wc_dir
1817
1818  # Create the common ancestry path
1819  I_path = sbox.ospath('I')
1820  svntest.main.run_svn(None, 'mkdir', I_path)
1821
1822  # Add a binary file to the common ancestry path
1823  theta_contents = open(os.path.join(sys.path[0], "theta.bin"), 'rb').read()
1824  theta_I_path = os.path.join(I_path, 'theta')
1825  svntest.main.file_write(theta_I_path, theta_contents, mode='wb')
1826  svntest.main.run_svn(None, 'add', theta_I_path)
1827  svntest.main.run_svn(None, 'propset', 'svn:mime-type',
1828                       'application/octet-stream', theta_I_path)
1829
1830  # Commit the ancestry
1831  expected_output = wc.State(wc_dir, {
1832    'I'       : Item(verb='Adding'),
1833    'I/theta' : Item(verb='Adding  (bin)'),
1834    })
1835
1836  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
1837  expected_status.add({
1838    'I'       : Item(status='  ', wc_rev=2),
1839    'I/theta' : Item(status='  ', wc_rev=2),
1840    })
1841
1842  svntest.actions.run_and_verify_commit(wc_dir,
1843                                        expected_output, expected_status)
1844
1845  # Create the first branch
1846  J_path = sbox.ospath('J')
1847  svntest.main.run_svn(None, 'copy', I_path, J_path)
1848
1849  # Commit the first branch
1850  expected_output = wc.State(wc_dir, {
1851    'J' : Item(verb='Adding'),
1852    })
1853
1854  expected_status.add({
1855    'J'       : Item(status='  ', wc_rev=3),
1856    'J/theta' : Item(status='  ', wc_rev=3),
1857    })
1858
1859  svntest.actions.run_and_verify_commit(wc_dir,
1860                                        expected_output, expected_status)
1861
1862  # Create the path where the files will be merged
1863  K_path = sbox.ospath('K')
1864  svntest.main.run_svn(None, 'mkdir', K_path)
1865
1866  # Commit the new path
1867  expected_output = wc.State(wc_dir, {
1868    'K' : Item(verb='Adding'),
1869    })
1870
1871  expected_status.add({
1872    'K'       : Item(status='  ', wc_rev=4),
1873    })
1874
1875  svntest.actions.run_and_verify_commit(wc_dir,
1876                                        expected_output, expected_status)
1877
1878  # Copy 'I/theta' to 'K/'. This file will be merged later.
1879  theta_K_path = os.path.join(K_path, 'theta')
1880  svntest.main.run_svn(None, 'copy', theta_I_path, theta_K_path)
1881
1882  # Commit the new file
1883  expected_output = wc.State(wc_dir, {
1884    'K/theta' : Item(verb='Adding  (bin)'),
1885    })
1886
1887  expected_status.add({
1888    'K/theta' : Item(status='  ', wc_rev=5),
1889    })
1890
1891  svntest.actions.run_and_verify_commit(wc_dir,
1892                                        expected_output, expected_status)
1893
1894  # Modify the original ancestry 'I/theta'
1895  svntest.main.file_append(theta_I_path, "some extra junk")
1896
1897  # Commit the modification
1898  expected_output = wc.State(wc_dir, {
1899    'I/theta' : Item(verb='Sending'),
1900    })
1901
1902  expected_status.tweak('I/theta', wc_rev=6)
1903
1904  svntest.actions.run_and_verify_commit(wc_dir,
1905                                        expected_output, expected_status)
1906
1907  # Create the second branch from the modified ancestry
1908  L_path = sbox.ospath('L')
1909  svntest.main.run_svn(None, 'copy', I_path, L_path)
1910
1911  # Commit the second branch
1912  expected_output = wc.State(wc_dir, {
1913    'L'       : Item(verb='Adding'),
1914    'L/theta' : Item(verb='Replacing'),
1915    })
1916
1917  expected_status.add({
1918    'L'       : Item(status='  ', wc_rev=7),
1919    'L/theta' : Item(status='  ', wc_rev=7),
1920    })
1921
1922  svntest.actions.run_and_verify_commit(wc_dir,
1923                                        expected_output, expected_status)
1924
1925  # Now merge first ('J/') and second ('L/') branches into 'K/'
1926  saved_cwd = os.getcwd()
1927
1928  os.chdir(K_path)
1929  theta_J_url = sbox.repo_url + '/J/theta'
1930  theta_L_url = sbox.repo_url + '/L/theta'
1931  svntest.actions.run_and_verify_svn(expected_merge_output(None,
1932                                                           ['U    theta\n',
1933                                                            ' U   theta\n',
1934                                                            ' G   theta\n',],
1935                                                           two_url=True),
1936                                     [],
1937                                     'merge', theta_J_url, theta_L_url)
1938  os.chdir(saved_cwd)
1939
1940  expected_status.tweak('K/theta', status='MM')
1941  svntest.actions.run_and_verify_status(wc_dir, expected_status)
1942
1943#----------------------------------------------------------------------
1944# A test for issue 1905
1945@Issue(1905)
1946@SkipUnless(server_has_mergeinfo)
1947def merge_funny_chars_on_path(sbox):
1948  "merge with funny characters"
1949
1950  sbox.build()
1951  wc_dir = sbox.wc_dir
1952
1953  # In following lists: 'd' stands for directory, 'f' for file
1954  # targets to be added by recursive add
1955  add_by_add = [
1956    ('d', 'dir_10', 'F%lename'),
1957    ('d', 'dir%20', 'F lename'),
1958    ('d', 'dir 30', 'Filename'),
1959    ('d', 'dir 40', None),
1960    ('f', 'F lename', None),
1961    ]
1962
1963  # targets to be added by 'svn mkdir' + add
1964  add_by_mkdir = [
1965    ('d', 'dir_11', 'F%lename'),
1966    ('d', 'dir%21', 'Filename'),
1967    ('d', 'dir 31', 'F lename'),
1968    ('d', 'dir 41', None),
1969    ]
1970
1971  for target in add_by_add:
1972    if target[0] == 'd':
1973      target_dir = os.path.join(wc_dir, 'A', 'B', 'E', target[1])
1974      os.mkdir(target_dir)
1975      if target[2]:
1976        target_path = os.path.join(wc_dir, 'A', 'B', 'E', '%s' % target[1],
1977                                   target[2])
1978        svntest.main.file_append(target_path, "%s/%s" % (target[1], target[2]))
1979      svntest.actions.run_and_verify_svn(None, [], 'add', target_dir)
1980    elif target[0] == 'f':
1981        target_path = os.path.join(wc_dir, 'A', 'B', 'E', '%s' % target[1])
1982        svntest.main.file_append(target_path, "%s" % target[1])
1983        svntest.actions.run_and_verify_svn(None, [], 'add', target_path)
1984    else:
1985      raise svntest.Failure
1986
1987
1988  for target in add_by_mkdir:
1989    if target[0] == 'd':
1990      target_dir = os.path.join(wc_dir, 'A', 'B', 'E', target[1])
1991      svntest.actions.run_and_verify_svn(None, [], 'mkdir', target_dir)
1992      if target[2]:
1993        target_path = os.path.join(wc_dir, 'A', 'B', 'E', '%s' % target[1],
1994                                   target[2])
1995        svntest.main.file_append(target_path, "%s/%s" % (target[1], target[2]))
1996        svntest.actions.run_and_verify_svn(None, [], 'add', target_path)
1997
1998  expected_output_dic = {}
1999  expected_status_dic = {}
2000
2001  for targets in add_by_add,add_by_mkdir:
2002    for target in targets:
2003      key = 'A/B/E/%s' % target[1]
2004      expected_output_dic[key] = Item(verb='Adding')
2005      expected_status_dic[key] = Item(status='  ', wc_rev=2)
2006
2007      if target[2]:
2008        key = 'A/B/E/%s/%s' % (target[1], target[2])
2009        expected_output_dic[key] = Item(verb='Adding')
2010        expected_status_dic[key] = Item(status='  ', wc_rev=2)
2011
2012
2013  expected_output = wc.State(wc_dir, expected_output_dic)
2014
2015  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
2016  expected_status.add(expected_status_dic)
2017
2018  svntest.actions.run_and_verify_commit(wc_dir,
2019                                        expected_output,
2020                                        expected_status)
2021
2022  # Do a regular merge of that change into a different dir.
2023  F_path = sbox.ospath('A/B/F')
2024  E_url = sbox.repo_url + '/A/B/E'
2025
2026  expected_output_dic = {}
2027  expected_disk_dic = {}
2028
2029  for targets in add_by_add,add_by_mkdir:
2030    for target in targets:
2031      key = '%s' % target[1]
2032      expected_output_dic[key] = Item(status='A ')
2033      if target[0] == 'd':
2034        expected_disk_dic[key] = Item(None, {})
2035      elif target[0] == 'f':
2036        expected_disk_dic[key] = Item("%s" % target[1], {})
2037      else:
2038        raise svntest.Failure
2039      if target[2]:
2040        key = '%s/%s' % (target[1], target[2])
2041        expected_output_dic[key] = Item(status='A ')
2042        expected_disk_dic[key] = Item('%s/%s' % (target[1], target[2]), {})
2043
2044  expected_output = wc.State(F_path, expected_output_dic)
2045  expected_mergeinfo_output = wc.State(F_path, {
2046    '' : Item(status=' U'),
2047    })
2048  expected_elision_output = wc.State(F_path, {
2049    })
2050  expected_disk = wc.State('', expected_disk_dic)
2051  expected_skip = wc.State('', { })
2052  expected_status = None  # status is optional
2053
2054  svntest.actions.run_and_verify_merge(F_path, '1', '2', E_url, None,
2055                                       expected_output,
2056                                       expected_mergeinfo_output,
2057                                       expected_elision_output,
2058                                       expected_disk,
2059                                       expected_status,
2060                                       expected_skip,
2061                                       [],
2062                                       False, # don't check props
2063                                       True) # but do a dry-run
2064
2065  expected_output_dic = {}
2066
2067  for targets in add_by_add,add_by_mkdir:
2068    for target in targets:
2069      key = '%s' % target[1]
2070      expected_output_dic[key] = Item(verb='Adding')
2071
2072  expected_output = wc.State(F_path, expected_output_dic)
2073  expected_output.add({
2074    '' : Item(verb='Sending'),
2075    })
2076
2077  svntest.actions.run_and_verify_commit(F_path,
2078                                        expected_output,
2079                                        None)
2080
2081#-----------------------------------------------------------------------
2082# Regression test for issue #2064
2083@Issue(2064)
2084def merge_keyword_expansions(sbox):
2085  "merge changes to keyword expansion property"
2086
2087  sbox.build()
2088
2089  wcpath = sbox.wc_dir
2090  tpath = os.path.join(wcpath, "t")
2091  bpath = os.path.join(wcpath, "b")
2092  t_fpath = os.path.join(tpath, 'f')
2093  b_fpath = os.path.join(bpath, 'f')
2094
2095  os.mkdir(tpath)
2096  svntest.main.run_svn(None, "add", tpath)
2097  # Commit r2.
2098  svntest.actions.run_and_verify_svn(None, [],
2099                                     "ci", "-m", "r2", wcpath)
2100
2101  # Copy t to b.
2102  svntest.main.run_svn(None, "cp", tpath, bpath)
2103  # Commit r3
2104  svntest.actions.run_and_verify_svn(None, [],
2105                                     "ci", "-m", "r3", wcpath)
2106
2107  # Add a file to t.
2108  svntest.main.file_append(t_fpath, "$Revision$")
2109  svntest.actions.run_and_verify_svn(None, [],
2110                                     'add', t_fpath)
2111  # Ask for keyword expansion in the file.
2112  svntest.actions.run_and_verify_svn(None, [],
2113                                     'propset', 'svn:keywords', 'Revision',
2114                                     t_fpath)
2115  # Commit r4
2116  svntest.actions.run_and_verify_svn(None, [],
2117                                     'ci', '-m', 'r4', wcpath)
2118
2119  # Update the wc before the merge.
2120  svntest.actions.run_and_verify_svn(None, [],
2121                                     'update', wcpath)
2122
2123  expected_status = svntest.actions.get_virginal_state(wcpath, 4)
2124  expected_status.add({
2125    't'    : Item(status='  ', wc_rev=4),
2126    't/f'  : Item(status='  ', wc_rev=4),
2127    'b'    : Item(status='  ', wc_rev=4),
2128  })
2129  svntest.actions.run_and_verify_status(wcpath, expected_status)
2130
2131  # Do the merge.
2132
2133  expected_output = wc.State(bpath, {
2134    'f'  : Item(status='A '),
2135    })
2136  expected_mergeinfo_output = wc.State(bpath, {
2137    '' : Item(status=' U'),
2138    })
2139  expected_elision_output = wc.State(bpath, {
2140    })
2141  expected_disk = wc.State('', {
2142    'f'      : Item("$Revision: 4 $"),
2143    })
2144  expected_status = wc.State(bpath, {
2145    ''       : Item(status=' M', wc_rev=4),
2146    'f'      : Item(status='A ', wc_rev='-', copied='+'),
2147    })
2148  expected_skip = wc.State(bpath, { })
2149
2150  svntest.actions.run_and_verify_merge(bpath, '2', 'HEAD',
2151                                       sbox.repo_url + '/t', None,
2152                                       expected_output,
2153                                       expected_mergeinfo_output,
2154                                       expected_elision_output,
2155                                       expected_disk,
2156                                       expected_status,
2157                                       expected_skip)
2158
2159#----------------------------------------------------------------------
2160@Issue(2132)
2161def merge_prop_change_to_deleted_target(sbox):
2162  "merge prop change into deleted target"
2163  # For issue #2132.
2164  sbox.build()
2165  wc_dir = sbox.wc_dir
2166
2167  # Add a property to alpha.
2168  alpha_path = sbox.ospath('A/B/E/alpha')
2169  svntest.actions.run_and_verify_svn(None, [],
2170                                     'propset', 'foo', 'foo_val',
2171                                     alpha_path)
2172
2173  # Commit the property add as r2.
2174  expected_output = svntest.wc.State(wc_dir, {
2175    'A/B/E/alpha' : Item(verb='Sending'),
2176    })
2177  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
2178  expected_status.tweak('A/B/E/alpha', wc_rev=2, status='  ')
2179  svntest.actions.run_and_verify_commit(wc_dir,
2180                                        expected_output, expected_status)
2181  svntest.actions.run_and_verify_svn(None, [],
2182                                     'up', wc_dir)
2183
2184  # Remove alpha entirely.
2185  svntest.actions.run_and_verify_svn(None, [], 'rm', alpha_path)
2186  expected_output = wc.State(wc_dir, {
2187    'A/B/E/alpha'  : Item(verb='Deleting'),
2188    })
2189  expected_status.tweak(wc_rev=2)
2190  expected_status.remove('A/B/E/alpha')
2191  svntest.actions.run_and_verify_commit(wc_dir,
2192                                        expected_output,
2193                                        expected_status,
2194                                        [], alpha_path)
2195
2196  # Try merging the original propset, which applies to a target that
2197  # no longer exists.  The bug would only reproduce when run from
2198  # inside the wc, so we cd in there.  We have to use
2199  # --ignore-ancestry here because our merge logic will otherwise
2200  # prevent a merge of changes we already have.
2201  os.chdir(wc_dir)
2202  svntest.actions.run_and_verify_svn(svntest.verify.AnyOutput, [], 'merge',
2203                                     '-r1:2', '--ignore-ancestry', '.')
2204
2205#----------------------------------------------------------------------
2206# A merge that replaces a directory
2207# Tests for Issue #2144 and Issue #2607
2208@SkipUnless(server_has_mergeinfo)
2209@Issue(2144,2607)
2210def merge_dir_replace(sbox):
2211  "merge a replacement of a directory"
2212
2213  set_up_dir_replace(sbox)
2214  wc_dir = sbox.wc_dir
2215
2216  C_path = sbox.ospath('A/C')
2217  F_path = sbox.ospath('A/B/F')
2218  F_url = sbox.repo_url + '/A/B/F'
2219  foo_path = os.path.join(F_path, 'foo')
2220  new_file2 = os.path.join(foo_path, "new file 2")
2221
2222  # Recreate foo in F and add a new folder and two files
2223  bar_path = os.path.join(foo_path, 'bar')
2224  foo_file = os.path.join(foo_path, "file foo")
2225  new_file3 = os.path.join(bar_path, "new file 3")
2226
2227  # Make a couple of directories, and add some files within them.
2228  svntest.actions.run_and_verify_svn(None, [], 'mkdir', foo_path)
2229  svntest.actions.run_and_verify_svn(None, [], 'mkdir', bar_path)
2230  svntest.main.file_append(new_file3, "Initial text in new file 3.\n")
2231  svntest.main.run_svn(None, "add", new_file3)
2232  svntest.main.file_append(foo_file, "Initial text in file foo.\n")
2233  svntest.main.run_svn(None, "add", foo_file)
2234
2235  # Commit the new content, creating r5.
2236  expected_output = wc.State(wc_dir, {
2237    'A/B/F/foo'                : Item(verb='Adding'),
2238    'A/B/F/foo/file foo'       : Item(verb='Adding'),
2239    'A/B/F/foo/bar'            : Item(verb='Adding'),
2240    'A/B/F/foo/bar/new file 3' : Item(verb='Adding'),
2241    })
2242  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
2243  expected_status.add({
2244    'A/B/F/foo'             : Item(status='  ', wc_rev=5),
2245    'A/B/F/foo/file foo'    : Item(status='  ', wc_rev=5),
2246    'A/B/F/foo/bar'         : Item(status='  ', wc_rev=5),
2247    'A/B/F/foo/bar/new file 3'  : Item(status='  ', wc_rev=5),
2248    'A/C'                   : Item(status='  ', wc_rev=3),
2249    'A/C/foo'               : Item(status='  ', wc_rev=3),
2250    'A/C/foo/new file'      : Item(status='  ', wc_rev=3),
2251    'A/C/foo/new file 2'    : Item(status='  ', wc_rev=3),
2252    })
2253  svntest.actions.run_and_verify_commit(wc_dir,
2254                                        expected_output,
2255                                        expected_status)
2256  # Merge replacement of foo onto C
2257  expected_output = wc.State(C_path, {
2258    'foo' : Item(status='R '),
2259    'foo/file foo'   : Item(status='A '),
2260    'foo/bar'        : Item(status='A '),
2261    'foo/bar/new file 3' : Item(status='A '),
2262    })
2263  expected_mergeinfo_output = wc.State(C_path, {
2264    '' : Item(status=' U'),
2265    })
2266  expected_elision_output = wc.State(C_path, {
2267    })
2268  expected_disk = wc.State('', {
2269    ''    : Item(props={SVN_PROP_MERGEINFO : '/A/B/F:2-5'}),
2270    'foo' : Item(),
2271    'foo/file foo'       : Item("Initial text in file foo.\n"),
2272    'foo/bar' : Item(),
2273    'foo/bar/new file 3' : Item("Initial text in new file 3.\n"),
2274    })
2275  expected_status = wc.State(C_path, {
2276    ''    : Item(status=' M', wc_rev=3),
2277    'foo' : Item(status='R ', wc_rev='-', copied='+'),
2278    'foo/new file 2' : Item(status='D ', wc_rev='3'),
2279    'foo/file foo'       : Item(status='  ', wc_rev='-', copied='+'),
2280    'foo/bar'            : Item(status='  ', wc_rev='-', copied='+'),
2281    'foo/bar/new file 3' : Item(status='  ', wc_rev='-', copied='+'),
2282    'foo/new file'   : Item(status='D ', wc_rev='3'),
2283    })
2284  expected_skip = wc.State(C_path, { })
2285  svntest.actions.run_and_verify_merge(C_path, '2', '5', F_url, None,
2286                                       expected_output,
2287                                       expected_mergeinfo_output,
2288                                       expected_elision_output,
2289                                       expected_disk,
2290                                       expected_status,
2291                                       expected_skip,
2292                                       [], True,
2293                                       False) # don't do a dry-run
2294                                              # the output differs
2295
2296  # Commit merge of foo onto C
2297  expected_output = svntest.wc.State(wc_dir, {
2298    'A/C'                    : Item(verb='Sending'),
2299    'A/C/foo'                : Item(verb='Replacing'),
2300    })
2301  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
2302  expected_status.add({
2303    'A/B/F/foo'             : Item(status='  ', wc_rev=5),
2304    'A/B/F/foo/file foo'    : Item(status='  ', wc_rev=5),
2305    'A/B/F/foo/bar'         : Item(status='  ', wc_rev=5),
2306    'A/B/F/foo/bar/new file 3'  : Item(status='  ', wc_rev=5),
2307    'A/C'                       : Item(status='  ', wc_rev=6),
2308    'A/C/foo'                   : Item(status='  ', wc_rev=6),
2309    'A/C/foo/file foo'          : Item(status='  ', wc_rev=6),
2310    'A/C/foo/bar'               : Item(status='  ', wc_rev=6),
2311    'A/C/foo/bar/new file 3'    : Item(status='  ', wc_rev=6),
2312    })
2313  svntest.actions.run_and_verify_commit(wc_dir,
2314                                        expected_output,
2315                                        expected_status)
2316
2317#----------------------------------------------------------------------
2318# A merge that replaces a directory and one of its children
2319# Tests for Issue #2690
2320@Issue(2690)
2321def merge_dir_and_file_replace(sbox):
2322  "replace both dir and one of its children"
2323
2324  set_up_dir_replace(sbox)
2325  wc_dir = sbox.wc_dir
2326
2327  C_path = sbox.ospath('A/C')
2328  F_path = sbox.ospath('A/B/F')
2329  F_url = sbox.repo_url + '/A/B/F'
2330  foo_path = os.path.join(F_path, 'foo')
2331  new_file2 = os.path.join(foo_path, "new file 2")
2332
2333  # Recreate foo and 'new file 2' in F and add a new folder with a file
2334  bar_path = os.path.join(foo_path, 'bar')
2335  new_file3 = os.path.join(bar_path, "new file 3")
2336  svntest.actions.run_and_verify_svn(None, [], 'mkdir', foo_path)
2337  svntest.actions.run_and_verify_svn(None, [], 'mkdir', bar_path)
2338  svntest.main.file_append(new_file3, "Initial text in new file 3.\n")
2339  svntest.main.run_svn(None, "add", new_file3)
2340  svntest.main.file_append(new_file2, "New text in new file 2.\n")
2341  svntest.main.run_svn(None, "add", new_file2)
2342
2343  expected_output = wc.State(wc_dir, {
2344    'A/B/F/foo' : Item(verb='Adding'),
2345    'A/B/F/foo/new file 2'     : Item(verb='Adding'),
2346    'A/B/F/foo/bar'            : Item(verb='Adding'),
2347    'A/B/F/foo/bar/new file 3' : Item(verb='Adding'),
2348    })
2349  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
2350  expected_status.add({
2351    'A/B/F/foo'             : Item(status='  ', wc_rev=5),
2352    'A/B/F/foo/new file 2'  : Item(status='  ', wc_rev=5),
2353    'A/B/F/foo/bar'         : Item(status='  ', wc_rev=5),
2354    'A/B/F/foo/bar/new file 3'  : Item(status='  ', wc_rev=5),
2355    'A/C/foo'               : Item(status='  ', wc_rev=3),
2356    'A/C/foo/new file'      : Item(status='  ', wc_rev=3),
2357    'A/C/foo/new file 2'    : Item(status='  ', wc_rev=3),
2358    })
2359  expected_status.tweak('A/C', wc_rev=3) # From mergeinfo
2360  svntest.actions.run_and_verify_commit(wc_dir,
2361                                        expected_output,
2362                                        expected_status)
2363  # Merge replacement of foo onto C
2364  expected_output = wc.State(C_path, {
2365    'foo'                : Item(status='R '),
2366    'foo/new file 2'     : Item(status='A '),
2367    'foo/bar'            : Item(status='A '),
2368    'foo/bar/new file 3' : Item(status='A '),
2369    })
2370  expected_mergeinfo_output = wc.State(C_path, {
2371    '' : Item(status=' U'),
2372    })
2373  expected_elision_output = wc.State(C_path, {
2374    })
2375  expected_disk = wc.State('', {
2376    ''    : Item(props={SVN_PROP_MERGEINFO : '/A/B/F:2-5'}),
2377    'foo' : Item(),
2378    'foo/new file 2' : Item("New text in new file 2.\n"),
2379    'foo/bar' : Item(),
2380    'foo/bar/new file 3' : Item("Initial text in new file 3.\n"),
2381    })
2382  expected_status = wc.State(C_path, {
2383    ''                   : Item(status=' M', wc_rev=3),
2384    'foo'                : Item(status='R ', wc_rev='-', copied='+'),
2385    'foo/new file 2'     : Item(status='  ', wc_rev='-', copied='+'),
2386    'foo/bar'            : Item(status='  ', wc_rev='-', copied='+'),
2387    'foo/bar/new file 3' : Item(status='  ', wc_rev='-', copied='+'),
2388    'foo/new file'       : Item(status='D ', wc_rev=3),
2389    })
2390  expected_skip = wc.State(C_path, { })
2391  svntest.actions.run_and_verify_merge(C_path, '2', '5', F_url, None,
2392                                       expected_output,
2393                                       expected_mergeinfo_output,
2394                                       expected_elision_output,
2395                                       expected_disk,
2396                                       expected_status,
2397                                       expected_skip,
2398                                       [],
2399                                       True,
2400                                       False) # don't do a dry-run
2401                                              # the output differs
2402
2403  # Commit merge of foo onto C
2404  expected_output = svntest.wc.State(wc_dir, {
2405    'A/C'                    : Item(verb='Sending'),
2406    'A/C/foo'                : Item(verb='Replacing'),
2407    })
2408
2409  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
2410  expected_status.add({
2411    'A/B/F/foo'                : Item(status='  ', wc_rev=5),
2412    'A/B/F/foo/new file 2'     : Item(status='  ', wc_rev=5),
2413    'A/B/F/foo/bar'            : Item(status='  ', wc_rev=5),
2414    'A/B/F/foo/bar/new file 3' : Item(status='  ', wc_rev=5),
2415    'A/C'                      : Item(status='  ', wc_rev=6),
2416    'A/C/foo'                  : Item(status='  ', wc_rev=6),
2417    'A/C/foo/new file 2'       : Item(status='  ', wc_rev=6),
2418    'A/C/foo/bar'              : Item(status='  ', wc_rev=6),
2419    'A/C/foo/bar/new file 3'   : Item(status='  ', wc_rev=6),
2420    })
2421  svntest.actions.run_and_verify_commit(wc_dir,
2422                                        expected_output,
2423                                        expected_status)
2424
2425  # Confirm the files are present in the repository.
2426  new_file_2_url = sbox.repo_url + '/A/C/foo/new file 2'
2427  svntest.actions.run_and_verify_svn(["New text in new file 2.\n"],
2428                                     [], 'cat',
2429                                     new_file_2_url)
2430  new_file_3_url = sbox.repo_url + '/A/C/foo/bar/new file 3'
2431  svntest.actions.run_and_verify_svn(["Initial text in new file 3.\n"],
2432                                     [], 'cat',
2433                                     new_file_3_url)
2434
2435#----------------------------------------------------------------------
2436@Issue(2144)
2437def merge_file_with_space_in_its_name(sbox):
2438  "merge a file whose name contains a space"
2439  # For issue #2144
2440  sbox.build()
2441  wc_dir = sbox.wc_dir
2442  new_file = sbox.ospath('new file')
2443
2444  # Make r2.
2445  svntest.main.file_append(new_file, "Initial text in the file.\n")
2446  svntest.main.run_svn(None, "add", new_file)
2447  svntest.actions.run_and_verify_svn(None, [],
2448                                     "ci", "-m", "r2", wc_dir)
2449
2450  # Make r3.
2451  svntest.main.file_append(new_file, "Next line of text in the file.\n")
2452  svntest.actions.run_and_verify_svn(None, [],
2453                                     "ci", "-m", "r3", wc_dir)
2454
2455  # Try to reverse merge.
2456  #
2457  # The reproduction recipe requires that no explicit merge target be
2458  # passed, so we run merge from inside the wc dir where the target
2459  # file (i.e., the URL basename) lives.
2460  os.chdir(wc_dir)
2461  target_url = sbox.repo_url + '/new%20file'
2462  svntest.actions.run_and_verify_svn(None, [],
2463                                     "merge", "-r3:2", target_url)
2464
2465#----------------------------------------------------------------------
2466# A merge between two branches using no revision number with the dir being
2467# created already existing as an unversioned directory.
2468# Tests for Issue #2222
2469@Issue(2222)
2470def merge_dir_branches(sbox):
2471  "merge between branches"
2472
2473  sbox.build()
2474  wc_dir = sbox.wc_dir
2475  wc_uuid = svntest.actions.get_wc_uuid(wc_dir)
2476
2477  F_path = sbox.ospath('A/B/F')
2478  F_url = sbox.repo_url + '/A/B/F'
2479  C_url = sbox.repo_url + '/A/C'
2480
2481  # Create foo in F
2482  foo_path = os.path.join(F_path, 'foo')
2483  svntest.actions.run_and_verify_svn(None, [], 'mkdir', foo_path)
2484
2485  expected_output = wc.State(wc_dir, {
2486    'A/B/F/foo' : Item(verb='Adding'),
2487    })
2488  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
2489  expected_status.add({
2490    'A/B/F/foo'    : Item(status='  ', wc_rev=2),
2491    })
2492  svntest.actions.run_and_verify_commit(wc_dir,
2493                                        expected_output,
2494                                        expected_status)
2495
2496  # Create an unversioned foo
2497  foo_path = sbox.ospath('foo')
2498  os.mkdir(foo_path)
2499
2500  # Merge from C to F onto the wc_dir
2501  # We can't use run_and_verify_merge because it doesn't support this
2502  # syntax of the merge command.
2503  ### TODO: We can use run_and_verify_merge() here now.
2504  expected_output = expected_merge_output(None, "A    " + foo_path + "\n")
2505  svntest.actions.run_and_verify_svn(expected_output, [],
2506                                     'merge', '--allow-mixed-revisions',
2507                                     C_url, F_url, wc_dir)
2508
2509  # Run info to check the copied rev to make sure it's right
2510  expected_info = {"Path" : re.escape(foo_path), # escape backslashes
2511                   "URL" : sbox.repo_url + "/foo",
2512                   "Repository Root" : sbox.repo_url,
2513                   "Repository UUID" : wc_uuid,
2514                   "Revision" : "2",
2515                   "Node Kind" : "directory",
2516                   "Schedule" : "add",
2517                   "Copied From URL" : F_url + "/foo",
2518                   "Copied From Rev" : "2",
2519                  }
2520  svntest.actions.run_and_verify_info([expected_info], foo_path)
2521
2522
2523#----------------------------------------------------------------------
2524def safe_property_merge(sbox):
2525  "property merges don't overwrite existing prop-mods"
2526
2527  sbox.build()
2528  wc_dir = sbox.wc_dir
2529
2530  # Add a property to two files and a directory, commit as r2.
2531  alpha_path = sbox.ospath('A/B/E/alpha')
2532  beta_path = sbox.ospath('A/B/E/beta')
2533  E_path = sbox.ospath('A/B/E')
2534
2535  svntest.actions.run_and_verify_svn(None, [],
2536                                     'propset', 'foo', 'foo_val',
2537                                     alpha_path, beta_path)
2538  svntest.actions.run_and_verify_svn(None, [],
2539                                     'propset', 'foo', 'foo_val',
2540                                     E_path)
2541
2542  expected_output = svntest.wc.State(wc_dir, {
2543    'A/B/E'       : Item(verb='Sending'),
2544    'A/B/E/alpha' : Item(verb='Sending'),
2545    'A/B/E/beta'  : Item(verb='Sending'),
2546    })
2547  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
2548  expected_status.tweak('A/B/E', 'A/B/E/alpha', 'A/B/E/beta',
2549                        wc_rev=2, status='  ')
2550  svntest.actions.run_and_verify_commit(wc_dir,
2551                                        expected_output, expected_status)
2552  svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir)
2553
2554  # Copy B to B2 as rev 3  (making a branch)
2555  B_url = sbox.repo_url + '/A/B'
2556  B2_url = sbox.repo_url + '/A/B2'
2557
2558  svntest.actions.run_and_verify_svn(None, [],
2559                                     'copy', '-m', 'copy B to B2',
2560                                     B_url, B2_url)
2561  svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir)
2562
2563  # Change the properties underneath B again, and commit as r4
2564  svntest.actions.run_and_verify_svn(None, [],
2565                                     'propset', 'foo', 'foo_val2',
2566                                     alpha_path)
2567  svntest.actions.run_and_verify_svn(None, [],
2568                                     'propdel', 'foo',
2569                                     beta_path)
2570  svntest.actions.run_and_verify_svn(None, [],
2571                                     'propset', 'foo', 'foo_val2',
2572                                     E_path)
2573  expected_output = svntest.wc.State(wc_dir, {
2574    'A/B/E'       : Item(verb='Sending'),
2575    'A/B/E/alpha' : Item(verb='Sending'),
2576    'A/B/E/beta'  : Item(verb='Sending'),
2577    })
2578  svntest.actions.run_and_verify_commit(wc_dir,
2579                                        expected_output, None)
2580  svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir)
2581
2582  # Make local propchanges to E, alpha and beta in the branch.
2583  alpha_path2 = sbox.ospath('A/B2/E/alpha')
2584  beta_path2 = sbox.ospath('A/B2/E/beta')
2585  E_path2 = sbox.ospath('A/B2/E')
2586
2587  svntest.actions.run_and_verify_svn(None, [],
2588                                     'propset', 'foo', 'branchval',
2589                                     alpha_path2, beta_path2)
2590  svntest.actions.run_and_verify_svn(None, [],
2591                                     'propset', 'foo', 'branchval',
2592                                     E_path2)
2593
2594  # Now merge the recent B change to the branch.  Because we already
2595  # have local propmods, we should get property conflicts.
2596  B2_path = sbox.ospath('A/B2')
2597
2598  expected_output = wc.State(B2_path, {
2599    'E'        : Item(status=' C'),
2600    'E/alpha'  : Item(status=' C'),
2601    'E/beta'   : Item(status=' C'),
2602    })
2603  expected_mergeinfo_output = wc.State(B2_path, {
2604    '' : Item(status=' U'),
2605    })
2606  expected_elision_output = wc.State(B2_path, {
2607    })
2608  expected_disk = wc.State('', {
2609    ''         : Item(props={SVN_PROP_MERGEINFO : "/A/B:4"}),
2610    'E'        : Item(),
2611    'E/alpha'  : Item("This is the file 'alpha'.\n"),
2612    'E/beta'   : Item("This is the file 'beta'.\n"),
2613    'F'        : Item(),
2614    'lambda'   : Item("This is the file 'lambda'.\n"),
2615    })
2616  expected_disk.tweak('E', 'E/alpha', 'E/beta',
2617                      props={'foo' : 'branchval'}) # local mods still present
2618
2619  expected_status = wc.State(B2_path, {
2620    ''        : Item(status=' M'),
2621    'E'       : Item(status=' C'),
2622    'E/alpha' : Item(status=' C'),
2623    'E/beta'  : Item(status=' C'),
2624    'F'       : Item(status='  '),
2625    'lambda'  : Item(status='  '),
2626    })
2627  expected_status.tweak(wc_rev=4)
2628
2629  expected_skip = wc.State('', { })
2630
2631  # should have 3 'prej' files left behind, describing prop conflicts:
2632  extra_files = ['alpha.*\.prej', 'beta.*\.prej', 'dir_conflicts.*\.prej']
2633
2634  svntest.actions.run_and_verify_merge(B2_path, '3', '4', B_url, None,
2635                                       expected_output,
2636                                       expected_mergeinfo_output,
2637                                       expected_elision_output,
2638                                       expected_disk,
2639                                       expected_status,
2640                                       expected_skip,
2641                                       [], True, False,
2642                                       extra_files=extra_files)
2643
2644#----------------------------------------------------------------------
2645# Test for issue 2035, whereby 'svn merge' wouldn't always mark
2646# property conflicts when it should.
2647@Issue(2035)
2648@SkipUnless(server_has_mergeinfo)
2649def property_merge_from_branch(sbox):
2650  "property merge conflict even without local mods"
2651
2652  sbox.build()
2653  wc_dir = sbox.wc_dir
2654
2655  # Add a property to a file and a directory, commit as r2.
2656  alpha_path = sbox.ospath('A/B/E/alpha')
2657  E_path = sbox.ospath('A/B/E')
2658
2659  svntest.actions.run_and_verify_svn(None, [],
2660                                     'propset', 'foo', 'foo_val',
2661                                     alpha_path)
2662  svntest.actions.run_and_verify_svn(None, [],
2663                                     'propset', 'foo', 'foo_val',
2664                                     E_path)
2665
2666  expected_output = svntest.wc.State(wc_dir, {
2667    'A/B/E'       : Item(verb='Sending'),
2668    'A/B/E/alpha' : Item(verb='Sending'),
2669    })
2670  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
2671  expected_status.tweak('A/B/E', 'A/B/E/alpha', wc_rev=2, status='  ')
2672  svntest.actions.run_and_verify_commit(wc_dir,
2673                                        expected_output, expected_status)
2674  svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir)
2675
2676  # Copy B to B2 as rev 3  (making a branch)
2677  B_url = sbox.repo_url + '/A/B'
2678  B2_url = sbox.repo_url + '/A/B2'
2679
2680  svntest.actions.run_and_verify_svn(None, [],
2681                                     'copy', '-m', 'copy B to B2',
2682                                     B_url, B2_url)
2683  svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir)
2684
2685  # Change the properties underneath B again, and commit as r4
2686  svntest.actions.run_and_verify_svn(None, [],
2687                                     'propset', 'foo', 'foo_val2',
2688                                     alpha_path)
2689  svntest.actions.run_and_verify_svn(None, [],
2690                                     'propset', 'foo', 'foo_val2',
2691                                     E_path)
2692  expected_output = svntest.wc.State(wc_dir, {
2693    'A/B/E'       : Item(verb='Sending'),
2694    'A/B/E/alpha' : Item(verb='Sending'),
2695    })
2696  svntest.actions.run_and_verify_commit(wc_dir,
2697                                        expected_output, None)
2698  svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir)
2699
2700  # Make different propchanges changes to the B2 branch and commit as r5.
2701  alpha_path2 = sbox.ospath('A/B2/E/alpha')
2702  E_path2 = sbox.ospath('A/B2/E')
2703
2704  svntest.actions.run_and_verify_svn(None, [],
2705                                     'propset', 'foo', 'branchval',
2706                                     alpha_path2)
2707  svntest.actions.run_and_verify_svn(None, [],
2708                                     'propset', 'foo', 'branchval',
2709                                     E_path2)
2710  expected_output = svntest.wc.State(wc_dir, {
2711    'A/B2/E'       : Item(verb='Sending'),
2712    'A/B2/E/alpha' : Item(verb='Sending'),
2713    })
2714  svntest.actions.run_and_verify_commit(wc_dir,
2715                                        expected_output, None)
2716  svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir)
2717
2718  # Now merge the recent B change to the branch.  There are no local
2719  # mods anywhere, but we should still get property conflicts anyway!
2720  B2_path = sbox.ospath('A/B2')
2721
2722  expected_output = wc.State(B2_path, {
2723    'E'        : Item(status=' C'),
2724    'E/alpha'  : Item(status=' C'),
2725    })
2726  expected_mergeinfo_output = wc.State(B2_path, {
2727    '' : Item(status=' U'),
2728    })
2729  expected_elision_output = wc.State(B2_path, {
2730    })
2731  expected_disk = wc.State('', {
2732    ''         : Item(props={SVN_PROP_MERGEINFO : '/A/B:4'}),
2733    'E'        : Item(),
2734    'E/alpha'  : Item("This is the file 'alpha'.\n"),
2735    'E/beta'   : Item("This is the file 'beta'.\n"),
2736    'F'        : Item(),
2737    'lambda'   : Item("This is the file 'lambda'.\n"),
2738    })
2739  expected_disk.tweak('E', 'E/alpha',
2740                      props={'foo' : 'branchval'})
2741
2742  expected_status = wc.State(B2_path, {
2743    ''        : Item(status=' M'),
2744    'E'       : Item(status=' C'),
2745    'E/alpha' : Item(status=' C'),
2746    'E/beta'  : Item(status='  '),
2747    'F'       : Item(status='  '),
2748    'lambda'  : Item(status='  '),
2749    })
2750  expected_status.tweak(wc_rev=5)
2751
2752  expected_skip = wc.State('', { })
2753
2754  # should have 2 'prej' files left behind, describing prop conflicts:
2755  extra_files = ['alpha.*\.prej', 'dir_conflicts.*\.prej']
2756
2757  svntest.actions.run_and_verify_merge(B2_path, '3', '4', B_url, None,
2758                                       expected_output,
2759                                       expected_mergeinfo_output,
2760                                       expected_elision_output,
2761                                       expected_disk,
2762                                       expected_status,
2763                                       expected_skip,
2764                                       [], True, False,
2765                                       extra_files=extra_files)
2766
2767#----------------------------------------------------------------------
2768# Another test for issue 2035, whereby sometimes 'svn merge' marked
2769# property conflicts when it shouldn't!
2770@Issue(2035)
2771def property_merge_undo_redo(sbox):
2772  "undo, then redo a property merge"
2773
2774  sbox.build()
2775  wc_dir = sbox.wc_dir
2776
2777  # Add a property to a file, commit as r2.
2778  alpha_path = sbox.ospath('A/B/E/alpha')
2779  svntest.actions.run_and_verify_svn(None, [],
2780                                     'propset', 'foo', 'foo_val',
2781                                     alpha_path)
2782
2783  expected_output = svntest.wc.State(wc_dir, {
2784    'A/B/E/alpha' : Item(verb='Sending'),
2785    })
2786
2787  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
2788  expected_status.tweak('A/B/E/alpha', wc_rev=2, status='  ')
2789  svntest.actions.run_and_verify_commit(wc_dir,
2790                                        expected_output, expected_status)
2791  svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir)
2792
2793  # Use 'svn merge' to undo the commit.  ('svn merge -r2:1')
2794  # Result should be a single local-prop-mod.
2795  expected_output = wc.State(wc_dir, {'A/B/E/alpha'  : Item(status=' U'), })
2796  expected_mergeinfo_output = wc.State(wc_dir, {
2797    '' : Item(status=' U'),
2798    })
2799  expected_elision_output = wc.State(wc_dir, {
2800    '' : Item(status=' U'),
2801    })
2802  expected_disk = svntest.main.greek_state.copy()
2803
2804  expected_status = svntest.actions.get_virginal_state(wc_dir, 2)
2805  expected_status.tweak('A/B/E/alpha', status=' M')
2806
2807  expected_skip = wc.State('', { })
2808
2809  svntest.actions.run_and_verify_merge(wc_dir, '2', '1',
2810                                       sbox.repo_url, None,
2811                                       expected_output,
2812                                       expected_mergeinfo_output,
2813                                       expected_elision_output,
2814                                       expected_disk,
2815                                       expected_status,
2816                                       expected_skip,
2817                                       [], True, False)
2818
2819  # Change mind, re-apply the change ('svn merge -r1:2').
2820  # This should merge cleanly into existing prop-mod, status shows nothing.
2821  expected_output = wc.State(wc_dir, {'A/B/E/alpha'  : Item(status=' C'), })
2822  expected_mergeinfo_output = wc.State(wc_dir, {})
2823  expected_elision_output = wc.State(wc_dir, {})
2824  expected_elision_output = wc.State(wc_dir, {})
2825  expected_disk = svntest.main.greek_state.copy()
2826  expected_disk.add({'A/B/E/alpha.prej'
2827     : Item("Trying to add new property 'foo'\n"
2828            + "but the property has been locally deleted.\n"
2829            + "Incoming property value:\nfoo_val\n")})
2830
2831  expected_status = svntest.actions.get_virginal_state(wc_dir, 2)
2832  expected_status.tweak('A/B/E/alpha', status=' C')
2833
2834  expected_skip = wc.State('', { })
2835
2836  # Re-merge r1.  We have to use --ignore-ancestry here.  Otherwise
2837  # the merge logic will claim we already have this change (because it
2838  # was unable to record the previous undoing merge).
2839  svntest.actions.run_and_verify_merge(wc_dir, '1', '2',
2840                                       sbox.repo_url, None,
2841                                       expected_output,
2842                                       expected_mergeinfo_output,
2843                                       expected_elision_output,
2844                                       expected_disk,
2845                                       expected_status,
2846                                       expected_skip,
2847                                       [], True, False,
2848                                       '--ignore-ancestry', wc_dir)
2849
2850
2851
2852#----------------------------------------------------------------------
2853@SkipUnless(server_has_mergeinfo)
2854def cherry_pick_text_conflict(sbox):
2855  "cherry-pick a dependent change, get conflict"
2856
2857  sbox.build()
2858  wc_dir = sbox.wc_dir
2859
2860  A_path = sbox.ospath('A')
2861  A_url = sbox.repo_url + '/A'
2862  mu_path = os.path.join(A_path, 'mu')
2863  branch_A_url = sbox.repo_url + '/copy-of-A'
2864  branch_mu_path = sbox.ospath('copy-of-A/mu')
2865
2866  # Create a branch of A.
2867  svntest.actions.run_and_verify_svn(None, [], 'cp',
2868                                     A_url, branch_A_url,
2869                                     '-m', "Creating copy-of-A")
2870
2871  # Update to get the branch.
2872  svntest.actions.run_and_verify_svn(None, [],
2873                                     'update', wc_dir)
2874
2875  # Change mu's text on the branch, producing r3 through r6.
2876  for rev in range(3, 7):
2877    svntest.main.file_append(branch_mu_path, ("r%d\n" % rev) * 3)
2878    svntest.actions.run_and_verify_svn(None, [],
2879                                       'ci', '-m',
2880                                       'Add lines to mu in r%d.' % rev, wc_dir)
2881
2882  # Mark r5 as merged into trunk, to create disparate revision ranges
2883  # which need to be merged.
2884  svntest.actions.run_and_verify_svn(
2885    expected_merge_output([[5]],
2886                          [' U   ' + A_path + '\n']),
2887    [], 'merge', '-c5', '--record-only',
2888    branch_A_url, A_path)
2889
2890
2891  # Try to merge r4:6 into trunk, without r3.  It should fail.
2892  expected_output = wc.State(A_path, {
2893    'mu'       : Item(status='C '),
2894    })
2895  expected_mergeinfo_output = wc.State(A_path, {
2896    ''          : Item(status=' G')
2897    })
2898  expected_elision_output = wc.State(A_path, {
2899    })
2900  expected_disk = wc.State('', {
2901    'mu'        : Item("This is the file 'mu'.\n"
2902                       + make_conflict_marker_text('', "r3\n" * 3 + "r4\n" * 3, 3, 4,
2903                                                   old_text='r3\n' * 3)),
2904    'B'         : Item(),
2905    'B/lambda'  : Item("This is the file 'lambda'.\n"),
2906    'B/E'       : Item(),
2907    'B/E/alpha' : Item("This is the file 'alpha'.\n"),
2908    'B/E/beta'  : Item("This is the file 'beta'.\n"),
2909    'B/F'       : Item(),
2910    'C'         : Item(),
2911    'D'         : Item(),
2912    'D/gamma'   : Item("This is the file 'gamma'.\n"),
2913    'D/H'       : Item(),
2914    'D/H/chi'   : Item("This is the file 'chi'.\n"),
2915    'D/H/psi'   : Item("This is the file 'psi'.\n"),
2916    'D/H/omega' : Item("This is the file 'omega'.\n"),
2917    'D/G'       : Item(),
2918    'D/G/pi'    : Item("This is the file 'pi'.\n"),
2919    'D/G/rho'   : Item("This is the file 'rho'.\n"),
2920    'D/G/tau'   : Item("This is the file 'tau'.\n"),
2921    })
2922  expected_status = wc.State(A_path, {
2923    ''          : Item(status=' M'),
2924    'mu'        : Item(status='C '),
2925    'B'         : Item(status='  '),
2926    'B/lambda'  : Item(status='  '),
2927    'B/E'       : Item(status='  '),
2928    'B/E/alpha' : Item(status='  '),
2929    'B/E/beta'  : Item(status='  '),
2930    'B/F'       : Item(status='  '),
2931    'C'         : Item(status='  '),
2932    'D'         : Item(status='  '),
2933    'D/gamma'   : Item(status='  '),
2934    'D/H'       : Item(status='  '),
2935    'D/H/chi'   : Item(status='  '),
2936    'D/H/psi'   : Item(status='  '),
2937    'D/H/omega' : Item(status='  '),
2938    'D/G'       : Item(status='  '),
2939    'D/G/pi'    : Item(status='  '),
2940    'D/G/rho'   : Item(status='  '),
2941    'D/G/tau'   : Item(status='  '),
2942    })
2943  expected_status.tweak(wc_rev=2)
2944  expected_skip = wc.State('', { })
2945  expected_error = ".*conflicts were produced while merging r3:4.*"
2946  svntest.actions.run_and_verify_merge(A_path, '3', '6', branch_A_url, None,
2947                                       expected_output,
2948                                       expected_mergeinfo_output,
2949                                       expected_elision_output,
2950                                       expected_disk,
2951                                       expected_status,
2952                                       expected_skip,
2953                                       expected_error,
2954                                       extra_files=
2955                                       ["mu\.working",
2956                                        "mu\.merge-right\.r4",
2957                                        "mu\.merge-left\.r3"])
2958
2959#----------------------------------------------------------------------
2960# Test for issue 2135
2961@Issue(2135)
2962def merge_file_replace(sbox):
2963  "merge a replacement of a file"
2964
2965  sbox.build()
2966  wc_dir = sbox.wc_dir
2967
2968  # File scheduled for deletion
2969  rho_path = sbox.ospath('A/D/G/rho')
2970  svntest.actions.run_and_verify_svn(None, [], 'rm', rho_path)
2971
2972  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
2973  expected_status.tweak('A/D/G/rho', status='D ')
2974  svntest.actions.run_and_verify_status(wc_dir, expected_status)
2975
2976  expected_output = svntest.wc.State(wc_dir, {
2977    'A/D/G/rho': Item(verb='Deleting'),
2978    })
2979
2980  expected_status.remove('A/D/G/rho')
2981
2982  # Commit rev 2
2983  svntest.actions.run_and_verify_commit(wc_dir,
2984                                        expected_output,
2985                                        expected_status)
2986  # Create and add a new file.
2987  svntest.main.file_write(rho_path, "new rho\n")
2988  svntest.actions.run_and_verify_svn(None, [], 'add', rho_path)
2989
2990  # Commit revsion 3
2991  expected_status.add({
2992    'A/D/G/rho' : Item(status='A ', wc_rev='0')
2993    })
2994  svntest.actions.run_and_verify_status(wc_dir, expected_status)
2995  expected_output = svntest.wc.State(wc_dir, {
2996    'A/D/G/rho': Item(verb='Adding'),
2997    })
2998
2999  svntest.actions.run_and_verify_commit(wc_dir,
3000                                        expected_output,
3001                                        None)
3002
3003  # Update working copy
3004  expected_output = svntest.wc.State(wc_dir, {})
3005  expected_disk   = svntest.main.greek_state.copy()
3006  expected_disk.tweak('A/D/G/rho', contents='new rho\n' )
3007  expected_status.tweak(wc_rev='3')
3008  expected_status.tweak('A/D/G/rho', status='  ')
3009
3010  svntest.actions.run_and_verify_update(wc_dir,
3011                                        expected_output,
3012                                        expected_disk,
3013                                        expected_status)
3014
3015  # merge changes from r3:1
3016  expected_output = svntest.wc.State(wc_dir, {
3017    'A/D/G/rho': Item(status='R ')
3018    })
3019  expected_mergeinfo_output = svntest.wc.State(wc_dir, {
3020    '' : Item(status=' U')
3021    })
3022  expected_elision_output = wc.State(wc_dir, {
3023    '' : Item(status=' U')
3024    })
3025  expected_status.tweak('A/D/G/rho', status='R ', copied='+', wc_rev='-')
3026  expected_skip = wc.State(wc_dir, { })
3027  expected_disk.tweak('A/D/G/rho', contents="This is the file 'rho'.\n")
3028  svntest.actions.run_and_verify_merge(wc_dir, '3', '1',
3029                                       sbox.repo_url, None,
3030                                       expected_output,
3031                                       expected_mergeinfo_output,
3032                                       expected_elision_output,
3033                                       expected_disk,
3034                                       expected_status,
3035                                       expected_skip)
3036
3037  # Now commit merged wc
3038  expected_output = svntest.wc.State(wc_dir, {
3039    'A/D/G/rho': Item(verb='Replacing'),
3040    })
3041  expected_status.tweak('A/D/G/rho', status='  ', copied=None, wc_rev='4')
3042  svntest.actions.run_and_verify_commit(wc_dir,
3043                                        expected_output,
3044                                        expected_status)
3045
3046#----------------------------------------------------------------------
3047# Test for issue 2522
3048# Same as merge_file_replace, but without update before merge.
3049@Issue(2522)
3050def merge_file_replace_to_mixed_rev_wc(sbox):
3051  "merge a replacement of a file to mixed rev wc"
3052
3053  sbox.build()
3054  wc_dir = sbox.wc_dir
3055
3056  # File scheduled for deletion
3057  rho_path = sbox.ospath('A/D/G/rho')
3058  svntest.actions.run_and_verify_svn(None, [], 'rm', rho_path)
3059
3060  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
3061  expected_status.tweak('A/D/G/rho', status='D ')
3062  svntest.actions.run_and_verify_status(wc_dir, expected_status)
3063
3064  expected_output = svntest.wc.State(wc_dir, {
3065    'A/D/G/rho': Item(verb='Deleting'),
3066    })
3067
3068  expected_status.remove('A/D/G/rho')
3069
3070  # Commit rev 2
3071  svntest.actions.run_and_verify_commit(wc_dir,
3072                                        expected_output,
3073                                        expected_status)
3074
3075  # Update working copy
3076  expected_disk   = svntest.main.greek_state.copy()
3077  expected_disk.remove('A/D/G/rho' )
3078  expected_output = svntest.wc.State(wc_dir, {})
3079  expected_status.tweak(wc_rev='2')
3080
3081  svntest.actions.run_and_verify_update(wc_dir,
3082                                        expected_output,
3083                                        expected_disk,
3084                                        expected_status)
3085
3086  # Create and add a new file.
3087  svntest.main.file_write(rho_path, "new rho\n")
3088  svntest.actions.run_and_verify_svn(None, [], 'add', rho_path)
3089
3090  # Commit revsion 3
3091  expected_status.add({
3092    'A/D/G/rho' : Item(status='A ', wc_rev='0')
3093    })
3094  svntest.actions.run_and_verify_status(wc_dir, expected_status)
3095  expected_output = svntest.wc.State(wc_dir, {
3096    'A/D/G/rho': Item(verb='Adding'),
3097    })
3098
3099  expected_disk.add({'A/D/G/rho' : Item(contents='new rho\n')} )
3100  expected_status.tweak(wc_rev='2')
3101  expected_status.tweak('A/D/G/rho', status='  ', wc_rev='3')
3102
3103  svntest.actions.run_and_verify_commit(wc_dir,
3104                                        expected_output,
3105                                        expected_status)
3106
3107  # merge changes from r3:1...
3108  #
3109  # ...but first:
3110  #
3111  # Since "." is at revision 2, r3 is not part of "."'s implicit mergeinfo.
3112  # Merge tracking permits only reverse merges from explicit or implicit
3113  # mergeinfo, so only r2 would be reverse merged if we left the WC as is.
3114  # Normally we'd simply update the whole working copy, but since that would
3115  # defeat the purpose of this test (see the comment below), instead we'll
3116  # update only "." using --depth empty.  This preserves the intent of the
3117  # original mixed-rev test for this issue, but allows the merge tracking
3118  # logic to consider r3 as valid for reverse merging.
3119  svntest.actions.run_and_verify_svn(None, [],
3120                                     'up', '--depth', 'empty', wc_dir)
3121  expected_status.tweak('', wc_rev=3)
3122  expected_output = svntest.wc.State(wc_dir, {
3123    'A/D/G/rho': Item(status='R ')
3124    })
3125  expected_mergeinfo_output = svntest.wc.State(wc_dir, {
3126    '' : Item(status=' U')
3127    })
3128  expected_elision_output = wc.State(wc_dir, {
3129    '' : Item(status=' U')
3130    })
3131  expected_status.tweak('A/D/G/rho', status='R ', copied='+', wc_rev='-')
3132  expected_skip = wc.State(wc_dir, { })
3133  expected_disk.tweak('A/D/G/rho', contents="This is the file 'rho'.\n")
3134  svntest.actions.run_and_verify_merge(wc_dir, '3', '1',
3135                                       sbox.repo_url, None,
3136                                       expected_output,
3137                                       expected_mergeinfo_output,
3138                                       expected_elision_output,
3139                                       expected_disk,
3140                                       expected_status,
3141                                       expected_skip,
3142                                       [],
3143                                       True, False, '--allow-mixed-revisions',
3144                                       wc_dir)
3145
3146  # When issue #2522 was filed, svn used to break the WC if we didn't
3147  # update here. But nowadays, this no longer happens, so the separate
3148  # update step which was done here originally has been removed.
3149
3150  # Now commit merged wc
3151  expected_output = svntest.wc.State(wc_dir, {
3152    'A/D/G/rho': Item(verb='Replacing'),
3153    })
3154  expected_status.tweak('A/D/G/rho', status='  ', copied=None, wc_rev='4')
3155  svntest.actions.run_and_verify_commit(wc_dir,
3156                                        expected_output,
3157                                        expected_status)
3158
3159#----------------------------------------------------------------------
3160# use -x -w option for ignoring whitespace during merge
3161@SkipUnless(server_has_mergeinfo)
3162def merge_ignore_whitespace(sbox):
3163  "ignore whitespace when merging"
3164
3165  sbox.build()
3166  wc_dir = sbox.wc_dir
3167
3168  # commit base version of iota
3169  file_name = "iota"
3170  file_path = os.path.join(wc_dir, file_name)
3171  file_url = sbox.repo_url + '/iota'
3172
3173  svntest.main.file_write(file_path,
3174                          "Aa\n"
3175                          "Bb\n"
3176                          "Cc\n")
3177  expected_output = svntest.wc.State(wc_dir, {
3178      'iota' : Item(verb='Sending'),
3179      })
3180  svntest.actions.run_and_verify_commit(wc_dir, expected_output, None)
3181
3182  # change the file, mostly whitespace changes + an extra line
3183  svntest.main.file_write(file_path, "A  a\nBb \n Cc\nNew line in iota\n")
3184  expected_output = wc.State(wc_dir, { file_name : Item(verb='Sending'), })
3185  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
3186  expected_status.tweak(file_name, wc_rev=3)
3187  svntest.actions.run_and_verify_commit(wc_dir,
3188                                        expected_output,
3189                                        expected_status)
3190
3191  # Backdate iota to revision 2, so we can merge in the rev 3 changes.
3192  svntest.actions.run_and_verify_svn(None, [],
3193                                     'up', '-r', '2', file_path)
3194  # Make some local whitespace changes, these should not conflict
3195  # with the remote whitespace changes as both will be ignored.
3196  svntest.main.file_write(file_path, "    Aa\nB b\nC c\n")
3197
3198  # Lines changed only by whitespace - both in local or remote -
3199  # should be ignored
3200  expected_output = wc.State(sbox.wc_dir, { file_name : Item(status='G ') })
3201  expected_mergeinfo_output = wc.State(sbox.wc_dir, {
3202    '' : Item(status=' U'),
3203    })
3204  expected_elision_output = wc.State(sbox.wc_dir, {
3205    })
3206  expected_disk = svntest.main.greek_state.copy()
3207  expected_disk.tweak(file_name,
3208                      contents="    Aa\n"
3209                               "B b\n"
3210                               "C c\n"
3211                               "New line in iota\n")
3212  expected_status = svntest.actions.get_virginal_state(sbox.wc_dir, 1)
3213  expected_status.tweak('', status=' M', wc_rev=1)
3214  expected_status.tweak(file_name, status='M ', wc_rev=2)
3215  expected_skip = wc.State('', { })
3216
3217  svntest.actions.run_and_verify_merge(sbox.wc_dir, '2', '3',
3218                                       sbox.repo_url, None,
3219                                       expected_output,
3220                                       expected_mergeinfo_output,
3221                                       expected_elision_output,
3222                                       expected_disk,
3223                                       expected_status,
3224                                       expected_skip,
3225                                       [], False, False,
3226                                       '--allow-mixed-revisions',
3227                                       '-x', '-w', wc_dir)
3228
3229#----------------------------------------------------------------------
3230# use -x --ignore-eol-style option for ignoring eolstyle during merge
3231@SkipUnless(server_has_mergeinfo)
3232def merge_ignore_eolstyle(sbox):
3233  "ignore eolstyle when merging"
3234
3235  sbox.build()
3236  wc_dir = sbox.wc_dir
3237
3238  # commit base version of iota
3239  file_name = "iota"
3240  file_path = os.path.join(wc_dir, file_name)
3241  file_url = sbox.repo_url + '/iota'
3242
3243  svntest.main.file_write(file_path,
3244                          "Aa\r\n"
3245                          "Bb\r\n"
3246                          "Cc\r\n",
3247                          "wb")
3248  expected_output = svntest.wc.State(wc_dir, {
3249      'iota' : Item(verb='Sending'),
3250      })
3251  svntest.actions.run_and_verify_commit(wc_dir, expected_output, None)
3252
3253  # change the file, mostly eol changes + an extra line
3254  svntest.main.file_write(file_path,
3255                          "Aa\r"
3256                          "Bb\n"
3257                          "Cc\r"
3258                          "New line in iota\n",
3259                          "wb")
3260  expected_output = wc.State(wc_dir, { file_name : Item(verb='Sending'), })
3261  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
3262  expected_status.tweak(file_name, wc_rev=3)
3263  svntest.actions.run_and_verify_commit(wc_dir,
3264                                        expected_output,
3265                                        expected_status)
3266
3267  # Backdate iota to revision 2, so we can merge in the rev 3 changes.
3268  svntest.actions.run_and_verify_svn(None, [],
3269                                     'up', '-r', '2', file_path)
3270  # Make some local eol changes, these should not conflict
3271  # with the remote eol changes as both will be ignored.
3272  svntest.main.file_write(file_path,
3273                          "Aa\n"
3274                          "Bb\r"
3275                          "Cc\n",
3276                          "wb")
3277
3278  # Lines changed only by eolstyle - both in local or remote -
3279  # should be ignored
3280  expected_output = wc.State(sbox.wc_dir, { file_name : Item(status='G ') })
3281  expected_mergeinfo_output = wc.State(sbox.wc_dir, {
3282    '' : Item(status=' U'),
3283    })
3284  expected_elision_output = wc.State(sbox.wc_dir, {
3285    })
3286  expected_disk = svntest.main.greek_state.copy()
3287  expected_disk.tweak(file_name,
3288                      contents="Aa\n"
3289                               "Bb\r"
3290                               "Cc\n"
3291                               "New line in iota\n")
3292  expected_status = svntest.actions.get_virginal_state(sbox.wc_dir, 1)
3293  expected_status.tweak('', status=' M')
3294  expected_status.tweak(file_name, status='M ', wc_rev=2)
3295  expected_skip = wc.State('', { })
3296
3297  svntest.actions.run_and_verify_merge2(sbox.wc_dir, '2', '3',
3298                                        sbox.repo_url, None,
3299                                        expected_output,
3300                                        expected_mergeinfo_output,
3301                                        expected_elision_output,
3302                                        expected_disk,
3303                                        expected_status,
3304                                        expected_skip,
3305                                        [], False, False, True,
3306                                        '--allow-mixed-revisions',
3307                                        '-x', '--ignore-eol-style', wc_dir)
3308
3309#----------------------------------------------------------------------
3310# eol-style handling during merge with conflicts, scenario 1:
3311# when a merge creates a conflict on a file, make sure the file and files
3312# r<left>, r<right> and .mine are in the eol-style defined for that file.
3313#
3314# This test for 'svn update' can be found in update_tests.py as
3315# conflict_markers_matching_eol.
3316@SkipUnless(server_has_mergeinfo)
3317def merge_conflict_markers_matching_eol(sbox):
3318  "conflict markers should match the file's eol style"
3319
3320  sbox.build()
3321  wc_dir = sbox.wc_dir
3322  filecount = 1
3323
3324  mu_path = sbox.ospath('A/mu')
3325
3326  if os.name == 'nt':
3327    native_nl = '\r\n'
3328  else:
3329    native_nl = '\n'
3330  crlf = '\r\n'
3331
3332  # Checkout a second working copy
3333  wc_backup = sbox.add_wc_path('backup')
3334  svntest.actions.run_and_verify_svn(None, [], 'checkout',
3335                                     sbox.repo_url, wc_backup)
3336
3337  # set starting revision
3338  cur_rev = 1
3339
3340  expected_disk = svntest.main.greek_state.copy()
3341  expected_status = svntest.actions.get_virginal_state(wc_dir, cur_rev)
3342  expected_backup_status = svntest.actions.get_virginal_state(wc_backup,
3343                                                              cur_rev)
3344
3345  path_backup = os.path.join(wc_backup, 'A', 'mu')
3346
3347  # do the test for each eol-style
3348  for eol, eolchar in zip(['CRLF', 'CR','native', 'LF'],
3349                          [crlf, '\015', native_nl, '\012']):
3350    # rewrite file mu and set the eol-style property.
3351    svntest.main.file_write(mu_path, "This is the file 'mu'."+ eolchar, 'wb')
3352    svntest.main.run_svn(None, 'propset', 'svn:eol-style', eol, mu_path)
3353
3354    expected_disk.add({
3355      'A/mu' : Item("This is the file 'mu'." + eolchar)
3356    })
3357    expected_output = svntest.wc.State(wc_dir, {
3358      'A/mu' : Item(verb='Sending'),
3359    })
3360    expected_status.tweak(wc_rev = cur_rev)
3361    expected_status.add({
3362      'A/mu' : Item(status='  ', wc_rev = cur_rev + 1),
3363    })
3364
3365    # Commit the original change and note the 'base' revision number
3366    svntest.actions.run_and_verify_commit(wc_dir, expected_output,
3367                                          expected_status)
3368    cur_rev = cur_rev + 1
3369    base_rev = cur_rev
3370
3371    svntest.main.run_svn(None, 'update', wc_backup)
3372
3373    # Make a local mod to mu
3374    svntest.main.file_append_binary(mu_path,
3375                                    'Original appended text for mu' + eolchar)
3376
3377    # Commit the original change and note the 'theirs' revision number
3378    svntest.main.run_svn(None, 'commit', '-m', 'test log', wc_dir)
3379    cur_rev = cur_rev + 1
3380    theirs_rev = cur_rev
3381
3382    # Make a local mod to mu, will conflict with the previous change
3383    svntest.main.file_append_binary(path_backup,
3384                                    'Conflicting appended text for mu'
3385                                    + eolchar)
3386
3387    # Create expected output tree for an update of the wc_backup.
3388    expected_backup_output = svntest.wc.State(wc_backup, {
3389      'A/mu' : Item(status='C '),
3390      })
3391
3392    # Create expected disk tree for the update.
3393    expected_backup_disk = expected_disk.copy()
3394
3395    # verify content of resulting conflicted file
3396    expected_backup_disk.add({
3397    'A/mu' : Item(contents= "This is the file 'mu'." + eolchar +
3398      "<<<<<<< .working" + eolchar +
3399      "Conflicting appended text for mu" + eolchar +
3400      "||||||| .merge-left.r" + str(cur_rev - 1) + eolchar +
3401      "=======" + eolchar +
3402      "Original appended text for mu" + eolchar +
3403      ">>>>>>> .merge-right.r" + str(cur_rev) + eolchar),
3404    })
3405    # verify content of base(left) file
3406    expected_backup_disk.add({
3407    'A/mu.merge-left.r' + str(base_rev) :
3408      Item(contents= "This is the file 'mu'." + eolchar)
3409    })
3410    # verify content of theirs(right) file
3411    expected_backup_disk.add({
3412    'A/mu.merge-right.r' + str(theirs_rev) :
3413      Item(contents= "This is the file 'mu'." + eolchar +
3414      "Original appended text for mu" + eolchar)
3415    })
3416    # verify content of mine file
3417    expected_backup_disk.add({
3418    'A/mu.working' : Item(contents= "This is the file 'mu'." +
3419      eolchar +
3420      "Conflicting appended text for mu" + eolchar)
3421    })
3422
3423    # Create expected status tree for the update.
3424    expected_backup_status.add({
3425      'A/mu'   : Item(status='  ', wc_rev=cur_rev),
3426    })
3427    expected_backup_status.tweak('A/mu', status='C ')
3428    expected_backup_status.tweak(wc_rev = cur_rev - 1)
3429    expected_backup_status.tweak('', status= ' M')
3430    expected_mergeinfo_output = wc.State(wc_backup, {
3431      '' : Item(status=' U'),
3432      })
3433    expected_elision_output = wc.State(wc_backup, {
3434      })
3435    expected_backup_skip = wc.State('', { })
3436
3437    svntest.actions.run_and_verify_merge2(wc_backup, cur_rev - 1, cur_rev,
3438                                          sbox.repo_url, None,
3439                                          expected_backup_output,
3440                                          expected_mergeinfo_output,
3441                                          expected_elision_output,
3442                                          expected_backup_disk,
3443                                          expected_backup_status,
3444                                          expected_backup_skip,
3445                                          keep_eol_style=True)
3446
3447    # cleanup for next run
3448    svntest.main.run_svn(None, 'revert', '-R', wc_backup)
3449    svntest.main.run_svn(None, 'update', wc_dir)
3450
3451#----------------------------------------------------------------------
3452# eol-style handling during merge, scenario 2:
3453# if part of that merge is a propchange (add, change, delete) of
3454# svn:eol-style, make sure the correct eol-style is applied before
3455# calculating the merge (and conflicts if any)
3456#
3457# This test for 'svn update' can be found in update_tests.py as
3458# update_eolstyle_handling.
3459@SkipUnless(server_has_mergeinfo)
3460def merge_eolstyle_handling(sbox):
3461  "handle eol-style propchange during merge"
3462
3463  sbox.build()
3464  wc_dir = sbox.wc_dir
3465
3466  mu_path = sbox.ospath('A/mu')
3467
3468  crlf = '\r\n'
3469
3470  # Checkout a second working copy
3471  wc_backup = sbox.add_wc_path('backup')
3472  svntest.actions.run_and_verify_svn(None, [], 'checkout',
3473                                     sbox.repo_url, wc_backup)
3474  path_backup = os.path.join(wc_backup, 'A', 'mu')
3475
3476  # Test 1: add the eol-style property and commit, change mu in the second
3477  # working copy and merge the last revision; there should be no conflict!
3478  svntest.main.run_svn(None, 'propset', 'svn:eol-style', "CRLF", mu_path)
3479  svntest.main.run_svn(None,
3480                       'commit', '-m', 'set eol-style property', wc_dir)
3481
3482  svntest.main.file_append_binary(path_backup, 'Added new line of text.\012')
3483
3484  expected_backup_disk = svntest.main.greek_state.copy()
3485  expected_backup_disk.tweak(
3486  'A/mu', contents= "This is the file 'mu'." + crlf +
3487    "Added new line of text." + crlf)
3488  expected_backup_output = svntest.wc.State(wc_backup, {
3489    'A/mu' : Item(status='GU'),
3490    })
3491  expected_mergeinfo_output = svntest.wc.State(wc_backup, {
3492    '' : Item(status=' U'),
3493    })
3494  expected_elision_output = wc.State(wc_backup, {
3495    })
3496  expected_backup_status = svntest.actions.get_virginal_state(wc_backup, 1)
3497  expected_backup_status.tweak('', status=' M')
3498  expected_backup_status.tweak('A/mu', status='MM')
3499
3500  expected_backup_skip = wc.State('', { })
3501
3502  svntest.actions.run_and_verify_merge2(wc_backup, '1', '2', sbox.repo_url,
3503                                        None,
3504                                        expected_backup_output,
3505                                        expected_mergeinfo_output,
3506                                        expected_elision_output,
3507                                        expected_backup_disk,
3508                                        expected_backup_status,
3509                                        expected_backup_skip,
3510                                        keep_eol_style=True)
3511
3512  # Test 2: now change the eol-style property to another value and commit,
3513  # merge this revision in the still changed mu in the second working copy;
3514  # there should be no conflict!
3515  svntest.main.run_svn(None, 'propset', 'svn:eol-style', "CR", mu_path)
3516  svntest.main.run_svn(None,
3517                       'commit', '-m', 'set eol-style property', wc_dir)
3518
3519  expected_backup_disk = svntest.main.greek_state.copy()
3520  expected_backup_disk.add({
3521  'A/mu' : Item(contents= "This is the file 'mu'.\015" +
3522    "Added new line of text.\015")
3523  })
3524  expected_backup_output = svntest.wc.State(wc_backup, {
3525    'A/mu' : Item(status='GU'),
3526    })
3527  expected_mergeinfo_output = svntest.wc.State(wc_backup, {
3528    '' : Item(status=' G'),
3529    })
3530  expected_backup_status = svntest.actions.get_virginal_state(wc_backup, 1)
3531  expected_backup_status.tweak('', status=' M')
3532  expected_backup_status.tweak('A/mu', status='MM')
3533  svntest.actions.run_and_verify_merge2(wc_backup, '2', '3', sbox.repo_url,
3534                                        None,
3535                                        expected_backup_output,
3536                                        expected_mergeinfo_output,
3537                                        expected_elision_output,
3538                                        expected_backup_disk,
3539                                        expected_backup_status,
3540                                        expected_backup_skip,
3541                                        keep_eol_style=True)
3542
3543  # Test 3: now delete the eol-style property and commit, merge this revision
3544  # in the still changed mu in the second working copy; there should be no
3545  # conflict!
3546  # EOL of mu should be unchanged (=CRLF).
3547  svntest.main.run_svn(None, 'propdel', 'svn:eol-style', mu_path)
3548  svntest.main.run_svn(None,
3549                       'commit', '-m', 'del eol-style property', wc_dir)
3550
3551  expected_backup_disk = svntest.main.greek_state.copy()
3552  expected_backup_disk.add({
3553  'A/mu' : Item(contents= "This is the file 'mu'.\015" +
3554    "Added new line of text.\015")
3555  })
3556  expected_backup_output = svntest.wc.State(wc_backup, {
3557    'A/mu' : Item(status=' G'),
3558    })
3559  expected_backup_status = svntest.actions.get_virginal_state(wc_backup, 1)
3560  expected_backup_status.tweak('', status=' M')
3561  expected_backup_status.tweak('A/mu', status='M ')
3562  svntest.actions.run_and_verify_merge2(wc_backup, '3', '4', sbox.repo_url,
3563                                        None,
3564                                        expected_backup_output,
3565                                        expected_mergeinfo_output,
3566                                        expected_elision_output,
3567                                        expected_backup_disk,
3568                                        expected_backup_status,
3569                                        expected_backup_skip,
3570                                        keep_eol_style=True)
3571
3572#----------------------------------------------------------------------
3573def create_deep_trees(wc_dir):
3574  """Create A/B/F/E by moving A/B/E to A/B/F/E.
3575     Copy A/B/F/E to A/B/F/E1.
3576     Copy A/B to A/copy-of-B, and return the expected status.
3577     At the end of this function WC would be at r4"""
3578
3579  A_path = os.path.join(wc_dir, 'A')
3580  A_B_path = os.path.join(A_path, 'B')
3581  A_B_E_path = os.path.join(A_B_path, 'E')
3582  A_B_F_path = os.path.join(A_B_path, 'F')
3583  A_B_F_E_path = os.path.join(A_B_F_path, 'E')
3584  A_B_F_E1_path = os.path.join(A_B_F_path, 'E1')
3585
3586  # Deepen the directory structure we're working with by moving E to
3587  # underneath F and committing, creating revision 2.
3588  svntest.main.run_svn(None, 'mv', A_B_E_path, A_B_F_path)
3589
3590  expected_output = wc.State(wc_dir, {
3591    'A/B/E'   : Item(verb='Deleting'),
3592    'A/B/F/E' : Item(verb='Adding')
3593    })
3594  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
3595  expected_status.remove('A/B/E', 'A/B/E/alpha', 'A/B/E/beta')
3596  expected_status.add({
3597    'A/B/F/E'       : Item(status='  ', wc_rev=2),
3598    'A/B/F/E/alpha' : Item(status='  ', wc_rev=2),
3599    'A/B/F/E/beta'  : Item(status='  ', wc_rev=2),
3600    })
3601  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
3602                                        expected_status)
3603
3604  svntest.main.run_svn(None, 'cp', A_B_F_E_path, A_B_F_E1_path)
3605
3606
3607  expected_output = wc.State(wc_dir, {
3608    'A/B/F/E1' : Item(verb='Adding')
3609    })
3610  expected_status.add({
3611    'A/B/F/E1'       : Item(status='  ', wc_rev=3),
3612    'A/B/F/E1/alpha' : Item(status='  ', wc_rev=3),
3613    'A/B/F/E1/beta'  : Item(status='  ', wc_rev=3),
3614    })
3615  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
3616                                        expected_status)
3617
3618  # Bring the entire WC up to date with rev 3.
3619  svntest.actions.run_and_verify_svn(None, [], 'update', wc_dir)
3620  expected_status.tweak(wc_rev=3)
3621
3622  # Copy B and commit, creating revision 4.
3623  copy_of_B_path = os.path.join(A_path, 'copy-of-B')
3624  svntest.main.run_svn(None, "cp", A_B_path, copy_of_B_path)
3625  expected_output = svntest.wc.State(wc_dir, {
3626    'A/copy-of-B' : Item(verb='Adding'),
3627    })
3628  expected_status.add({
3629    'A/copy-of-B'            : Item(status='  ', wc_rev=4),
3630    'A/copy-of-B/F'          : Item(status='  ', wc_rev=4),
3631    'A/copy-of-B/F/E'        : Item(status='  ', wc_rev=4),
3632    'A/copy-of-B/F/E/alpha'  : Item(status='  ', wc_rev=4),
3633    'A/copy-of-B/F/E/beta'   : Item(status='  ', wc_rev=4),
3634    'A/copy-of-B/F/E1'       : Item(status='  ', wc_rev=4),
3635    'A/copy-of-B/F/E1/alpha' : Item(status='  ', wc_rev=4),
3636    'A/copy-of-B/F/E1/beta'  : Item(status='  ', wc_rev=4),
3637    'A/copy-of-B/lambda'     : Item(status='  ', wc_rev=4),
3638    })
3639  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
3640                                        expected_status)
3641
3642  expected_disk = svntest.main.greek_state.copy()
3643  expected_disk.remove('A/B/E', 'A/B/E/alpha', 'A/B/E/beta')
3644  expected_disk.add({
3645    'A/B/F/E'        : Item(),
3646    'A/B/F/E/alpha'  : Item(contents="This is the file 'alpha'.\n"),
3647    'A/B/F/E/beta'   : Item(contents="This is the file 'beta'.\n"),
3648    'A/B/F/E1'       : Item(),
3649    'A/B/F/E1/alpha' : Item(contents="This is the file 'alpha'.\n"),
3650    'A/B/F/E1/beta'  : Item(contents="This is the file 'beta'.\n"),
3651    'A/copy-of-B'            : Item(),
3652    'A/copy-of-B/F'          : Item(props={}),
3653    'A/copy-of-B/F/E'        : Item(),
3654    'A/copy-of-B/F/E/alpha'  : Item(contents="This is the file 'alpha'.\n"),
3655    'A/copy-of-B/F/E/beta'   : Item(contents="This is the file 'beta'.\n"),
3656    'A/copy-of-B/F/E1'       : Item(),
3657    'A/copy-of-B/F/E1/alpha' : Item(contents="This is the file 'alpha'.\n"),
3658    'A/copy-of-B/F/E1/beta'  : Item(contents="This is the file 'beta'.\n"),
3659    'A/copy-of-B/lambda'     : Item(contents="This is the file 'lambda'.\n"),
3660    })
3661  svntest.actions.verify_disk(wc_dir, expected_disk, True)
3662
3663  # Bring the entire WC up to date with rev 4.
3664  svntest.actions.run_and_verify_svn(None, [], 'update', wc_dir)
3665
3666  svntest.actions.verify_disk(wc_dir, expected_disk, True)
3667
3668  expected_status.tweak(wc_rev=4)
3669  expected_disk.tweak('A/copy-of-B/F/E', 'A/copy-of-B/F/E1', status=' M')
3670  return expected_status
3671
3672#----------------------------------------------------------------------
3673@SkipUnless(server_has_mergeinfo)
3674def avoid_repeated_merge_using_inherited_merge_info(sbox):
3675  "use inherited mergeinfo to avoid repeated merge"
3676
3677  sbox.build()
3678  wc_dir = sbox.wc_dir
3679
3680  A_path = sbox.ospath('A')
3681  A_B_path = os.path.join(A_path, 'B')
3682  A_B_E_path = os.path.join(A_B_path, 'E')
3683  A_B_F_path = os.path.join(A_B_path, 'F')
3684  copy_of_B_path = os.path.join(A_path, 'copy-of-B')
3685
3686  # Create a deeper directory structure.
3687  expected_status = create_deep_trees(wc_dir)
3688
3689  # Edit alpha and commit it, creating revision 5.
3690  alpha_path = os.path.join(A_B_F_path, 'E', 'alpha')
3691  new_content_for_alpha = 'new content to alpha\n'
3692  svntest.main.file_write(alpha_path, new_content_for_alpha)
3693  expected_output = svntest.wc.State(wc_dir, {
3694    'A/B/F/E/alpha' : Item(verb='Sending'),
3695    })
3696  expected_status.tweak('A/B/F/E/alpha', wc_rev=5)
3697  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
3698                                        expected_status)
3699
3700  # Bring the entire WC up to date with rev 5.
3701  svntest.actions.run_and_verify_svn(None, [], 'update', wc_dir)
3702
3703  # Merge changes from rev 5 of B (to alpha) into copy_of_B.
3704  expected_output = wc.State(copy_of_B_path, {
3705    'F/E/alpha'   : Item(status='U '),
3706    })
3707  expected_mergeinfo_output = wc.State(copy_of_B_path, {
3708    '' : Item(status=' U'),
3709    })
3710  expected_elision_output = wc.State(copy_of_B_path, {
3711    })
3712  expected_status = wc.State(copy_of_B_path, {
3713    ''           : Item(status=' M', wc_rev=5),
3714    'F/E'        : Item(status='  ', wc_rev=5),
3715    'F/E/alpha'  : Item(status='M ', wc_rev=5),
3716    'F/E/beta'   : Item(status='  ', wc_rev=5),
3717    'F/E1'       : Item(status='  ', wc_rev=5),
3718    'F/E1/alpha' : Item(status='  ', wc_rev=5),
3719    'F/E1/beta'  : Item(status='  ', wc_rev=5),
3720    'lambda'     : Item(status='  ', wc_rev=5),
3721    'F'          : Item(status='  ', wc_rev=5),
3722    })
3723  expected_disk = wc.State('', {
3724    ''           : Item(props={SVN_PROP_MERGEINFO : '/A/B:5'}),
3725    'F/E'        : Item(),
3726    'F/E/alpha'  : Item(new_content_for_alpha),
3727    'F/E/beta'   : Item("This is the file 'beta'.\n"),
3728    'F/E1'       : Item(),
3729    'F/E1/alpha' : Item("This is the file 'alpha'.\n"),
3730    'F/E1/beta'  : Item("This is the file 'beta'.\n"),
3731    'F'          : Item(),
3732    'lambda'     : Item("This is the file 'lambda'.\n")
3733    })
3734  expected_skip = wc.State(copy_of_B_path, { })
3735
3736  svntest.actions.run_and_verify_merge(copy_of_B_path, '4', '5',
3737                                       sbox.repo_url + '/A/B', None,
3738                                       expected_output,
3739                                       expected_mergeinfo_output,
3740                                       expected_elision_output,
3741                                       expected_disk,
3742                                       expected_status,
3743                                       expected_skip,
3744                                       check_props=True)
3745
3746  # Commit the result of the merge, creating revision 6.
3747  expected_output = svntest.wc.State(copy_of_B_path, {
3748    ''          : Item(verb='Sending'),
3749    'F/E/alpha' : Item(verb='Sending'),
3750    })
3751  svntest.actions.run_and_verify_commit(copy_of_B_path, expected_output,
3752                                        None)
3753
3754  # Update the WC to bring /A/copy_of_B/F from rev 4 to rev 6.
3755  # Without this update, a subsequent merge will not find any merge
3756  # info for /A/copy_of_B/F -- nor its parent dir in the repos -- at
3757  # rev 4.  Mergeinfo wasn't introduced until rev 6.
3758  copy_of_B_F_E_path = os.path.join(copy_of_B_path, 'F', 'E')
3759  svntest.actions.run_and_verify_svn(None, [], 'update', wc_dir)
3760
3761  # Attempt to re-merge changes to alpha from rev 4.  Use the merge
3762  # info inherited from the grandparent (copy-of-B) of our merge
3763  # target (/A/copy-of-B/F/E) to avoid a repeated merge.
3764  expected_status = wc.State(copy_of_B_F_E_path, {
3765    ''      : Item(status='  ', wc_rev=6),
3766    'alpha' : Item(status='  ', wc_rev=6),
3767    'beta'  : Item(status='  ', wc_rev=6),
3768    })
3769  svntest.actions.run_and_verify_svn(
3770    expected_merge_output([[5]],
3771                          [' U   ' + copy_of_B_F_E_path + '\n',
3772                           ' G   ' + copy_of_B_F_E_path + '\n'],
3773                          elides=True),
3774    [], 'merge', '-r4:5',
3775    sbox.repo_url + '/A/B/F/E',
3776    copy_of_B_F_E_path)
3777  svntest.actions.run_and_verify_status(copy_of_B_F_E_path,
3778                                        expected_status)
3779
3780#----------------------------------------------------------------------
3781@SkipUnless(server_has_mergeinfo)
3782@Issue(2821)
3783def avoid_repeated_merge_on_subtree_with_merge_info(sbox):
3784  "use subtree's mergeinfo to avoid repeated merge"
3785  # Create deep trees A/B/F/E and A/B/F/E1 and copy A/B to A/copy-of-B
3786  # with the help of 'create_deep_trees'
3787  # As /A/copy-of-B/F/E1 is not a child of /A/copy-of-B/F/E,
3788  # set_path should not be called on /A/copy-of-B/F/E1 while
3789  # doing a implicit subtree merge on /A/copy-of-B/F/E.
3790  sbox.build()
3791  wc_dir = sbox.wc_dir
3792
3793  A_path = sbox.ospath('A')
3794  A_B_path = os.path.join(A_path, 'B')
3795  A_B_E_path = os.path.join(A_B_path, 'E')
3796  A_B_F_path = os.path.join(A_B_path, 'F')
3797  A_B_F_E_path = os.path.join(A_B_F_path, 'E')
3798  copy_of_B_path = os.path.join(A_path, 'copy-of-B')
3799  copy_of_B_F_path = os.path.join(A_path, 'copy-of-B', 'F')
3800  A_copy_of_B_F_E_alpha_path = os.path.join(A_path, 'copy-of-B', 'F',
3801                                            'E', 'alpha')
3802
3803  # Create a deeper directory structure.
3804  expected_status = create_deep_trees(wc_dir)
3805
3806  # Edit alpha and commit it, creating revision 5.
3807  alpha_path = os.path.join(A_B_F_E_path, 'alpha')
3808  new_content_for_alpha1 = 'new content to alpha\n'
3809  svntest.main.file_write(alpha_path, new_content_for_alpha1)
3810
3811  expected_output = svntest.wc.State(wc_dir, {
3812    'A/B/F/E/alpha' : Item(verb='Sending'),
3813    })
3814  expected_status.tweak('A/B/F/E/alpha', wc_rev=5)
3815  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
3816                                        expected_status)
3817
3818  for path_and_mergeinfo in (('E', '/A/B/F/E:5'),
3819                             ('E1', '/A/B/F/E:5')):
3820    path_name = os.path.join(copy_of_B_path, 'F', path_and_mergeinfo[0])
3821
3822    # Merge r5 to path_name.
3823    expected_output = wc.State(path_name, {
3824      'alpha'   : Item(status='U '),
3825      })
3826    expected_mergeinfo_output = wc.State(path_name, {
3827      '' : Item(status=' U'),
3828      })
3829    expected_elision_output = wc.State(path_name, {})
3830    expected_status = wc.State(path_name, {
3831      ''      : Item(status=' M', wc_rev=4),
3832      'alpha' : Item(status='M ', wc_rev=4),
3833      'beta'  : Item(status='  ', wc_rev=4),
3834      })
3835    expected_disk = wc.State('', {
3836      ''        : Item(props={SVN_PROP_MERGEINFO : path_and_mergeinfo[1]}),
3837      'alpha'   : Item(new_content_for_alpha1),
3838      'beta'    : Item("This is the file 'beta'.\n"),
3839      })
3840    expected_skip = wc.State(path_name, { })
3841
3842    svntest.actions.run_and_verify_merge(path_name, '4', '5',
3843                                         sbox.repo_url + '/A/B/F/E', None,
3844                                         expected_output,
3845                                         expected_mergeinfo_output,
3846                                         expected_elision_output,
3847                                         expected_disk,
3848                                         expected_status,
3849                                         expected_skip,
3850                                         check_props=True)
3851
3852    # Commit the result of the merge, creating new revision.
3853    expected_output = svntest.wc.State(path_name, {
3854      ''      : Item(verb='Sending'),
3855      'alpha' : Item(verb='Sending'),
3856      })
3857    svntest.actions.run_and_verify_commit(path_name,
3858                                          expected_output, None, [], wc_dir)
3859
3860  # Edit A/B/F/E/alpha and commit it, creating revision 8.
3861  new_content_for_alpha = 'new content to alpha\none more line\n'
3862  svntest.main.file_write(alpha_path, new_content_for_alpha)
3863
3864  expected_output = svntest.wc.State(A_B_F_E_path, {
3865    'alpha' : Item(verb='Sending'),
3866    })
3867  expected_status = wc.State(A_B_F_E_path, {
3868    ''      : Item(status='  ', wc_rev=4),
3869    'alpha' : Item(status='  ', wc_rev=8),
3870    'beta'  : Item(status='  ', wc_rev=4),
3871    })
3872  svntest.actions.run_and_verify_commit(A_B_F_E_path, expected_output,
3873                                        expected_status, [], wc_dir)
3874
3875  # Update the WC to bring /A/copy_of_B to rev 8.
3876  # Without this update expected_status tree would be cumbersome to
3877  # understand.
3878  svntest.actions.run_and_verify_svn(None, [], 'update', wc_dir)
3879
3880  # Merge changes from rev 4:8 of A/B into A/copy_of_B.  A/copy_of_B/F/E1
3881  # has explicit mergeinfo and exists at r4 in the merge source, so it
3882  # should be treated as a subtree with intersecting mergeinfo and its
3883  # mergeinfo updated.
3884  expected_output = wc.State(copy_of_B_path, {
3885    'F/E/alpha' : Item(status='U ')
3886    })
3887  expected_mergeinfo_output = wc.State(copy_of_B_path, {
3888    ''    : Item(status=' U'),
3889    'F/E' : Item(status=' U')
3890    })
3891  expected_elision_output = wc.State(copy_of_B_path, {
3892    'F/E' : Item(status=' U')
3893    })
3894  expected_status = wc.State(copy_of_B_path, {
3895    # The subtree mergeinfo on F/E1 is not updated because
3896    # this merge does not affect that subtree.
3897    ''           : Item(status=' M', wc_rev=8),
3898    'F/E'        : Item(status=' M', wc_rev=8),
3899    'F/E/alpha'  : Item(status='M ', wc_rev=8),
3900    'F/E/beta'   : Item(status='  ', wc_rev=8),
3901    'F/E1'       : Item(status='  ', wc_rev=8),
3902    'F/E1/alpha' : Item(status='  ', wc_rev=8),
3903    'F/E1/beta'  : Item(status='  ', wc_rev=8),
3904    'lambda'     : Item(status='  ', wc_rev=8),
3905    'F'          : Item(status='  ', wc_rev=8)
3906    })
3907  expected_disk = wc.State('', {
3908    ''           : Item(props={SVN_PROP_MERGEINFO : '/A/B:5-8'}),
3909    'F/E'        : Item(props={}),  # elision!
3910    'F/E/alpha'  : Item(new_content_for_alpha),
3911    'F/E/beta'   : Item("This is the file 'beta'.\n"),
3912    'F'          : Item(),
3913    'F/E1'       : Item(props={SVN_PROP_MERGEINFO :
3914                               '/A/B/F/E:5'}),
3915    'F/E1/alpha' : Item(new_content_for_alpha1),
3916    'F/E1/beta'  : Item("This is the file 'beta'.\n"),
3917    'lambda'     : Item("This is the file 'lambda'.\n")
3918    })
3919  expected_skip = wc.State(copy_of_B_path, { })
3920  svntest.actions.run_and_verify_merge(copy_of_B_path, '4', '8',
3921                                       sbox.repo_url + '/A/B', None,
3922                                       expected_output,
3923                                       expected_mergeinfo_output,
3924                                       expected_elision_output,
3925                                       expected_disk,
3926                                       expected_status,
3927                                       expected_skip,
3928                                       check_props=True)
3929
3930  # Test for part of Issue #2821, see
3931  # https://issues.apache.org/jira/browse/SVN-2821#desc22
3932  #
3933  # Revert all local changes.
3934  svntest.actions.run_and_verify_svn(None, [], 'revert', '-R', wc_dir)
3935
3936  # Make a text mod to A/copy-of-B/F/E/alpha
3937  newer_content_for_alpha = "Conflicting content"
3938  svntest.main.file_write(A_copy_of_B_F_E_alpha_path,
3939                          newer_content_for_alpha)
3940
3941  # Re-merge r5 to A/copy-of-B/F, this *should* be a no-op as the mergeinfo
3942  # on A/copy-of-B/F/E should prevent any attempt to merge r5 into that
3943  # subtree.  The merge will leave a few local changes as mergeinfo is set
3944  # on A/copy-of-B/F, the mergeinfo on A/copy-of-B/F/E elides to it.  The
3945  # mergeinfo on A/copy-of-B/F/E1 remains unchanged as that subtree was
3946  # untouched by the merge.
3947  expected_output = wc.State(copy_of_B_F_path, {})
3948  expected_mergeinfo_output = wc.State(copy_of_B_F_path, {
3949    ''  : Item(status=' U'),
3950    })
3951  expected_elision_output = wc.State(copy_of_B_F_path, {
3952    'E' : Item(status=' U')
3953    })
3954  expected_status = wc.State(copy_of_B_F_path, {
3955    ''         : Item(status=' M', wc_rev=8),
3956    'E'        : Item(status=' M', wc_rev=8),
3957    'E/alpha'  : Item(status='M ', wc_rev=8),
3958    'E/beta'   : Item(status='  ', wc_rev=8),
3959    'E1'       : Item(status='  ', wc_rev=8),
3960    'E1/alpha' : Item(status='  ', wc_rev=8),
3961    'E1/beta'  : Item(status='  ', wc_rev=8),
3962    })
3963  expected_disk = wc.State('', {
3964    ''         : Item(props={SVN_PROP_MERGEINFO : '/A/B/F:5'}),
3965    'E'        : Item(props={}),
3966    'E/alpha'  : Item(newer_content_for_alpha),
3967    'E/beta'   : Item("This is the file 'beta'.\n"),
3968    'E1'       : Item(props={SVN_PROP_MERGEINFO :
3969                               '/A/B/F/E:5'}),
3970    'E1/alpha' : Item(new_content_for_alpha1),
3971    'E1/beta'  : Item("This is the file 'beta'.\n")
3972    })
3973  expected_skip = wc.State(copy_of_B_F_path, { })
3974  svntest.actions.run_and_verify_merge(copy_of_B_F_path, '4', '5',
3975                                       sbox.repo_url + '/A/B/F', None,
3976                                       expected_output,
3977                                       expected_mergeinfo_output,
3978                                       expected_elision_output,
3979                                       expected_disk,
3980                                       expected_status,
3981                                       expected_skip,
3982                                       check_props=True)
3983
3984#----------------------------------------------------------------------
3985def tweak_src_then_merge_to_dest(sbox, src_path, dst_path,
3986                                 canon_src_path, contents, cur_rev):
3987  """Edit src and commit it. This results in new_rev.
3988   Merge new_rev to dst_path. Return new_rev."""
3989
3990  wc_dir = sbox.wc_dir
3991  new_rev = cur_rev + 1
3992  svntest.main.file_write(src_path, contents)
3993
3994  expected_output = svntest.wc.State(src_path, {
3995    '': Item(verb='Sending'),
3996    })
3997
3998  expected_status = wc.State(src_path,
3999                             { '': Item(wc_rev=new_rev, status='  ')})
4000
4001  svntest.actions.run_and_verify_commit(src_path, expected_output,
4002                                        expected_status)
4003
4004  # Update the WC to new_rev so that it would be easier to expect everyone
4005  # to be at new_rev.
4006  svntest.actions.run_and_verify_svn(None, [], 'update', wc_dir)
4007
4008  # Merge new_rev of src_path to dst_path.
4009
4010  expected_status = wc.State(dst_path,
4011                             { '': Item(wc_rev=new_rev, status='MM')})
4012
4013  merge_url = sbox.repo_url + '/' + canon_src_path
4014  if sys.platform == 'win32':
4015    merge_url = merge_url.replace('\\', '/')
4016
4017  svntest.actions.run_and_verify_svn(
4018    expected_merge_output([[new_rev]],
4019                          ['U    ' + dst_path + '\n',
4020                           ' U   ' + dst_path + '\n']),
4021    [], 'merge', '-c', str(new_rev), merge_url, dst_path)
4022
4023  svntest.actions.run_and_verify_status(dst_path, expected_status)
4024
4025  return new_rev
4026
4027#----------------------------------------------------------------------
4028@SkipUnless(server_has_mergeinfo)
4029def obey_reporter_api_semantics_while_doing_subtree_merges(sbox):
4030  "drive reporter api in depth first order"
4031
4032  # Copy /A/D to /A/copy-of-D it results in rONE.
4033  # Create children at different hierarchies having some merge-info
4034  # to test the set_path calls on a reporter in a depth-first order.
4035  # On all 'file' descendants of /A/copy-of-D/ we run merges.
4036  # We create /A/D/umlaut directly over URL it results in rev rTWO.
4037  # When we merge rONE+1:TWO of /A/D on /A/copy-of-D it should merge smoothly.
4038
4039  sbox.build()
4040  wc_dir = sbox.wc_dir
4041
4042  A_path = sbox.ospath('A')
4043  A_D_path = sbox.ospath('A/D')
4044  copy_of_A_D_path = sbox.ospath('A/copy-of-D')
4045
4046  svntest.main.run_svn(None, "cp", A_D_path, copy_of_A_D_path)
4047
4048  expected_output = svntest.wc.State(wc_dir, {
4049    'A/copy-of-D' : Item(verb='Adding'),
4050    })
4051  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
4052  expected_status.add({
4053    'A/copy-of-D'         : Item(status='  ', wc_rev=2),
4054    'A/copy-of-D/G'       : Item(status='  ', wc_rev=2),
4055    'A/copy-of-D/G/pi'    : Item(status='  ', wc_rev=2),
4056    'A/copy-of-D/G/rho'   : Item(status='  ', wc_rev=2),
4057    'A/copy-of-D/G/tau'   : Item(status='  ', wc_rev=2),
4058    'A/copy-of-D/H'       : Item(status='  ', wc_rev=2),
4059    'A/copy-of-D/H/chi'   : Item(status='  ', wc_rev=2),
4060    'A/copy-of-D/H/omega' : Item(status='  ', wc_rev=2),
4061    'A/copy-of-D/H/psi'   : Item(status='  ', wc_rev=2),
4062    'A/copy-of-D/gamma'   : Item(status='  ', wc_rev=2),
4063    })
4064  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
4065                                        expected_status)
4066
4067
4068  cur_rev = 2
4069  for path in (["A", "D", "G", "pi"],
4070               ["A", "D", "G", "rho"],
4071               ["A", "D", "G", "tau"],
4072               ["A", "D", "H", "chi"],
4073               ["A", "D", "H", "omega"],
4074               ["A", "D", "H", "psi"],
4075               ["A", "D", "gamma"]):
4076    path_name = os.path.join(wc_dir, *path)
4077    canon_path_name = os.path.join(*path)
4078    path[1] = "copy-of-D"
4079    copy_of_path_name = os.path.join(wc_dir, *path)
4080    var_name = 'new_content_for_' + path[len(path) - 1]
4081    file_contents = "new content to " + path[len(path) - 1] + "\n"
4082    globals()[var_name] = file_contents
4083    cur_rev = tweak_src_then_merge_to_dest(sbox, path_name,
4084                                           copy_of_path_name, canon_path_name,
4085                                           file_contents, cur_rev)
4086
4087  copy_of_A_D_wc_rev = cur_rev
4088  svntest.actions.run_and_verify_svn(['Committing transaction...\n',
4089                                      'Committed revision ' + str(cur_rev+1) +
4090                                      '.\n'],
4091                                     [],
4092                                     'mkdir', sbox.repo_url + '/A/D/umlaut',
4093                                     '-m', "log msg")
4094  rev_to_merge_to_copy_of_D = cur_rev + 1
4095
4096  # All the file descendants of /A/copy-of-D/ have already been merged
4097  # so the only notification we expect is for the added 'umlaut'.
4098  expected_output = wc.State(copy_of_A_D_path, {
4099    'umlaut'  : Item(status='A '),
4100    })
4101  expected_mergeinfo_output = wc.State(copy_of_A_D_path, {
4102    '' : Item(status=' U'),
4103    })
4104  expected_elision_output = wc.State(copy_of_A_D_path, {
4105    })
4106  # No subtree with explicit mergeinfo is affected by this merge, so they
4107  # all remain unchanged from before the merge.  The only mergeinfo updated
4108  # is that on the target 'A/copy-of-D.
4109  expected_status = wc.State(copy_of_A_D_path, {
4110    ''        : Item(status=' M', wc_rev=copy_of_A_D_wc_rev),
4111    'G'       : Item(status='  ', wc_rev=copy_of_A_D_wc_rev),
4112    'G/pi'    : Item(status='MM', wc_rev=copy_of_A_D_wc_rev),
4113    'G/rho'   : Item(status='MM', wc_rev=copy_of_A_D_wc_rev),
4114    'G/tau'   : Item(status='MM', wc_rev=copy_of_A_D_wc_rev),
4115    'H'       : Item(status='  ', wc_rev=copy_of_A_D_wc_rev),
4116    'H/chi'   : Item(status='MM', wc_rev=copy_of_A_D_wc_rev),
4117    'H/omega' : Item(status='MM', wc_rev=copy_of_A_D_wc_rev),
4118    'H/psi'   : Item(status='MM', wc_rev=copy_of_A_D_wc_rev),
4119    'gamma'   : Item(status='MM', wc_rev=copy_of_A_D_wc_rev),
4120    'umlaut'  : Item(status='A ', copied='+', wc_rev='-'),
4121    })
4122
4123  merged_rangelist = "3-%d" % rev_to_merge_to_copy_of_D
4124
4125  expected_disk = wc.State('', {
4126    ''        : Item(props={SVN_PROP_MERGEINFO : '/A/D:' + merged_rangelist}),
4127    'G'       : Item(),
4128    'G/pi'    : Item(new_content_for_pi,
4129                     props={SVN_PROP_MERGEINFO : '/A/D/G/pi:3'}),
4130    'G/rho'   : Item(new_content_for_rho,
4131                     props={SVN_PROP_MERGEINFO : '/A/D/G/rho:4'}),
4132    'G/tau'   : Item(new_content_for_tau,
4133                     props={SVN_PROP_MERGEINFO : '/A/D/G/tau:5'}),
4134    'H'       : Item(),
4135    'H/chi'   : Item(new_content_for_chi,
4136                     props={SVN_PROP_MERGEINFO : '/A/D/H/chi:6'}),
4137    'H/omega' : Item(new_content_for_omega,
4138                     props={SVN_PROP_MERGEINFO : '/A/D/H/omega:7'}),
4139    'H/psi'   : Item(new_content_for_psi,
4140                     props={SVN_PROP_MERGEINFO : '/A/D/H/psi:8'}),
4141    'gamma'   : Item(new_content_for_gamma,
4142                     props={SVN_PROP_MERGEINFO : '/A/D/gamma:9'}),
4143    'umlaut'  : Item(),
4144    })
4145  expected_skip = wc.State(copy_of_A_D_path, { })
4146  svntest.actions.run_and_verify_merge(copy_of_A_D_path,
4147                                       2,
4148                                       str(rev_to_merge_to_copy_of_D),
4149                                       sbox.repo_url + '/A/D', None,
4150                                       expected_output,
4151                                       expected_mergeinfo_output,
4152                                       expected_elision_output,
4153                                       expected_disk,
4154                                       expected_status,
4155                                       expected_skip,
4156                                       check_props=True)
4157
4158#----------------------------------------------------------------------
4159@SkipUnless(server_has_mergeinfo)
4160@Issues(2733,2734)
4161def mergeinfo_inheritance(sbox):
4162  "target inherits mergeinfo from nearest ancestor"
4163
4164  # Test for Issues #2733 and #2734.
4165  #
4166  # When the target of a merge has no explicit mergeinfo and the merge
4167  # would result in mergeinfo being added to the target which...
4168  #
4169  #   ...is a subset of the *local*  mergeinfo on one of the target's
4170  #   ancestors (it's nearest ancestor takes precedence), then the merge is
4171  #   not repeated and no mergeinfo should be set on the target (Issue #2734).
4172  #
4173  # OR
4174  #
4175  #   ...is not a subset it's nearest ancestor, the target should inherit the
4176  #   non-inersecting mergeinfo (local or committed, the former takes
4177  #   precedence) from it's nearest ancestor (Issue #2733).
4178
4179  sbox.build()
4180  wc_dir = sbox.wc_dir
4181  wc_disk, wc_status = set_up_branch(sbox)
4182
4183  # Some paths we'll care about
4184  A_COPY_path      = sbox.ospath('A_COPY')
4185  B_COPY_path      = sbox.ospath('A_COPY/B')
4186  beta_COPY_path   = sbox.ospath('A_COPY/B/E/beta')
4187  E_COPY_path      = sbox.ospath('A_COPY/B/E')
4188  omega_COPY_path  = sbox.ospath('A_COPY/D/H/omega')
4189  D_COPY_path      = sbox.ospath('A_COPY/D')
4190  G_COPY_path      = sbox.ospath('A_COPY/D/G')
4191
4192  # Now start merging...
4193
4194  # Merge r4 into A_COPY/D/
4195  expected_output = wc.State(D_COPY_path, {
4196    'G/rho' : Item(status='U '),
4197    })
4198  expected_mergeinfo_output = wc.State(D_COPY_path, {
4199    '' : Item(status=' U'),
4200    })
4201  expected_elision_output = wc.State(D_COPY_path, {
4202    })
4203  expected_status = wc.State(D_COPY_path, {
4204    ''        : Item(status=' M', wc_rev=2),
4205    'G'       : Item(status='  ', wc_rev=2),
4206    'G/pi'    : Item(status='  ', wc_rev=2),
4207    'G/rho'   : Item(status='M ', wc_rev=2),
4208    'G/tau'   : Item(status='  ', wc_rev=2),
4209    'H'       : Item(status='  ', wc_rev=2),
4210    'H/chi'   : Item(status='  ', wc_rev=2),
4211    'H/psi'   : Item(status='  ', wc_rev=2),
4212    'H/omega' : Item(status='  ', wc_rev=2),
4213    'gamma'   : Item(status='  ', wc_rev=2),
4214    })
4215  # We test issue #2733 here (with a directory as the merge target).
4216  # r1 should be inherited from 'A_COPY'.
4217  expected_disk = wc.State('', {
4218    ''        : Item(props={SVN_PROP_MERGEINFO : '/A/D:4'}),
4219    'G'       : Item(),
4220    'G/pi'    : Item("This is the file 'pi'.\n"),
4221    'G/rho'   : Item("New content"),
4222    'G/tau'   : Item("This is the file 'tau'.\n"),
4223    'H'       : Item(),
4224    'H/chi'   : Item("This is the file 'chi'.\n"),
4225    'H/psi'   : Item("This is the file 'psi'.\n"),
4226    'H/omega' : Item("This is the file 'omega'.\n"),
4227    'gamma'   : Item("This is the file 'gamma'.\n")
4228    })
4229  expected_skip = wc.State(D_COPY_path, { })
4230  svntest.actions.run_and_verify_merge(D_COPY_path, '3', '4',
4231                                       sbox.repo_url + '/A/D', None,
4232                                       expected_output,
4233                                       expected_mergeinfo_output,
4234                                       expected_elision_output,
4235                                       expected_disk,
4236                                       expected_status,
4237                                       expected_skip,
4238                                       check_props=True)
4239
4240  # Merge r4 again, this time into A_COPY/D/G.  An ancestor directory
4241  # (A_COPY/D) exists with identical local mergeinfo, so the merge
4242  # should not be repeated.  We test issue #2734 here with (with a
4243  # directory as the merge target).
4244  expected_output = wc.State(G_COPY_path, { })
4245  # A_COPY/D/G gets mergeinfo set, but it immediately elides to A_COPY/D.
4246  expected_mergeinfo_output = wc.State(G_COPY_path, {
4247    '' : Item(status=' G'),
4248    })
4249  expected_elision_output = wc.State(G_COPY_path, {
4250    '' : Item(status=' U'),
4251    })
4252  expected_status = wc.State(G_COPY_path, {
4253    ''    : Item(status='  ', wc_rev=2),
4254    'pi'  : Item(status='  ', wc_rev=2),
4255    'rho' : Item(status='M ', wc_rev=2),
4256    'tau' : Item(status='  ', wc_rev=2),
4257    })
4258  expected_disk = wc.State('', {
4259    'pi'  : Item("This is the file 'pi'.\n"),
4260    'rho' : Item("New content"),
4261    'tau' : Item("This is the file 'tau'.\n"),
4262    })
4263  expected_skip = wc.State(G_COPY_path, { })
4264  svntest.actions.run_and_verify_merge(G_COPY_path, '3', '4',
4265                                       sbox.repo_url + '/A/D/G', None,
4266                                       expected_output,
4267                                       expected_mergeinfo_output,
4268                                       expected_elision_output,
4269                                       expected_disk,
4270                                       expected_status,
4271                                       expected_skip,
4272                                       check_props=True)
4273
4274  # Merge r5 into A_COPY/B.  Again, r1 should be inherited from
4275  # A_COPY (Issue #2733)
4276  expected_output = wc.State(B_COPY_path, {
4277    'E/beta' : Item(status='U '),
4278    })
4279  expected_mergeinfo_output = wc.State(B_COPY_path, {
4280    '' : Item(status=' U'),
4281    })
4282  expected_elision_output = wc.State(B_COPY_path, {
4283    })
4284  expected_status = wc.State(B_COPY_path, {
4285    ''        : Item(status=' M', wc_rev=2),
4286    'E'       : Item(status='  ', wc_rev=2),
4287    'E/alpha' : Item(status='  ', wc_rev=2),
4288    'E/beta'  : Item(status='M ', wc_rev=2),
4289    'lambda'  : Item(status='  ', wc_rev=2),
4290    'F'       : Item(status='  ', wc_rev=2),
4291    })
4292  expected_disk = wc.State('', {
4293    ''        : Item(props={SVN_PROP_MERGEINFO : '/A/B:5'}),
4294    'E'       : Item(),
4295    'E/alpha' : Item("This is the file 'alpha'.\n"),
4296    'E/beta'  : Item("New content"),
4297    'F'       : Item(),
4298    'lambda'  : Item("This is the file 'lambda'.\n")
4299    })
4300  expected_skip = wc.State(B_COPY_path, { })
4301
4302  svntest.actions.run_and_verify_merge(B_COPY_path, '4', '5',
4303                                       sbox.repo_url + '/A/B', None,
4304                                       expected_output,
4305                                       expected_mergeinfo_output,
4306                                       expected_elision_output,
4307                                       expected_disk,
4308                                       expected_status,
4309                                       expected_skip,
4310                                       check_props=True)
4311
4312  # Merge r5 again, this time into A_COPY/B/E/beta.  An ancestor
4313  # directory (A_COPY/B) exists with identical local mergeinfo, so
4314  # the merge should not be repeated (Issue #2734 with a file as the
4315  # merge target).
4316  expected_skip = wc.State(beta_COPY_path, { })
4317
4318  # run_and_verify_merge doesn't support merging to a file WCPATH
4319  # so use run_and_verify_svn.
4320  ### TODO: We can use run_and_verify_merge() here now.
4321  svntest.actions.run_and_verify_svn([], [], 'merge', '-c5',
4322                                     sbox.repo_url + '/A/B/E/beta',
4323                                     beta_COPY_path)
4324
4325  # The merge wasn't repeated so beta shouldn't have any mergeinfo.
4326  # We are implicitly testing that without looking at the prop value
4327  # itself, just beta's prop modification status.
4328  expected_status = wc.State(beta_COPY_path, {
4329    ''        : Item(status='M ', wc_rev=2),
4330    })
4331  svntest.actions.run_and_verify_status(beta_COPY_path, expected_status)
4332
4333  # Merge r3 into A_COPY.  A_COPY's has two subtrees with mergeinfo,
4334  # A_COPY/B/E/beta and A_COPY/D.  Only the latter is effected by this
4335  # merge so only its mergeinfo is updated to include r3.
4336  expected_output = wc.State(A_COPY_path, {
4337    'D/H/psi'   : Item(status='U '),
4338    })
4339  expected_mergeinfo_output = wc.State(A_COPY_path, {
4340    ''  : Item(status=' U'),
4341    'D' : Item(status=' G'),
4342    })
4343  expected_elision_output = wc.State(A_COPY_path, {
4344    })
4345  expected_status = wc.State(A_COPY_path, {
4346    ''          : Item(status=' M', wc_rev=2),
4347    'B'         : Item(status=' M', wc_rev=2),
4348    'mu'        : Item(status='  ', wc_rev=2),
4349    'B/E'       : Item(status='  ', wc_rev=2),
4350    'B/E/alpha' : Item(status='  ', wc_rev=2),
4351    'B/E/beta'  : Item(status='M ', wc_rev=2),
4352    'B/lambda'  : Item(status='  ', wc_rev=2),
4353    'B/F'       : Item(status='  ', wc_rev=2),
4354    'C'         : Item(status='  ', wc_rev=2),
4355    'D'         : Item(status=' M', wc_rev=2),
4356    'D/G'       : Item(status='  ', wc_rev=2),
4357    'D/G/pi'    : Item(status='  ', wc_rev=2),
4358    'D/G/rho'   : Item(status='M ', wc_rev=2),
4359    'D/G/tau'   : Item(status='  ', wc_rev=2),
4360    'D/gamma'   : Item(status='  ', wc_rev=2),
4361    'D/H'       : Item(status='  ', wc_rev=2),
4362    'D/H/chi'   : Item(status='  ', wc_rev=2),
4363    'D/H/psi'   : Item(status='M ', wc_rev=2),
4364    'D/H/omega' : Item(status='  ', wc_rev=2),
4365    })
4366  expected_disk = wc.State('', {
4367    ''          : Item(props={SVN_PROP_MERGEINFO : '/A:3'}),
4368    'B'         : Item(props={SVN_PROP_MERGEINFO : '/A/B:5'}),
4369    'mu'        : Item("This is the file 'mu'.\n"),
4370    'B/E'       : Item(),
4371    'B/E/alpha' : Item("This is the file 'alpha'.\n"),
4372    'B/E/beta'  : Item("New content"),
4373    'B/lambda'  : Item("This is the file 'lambda'.\n"),
4374    'B/F'       : Item(),
4375    'C'         : Item(),
4376    'D'         : Item(props={SVN_PROP_MERGEINFO : '/A/D:3-4'}),
4377    'D/G'       : Item(),
4378    'D/G/pi'    : Item("This is the file 'pi'.\n"),
4379    'D/G/rho'   : Item("New content"),
4380    'D/G/tau'   : Item("This is the file 'tau'.\n"),
4381    'D/gamma'   : Item("This is the file 'gamma'.\n"),
4382    'D/H'       : Item(),
4383    'D/H/chi'   : Item("This is the file 'chi'.\n"),
4384    'D/H/psi'   : Item("New content"),
4385    'D/H/omega' : Item("This is the file 'omega'.\n"),
4386    })
4387  expected_skip = wc.State(A_COPY_path, { })
4388  svntest.actions.run_and_verify_merge(A_COPY_path, '2', '3',
4389                                       sbox.repo_url + '/A', None,
4390                                       expected_output,
4391                                       expected_mergeinfo_output,
4392                                       expected_elision_output,
4393                                       expected_disk,
4394                                       expected_status,
4395                                       expected_skip,
4396                                       check_props=True)
4397
4398  # Merge r6 into A_COPY/D/H/omega, it should inherit it's nearest
4399  # ancestor's (A_COPY/D) mergeinfo (Issue #2733 with a file as the
4400  # merge target).
4401  expected_skip = wc.State(omega_COPY_path, { })
4402  # run_and_verify_merge doesn't support merging to a file WCPATH
4403  # so use run_and_verify_svn.
4404  ### TODO: We can use run_and_verify_merge() here now.
4405  svntest.actions.run_and_verify_svn(
4406    expected_merge_output([[6]],
4407                          ['U    ' + omega_COPY_path + '\n',
4408                           ' G   ' + omega_COPY_path + '\n']),
4409    [], 'merge', '-c6',
4410    sbox.repo_url + '/A/D/H/omega',
4411    omega_COPY_path)
4412
4413  # Check that mergeinfo was properly set on A_COPY/D/H/omega
4414  svntest.actions.run_and_verify_svn(["/A/D/H/omega:3-4,6\n"],
4415                                     [],
4416                                     'propget', SVN_PROP_MERGEINFO,
4417                                     omega_COPY_path)
4418
4419  # Given a merge target *without* any of the following:
4420  #
4421  #   1) Explicit mergeinfo set on itself in the WC
4422  #   2) Any WC ancestor to inherit mergeinfo from
4423  #   3) Any mergeinfo for the target in the repository
4424  #
4425  # Check that the target still inherits mergeinfo from it's nearest
4426  # repository ancestor.
4427  #
4428  # Commit all the merges thus far
4429  expected_output = wc.State(wc_dir, {
4430    'A_COPY'           : Item(verb='Sending'),
4431    'A_COPY/B'         : Item(verb='Sending'),
4432    'A_COPY/B/E/beta'  : Item(verb='Sending'),
4433    'A_COPY/D'         : Item(verb='Sending'),
4434    'A_COPY/D/G/rho'   : Item(verb='Sending'),
4435    'A_COPY/D/H/omega' : Item(verb='Sending'),
4436    'A_COPY/D/H/psi'   : Item(verb='Sending'),
4437    })
4438  wc_status.tweak('A_COPY', 'A_COPY/B', 'A_COPY/B/E/beta', 'A_COPY/D',
4439                  'A_COPY/D/G/rho', 'A_COPY/D/H/omega', 'A_COPY/D/H/psi',
4440                  wc_rev=7)
4441  svntest.actions.run_and_verify_commit(wc_dir,
4442                                        expected_output,
4443                                        wc_status)
4444
4445  # In single-db mode you can't create a disconnected working copy by just
4446  # copying a subdir
4447
4448  ## Copy the subtree A_COPY/B/E from the working copy, making the
4449  ## disconnected WC E_only.
4450  #other_wc = sbox.add_wc_path('E_only')
4451  #svntest.actions.duplicate_dir(E_COPY_path, other_wc)
4452  #
4453  ## Update the disconnected WC it so it will get the most recent mergeinfo
4454  ## from the repos when merging.
4455  #svntest.actions.run_and_verify_svn(exp_noop_up_out(7), [], 'up',
4456  #                                   other_wc)
4457  #
4458  ## Merge r5:4 into the root of the disconnected WC.
4459  ## E_only has no explicit mergeinfo and since it's the root of the WC
4460  ## cannot inherit any mergeinfo from a working copy ancestor path. Nor
4461  ## does it have any mergeinfo explicitly set on it in the repository.
4462  ## An ancestor path on the repository side, A_COPY/B does have the merge
4463  ## info '/A/B:5' however and E_only should inherit this, resulting in
4464  ## empty mergeinfo after the removal of r5 (A_COPY has mergeinfo of
4465  ## '/A:3' so this empty mergeinfo is needed to override that.
4466  #expected_output = wc.State(other_wc,
4467  #                           {'beta' : Item(status='U ')})
4468  #expected_mergeinfo_output = wc.State(other_wc, {
4469  #  '' : Item(status=' G')
4470  #  })
4471  #expected_elision_output = wc.State(other_wc, {
4472  #  })
4473  #expected_status = wc.State(other_wc, {
4474  #  ''      : Item(status=' M', wc_rev=7),
4475  #  'alpha' : Item(status='  ', wc_rev=7),
4476  #  'beta'  : Item(status='M ', wc_rev=7),
4477  #  })
4478  #expected_disk = wc.State('', {
4479  #  ''      : Item(props={SVN_PROP_MERGEINFO : ''}),
4480  #  'alpha' : Item("This is the file 'alpha'.\n"),
4481  #  'beta'  : Item("This is the file 'beta'.\n"),
4482  #  })
4483  #expected_skip = wc.State(other_wc, { })
4484  #
4485  #svntest.actions.run_and_verify_merge(other_wc, '5', '4',
4486  #                                     sbox.repo_url + '/A/B/E', None,
4487  #                                     expected_output,
4488  #                                     expected_mergeinfo_output,
4489  #                                     expected_elision_output,
4490  #                                     expected_disk,
4491  #                                     expected_status,
4492  #                                     expected_skip,
4493  #                                     check_props=True)
4494
4495#----------------------------------------------------------------------
4496@SkipUnless(server_has_mergeinfo)
4497def mergeinfo_elision(sbox):
4498  "mergeinfo elides to ancestor with identical info"
4499
4500  # When a merge would result in mergeinfo on a target which is identical
4501  # to mergeinfo (local or committed) on one of the node's ancestors (the
4502  # nearest ancestor takes precedence), then the mergeinfo elides from the
4503  # target to the nearest ancestor (e.g. no mergeinfo is set on the target
4504  # or committed mergeinfo is removed).
4505
4506  sbox.build()
4507  wc_dir = sbox.wc_dir
4508  wc_disk, wc_status = set_up_branch(sbox)
4509
4510  # Some paths we'll care about
4511  A_COPY_path      = sbox.ospath('A_COPY')
4512  beta_COPY_path   = sbox.ospath('A_COPY/B/E/beta')
4513  G_COPY_path      = sbox.ospath('A_COPY/D/G')
4514
4515  # Now start merging...
4516
4517  # Merge r5 into A_COPY/B/E/beta.
4518  expected_skip = wc.State(beta_COPY_path, { })
4519
4520  # run_and_verify_merge doesn't support merging to a file WCPATH
4521  # so use run_and_verify_svn.
4522  ### TODO: We can use run_and_verify_merge() here now.
4523  svntest.actions.run_and_verify_svn(
4524    expected_merge_output([[5]],
4525                          ['U    ' + beta_COPY_path + '\n',
4526                           ' U   ' + beta_COPY_path + '\n']),
4527    [], 'merge', '-c5',
4528    sbox.repo_url + '/A/B/E/beta',
4529    beta_COPY_path)
4530
4531  # Check beta's status and props.
4532  expected_status = wc.State(beta_COPY_path, {
4533    ''        : Item(status='MM', wc_rev=2),
4534    })
4535  svntest.actions.run_and_verify_status(beta_COPY_path, expected_status)
4536
4537  svntest.actions.run_and_verify_svn(["/A/B/E/beta:5\n"], [],
4538                                     'propget', SVN_PROP_MERGEINFO,
4539                                     beta_COPY_path)
4540
4541  # Commit the merge
4542  expected_output = wc.State(wc_dir, {
4543    'A_COPY/B/E/beta' : Item(verb='Sending'),
4544    })
4545  wc_status.tweak('A_COPY/B/E/beta', wc_rev=7)
4546  svntest.actions.run_and_verify_commit(wc_dir,
4547                                        expected_output,
4548                                        wc_status)
4549
4550  # Update A_COPY to get all paths to the same working revision.
4551  svntest.actions.run_and_verify_svn(exp_noop_up_out(7), [],
4552                                     'up', wc_dir)
4553  wc_status.tweak(wc_rev=7)
4554
4555  # Merge r4 into A_COPY/D/G.
4556  expected_output = wc.State(G_COPY_path, {
4557    'rho' : Item(status='U ')
4558    })
4559  expected_mergeinfo_output = wc.State(G_COPY_path, {
4560    '' : Item(status=' U')
4561    })
4562  expected_elision_output = wc.State(G_COPY_path, {
4563    })
4564  expected_status = wc.State(G_COPY_path, {
4565    ''    : Item(status=' M', wc_rev=7),
4566    'pi'  : Item(status='  ', wc_rev=7),
4567    'rho' : Item(status='M ', wc_rev=7),
4568    'tau' : Item(status='  ', wc_rev=7),
4569    })
4570  expected_disk = wc.State('', {
4571    ''    : Item(props={SVN_PROP_MERGEINFO : '/A/D/G:4'}),
4572    'pi'  : Item("This is the file 'pi'.\n"),
4573    'rho' : Item("New content"),
4574    'tau' : Item("This is the file 'tau'.\n"),
4575    })
4576  expected_skip = wc.State(G_COPY_path, { })
4577
4578  svntest.actions.run_and_verify_merge(G_COPY_path, '3', '4',
4579                                       sbox.repo_url + '/A/D/G', None,
4580                                       expected_output,
4581                                       expected_mergeinfo_output,
4582                                       expected_elision_output,
4583                                       expected_disk,
4584                                       expected_status,
4585                                       expected_skip,
4586                                       check_props=True)
4587
4588  # Merge r3:6 into A_COPY.  The merge doesn't touch either of A_COPY's
4589  # subtrees with explicit mergeinfo, so those are left alone.
4590  expected_output = wc.State(A_COPY_path, {
4591    'D/H/omega' : Item(status='U ')
4592    })
4593  expected_mergeinfo_output = wc.State(A_COPY_path, {
4594    '' : Item(status=' U')
4595    })
4596  expected_elision_output = wc.State(A_COPY_path, {
4597    })
4598  expected_status = wc.State(A_COPY_path, {
4599    ''          : Item(status=' M', wc_rev=7),
4600    'B'         : Item(status='  ', wc_rev=7),
4601    'mu'        : Item(status='  ', wc_rev=7),
4602    'B/E'       : Item(status='  ', wc_rev=7),
4603    'B/E/alpha' : Item(status='  ', wc_rev=7),
4604    'B/E/beta'  : Item(status='  ', wc_rev=7),
4605    'B/lambda'  : Item(status='  ', wc_rev=7),
4606    'B/F'       : Item(status='  ', wc_rev=7),
4607    'C'         : Item(status='  ', wc_rev=7),
4608    'D'         : Item(status='  ', wc_rev=7),
4609    'D/G'       : Item(status=' M', wc_rev=7),
4610    'D/G/pi'    : Item(status='  ', wc_rev=7),
4611    'D/G/rho'   : Item(status='M ', wc_rev=7),
4612    'D/G/tau'   : Item(status='  ', wc_rev=7),
4613    'D/gamma'   : Item(status='  ', wc_rev=7),
4614    'D/H'       : Item(status='  ', wc_rev=7),
4615    'D/H/chi'   : Item(status='  ', wc_rev=7),
4616    'D/H/psi'   : Item(status='  ', wc_rev=7),
4617    'D/H/omega' : Item(status='M ', wc_rev=7),
4618    })
4619  expected_disk = wc.State('', {
4620    ''          : Item(props={SVN_PROP_MERGEINFO : '/A:4-6'}),
4621    'B'         : Item(),
4622    'mu'        : Item("This is the file 'mu'.\n"),
4623    'B/E'       : Item(),
4624    'B/E/alpha' : Item("This is the file 'alpha'.\n"),
4625    'B/E/beta'  : Item("New content",
4626                       props={SVN_PROP_MERGEINFO : '/A/B/E/beta:5'}),
4627    'B/lambda'  : Item("This is the file 'lambda'.\n"),
4628    'B/F'       : Item(),
4629    'C'         : Item(),
4630    'D'         : Item(),
4631    'D/G'       : Item(props={SVN_PROP_MERGEINFO : '/A/D/G:4'}),
4632    'D/G/pi'    : Item("This is the file 'pi'.\n"),
4633    'D/G/rho'   : Item("New content"),
4634    'D/G/tau'   : Item("This is the file 'tau'.\n"),
4635    'D/gamma'   : Item("This is the file 'gamma'.\n"),
4636    'D/H'       : Item(),
4637    'D/H/chi'   : Item("This is the file 'chi'.\n"),
4638    'D/H/psi'   : Item("This is the file 'psi'.\n"),
4639    'D/H/omega' : Item("New content"),
4640    })
4641  expected_skip = wc.State(A_COPY_path, { })
4642  svntest.actions.run_and_verify_merge(A_COPY_path, '3', '6',
4643                                       sbox.repo_url + '/A', None,
4644                                       expected_output,
4645                                       expected_mergeinfo_output,
4646                                       expected_elision_output,
4647                                       expected_disk,
4648                                       expected_status,
4649                                       expected_skip,
4650                                       check_props=True)
4651  # New repeat the above merge but with the --record-only option.
4652  # This would result in identical mergeinfo
4653  # (r4-6) on A_COPY and two of its descendants, A_COPY/D/G and
4654  # A_COPY/B/E/beta, so the mergeinfo on the latter two should elide
4655  # to A_COPY.  In the case of A_COPY/D/G this means its wholly uncommitted
4656  # mergeinfo is removed leaving no prop mods.  In the case of
4657  # A_COPY/B/E/beta its committed mergeinfo prop is removed leaving a prop
4658  # change.
4659
4660  # to A_COPY.
4661  expected_output = wc.State(A_COPY_path, {})
4662  expected_mergeinfo_output = wc.State(A_COPY_path, {
4663    ''         : Item(status=' G'),
4664    'D/G'      : Item(status=' G'),
4665    'B/E/beta' : Item(status=' U'),
4666    })
4667  expected_elision_output = wc.State(A_COPY_path, {
4668    })
4669  expected_elision_output = wc.State(A_COPY_path, {
4670    'B/E/beta' : Item(status=' U'),
4671    'D/G'      : Item(status=' U'),
4672    })
4673  expected_status.tweak('B/E/beta', status=' M')
4674  expected_status.tweak('D/G', status='  ')
4675  expected_disk.tweak('B/E/beta', 'D/G', props={})
4676  svntest.actions.run_and_verify_merge(A_COPY_path, '3', '6',
4677                                       sbox.repo_url + '/A', None,
4678                                       expected_output,
4679                                       expected_mergeinfo_output,
4680                                       expected_elision_output,
4681                                       expected_disk,
4682                                       expected_status,
4683                                       expected_skip,
4684                                       [], True, True,
4685                                       '--record-only',
4686                                       A_COPY_path)
4687
4688  # Reverse merge r5 out of A_COPY/B/E/beta.  The mergeinfo on
4689  # A_COPY/B/E/beta which previously elided will now return,
4690  # minus r5 of course.
4691  expected_skip = wc.State(beta_COPY_path, { })
4692
4693  # run_and_verify_merge doesn't support merging to a file WCPATH
4694  # so use run_and_verify_svn.
4695  ### TODO: We can use run_and_verify_merge() here now.
4696  svntest.actions.run_and_verify_svn(
4697    expected_merge_output([[-5]],
4698                          ['U    ' + beta_COPY_path + '\n',
4699                           ' G   ' + beta_COPY_path + '\n']),
4700    [], 'merge', '-c-5',
4701    sbox.repo_url + '/A/B/E/beta',
4702    beta_COPY_path)
4703
4704  # Check beta's status and props.
4705  expected_status = wc.State(beta_COPY_path, {
4706    ''        : Item(status='MM', wc_rev=7),
4707    })
4708  svntest.actions.run_and_verify_status(beta_COPY_path, expected_status)
4709
4710  svntest.actions.run_and_verify_svn(["/A/B/E/beta:4,6\n"], [],
4711                                     'propget', SVN_PROP_MERGEINFO,
4712                                     beta_COPY_path)
4713
4714  # Merge r5 back into A_COPY/B/E/beta.  Now the mergeinfo on the merge
4715  # target (A_COPY/B/E/beta) is identical to it's nearest ancestor with
4716  # mergeinfo (A_COPY) and so the former should elide.
4717  # run_and_verify_merge doesn't support merging to a file WCPATH
4718  # so use run_and_verify_svn.
4719  svntest.actions.run_and_verify_svn(
4720    expected_merge_output([[5]],
4721                          ['G    ' + beta_COPY_path + '\n',
4722                           ' G   ' + beta_COPY_path + '\n',   # Update mergeinfo
4723                           ' U   ' + beta_COPY_path + '\n',], # Elide mereginfo,
4724                          elides=True),
4725    [], 'merge', '-c5',
4726    sbox.repo_url + '/A/B/E/beta',
4727    beta_COPY_path)
4728
4729  # Check beta's status and props.
4730  expected_status = wc.State(beta_COPY_path, {
4731    ''        : Item(status=' M', wc_rev=7),
4732    })
4733  svntest.actions.run_and_verify_status(beta_COPY_path, expected_status)
4734
4735  # Once again A_COPY/B/E/beta has no mergeinfo.
4736  svntest.actions.run_and_verify_svn([], '.*W200017: Property.*not found',
4737                                     'propget', SVN_PROP_MERGEINFO,
4738                                     beta_COPY_path)
4739
4740#----------------------------------------------------------------------
4741@SkipUnless(server_has_mergeinfo)
4742def mergeinfo_inheritance_and_discontinuous_ranges(sbox):
4743  "discontinuous merges produce correct mergeinfo"
4744
4745  # When a merge target has no explicit mergeinfo and is subject
4746  # to multiple merges, the resulting mergeinfo on the target
4747  # should reflect the combination of the inherited mergeinfo
4748  # with each merge performed.
4749  #
4750  # Also tests implied merge source and target when only a revision
4751  # range is specified.
4752
4753  sbox.build()
4754  wc_dir = sbox.wc_dir
4755
4756  # Some paths we'll care about
4757  A_url = sbox.repo_url + '/A'
4758  A_COPY_path      = sbox.ospath('A_COPY')
4759  D_COPY_path      = sbox.ospath('A_COPY/D')
4760  A_COPY_rho_path  = sbox.ospath('A_COPY/D/G/rho')
4761
4762  expected_disk, expected_status = set_up_branch(sbox)
4763
4764  # Merge r4 into A_COPY
4765  saved_cwd = os.getcwd()
4766
4767  os.chdir(A_COPY_path)
4768  svntest.actions.run_and_verify_svn(
4769    expected_merge_output([[4]],
4770                          ['U    ' + os.path.join("D", "G", "rho") + '\n',
4771                           ' U   .\n']),
4772    [], 'merge', '-c4', A_url)
4773  os.chdir(saved_cwd)
4774
4775  # Check the results of the merge.
4776  expected_status.tweak("A_COPY", status=' M')
4777  expected_status.tweak("A_COPY/D/G/rho", status='M ')
4778  svntest.actions.run_and_verify_status(wc_dir, expected_status)
4779  svntest.actions.run_and_verify_svn(["/A:4\n"], [],
4780                                     'propget', SVN_PROP_MERGEINFO,
4781                                     A_COPY_path)
4782
4783  # Merge r2:6 into A_COPY/D
4784  #
4785  # A_COPY/D should inherit the mergeinfo '/A:4' from A_COPY
4786  # combine it with the discontinous merges performed directly on
4787  # it (A/D/ 2:3 and A/D 4:6) resulting in '/A/D:3-6'.
4788  expected_output = wc.State(D_COPY_path, {
4789    'H/psi'   : Item(status='U '),
4790    'H/omega' : Item(status='U '),
4791    })
4792  expected_mergeinfo_output = wc.State(D_COPY_path, {
4793    '' : Item(status=' G'),
4794    })
4795  expected_elision_output = wc.State(D_COPY_path, {
4796    })
4797  expected_status = wc.State(D_COPY_path, {
4798    ''        : Item(status=' M', wc_rev=2),
4799    'G'       : Item(status='  ', wc_rev=2),
4800    'G/pi'    : Item(status='  ', wc_rev=2),
4801    'G/rho'   : Item(status='M ', wc_rev=2),
4802    'G/tau'   : Item(status='  ', wc_rev=2),
4803    'H'       : Item(status='  ', wc_rev=2),
4804    'H/chi'   : Item(status='  ', wc_rev=2),
4805    'H/psi'   : Item(status='M ', wc_rev=2),
4806    'H/omega' : Item(status='M ', wc_rev=2),
4807    'gamma'   : Item(status='  ', wc_rev=2),
4808    })
4809  expected_disk = wc.State('', {
4810    ''        : Item(props={SVN_PROP_MERGEINFO : '/A/D:3-6'}),
4811    'G'       : Item(),
4812    'G/pi'    : Item("This is the file 'pi'.\n"),
4813    'G/rho'   : Item("New content"),
4814    'G/tau'   : Item("This is the file 'tau'.\n"),
4815    'H'       : Item(),
4816    'H/chi'   : Item("This is the file 'chi'.\n"),
4817    'H/psi'   : Item("New content"),
4818    'H/omega' : Item("New content"),
4819    'gamma'   : Item("This is the file 'gamma'.\n")
4820    })
4821  expected_skip = wc.State(D_COPY_path, { })
4822
4823  svntest.actions.run_and_verify_merge(D_COPY_path, '2', '6',
4824                                       sbox.repo_url + '/A/D', None,
4825                                       expected_output,
4826                                       expected_mergeinfo_output,
4827                                       expected_elision_output,
4828                                       expected_disk,
4829                                       expected_status,
4830                                       expected_skip,
4831                                       check_props=True)
4832
4833  # Wipe the memory of a portion of the previous merge...
4834  ### It'd be nice to use 'merge --record-only' here, but we can't (yet)
4835  ### wipe all ranges for a file due to the bug pointed out in r864719.
4836  mu_copy_path = os.path.join(A_COPY_path, 'mu')
4837  svntest.actions.run_and_verify_svn(["property '" + SVN_PROP_MERGEINFO
4838                                      + "' set on '" +
4839                                      mu_copy_path + "'\n"], [], 'propset',
4840                                     SVN_PROP_MERGEINFO, '', mu_copy_path)
4841  # ...and confirm that we can commit the wiped mergeinfo...
4842  expected_output = wc.State(wc_dir, {
4843    'A_COPY/mu' : Item(verb='Sending'),
4844    })
4845  svntest.actions.run_and_verify_commit(wc_dir,
4846                                        expected_output,
4847                                        None,
4848                                        [],
4849                                        mu_copy_path)
4850  # ...and that the presence of the property is retained, even when
4851  # the value has been wiped.
4852  svntest.actions.run_and_verify_svn(['\n'], [], 'propget',
4853                                     SVN_PROP_MERGEINFO, mu_copy_path)
4854
4855#----------------------------------------------------------------------
4856@SkipUnless(server_has_mergeinfo)
4857@Issue(2754)
4858def merge_to_target_with_copied_children(sbox):
4859  "merge works when target has copied children"
4860
4861  # Test for Issue #2754 Can't merge to target with copied/moved children
4862
4863  sbox.build()
4864  wc_dir = sbox.wc_dir
4865  expected_disk, expected_status = set_up_branch(sbox)
4866
4867  # Some paths we'll care about
4868  D_COPY_path = sbox.ospath('A_COPY/D')
4869  G_COPY_path = sbox.ospath('A_COPY/D/G')
4870  rho_COPY_COPY_path = sbox.ospath('A_COPY/D/G/rho_copy')
4871
4872  # URL to URL copy A_COPY/D/G/rho to A_COPY/D/G/rho_copy
4873  svntest.actions.run_and_verify_svn(None, [], 'copy',
4874                                     sbox.repo_url + '/A_COPY/D/G/rho',
4875                                     sbox.repo_url + '/A_COPY/D/G/rho_copy',
4876                                     '-m', 'copy')
4877
4878  # Update WC.
4879  expected_output = wc.State(wc_dir,
4880                             {'A_COPY/D/G/rho_copy' : Item(status='A ')})
4881  expected_disk.add({
4882    'A_COPY/D/G/rho_copy' : Item("This is the file 'rho'.\n", props={})
4883    })
4884  expected_status.tweak(wc_rev=7)
4885  expected_status.add({'A_COPY/D/G/rho_copy' : Item(status='  ', wc_rev=7)})
4886  svntest.actions.run_and_verify_update(wc_dir,
4887                                        expected_output,
4888                                        expected_disk,
4889                                        expected_status,
4890                                        check_props=True)
4891
4892  # Merge r4 into A_COPY/D/G/rho_copy.
4893  svntest.actions.run_and_verify_svn(
4894    expected_merge_output([[4]],
4895                          ['U    ' + rho_COPY_COPY_path + '\n',
4896                           ' U   ' + rho_COPY_COPY_path + '\n']),
4897    [], 'merge', '-c4',
4898    sbox.repo_url + '/A/D/G/rho',
4899    rho_COPY_COPY_path)
4900
4901  # Merge r3:5 into A_COPY/D/G.
4902  expected_output = wc.State(G_COPY_path, {
4903    'rho' : Item(status='U ')
4904    })
4905  expected_mergeinfo_output = wc.State(G_COPY_path, {
4906    ''         : Item(status=' U'),
4907    })
4908  expected_elision_output = wc.State(G_COPY_path, {
4909    })
4910  expected_status = wc.State(G_COPY_path, {
4911    ''         : Item(status=' M', wc_rev=7),
4912    'pi'       : Item(status='  ', wc_rev=7),
4913    'rho'      : Item(status='M ', wc_rev=7),
4914    'rho_copy' : Item(status='MM', wc_rev=7),
4915    'tau'      : Item(status='  ', wc_rev=7),
4916    })
4917  expected_disk = wc.State('', {
4918    ''         : Item(props={SVN_PROP_MERGEINFO : '/A/D/G:4-5'}),
4919    'pi'       : Item("This is the file 'pi'.\n"),
4920    'rho'      : Item("New content"),
4921    'rho_copy' : Item("New content",
4922                      props={SVN_PROP_MERGEINFO : '/A/D/G/rho:4'}),
4923    'tau'      : Item("This is the file 'tau'.\n"),
4924    })
4925  expected_skip = wc.State(G_COPY_path, { })
4926  svntest.actions.run_and_verify_merge(G_COPY_path, '3', '5',
4927                                       sbox.repo_url + '/A/D/G', None,
4928                                       expected_output,
4929                                       expected_mergeinfo_output,
4930                                       expected_elision_output,
4931                                       expected_disk,
4932                                       expected_status,
4933                                       expected_skip,
4934                                       check_props=True)
4935
4936#----------------------------------------------------------------------
4937@SkipUnless(server_has_mergeinfo)
4938@Issue(3188)
4939def merge_to_switched_path(sbox):
4940  "merge to switched path does not inherit or elide"
4941
4942  # When the target of a merge is a switched path we don't inherit WC
4943  # mergeinfo from above the target or attempt to elide the mergeinfo
4944  # set on the target as a result of the merge.
4945
4946  sbox.build()
4947  wc_dir = sbox.wc_dir
4948  wc_disk, wc_status = set_up_branch(sbox)
4949
4950  # Some paths we'll care about
4951  A_COPY_path = sbox.ospath('A_COPY')
4952  A_COPY_D_path = sbox.ospath('A_COPY/D')
4953  G_COPY_path = sbox.ospath('A/D/G_COPY')
4954  A_COPY_D_G_path = sbox.ospath('A_COPY/D/G')
4955  A_COPY_D_G_rho_path = sbox.ospath('A_COPY/D/G/rho')
4956
4957  expected = svntest.verify.UnorderedOutput(
4958         ["A         " + G_COPY_path + "\n",
4959          "A         " + os.path.join(G_COPY_path, "pi") + "\n",
4960          "A         " + os.path.join(G_COPY_path, "rho") + "\n",
4961          "A         " + os.path.join(G_COPY_path, "tau") + "\n",
4962          ])
4963
4964  # r7 - Copy A/D/G to A/D/G_COPY and commit.
4965  svntest.actions.run_and_verify_svn(expected, [], 'copy',
4966                                     sbox.repo_url + "/A/D/G",
4967                                     G_COPY_path)
4968
4969  expected_output = wc.State(wc_dir, {'A/D/G_COPY' : Item(verb='Adding')})
4970  wc_status.add({
4971    "A/D/G_COPY"     : Item(status='  ', wc_rev=7),
4972    "A/D/G_COPY/pi"  : Item(status='  ', wc_rev=7),
4973    "A/D/G_COPY/rho" : Item(status='  ', wc_rev=7),
4974    "A/D/G_COPY/tau" : Item(status='  ', wc_rev=7),
4975    })
4976
4977  svntest.actions.run_and_verify_commit(wc_dir, expected_output, wc_status)
4978
4979  # r8 - modify and commit A/D/G_COPY/rho
4980  svntest.main.file_write(sbox.ospath('A/D/G_COPY/rho'),
4981                          "New *and* improved rho content")
4982  expected_output = wc.State(wc_dir, {'A/D/G_COPY/rho' : Item(verb='Sending')})
4983  wc_status.tweak('A/D/G_COPY/rho', wc_rev=8)
4984  svntest.actions.run_and_verify_commit(wc_dir, expected_output, wc_status)
4985
4986  # Switch A_COPY/D/G to A/D/G.
4987  wc_disk.add({
4988    "A"  : Item(),
4989    "A/D/G_COPY"     : Item(),
4990    "A/D/G_COPY/pi"  : Item("This is the file 'pi'.\n"),
4991    "A/D/G_COPY/rho" : Item("New *and* improved rho content"),
4992    "A/D/G_COPY/tau" : Item("This is the file 'tau'.\n"),
4993    })
4994  wc_disk.tweak('A_COPY/D/G/rho',contents="New content")
4995  wc_status.tweak("A_COPY/D/G", wc_rev=8, switched='S')
4996  wc_status.tweak("A_COPY/D/G/pi", wc_rev=8)
4997  wc_status.tweak("A_COPY/D/G/rho", wc_rev=8)
4998  wc_status.tweak("A_COPY/D/G/tau", wc_rev=8)
4999  expected_output = svntest.wc.State(sbox.wc_dir, {
5000    "A_COPY/D/G/rho"         : Item(status='U '),
5001    })
5002  svntest.actions.run_and_verify_switch(sbox.wc_dir, A_COPY_D_G_path,
5003                                        sbox.repo_url + "/A/D/G",
5004                                        expected_output, wc_disk, wc_status,
5005                                        [], 1)
5006
5007  # Update working copy to allow elision (if any).
5008  svntest.actions.run_and_verify_svn(exp_noop_up_out(8), [],
5009                                     'up', wc_dir)
5010
5011  # Set some mergeinfo on a working copy parent of our switched subtree
5012  # A_COPY/D/G.  Because the subtree is switched it should *not* inherit
5013  # this mergeinfo.
5014  svntest.actions.run_and_verify_svn(["property '" + SVN_PROP_MERGEINFO +
5015                                      "' set on '" + A_COPY_path + "'" +
5016                                      "\n"], [], 'ps', SVN_PROP_MERGEINFO,
5017                                     '/A:4', A_COPY_path)
5018
5019  # Merge r8 from A/D/G_COPY into our switched target A_COPY/D/G.
5020  # A_COPY/D/G should get mergeinfo for r8 as a result of the merge,
5021  # but because it's switched should not inherit the mergeinfo from
5022  # its nearest WC ancestor with mergeinfo (A_COPY: svn:mergeinfo : /A:4)
5023  expected_output = wc.State(A_COPY_D_G_path, {
5024    'rho' : Item(status='U ')
5025    })
5026  expected_mergeinfo_output = wc.State(A_COPY_D_G_path, {
5027    '' : Item(status=' U')
5028    })
5029  expected_elision_output = wc.State(A_COPY_D_G_path, {
5030    })
5031  # Note: A_COPY/D/G won't show as switched.
5032  expected_status = wc.State(A_COPY_D_G_path, {
5033    ''         : Item(status=' M', wc_rev=8),
5034    'pi'       : Item(status='  ', wc_rev=8),
5035    'rho'      : Item(status='M ', wc_rev=8),
5036    'tau'      : Item(status='  ', wc_rev=8),
5037    })
5038  expected_status.tweak('', switched='S')
5039  expected_disk = wc.State('', {
5040    ''         : Item(props={SVN_PROP_MERGEINFO : '/A/D/G_COPY:8'}),
5041    'pi'       : Item("This is the file 'pi'.\n"),
5042    'rho'      : Item("New *and* improved rho content"),
5043    'tau'      : Item("This is the file 'tau'.\n"),
5044    })
5045  expected_skip = wc.State(A_COPY_D_G_path, { })
5046
5047  svntest.actions.run_and_verify_merge(A_COPY_D_G_path, '7', '8',
5048                                       sbox.repo_url + '/A/D/G_COPY', None,
5049                                       expected_output,
5050                                       expected_mergeinfo_output,
5051                                       expected_elision_output,
5052                                       expected_disk,
5053                                       expected_status, expected_skip,
5054                                       check_props=True)
5055
5056  # Check that the mergeinfo set on a switched target can elide to the
5057  # repository.
5058  #
5059  # Specifically this is testing the "switched target" portions of
5060  # issue #3188 'Mergeinfo on switched targets/subtrees should
5061  # elide to repos'.
5062  #
5063  # Revert the previous merge and manually set 'svn:mergeinfo : /A/D:4'
5064  # on 'merge_tests-1\A_COPY\D'.  Now merge -c-4 from /A/D/G into A_COPY/D/G.
5065  # This should produce no mergeinfo on A_COPY/D/G'.  If the A_COPY/D/G was
5066  # unswitched this merge would normally set empty mergeinfo on A_COPY/D/G,
5067  # but as it is switched this empty mergeinfo just elides to the
5068  # repository (empty mergeinfo on a path can elide if that path doesn't
5069  # inherit *any* mergeinfo).
5070  svntest.actions.run_and_verify_svn(["Reverted '" + A_COPY_path+ "'\n",
5071                                      "Reverted '" + A_COPY_D_G_path+ "'\n",
5072                                      "Reverted '" + A_COPY_D_G_rho_path +
5073                                      "'\n"],
5074                                     [], 'revert', '-R', wc_dir)
5075  svntest.actions.run_and_verify_svn(["property '" + SVN_PROP_MERGEINFO +
5076                                      "' set on '" + A_COPY_D_path+ "'" +
5077                                      "\n"], [], 'ps', SVN_PROP_MERGEINFO,
5078                                     '/A/D:4', A_COPY_D_path)
5079  svntest.actions.run_and_verify_svn(
5080    expected_merge_output([[-4]],
5081                          ['U    ' + A_COPY_D_G_rho_path + '\n',
5082                           ' U   ' + A_COPY_D_G_path + '\n'],
5083                          elides=True),
5084    [], 'merge', '-c-4',
5085    sbox.repo_url + '/A/D/G_COPY',
5086    A_COPY_D_G_path)
5087  wc_status.tweak("A_COPY/D", status=' M')
5088  wc_status.tweak("A_COPY/D/G/rho", status='M ')
5089  wc_status.tweak(wc_rev=8)
5090  svntest.actions.run_and_verify_status(wc_dir, wc_status)
5091  check_mergeinfo_recursively(A_COPY_D_path,
5092                              { A_COPY_D_path : '/A/D:4' })
5093
5094#----------------------------------------------------------------------
5095# Test for issues
5096#
5097#   2823: Account for mergeinfo differences for switched
5098#         directories when gathering mergeinfo
5099#
5100#   2839: Support non-inheritable mergeinfo revision ranges
5101#
5102#   3187: Reverse merges don't work properly with
5103#         non-inheritable ranges.
5104#
5105#   3188: Mergeinfo on switched targets/subtrees should
5106#         elide to repos
5107@SkipUnless(server_has_mergeinfo)
5108@Issue(2823,2839,3187,3188,4056)
5109def merge_to_path_with_switched_children(sbox):
5110  "merge to path with switched children"
5111
5112  # Merging to a target with switched children requires special handling
5113  # to keep mergeinfo correct:
5114  #
5115  #   1) If the target of a merge has switched children without explicit
5116  #      mergeinfo, the switched children should get mergeinfo set on
5117  #      them as a result of the merge.  This mergeinfo includes the
5118  #      mergeinfo resulting from the merge *and* any mergeinfo inherited
5119  #      from the repos for the switched path.
5120  #
5121  #   2) Mergeinfo on switched children should never elide.
5122  #
5123  #   3) The path the switched child overrides cannot be modified by the
5124  #      merge (it isn't present in the WC) so should not inherit any
5125  #      mergeinfo added as a result of the merge.  To prevent this, the
5126  #      immediate parent of any switched child should have non-inheritable
5127  #      mergeinfo added/modified for the merge performed.
5128  #
5129  #   4) Because of 3, siblings of switched children will not inherit the
5130  #      mergeinfo resulting from the merge, so must get their own, full set
5131  #      of mergeinfo.
5132
5133  sbox.build()
5134  wc_dir = sbox.wc_dir
5135  wc_disk, wc_status = set_up_branch(sbox, False, 3)
5136
5137  # Some paths we'll care about
5138  D_path = sbox.ospath('A/D')
5139  A_COPY_path = sbox.ospath('A_COPY')
5140  A_COPY_beta_path = sbox.ospath('A_COPY/B/E/beta')
5141  A_COPY_chi_path = sbox.ospath('A_COPY/D/H/chi')
5142  A_COPY_omega_path = sbox.ospath('A_COPY/D/H/omega')
5143  A_COPY_psi_path = sbox.ospath('A_COPY/D/H/psi')
5144  A_COPY_G_path = sbox.ospath('A_COPY/D/G')
5145  A_COPY_rho_path = sbox.ospath('A_COPY/D/G/rho')
5146  A_COPY_H_path = sbox.ospath('A_COPY/D/H')
5147  A_COPY_D_path = sbox.ospath('A_COPY/D')
5148  A_COPY_gamma_path = sbox.ospath('A_COPY/D/gamma')
5149  H_COPY_2_path = sbox.ospath('A_COPY_2/D/H')
5150
5151  svntest.actions.run_and_verify_svn(exp_noop_up_out(8), [], 'up',
5152                                     wc_dir)
5153  wc_status.tweak(wc_rev=8)
5154
5155  # Switch a file and dir path in the branch:
5156
5157  # Switch A_COPY/D/G to A_COPY_2/D/G.
5158  wc_status.tweak("A_COPY/D/G", switched='S')
5159  expected_output = svntest.wc.State(sbox.wc_dir, {})
5160  svntest.actions.run_and_verify_switch(sbox.wc_dir, A_COPY_G_path,
5161                                        sbox.repo_url + "/A_COPY_2/D/G",
5162                                        expected_output, wc_disk, wc_status,
5163                                        [], 1)
5164
5165  # Switch A_COPY/D/G/rho to A_COPY_3/D/G/rho.
5166  wc_status.tweak("A_COPY/D/G/rho", switched='S')
5167  expected_output = svntest.wc.State(sbox.wc_dir, {})
5168  svntest.actions.run_and_verify_switch(sbox.wc_dir, A_COPY_rho_path,
5169                                        sbox.repo_url + "/A_COPY_3/D/G/rho",
5170                                        expected_output, wc_disk, wc_status,
5171                                        [], 1)
5172
5173  # Switch A_COPY/D/H/psi to A_COPY_2/D/H/psi.
5174  wc_status.tweak("A_COPY/D/H/psi", switched='S')
5175  expected_output = svntest.wc.State(sbox.wc_dir, {})
5176  svntest.actions.run_and_verify_switch(sbox.wc_dir, A_COPY_psi_path,
5177                                        sbox.repo_url + "/A_COPY_2/D/H/psi",
5178                                        expected_output, wc_disk, wc_status,
5179                                        [], 1)
5180
5181  # Target with switched file child:
5182  #
5183  # Merge r8 from A/D/H into A_COPY/D/H.  The switched child of
5184  # A_COPY/D/H, file A_COPY/D/H/psi (which has no mergeinfo prior
5185  # to the merge), is unaffected by the merge so does not get it's
5186  # own explicit mergeinfo.
5187  #
5188  # A_COPY/D/H/psi's parent A_COPY/D/H has no pre-exiting explicit
5189  # mergeinfo so should get its own mergeinfo, the non-inheritable
5190  # r8 resulting from the merge.
5191  #
5192  # A_COPY/D/H/psi's unswitched sibling, A_COPY/D/H/omega is affected
5193  # by the merge but won't inherit r8 from A_COPY/D/H, so it needs its
5194  # own mergeinfo.
5195  expected_output = wc.State(A_COPY_H_path, {
5196    'omega' : Item(status='U '),
5197    })
5198  expected_mergeinfo_output = wc.State(A_COPY_H_path, {
5199    ''      : Item(status=' U'),
5200    'omega' : Item(status=' U')
5201    })
5202  expected_elision_output = wc.State(A_COPY_H_path, {
5203    'omega' : Item(status=' U')
5204    })
5205  expected_status = wc.State(A_COPY_H_path, {
5206    ''      : Item(status=' M', wc_rev=8),
5207    'psi'   : Item(status='  ', wc_rev=8, switched='S'),
5208    'omega' : Item(status='M ', wc_rev=8),
5209    'chi'   : Item(status='  ', wc_rev=8),
5210    })
5211  expected_disk = wc.State('', {
5212    ''      : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:8'}),
5213    'psi'   : Item("This is the file 'psi'.\n"),
5214    'omega' : Item("New content"),
5215    'chi'   : Item("This is the file 'chi'.\n"),
5216    })
5217  expected_skip = wc.State(A_COPY_H_path, { })
5218
5219  svntest.actions.run_and_verify_merge(A_COPY_H_path, '7', '8',
5220                                       sbox.repo_url + '/A/D/H', None,
5221                                       expected_output,
5222                                       expected_mergeinfo_output,
5223                                       expected_elision_output,
5224                                       expected_disk,
5225                                       expected_status, expected_skip,
5226                                       check_props=True)
5227
5228  # Target with switched dir child:
5229  #
5230  # Merge r6 from A/D into A_COPY/D.  The only subtrees with explicit
5231  # mergeinfo (or switched) that are affected by the merge are A_COPY/D/G
5232  # and A_COPY/D/G/rho.  Only these two subtrees, and the target itself,
5233  # should receive mergeinfo updates.
5234  expected_output = wc.State(A_COPY_D_path, {
5235    'G/rho' : Item(status='U ')
5236    })
5237  expected_mergeinfo_output = wc.State(A_COPY_D_path, {
5238    ''      : Item(status=' U'),
5239    'G'     : Item(status=' U'),
5240    'G/rho' : Item(status=' U')
5241    })
5242  expected_elision_output = wc.State(A_COPY_D_path, {
5243    })
5244  expected_status_D = wc.State(A_COPY_D_path, {
5245    ''        : Item(status=' M', wc_rev=8),
5246    'H'       : Item(status=' M', wc_rev=8),
5247    'H/chi'   : Item(status='  ', wc_rev=8),
5248    'H/omega' : Item(status='M ', wc_rev=8),
5249    'H/psi'   : Item(status='  ', wc_rev=8, switched='S'),
5250    'G'       : Item(status=' M', wc_rev=8, switched='S'),
5251    'G/pi'    : Item(status='  ', wc_rev=8),
5252    'G/rho'   : Item(status='MM', wc_rev=8, switched='S'),
5253    'G/tau'   : Item(status='  ', wc_rev=8),
5254    'gamma'   : Item(status='  ', wc_rev=8),
5255    })
5256  expected_disk_D = wc.State('', {
5257    ''        : Item(props={SVN_PROP_MERGEINFO : '/A/D:6*'}),
5258    'H'       : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:8'}),
5259    'H/chi'   : Item("This is the file 'chi'.\n"),
5260    'H/omega' : Item("New content"),
5261    'H/psi'   : Item("This is the file 'psi'.\n",),
5262    'G'       : Item(props={SVN_PROP_MERGEINFO : '/A/D/G:6*'}),
5263    'G/pi'    : Item("This is the file 'pi'.\n"),
5264    'G/rho'   : Item("New content",
5265                     props={SVN_PROP_MERGEINFO : '/A/D/G/rho:6'}),
5266    'G/tau'   : Item("This is the file 'tau'.\n"),
5267    'gamma'   : Item("This is the file 'gamma'.\n"),
5268    })
5269  expected_skip_D = wc.State(A_COPY_D_path, { })
5270  svntest.actions.run_and_verify_merge(A_COPY_D_path, '5', '6',
5271                                       sbox.repo_url + '/A/D', None,
5272                                       expected_output,
5273                                       expected_mergeinfo_output,
5274                                       expected_elision_output,
5275                                       expected_disk_D,
5276                                       expected_status_D, expected_skip_D,
5277                                       check_props=True)
5278
5279
5280  # Merge r5 from A/D into A_COPY/D.  This updates the mergeinfo on the
5281  # target A_COPY\D because the target is always updated.  It also updates
5282  # the mergeinfo on A_COPY\D\H because that path has explicit mergeinfo
5283  # and has a subtree affected by the merge.  Lastly, mergeinfo on
5284  # A_COPY/D/H/psi is added because that path is switched.
5285  expected_output = wc.State(A_COPY_D_path, {
5286    'H/psi' : Item(status='U ')})
5287  expected_mergeinfo_output = wc.State(A_COPY_D_path, {
5288    ''      : Item(status=' G'),
5289    'H'     : Item(status=' G'),
5290    'H/psi' : Item(status=' U')
5291    })
5292  expected_elision_output = wc.State(A_COPY_D_path, {
5293    })
5294  expected_disk_D.tweak('', props={SVN_PROP_MERGEINFO : '/A/D:5,6*'})
5295  expected_disk_D.tweak('H', props={SVN_PROP_MERGEINFO : '/A/D/H:5*,8'})
5296  expected_disk_D.tweak('H/psi', contents="New content",
5297                        props={SVN_PROP_MERGEINFO :'/A/D/H/psi:5'})
5298  expected_status_D.tweak('H/psi', status='MM')
5299  svntest.actions.run_and_verify_merge(A_COPY_D_path, '4', '5',
5300                                       sbox.repo_url + '/A/D', None,
5301                                       expected_output,
5302                                       expected_mergeinfo_output,
5303                                       expected_elision_output,
5304                                       expected_disk_D,
5305                                       expected_status_D, expected_skip_D,
5306                                       check_props=True)
5307
5308  # Finally, merge r4:8 into A_COPY.  A_COPY gets mergeinfo for r5-8 added but
5309  # since none of A_COPY's subtrees with mergeinfo are affected, none of them
5310  # get any mergeinfo changes.
5311  expected_output = wc.State(A_COPY_path, {
5312    'B/E/beta' : Item(status='U ')
5313    })
5314  expected_mergeinfo_output = wc.State(A_COPY_path, {
5315    '' : Item(status=' U')
5316    })
5317  expected_elision_output = wc.State(A_COPY_path, {
5318    })
5319  expected_status = wc.State(A_COPY_path, {
5320    ''          : Item(status=' M', wc_rev=8),
5321    'B'         : Item(status='  ', wc_rev=8),
5322    'mu'        : Item(status='  ', wc_rev=8),
5323    'B/E'       : Item(status='  ', wc_rev=8),
5324    'B/E/alpha' : Item(status='  ', wc_rev=8),
5325    'B/E/beta'  : Item(status='M ', wc_rev=8),
5326    'B/lambda'  : Item(status='  ', wc_rev=8),
5327    'B/F'       : Item(status='  ', wc_rev=8),
5328    'C'         : Item(status='  ', wc_rev=8),
5329    'D'         : Item(status=' M', wc_rev=8),
5330    'D/G'       : Item(status=' M', wc_rev=8, switched='S'),
5331    'D/G/pi'    : Item(status='  ', wc_rev=8),
5332    'D/G/rho'   : Item(status='MM', wc_rev=8, switched='S'),
5333    'D/G/tau'   : Item(status='  ', wc_rev=8),
5334    'D/gamma'   : Item(status='  ', wc_rev=8),
5335    'D/H'       : Item(status=' M', wc_rev=8),
5336    'D/H/chi'   : Item(status='  ', wc_rev=8),
5337    'D/H/psi'   : Item(status='MM', wc_rev=8, switched='S'),
5338    'D/H/omega' : Item(status='M ', wc_rev=8),
5339    })
5340  expected_disk = wc.State('', {
5341    ''          : Item(props={SVN_PROP_MERGEINFO : '/A:5-8'}),
5342    'B'         : Item(),
5343    'mu'        : Item("This is the file 'mu'.\n"),
5344    'B/E'       : Item(),
5345    'B/E/alpha' : Item("This is the file 'alpha'.\n"),
5346    'B/E/beta'  : Item("New content"),
5347    'B/lambda'  : Item("This is the file 'lambda'.\n"),
5348    'B/F'       : Item(),
5349    'C'         : Item(),
5350    'D'         : Item(props={SVN_PROP_MERGEINFO : '/A/D:5,6*'}),
5351    'D/G'       : Item(props={SVN_PROP_MERGEINFO : '/A/D/G:6*'}),
5352    'D/G/pi'    : Item("This is the file 'pi'.\n"),
5353    'D/G/rho'   : Item("New content",
5354                       props={SVN_PROP_MERGEINFO : '/A/D/G/rho:6'}),
5355    'D/G/tau'   : Item("This is the file 'tau'.\n"),
5356    'D/gamma'   : Item("This is the file 'gamma'.\n"),
5357    'D/H'       : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:5*,8'}),
5358    'D/H/chi'   : Item("This is the file 'chi'.\n"),
5359    'D/H/psi'   : Item("New content",
5360                       props={SVN_PROP_MERGEINFO : '/A/D/H/psi:5'}),
5361    'D/H/omega' : Item("New content"),
5362    })
5363  expected_skip = wc.State(A_COPY_path, { })
5364  svntest.actions.run_and_verify_merge(A_COPY_path, '4', '8',
5365                                       sbox.repo_url + '/A', None,
5366                                       expected_output,
5367                                       expected_mergeinfo_output,
5368                                       expected_elision_output,
5369                                       expected_disk,
5370                                       expected_status, expected_skip,
5371                                       check_props=True)
5372  # Commit changes thus far.
5373  expected_output = svntest.wc.State(wc_dir, {
5374    'A_COPY'           : Item(verb='Sending'),
5375    'A_COPY/B/E/beta'  : Item(verb='Sending'),
5376    'A_COPY/D'         : Item(verb='Sending'),
5377    'A_COPY/D/G'       : Item(verb='Sending'),
5378    'A_COPY/D/G/rho'   : Item(verb='Sending'),
5379    'A_COPY/D/H'         : Item(verb='Sending'),
5380    'A_COPY/D/H/omega'   : Item(verb='Sending'),
5381    'A_COPY/D/H/psi'     : Item(verb='Sending'),
5382    })
5383  wc_status.tweak('A_COPY', 'A_COPY/B/E/beta', 'A_COPY/D', 'A_COPY/D/G',
5384                  'A_COPY/D/G/rho', 'A_COPY/D/H', 'A_COPY/D/H/omega',
5385                  'A_COPY/D/H/psi', wc_rev=9)
5386  svntest.actions.run_and_verify_commit(wc_dir, expected_output, wc_status)
5387
5388  # Unswitch A_COPY/D/H/psi.
5389  expected_output = svntest.wc.State(wc_dir, {
5390    'A_COPY/D/H/psi' : Item(status='UU')})
5391  wc_status.tweak("A_COPY/D/H/psi", switched=None, wc_rev=9)
5392  wc_disk.tweak("A_COPY",
5393                props={SVN_PROP_MERGEINFO : '/A:5-8'})
5394  wc_disk.tweak("A_COPY/B/E/beta",
5395                contents="New content")
5396  wc_disk.tweak("A_COPY/D",
5397                props={SVN_PROP_MERGEINFO : '/A/D:5,6*'})
5398  wc_disk.tweak("A_COPY/D/G",
5399                props={SVN_PROP_MERGEINFO : '/A/D/G:6*'})
5400  wc_disk.tweak("A_COPY/D/G/rho",
5401                contents="New content",
5402                props={SVN_PROP_MERGEINFO : '/A/D/G/rho:6'})
5403  wc_disk.tweak("A_COPY/D/H",
5404                props={SVN_PROP_MERGEINFO : '/A/D/H:5*,8'})
5405  wc_disk.tweak("A_COPY/D/H/omega",
5406                contents="New content")
5407  wc_disk.tweak("A_COPY_2", props={})
5408  svntest.actions.run_and_verify_switch(sbox.wc_dir, A_COPY_psi_path,
5409                                        sbox.repo_url + "/A_COPY/D/H/psi",
5410                                        expected_output, wc_disk, wc_status,
5411                                        [], 1)
5412
5413  # Non-inheritable mergeinfo ranges on a target don't prevent repeat
5414  # merges of that range on the target's children.
5415  #
5416  # Non-inheritable mergeinfo ranges on a target are removed if the target
5417  # no longer has any switched children and a repeat merge is performed.
5418  #
5419  # Merge r4:8 from A/D/H into A_COPY/D/H.  A_COPY/D/H already has mergeinfo
5420  # for r5 and r8 but it is marked as uninheritable so the repeat merge is
5421  # allowed on its children, notably the now unswitched A_COPY/D/H/psi.
5422  # Since A_COPY/D/H no longer has any switched children and the merge of
5423  # r4:8 has been repeated the previously uninheritable ranges 5* and 8* on
5424  # A_COPY/D/H are made inheritable and combined with r6-7.  A_COPY/D/H/omega
5425  # has explicit mergeinfo, but is not touched by the merge, so is left as-is.
5426  expected_output = wc.State(A_COPY_H_path, {
5427    'psi' : Item(status='U ')
5428    })
5429  expected_mergeinfo_output = wc.State(A_COPY_H_path, {
5430    ''    : Item(status=' U'),
5431    'psi' : Item(status=' G')
5432    })
5433  expected_elision_output = wc.State(A_COPY_H_path, {
5434    'psi' : Item(status=' U')
5435    })
5436  expected_status = wc.State(A_COPY_H_path, {
5437    ''      : Item(status=' M', wc_rev=9),
5438    'psi'   : Item(status='M ', wc_rev=9),
5439    'omega' : Item(status='  ', wc_rev=9),
5440    'chi'   : Item(status='  ', wc_rev=8),
5441    })
5442  expected_disk = wc.State('', {
5443    ''      : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:5-8'}),
5444    'psi'   : Item("New content"),
5445    'omega' : Item("New content"),
5446    'chi'   : Item("This is the file 'chi'.\n"),
5447    })
5448  expected_skip = wc.State(A_COPY_H_path, { })
5449  svntest.actions.run_and_verify_merge(A_COPY_H_path, '4', '8',
5450                                       sbox.repo_url + '/A/D/H', None,
5451                                       expected_output,
5452                                       expected_mergeinfo_output,
5453                                       expected_elision_output,
5454                                       expected_disk,
5455                                       expected_status, expected_skip,
5456                                       [],
5457                                       True, False, '--allow-mixed-revisions',
5458                                       A_COPY_H_path)
5459
5460  # Non-inheritable mergeinfo ranges on a target do prevent repeat
5461  # merges on the target itself.
5462  #
5463  # Add a prop A/D and commit it as r10.  Merge r10 into A_COPY/D.  Since
5464  # A_COPY/D has a switched child it gets r10 added as a non-inheritable
5465  # range.  Repeat the same merge checking that no repeat merge is
5466  # attempted on A_COPY/D.
5467  svntest.actions.run_and_verify_svn(["property 'prop:name' set on '" +
5468                                      D_path + "'\n"], [], 'ps',
5469                                     'prop:name', 'propval', D_path)
5470  expected_output = svntest.wc.State(wc_dir, {
5471    'A/D'            : Item(verb='Sending'),
5472    'A_COPY/D/H'       : Item(verb='Sending'),
5473    'A_COPY/D/H/psi'   : Item(verb='Sending'),
5474    })
5475  wc_status.tweak('A_COPY/D', wc_rev=9)
5476  wc_status.tweak('A/D', 'A_COPY/D/H', 'A_COPY/D/H/psi', wc_rev=10)
5477  svntest.actions.run_and_verify_commit(wc_dir, expected_output, wc_status)
5478  expected_output = wc.State(A_COPY_D_path, {
5479    '' : Item(status=' U')
5480    })
5481  expected_mergeinfo_output = wc.State(A_COPY_D_path, {
5482    '' : Item(status=' U')
5483    })
5484  expected_elision_output = wc.State(A_COPY_D_path, {
5485    })
5486  # Reuse expected status and disk from last merge to A_COPY/D
5487  expected_status_D.tweak(status='  ')
5488  expected_status_D.tweak('', status=' M', wc_rev=9)
5489  expected_status_D.tweak('H', wc_rev=10)
5490  expected_status_D.tweak('H/psi', wc_rev=10, switched=None)
5491  expected_status_D.tweak('H/omega', wc_rev=9)
5492  expected_status_D.tweak('G', 'G/rho', switched='S', wc_rev=9)
5493  expected_disk_D.tweak('', props={SVN_PROP_MERGEINFO : '/A/D:5,6*,10',
5494                                   "prop:name" : "propval"})
5495  expected_disk_D.tweak('G/rho',
5496                        props={SVN_PROP_MERGEINFO : '/A/D/G/rho:6'})
5497  expected_disk_D.tweak('H', props={SVN_PROP_MERGEINFO : '/A/D/H:5-8'})
5498  expected_disk_D.tweak('H/psi', contents="New content", props={})
5499  svntest.actions.run_and_verify_merge(A_COPY_D_path, '9', '10',
5500                                       sbox.repo_url + '/A/D', None,
5501                                       expected_output,
5502                                       expected_mergeinfo_output,
5503                                       expected_elision_output,
5504                                       expected_disk_D,
5505                                       expected_status_D, expected_skip_D,
5506                                       [],
5507                                       True, False, '--allow-mixed-revisions',
5508                                       A_COPY_D_path)
5509  # Repeated merge is a no-op, though we still see the notification reporting
5510  # the mergeinfo describing the merge has been recorded, though this time it
5511  # is a ' G' notification because there is a local mergeinfo change.
5512  expected_output = wc.State(A_COPY_D_path, {})
5513  expected_mergeinfo_output = wc.State(A_COPY_D_path, {
5514    '' : Item(status=' G')
5515    })
5516  svntest.actions.run_and_verify_merge(A_COPY_D_path, '9', '10',
5517                                       sbox.repo_url + '/A/D', None,
5518                                       expected_output,
5519                                       expected_mergeinfo_output,
5520                                       expected_elision_output,
5521                                       expected_disk_D,
5522                                       expected_status_D, expected_skip_D,
5523                                       [],
5524                                       True, False, '--allow-mixed-revisions',
5525                                       A_COPY_D_path)
5526
5527  # Test issue #3187 'Reverse merges don't work properly with
5528  # non-inheritable ranges'.
5529  #
5530  # Test the "switched subtrees" portion of issue #3188 'Mergeinfo on
5531  # switched targets/subtrees should elide to repos'.
5532  #
5533  # Reverse merge r5-8, this should revert all the subtree merges done to
5534  # A_COPY thus far and remove all mergeinfo.
5535
5536  # Revert all local changes.  This leaves just the mergeinfo for r5-8
5537  # on A_COPY and its various subtrees.
5538  svntest.actions.run_and_verify_svn(None, [], 'revert', '-R', wc_dir)
5539
5540  # Update merge target so working revisions are uniform and all
5541  # possible elision occurs.
5542  svntest.actions.run_and_verify_svn(exp_noop_up_out(10), [],
5543                                     'up', A_COPY_path)
5544
5545  #  Do the reverse merge.
5546  expected_output = wc.State(A_COPY_path, {
5547    'B/E/beta'  : Item(status='U '),
5548    'D/G/rho'   : Item(status='U '),
5549    'D/H/omega' : Item(status='U '),
5550    'D/H/psi'   : Item(status='U ')
5551    })
5552  expected_mergeinfo_output = wc.State(A_COPY_path, {
5553    ''          : Item(status=' U'),
5554    'D'         : Item(status=' U'),
5555    'D/G'       : Item(status=' U'),
5556    'D/G/rho'   : Item(status=' U'),
5557    'D/H'       : Item(status=' U'),
5558    })
5559  expected_elision_output = wc.State(A_COPY_path, {
5560    ''          : Item(status=' U'),
5561    'D'         : Item(status=' U'),
5562    'D/G'       : Item(status=' U'),
5563    'D/G/rho'   : Item(status=' U'),
5564    'D/H'       : Item(status=' U'),
5565    })
5566  expected_status = wc.State(A_COPY_path, {
5567    ''          : Item(status=' M', wc_rev=10),
5568    'B'         : Item(status='  ', wc_rev=10),
5569    'mu'        : Item(status='  ', wc_rev=10),
5570    'B/E'       : Item(status='  ', wc_rev=10),
5571    'B/E/alpha' : Item(status='  ', wc_rev=10),
5572    'B/E/beta'  : Item(status='M ', wc_rev=10),
5573    'B/lambda'  : Item(status='  ', wc_rev=10),
5574    'B/F'       : Item(status='  ', wc_rev=10),
5575    'C'         : Item(status='  ', wc_rev=10),
5576    'D'         : Item(status=' M', wc_rev=10),
5577    'D/G'       : Item(status=' M', wc_rev=10, switched='S'),
5578    'D/G/pi'    : Item(status='  ', wc_rev=10),
5579    'D/G/rho'   : Item(status='MM', wc_rev=10, switched='S'),
5580    'D/G/tau'   : Item(status='  ', wc_rev=10),
5581    'D/gamma'   : Item(status='  ', wc_rev=10),
5582    'D/H'       : Item(status=' M', wc_rev=10),
5583    'D/H/chi'   : Item(status='  ', wc_rev=10),
5584    'D/H/psi'   : Item(status='M ', wc_rev=10),
5585    'D/H/omega' : Item(status='M ', wc_rev=10),
5586    })
5587  expected_disk = wc.State('', {
5588    'B'         : Item(),
5589    'mu'        : Item("This is the file 'mu'.\n"),
5590    'B/E'       : Item(),
5591    'B/E/alpha' : Item("This is the file 'alpha'.\n"),
5592    'B/E/beta'  : Item("This is the file 'beta'.\n"),
5593    'B/lambda'  : Item("This is the file 'lambda'.\n"),
5594    'B/F'       : Item(),
5595    'C'         : Item(),
5596    'D'         : Item(),
5597    'D/G'       : Item(),
5598    'D/G/pi'    : Item("This is the file 'pi'.\n"),
5599    'D/G/rho'   : Item("This is the file 'rho'.\n"),
5600    'D/G/tau'   : Item("This is the file 'tau'.\n"),
5601    'D/gamma'   : Item("This is the file 'gamma'.\n"),
5602    'D/H'       : Item(),
5603    'D/H/chi'   : Item("This is the file 'chi'.\n"),
5604    'D/H/psi'   : Item("This is the file 'psi'.\n"),
5605    'D/H/omega' : Item("This is the file 'omega'.\n"),
5606    })
5607  expected_skip = wc.State(A_COPY_path, { })
5608  svntest.actions.run_and_verify_merge(A_COPY_path, '8', '4',
5609                                       sbox.repo_url + '/A', None,
5610                                       expected_output,
5611                                       expected_mergeinfo_output,
5612                                       expected_elision_output,
5613                                       expected_disk,
5614                                       expected_status, expected_skip,
5615                                       check_props=True)
5616
5617#----------------------------------------------------------------------
5618# Test for issue 2047: Merge from parent dir fails while it succeeds from
5619# the direct dir
5620@Issue(2047)
5621def merge_with_implicit_target_file(sbox):
5622  "merge a change to a file, using relative path"
5623
5624  sbox.build()
5625  wc_dir = sbox.wc_dir
5626
5627  # Make a change to A/mu, then revert it using 'svn merge -r 2:1 A/mu'
5628
5629  # change A/mu and commit
5630  A_path = sbox.ospath('A')
5631  mu_path = os.path.join(A_path, 'mu')
5632
5633  svntest.main.file_append(mu_path, "A whole new line.\n")
5634
5635  expected_output = svntest.wc.State(wc_dir, {
5636    'A/mu' : Item(verb='Sending'),
5637    })
5638  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
5639  expected_status.tweak('A/mu', wc_rev=2)
5640  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
5641                                        expected_status)
5642
5643  # Update to revision 2.
5644  svntest.actions.run_and_verify_svn(None, [], 'update', wc_dir)
5645
5646  # Revert the change committed in r2
5647  os.chdir(wc_dir)
5648
5649  # run_and_verify_merge doesn't accept file paths.
5650  svntest.actions.run_and_verify_svn(None, [], 'merge', '-r', '2:1',
5651                                     'A/mu')
5652
5653#----------------------------------------------------------------------
5654# Test practical application of issue #2769 fix, empty rev range elision,
5655# and elision to the repos.
5656@Issue(2769)
5657@SkipUnless(server_has_mergeinfo)
5658def empty_mergeinfo(sbox):
5659  "mergeinfo can explicitly be empty"
5660
5661  # A bit o' history: The fix for issue #2769 originally permitted mergeinfo
5662  # with empty range lists and as a result we permitted partial elision and
5663  # had a whole slew of tests here for that.  But the fix of issue #3029 now
5664  # prevents svn ps or svn merge from creating mergeinfo with paths mapped to
5665  # empty ranges, only empty mergeinfo is allowed.  As a result this test now
5666  # covers the following areas:
5667  #
5668  #   A) Merging a set of revisions into a path, then reverse merging the
5669  #      same set out of a subtree of path results in empty mergeinfo
5670  #      (i.e. "") on the subtree.
5671  #
5672  #   B) Empty mergeinfo elides to empty mergeinfo.
5673  #
5674  #   C) If a merge sets empty mergeinfo on its target and that target has
5675  #      no ancestor in either the WC or the repository with explicit
5676  #      mergeinfo, then the target's mergeinfo is removed (a.k.a. elides
5677  #      to nothing).
5678  sbox.build()
5679  wc_dir = sbox.wc_dir
5680  wc_disk, wc_status = set_up_branch(sbox)
5681
5682  # Some paths we'll care about
5683  A_COPY_path = sbox.ospath('A_COPY')
5684  H_COPY_path = sbox.ospath('A_COPY/D/H')
5685  psi_COPY_path = sbox.ospath('A_COPY/D/H/psi')
5686  rho_COPY_path = sbox.ospath('A_COPY/D/G/rho')
5687
5688  # Test area A -- Merge r2:4 into A_COPY then reverse merge 4:2 to
5689  # A_COPY/D/G.  A_COPY/D/G should end up with empty mergeinfo to
5690  # override that of A_COPY.
5691  expected_output = wc.State(A_COPY_path, {
5692    'D/H/psi'   : Item(status='U '),
5693    'D/G/rho'   : Item(status='U '),
5694    })
5695  expected_mergeinfo_output = wc.State(A_COPY_path, {
5696    '' : Item(status=' U'),
5697    })
5698  expected_elision_output = wc.State(A_COPY_path, {
5699    })
5700  expected_status = wc.State(A_COPY_path, {
5701    ''          : Item(status=' M', wc_rev=2),
5702    'B'         : Item(status='  ', wc_rev=2),
5703    'mu'        : Item(status='  ', wc_rev=2),
5704    'B/E'       : Item(status='  ', wc_rev=2),
5705    'B/E/alpha' : Item(status='  ', wc_rev=2),
5706    'B/E/beta'  : Item(status='  ', wc_rev=2),
5707    'B/lambda'  : Item(status='  ', wc_rev=2),
5708    'B/F'       : Item(status='  ', wc_rev=2),
5709    'C'         : Item(status='  ', wc_rev=2),
5710    'D'         : Item(status='  ', wc_rev=2),
5711    'D/G'       : Item(status='  ', wc_rev=2),
5712    'D/G/pi'    : Item(status='  ', wc_rev=2),
5713    'D/G/rho'   : Item(status='M ', wc_rev=2),
5714    'D/G/tau'   : Item(status='  ', wc_rev=2),
5715    'D/gamma'   : Item(status='  ', wc_rev=2),
5716    'D/H'       : Item(status='  ', wc_rev=2),
5717    'D/H/chi'   : Item(status='  ', wc_rev=2),
5718    'D/H/psi'   : Item(status='M ', wc_rev=2),
5719    'D/H/omega' : Item(status='  ', wc_rev=2),
5720    })
5721  expected_disk = wc.State('', {
5722    ''          : Item(props={SVN_PROP_MERGEINFO : '/A:3-4'}),
5723    'B'         : Item(),
5724    'mu'        : Item("This is the file 'mu'.\n"),
5725    'B/E'       : Item(),
5726    'B/E/alpha' : Item("This is the file 'alpha'.\n"),
5727    'B/E/beta'  : Item("This is the file 'beta'.\n"),
5728    'B/lambda'  : Item("This is the file 'lambda'.\n"),
5729    'B/F'       : Item(),
5730    'C'         : Item(),
5731    'D'         : Item(),
5732    'D/G'       : Item(),
5733    'D/G/pi'    : Item("This is the file 'pi'.\n"),
5734    'D/G/rho'   : Item("New content"),
5735    'D/G/tau'   : Item("This is the file 'tau'.\n"),
5736    'D/gamma'   : Item("This is the file 'gamma'.\n"),
5737    'D/H'       : Item(),
5738    'D/H/chi'   : Item("This is the file 'chi'.\n"),
5739    'D/H/psi'   : Item("New content"),
5740    'D/H/omega' : Item("This is the file 'omega'.\n"),
5741    })
5742  expected_skip = wc.State(A_COPY_path, { })
5743  svntest.actions.run_and_verify_merge(A_COPY_path, '2', '4',
5744                                       sbox.repo_url + '/A', None,
5745                                       expected_output,
5746                                       expected_mergeinfo_output,
5747                                       expected_elision_output,
5748                                       expected_disk,
5749                                       expected_status,
5750                                       expected_skip,
5751                                       check_props=True)
5752  # Now do the reverse merge into the subtree.
5753  expected_output = wc.State(H_COPY_path, {
5754    'psi' : Item(status='G '),
5755    })
5756  expected_mergeinfo_output = wc.State(H_COPY_path, {
5757    '' : Item(status=' G'),
5758    })
5759  expected_elision_output = wc.State(H_COPY_path, {
5760    })
5761  expected_status = wc.State(H_COPY_path, {
5762    ''      : Item(status=' M', wc_rev=2),
5763    'chi'   : Item(status='  ', wc_rev=2),
5764    'psi'   : Item(status='  ', wc_rev=2),
5765    'omega' : Item(status='  ', wc_rev=2),
5766    })
5767  expected_disk = wc.State('', {
5768    ''      : Item(props={SVN_PROP_MERGEINFO : ''}),
5769    'chi'   : Item("This is the file 'chi'.\n"),
5770    'psi'   : Item("This is the file 'psi'.\n"),
5771    'omega' : Item("This is the file 'omega'.\n"),
5772    })
5773  expected_skip = wc.State(H_COPY_path, { })
5774  svntest.actions.run_and_verify_merge(H_COPY_path, '4', '2',
5775                                       sbox.repo_url + '/A/D/H', None,
5776                                       expected_output,
5777                                       expected_mergeinfo_output,
5778                                       expected_elision_output,
5779                                       expected_disk,
5780                                       expected_status,
5781                                       expected_skip,
5782                                       check_props=True)
5783
5784  # Test areas B and C -- Reverse merge r3 into A_COPY, this would result in
5785  # empty mergeinfo on A_COPY and A_COPY/D/H, but the empty mergeinfo on the
5786  # latter elides to the former.  And then the empty mergeinfo on A_COPY,
5787  # which has no parent with explicit mergeinfo to override (in either the WC
5788  # or the repos) itself elides.  This leaves the WC in the same unmodified
5789  # state as after the call to set_up_branch().
5790  expected_output = expected_merge_output(
5791    [[4,3]], ['G    ' + rho_COPY_path + '\n',
5792              ' G   ' + A_COPY_path   + '\n',
5793              ' U   ' + H_COPY_path   + '\n',
5794              ' U   ' + A_COPY_path   + '\n',],
5795    elides=True)
5796  svntest.actions.run_and_verify_svn(expected_output,
5797                                     [], 'merge', '-r4:2',
5798                                     sbox.repo_url + '/A',
5799                                     A_COPY_path)
5800  svntest.actions.run_and_verify_status(wc_dir, wc_status)
5801  # Check that A_COPY's mergeinfo is gone.
5802  svntest.actions.run_and_verify_svn([], '.*W200017: Property.*not found',
5803                                     'pg', 'svn:mergeinfo',
5804                                     A_COPY_path)
5805
5806#----------------------------------------------------------------------
5807@SkipUnless(server_has_mergeinfo)
5808@Issue(2781)
5809def prop_add_to_child_with_mergeinfo(sbox):
5810  "merge adding prop to child of merge target works"
5811
5812  # Test for Issue #2781 Prop add to child of merge target corrupts WC if
5813  # child has mergeinfo.
5814
5815  sbox.build()
5816  wc_dir = sbox.wc_dir
5817  expected_disk, expected_status = set_up_branch(sbox)
5818
5819  # Some paths we'll care about
5820  beta_path = sbox.ospath('A/B/E/beta')
5821  beta_COPY_path = sbox.ospath('A_COPY/B/E/beta')
5822  B_COPY_path = sbox.ospath('A_COPY/B')
5823
5824  # Set a non-mergeinfo prop on a file.
5825  svntest.actions.run_and_verify_svn(["property 'prop:name' set on '" +
5826                                      beta_path + "'\n"], [], 'ps',
5827                                     'prop:name', 'propval', beta_path)
5828  expected_disk.tweak('A/B/E/beta', props={'prop:name' : 'propval'})
5829  expected_status.tweak('A/B/E/beta', wc_rev=7)
5830  expected_output = wc.State(wc_dir,
5831                             {'A/B/E/beta' : Item(verb='Sending')})
5832  svntest.actions.run_and_verify_commit(wc_dir,
5833                                        expected_output,
5834                                        expected_status)
5835
5836  # Merge r4:5 from A/B/E/beta into A_COPY/B/E/beta.
5837  svntest.actions.run_and_verify_svn(
5838    expected_merge_output([[5]],
5839                          ['U    ' + beta_COPY_path +'\n',
5840                           ' U   ' + beta_COPY_path +'\n',]),
5841    [], 'merge', '-c5',
5842    sbox.repo_url + '/A/B/E/beta',
5843    beta_COPY_path)
5844
5845  # Merge r6:7 into A_COPY/B.  In issue #2781 this adds a bogus
5846  # and incomplete entry in A_COPY/B/.svn/entries for 'beta'.
5847  expected_output = wc.State(B_COPY_path, {
5848    'E/beta' : Item(status=' U'),
5849    })
5850  expected_mergeinfo_output = wc.State(B_COPY_path, {
5851    ''       : Item(status=' U'),
5852    'E/beta' : Item(status=' G'),
5853    })
5854  expected_elision_output = wc.State(B_COPY_path, {
5855    })
5856  expected_status = wc.State(B_COPY_path, {
5857    ''        : Item(status=' M', wc_rev=2),
5858    'E'       : Item(status='  ', wc_rev=2),
5859    'E/alpha' : Item(status='  ', wc_rev=2),
5860    'E/beta'  : Item(status='MM', wc_rev=2),
5861    'lambda'  : Item(status='  ', wc_rev=2),
5862    'F'       : Item(status='  ', wc_rev=2),
5863    })
5864  expected_disk = wc.State('', {
5865    ''        : Item(props={SVN_PROP_MERGEINFO : '/A/B:7'}),
5866    'E'       : Item(),
5867    'E/alpha' : Item("This is the file 'alpha'.\n"),
5868    'E/beta'  : Item(contents="New content",
5869                     props={SVN_PROP_MERGEINFO : '/A/B/E/beta:5,7',
5870                            'prop:name' : 'propval'}),
5871    'F'       : Item(),
5872    'lambda'  : Item("This is the file 'lambda'.\n")
5873    })
5874  expected_skip = wc.State(B_COPY_path, { })
5875  svntest.actions.run_and_verify_merge(B_COPY_path, '6', '7',
5876                                       sbox.repo_url + '/A/B', None,
5877                                       expected_output,
5878                                       expected_mergeinfo_output,
5879                                       expected_elision_output,
5880                                       expected_disk,
5881                                       expected_status,
5882                                       expected_skip,
5883                                       check_props=True)
5884
5885#----------------------------------------------------------------------
5886@Issue(2788,3383)
5887def foreign_repos_does_not_update_mergeinfo(sbox):
5888  "set no mergeinfo when merging from foreign repos"
5889
5890  # Test for issue #2788 and issue #3383.
5891
5892  sbox.build()
5893  wc_dir = sbox.wc_dir
5894  expected_disk, expected_status = set_up_branch(sbox)
5895
5896  # Set up for test of issue #2788.
5897
5898  # Create a second repository with the same greek tree
5899  repo_dir = sbox.repo_dir
5900  other_repo_dir, other_repo_url = sbox.add_repo_path("other")
5901  other_wc_dir = sbox.add_wc_path("other")
5902  svntest.main.copy_repos(repo_dir, other_repo_dir, 6, 1)
5903
5904  # Merge r3:4 (using implied peg revisions) from 'other' repos into
5905  # A_COPY/D/G.  Merge should succeed, but no mergeinfo should be set.
5906  G_COPY_path = sbox.ospath('A_COPY/D/G')
5907  svntest.actions.run_and_verify_svn(expected_merge_output([[4]],
5908                                      'U    ' +
5909                                      os.path.join(G_COPY_path,
5910                                                   "rho") + '\n', True),
5911                                     [], 'merge', '-c4',
5912                                     other_repo_url + '/A/D/G',
5913                                     G_COPY_path)
5914
5915  # Merge r4:5 (using explicit peg revisions) from 'other' repos into
5916  # A_COPY/B/E.  Merge should succeed, but no mergeinfo should be set.
5917  E_COPY_path = sbox.ospath('A_COPY/B/E')
5918  svntest.actions.run_and_verify_svn(expected_merge_output([[5]],
5919                                      'U    ' +
5920                                      os.path.join(E_COPY_path,
5921                                                   "beta") +'\n', True),
5922                                     [], 'merge',
5923                                     other_repo_url + '/A/B/E@4',
5924                                     other_repo_url + '/A/B/E@5',
5925                                     E_COPY_path)
5926
5927  expected_status.tweak('A_COPY/D/G/rho', 'A_COPY/B/E/beta', status='M ')
5928  svntest.actions.run_and_verify_status(wc_dir, expected_status)
5929
5930  # Set up for test of issue #3383.
5931  svntest.actions.run_and_verify_svn(None, [], 'revert', '-R', wc_dir)
5932
5933  # Get a working copy for the foreign repos.
5934  svntest.actions.run_and_verify_svn(None, [], 'co', other_repo_url,
5935                                     other_wc_dir)
5936
5937  # Create mergeinfo on the foreign repos on an existing directory and
5938  # file and an added directory and file.  Commit as r7.  And no, we aren't
5939  # checking these intermediate steps very thoroughly, but we test these
5940  # simple merges to *death* elsewhere.
5941
5942  # Create mergeinfo on an existing directory.
5943  svntest.actions.run_and_verify_svn(None, [], 'merge',
5944                                     other_repo_url + '/A',
5945                                     os.path.join(other_wc_dir, 'A_COPY'),
5946                                     '-c5')
5947
5948  # Create mergeinfo on an existing file.
5949  svntest.actions.run_and_verify_svn(None, [], 'merge',
5950                                     other_repo_url + '/A/D/H/psi',
5951                                     os.path.join(other_wc_dir, 'A_COPY',
5952                                                  'D', 'H', 'psi'),
5953                                     '-c3')
5954
5955  # Add a new directory with mergeinfo in the foreign repos.
5956  new_dir = os.path.join(other_wc_dir, 'A_COPY', 'N')
5957  svntest.actions.run_and_verify_svn(None, [], 'mkdir', new_dir)
5958  svntest.actions.run_and_verify_svn(None, [], 'ps',
5959                                     SVN_PROP_MERGEINFO, '', new_dir)
5960
5961  # Add a new file with mergeinfo in the foreign repos.
5962  new_file = os.path.join(other_wc_dir, 'A_COPY', 'nu')
5963  svntest.main.file_write(new_file, "This is the file 'nu'.\n")
5964  svntest.actions.run_and_verify_svn(None, [], 'add', new_file)
5965  svntest.actions.run_and_verify_svn(None, [], 'ps',
5966                                     SVN_PROP_MERGEINFO, '', new_file)
5967
5968  expected_output = wc.State(other_wc_dir,{
5969    'A_COPY'          : Item(verb='Sending'), # Mergeinfo created
5970    'A_COPY/B/E/beta' : Item(verb='Sending'),
5971    'A_COPY/D/H/psi'  : Item(verb='Sending'), # Mergeinfo created
5972    'A_COPY/N'        : Item(verb='Adding'),  # Has empty mergeinfo
5973    'A_COPY/nu'       : Item(verb='Adding'),  # Has empty mergeinfo
5974    })
5975  svntest.actions.run_and_verify_commit(other_wc_dir, expected_output,
5976                                        None, [], other_wc_dir,
5977                                        '-m',
5978                                        'create mergeinfo on foreign repos')
5979  # Now merge a diff from the foreign repos that contains the mergeinfo
5980  # addition in r7 to A_COPY.  The mergeinfo diff should *not* be applied
5981  # to A_COPY since it refers to a foreign repository...
5982  svntest.actions.run_and_verify_svn(None, [], 'merge',
5983                                     other_repo_url + '/A@1',
5984                                     other_repo_url + '/A_COPY@7',
5985                                     sbox.ospath('A_COPY'))
5986  #...which means there should be no mergeinfo anywhere in WC_DIR, since
5987  # this test never created any.
5988  svntest.actions.run_and_verify_svn([], [], 'pg',
5989                                     SVN_PROP_MERGEINFO, '-vR',
5990                                     wc_dir)
5991
5992#----------------------------------------------------------------------
5993# This test involves tree conflicts.
5994@XFail()
5995@Issue(2897)
5996def avoid_reflected_revs(sbox):
5997  "avoid repeated merges for cyclic merging"
5998
5999  # See <https://issues.apache.org/jira/browse/SVN-2897>.
6000  #
6001  # This test cherry-picks some changes (all of them, in fact) from the
6002  # parent branch 'A' to the child branch 'A_COPY', and then tries to
6003  # reintegrate 'A_COPY' to 'A' (explicitly specifying a revision range
6004  # on the source branch).  It expects the changes that are unique to the
6005  # branch 'A_COPY' to be merged to 'A'.
6006  #
6007  #   A     --1----[3]---[5]----------?
6008  #            \     \_____\___      /
6009  #             \           \  \    /
6010  #   A_COPY     2-[---4-----6--7--8]-
6011
6012  # Create a WC with a single branch
6013  sbox.build()
6014  wc_dir = sbox.wc_dir
6015  wc_disk, wc_status = set_up_branch(sbox, True, 1)
6016
6017  # Some paths we'll care about
6018  A_path = sbox.ospath('A')
6019  A_COPY_path = sbox.ospath('A_COPY')
6020  tfile1_path = sbox.ospath('A/tfile1')
6021  tfile2_path = sbox.ospath('A/tfile2')
6022  bfile1_path = os.path.join(A_COPY_path, 'bfile1')
6023  bfile2_path = os.path.join(A_COPY_path, 'bfile2')
6024
6025  # Contents to be added to files
6026  tfile1_content = "This is tfile1\n"
6027  tfile2_content = "This is tfile2\n"
6028  bfile1_content = "This is bfile1\n"
6029  bfile2_content = "This is bfile2\n"
6030
6031  # We'll consider A as the trunk and A_COPY as the feature branch
6032  # r3 - Create a tfile1 in A
6033  svntest.main.file_write(tfile1_path, tfile1_content)
6034  svntest.actions.run_and_verify_svn(None, [], 'add', tfile1_path)
6035  expected_output = wc.State(wc_dir, {'A/tfile1' : Item(verb='Adding')})
6036  wc_status.add({'A/tfile1'     : Item(status='  ', wc_rev=3)})
6037  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
6038                                        wc_status)
6039
6040  # r4 - Create a bfile1 in A_COPY
6041  svntest.main.file_write(bfile1_path, bfile1_content)
6042  svntest.actions.run_and_verify_svn(None, [], 'add', bfile1_path)
6043  expected_output = wc.State(wc_dir, {'A_COPY/bfile1' : Item(verb='Adding')})
6044  wc_status.add({'A_COPY/bfile1'     : Item(status='  ', wc_rev=4)})
6045  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
6046                                        wc_status)
6047
6048  # r5 - Create one more file in A
6049  svntest.main.file_write(tfile2_path, tfile2_content)
6050  svntest.actions.run_and_verify_svn(None, [], 'add', tfile2_path)
6051  expected_output = wc.State(wc_dir, {'A/tfile2' : Item(verb='Adding')})
6052  wc_status.add({'A/tfile2'     : Item(status='  ', wc_rev=5)})
6053  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
6054                                        wc_status)
6055
6056  # Merge r5 from /A to /A_COPY, creating r6
6057  expected_output = wc.State(A_COPY_path, {
6058    'tfile2'   : Item(status='A '),
6059    })
6060  expected_mergeinfo_output = wc.State(A_COPY_path, {
6061    '' : Item(status=' U'),
6062    })
6063  expected_elision_output = wc.State(A_COPY_path, {
6064    })
6065  expected_status = wc.State(A_COPY_path, {
6066    ''         : Item(status=' M', wc_rev=2),
6067    'tfile2'   : Item(status='A ', wc_rev='-', copied='+'),
6068    'bfile1'   : Item(status='  ', wc_rev=4),
6069    'mu'       : Item(status='  ', wc_rev=2),
6070    'C'        : Item(status='  ', wc_rev=2),
6071    'D'        : Item(status='  ', wc_rev=2),
6072    'B'        : Item(status='  ', wc_rev=2),
6073    'B/lambda' : Item(status='  ', wc_rev=2),
6074    'B/E'      : Item(status='  ', wc_rev=2),
6075    'B/E/alpha': Item(status='  ', wc_rev=2),
6076    'B/E/beta' : Item(status='  ', wc_rev=2),
6077    'B/F'      : Item(status='  ', wc_rev=2),
6078    'D/gamma'  : Item(status='  ', wc_rev=2),
6079    'D/G'      : Item(status='  ', wc_rev=2),
6080    'D/G/pi'   : Item(status='  ', wc_rev=2),
6081    'D/G/rho'  : Item(status='  ', wc_rev=2),
6082    'D/G/tau'  : Item(status='  ', wc_rev=2),
6083    'D/H'      : Item(status='  ', wc_rev=2),
6084    'D/H/chi'  : Item(status='  ', wc_rev=2),
6085    'D/H/omega': Item(status='  ', wc_rev=2),
6086    'D/H/psi'  : Item(status='  ', wc_rev=2),
6087    })
6088  expected_disk = wc.State('', {
6089    ''         : Item(props={SVN_PROP_MERGEINFO : '/A:5'}),
6090    'tfile2'   : Item(tfile2_content),
6091    'bfile1'   : Item(bfile1_content),
6092    'mu'       : Item("This is the file 'mu'.\n"),
6093    'C'        : Item(),
6094    'D'        : Item(),
6095    'B'        : Item(),
6096    'B/lambda' : Item("This is the file 'lambda'.\n"),
6097    'B/E'      : Item(),
6098    'B/E/alpha': Item("This is the file 'alpha'.\n"),
6099    'B/E/beta' : Item("This is the file 'beta'.\n"),
6100    'B/F'      : Item(),
6101    'D/gamma'  : Item("This is the file 'gamma'.\n"),
6102    'D/G'      : Item(),
6103    'D/G/pi'   : Item("This is the file 'pi'.\n"),
6104    'D/G/rho'  : Item("This is the file 'rho'.\n"),
6105    'D/G/tau'  : Item("This is the file 'tau'.\n"),
6106    'D/H'      : Item(),
6107    'D/H/chi'  : Item("This is the file 'chi'.\n"),
6108    'D/H/omega': Item("This is the file 'omega'.\n"),
6109    'D/H/psi'  : Item("This is the file 'psi'.\n"),
6110    })
6111  expected_skip = wc.State(A_COPY_path, {})
6112
6113  svntest.actions.run_and_verify_merge(A_COPY_path, '4', '5',
6114                                       sbox.repo_url + '/A', None,
6115                                       expected_output,
6116                                       expected_mergeinfo_output,
6117                                       expected_elision_output,
6118                                       expected_disk,
6119                                       expected_status,
6120                                       expected_skip,
6121                                       [], True, False,
6122                                       A_COPY_path,
6123                                       '--allow-mixed-revisions')
6124
6125  svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir)
6126  expected_output = wc.State(wc_dir, {
6127    'A_COPY'        : Item(verb='Sending'),
6128    'A_COPY/tfile2' : Item(verb='Adding'),
6129    })
6130  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
6131                                        None)
6132
6133  # Merge r3 from /A to /A_COPY, creating r7
6134  expected_output = wc.State(A_COPY_path, {
6135    'tfile1'   : Item(status='A '),
6136    })
6137  expected_mergeinfo_output = wc.State(A_COPY_path, {
6138    '' : Item(status=' U'),
6139    })
6140  expected_elision_output = wc.State(A_COPY_path, {
6141    })
6142  expected_status.tweak(wc_rev=5)
6143  expected_status.tweak('', wc_rev=6)
6144  expected_status.tweak('tfile2', status='  ', copied=None, wc_rev=6)
6145  expected_status.add({
6146   'tfile1'    : Item(status='A ', wc_rev='-', copied='+'),
6147    })
6148  expected_disk.tweak('', props={SVN_PROP_MERGEINFO : '/A:3,5'})
6149  expected_disk.add({
6150    'tfile1'   : Item(tfile1_content),
6151    })
6152
6153  svntest.actions.run_and_verify_merge(A_COPY_path, '2', '3',
6154                                       sbox.repo_url + '/A', None,
6155                                       expected_output,
6156                                       expected_mergeinfo_output,
6157                                       expected_elision_output,
6158                                       expected_disk,
6159                                       expected_status,
6160                                       expected_skip,
6161                                       [], True, False,
6162                                       A_COPY_path,
6163                                       '--allow-mixed-revisions')
6164
6165  svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir)
6166  expected_output = wc.State(wc_dir, {
6167    'A_COPY'        : Item(verb='Sending'),
6168    'A_COPY/tfile1' : Item(verb='Adding'),
6169    })
6170  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
6171                                        None)
6172
6173  # r8 - Add bfile2 to A_COPY
6174  svntest.main.file_write(bfile2_path, bfile2_content)
6175  svntest.actions.run_and_verify_svn(None, [], 'add', bfile2_path)
6176  expected_output = wc.State(wc_dir, {'A_COPY/bfile2' : Item(verb='Adding')})
6177  wc_status.tweak(wc_rev=6)
6178  wc_status.add({
6179    'A_COPY/bfile2' : Item(status='  ', wc_rev=8),
6180    'A_COPY'        : Item(status='  ', wc_rev=7),
6181    'A_COPY/tfile2' : Item(status='  ', wc_rev=6),
6182    'A_COPY/tfile1' : Item(status='  ', wc_rev=7),
6183    })
6184  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
6185                                        wc_status)
6186
6187  # Merge 2:8 from A_COPY(feature branch) to A(trunk).
6188  expected_output = wc.State(A_path, {
6189    'bfile2' : Item(status='A '),
6190    'bfile1' : Item(status='A '),
6191    })
6192  expected_mergeinfo_output = wc.State(A_path, {
6193    '' : Item(status=' U'),
6194    })
6195  expected_elision_output = wc.State(A_path, {
6196    })
6197  expected_status = wc.State(A_path, {
6198    ''          : Item(status=' M', wc_rev=6),
6199    'bfile2'    : Item(status='A ', wc_rev='-', copied='+'),
6200    'bfile1'    : Item(status='A ', wc_rev='-', copied='+'),
6201    'tfile2'    : Item(status='  ', wc_rev=6),
6202    'tfile1'    : Item(status='  ', wc_rev=6),
6203    'mu'        : Item(status='  ', wc_rev=6),
6204    'C'         : Item(status='  ', wc_rev=6),
6205    'D'         : Item(status='  ', wc_rev=6),
6206    'B'         : Item(status='  ', wc_rev=6),
6207    'B/lambda'  : Item(status='  ', wc_rev=6),
6208    'B/E'       : Item(status='  ', wc_rev=6),
6209    'B/E/alpha' : Item(status='  ', wc_rev=6),
6210    'B/E/beta'  : Item(status='  ', wc_rev=6),
6211    'B/F'       : Item(status='  ', wc_rev=6),
6212    'D/gamma'   : Item(status='  ', wc_rev=6),
6213    'D/G'       : Item(status='  ', wc_rev=6),
6214    'D/G/pi'    : Item(status='  ', wc_rev=6),
6215    'D/G/rho'   : Item(status='  ', wc_rev=6),
6216    'D/G/tau'   : Item(status='  ', wc_rev=6),
6217    'D/H'       : Item(status='  ', wc_rev=6),
6218    'D/H/chi'   : Item(status='  ', wc_rev=6),
6219    'D/H/omega' : Item(status='  ', wc_rev=6),
6220    'D/H/psi'   : Item(status='  ', wc_rev=6),
6221    })
6222  expected_disk = wc.State('', {
6223    ''          : Item(props={SVN_PROP_MERGEINFO : '/A_COPY:3-8'}),
6224    'bfile2'    : Item(bfile2_content),
6225    'bfile1'    : Item(bfile1_content),
6226    'tfile2'    : Item(tfile2_content),
6227    'tfile1'    : Item(tfile1_content),
6228    'mu'        : Item("This is the file 'mu'.\n"),
6229    'C'         : Item(),
6230    'D'         : Item(),
6231    'B'         : Item(),
6232    'B/lambda'  : Item("This is the file 'lambda'.\n"),
6233    'B/E'       : Item(),
6234    'B/E/alpha' : Item("This is the file 'alpha'.\n"),
6235    'B/E/beta'  : Item("This is the file 'beta'.\n"),
6236    'B/F'       : Item(),
6237    'D/gamma'   : Item("This is the file 'gamma'.\n"),
6238    'D/G'       : Item(),
6239    'D/G/pi'    : Item("This is the file 'pi'.\n"),
6240    'D/G/rho'   : Item("This is the file 'rho'.\n"),
6241    'D/G/tau'   : Item("This is the file 'tau'.\n"),
6242    'D/H'       : Item(),
6243    'D/H/chi'   : Item("This is the file 'chi'.\n"),
6244    'D/H/omega' : Item("This is the file 'omega'.\n"),
6245    'D/H/psi'   : Item("This is the file 'psi'.\n"),
6246    })
6247
6248  expected_skip = wc.State(A_path, {})
6249
6250  svntest.actions.run_and_verify_merge(A_path, '2', '8',
6251                                       sbox.repo_url + '/A_COPY', None,
6252                                       expected_output,
6253                                       expected_mergeinfo_output,
6254                                       expected_elision_output,
6255                                       expected_disk,
6256                                       expected_status,
6257                                       expected_skip,
6258                                       check_props=True)
6259
6260#----------------------------------------------------------------------
6261@SkipUnless(server_has_mergeinfo)
6262def update_loses_mergeinfo(sbox):
6263  "update does not merge mergeinfo"
6264
6265  """
6266  When a working copy path receives a fresh svn:mergeinfo property due to
6267  an update, and the path has local mergeinfo changes, then the local
6268  mergeinfo should be merged with the incoming mergeinfo.
6269  """
6270
6271  sbox.build()
6272  wc_dir = sbox.wc_dir
6273  A_C_wc_dir = sbox.ospath('A/C')
6274  A_B_url = sbox.repo_url + '/A/B'
6275  A_B_J_url = sbox.repo_url + '/A/B/J'
6276  A_B_K_url = sbox.repo_url + '/A/B/K'
6277  svntest.actions.run_and_verify_svn(['Committing transaction...\n',
6278                                            'Committed revision 2.\n'],
6279                                     [],
6280                                     'mkdir', '-m', 'rev 2', A_B_J_url)
6281  svntest.actions.run_and_verify_svn(['Committing transaction...\n',
6282                                            'Committed revision 3.\n'],
6283                                     [],
6284                                     'mkdir', '-m', 'rev 3', A_B_K_url)
6285
6286  other_wc = sbox.add_wc_path('other')
6287  svntest.actions.duplicate_dir(wc_dir, other_wc)
6288
6289  expected_output = wc.State(A_C_wc_dir, {'J' : Item(status='A ')})
6290  expected_mergeinfo_output = wc.State(A_C_wc_dir, {
6291    '' : Item(status=' U')
6292    })
6293  expected_elision_output = wc.State(A_C_wc_dir, {
6294    })
6295  expected_disk = wc.State('', {
6296    'J'       : Item(),
6297    ''        : Item(props={SVN_PROP_MERGEINFO : '/A/B:2'}),
6298    })
6299  expected_status = wc.State(A_C_wc_dir,
6300                             { ''    : Item(wc_rev=1, status=' M'),
6301                               'J'   : Item(status='A ',
6302                                            wc_rev='-', copied='+')
6303                             }
6304                            )
6305  expected_skip = wc.State('', { })
6306  svntest.actions.run_and_verify_merge(A_C_wc_dir, '1', '2',
6307                                       A_B_url, None,
6308                                       expected_output,
6309                                       expected_mergeinfo_output,
6310                                       expected_elision_output,
6311                                       expected_disk,
6312                                       expected_status,
6313                                       expected_skip,
6314                                       check_props=1)
6315  expected_output = wc.State(A_C_wc_dir, {
6316    ''  : Item(verb='Sending'),
6317    'J' : Item(verb='Adding')
6318    })
6319  expected_status = wc.State(A_C_wc_dir,
6320                             { ''    : Item(status='  ', wc_rev=4),
6321                               'J'   : Item(status='  ', wc_rev=4)
6322                             }
6323                            )
6324  svntest.actions.run_and_verify_commit(A_C_wc_dir,
6325                                        expected_output,
6326                                        expected_status)
6327
6328  other_A_C_wc_dir = os.path.join(other_wc, 'A', 'C')
6329  expected_output = wc.State(other_A_C_wc_dir, {'K' : Item(status='A ')})
6330  expected_mergeinfo_output = wc.State(other_A_C_wc_dir, {
6331    '' : Item(status=' U')
6332    })
6333  expected_elision_output = wc.State(other_A_C_wc_dir, {
6334    })
6335  expected_disk = wc.State('', {
6336    'K'       : Item(),
6337    ''        : Item(props={SVN_PROP_MERGEINFO : '/A/B:3'}),
6338    })
6339  expected_status = wc.State(other_A_C_wc_dir,
6340                             { ''    : Item(wc_rev=1, status=' M'),
6341                               'K'   : Item(status='A ',
6342                                            wc_rev='-', copied='+')
6343                             }
6344                            )
6345  expected_skip = wc.State('', { })
6346  svntest.actions.run_and_verify_merge(other_A_C_wc_dir, '2', '3',
6347                                       A_B_url, None,
6348                                       expected_output,
6349                                       expected_mergeinfo_output,
6350                                       expected_elision_output,
6351                                       expected_disk,
6352                                       expected_status,
6353                                       expected_skip,
6354                                       check_props=1)
6355  expected_output = wc.State(other_A_C_wc_dir,
6356                             {'J' : Item(status='A '),
6357                              ''  : Item(status=' G')
6358                             }
6359                            )
6360  expected_disk = wc.State('', {
6361    ''        : Item(props={SVN_PROP_MERGEINFO : '/A/B:2-3'}),
6362    'J'       : Item(),
6363    'K'       : Item(),
6364    })
6365  expected_status = wc.State(other_A_C_wc_dir,
6366                             { ''    : Item(wc_rev=4, status=' M'),
6367                               'J'   : Item(status='  ', wc_rev='4'),
6368                               'K'   : Item(status='A ',
6369                                            wc_rev='-', copied='+')
6370                             }
6371                            )
6372  svntest.actions.run_and_verify_update(other_A_C_wc_dir,
6373                                        expected_output,
6374                                        expected_disk,
6375                                        expected_status,
6376                                        check_props=True)
6377
6378#----------------------------------------------------------------------
6379# Tests part of issue# 2829.
6380@Issue(2829)
6381@SkipUnless(server_has_mergeinfo)
6382def merge_loses_mergeinfo(sbox):
6383  "merge should merge mergeinfo"
6384
6385  """
6386  When a working copy has no mergeinfo(due to local full revert of all merges),
6387  and merge is attempted for someother revision rX, The new mergeinfo should be
6388  /merge/src: rX not all the reverted ones reappearing along with rX.
6389  """
6390
6391  sbox.build()
6392  wc_dir = sbox.wc_dir
6393  A_C_wc_dir = sbox.ospath('A/C')
6394  A_B_url = sbox.repo_url + '/A/B'
6395  A_B_J_url = sbox.repo_url + '/A/B/J'
6396  A_B_K_url = sbox.repo_url + '/A/B/K'
6397  svntest.actions.run_and_verify_svn(['Committing transaction...\n',
6398                                            'Committed revision 2.\n'],
6399                                     [],
6400                                     'mkdir', '-m', 'rev 2', A_B_J_url)
6401  svntest.actions.run_and_verify_svn(['Committing transaction...\n',
6402                                            'Committed revision 3.\n'],
6403                                     [],
6404                                     'mkdir', '-m', 'rev 3', A_B_K_url)
6405
6406  expected_output = wc.State(A_C_wc_dir, {'J' : Item(status='A ')})
6407  expected_mergeinfo_output = wc.State(A_C_wc_dir, {
6408    '' : Item(status=' U'),
6409    })
6410  expected_elision_output = wc.State(A_C_wc_dir, {
6411    })
6412  expected_disk = wc.State('', {
6413    'J'       : Item(),
6414    ''        : Item(props={SVN_PROP_MERGEINFO : '/A/B:2'}),
6415    })
6416  expected_status = wc.State(A_C_wc_dir,
6417                             { ''    : Item(wc_rev=1, status=' M'),
6418                               'J'   : Item(status='A ',
6419                                            wc_rev='-', copied='+')
6420                             }
6421                            )
6422  expected_skip = wc.State('', { })
6423  svntest.actions.run_and_verify_merge(A_C_wc_dir, '1', '2',
6424                                       A_B_url, None,
6425                                       expected_output,
6426                                       expected_mergeinfo_output,
6427                                       expected_elision_output,
6428                                       expected_disk,
6429                                       expected_status,
6430                                       expected_skip,
6431                                       check_props=1)
6432  expected_output = wc.State(A_C_wc_dir, {
6433    ''  : Item(verb='Sending'),
6434    'J' : Item(verb='Adding')
6435    })
6436  expected_status = wc.State(A_C_wc_dir,
6437                             { ''    : Item(status='  ', wc_rev=4),
6438                               'J'   : Item(status='  ', wc_rev=4)
6439                             }
6440                            )
6441  svntest.actions.run_and_verify_commit(A_C_wc_dir,
6442                                        expected_output,
6443                                        expected_status)
6444  expected_output = wc.State(A_C_wc_dir, {'J' : Item(status='D ')})
6445  expected_elision_output = wc.State(A_C_wc_dir, {
6446    '' : Item(status=' U'),
6447    })
6448  expected_disk = wc.State('', {})
6449  expected_status = wc.State(A_C_wc_dir,
6450                             { ''    : Item(wc_rev=4, status=' M'),
6451                               'J'   : Item(wc_rev=4, status='D ')
6452                             }
6453                            )
6454  svntest.actions.run_and_verify_merge(A_C_wc_dir, '2', '1',
6455                                       A_B_url, None,
6456                                       expected_output,
6457                                       expected_mergeinfo_output,
6458                                       expected_elision_output,
6459                                       expected_disk,
6460                                       expected_status,
6461                                       expected_skip,
6462                                       check_props=1)
6463
6464  expected_output = wc.State(A_C_wc_dir, {'K' : Item(status='A ')})
6465  expected_disk = wc.State('', {
6466    'K'       : Item(),
6467    ''        : Item(props={SVN_PROP_MERGEINFO : '/A/B:3'}),
6468    })
6469  expected_status = wc.State(A_C_wc_dir,
6470                             { ''    : Item(wc_rev=4, status=' M'),
6471                               'K'   : Item(status='A ',
6472                                            wc_rev='-', copied='+'),
6473                               'J'   : Item(wc_rev=4, status='D ')
6474                             }
6475                            )
6476  expected_mergeinfo_output = wc.State(A_C_wc_dir, {
6477    '' : Item(status=' G'),
6478    })
6479  expected_elision_output = wc.State(A_C_wc_dir, {
6480    })
6481  svntest.actions.run_and_verify_merge(A_C_wc_dir, '2', '3',
6482                                       A_B_url, None,
6483                                       expected_output,
6484                                       expected_mergeinfo_output,
6485                                       expected_elision_output,
6486                                       expected_disk,
6487                                       expected_status,
6488                                       expected_skip,
6489                                       check_props=1)
6490
6491#----------------------------------------------------------------------
6492@Issue(2853)
6493def single_file_replace_style_merge_capability(sbox):
6494  "replace-style merge capability for a single file"
6495
6496  # Test for issue #2853, do_single_file_merge() lacks "Replace-style
6497  # merge" capability
6498
6499  sbox.build()
6500  wc_dir = sbox.wc_dir
6501  iota_path = sbox.ospath('iota')
6502  mu_path = sbox.ospath('A/mu')
6503
6504  # delete mu and replace it with a copy of iota
6505  svntest.main.run_svn(None, 'rm', mu_path)
6506  svntest.main.run_svn(None, 'mv', iota_path, mu_path)
6507
6508  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
6509  expected_status.tweak('A/mu', status='  ', wc_rev=2)
6510  expected_status.remove('iota')
6511  expected_output = svntest.wc.State(wc_dir, {
6512    'iota': Item(verb='Deleting'),
6513    'A/mu': Item(verb='Replacing'),
6514    })
6515  svntest.actions.run_and_verify_commit(wc_dir,
6516                                        expected_output,
6517                                        expected_status)
6518
6519  # Merge the file mu alone to rev1
6520  svntest.actions.run_and_verify_svn(expected_merge_output(None,
6521                                       ['R    ' + mu_path + '\n']),
6522                                     [],
6523                                     'merge',
6524                                     mu_path + '@2',
6525                                     mu_path + '@1',
6526                                     mu_path)
6527
6528#----------------------------------------------------------------------
6529# Test for issue 2786 fix.
6530@Issue(2786)
6531@SkipUnless(server_has_mergeinfo)
6532def merge_to_out_of_date_target(sbox):
6533  "merge to ood path can lead to inaccurate mergeinfo"
6534
6535  # Create a WC with a branch.
6536  sbox.build()
6537  wc_dir = sbox.wc_dir
6538  wc_disk, wc_status = set_up_branch(sbox, False, 1)
6539
6540  # Make second working copy
6541  other_wc = sbox.add_wc_path('other')
6542  svntest.actions.duplicate_dir(wc_dir, other_wc)
6543
6544  # Some paths we'll care about
6545  A_COPY_H_path = sbox.ospath('A_COPY/D/H')
6546  other_A_COPY_H_path = os.path.join(other_wc, "A_COPY", "D", "H")
6547
6548  # Merge -c3 into A_COPY/D/H of first WC.
6549  expected_output = wc.State(A_COPY_H_path, {
6550    'psi' : Item(status='U ')
6551    })
6552  expected_mergeinfo_output = wc.State(A_COPY_H_path, {
6553    '' : Item(status=' U'),
6554    })
6555  expected_elision_output = wc.State(A_COPY_H_path, {
6556    })
6557  expected_status = wc.State(A_COPY_H_path, {
6558    ''      : Item(status=' M', wc_rev=2),
6559    'psi'   : Item(status='M ', wc_rev=2),
6560    'omega' : Item(status='  ', wc_rev=2),
6561    'chi'   : Item(status='  ', wc_rev=2),
6562    })
6563  expected_disk = wc.State('', {
6564    ''      : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:3'}),
6565    'psi'   : Item("New content"),
6566    'omega' : Item("This is the file 'omega'.\n"),
6567    'chi'   : Item("This is the file 'chi'.\n"),
6568    })
6569  expected_skip = wc.State(A_COPY_H_path, { })
6570  svntest.actions.run_and_verify_merge(A_COPY_H_path, '2', '3',
6571                                       sbox.repo_url + '/A/D/H', None,
6572                                       expected_output,
6573                                       expected_mergeinfo_output,
6574                                       expected_elision_output,
6575                                       expected_disk,
6576                                       expected_status, expected_skip,
6577                                       check_props=True)
6578
6579  # Commit merge to first WC.
6580  wc_status.tweak('A_COPY/D/H/psi', 'A_COPY/D/H', wc_rev=7)
6581  expected_output = svntest.wc.State(wc_dir, {
6582    'A_COPY/D/H'    : Item(verb='Sending'),
6583    'A_COPY/D/H/psi': Item(verb='Sending'),
6584    })
6585  svntest.actions.run_and_verify_commit(wc_dir,
6586                                        expected_output,
6587                                        wc_status)
6588
6589  # Merge -c6 into A_COPY/D/H of other WC.
6590  expected_output = wc.State(other_A_COPY_H_path, {
6591    'omega' : Item(status='U ')
6592    })
6593  expected_mergeinfo_output = wc.State(other_A_COPY_H_path, {
6594    '' : Item(status=' U'),
6595    })
6596  expected_elision_output = wc.State(other_A_COPY_H_path, {
6597    })
6598  expected_status = wc.State(other_A_COPY_H_path, {
6599    ''      : Item(status=' M', wc_rev=2),
6600    'psi'   : Item(status='  ', wc_rev=2),
6601    'omega' : Item(status='M ', wc_rev=2),
6602    'chi'   : Item(status='  ', wc_rev=2),
6603    })
6604  expected_disk = wc.State('', {
6605    ''      : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:6'}),
6606    'psi'   : Item("This is the file 'psi'.\n"),
6607    'omega' : Item("New content"),
6608    'chi'   : Item("This is the file 'chi'.\n"),
6609    })
6610  expected_skip = wc.State(other_A_COPY_H_path, { })
6611  svntest.actions.run_and_verify_merge(other_A_COPY_H_path, '5', '6',
6612                                       sbox.repo_url + '/A/D/H', None,
6613                                       expected_output,
6614                                       expected_mergeinfo_output,
6615                                       expected_elision_output,
6616                                       expected_disk,
6617                                       expected_status, expected_skip,
6618                                       check_props=1)
6619
6620  # Update A_COPY/D/H in other WC.  Local mergeinfo for r6 on A_COPY/D/H
6621  # should be *merged* with r3 from first WC.
6622  expected_output = svntest.wc.State(other_A_COPY_H_path, {
6623    ''     : Item(status=' G'),
6624    'psi' : Item(status='U ')
6625    })
6626  other_disk = wc.State('', {
6627    ''      : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:3,6'}),
6628    'psi'   : Item(contents="New content"),
6629    'chi'   : Item("This is the file 'chi'.\n"),
6630    'omega' : Item(contents="New content"),
6631    })
6632  other_status = wc.State(other_A_COPY_H_path,{
6633    ''      : Item(wc_rev=7, status=' M'),
6634    'chi'   : Item(wc_rev=7, status='  '),
6635    'psi'   : Item(wc_rev=7, status='  '),
6636    'omega' : Item(wc_rev=7, status='M ')
6637    })
6638  svntest.actions.run_and_verify_update(other_A_COPY_H_path,
6639                                        expected_output,
6640                                        other_disk,
6641                                        other_status,
6642                                        check_props=True)
6643
6644#----------------------------------------------------------------------
6645@SkipUnless(server_has_mergeinfo)
6646def merge_with_depth_files(sbox):
6647  "merge test for --depth files"
6648
6649  sbox.build()
6650  wc_dir = sbox.wc_dir
6651
6652  # Some paths we'll care about
6653  mu_path = sbox.ospath('A/mu')
6654  gamma_path = sbox.ospath('A/D/gamma')
6655  Acopy_path = sbox.ospath('A_copy')
6656  Acopy_mu_path = sbox.ospath('A_copy/mu')
6657  A_url = sbox.repo_url + '/A'
6658  Acopy_url = sbox.repo_url + '/A_copy'
6659
6660  # Copy A_url to A_copy_url
6661  svntest.actions.run_and_verify_svn(None, [], 'cp',
6662                                     A_url, Acopy_url,
6663                                     '-m', 'create a new copy of A')
6664
6665  svntest.main.file_write(mu_path, "this is file 'mu' modified.\n")
6666  svntest.main.file_write(gamma_path, "this is file 'gamma' modified.\n")
6667
6668  # Create expected output tree for commit
6669  expected_output = wc.State(wc_dir, {
6670    'A/mu'        : Item(verb='Sending'),
6671    'A/D/gamma'   : Item(verb='Sending'),
6672    })
6673
6674  # Create expected status tree for commit
6675  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
6676  expected_status.add({
6677    'A/mu'          : Item(status='  ', wc_rev=3),
6678    'A/D/gamma'     : Item(status='  ', wc_rev=3),
6679    })
6680
6681  # Commit the modified contents
6682  svntest.actions.run_and_verify_commit(wc_dir,
6683                                        expected_output,
6684                                        expected_status)
6685
6686  # Update working copy
6687  svntest.actions.run_and_verify_svn(None, [],
6688                                     'up', Acopy_path)
6689
6690  # Merge r1:3 into A_copy with --depth files.  The merge only affects
6691  # 'A_copy' and its one file child 'mu', so 'A_copy' gets non-inheritable
6692  # mergeinfo for -r1:3 and 'mu' gets its own complete set of mergeinfo:
6693  # r1 from its parent, and r1:3 from the merge itself.
6694  expected_output = wc.State(Acopy_path, {
6695    'mu'   : Item(status='U '),
6696    })
6697  expected_mergeinfo_output = wc.State(Acopy_path, {
6698    ''   : Item(status=' U'),
6699    'mu' : Item(status=' U'),
6700    })
6701  expected_elision_output = wc.State(Acopy_path, {
6702    })
6703  expected_status = wc.State(Acopy_path, {
6704    ''          : Item(status=' M'),
6705    'B'         : Item(status='  '),
6706    'mu'        : Item(status='MM'),
6707    'B/E'       : Item(status='  '),
6708    'B/E/alpha' : Item(status='  '),
6709    'B/E/beta'  : Item(status='  '),
6710    'B/lambda'  : Item(status='  '),
6711    'B/F'       : Item(status='  '),
6712    'C'         : Item(status='  '),
6713    'D'         : Item(status='  '),
6714    'D/G'       : Item(status='  '),
6715    'D/G/pi'    : Item(status='  '),
6716    'D/G/rho'   : Item(status='  '),
6717    'D/G/tau'   : Item(status='  '),
6718    'D/gamma'   : Item(status='  '),
6719    'D/H'       : Item(status='  '),
6720    'D/H/chi'   : Item(status='  '),
6721    'D/H/psi'   : Item(status='  '),
6722    'D/H/omega' : Item(status='  '),
6723    })
6724  expected_status.tweak(wc_rev=3)
6725  expected_disk = wc.State('', {
6726    ''          : Item(props={SVN_PROP_MERGEINFO : '/A:2-3*'}),
6727    'B'         : Item(),
6728    'mu'        : Item("this is file 'mu' modified.\n",
6729                       props={SVN_PROP_MERGEINFO : '/A/mu:2-3'}),
6730    'B/E'       : Item(),
6731    'B/E/alpha' : Item("This is the file 'alpha'.\n"),
6732    'B/E/beta'  : Item("This is the file 'beta'.\n"),
6733    'B/lambda'  : Item("This is the file 'lambda'.\n"),
6734    'B/F'       : Item(),
6735    'C'         : Item(),
6736    'D'         : Item(),
6737    'D/G'       : Item(),
6738    'D/G/pi'    : Item("This is the file 'pi'.\n"),
6739    'D/G/rho'   : Item("This is the file 'rho'.\n"),
6740    'D/G/tau'   : Item("This is the file 'tau'.\n"),
6741    'D/gamma'   : Item("This is the file 'gamma'.\n"),
6742    'D/H'       : Item(),
6743    'D/H/chi'   : Item("This is the file 'chi'.\n"),
6744    'D/H/psi'   : Item("This is the file 'psi'.\n"),
6745    'D/H/omega' : Item("This is the file 'omega'.\n"),
6746    })
6747  expected_skip = wc.State(Acopy_path, { })
6748  svntest.actions.run_and_verify_merge(Acopy_path, '1', '3',
6749                                       sbox.repo_url + '/A', None,
6750                                       expected_output,
6751                                       expected_mergeinfo_output,
6752                                       expected_elision_output,
6753                                       expected_disk,
6754                                       expected_status, expected_skip,
6755                                       [], True, True,
6756                                       '--depth', 'files', Acopy_path)
6757
6758#----------------------------------------------------------------------
6759# Test for issue #2976 Subtrees can lose non-inheritable ranges.
6760#
6761# Also test for a bug with paths added as the immediate child of the
6762# merge target when the merge target has non-inheritable mergeinfo
6763# and is also the current working directory, see
6764# http://svn.haxx.se/dev/archive-2008-12/0133.shtml.
6765#
6766# Test for issue #3392 'Parsing error with reverse merges and
6767# non-inheritable mergeinfo.
6768#
6769# Test issue #3407 'Shallow merges incorrectly set mergeinfo on children'.
6770@SkipUnless(server_has_mergeinfo)
6771@Issues(2976,3392,3407,4057)
6772def merge_away_subtrees_noninheritable_ranges(sbox):
6773  "subtrees can lose non-inheritable ranges"
6774
6775  sbox.build()
6776  wc_dir = sbox.wc_dir
6777  wc_disk, wc_status = set_up_branch(sbox, nbr_of_branches=2)
6778
6779  # Some paths we'll care about
6780  H_path      = sbox.ospath('A/D/H')
6781  D_COPY_path = sbox.ospath('A_COPY/D')
6782  A_COPY_path = sbox.ospath('A_COPY')
6783  nu_path     = sbox.ospath('A/nu')
6784  mu_path     = sbox.ospath('A/mu')
6785  mu_2_path   = sbox.ospath('A_COPY_2/mu')
6786  D_COPY_2_path = sbox.ospath('A_COPY_2/D')
6787  H_COPY_2_path = sbox.ospath('A_COPY_2/D/H')
6788  mu_COPY_path  = sbox.ospath('A_COPY/mu')
6789  nu_COPY_path  = sbox.ospath('A_COPY/nu')
6790
6791  # Make a change to directory A/D/H and commit as r8.
6792  svntest.actions.run_and_verify_svn(exp_noop_up_out(7), [],
6793                                     'update', wc_dir)
6794
6795  svntest.actions.run_and_verify_svn(
6796    ["property 'prop:name' set on '" + H_path + "'\n"], [],
6797    'ps', 'prop:name', 'propval', H_path)
6798  expected_output = svntest.wc.State(wc_dir, {
6799    'A/D/H' : Item(verb='Sending'),})
6800  wc_status.tweak(wc_rev=7)
6801  wc_status.tweak('A/D/H', wc_rev=8)
6802  svntest.actions.run_and_verify_commit(wc_dir, expected_output, wc_status)
6803
6804  # Merge r6:8 --depth immediates to A_COPY/D.  This should merge the
6805  # prop change from r8 to A_COPY/H but not the change to A_COPY/D/H/omega
6806  # from r7 since that is below the depth we are merging to.  Instead,
6807  # non-inheritable mergeinfo should be set on the immediate directory
6808  # child of A_COPY/D that is affected by the merge: A_COPY/D/H.
6809  expected_output = wc.State(D_COPY_path, {
6810    'H' : Item(status=' U'),
6811    })
6812  expected_mergeinfo_output = wc.State(D_COPY_path, {
6813    ''  : Item(status=' U'),
6814    'H' : Item(status=' U'),
6815    })
6816  expected_elision_output = wc.State(D_COPY_path, {
6817    })
6818  expected_status = wc.State(D_COPY_path, {
6819    ''        : Item(status=' M', wc_rev=7),
6820    'H'       : Item(status=' M', wc_rev=7),
6821    'H/chi'   : Item(status='  ', wc_rev=7),
6822    'H/omega' : Item(status='  ', wc_rev=7),
6823    'H/psi'   : Item(status='  ', wc_rev=7),
6824    'G'       : Item(status='  ', wc_rev=7),
6825    'G/pi'    : Item(status='  ', wc_rev=7),
6826    'G/rho'   : Item(status='  ', wc_rev=7),
6827    'G/tau'   : Item(status='  ', wc_rev=7),
6828    'gamma'   : Item(status='  ', wc_rev=7),
6829    })
6830  expected_disk = wc.State('', {
6831    ''        : Item(props={SVN_PROP_MERGEINFO : '/A/D:7-8'}),
6832    'H'       : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:7-8*',
6833                            'prop:name' : 'propval'}),
6834    'H/chi'   : Item("This is the file 'chi'.\n"),
6835    'H/omega' : Item("This is the file 'omega'.\n"),
6836    'H/psi'   : Item("This is the file 'psi'.\n"),
6837    'G'       : Item(),
6838    'G/pi'    : Item("This is the file 'pi'.\n"),
6839    'G/rho'   : Item("This is the file 'rho'.\n"),
6840    'G/tau'   : Item("This is the file 'tau'.\n"),
6841    'gamma'   : Item("This is the file 'gamma'.\n"),
6842    })
6843  expected_skip = wc.State(D_COPY_path, { })
6844  svntest.actions.run_and_verify_merge(D_COPY_path, '6', '8',
6845                                       sbox.repo_url + '/A/D', None,
6846                                       expected_output,
6847                                       expected_mergeinfo_output,
6848                                       expected_elision_output,
6849                                       expected_disk,
6850                                       expected_status, expected_skip,
6851                                       [], True, True,
6852                                       '--depth', 'immediates', D_COPY_path)
6853
6854  # Repeat the previous merge but at default depth of infinity.  The change
6855  # to A_COPY/D/H/omega should now happen and the non-inheritable ranges on
6856  # A_COPY/D/G and A_COPY/D/H be changed to inheritable and then elide to
6857  # A_COPY/D.
6858  expected_output = wc.State(D_COPY_path, {
6859    'H/omega' : Item(status='U '),
6860    })
6861  expected_mergeinfo_output = wc.State(D_COPY_path, {
6862    ''        : Item(status=' G'),
6863    'H'       : Item(status=' G'),
6864    'H/omega' : Item(status=' G'),
6865    })
6866  expected_elision_output = wc.State(D_COPY_path, {
6867    'H'       : Item(status=' U'),
6868    'H/omega' : Item(status=' U'),
6869    })
6870  expected_disk.tweak('', props={SVN_PROP_MERGEINFO : '/A/D:7-8'})
6871  expected_disk.tweak('H', props={'prop:name' : 'propval'})
6872  expected_disk.tweak('G', props={})
6873  expected_disk.tweak('H/omega', contents="New content")
6874  expected_status.tweak('G', status='  ')
6875  expected_status.tweak('H/omega', status='M ')
6876  svntest.actions.run_and_verify_merge(D_COPY_path, '6', '8',
6877                                       sbox.repo_url + '/A/D', None,
6878                                       expected_output,
6879                                       expected_mergeinfo_output,
6880                                       expected_elision_output,
6881                                       expected_disk,
6882                                       expected_status, expected_skip,
6883                                       [], True, True)
6884
6885  # Now test the problem described in
6886  # http://svn.haxx.se/dev/archive-2008-12/0133.shtml.
6887  #
6888  # First revert all local mods.
6889  svntest.actions.run_and_verify_svn(None, [], 'revert', '-R', wc_dir)
6890
6891  # r9: Merge all available revisions from A to A_COPY at a depth of empty
6892  # this will create non-inheritable mergeinfo on A_COPY.
6893  svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir)
6894  wc_status.tweak(wc_rev=8)
6895  svntest.actions.run_and_verify_svn(None, [],
6896                                     'merge', '--depth', 'empty',
6897                                     sbox.repo_url + '/A', A_COPY_path)
6898  wc_status.tweak('A_COPY', wc_rev=9)
6899  expected_output = wc.State(wc_dir, {'A_COPY' : Item(verb='Sending')})
6900  svntest.actions.run_and_verify_commit(wc_dir, expected_output, wc_status)
6901
6902  # r10: Add the file A/nu.
6903  svntest.main.file_write(nu_path, "This is the file 'nu'.\n")
6904  svntest.actions.run_and_verify_svn(None, [], 'add', nu_path)
6905  expected_output = wc.State(wc_dir, {'A/nu' : Item(verb='Adding')})
6906  wc_status.add({'A/nu' : Item(status='  ', wc_rev=10)})
6907  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
6908                                        wc_status)
6909
6910  # Now merge -c10 from A to A_COPY.
6911  svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir)
6912  expected_output = wc.State('', {
6913    'nu': Item(status='A '),
6914    })
6915  expected_mergeinfo_output = wc.State('', {
6916    ''   : Item(status=' U'),
6917    'nu' : Item(status=' U'),
6918    })
6919  expected_elision_output = wc.State('', {
6920    })
6921  expected_status = wc.State('', {
6922    ''          : Item(status=' M'),
6923    'nu'        : Item(status='A ', copied='+'),
6924    'B'         : Item(status='  '),
6925    'mu'        : Item(status='  '),
6926    'B/E'       : Item(status='  '),
6927    'B/E/alpha' : Item(status='  '),
6928    'B/E/beta'  : Item(status='  '),
6929    'B/lambda'  : Item(status='  '),
6930    'B/F'       : Item(status='  '),
6931    'C'         : Item(status='  '),
6932    'D'         : Item(status='  '),
6933    'D/G'       : Item(status='  '),
6934    'D/G/pi'    : Item(status='  '),
6935    'D/G/rho'   : Item(status='  '),
6936    'D/G/tau'   : Item(status='  '),
6937    'D/gamma'   : Item(status='  '),
6938    'D/H'       : Item(status='  '),
6939    'D/H/chi'   : Item(status='  '),
6940    'D/H/psi'   : Item(status='  '),
6941    'D/H/omega' : Item(status='  '),
6942    })
6943  expected_status.tweak(wc_rev=10)
6944  expected_status.tweak('nu', wc_rev='-')
6945  expected_disk = wc.State('', {
6946    ''          : Item(props={SVN_PROP_MERGEINFO : '/A:2-8*,10'}),
6947    'nu'        : Item("This is the file 'nu'.\n",
6948                       props={SVN_PROP_MERGEINFO : '/A/nu:10'}),
6949    'B'         : Item(),
6950    'mu'        : Item("This is the file 'mu'.\n"),
6951    'B/E'       : Item(),
6952    'B/E/alpha' : Item("This is the file 'alpha'.\n"),
6953    'B/E/beta'  : Item("This is the file 'beta'.\n"),
6954    'B/lambda'  : Item("This is the file 'lambda'.\n"),
6955    'B/F'       : Item(),
6956    'C'         : Item(),
6957    'D'         : Item(),
6958    'D/G'       : Item(),
6959    'D/G/pi'    : Item("This is the file 'pi'.\n"),
6960    'D/G/rho'   : Item("This is the file 'rho'.\n"),
6961    'D/G/tau'   : Item("This is the file 'tau'.\n"),
6962    'D/gamma'   : Item("This is the file 'gamma'.\n"),
6963    'D/H'       : Item(),
6964    'D/H/chi'   : Item("This is the file 'chi'.\n"),
6965    'D/H/psi'   : Item("This is the file 'psi'.\n"),
6966    'D/H/omega' : Item("This is the file 'omega'.\n"),
6967    })
6968  expected_skip = wc.State('.', { })
6969  saved_cwd = os.getcwd()
6970  os.chdir(A_COPY_path)
6971  svntest.actions.run_and_verify_merge('', '9', '10',
6972                                       sbox.repo_url + '/A', None,
6973                                       expected_output,
6974                                       expected_mergeinfo_output,
6975                                       expected_elision_output,
6976                                       expected_disk,
6977                                       expected_status,
6978                                       expected_skip,
6979                                       check_props=True)
6980  os.chdir(saved_cwd)
6981
6982  # If a merge target has inheritable and non-inheritable ranges and has a
6983  # child with no explicit mergeinfo, test that a merge which brings
6984  # mergeinfo changes to that child (i.e. as part of the diff) properly
6985  # records mergeinfo on the child that includes both the incoming mergeinfo
6986  # *and* the mergeinfo inherited from it's parent.
6987  #
6988  # First revert all local changes and remove A_COPY/C/nu from disk.
6989  svntest.actions.run_and_verify_svn(None, [], 'revert', '-R', wc_dir)
6990
6991  # Make a text change to A_COPY_2/mu in r11 and then merge that
6992  # change to A/mu in r12.  This will create mergeinfo of '/A_COPY_2/mu:11'
6993  # on A/mu.
6994  svntest.main.file_write(mu_2_path, 'new content')
6995  svntest.actions.run_and_verify_svn(None, [], 'ci', '-m', 'log msg',
6996                                     wc_dir)
6997  svntest.actions.run_and_verify_svn(
6998    expected_merge_output([[11]],
6999                          ['U    ' + mu_path + '\n',
7000                           ' U   ' + mu_path + '\n']),
7001    [], 'merge', '-c11', sbox.repo_url + '/A_COPY_2/mu', mu_path)
7002  svntest.actions.run_and_verify_svn(None, [], 'ci', '-m', 'log msg',
7003                                     wc_dir)
7004
7005  # Now merge r12 from A to A_COPY.  A_COPY/mu should get the mergeinfo from
7006  # r12, '/A_COPY_2/mu:11' as well as mergeinfo describing the merge itself,
7007  # '/A/mu:12'.
7008  expected_output = wc.State('.', {
7009    'mu': Item(status='UG'),
7010    })
7011  expected_mergeinfo_output = wc.State('.', {
7012    ''   : Item(status=' U'),
7013    'mu' : Item(status=' G'),
7014    })
7015  expected_elision_output = wc.State('.', {
7016    })
7017  expected_status = wc.State('', {
7018    ''          : Item(status=' M'),
7019    'B'         : Item(status='  '),
7020    'mu'        : Item(status='MM'),
7021    'B/E'       : Item(status='  '),
7022    'B/E/alpha' : Item(status='  '),
7023    'B/E/beta'  : Item(status='  '),
7024    'B/lambda'  : Item(status='  '),
7025    'B/F'       : Item(status='  '),
7026    'C'         : Item(status='  '),
7027    'D'         : Item(status='  '),
7028    'D/G'       : Item(status='  '),
7029    'D/G/pi'    : Item(status='  '),
7030    'D/G/rho'   : Item(status='  '),
7031    'D/G/tau'   : Item(status='  '),
7032    'D/gamma'   : Item(status='  '),
7033    'D/H'       : Item(status='  '),
7034    'D/H/chi'   : Item(status='  '),
7035    'D/H/psi'   : Item(status='  '),
7036    'D/H/omega' : Item(status='  '),
7037    })
7038  expected_status.tweak(wc_rev=10)
7039  expected_disk = wc.State('', {
7040    ''          : Item(props={SVN_PROP_MERGEINFO : '/A:2-8*,12'}),
7041    'B'         : Item(),
7042    'mu'        : Item("new content",
7043                       props={SVN_PROP_MERGEINFO : '/A/mu:12\n/A_COPY_2/mu:11'}),
7044    'B/E'       : Item(),
7045    'B/E/alpha' : Item("This is the file 'alpha'.\n"),
7046    'B/E/beta'  : Item("This is the file 'beta'.\n"),
7047    'B/lambda'  : Item("This is the file 'lambda'.\n"),
7048    'B/F'       : Item(),
7049    'C'         : Item(),
7050    'D'         : Item(),
7051    'D/G'       : Item(),
7052    'D/G/pi'    : Item("This is the file 'pi'.\n"),
7053    'D/G/rho'   : Item("This is the file 'rho'.\n"),
7054    'D/G/tau'   : Item("This is the file 'tau'.\n"),
7055    'D/gamma'   : Item("This is the file 'gamma'.\n"),
7056    'D/H'       : Item(),
7057    'D/H/chi'   : Item("This is the file 'chi'.\n"),
7058    'D/H/psi'   : Item("This is the file 'psi'.\n"),
7059    'D/H/omega' : Item("This is the file 'omega'.\n"),
7060    })
7061  expected_skip = wc.State('.', { })
7062  saved_cwd = os.getcwd()
7063  os.chdir(A_COPY_path)
7064  # Don't do a dry-run, because it will differ due to the way merge
7065  # sets override mergeinfo on the children of paths with non-inheritable
7066  # ranges.
7067  svntest.actions.run_and_verify_merge('.', '11', '12',
7068                                       sbox.repo_url + '/A', None,
7069                                       expected_output,
7070                                       expected_mergeinfo_output,
7071                                       expected_elision_output,
7072                                       expected_disk,
7073                                       expected_status,
7074                                       expected_skip,
7075                                       [], True, False)
7076  os.chdir(saved_cwd)
7077
7078  # Test for issue #3392
7079  #
7080  # Revert local changes and update.
7081  svntest.actions.run_and_verify_svn(None, [], 'revert', '-R', wc_dir)
7082  svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir)
7083
7084  # Merge r8 from A/D/H to A_COPY_D/H at depth empty.  Since r8 affects only
7085  # A_COPY/D/H itself, the resulting mergeinfo is inheritable.  Commit this
7086  # merge as r13.
7087  expected_output = wc.State(H_COPY_2_path, {
7088    ''    : Item(status=' U'),
7089    })
7090  expected_mergeinfo_output = wc.State(H_COPY_2_path, {
7091    '' : Item(status=' U'),
7092    })
7093  expected_elision_output = wc.State(H_COPY_2_path, {
7094    })
7095  expected_status = wc.State(H_COPY_2_path, {
7096    ''      : Item(status=' M', wc_rev=12),
7097    'psi'   : Item(status='  ', wc_rev=12),
7098    'omega' : Item(status='  ', wc_rev=12),
7099    'chi'   : Item(status='  ', wc_rev=12),
7100    })
7101  expected_disk = wc.State('', {
7102    ''      : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:8',
7103                          "prop:name" : "propval"}),
7104    'psi'   : Item("This is the file 'psi'.\n"),
7105    'omega' : Item("This is the file 'omega'.\n"),
7106    'chi'   : Item("This is the file 'chi'.\n"),
7107    })
7108  expected_skip = wc.State(H_COPY_2_path, {})
7109  svntest.actions.run_and_verify_merge(H_COPY_2_path, '7', '8',
7110                                       sbox.repo_url + '/A/D/H', None,
7111                                       expected_output,
7112                                       expected_mergeinfo_output,
7113                                       expected_elision_output,
7114                                       expected_disk,
7115                                       expected_status, expected_skip,
7116                                       [], True, True,
7117                                       '--depth', 'empty', H_COPY_2_path)
7118  svntest.actions.run_and_verify_svn(None, [], 'commit', '-m',
7119                                     'log msg', wc_dir)
7120  svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir)
7121  # Now reverse the prior merge.  Issue #3392 manifests itself here with
7122  # a mergeinfo parsing error:
7123  #   >svn merge %url%/A/D/H merge_tests-62\A_COPY_2\D\H -c-8
7124  #   --- Reverse-merging r8 into 'merge_tests-62\A_COPY_2\D\H':
7125  #    U   merge_tests-62\A_COPY_2\D\H
7126  #   ..\..\..\subversion\libsvn_subr\mergeinfo.c:590: (apr_err=200020)
7127  #   svn: Could not parse mergeinfo string '-8'
7128  #   ..\..\..\subversion\libsvn_subr\kitchensink.c:52: (apr_err=200022)
7129  #   svn: Negative revision number found parsing '-8'
7130  #
7131  # Status is identical but for the working revision.
7132  expected_status.tweak(wc_rev=13)
7133  # The mergeinfo and prop:name props should disappear.
7134  expected_disk.remove('')
7135  expected_elision_output = wc.State(H_COPY_2_path, {
7136    '' : Item(status=' U'),
7137    })
7138  svntest.actions.run_and_verify_merge(H_COPY_2_path, '8', '7',
7139                                       sbox.repo_url + '/A/D/H', None,
7140                                       expected_output,
7141                                       expected_mergeinfo_output,
7142                                       expected_elision_output,
7143                                       expected_disk,
7144                                       expected_status, expected_skip,
7145                                       check_props=True)
7146
7147  # Test issue #3407 'Shallow merges incorrectly set mergeinfo on children'.
7148  #
7149  # Revert all local mods.
7150  svntest.actions.run_and_verify_svn(None, [], 'revert', '-R', wc_dir)
7151
7152  # Merge all available changes from A to A_COPY at --depth empty. Only the
7153  # mergeinfo on A_COPY should be affected.
7154  svntest.actions.run_and_verify_svn(
7155    expected_merge_output([[9,13]],
7156                          [' U   ' + A_COPY_path + '\n']),
7157    [], 'merge', '--depth', 'empty',
7158    sbox.repo_url + '/A', A_COPY_path)
7159  svntest.actions.run_and_verify_svn([A_COPY_path + ' - /A:2-13*\n'],
7160                                     [], 'pg', SVN_PROP_MERGEINFO,
7161                                     '-R', A_COPY_path)
7162
7163  # Merge all available changes from A to A_COPY at --depth files. Only the
7164  # mergeinfo on A_COPY and its file children should be affected.
7165  svntest.actions.run_and_verify_svn(None, [], 'revert', '-R', wc_dir)
7166  # Revisions 2-13 are already merged to A_COPY and now they will be merged
7167  # to A_COPY's file children.  Due to the way we drive the merge editor
7168  # r2-3, which are inoperative on A_COPY's file children, do not show up
7169  # in the merge notifications, although those revs are included in the
7170  # recorded mergeinfo.
7171  expected_output = expected_merge_output([[4,13],  # Merge notification
7172                                           [9,13],  # Merge notification
7173                                           [2,13]], # Mergeinfo notification
7174                                          ['UU   %s\n' % (mu_COPY_path),
7175                                           'A    %s\n' % (nu_COPY_path),
7176                                           ' U   %s\n' % (A_COPY_path),
7177                                           ' G   %s\n' % (mu_COPY_path),
7178                                           ' U   %s\n' % (nu_COPY_path),])
7179  svntest.actions.run_and_verify_svn(expected_output, [],
7180                                     'merge', '--depth', 'files',
7181                                     sbox.repo_url + '/A', A_COPY_path)
7182  expected_output = svntest.verify.UnorderedOutput(
7183      [A_COPY_path  + ' - /A:2-13*\n',
7184       mu_COPY_path + ' - /A/mu:2-13\n',
7185       nu_COPY_path + ' - /A/nu:10-13\n',])
7186  svntest.actions.run_and_verify_svn(expected_output,
7187                                     [], 'pg', SVN_PROP_MERGEINFO,
7188                                     '-R', A_COPY_path)
7189
7190#----------------------------------------------------------------------
7191# Test for issue #2827
7192# Handle merge info for sparsely-populated directories
7193@Issue(2827)
7194@SkipUnless(server_has_mergeinfo)
7195def merge_to_sparse_directories(sbox):
7196  "merge to sparse directories"
7197
7198  # Merges into sparse working copies should set non-inheritable mergeinfo
7199  # on the deepest directories present in the WC.
7200
7201  sbox.build()
7202  wc_dir = sbox.wc_dir
7203  wc_disk, wc_status = set_up_branch(sbox, False, 1)
7204
7205  # Some paths we'll care about
7206  A_path = sbox.ospath('A')
7207  D_path = sbox.ospath('A/D')
7208  I_path = sbox.ospath('A/C/I')
7209  G_path = sbox.ospath('A/D/G')
7210  A_COPY_path = sbox.ospath('A_COPY')
7211
7212  # Make a few more changes to the merge source...
7213
7214  # r7 - modify and commit A/mu
7215  svntest.main.file_write(sbox.ospath('A/mu'),
7216                          "New content")
7217  expected_output = wc.State(wc_dir, {'A/mu' : Item(verb='Sending')})
7218  wc_status.tweak('A/mu', wc_rev=7)
7219  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
7220                                        wc_status)
7221  wc_disk.tweak('A/mu', contents="New content")
7222
7223  # r8 - Add a prop to A/D and commit.
7224  svntest.actions.run_and_verify_svn(exp_noop_up_out(7), [],
7225                                     'up', wc_dir)
7226  svntest.actions.run_and_verify_svn(["property 'prop:name' set on '" +
7227                                      D_path + "'\n"], [], 'ps',
7228                                     'prop:name', 'propval', D_path)
7229  expected_output = svntest.wc.State(wc_dir, {
7230    'A/D'            : Item(verb='Sending'),
7231    })
7232  wc_status.tweak(wc_rev=7)
7233  wc_status.tweak('A/D', wc_rev=8)
7234  svntest.actions.run_and_verify_commit(wc_dir, expected_output, wc_status)
7235
7236  # r9 - Add a prop to A and commit.
7237  svntest.actions.run_and_verify_svn(exp_noop_up_out(8), [],
7238                                     'up', wc_dir)
7239  svntest.actions.run_and_verify_svn(["property 'prop:name' set on '" +
7240                                      A_path + "'\n"], [], 'ps',
7241                                     'prop:name', 'propval', A_path)
7242  expected_output = svntest.wc.State(wc_dir, {
7243    'A'            : Item(verb='Sending'),
7244    })
7245  wc_status.tweak(wc_rev=8)
7246  wc_status.tweak('A', wc_rev=9)
7247  svntest.actions.run_and_verify_commit(wc_dir, expected_output, wc_status)
7248
7249  # Do an --immediates checkout of A_COPY
7250  immediates_dir = sbox.add_wc_path('immediates')
7251  expected_output = wc.State(immediates_dir, {
7252    'B'  : Item(status='A '),
7253    'mu' : Item(status='A '),
7254    'C'  : Item(status='A '),
7255    'D'  : Item(status='A '),
7256    })
7257  expected_disk = wc.State('', {
7258    'B'  : Item(),
7259    'mu' : Item("This is the file 'mu'.\n"),
7260    'C'  : Item(),
7261    'D'  : Item(),
7262    })
7263  svntest.actions.run_and_verify_checkout(sbox.repo_url + "/A_COPY",
7264                                          immediates_dir,
7265                                          expected_output, expected_disk,
7266                                          [],
7267                                          "--depth", "immediates")
7268
7269  # Merge r4:9 into the immediates WC.
7270  # The root of the immediates WC should get inheritable r4:9 as should
7271  # the one file present 'mu'.  The three directory children present, 'B',
7272  # 'C', and 'D' are checked out at depth empty; the two of these affected
7273  # by the merge, 'B' and 'D', get non-inheritable mergeinfo for r4:9.
7274  # The root and 'D' do should also get the changes
7275  # that affect them directly (the prop adds from r8 and r9).
7276  #
7277  # Currently this fails due to r1424469.  For a full explanation see
7278  # http://svn.haxx.se/dev/archive-2012-12/0472.shtml
7279  # and http://svn.haxx.se/dev/archive-2012-12/0475.shtml
7280  expected_output = wc.State(immediates_dir, {
7281    'D'   : Item(status=' U'),
7282    'mu'  : Item(status='U '),
7283    ''    : Item(status=' U'),
7284    # Shadowed below skips
7285    'D/H/omega' : Item(status='  ', treeconflict='U'),
7286    'B/E/beta'  : Item(status='  ', treeconflict='U'),
7287    })
7288  expected_mergeinfo_output = wc.State(immediates_dir, {
7289    ''  : Item(status=' U'),
7290    'B' : Item(status=' U'),
7291    'D' : Item(status=' U'),
7292    })
7293  expected_elision_output = wc.State(immediates_dir, {
7294    })
7295  expected_status = wc.State(immediates_dir, {
7296    ''          : Item(status=' M', wc_rev=9),
7297    'B'         : Item(status=' M', wc_rev=9),
7298    'mu'        : Item(status='M ', wc_rev=9),
7299    'C'         : Item(status='  ', wc_rev=9),
7300    'D'         : Item(status=' M', wc_rev=9),
7301    })
7302  expected_disk = wc.State('', {
7303    ''          : Item(props={SVN_PROP_MERGEINFO : '/A:5-9',
7304                              "prop:name" : "propval"}),
7305    'B'         : Item(props={SVN_PROP_MERGEINFO : '/A/B:5-9*'}),
7306    'mu'        : Item("New content"),
7307    'C'         : Item(),
7308    'D'         : Item(props={SVN_PROP_MERGEINFO : '/A/D:5-9*',
7309                              "prop:name" : "propval"}),
7310    })
7311  expected_skip = svntest.wc.State(immediates_dir, {
7312    'D/H'       : Item(verb='Skipped missing target'),
7313    'B/E'       : Item(verb='Skipped missing target'),
7314    })
7315  svntest.actions.run_and_verify_merge(immediates_dir, '4', '9',
7316                                       sbox.repo_url + '/A', None,
7317                                       expected_output,
7318                                       expected_mergeinfo_output,
7319                                       expected_elision_output,
7320                                       expected_disk,
7321                                       expected_status,
7322                                       expected_skip,
7323                                       check_props=True)
7324
7325  # Do a --files checkout of A_COPY
7326  files_dir = sbox.add_wc_path('files')
7327  expected_output = wc.State(files_dir, {
7328    'mu' : Item(status='A '),
7329    })
7330  expected_disk = wc.State('', {
7331    'mu' : Item("This is the file 'mu'.\n"),
7332    })
7333  svntest.actions.run_and_verify_checkout(sbox.repo_url + "/A_COPY",
7334                                          files_dir,
7335                                          expected_output, expected_disk,
7336                                          [],
7337                                          "--depth", "files")
7338
7339  # Merge r4:9 into the files WC.
7340  # The root of the files WC should get non-inheritable r4:9 and its one
7341  # present child 'mu' should get the same but inheritable.  The root
7342  # should also get the change that affects it directly (the prop add
7343  # from r9).
7344  expected_output = wc.State(files_dir, {
7345    'mu' : Item(status='U '),
7346    ''   : Item(status=' U'),
7347    # Below the skips
7348    'D/H/omega' : Item(status='  ', treeconflict='U'),
7349    'B/E/beta'  : Item(status='  ', treeconflict='U'),
7350    })
7351  expected_mergeinfo_output = wc.State(files_dir, {
7352    ''   : Item(status=' U'),
7353    'mu' : Item(status=' U'),
7354    })
7355  expected_elision_output = wc.State(files_dir, {
7356    })
7357  expected_status = wc.State(files_dir, {
7358    ''          : Item(status=' M', wc_rev=9),
7359    'mu'        : Item(status='MM', wc_rev=9),
7360    })
7361  expected_disk = wc.State('', {
7362    ''          : Item(props={SVN_PROP_MERGEINFO : '/A:5-9*',
7363                              "prop:name" : "propval"}),
7364    'mu'        : Item("New content",
7365                       props={SVN_PROP_MERGEINFO : '/A/mu:5-9'}),
7366    })
7367  expected_skip = svntest.wc.State(files_dir, {
7368    'D'         : Item(verb='Skipped missing target'),
7369    'B'         : Item(verb='Skipped missing target'),
7370    })
7371  svntest.actions.run_and_verify_merge(files_dir, '4', '9',
7372                                       sbox.repo_url + '/A', None,
7373                                       expected_output,
7374                                       expected_mergeinfo_output,
7375                                       expected_elision_output,
7376                                       expected_disk,
7377                                       expected_status,
7378                                       expected_skip,
7379                                       check_props=True)
7380
7381  # Do an --empty checkout of A_COPY
7382  empty_dir = sbox.add_wc_path('empty')
7383  expected_output = wc.State(empty_dir, {})
7384  expected_disk = wc.State('', {})
7385  svntest.actions.run_and_verify_checkout(sbox.repo_url + "/A_COPY",
7386                                          empty_dir,
7387                                          expected_output, expected_disk,
7388                                          [],
7389                                          "--depth", "empty")
7390
7391  # Merge r4:9 into the empty WC.
7392  # The root of the files WC should get non-inheritable r4:9 and also get
7393  # the one change that affects it directly (the prop add from r9).
7394  expected_output = wc.State(empty_dir, {
7395    ''   : Item(status=' U'),
7396    # Below the skips
7397    'B/E/beta'  : Item(status='  ', treeconflict='U'),
7398    'D/H/omega' : Item(status='  ', treeconflict='U'),
7399    })
7400  expected_mergeinfo_output = wc.State(empty_dir, {
7401    '' : Item(status=' U'),
7402    })
7403  expected_elision_output = wc.State(empty_dir, {
7404    })
7405  expected_status = wc.State(empty_dir, {
7406    ''          : Item(status=' M', wc_rev=9),
7407    })
7408  expected_disk = wc.State('', {
7409    ''          : Item(props={SVN_PROP_MERGEINFO : '/A:5-9*',
7410                              "prop:name" : "propval"}),
7411    })
7412  expected_skip = svntest.wc.State(empty_dir, {
7413    'mu'        : Item(verb='Skipped missing target'),
7414    'D'         : Item(verb='Skipped missing target'),
7415    'B'         : Item(verb='Skipped missing target'),
7416    })
7417  svntest.actions.run_and_verify_merge(empty_dir, '4', '9',
7418                                       sbox.repo_url + '/A', None,
7419                                       expected_output,
7420                                       expected_mergeinfo_output,
7421                                       expected_elision_output,
7422                                       expected_disk,
7423                                       expected_status,
7424                                       expected_skip,
7425                                       check_props=True)
7426
7427  # Check that default depth for merge is infinity.
7428  #
7429  # Revert the previous changes to the immediates WC and update one
7430  # child in that WC to depth infinity.
7431  svntest.actions.run_and_verify_svn(None, [], 'revert', '-R',
7432                                     immediates_dir)
7433  svntest.actions.run_and_verify_svn(None, [], 'up', '--set-depth',
7434                                     'infinity',
7435                                     os.path.join(immediates_dir, 'D'))
7436  # Now merge r6 into the immediates WC, even though the root of the
7437  # is at depth immediates, the subtree rooted at child 'D' is fully
7438  # present, so a merge of r6 should affect 'D/H/omega'.
7439  expected_output = wc.State(immediates_dir, {
7440    'D/H/omega'  : Item(status='U '),
7441    })
7442  expected_mergeinfo_output = wc.State(immediates_dir, {
7443    '' : Item(status=' U'),
7444    })
7445  expected_elision_output = wc.State(immediates_dir, {
7446    })
7447  expected_status = wc.State(immediates_dir, {
7448    ''          : Item(status=' M', wc_rev=9),
7449    'B'         : Item(status='  ', wc_rev=9),
7450    'mu'        : Item(status='  ', wc_rev=9),
7451    'C'         : Item(status='  ', wc_rev=9),
7452    'D'         : Item(status='  ', wc_rev=9),
7453    'D/gamma'   : Item(status='  ', wc_rev=9),
7454    'D/G'       : Item(status='  ', wc_rev=9),
7455    'D/G/pi'    : Item(status='  ', wc_rev=9),
7456    'D/G/rho'   : Item(status='  ', wc_rev=9),
7457    'D/G/tau'   : Item(status='  ', wc_rev=9),
7458    'D/H'       : Item(status='  ', wc_rev=9),
7459    'D/H/chi'   : Item(status='  ', wc_rev=9),
7460    'D/H/omega' : Item(status='M ', wc_rev=9),
7461    'D/H/psi'   : Item(status='  ', wc_rev=9),
7462    })
7463  expected_disk = wc.State('', {
7464    ''          : Item(props={SVN_PROP_MERGEINFO : '/A:6'}),
7465    'B'         : Item(),
7466    'mu'        : Item("This is the file 'mu'.\n"),
7467    'C'         : Item(),
7468    'D'         : Item(),
7469    'D/G'       : Item(),
7470    'D/G/pi'    : Item("This is the file 'pi'.\n"),
7471    'D/G/rho'   : Item("This is the file 'rho'.\n"),
7472    'D/G/tau'   : Item("This is the file 'tau'.\n"),
7473    'D/gamma'   : Item("This is the file 'gamma'.\n"),
7474    'D/H'       : Item(),
7475    'D/H/chi'   : Item("This is the file 'chi'.\n"),
7476    'D/H/psi'   : Item("This is the file 'psi'.\n"),
7477    'D/H/omega' : Item("New content"),
7478    })
7479  expected_skip = wc.State(immediates_dir, {})
7480  svntest.actions.run_and_verify_merge(immediates_dir, '5', '6',
7481                                       sbox.repo_url + '/A', None,
7482                                       expected_output,
7483                                       expected_mergeinfo_output,
7484                                       expected_elision_output,
7485                                       expected_disk,
7486                                       expected_status,
7487                                       expected_skip,
7488                                       check_props=True)
7489
7490#----------------------------------------------------------------------
7491@SkipUnless(server_has_mergeinfo)
7492def merge_old_and_new_revs_from_renamed_dir(sbox):
7493  "merge -rold(before rename):head renamed dir"
7494
7495  # See the email on dev@ from Paul Burba, 2007-09-27, "RE: svn commit:
7496  # r26803 - [...]", <http://svn.haxx.se/dev/archive-2007-09/0706.shtml> or
7497  # <http://subversion.tigris.org/ds/viewMessage.do?dsForumId=462&dsMessageId=927127>.
7498
7499  # Create a WC with a single branch
7500  sbox.build()
7501  wc_dir = sbox.wc_dir
7502  wc_disk, wc_status = set_up_branch(sbox, True, 1)
7503
7504  # Some paths we'll care about
7505  A_url = sbox.repo_url + '/A'
7506  A_MOVED_url = sbox.repo_url + '/A_MOVED'
7507  A_COPY_path = sbox.ospath('A_COPY')
7508  mu_path = sbox.ospath('A/mu')
7509  A_MOVED_mu_path = sbox.ospath('A_MOVED/mu')
7510
7511  # Make a modification to A/mu
7512  svntest.main.file_write(mu_path, "This is the file 'mu' modified.\n")
7513  expected_output = wc.State(wc_dir, {'A/mu' : Item(verb='Sending')})
7514  wc_status.add({'A/mu'     : Item(status='  ', wc_rev=3)})
7515  svntest.actions.run_and_verify_commit(wc_dir, expected_output, wc_status)
7516
7517  # Move A to A_MOVED
7518  svntest.actions.run_and_verify_svn(['Committing transaction...\n',
7519                                            'Committed revision 4.\n'],
7520                                     [], 'mv', '-m', 'mv A to A_MOVED',
7521                                     A_url, A_MOVED_url)
7522
7523  # Update the working copy to get A_MOVED
7524  svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir)
7525
7526  # Make a modification to A_MOVED/mu
7527  svntest.main.file_write(A_MOVED_mu_path, "This is 'mu' in A_MOVED.\n")
7528  expected_output = wc.State(wc_dir, {'A_MOVED/mu' : Item(verb='Sending')})
7529  expected_status = svntest.actions.get_virginal_state(wc_dir, 4)
7530  expected_status.remove('A', 'A/mu', 'A/C', 'A/D', 'A/B', 'A/B/lambda',
7531                         'A/B/E', 'A/B/E/alpha', 'A/B/E/beta', 'A/B/F',
7532                         'A/D/gamma', 'A/D/G', 'A/D/G/pi', 'A/D/G/rho',
7533                         'A/D/G/tau', 'A/D/H', 'A/D/H/chi', 'A/D/H/omega',
7534                         'A/D/H/psi')
7535  expected_status.add({
7536    ''                 : Item(status='  ', wc_rev=4),
7537    'iota'             : Item(status='  ', wc_rev=4),
7538    'A_MOVED'          : Item(status='  ', wc_rev=4),
7539    'A_MOVED/mu'       : Item(status='  ', wc_rev=5),
7540    'A_MOVED/C'        : Item(status='  ', wc_rev=4),
7541    'A_MOVED/D'        : Item(status='  ', wc_rev=4),
7542    'A_MOVED/B'        : Item(status='  ', wc_rev=4),
7543    'A_MOVED/B/lambda' : Item(status='  ', wc_rev=4),
7544    'A_MOVED/B/E'      : Item(status='  ', wc_rev=4),
7545    'A_MOVED/B/E/alpha': Item(status='  ', wc_rev=4),
7546    'A_MOVED/B/E/beta' : Item(status='  ', wc_rev=4),
7547    'A_MOVED/B/F'      : Item(status='  ', wc_rev=4),
7548    'A_MOVED/D/gamma'  : Item(status='  ', wc_rev=4),
7549    'A_MOVED/D/G'      : Item(status='  ', wc_rev=4),
7550    'A_MOVED/D/G/pi'   : Item(status='  ', wc_rev=4),
7551    'A_MOVED/D/G/rho'  : Item(status='  ', wc_rev=4),
7552    'A_MOVED/D/G/tau'  : Item(status='  ', wc_rev=4),
7553    'A_MOVED/D/H'      : Item(status='  ', wc_rev=4),
7554    'A_MOVED/D/H/chi'  : Item(status='  ', wc_rev=4),
7555    'A_MOVED/D/H/omega': Item(status='  ', wc_rev=4),
7556    'A_MOVED/D/H/psi'  : Item(status='  ', wc_rev=4),
7557    'A_COPY'           : Item(status='  ', wc_rev=4),
7558    'A_COPY/mu'        : Item(status='  ', wc_rev=4),
7559    'A_COPY/C'         : Item(status='  ', wc_rev=4),
7560    'A_COPY/D'         : Item(status='  ', wc_rev=4),
7561    'A_COPY/B'         : Item(status='  ', wc_rev=4),
7562    'A_COPY/B/lambda'  : Item(status='  ', wc_rev=4),
7563    'A_COPY/B/E'       : Item(status='  ', wc_rev=4),
7564    'A_COPY/B/E/alpha' : Item(status='  ', wc_rev=4),
7565    'A_COPY/B/E/beta'  : Item(status='  ', wc_rev=4),
7566    'A_COPY/B/F'       : Item(status='  ', wc_rev=4),
7567    'A_COPY/D/gamma'   : Item(status='  ', wc_rev=4),
7568    'A_COPY/D/G'       : Item(status='  ', wc_rev=4),
7569    'A_COPY/D/G/pi'    : Item(status='  ', wc_rev=4),
7570    'A_COPY/D/G/rho'   : Item(status='  ', wc_rev=4),
7571    'A_COPY/D/G/tau'   : Item(status='  ', wc_rev=4),
7572    'A_COPY/D/H'       : Item(status='  ', wc_rev=4),
7573    'A_COPY/D/H/chi'   : Item(status='  ', wc_rev=4),
7574    'A_COPY/D/H/omega' : Item(status='  ', wc_rev=4),
7575    'A_COPY/D/H/psi'   : Item(status='  ', wc_rev=4),
7576    })
7577  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
7578                                        expected_status)
7579
7580  # Merge /A_MOVED to /A_COPY - this happens in multiple passes
7581  # because /A_MOVED has renames in its history between the boundaries
7582  # of the requested merge range.
7583  expected_output = wc.State(A_COPY_path, {
7584    'mu' : Item(status='G ', prev_status='U '), # mu gets touched twice
7585    })
7586  expected_mergeinfo_output = wc.State(A_COPY_path, {
7587    '' : Item(status=' G', prev_status=' U'),
7588    })
7589  expected_elision_output = wc.State(A_COPY_path, {
7590    })
7591  expected_status = wc.State(A_COPY_path, {
7592    ''         : Item(status=' M', wc_rev=4),
7593    'mu'       : Item(status='M ', wc_rev=4),
7594    'C'        : Item(status='  ', wc_rev=4),
7595    'D'        : Item(status='  ', wc_rev=4),
7596    'B'        : Item(status='  ', wc_rev=4),
7597    'B/lambda' : Item(status='  ', wc_rev=4),
7598    'B/E'      : Item(status='  ', wc_rev=4),
7599    'B/E/alpha': Item(status='  ', wc_rev=4),
7600    'B/E/beta' : Item(status='  ', wc_rev=4),
7601    'B/F'      : Item(status='  ', wc_rev=4),
7602    'D/gamma'  : Item(status='  ', wc_rev=4),
7603    'D/G'      : Item(status='  ', wc_rev=4),
7604    'D/G/pi'   : Item(status='  ', wc_rev=4),
7605    'D/G/rho'  : Item(status='  ', wc_rev=4),
7606    'D/G/tau'  : Item(status='  ', wc_rev=4),
7607    'D/H'      : Item(status='  ', wc_rev=4),
7608    'D/H/chi'  : Item(status='  ', wc_rev=4),
7609    'D/H/omega': Item(status='  ', wc_rev=4),
7610    'D/H/psi'  : Item(status='  ', wc_rev=4),
7611    })
7612  expected_disk = wc.State('', {
7613    ''         : Item(props={SVN_PROP_MERGEINFO : '/A:3\n/A_MOVED:4-5'}),
7614    'mu'       : Item("This is 'mu' in A_MOVED.\n"),
7615    'C'        : Item(),
7616    'D'        : Item(),
7617    'B'        : Item(),
7618    'B/lambda' : Item("This is the file 'lambda'.\n"),
7619    'B/E'      : Item(),
7620    'B/E/alpha': Item("This is the file 'alpha'.\n"),
7621    'B/E/beta' : Item("This is the file 'beta'.\n"),
7622    'B/F'      : Item(),
7623    'D/gamma'  : Item("This is the file 'gamma'.\n"),
7624    'D/G'      : Item(),
7625    'D/G/pi'   : Item("This is the file 'pi'.\n"),
7626    'D/G/rho'  : Item("This is the file 'rho'.\n"),
7627    'D/G/tau'  : Item("This is the file 'tau'.\n"),
7628    'D/H'      : Item(),
7629    'D/H/chi'  : Item("This is the file 'chi'.\n"),
7630    'D/H/omega': Item("This is the file 'omega'.\n"),
7631    'D/H/psi'  : Item("This is the file 'psi'.\n"),
7632    })
7633  expected_skip = wc.State(A_COPY_path, {})
7634
7635  ### Disabling dry_run mode because currently it can't handle the way
7636  ### 'mu' gets textually modified in multiple passes.
7637  svntest.actions.run_and_verify_merge(A_COPY_path, '2', '5',
7638                                       A_MOVED_url, None,
7639                                       expected_output,
7640                                       expected_mergeinfo_output,
7641                                       expected_elision_output,
7642                                       expected_disk,
7643                                       expected_status,
7644                                       expected_skip,
7645                                       [], True, False)
7646
7647#----------------------------------------------------------------------
7648@SkipUnless(server_has_mergeinfo)
7649def merge_with_child_having_different_rev_ranges_to_merge(sbox):
7650  "child having different rev ranges to merge"
7651  #Modify A/mu to 30 lines with a content 'line1'...'line30' commit it at r2.
7652  #Create a branch A_COPY from A, commit it at r3.
7653  #Modify A/mu line number 7 to 'LINE7' modify and commit at r4.
7654  #Modify A/mu line number 17 to 'LINE17' modify, set prop 'prop1' on 'A'
7655  #with a value 'val1' and commit at r5.
7656  #Modify A/mu line number 27 to 'LINE27' modify and commit at r6.
7657  #Merge r5 to 'A/mu' as a single file merge explicitly to 'A_COPY/mu'.
7658  #Merge r3:6 from 'A' to 'A_COPY
7659  #This should merge r4 and then r5 through r6.
7660  #Revert r5 and r6 via single file merge on A_COPY/mu.
7661  #Revert r6 through r4 on A_COPY this should get back us the pristine copy.
7662  #Merge r3:6 from 'A' to 'A_COPY
7663  #Revert r5 on A_COPY/mu
7664  #Modify line number 17 with 'some other line17' of A_COPY/mu
7665  #Merge r6:3 from 'A' to 'A_COPY, This should leave line number 17
7666  #undisturbed in A_COPY/mu, rest should be reverted.
7667
7668  # Create a WC
7669  sbox.build()
7670  wc_dir = sbox.wc_dir
7671  A_path = sbox.ospath('A')
7672  mu_path = sbox.ospath('A/mu')
7673  A_url = sbox.repo_url + '/A'
7674  A_mu_url = sbox.repo_url + '/A/mu'
7675  A_COPY_url = sbox.repo_url + '/A_COPY'
7676  A_COPY_path = sbox.ospath('A_COPY')
7677  A_COPY_mu_path = sbox.ospath('A_COPY/mu')
7678  thirty_line_dummy_text = 'line1\n'
7679  for i in range(2, 31):
7680    thirty_line_dummy_text += 'line' + str(i) + '\n'
7681
7682  svntest.main.file_write(mu_path, thirty_line_dummy_text)
7683  expected_output = wc.State(wc_dir, {'A/mu' : Item(verb='Sending')})
7684  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
7685  expected_status.tweak('A/mu', wc_rev=2)
7686  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
7687                                        expected_status)
7688  svntest.actions.run_and_verify_svn(None, [],
7689                                     'cp', A_url, A_COPY_url, '-m', 'rev 3')
7690  # Update the working copy to get A_COPY
7691  svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir)
7692  expected_status.add({'A_COPY'           : Item(status='  '),
7693                       'A_COPY/mu'        : Item(status='  '),
7694                       'A_COPY/C'         : Item(status='  '),
7695                       'A_COPY/D'         : Item(status='  '),
7696                       'A_COPY/B'         : Item(status='  '),
7697                       'A_COPY/B/lambda'  : Item(status='  '),
7698                       'A_COPY/B/E'       : Item(status='  '),
7699                       'A_COPY/B/E/alpha' : Item(status='  '),
7700                       'A_COPY/B/E/beta'  : Item(status='  '),
7701                       'A_COPY/B/F'       : Item(status='  '),
7702                       'A_COPY/D/gamma'   : Item(status='  '),
7703                       'A_COPY/D/G'       : Item(status='  '),
7704                       'A_COPY/D/G/pi'    : Item(status='  '),
7705                       'A_COPY/D/G/rho'   : Item(status='  '),
7706                       'A_COPY/D/G/tau'   : Item(status='  '),
7707                       'A_COPY/D/H'       : Item(status='  '),
7708                       'A_COPY/D/H/chi'   : Item(status='  '),
7709                       'A_COPY/D/H/omega' : Item(status='  '),
7710                       'A_COPY/D/H/psi'   : Item(status='  ')})
7711  expected_status.tweak(wc_rev=3)
7712  tweaked_7th_line = thirty_line_dummy_text.replace('line7', 'LINE 7')
7713  svntest.main.file_write(mu_path, tweaked_7th_line)
7714  expected_status.tweak('A/mu', wc_rev=4)
7715  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
7716                                        expected_status)
7717  svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir)
7718  expected_status.tweak(wc_rev=4)
7719  tweaked_17th_line = tweaked_7th_line.replace('line17', 'LINE 17')
7720  svntest.main.file_write(mu_path, tweaked_17th_line)
7721  svntest.main.run_svn(None, 'propset', 'prop1', 'val1', A_path)
7722  expected_output = wc.State(wc_dir,
7723                             {
7724                              'A'    : Item(verb='Sending'),
7725                              'A/mu' : Item(verb='Sending')
7726                             }
7727                            )
7728  expected_status.tweak('A', wc_rev=5)
7729  expected_status.tweak('A/mu', wc_rev=5)
7730  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
7731                                        expected_status)
7732  tweaked_27th_line = tweaked_17th_line.replace('line27', 'LINE 27')
7733  svntest.main.file_write(mu_path, tweaked_27th_line)
7734  expected_status.tweak('A/mu', wc_rev=6)
7735  expected_output = wc.State(wc_dir, {'A/mu' : Item(verb='Sending')})
7736  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
7737                                        expected_status)
7738  # Merge r5 to A_COPY/mu
7739  svntest.actions.run_and_verify_svn(
7740    expected_merge_output([[5]],
7741                          ['U    ' + A_COPY_mu_path + '\n',
7742                           ' U   ' + A_COPY_mu_path + '\n']),
7743    [], 'merge', '-r4:5', A_mu_url, A_COPY_mu_path)
7744
7745  expected_skip = wc.State(A_COPY_path, {})
7746  expected_output = wc.State(A_COPY_path, {
7747    ''   : Item(status=' U'),
7748    'mu' : Item(status='G ', prev_status='G '), # Updated twice
7749    })
7750  expected_mergeinfo_output = wc.State(A_COPY_path, {
7751    ''   : Item(status=' U'),
7752    'mu' : Item(status=' G'),
7753    })
7754  expected_elision_output = wc.State(A_COPY_path, {
7755    'mu' : Item(status=' U'),
7756    })
7757  expected_status = wc.State(A_COPY_path, {
7758    ''         : Item(status=' M', wc_rev=4),
7759    'mu'       : Item(status='M ', wc_rev=4),
7760    'C'        : Item(status='  ', wc_rev=4),
7761    'D'        : Item(status='  ', wc_rev=4),
7762    'B'        : Item(status='  ', wc_rev=4),
7763    'B/lambda' : Item(status='  ', wc_rev=4),
7764    'B/E'      : Item(status='  ', wc_rev=4),
7765    'B/E/alpha': Item(status='  ', wc_rev=4),
7766    'B/E/beta' : Item(status='  ', wc_rev=4),
7767    'B/F'      : Item(status='  ', wc_rev=4),
7768    'D/gamma'  : Item(status='  ', wc_rev=4),
7769    'D/G'      : Item(status='  ', wc_rev=4),
7770    'D/G/pi'   : Item(status='  ', wc_rev=4),
7771    'D/G/rho'  : Item(status='  ', wc_rev=4),
7772    'D/G/tau'  : Item(status='  ', wc_rev=4),
7773    'D/H'      : Item(status='  ', wc_rev=4),
7774    'D/H/chi'  : Item(status='  ', wc_rev=4),
7775    'D/H/omega': Item(status='  ', wc_rev=4),
7776    'D/H/psi'  : Item(status='  ', wc_rev=4),
7777    })
7778  expected_disk = wc.State('', {
7779    ''         : Item(props={SVN_PROP_MERGEINFO : '/A:4-6',
7780                             'prop1' : 'val1'}),
7781    'mu'       : Item(tweaked_27th_line),
7782    'C'        : Item(),
7783    'D'        : Item(),
7784    'B'        : Item(),
7785    'B/lambda' : Item("This is the file 'lambda'.\n"),
7786    'B/E'      : Item(),
7787    'B/E/alpha': Item("This is the file 'alpha'.\n"),
7788    'B/E/beta' : Item("This is the file 'beta'.\n"),
7789    'B/F'      : Item(),
7790    'D/gamma'  : Item("This is the file 'gamma'.\n"),
7791    'D/G'      : Item(),
7792    'D/G/pi'   : Item("This is the file 'pi'.\n"),
7793    'D/G/rho'  : Item("This is the file 'rho'.\n"),
7794    'D/G/tau'  : Item("This is the file 'tau'.\n"),
7795    'D/H'      : Item(),
7796    'D/H/chi'  : Item("This is the file 'chi'.\n"),
7797    'D/H/omega': Item("This is the file 'omega'.\n"),
7798    'D/H/psi'  : Item("This is the file 'psi'.\n"),
7799    })
7800  svntest.actions.run_and_verify_merge(A_COPY_path, '3', '6',
7801                                       A_url, None,
7802                                       expected_output,
7803                                       expected_mergeinfo_output,
7804                                       expected_elision_output,
7805                                       expected_disk,
7806                                       expected_status,
7807                                       expected_skip,
7808                                       check_props=True)
7809  # Revert r5 and r6 on A_COPY/mu
7810  svntest.actions.run_and_verify_svn(
7811    expected_merge_output([[6,5]],
7812                          ['G    ' + A_COPY_mu_path + '\n',
7813                           ' G   ' + A_COPY_mu_path + '\n']),
7814    [], 'merge', '-r6:4', A_mu_url, A_COPY_mu_path)
7815
7816  expected_output = wc.State(A_COPY_path, {
7817    ''   : Item(status=' G'), # merged removal of prop1 property
7818    'mu' : Item(status='G '), # merged reversion of text changes
7819    })
7820  expected_mergeinfo_output = wc.State(A_COPY_path, {
7821    ''   : Item(status=' G'),
7822    'mu' : Item(status=' G'),
7823    })
7824  expected_elision_output = wc.State(A_COPY_path, {
7825    ''   : Item(status=' U'),
7826    'mu' : Item(status=' U'),
7827    })
7828  expected_status.tweak('', status='  ')
7829  expected_status.tweak('mu', status='  ')
7830  expected_disk.tweak('', props={})
7831  expected_disk.remove('')
7832  expected_disk.tweak('mu', contents=thirty_line_dummy_text)
7833  svntest.actions.run_and_verify_merge(A_COPY_path, '6', '3',
7834                                       A_url, None,
7835                                       expected_output,
7836                                       expected_mergeinfo_output,
7837                                       expected_elision_output,
7838                                       expected_disk,
7839                                       expected_status,
7840                                       expected_skip,
7841                                       check_props=True)
7842
7843  expected_disk.add({'' : Item(props={SVN_PROP_MERGEINFO : '/A:4-6',
7844                                      'prop1' : 'val1'})})
7845  expected_disk.tweak('mu', contents=tweaked_27th_line)
7846  expected_output = wc.State(A_COPY_path, {
7847    ''   : Item(status=' U'), # new mergeinfo and prop1 property
7848    'mu' : Item(status='U '), # text changes
7849    })
7850  expected_mergeinfo_output = wc.State(A_COPY_path, {
7851    '' : Item(status=' U'),
7852    })
7853  expected_elision_output = wc.State(A_COPY_path, {
7854    })
7855  expected_status.tweak('', status=' M')
7856  expected_status.tweak('mu', status='M ')
7857  svntest.actions.run_and_verify_merge(A_COPY_path, '3', '6',
7858                                       A_url, None,
7859                                       expected_output,
7860                                       expected_mergeinfo_output,
7861                                       expected_elision_output,
7862                                       expected_disk,
7863                                       expected_status,
7864                                       expected_skip,
7865                                       check_props=True)
7866  #Revert r5 on A_COPY/mu
7867  svntest.actions.run_and_verify_svn(
7868    expected_merge_output([[-5]],
7869                          ['G    ' + A_COPY_mu_path + '\n',
7870                           ' G   ' + A_COPY_mu_path + '\n']),
7871    [], 'merge', '-r5:4', A_mu_url, A_COPY_mu_path)
7872  tweaked_17th_line_1 = tweaked_27th_line.replace('LINE 17',
7873                                                  'some other line17')
7874  tweaked_17th_line_2 = thirty_line_dummy_text.replace('line17',
7875                                                       'some other line17')
7876  svntest.main.file_write(A_COPY_mu_path, tweaked_17th_line_1)
7877  expected_output = wc.State(A_COPY_path, {
7878    ''   : Item(status=' G'),
7879    'mu' : Item(status='G ', prev_status='G '),
7880    })
7881  expected_mergeinfo_output = wc.State(A_COPY_path, {
7882    ''   : Item(status=' G'),
7883    'mu' : Item(status=' G'),
7884    })
7885  expected_elision_output = wc.State(A_COPY_path, {
7886    ''   : Item(status=' U'),
7887    'mu' : Item(status=' U'),
7888    })
7889  expected_status.tweak('', status='  ')
7890  expected_status.tweak('mu', status='M ')
7891  expected_disk.remove('')
7892  expected_disk.tweak('mu', contents=tweaked_17th_line_2)
7893  svntest.actions.run_and_verify_merge(A_COPY_path, '6', '3',
7894                                       A_url, None,
7895                                       expected_output,
7896                                       expected_mergeinfo_output,
7897                                       expected_elision_output,
7898                                       expected_disk,
7899                                       expected_status,
7900                                       expected_skip,
7901                                       check_props=True)
7902
7903#----------------------------------------------------------------------
7904@SkipUnless(server_has_mergeinfo)
7905def merge_old_and_new_revs_from_renamed_file(sbox):
7906  "merge -rold(before rename):head renamed file"
7907
7908  ## See http://svn.haxx.se/dev/archive-2007-09/0706.shtml ##
7909
7910  # Create a WC
7911  sbox.build()
7912  wc_dir = sbox.wc_dir
7913
7914  # Some paths we'll care about
7915  mu_url = sbox.repo_url + '/A/mu'
7916  mu_MOVED_url = sbox.repo_url + '/A/mu_MOVED'
7917  mu_COPY_url = sbox.repo_url + '/A/mu_COPY'
7918  mu_COPY_path = sbox.ospath('A/mu_COPY')
7919  mu_path = sbox.ospath('A/mu')
7920  mu_MOVED_path = sbox.ospath('A/mu_MOVED')
7921
7922  # Copy mu to mu_COPY
7923  svntest.actions.run_and_verify_svn(['Committing transaction...\n',
7924                                            'Committed revision 2.\n'],
7925                                     [], 'cp', '-m', 'cp mu to mu_COPY',
7926                                     mu_url, mu_COPY_url)
7927
7928  # Make a modification to A/mu
7929  svntest.main.file_write(mu_path, "This is the file 'mu' modified.\n")
7930  expected_output = wc.State(wc_dir, {'A/mu' : Item(verb='Sending')})
7931  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
7932  expected_status.tweak('A/mu', wc_rev=3)
7933  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
7934                                        expected_status)
7935
7936  # Move mu to mu_MOVED
7937  svntest.actions.run_and_verify_svn(['Committing transaction...\n',
7938                                            'Committed revision 4.\n'],
7939                                     [], 'mv', '-m', 'mv mu to mu_MOVED',
7940                                     mu_url, mu_MOVED_url)
7941
7942  # Update the working copy to get mu_MOVED
7943  svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir)
7944
7945  # Make a modification to mu_MOVED
7946  svntest.main.file_write(mu_MOVED_path, "This is 'mu' in mu_MOVED.\n")
7947  expected_output = wc.State(wc_dir, {'A/mu_MOVED' : Item(verb='Sending')})
7948  expected_status = svntest.actions.get_virginal_state(wc_dir, 4)
7949  expected_status.remove('A/mu')
7950  expected_status.add({
7951    'A/mu_MOVED'  : Item(status='  ', wc_rev=5),
7952    'A/mu_COPY'   : Item(status='  ', wc_rev=4),
7953    })
7954  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
7955                                        expected_status)
7956
7957  # Merge A/mu_MOVED to A/mu_COPY - this happens in multiple passes
7958  # because A/mu_MOVED has renames in its history between the
7959  # boundaries of the requested merge range.
7960  expected_output = expected_merge_output([[2,3],[4,5]],
7961                                          ['U    %s\n' % (mu_COPY_path),
7962                                           ' U   %s\n' % (mu_COPY_path),
7963                                           'G    %s\n' % (mu_COPY_path),
7964                                           ' G   %s\n' % (mu_COPY_path),])
7965  svntest.actions.run_and_verify_svn(expected_output,
7966                                     [], 'merge', '-r', '1:5',
7967                                     mu_MOVED_url,
7968                                     mu_COPY_path)
7969  svntest.actions.run_and_verify_svn(['/A/mu:2-3\n',
7970                                            '/A/mu_MOVED:4-5\n'],
7971                                     [], 'propget', SVN_PROP_MERGEINFO,
7972                                     mu_COPY_path)
7973
7974#----------------------------------------------------------------------
7975@SkipUnless(server_has_mergeinfo)
7976def merge_with_auto_rev_range_detection(sbox):
7977  "merge with auto detection of revision ranges"
7978
7979  ## See http://svn.haxx.se/dev/archive-2007-09/0735.shtml ##
7980
7981  # Create a WC
7982  sbox.build()
7983  wc_dir = sbox.wc_dir
7984
7985  # Some paths we'll care about
7986  A_url = sbox.repo_url + '/A'
7987  A_COPY_url = sbox.repo_url + '/A_COPY'
7988  B1_path = sbox.ospath('A/B1')
7989  B1_mu_path = sbox.ospath('A/B1/mu')
7990  A_COPY_path = sbox.ospath('A_COPY')
7991
7992  # Create B1 inside A
7993  svntest.actions.run_and_verify_svn(["A         " + B1_path + "\n"],
7994                                     [], 'mkdir',
7995                                     B1_path)
7996
7997  # Add a file mu inside B1
7998  svntest.main.file_write(B1_mu_path, "This is the file 'mu'.\n")
7999  svntest.actions.run_and_verify_svn(["A         " + B1_mu_path + "\n"],
8000                                     [], 'add', B1_mu_path)
8001
8002  # Commit B1 and B1/mu
8003  expected_output = wc.State(wc_dir, {
8004    'A/B1'      : Item(verb='Adding'),
8005    'A/B1/mu'   : Item(verb='Adding'),
8006    })
8007  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
8008  expected_status.add({
8009    'A/B1'      : Item(status='  ', wc_rev=2),
8010    'A/B1/mu'   : Item(status='  ', wc_rev=2),
8011    })
8012  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
8013                                        expected_status)
8014
8015  # Copy A to A_COPY
8016  svntest.actions.run_and_verify_svn(['Committing transaction...\n',
8017                                            'Committed revision 3.\n'],
8018                                     [], 'cp', '-m', 'cp A to A_COPY',
8019                                     A_url, A_COPY_url)
8020
8021  # Make a modification to A/B1/mu
8022  svntest.main.file_write(B1_mu_path, "This is the file 'mu' modified.\n")
8023  expected_output = wc.State(wc_dir, {'A/B1/mu' : Item(verb='Sending')})
8024  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
8025  expected_status.add({
8026    'A/B1'      : Item(status='  ', wc_rev=2),
8027    'A/B1/mu'   : Item(status='  ', wc_rev=4),
8028    })
8029  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
8030                                        expected_status)
8031
8032  # Update the working copy to get A_COPY
8033  svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir)
8034
8035  # Merge /A to /A_COPY
8036  expected_output = wc.State(A_COPY_path, {
8037    'B1/mu' : Item(status='U '),
8038    })
8039  expected_mergeinfo_output = wc.State(A_COPY_path, {
8040    '' : Item(status=' U'),
8041    })
8042  expected_elision_output = wc.State(A_COPY_path, {
8043    })
8044  expected_status = wc.State(A_COPY_path, {
8045    ''         : Item(status=' M', wc_rev=4),
8046    'mu'       : Item(status='  ', wc_rev=4),
8047    'C'        : Item(status='  ', wc_rev=4),
8048    'D'        : Item(status='  ', wc_rev=4),
8049    'B'        : Item(status='  ', wc_rev=4),
8050    'B/lambda' : Item(status='  ', wc_rev=4),
8051    'B/E'      : Item(status='  ', wc_rev=4),
8052    'B/E/alpha': Item(status='  ', wc_rev=4),
8053    'B/E/beta' : Item(status='  ', wc_rev=4),
8054    'B/F'      : Item(status='  ', wc_rev=4),
8055    'B1'       : Item(status='  ', wc_rev=4),
8056    'B1/mu'    : Item(status='M ', wc_rev=4),
8057    'D/gamma'  : Item(status='  ', wc_rev=4),
8058    'D/G'      : Item(status='  ', wc_rev=4),
8059    'D/G/pi'   : Item(status='  ', wc_rev=4),
8060    'D/G/rho'  : Item(status='  ', wc_rev=4),
8061    'D/G/tau'  : Item(status='  ', wc_rev=4),
8062    'D/H'      : Item(status='  ', wc_rev=4),
8063    'D/H/chi'  : Item(status='  ', wc_rev=4),
8064    'D/H/omega': Item(status='  ', wc_rev=4),
8065    'D/H/psi'  : Item(status='  ', wc_rev=4),
8066    })
8067  expected_disk = wc.State('', {
8068    ''         : Item(props={SVN_PROP_MERGEINFO : '/A:3-4'}),
8069    'mu'       : Item("This is the file 'mu'.\n"),
8070    'C'        : Item(),
8071    'D'        : Item(),
8072    'B'        : Item(),
8073    'B/lambda' : Item("This is the file 'lambda'.\n"),
8074    'B/E'      : Item(),
8075    'B/E/alpha': Item("This is the file 'alpha'.\n"),
8076    'B/E/beta' : Item("This is the file 'beta'.\n"),
8077    'B/F'      : Item(),
8078    'B1'       : Item(),
8079    'B1/mu'    : Item("This is the file 'mu' modified.\n"),
8080    'D/gamma'  : Item("This is the file 'gamma'.\n"),
8081    'D/G'      : Item(),
8082    'D/G/pi'   : Item("This is the file 'pi'.\n"),
8083    'D/G/rho'  : Item("This is the file 'rho'.\n"),
8084    'D/G/tau'  : Item("This is the file 'tau'.\n"),
8085    'D/H'      : Item(),
8086    'D/H/chi'  : Item("This is the file 'chi'.\n"),
8087    'D/H/omega': Item("This is the file 'omega'.\n"),
8088    'D/H/psi'  : Item("This is the file 'psi'.\n"),
8089    })
8090  expected_skip = wc.State(A_COPY_path, {})
8091  svntest.actions.run_and_verify_merge(A_COPY_path, None, None,
8092                                       A_url, None,
8093                                       expected_output,
8094                                       expected_mergeinfo_output,
8095                                       expected_elision_output,
8096                                       expected_disk,
8097                                       expected_status,
8098                                       expected_skip,
8099                                       [], True, True)
8100
8101#----------------------------------------------------------------------
8102# Test for issue 2818: Provide a 'merge' API which allows for merging of
8103# arbitrary revision ranges (e.g. '-c 3,5,7')
8104@Issue(2818)
8105@SkipUnless(server_has_mergeinfo)
8106def cherry_picking(sbox):
8107  "command line supports cherry picked merge ranges"
8108
8109  sbox.build()
8110  wc_dir = sbox.wc_dir
8111  wc_disk, wc_status = set_up_branch(sbox)
8112
8113  # Some paths we'll care about
8114  H_path = sbox.ospath('A/D/H')
8115  G_path = sbox.ospath('A/D/G')
8116  A_COPY_path = sbox.ospath('A_COPY')
8117  D_COPY_path = sbox.ospath('A_COPY/D')
8118  G_COPY_path = sbox.ospath('A_COPY/D/G')
8119  H_COPY_path = sbox.ospath('A_COPY/D/H')
8120  rho_COPY_path = sbox.ospath('A_COPY/D/G/rho')
8121  omega_COPY_path = sbox.ospath('A_COPY/D/H/omega')
8122  psi_COPY_path = sbox.ospath('A_COPY/D/H/psi')
8123
8124  # Update working copy
8125  expected_output = svntest.wc.State(wc_dir, {})
8126  wc_status.tweak(wc_rev='6')
8127  svntest.actions.run_and_verify_update(wc_dir, expected_output,
8128                                        wc_disk, wc_status,
8129                                        check_props=True)
8130
8131  # Make some prop changes to some dirs.
8132  svntest.actions.run_and_verify_svn(["property 'prop:name' set on '" +
8133                                      G_path + "'\n"], [], 'ps',
8134                                     'prop:name', 'propval', G_path)
8135  expected_output = svntest.wc.State(wc_dir, {'A/D/G': Item(verb='Sending'),})
8136  wc_status.tweak('A/D/G', wc_rev=7)
8137  wc_disk.tweak('A/D/G', props={'prop:name' : 'propval'})
8138
8139  svntest.actions.run_and_verify_commit(wc_dir, expected_output, wc_status)
8140  svntest.actions.run_and_verify_svn(["property 'prop:name' set on '" +
8141                                      H_path + "'\n"], [], 'ps',
8142                                     'prop:name', 'propval', H_path)
8143  expected_output = svntest.wc.State(wc_dir, {'A/D/H': Item(verb='Sending'),})
8144  wc_status.tweak('A/D/H', wc_rev=8)
8145  wc_disk.tweak('A/D/H', props={'prop:name' : 'propval'})
8146  svntest.actions.run_and_verify_commit(wc_dir, expected_output, wc_status)
8147
8148  # Do multiple additive merges to a file"
8149  # Merge -r2:4 -c6 into A_COPY/D/G/rho.
8150  expected_skip = wc.State(rho_COPY_path, { })
8151  # run_and_verify_merge doesn't support merging to a file WCPATH
8152  # so use run_and_verify_svn.
8153  ### TODO: We can use run_and_verify_merge() here now.
8154  svntest.actions.run_and_verify_svn(
8155    expected_merge_output([[3,4],[6]],
8156                          ['U    ' + rho_COPY_path + '\n',
8157                           ' U   ' + rho_COPY_path + '\n',
8158                           ' G   ' + rho_COPY_path + '\n',]),
8159    [], 'merge', '-r2:4', '-c6',
8160    sbox.repo_url + '/A/D/G/rho', rho_COPY_path)
8161
8162  # Check rho's status and props.
8163  expected_status = wc.State(rho_COPY_path,
8164                             {'' : Item(status='MM', wc_rev=6)})
8165  svntest.actions.run_and_verify_status(rho_COPY_path, expected_status)
8166  svntest.actions.run_and_verify_svn(["/A/D/G/rho:3-4,6\n"], [],
8167                                     'propget', SVN_PROP_MERGEINFO,
8168                                     rho_COPY_path)
8169
8170  #Do multiple additive merges to a directory:
8171  # Merge -c6 -c8 into A_COPY/D/H
8172  expected_output = expected_merge_output(
8173    [[6],[8]],
8174    ['U    ' + omega_COPY_path + '\n',
8175     ' U   ' + H_COPY_path + '\n',
8176     ' G   ' + H_COPY_path + '\n',])
8177  svntest.actions.run_and_verify_svn(expected_output,
8178                                     [], 'merge', '-c6', '-c8',
8179                                     sbox.repo_url + '/A/D/H',
8180                                     H_COPY_path)
8181
8182  # Check A_COPY/D/H's status and props.
8183  expected_status = wc.State(H_COPY_path,
8184                             {''     : Item(status=' M', wc_rev=6),
8185                              'psi'  : Item(status='  ', wc_rev=6),
8186                              'chi'  : Item(status='  ', wc_rev=6),
8187                              'omega': Item(status='M ', wc_rev=6),})
8188  svntest.actions.run_and_verify_status(H_COPY_path, expected_status)
8189  svntest.actions.run_and_verify_svn([H_COPY_path + " - /A/D/H:6,8\n"],
8190                                     [], 'propget', '-R', SVN_PROP_MERGEINFO,
8191                                     H_COPY_path)
8192
8193  # Do multiple reverse merges to a directory:
8194  # Merge -c-6 -c-3 into A_COPY
8195  expected_output = expected_merge_output(
8196    [[-3],[-6]],
8197    ['G    ' + omega_COPY_path + '\n',
8198     ' U   ' + A_COPY_path + '\n',
8199     ' U   ' + H_COPY_path + '\n',
8200     ' G   ' + A_COPY_path + '\n',
8201     ' G   ' + H_COPY_path + '\n',],
8202    elides=True)
8203  svntest.actions.run_and_verify_svn(expected_output,
8204                                     [], 'merge', '-c-3', '-c-6',
8205                                     sbox.repo_url + '/A',
8206                                     A_COPY_path)
8207  expected_status = wc.State(A_COPY_path,
8208                             {''          : Item(status='  ', wc_rev=6),
8209                              'B'         : Item(status='  ', wc_rev=6),
8210                              'B/lambda'  : Item(status='  ', wc_rev=6),
8211                              'B/E'       : Item(status='  ', wc_rev=6),
8212                              'B/E/alpha' : Item(status='  ', wc_rev=6),
8213                              'B/E/beta'  : Item(status='  ', wc_rev=6),
8214                              'B/F'       : Item(status='  ', wc_rev=6),
8215                              'mu'        : Item(status='  ', wc_rev=6),
8216                              'C'         : Item(status='  ', wc_rev=6),
8217                              'D'         : Item(status='  ', wc_rev=6),
8218                              'D/gamma'   : Item(status='  ', wc_rev=6),
8219                              'D/G'       : Item(status='  ', wc_rev=6),
8220                              'D/G/pi'    : Item(status='  ', wc_rev=6),
8221                              'D/G/rho'   : Item(status='MM', wc_rev=6),
8222                              'D/G/tau'   : Item(status='  ', wc_rev=6),
8223                              'D/H'       : Item(status=' M', wc_rev=6),
8224                              'D/H/chi'   : Item(status='  ', wc_rev=6),
8225                              'D/H/psi'   : Item(status='  ', wc_rev=6),
8226                              'D/H/omega' : Item(status='  ', wc_rev=6),})
8227  svntest.actions.run_and_verify_status(A_COPY_path, expected_status)
8228  # A_COPY/D/G/rho is untouched by the merge so its mergeinfo
8229  # remains unchanged.
8230  expected_out = H_COPY_path +  " - /A/D/H:8\n|" + \
8231                 rho_COPY_path + " - /A/D/G/rho:3-4,6\n"
8232  # Construct proper regex for '\' infested Windows paths.
8233  if sys.platform == 'win32':
8234    expected_out = expected_out.replace("\\", "\\\\")
8235  svntest.actions.run_and_verify_svn(expected_out, [],
8236                                     'propget', '-R', SVN_PROP_MERGEINFO,
8237                                     A_COPY_path)
8238
8239  # Do both additive and reverse merges to a directory:
8240  # Merge -r2:3 -c-4 -r4:7 to A_COPY/D
8241  expected_output = expected_merge_output(
8242    [[3], [-4], [6,7], [5,7]],
8243    [' U   ' + G_COPY_path     + '\n',
8244     'U    ' + omega_COPY_path + '\n',
8245     'U    ' + psi_COPY_path   + '\n',
8246     ' U   ' + D_COPY_path     + '\n',
8247     ' G   ' + D_COPY_path     + '\n',
8248     ' U   ' + H_COPY_path     + '\n',
8249     ' G   ' + H_COPY_path     + '\n',
8250     'G    ' + rho_COPY_path   + '\n',
8251     ' U   ' + rho_COPY_path   + '\n',
8252     ' G   ' + rho_COPY_path   + '\n'],
8253    elides=True)
8254  svntest.actions.run_and_verify_svn(expected_output, [], 'merge',
8255                                     '-r2:3', '-c-4', '-r4:7',
8256                                     sbox.repo_url + '/A/D',
8257                                     D_COPY_path)
8258  expected_status = wc.State(D_COPY_path,
8259                             {''        : Item(status=' M', wc_rev=6),
8260                              'gamma'   : Item(status='  ', wc_rev=6),
8261                              'G'       : Item(status=' M', wc_rev=6),
8262                              'G/pi'    : Item(status='  ', wc_rev=6),
8263                              'G/rho'   : Item(status='  ', wc_rev=6),
8264                              'G/tau'   : Item(status='  ', wc_rev=6),
8265                              'H'       : Item(status=' M', wc_rev=6),
8266                              'H/chi'   : Item(status='  ', wc_rev=6),
8267                              'H/psi'   : Item(status='M ', wc_rev=6),
8268                              'H/omega' : Item(status='M ', wc_rev=6),})
8269  svntest.actions.run_and_verify_status(D_COPY_path, expected_status)
8270  expected_out = D_COPY_path + " - /A/D:3,5-7\n|" + \
8271    H_COPY_path +  " - /A/D/H:3,5-8\n|" + \
8272    rho_COPY_path + " - /A/D/G/rho:3-4,6\n"
8273  # Construct proper regex for '\' infested Windows paths.
8274  if sys.platform == 'win32':
8275    expected_out = expected_out.replace("\\", "\\\\")
8276  svntest.actions.run_and_verify_svn(expected_out, [],
8277                                     'propget', '-R', SVN_PROP_MERGEINFO,
8278                                     D_COPY_path)
8279
8280#----------------------------------------------------------------------
8281@SkipUnless(server_has_mergeinfo)
8282@Issue(2969)
8283def propchange_of_subdir_raises_conflict(sbox):
8284  "merge of propchange on subdir raises conflict"
8285
8286  ## See https://issues.apache.org/jira/browse/SVN-2969. ##
8287
8288  # Create a WC with a single branch
8289  sbox.build()
8290  wc_dir = sbox.wc_dir
8291  wc_disk, wc_status = set_up_branch(sbox, True, 1)
8292
8293  # Some paths we'll care about
8294  B_url = sbox.repo_url + '/A/B'
8295  E_path = sbox.ospath('A/B/E')
8296  lambda_path = sbox.ospath('A/B/lambda')
8297  A_COPY_B_path = sbox.ospath('A_COPY/B')
8298  A_COPY_B_E_path = sbox.ospath('A_COPY/B/E')
8299  A_COPY_lambda_path = sbox.ospath('A_COPY/B/E/lambda')
8300
8301  # Set a property on A/B/E and Make a modification to A/B/lambda
8302  svntest.main.run_svn(None, 'propset', 'x', 'x', E_path)
8303
8304  svntest.main.file_write(lambda_path, "This is the file 'lambda' modified.\n")
8305  expected_output = wc.State(wc_dir, {
8306    'A/B/lambda' : Item(verb='Sending'),
8307    'A/B/E'      : Item(verb='Sending'),
8308    })
8309  wc_status.add({
8310    'A/B/lambda'     : Item(status='  ', wc_rev=3),
8311    'A/B/E'          : Item(status='  ', wc_rev=3),
8312    })
8313  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
8314                                        wc_status)
8315
8316  # Merge /A/B to /A_COPY/B ie., r1 to r3 with depth files
8317  expected_output = wc.State(A_COPY_B_path, {
8318    'lambda' : Item(status='U '),
8319    })
8320  expected_mergeinfo_output = wc.State(A_COPY_B_path, {
8321    ''       : Item(status=' U'),
8322    'lambda' : Item(status=' U'),
8323    })
8324  expected_elision_output = wc.State(A_COPY_B_path, {
8325    })
8326  expected_disk = wc.State('', {
8327    ''        : Item(props={SVN_PROP_MERGEINFO : '/A/B:2-3*'}),
8328    'lambda'  : Item(contents="This is the file 'lambda' modified.\n",
8329                     props={SVN_PROP_MERGEINFO : '/A/B/lambda:2-3'}),
8330    'F'       : Item(),
8331    'E'       : Item(),
8332    'E/alpha' : Item(contents="This is the file 'alpha'.\n"),
8333    'E/beta'  : Item(contents="This is the file 'beta'.\n"),
8334    })
8335  expected_status = wc.State(A_COPY_B_path, {
8336    ''         : Item(status=' M', wc_rev=2),
8337    'lambda'   : Item(status='MM', wc_rev=2),
8338    'F'        : Item(status='  ', wc_rev=2),
8339    'E'        : Item(status='  ', wc_rev=2),
8340    'E/alpha'  : Item(status='  ', wc_rev=2),
8341    'E/beta'   : Item(status='  ', wc_rev=2),
8342    })
8343  expected_skip = wc.State(A_COPY_B_path, {})
8344
8345  svntest.actions.run_and_verify_merge(A_COPY_B_path, None, None,
8346                                       B_url, None,
8347                                       expected_output,
8348                                       expected_mergeinfo_output,
8349                                       expected_elision_output,
8350                                       expected_disk,
8351                                       expected_status,
8352                                       expected_skip,
8353                                       [], True, True,
8354                                       '--depth', 'files',
8355                                       A_COPY_B_path)
8356
8357  # Merge /A/B to /A_COPY/B ie., r1 to r3 with infinite depth
8358  expected_output = wc.State(A_COPY_B_path, {
8359    'E'       : Item(status=' U'),
8360    })
8361  expected_mergeinfo_output = wc.State(A_COPY_B_path, {
8362    ''       : Item(status=' G'),
8363    'E'      : Item(status=' G'),
8364    })
8365  expected_elision_output = wc.State(A_COPY_B_path, {
8366    'E'      : Item(status=' U'),
8367    'lambda' : Item(status=' U'),
8368    })
8369  expected_disk = wc.State('', {
8370    ''        : Item(props={SVN_PROP_MERGEINFO : '/A/B:2-3'}),
8371    'lambda'  : Item(contents="This is the file 'lambda' modified.\n"),
8372    'F'       : Item(),
8373    'E'       : Item(props={'x': 'x'}),
8374    'E/alpha' : Item(contents="This is the file 'alpha'.\n"),
8375    'E/beta'  : Item(contents="This is the file 'beta'.\n"),
8376    })
8377  expected_status = wc.State(A_COPY_B_path, {
8378    ''         : Item(status=' M', wc_rev=2),
8379    'lambda'   : Item(status='M ', wc_rev=2),
8380    'F'        : Item(status='  ', wc_rev=2),
8381    'E'        : Item(status=' M', wc_rev=2),
8382    'E/alpha'  : Item(status='  ', wc_rev=2),
8383    'E/beta'   : Item(status='  ', wc_rev=2),
8384    })
8385  svntest.actions.run_and_verify_merge(A_COPY_B_path, None, None,
8386                                       B_url, None,
8387                                       expected_output,
8388                                       expected_mergeinfo_output,
8389                                       expected_elision_output,
8390                                       expected_disk,
8391                                       expected_status,
8392                                       expected_skip,
8393                                       [], 1, 1)
8394
8395#----------------------------------------------------------------------
8396# Test for issue #2971: Reverse merge of prop add segfaults if
8397# merging to parent of first merge
8398@Issue(2971)
8399@SkipUnless(server_has_mergeinfo)
8400def reverse_merge_prop_add_on_child(sbox):
8401  "reverse merge of prop add on child"
8402
8403  sbox.build()
8404  wc_dir = sbox.wc_dir
8405  wc_disk, wc_status = set_up_branch(sbox, True, 1)
8406
8407  # Some paths we'll care about
8408  G_path = sbox.ospath('A/D/G')
8409  D_COPY_path = sbox.ospath('A_COPY/D')
8410  G_COPY_path = sbox.ospath('A_COPY/D/G')
8411
8412  # Make some prop changes to some dirs.
8413  svntest.actions.run_and_verify_svn(["property 'prop:name' set on '" +
8414                                      G_path + "'\n"], [], 'ps',
8415                                     'prop:name', 'propval', G_path)
8416  expected_output = svntest.wc.State(wc_dir, {'A/D/G': Item(verb='Sending'),})
8417  wc_status.tweak('A/D/G', wc_rev=3)
8418  wc_disk.tweak('A/D/G', props={'prop:name' : 'propval'})
8419
8420  svntest.actions.run_and_verify_commit(wc_dir, expected_output, wc_status)
8421
8422  # Merge -c3's prop add to A_COPY/D/G
8423  expected_output = wc.State(G_COPY_path, {
8424    '' : Item(status=' U')
8425    })
8426  expected_mergeinfo_output = wc.State(G_COPY_path, {
8427    '' : Item(status=' U'),
8428    })
8429  expected_elision_output = wc.State(G_COPY_path, {
8430    })
8431  expected_status = wc.State(G_COPY_path, {
8432    ''    : Item(status=' M', wc_rev=2),
8433    'pi'  : Item(status='  ', wc_rev=2),
8434    'rho' : Item(status='  ', wc_rev=2),
8435    'tau' : Item(status='  ', wc_rev=2),
8436    })
8437  expected_disk = wc.State('', {
8438    ''    : Item(props={SVN_PROP_MERGEINFO : '/A/D/G:3',
8439                        'prop:name' : 'propval'}),
8440    'pi'  : Item("This is the file 'pi'.\n"),
8441    'rho' : Item("This is the file 'rho'.\n"),
8442    'tau' : Item("This is the file 'tau'.\n"),
8443    })
8444  expected_skip = wc.State(G_COPY_path, { })
8445  svntest.actions.run_and_verify_merge(G_COPY_path, '2', '3',
8446                                       sbox.repo_url + '/A/D/G', None,
8447                                       expected_output,
8448                                       expected_mergeinfo_output,
8449                                       expected_elision_output,
8450                                       expected_disk,
8451                                       expected_status,
8452                                       expected_skip,
8453                                       check_props=True)
8454
8455  # Now merge -c-3 but target the previous target's parent instead.
8456  expected_output = wc.State(D_COPY_path, {
8457    'G' : Item(status=' G'),
8458    })
8459  expected_mergeinfo_output = wc.State(D_COPY_path, {
8460    ''  : Item(status=' U'),
8461    'G' : Item(status=' G'),
8462    })
8463  expected_elision_output = wc.State(D_COPY_path, {
8464    ''  : Item(status=' U'),
8465    'G' : Item(status=' U'),
8466    })
8467  expected_status = wc.State(D_COPY_path, {
8468    ''        : Item(status='  ', wc_rev=2),
8469    'G'       : Item(status='  ', wc_rev=2),
8470    'G/pi'    : Item(status='  ', wc_rev=2),
8471    'G/rho'   : Item(status='  ', wc_rev=2),
8472    'G/tau'   : Item(status='  ', wc_rev=2),
8473    'H'       : Item(status='  ', wc_rev=2),
8474    'H/chi'   : Item(status='  ', wc_rev=2),
8475    'H/psi'   : Item(status='  ', wc_rev=2),
8476    'H/omega' : Item(status='  ', wc_rev=2),
8477    'gamma'   : Item(status='  ', wc_rev=2),
8478    })
8479  expected_disk = wc.State('', {
8480    'G'       : Item(),
8481    'G/pi'    : Item("This is the file 'pi'.\n"),
8482    'G/rho'   : Item("This is the file 'rho'.\n"),
8483    'G/tau'   : Item("This is the file 'tau'.\n"),
8484    'H'       : Item(),
8485    'H/chi'   : Item("This is the file 'chi'.\n"),
8486    'H/psi'   : Item("This is the file 'psi'.\n"),
8487    'H/omega' : Item("This is the file 'omega'.\n"),
8488    'gamma'   : Item("This is the file 'gamma'.\n")
8489    })
8490  expected_skip = wc.State(D_COPY_path, { })
8491  svntest.actions.run_and_verify_merge(D_COPY_path, '3', '2',
8492                                       sbox.repo_url + '/A/D', None,
8493                                       expected_output,
8494                                       expected_mergeinfo_output,
8495                                       expected_elision_output,
8496                                       expected_disk,
8497                                       expected_status,
8498                                       expected_skip,
8499                                       check_props=True)
8500
8501#----------------------------------------------------------------------
8502@XFail()
8503@Issues(2970,3642)
8504def merge_target_with_non_inheritable_mergeinfo(sbox):
8505  "merge target with non inheritable mergeinfo"
8506
8507  ## See https://issues.apache.org/jira/browse/SVN-2970. ##
8508
8509  # Create a WC with a single branch
8510  sbox.build()
8511  wc_dir = sbox.wc_dir
8512  wc_disk, wc_status = set_up_branch(sbox, True, 1)
8513
8514  # Some paths we'll care about
8515  B_url = sbox.repo_url + '/A/B'
8516  lambda_path = sbox.ospath('A/B/lambda')
8517  newfile_path = sbox.ospath('A/B/E/newfile')
8518  A_COPY_B_path = sbox.ospath('A_COPY/B')
8519
8520  # Make a modifications to A/B/lambda and add A/B/E/newfile
8521  svntest.main.file_write(lambda_path, "This is the file 'lambda' modified.\n")
8522  svntest.main.file_write(newfile_path, "This is the file 'newfile'.\n")
8523  svntest.actions.run_and_verify_svn(None, [], 'add', newfile_path)
8524  expected_output = wc.State(wc_dir, {
8525    'A/B/lambda'    : Item(verb='Sending'),
8526    'A/B/E/newfile' : Item(verb='Adding'),
8527    })
8528  wc_status.add({
8529    'A/B/lambda'     : Item(status='  ', wc_rev=3),
8530    'A/B/E/newfile'  : Item(status='  ', wc_rev=3),
8531    })
8532  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
8533                                        wc_status)
8534
8535  # Merge /A/B to /A_COPY/B ie., r1 to r3 with depth immediates
8536  expected_output = wc.State(A_COPY_B_path, {
8537    'lambda' : Item(status='U '),
8538    })
8539  # Issue #3642 https://issues.apache.org/jira/browse/SVN-3642
8540  #
8541  # We don't expect A_COPY/B/F to have mergeinfo recorded on it because
8542  # not only is it unaffected by the merge at depth immediates, it could
8543  # never be affected by the merge, regardless of depth.
8544  expected_mergeinfo_output = wc.State(A_COPY_B_path, {
8545    ''   : Item(status=' U'),
8546    'E'  : Item(status=' U'),
8547    })
8548  expected_elision_output = wc.State(A_COPY_B_path, {
8549    })
8550  expected_disk = wc.State('', {
8551    ''        : Item(props={SVN_PROP_MERGEINFO : '/A/B:2-3'}),
8552    'lambda'  : Item(contents="This is the file 'lambda' modified.\n"),
8553    'F'       : Item(), # No mergeinfo!
8554    'E'       : Item(props={SVN_PROP_MERGEINFO : '/A/B/E:2-3*'}),
8555    'E/alpha' : Item(contents="This is the file 'alpha'.\n"),
8556    'E/beta'  : Item(contents="This is the file 'beta'.\n"),
8557    })
8558  expected_status = wc.State(A_COPY_B_path, {
8559    ''         : Item(status=' M', wc_rev=2),
8560    'lambda'   : Item(status='M ', wc_rev=2),
8561    'F'        : Item(status='  ', wc_rev=2),
8562    'E'        : Item(status=' M', wc_rev=2),
8563    'E/alpha'  : Item(status='  ', wc_rev=2),
8564    'E/beta'   : Item(status='  ', wc_rev=2),
8565    })
8566  expected_skip = wc.State(A_COPY_B_path, {})
8567
8568  svntest.actions.run_and_verify_merge(A_COPY_B_path, None, None,
8569                                       B_url, None,
8570                                       expected_output,
8571                                       expected_mergeinfo_output,
8572                                       expected_elision_output,
8573                                       expected_disk,
8574                                       expected_status,
8575                                       expected_skip,
8576                                       [], True, True,
8577                                       '--depth', 'immediates',
8578                                       A_COPY_B_path)
8579
8580  # Merge /A/B to /A_COPY/B ie., r1 to r3 with infinite depth
8581  expected_output = wc.State(A_COPY_B_path, {
8582    'E/newfile'     : Item(status='A '),
8583    })
8584  expected_mergeinfo_output = wc.State(A_COPY_B_path, {
8585    ''  : Item(status=' G'),
8586    })
8587  expected_elision_output = wc.State(A_COPY_B_path, {
8588    })
8589  expected_disk = wc.State('', {
8590    ''          : Item(props={SVN_PROP_MERGEINFO : '/A/B:2-3'}),
8591    'lambda'    : Item(contents="This is the file 'lambda' modified.\n"),
8592    'F'         : Item(),
8593    'E'         : Item(),
8594    'E/alpha'   : Item(contents="This is the file 'alpha'.\n"),
8595    'E/beta'    : Item(contents="This is the file 'beta'.\n"),
8596    'E/newfile' : Item(contents="This is the file 'newfile'.\n"),
8597    })
8598  expected_status = wc.State(A_COPY_B_path, {
8599    ''            : Item(status=' M', wc_rev=2),
8600    'lambda'      : Item(status='M ', wc_rev=2),
8601    'F'           : Item(status='  ', wc_rev=2),
8602    'E'           : Item(status='  ', wc_rev=2),
8603    'E/alpha'     : Item(status='  ', wc_rev=2),
8604    'E/beta'      : Item(status='  ', wc_rev=2),
8605    'E/newfile'   : Item(status='A ', wc_rev=2),
8606    })
8607
8608  svntest.actions.run_and_verify_merge(A_COPY_B_path, None, None,
8609                                       B_url, None,
8610                                       expected_output,
8611                                       expected_mergeinfo_output,
8612                                       expected_elision_output,
8613                                       expected_disk,
8614                                       expected_status,
8615                                       expected_skip,
8616                                       [], True, True)
8617
8618#----------------------------------------------------------------------
8619@SkipUnless(server_has_mergeinfo)
8620def self_reverse_merge(sbox):
8621  "revert a commit on a target"
8622
8623  sbox.build()
8624  wc_dir = sbox.wc_dir
8625
8626  # Make changes to the working copy
8627  mu_path = sbox.ospath('A/mu')
8628  svntest.main.file_append(mu_path, 'appended mu text')
8629
8630  # Created expected output tree for 'svn ci'
8631  expected_output = wc.State(wc_dir, {
8632    'A/mu' : Item(verb='Sending'),
8633    })
8634
8635  # Create expected status tree; all local revisions should be at 1,
8636  # but mu should be at revision 2.
8637  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
8638  expected_status.tweak('A/mu', wc_rev=2)
8639
8640  svntest.actions.run_and_verify_commit(wc_dir,
8641                                        expected_output,
8642                                        expected_status)
8643
8644  # update to HEAD so that the to-be-undone revision is found in the
8645  # implicit mergeinfo (the natural history) of the target.
8646  svntest.actions.run_and_verify_svn(None, [], 'update', wc_dir)
8647
8648  expected_output = wc.State(wc_dir, {
8649    'A/mu' : Item(status='U ')
8650    })
8651  expected_mergeinfo_output = wc.State(wc_dir, {
8652    '' : Item(status=' U'),
8653    })
8654  expected_elision_output = wc.State(wc_dir, {
8655    '' : Item(status=' U'),
8656    })
8657  expected_skip = wc.State(wc_dir, { })
8658  expected_disk = svntest.main.greek_state.copy()
8659  expected_status = svntest.actions.get_virginal_state(wc_dir, 2)
8660  expected_status.tweak('A/mu', status='M ')
8661  svntest.actions.run_and_verify_merge(wc_dir, '2', '1', sbox.repo_url,
8662                                       None, expected_output,
8663                                       expected_mergeinfo_output,
8664                                       expected_elision_output,
8665                                       expected_disk,
8666                                       expected_status, expected_skip,
8667                                       [], True, True)
8668  svntest.actions.run_and_verify_svn(None, [], 'revert', '-R', wc_dir)
8669
8670  # record dummy self mergeinfo to test the fact that self-reversal should work
8671  # irrespective of mergeinfo.
8672  svntest.actions.run_and_verify_svn(None, [], 'ps', SVN_PROP_MERGEINFO,
8673                                     '/:1', wc_dir)
8674
8675  # Bad svntest.main.greek_state does not have '', so adding it explicitly.
8676  expected_disk.add({'' : Item(props={SVN_PROP_MERGEINFO : '/:1'})})
8677  expected_status = svntest.actions.get_virginal_state(wc_dir, 2)
8678  expected_status.tweak('', status = ' M')
8679  expected_status.tweak('A/mu', status = 'M ')
8680  expected_mergeinfo_output = wc.State(wc_dir, {
8681    '' : Item(status=' G'),
8682    })
8683  expected_elision_output = wc.State(wc_dir, {
8684    })
8685  svntest.actions.run_and_verify_merge(wc_dir, '2', '1', sbox.repo_url,
8686                                       None, expected_output,
8687                                       expected_mergeinfo_output,
8688                                       expected_elision_output,
8689                                       expected_disk,
8690                                       expected_status, expected_skip,
8691                                       [], True, True)
8692
8693#----------------------------------------------------------------------
8694@SkipUnless(server_has_mergeinfo)
8695def ignore_ancestry_and_mergeinfo(sbox):
8696  "--ignore-ancestry also ignores mergeinfo"
8697
8698  # Create a WC with a single branch
8699  sbox.build()
8700  wc_dir = sbox.wc_dir
8701  wc_disk, wc_status = set_up_branch(sbox, True, 1)
8702
8703  # Some paths we'll care about
8704  A_B_url = sbox.repo_url + '/A/B'
8705  A_COPY_B_path = sbox.ospath('A_COPY/B')
8706  lambda_path = sbox.ospath('A/B/lambda')
8707  A_COPY_lambda_path = sbox.ospath('A_COPY/B/lambda')
8708
8709  # Make modifications to A/B/lambda
8710  svntest.main.file_write(lambda_path, "This is the file 'lambda' modified.\n")
8711  expected_output = wc.State(wc_dir, {
8712    'A/B/lambda'    : Item(verb='Sending'),
8713    })
8714  wc_status.add({
8715    'A/B/lambda'     : Item(status='  ', wc_rev=3),
8716    })
8717  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
8718                                        wc_status)
8719
8720  svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir)
8721
8722  # Merge /A/B to /A_COPY/B ie., r1 to r3 with depth immediates
8723  expected_output = wc.State(A_COPY_B_path, {
8724    'lambda' : Item(status='U '),
8725    })
8726  expected_mergeinfo_output = wc.State(A_COPY_B_path, {
8727    '' : Item(status=' U'),
8728    })
8729  expected_elision_output = wc.State(A_COPY_B_path, {
8730    })
8731  expected_disk = wc.State('', {
8732    ''        : Item(props={SVN_PROP_MERGEINFO : '/A/B:2-3'}),
8733    'lambda'  : Item(contents="This is the file 'lambda' modified.\n"),
8734    'F'       : Item(props={}),
8735    'E'       : Item(props={}),
8736    'E/alpha' : Item(contents="This is the file 'alpha'.\n"),
8737    'E/beta'  : Item(contents="This is the file 'beta'.\n"),
8738    })
8739  expected_status = wc.State(A_COPY_B_path, {
8740    ''         : Item(status=' M', wc_rev=3),
8741    'lambda'   : Item(status='M ', wc_rev=3),
8742    'F'        : Item(status='  ', wc_rev=3),
8743    'E'        : Item(status='  ', wc_rev=3),
8744    'E/alpha'  : Item(status='  ', wc_rev=3),
8745    'E/beta'   : Item(status='  ', wc_rev=3),
8746    })
8747  expected_skip = wc.State(A_COPY_B_path, {})
8748
8749  svntest.actions.run_and_verify_merge(A_COPY_B_path, 1, 3,
8750                                       A_B_url, None,
8751                                       expected_output,
8752                                       expected_mergeinfo_output,
8753                                       expected_elision_output,
8754                                       expected_disk,
8755                                       expected_status,
8756                                       expected_skip,
8757                                       [], True, True)
8758
8759  # Now, revert lambda and repeat the merge.  Nothing should happen.
8760  svntest.actions.run_and_verify_svn(None, [], 'revert', '-R',
8761                                     A_COPY_lambda_path)
8762  expected_output.remove('lambda')
8763  expected_disk.tweak('lambda', contents="This is the file 'lambda'.\n")
8764  expected_status.tweak('lambda', status='  ')
8765  expected_mergeinfo_output = wc.State(A_COPY_B_path, {
8766    '' : Item(status=' G'),
8767    })
8768  svntest.actions.run_and_verify_merge(A_COPY_B_path, 1, 3,
8769                                       A_B_url, None,
8770                                       expected_output,
8771                                       expected_mergeinfo_output,
8772                                       expected_elision_output,
8773                                       expected_disk,
8774                                       expected_status,
8775                                       expected_skip,
8776                                       [], True, True)
8777
8778  # Now, try the merge again with --ignore-ancestry.  We should get
8779  # lambda re-modified. */
8780  expected_output = wc.State(A_COPY_B_path, {
8781    'lambda' : Item(status='U '),
8782    })
8783  expected_mergeinfo_output = wc.State(A_COPY_B_path, {})
8784  expected_elision_output = wc.State(A_COPY_B_path, {
8785    })
8786  expected_disk.tweak('lambda',
8787                      contents="This is the file 'lambda' modified.\n")
8788  expected_status.tweak('lambda', status='M ')
8789  svntest.actions.run_and_verify_merge(A_COPY_B_path, 1, 3,
8790                                       A_B_url, None,
8791                                       expected_output,
8792                                       expected_mergeinfo_output,
8793                                       expected_elision_output,
8794                                       expected_disk,
8795                                       expected_status,
8796                                       expected_skip,
8797                                       [], True, True,
8798                                       '--ignore-ancestry', A_COPY_B_path)
8799
8800#----------------------------------------------------------------------
8801@SkipUnless(server_has_mergeinfo)
8802@Issue(3032)
8803def merge_from_renamed_branch_fails_while_avoiding_repeat_merge(sbox):
8804  "merge from renamed branch"
8805  #Copy A/C to A/COPY_C results in r2.
8806  #Rename A/COPY_C to A/RENAMED_C results in r3.
8807  #Add A/RENAMED_C/file1 and commit, results in r4.
8808  #Change A/RENAMED_C/file1 and commit, results in r5.
8809  #Merge r4 from A/RENAMED_C to A/C
8810  #Merge r2:5 from A/RENAMED_C to A/C <-- This fails tracked via #3032.
8811
8812  ## See https://issues.apache.org/jira/browse/SVN-3032. ##
8813
8814  # Create a WC with a single branch
8815  sbox.build()
8816  wc_dir = sbox.wc_dir
8817  # Some paths we'll care about
8818  A_C_url = sbox.repo_url + '/A/C'
8819  A_COPY_C_url = sbox.repo_url + '/A/COPY_C'
8820  A_RENAMED_C_url = sbox.repo_url + '/A/RENAMED_C'
8821  A_C_path = sbox.ospath('A/C')
8822  A_RENAMED_C_path = sbox.ospath('A/RENAMED_C')
8823  A_RENAMED_C_file1_path = sbox.ospath('A/RENAMED_C/file1')
8824
8825  svntest.main.run_svn(None, 'cp', A_C_url, A_COPY_C_url, '-m', 'copy...')
8826  svntest.main.run_svn(None, 'mv', A_COPY_C_url, A_RENAMED_C_url, '-m',
8827                       'rename...')
8828  svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir)
8829
8830  svntest.main.file_write(A_RENAMED_C_file1_path, "This is the file1.\n")
8831  svntest.main.run_svn(None, 'add', A_RENAMED_C_file1_path)
8832  expected_output = wc.State(A_RENAMED_C_path, {
8833    'file1'    : Item(verb='Adding'),
8834    })
8835  expected_status = wc.State(A_RENAMED_C_path, {
8836    ''        : Item(status='  ', wc_rev=3),
8837    'file1'   : Item(status='  ', wc_rev=4),
8838    })
8839  svntest.actions.run_and_verify_commit(A_RENAMED_C_path, expected_output,
8840                                        expected_status)
8841  svntest.main.file_write(A_RENAMED_C_file1_path,
8842                          "This is the file1 modified.\n")
8843  expected_output = wc.State(A_RENAMED_C_path, {
8844    'file1'    : Item(verb='Sending'),
8845    })
8846  expected_status.tweak('file1', wc_rev=5)
8847  svntest.actions.run_and_verify_commit(A_RENAMED_C_path, expected_output,
8848                                        expected_status)
8849
8850  expected_skip = wc.State(A_C_path, {})
8851  expected_output = wc.State(A_C_path, {
8852    'file1'    : Item(status='A '),
8853    })
8854  expected_mergeinfo_output = wc.State(A_C_path, {
8855    '' : Item(status=' U'),
8856    })
8857  expected_elision_output = wc.State(A_C_path, {
8858    })
8859  expected_disk = wc.State('', {
8860    ''       : Item(props={SVN_PROP_MERGEINFO : '/A/RENAMED_C:4'}),
8861    'file1'  : Item("This is the file1.\n"),
8862    })
8863  expected_status = wc.State(A_C_path, {
8864    ''        : Item(status=' M', wc_rev=3),
8865    'file1'   : Item(status='A ', wc_rev='-', copied='+'),
8866    })
8867  svntest.actions.run_and_verify_merge(A_C_path, 3, 4,
8868                                       A_RENAMED_C_url, None,
8869                                       expected_output,
8870                                       expected_mergeinfo_output,
8871                                       expected_elision_output,
8872                                       expected_disk,
8873                                       expected_status,
8874                                       expected_skip,
8875                                       [], True, True)
8876
8877  expected_output = wc.State(A_C_path, {
8878    'file1'    : Item(status='U '),
8879    })
8880  expected_mergeinfo_output = wc.State(A_C_path, {
8881    '' : Item(status=' G'),
8882    })
8883  expected_disk = wc.State('', {
8884    ''       : Item(props={SVN_PROP_MERGEINFO : '/A/RENAMED_C:3-5'}),
8885    'file1'  : Item("This is the file1 modified.\n"),
8886    })
8887  expected_status = wc.State(A_C_path, {
8888    ''        : Item(status=' M', wc_rev=3),
8889    'file1'   : Item(status='A ', wc_rev='-', copied='+'),
8890    })
8891  svntest.actions.run_and_verify_merge(A_C_path, 2, 5,
8892                                       A_RENAMED_C_url, None,
8893                                       expected_output,
8894                                       expected_mergeinfo_output,
8895                                       expected_elision_output,
8896                                       expected_disk,
8897                                       expected_status,
8898                                       expected_skip,
8899                                       [], True, True)
8900
8901#----------------------------------------------------------------------
8902# Test for part of issue #2877: 'do subtree merge only if subtree has
8903# explicit mergeinfo set and exists in the merge source'
8904@SkipUnless(server_has_mergeinfo)
8905@Issue(2877)
8906def merge_source_normalization_and_subtree_merges(sbox):
8907  "normalized mergeinfo is recorded on subtrees"
8908
8909  sbox.build()
8910  wc_dir = sbox.wc_dir
8911
8912  # Some paths we'll care about
8913  D_COPY_path = sbox.ospath('A_COPY/D')
8914  G_COPY_path = sbox.ospath('A_COPY/D/G')
8915
8916  # Use our helper to copy 'A' to 'A_COPY' then make some changes under 'A'
8917  wc_disk, wc_status = set_up_branch(sbox)
8918
8919  # r7 - Move A to A_MOVED
8920  svntest.actions.run_and_verify_svn(['Committing transaction...\n',
8921                                            'Committed revision 7.\n'],
8922                                     [], 'mv', '-m', 'mv A to A_MOVED',
8923                                     sbox.repo_url + '/A',
8924                                     sbox.repo_url + '/A_MOVED')
8925  wc_status.add({
8926      'A_MOVED/B'         : Item(),
8927      'A_MOVED/B/lambda'  : Item(),
8928      'A_MOVED/B/E'       : Item(),
8929      'A_MOVED/B/E/alpha' : Item(),
8930      'A_MOVED/B/E/beta'  : Item(),
8931      'A_MOVED/B/F'       : Item(),
8932      'A_MOVED/mu'        : Item(),
8933      'A_MOVED/C'         : Item(),
8934      'A_MOVED/D'         : Item(),
8935      'A_MOVED/D/gamma'   : Item(),
8936      'A_MOVED/D/G'       : Item(),
8937      'A_MOVED/D/G/pi'    : Item(),
8938      'A_MOVED/D/G/rho'   : Item(),
8939      'A_MOVED/D/G/tau'   : Item(),
8940      'A_MOVED/D/H'       : Item(),
8941      'A_MOVED/D/H/chi'   : Item(),
8942      'A_MOVED/D/H/omega' : Item(),
8943      'A_MOVED/D/H/psi'   : Item(),
8944      'A_MOVED'           : Item()})
8945  wc_status.remove('A', 'A/B', 'A/B/lambda', 'A/B/E', 'A/B/E/alpha',
8946                   'A/B/E/beta', 'A/B/F', 'A/mu', 'A/C', 'A/D',
8947                   'A/D/gamma', 'A/D/G', 'A/D/G/pi', 'A/D/G/rho',
8948                   'A/D/G/tau' , 'A/D/H', 'A/D/H/chi', 'A/D/H/omega',
8949                   'A/D/H/psi')
8950  wc_status.tweak(status='  ', wc_rev=7)
8951
8952  # Update the WC
8953  svntest.actions.run_and_verify_svn(None, [],
8954                                     'update', wc_dir)
8955
8956  # r8 - Make a text mod to 'A_MOVED/D/G/tau'
8957  svntest.main.file_write(sbox.ospath('A_MOVED/D/G/tau'),
8958                          "New content")
8959  expected_output = wc.State(wc_dir,
8960                             {'A_MOVED/D/G/tau' : Item(verb='Sending')})
8961  wc_status.tweak('A_MOVED/D/G/tau', status='  ', wc_rev=8)
8962  svntest.actions.run_and_verify_commit(wc_dir, expected_output, wc_status)
8963
8964  # Merge -c4 URL/A_MOVED/D/G A_COPY/D/G.
8965  #
8966  # A_MOVED/D/G doesn't exist at r3:4, it's still A/D/G,
8967  # so the merge source normalization logic should set
8968  # mergeinfo of '/A/D/G:4' on A_COPY/D/G, *not* 'A_MOVED/D/G:4',
8969  # see issue #2953.
8970  expected_output = wc.State(G_COPY_path, {
8971    'rho' : Item(status='U ')
8972    })
8973  expected_mergeinfo_output = wc.State(G_COPY_path, {
8974    '' : Item(status=' U'),
8975    })
8976  expected_elision_output = wc.State(G_COPY_path, {
8977    })
8978  expected_status = wc.State(G_COPY_path, {
8979    ''    : Item(status=' M', wc_rev=7),
8980    'pi'  : Item(status='  ', wc_rev=7),
8981    'rho' : Item(status='M ', wc_rev=7),
8982    'tau' : Item(status='  ', wc_rev=7),
8983    })
8984  expected_disk = wc.State('', {
8985    ''    : Item(props={SVN_PROP_MERGEINFO : '/A/D/G:4'}),
8986    'pi'  : Item("This is the file 'pi'.\n"),
8987    'rho' : Item("New content"),
8988    'tau' : Item("This is the file 'tau'.\n"),
8989    })
8990  expected_skip = wc.State(G_COPY_path, { })
8991  svntest.actions.run_and_verify_merge(G_COPY_path, '3', '4',
8992                                       sbox.repo_url + '/A_MOVED/D/G',
8993                                       None,
8994                                       expected_output,
8995                                       expected_mergeinfo_output,
8996                                       expected_elision_output,
8997                                       expected_disk,
8998                                       expected_status,
8999                                       expected_skip,
9000                                       check_props=True)
9001
9002  # Merge -c8 URL/A_MOVED/D A_COPY/D.
9003  #
9004  # The merge target A_COPY/D and the subtree at A_COPY/D/G
9005  # should both have their mergeinfo updated with r8
9006  # from A_MOVED_D, see reopened issue #2877.
9007  expected_output = wc.State(D_COPY_path, {
9008    'G/tau' : Item(status='U '),
9009    })
9010  expected_mergeinfo_output = wc.State(D_COPY_path, {
9011    ''  : Item(status=' U'),
9012    'G' : Item(status=' G'),
9013    })
9014  expected_elision_output = wc.State(D_COPY_path, {
9015    })
9016  expected_status = wc.State(D_COPY_path, {
9017    ''        : Item(status=' M', wc_rev=7),
9018    'G'       : Item(status=' M', wc_rev=7),
9019    'G/pi'    : Item(status='  ', wc_rev=7),
9020    'G/rho'   : Item(status='M ', wc_rev=7),
9021    'G/tau'   : Item(status='M ', wc_rev=7),
9022    'H'       : Item(status='  ', wc_rev=7),
9023    'H/chi'   : Item(status='  ', wc_rev=7),
9024    'H/psi'   : Item(status='  ', wc_rev=7),
9025    'H/omega' : Item(status='  ', wc_rev=7),
9026    'gamma'   : Item(status='  ', wc_rev=7),
9027    })
9028  expected_disk = wc.State('', {
9029    ''        : Item(props={SVN_PROP_MERGEINFO : '/A_MOVED/D:8'}),
9030    'G'       : Item(props={SVN_PROP_MERGEINFO :
9031                            '/A/D/G:4\n/A_MOVED/D/G:8'}),
9032    'G/pi'    : Item("This is the file 'pi'.\n"),
9033    'G/rho'   : Item("New content"),
9034    'G/tau'   : Item("New content"),
9035    'H'       : Item(),
9036    'H/chi'   : Item("This is the file 'chi'.\n"),
9037    'H/psi'   : Item("This is the file 'psi'.\n"),
9038    'H/omega' : Item("This is the file 'omega'.\n"),
9039    'gamma'   : Item("This is the file 'gamma'.\n")
9040    })
9041  expected_skip = wc.State(D_COPY_path, { })
9042  svntest.actions.run_and_verify_merge(D_COPY_path, '7', '8',
9043                                       sbox.repo_url + '/A_MOVED/D',
9044                                       None,
9045                                       expected_output,
9046                                       expected_mergeinfo_output,
9047                                       expected_elision_output,
9048                                       expected_disk,
9049                                       expected_status,
9050                                       expected_skip,
9051                                       check_props=True)
9052
9053#----------------------------------------------------------------------
9054# Tests for issue #3067: 'subtrees with intersecting mergeinfo, that don't
9055# exist at the start of a merge range shouldn't break the merge'
9056@SkipUnless(server_has_mergeinfo)
9057@Issue(3067)
9058def new_subtrees_should_not_break_merge(sbox):
9059  "subtrees added after start of merge range are ok"
9060
9061  sbox.build()
9062  wc_dir = sbox.wc_dir
9063  wc_disk, wc_status = set_up_branch(sbox)
9064
9065  # Some paths we'll care about
9066  A_COPY_path   = sbox.ospath('A_COPY')
9067  D_COPY_path   = sbox.ospath('A_COPY/D')
9068  nu_path       = sbox.ospath('A/D/H/nu')
9069  nu_COPY_path  = sbox.ospath('A_COPY/D/H/nu')
9070  rho_COPY_path = sbox.ospath('A_COPY/D/G/rho')
9071  H_COPY_path   = sbox.ospath('A_COPY/D/H')
9072
9073  # Create 'A/D/H/nu', commit it as r7, make a text mod to it in r8.
9074  svntest.main.file_write(nu_path, "This is the file 'nu'.\n")
9075  svntest.actions.run_and_verify_svn(None, [], 'add', nu_path)
9076  expected_output = wc.State(wc_dir, {'A/D/H/nu' : Item(verb='Adding')})
9077  wc_status.add({'A/D/H/nu' : Item(status='  ', wc_rev=7)})
9078  svntest.actions.run_and_verify_commit(wc_dir, expected_output, wc_status)
9079  svntest.main.file_write(nu_path, "New content")
9080  expected_output = wc.State(wc_dir, {'A/D/H/nu' : Item(verb='Sending')})
9081  wc_status.tweak('A/D/H/nu', wc_rev=8)
9082  svntest.actions.run_and_verify_commit(wc_dir, expected_output, wc_status)
9083
9084  # Merge r7 to A_COPY/D/H, then, so it has it's own explicit mergeinfo,
9085  # then merge r8 to A_COPY/D/H/nu so it too has explicit mergeinfo.
9086  expected_output = wc.State(H_COPY_path, {
9087    'nu'    : Item(status='A '),
9088    })
9089  expected_mergeinfo_output = wc.State(H_COPY_path, {
9090    '' : Item(status=' U'),
9091    })
9092  expected_elision_output = wc.State(H_COPY_path, {
9093    })
9094  expected_status = wc.State(H_COPY_path, {
9095    ''      : Item(status=' M', wc_rev=2),
9096    'psi'   : Item(status='  ', wc_rev=2),
9097    'omega' : Item(status='  ', wc_rev=2),
9098    'chi'   : Item(status='  ', wc_rev=2),
9099    'nu'    : Item(status='A ', copied='+', wc_rev='-'),
9100    })
9101  expected_disk = wc.State('', {
9102    ''      : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:7'}),
9103    'psi'   : Item("This is the file 'psi'.\n"),
9104    'omega' : Item("This is the file 'omega'.\n"),
9105    'chi'   : Item("This is the file 'chi'.\n"),
9106    'nu'    : Item("This is the file 'nu'.\n"),
9107    })
9108  expected_skip = wc.State(H_COPY_path, {})
9109  svntest.actions.run_and_verify_merge(H_COPY_path, '6', '7',
9110                                       sbox.repo_url + '/A/D/H', None,
9111                                       expected_output,
9112                                       expected_mergeinfo_output,
9113                                       expected_elision_output,
9114                                       expected_disk,
9115                                       expected_status, expected_skip,
9116                                       check_props=True)
9117  # run_and_verify_merge doesn't support merging to a file WCPATH
9118  # so use run_and_verify_svn.
9119  ### TODO: We can use run_and_verify_merge() here now.
9120  svntest.actions.run_and_verify_svn(
9121    expected_merge_output([[8]],
9122                          ['U    ' + nu_COPY_path + '\n',
9123                           ' G   ' + nu_COPY_path + '\n']),
9124    [], 'merge', '-c8', '--allow-mixed-revisions',
9125    sbox.repo_url + '/A/D/H/nu', nu_COPY_path)
9126
9127  # Merge -r4:6 to A_COPY, then reverse merge r6 from A_COPY/D.
9128  expected_output = wc.State(A_COPY_path, {
9129    'B/E/beta' : Item(status='U '),
9130    'D/H/omega': Item(status='U '),
9131    })
9132  expected_mergeinfo_output = wc.State(A_COPY_path, {
9133    ''       : Item(status=' U'),
9134    'D/H'    : Item(status=' G'),
9135    })
9136  expected_elision_output = wc.State(A_COPY_path, {
9137    })
9138  expected_status = wc.State(A_COPY_path, {
9139    ''          : Item(status=' M', wc_rev=2),
9140    'B'         : Item(status='  ', wc_rev=2),
9141    'mu'        : Item(status='  ', wc_rev=2),
9142    'B/E'       : Item(status='  ', wc_rev=2),
9143    'B/E/alpha' : Item(status='  ', wc_rev=2),
9144    'B/E/beta'  : Item(status='M ', wc_rev=2),
9145    'B/lambda'  : Item(status='  ', wc_rev=2),
9146    'B/F'       : Item(status='  ', wc_rev=2),
9147    'C'         : Item(status='  ', wc_rev=2),
9148    'D'         : Item(status='  ', wc_rev=2),
9149    'D/G'       : Item(status='  ', wc_rev=2),
9150    'D/G/pi'    : Item(status='  ', wc_rev=2),
9151    'D/G/rho'   : Item(status='  ', wc_rev=2),
9152    'D/G/tau'   : Item(status='  ', wc_rev=2),
9153    'D/gamma'   : Item(status='  ', wc_rev=2),
9154    'D/H'       : Item(status=' M', wc_rev=2),
9155    'D/H/chi'   : Item(status='  ', wc_rev=2),
9156    'D/H/psi'   : Item(status='  ', wc_rev=2),
9157    'D/H/omega' : Item(status='M ', wc_rev=2),
9158    'D/H/nu'    : Item(status='A ', copied='+', wc_rev='-'),
9159    })
9160  expected_disk = wc.State('', {
9161    ''          : Item(props={SVN_PROP_MERGEINFO : '/A:5-6'}),
9162    'B'         : Item(),
9163    'mu'        : Item("This is the file 'mu'.\n"),
9164    'B/E'       : Item(),
9165    'B/E/alpha' : Item("This is the file 'alpha'.\n"),
9166    'B/E/beta'  : Item("New content"),
9167    'B/lambda'  : Item("This is the file 'lambda'.\n"),
9168    'B/F'       : Item(),
9169    'C'         : Item(),
9170    'D'         : Item(),
9171    'D/G'       : Item(),
9172    'D/G/pi'    : Item("This is the file 'pi'.\n"),
9173    'D/G/rho'   : Item("This is the file 'rho'.\n"),
9174    'D/G/tau'   : Item("This is the file 'tau'.\n"),
9175    'D/gamma'   : Item("This is the file 'gamma'.\n"),
9176    'D/H'       : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:5-7'}),
9177    'D/H/chi'   : Item("This is the file 'chi'.\n"),
9178    'D/H/psi'   : Item("This is the file 'psi'.\n"),
9179    'D/H/omega' : Item("New content"),
9180    'D/H/nu'    : Item("New content",
9181                       props={SVN_PROP_MERGEINFO : '/A/D/H/nu:7-8'}),
9182    })
9183  expected_skip = wc.State(A_COPY_path, { })
9184  svntest.actions.run_and_verify_merge(A_COPY_path, '4', '6',
9185                                       sbox.repo_url + '/A', None,
9186                                       expected_output,
9187                                       expected_mergeinfo_output,
9188                                       expected_elision_output,
9189                                       expected_disk,
9190                                       expected_status,
9191                                       expected_skip,
9192                                       check_props=True)
9193  expected_output = wc.State(D_COPY_path, {
9194    'H/omega': Item(status='G '),
9195    })
9196  expected_mergeinfo_output = wc.State(D_COPY_path, {
9197    ''  : Item(status=' G'),
9198    'H' : Item(status=' G'),
9199    })
9200  expected_elision_output = wc.State(D_COPY_path, {
9201    })
9202  expected_status = wc.State(D_COPY_path, {
9203    ''        : Item(status=' M', wc_rev=2),
9204    'G'       : Item(status='  ', wc_rev=2),
9205    'G/pi'    : Item(status='  ', wc_rev=2),
9206    'G/rho'   : Item(status='  ', wc_rev=2),
9207    'G/tau'   : Item(status='  ', wc_rev=2),
9208    'gamma'   : Item(status='  ', wc_rev=2),
9209    'H'       : Item(status=' M', wc_rev=2),
9210    'H/chi'   : Item(status='  ', wc_rev=2),
9211    'H/psi'   : Item(status='  ', wc_rev=2),
9212    'H/omega' : Item(status='  ', wc_rev=2),
9213    'H/nu'    : Item(status='A ', copied='+', wc_rev='-'),
9214    })
9215  expected_disk = wc.State('', {
9216    ''        : Item(props={SVN_PROP_MERGEINFO : '/A/D:5'}),
9217    'G/pi'    : Item("This is the file 'pi'.\n"),
9218    'G/rho'   : Item("This is the file 'rho'.\n"),
9219    'G/tau'   : Item("This is the file 'tau'.\n"),
9220    'gamma'   : Item("This is the file 'gamma'.\n"),
9221    'H'       : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:5,7'}),
9222    'H/chi'   : Item("This is the file 'chi'.\n"),
9223    'H/psi'   : Item("This is the file 'psi'.\n"),
9224    'H/omega' : Item("This is the file 'omega'.\n"),
9225    'H/nu'    : Item("New content",
9226                     props={SVN_PROP_MERGEINFO : '/A/D/H/nu:7-8'}),
9227    })
9228  expected_skip = wc.State(D_COPY_path, { })
9229  svntest.actions.run_and_verify_merge(D_COPY_path, '6', '5',
9230                                       sbox.repo_url + '/A/D', None,
9231                                       expected_output,
9232                                       expected_mergeinfo_output,
9233                                       expected_elision_output,
9234                                       expected_disk,
9235                                       expected_status,
9236                                       expected_skip,
9237                                       check_props=True)
9238  # Now once again merge r6 to A_COPY.  A_COPY already has r6 in its mergeinfo
9239  # so we expect only subtree merges on A_COPY/D, A_COPY_D_H, and
9240  # A_COPY/D/H/nu.  The fact that A/D/H/nu doesn't exist at r6 should not cause
9241  # the merge to fail -- see
9242  # https://issues.apache.org/jira/browse/SVN-3067#desc7.
9243  expected_output = wc.State(A_COPY_path, {
9244    'D/H/omega': Item(status='U '),
9245    })
9246  expected_mergeinfo_output = wc.State(A_COPY_path, {
9247    ''    : Item(status=' G'),
9248    'D'   : Item(status=' G'),
9249    'D/H' : Item(status=' G'),
9250    })
9251  expected_elision_output = wc.State(A_COPY_path, {
9252    'D'   : Item(status=' U'),
9253    })
9254  expected_status = wc.State(A_COPY_path, {
9255    ''          : Item(status=' M', wc_rev=2),
9256    'B'         : Item(status='  ', wc_rev=2),
9257    'mu'        : Item(status='  ', wc_rev=2),
9258    'B/E'       : Item(status='  ', wc_rev=2),
9259    'B/E/alpha' : Item(status='  ', wc_rev=2),
9260    'B/E/beta'  : Item(status='M ', wc_rev=2),
9261    'B/lambda'  : Item(status='  ', wc_rev=2),
9262    'B/F'       : Item(status='  ', wc_rev=2),
9263    'C'         : Item(status='  ', wc_rev=2),
9264    'D'         : Item(status='  ', wc_rev=2),
9265    'D/G'       : Item(status='  ', wc_rev=2),
9266    'D/G/pi'    : Item(status='  ', wc_rev=2),
9267    'D/G/rho'   : Item(status='  ', wc_rev=2),
9268    'D/G/tau'   : Item(status='  ', wc_rev=2),
9269    'D/gamma'   : Item(status='  ', wc_rev=2),
9270    'D/H'       : Item(status=' M', wc_rev=2),
9271    'D/H/chi'   : Item(status='  ', wc_rev=2),
9272    'D/H/psi'   : Item(status='  ', wc_rev=2),
9273    'D/H/omega' : Item(status='M ', wc_rev=2),
9274    'D/H/nu'    : Item(status='A ', copied='+', wc_rev='-'),
9275    })
9276  expected_disk_1 = wc.State('', {
9277    ''          : Item(props={SVN_PROP_MERGEINFO : '/A:5-6'}),
9278    'B'         : Item(),
9279    'mu'        : Item("This is the file 'mu'.\n"),
9280    'B/E'       : Item(),
9281    'B/E/alpha' : Item("This is the file 'alpha'.\n"),
9282    'B/E/beta'  : Item("New content"),
9283    'B/lambda'  : Item("This is the file 'lambda'.\n"),
9284    'B/F'       : Item(),
9285    'C'         : Item(),
9286    'D'         : Item(), # Mergeinfo elides to 'A_COPY'
9287    'D/G'       : Item(),
9288    'D/G/pi'    : Item("This is the file 'pi'.\n"),
9289    'D/G/rho'   : Item("This is the file 'rho'.\n"),
9290    'D/G/tau'   : Item("This is the file 'tau'.\n"),
9291    'D/gamma'   : Item("This is the file 'gamma'.\n"),
9292    'D/H'       : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:5-7'}),
9293    'D/H/chi'   : Item("This is the file 'chi'.\n"),
9294    'D/H/psi'   : Item("This is the file 'psi'.\n"),
9295    'D/H/omega' : Item("New content"),
9296    'D/H/nu'    : Item("New content",
9297                       props={SVN_PROP_MERGEINFO : '/A/D/H/nu:7-8'}),
9298    })
9299  expected_skip = wc.State(A_COPY_path, { })
9300  svntest.actions.run_and_verify_merge(A_COPY_path, '5', '6',
9301                                       sbox.repo_url + '/A', None,
9302                                       expected_output,
9303                                       expected_mergeinfo_output,
9304                                       expected_elision_output,
9305                                       expected_disk_1,
9306                                       expected_status,
9307                                       expected_skip,
9308                                       check_props=True)
9309
9310  # Commit this merge as r9.
9311  #
9312  # Update the wc first to make setting the expected status a bit easier.
9313  svntest.actions.run_and_verify_svn(exp_noop_up_out(8), [],
9314                                     'up', wc_dir)
9315  wc_status.tweak(wc_rev=8)
9316  expected_output = wc.State(wc_dir, {
9317    'A_COPY'           : Item(verb='Sending'),
9318    'A_COPY/B/E/beta'  : Item(verb='Sending'),
9319    'A_COPY/D/H'       : Item(verb='Sending'),
9320    'A_COPY/D/H/nu'    : Item(verb='Adding'),
9321    'A_COPY/D/H/omega' : Item(verb='Sending'),
9322    })
9323  wc_status.tweak('A_COPY',
9324                  'A_COPY/B/E/beta',
9325                  'A_COPY/D/H',
9326                  'A_COPY/D/H/omega',
9327                  wc_rev=9)
9328  wc_status.add({'A_COPY/D/H/nu' : Item(status='  ', wc_rev=9)})
9329  svntest.actions.run_and_verify_commit(wc_dir, expected_output, wc_status)
9330  # Update the WC.
9331  svntest.actions.run_and_verify_svn(exp_noop_up_out(9), [],
9332                                     'up', wc_dir)
9333  wc_status.tweak(wc_rev=9)
9334
9335  # Yet another test for issue #3067.  Merge -rX:Y, where X>Y (reverse merge)
9336  # and the merge target has a subtree that came into existence at some rev
9337  # N where X < N < Y.  This merge should simply delete the subtree.
9338  #
9339  # For this test merge -r9:2 to A_COPY.  This should revert all the merges
9340  # done thus far, leaving the tree rooted at A_COPY with no explicit
9341  # mergeinfo.
9342  expected_output = wc.State(A_COPY_path, {
9343    'B/E/beta' : Item(status='U '),
9344    'D/H/omega': Item(status='U '),
9345    'D/H/nu'   : Item(status='D '),
9346    })
9347  expected_mergeinfo_output = wc.State(A_COPY_path, {
9348    ''   : Item(status=' U'),
9349    'D/H': Item(status=' U'),
9350    })
9351  expected_elision_output = wc.State(A_COPY_path, {
9352    ''   : Item(status=' U'),
9353    'D/H': Item(status=' U'),
9354    })
9355  expected_status = wc.State(A_COPY_path, {
9356    ''          : Item(status=' M', wc_rev=9),
9357    'B'         : Item(status='  ', wc_rev=9),
9358    'mu'        : Item(status='  ', wc_rev=9),
9359    'B/E'       : Item(status='  ', wc_rev=9),
9360    'B/E/alpha' : Item(status='  ', wc_rev=9),
9361    'B/E/beta'  : Item(status='M ', wc_rev=9),
9362    'B/lambda'  : Item(status='  ', wc_rev=9),
9363    'B/F'       : Item(status='  ', wc_rev=9),
9364    'C'         : Item(status='  ', wc_rev=9),
9365    'D'         : Item(status='  ', wc_rev=9),
9366    'D/G'       : Item(status='  ', wc_rev=9),
9367    'D/G/pi'    : Item(status='  ', wc_rev=9),
9368    'D/G/rho'   : Item(status='  ', wc_rev=9),
9369    'D/G/tau'   : Item(status='  ', wc_rev=9),
9370    'D/gamma'   : Item(status='  ', wc_rev=9),
9371    'D/H'       : Item(status=' M', wc_rev=9),
9372    'D/H/chi'   : Item(status='  ', wc_rev=9),
9373    'D/H/psi'   : Item(status='  ', wc_rev=9),
9374    'D/H/omega' : Item(status='M ', wc_rev=9),
9375    'D/H/nu'    : Item(status='D ', wc_rev=9),
9376    })
9377  expected_disk = wc.State('', {
9378    'B'         : Item(),
9379    'mu'        : Item("This is the file 'mu'.\n"),
9380    'B/E'       : Item(),
9381    'B/E/alpha' : Item("This is the file 'alpha'.\n"),
9382    'B/E/beta'  : Item("This is the file 'beta'.\n"),
9383    'B/lambda'  : Item("This is the file 'lambda'.\n"),
9384    'B/F'       : Item(),
9385    'C'         : Item(),
9386    'D'         : Item(),
9387    'D/G'       : Item(),
9388    'D/G/pi'    : Item("This is the file 'pi'.\n"),
9389    'D/G/rho'   : Item("This is the file 'rho'.\n"),
9390    'D/G/tau'   : Item("This is the file 'tau'.\n"),
9391    'D/gamma'   : Item("This is the file 'gamma'.\n"),
9392    'D/H'       : Item(),
9393    'D/H/chi'   : Item("This is the file 'chi'.\n"),
9394    'D/H/psi'   : Item("This is the file 'psi'.\n"),
9395    'D/H/omega' : Item("This is the file 'omega'.\n"),
9396    })
9397  expected_skip = wc.State(A_COPY_path, { })
9398  svntest.actions.run_and_verify_merge(A_COPY_path, '9', '2',
9399                                       sbox.repo_url + '/A', None,
9400                                       expected_output,
9401                                       expected_mergeinfo_output,
9402                                       expected_elision_output,
9403                                       expected_disk,
9404                                       expected_status,
9405                                       expected_skip,
9406                                       check_props=True)
9407
9408  # Revert the previous merge, then merge r4 to A_COPY/D/G/rho.  Commit
9409  # this merge as r10.
9410  svntest.actions.run_and_verify_svn(None, [], 'revert', '-R', wc_dir)
9411  svntest.actions.run_and_verify_svn(
9412    expected_merge_output([[4]],
9413                          ['U    ' + rho_COPY_path + '\n',
9414                           ' G   ' + rho_COPY_path + '\n']),
9415    [], 'merge', '-c4', sbox.repo_url + '/A/D/G/rho', rho_COPY_path)
9416  expected_output = wc.State(wc_dir, {
9417    'A_COPY/D/G/rho'    : Item(verb='Sending'),})
9418  wc_status.tweak('A_COPY/D/G/rho', wc_rev=10)
9419  svntest.actions.run_and_verify_commit(wc_dir, expected_output, wc_status)
9420  svntest.actions.run_and_verify_svn(exp_noop_up_out(10), [],
9421                                     'up', wc_dir)
9422  wc_status.tweak(wc_rev=10)
9423
9424  # Yet another test for issue #3067.  Merge -rX:Y, where X>Y (reverse merge)
9425  # and the merge target has a subtree that doesn't exist in the merge source
9426  # between X and Y.  This merge should no effect on that subtree.
9427  #
9428  # Specifically, merge -c4 to A_COPY.  This should revert the previous merge
9429  # of r4 directly to A_COPY/D/G/rho.  The subtree A_COPY/D/H/nu, whose merge
9430  # source A/D/H/nu doesn't in r4:3, shouldn't be affected nor should it break
9431  # the merge editor.
9432  expected_output = wc.State(A_COPY_path, {
9433    'D/G/rho': Item(status='U '),
9434    })
9435  expected_mergeinfo_output = wc.State(A_COPY_path, {
9436    ''        : Item(status=' U'),
9437    'D/G/rho' : Item(status=' U'),
9438    })
9439  expected_elision_output = wc.State(A_COPY_path, {
9440    'D/G/rho' : Item(status=' U'),
9441    })
9442  expected_status = wc.State(A_COPY_path, {
9443    ''          : Item(status='  ', wc_rev=10),
9444    'B'         : Item(status='  ', wc_rev=10),
9445    'mu'        : Item(status='  ', wc_rev=10),
9446    'B/E'       : Item(status='  ', wc_rev=10),
9447    'B/E/alpha' : Item(status='  ', wc_rev=10),
9448    'B/E/beta'  : Item(status='  ', wc_rev=10),
9449    'B/lambda'  : Item(status='  ', wc_rev=10),
9450    'B/F'       : Item(status='  ', wc_rev=10),
9451    'C'         : Item(status='  ', wc_rev=10),
9452    'D'         : Item(status='  ', wc_rev=10),
9453    'D/G'       : Item(status='  ', wc_rev=10),
9454    'D/G/pi'    : Item(status='  ', wc_rev=10),
9455    'D/G/rho'   : Item(status='MM', wc_rev=10),
9456    'D/G/tau'   : Item(status='  ', wc_rev=10),
9457    'D/gamma'   : Item(status='  ', wc_rev=10),
9458    'D/H'       : Item(status='  ', wc_rev=10),
9459    'D/H/chi'   : Item(status='  ', wc_rev=10),
9460    'D/H/psi'   : Item(status='  ', wc_rev=10),
9461    'D/H/omega' : Item(status='  ', wc_rev=10),
9462    'D/H/nu'    : Item(status='  ', wc_rev=10),
9463    })
9464  # Use expected_disk_1 from above since we should be
9465  # returning to that state.
9466  expected_skip = wc.State(A_COPY_path, { })
9467  svntest.actions.run_and_verify_merge(A_COPY_path, '4', '3',
9468                                       sbox.repo_url + '/A', None,
9469                                       expected_output,
9470                                       expected_mergeinfo_output,
9471                                       expected_elision_output,
9472                                       expected_disk_1,
9473                                       expected_status,
9474                                       expected_skip,
9475                                       check_props=True)
9476
9477#----------------------------------------------------------------------
9478@SkipUnless(server_has_mergeinfo)
9479def dont_add_mergeinfo_from_own_history(sbox):
9480  "cyclic merges don't add mergeinfo from own history"
9481
9482  sbox.build()
9483  wc_dir = sbox.wc_dir
9484  wc_disk, wc_status = set_up_branch(sbox)
9485
9486  # Some paths we'll care about
9487  A_path        = sbox.ospath('A')
9488  A_MOVED_path  = sbox.ospath('A_MOVED')
9489  mu_path       = sbox.ospath('A/mu')
9490  mu_MOVED_path = sbox.ospath('A_MOVED/mu')
9491  A_COPY_path   = sbox.ospath('A_COPY')
9492  mu_COPY_path  = sbox.ospath('A_COPY/mu')
9493
9494  # Merge r3 from 'A' to 'A_COPY', make a text mod to 'A_COPY/mu' and
9495  # commit both as r7.  This results in mergeinfo of '/A:3' on 'A_COPY'.
9496  # Then merge r7 from 'A_COPY' to 'A'.  This attempts to add the mergeinfo
9497  # '/A:3' to 'A', but that is self-referrential and should be filtered out,
9498  # leaving only the mergeinfo '/A_COPY:7' on 'A'.
9499  expected_output = wc.State(A_COPY_path, {
9500    'D/H/psi' : Item(status='U '),
9501    })
9502  expected_mergeinfo_output = wc.State(A_COPY_path, {
9503    '' : Item(status=' U'),
9504    })
9505  expected_elision_output = wc.State(A_COPY_path, {
9506    })
9507  expected_A_COPY_status = wc.State(A_COPY_path, {
9508    ''          : Item(status=' M', wc_rev=2),
9509    'B'         : Item(status='  ', wc_rev=2),
9510    'mu'        : Item(status='  ', wc_rev=2),
9511    'B/E'       : Item(status='  ', wc_rev=2),
9512    'B/E/alpha' : Item(status='  ', wc_rev=2),
9513    'B/E/beta'  : Item(status='  ', wc_rev=2),
9514    'B/lambda'  : Item(status='  ', wc_rev=2),
9515    'B/F'       : Item(status='  ', wc_rev=2),
9516    'C'         : Item(status='  ', wc_rev=2),
9517    'D'         : Item(status='  ', wc_rev=2),
9518    'D/G'       : Item(status='  ', wc_rev=2),
9519    'D/G/pi'    : Item(status='  ', wc_rev=2),
9520    'D/G/rho'   : Item(status='  ', wc_rev=2),
9521    'D/G/tau'   : Item(status='  ', wc_rev=2),
9522    'D/gamma'   : Item(status='  ', wc_rev=2),
9523    'D/H'       : Item(status='  ', wc_rev=2),
9524    'D/H/chi'   : Item(status='  ', wc_rev=2),
9525    'D/H/psi'   : Item(status='M ', wc_rev=2),
9526    'D/H/omega' : Item(status='  ', wc_rev=2),
9527    })
9528  expected_A_COPY_disk = wc.State('', {
9529    ''          : Item(props={SVN_PROP_MERGEINFO : '/A:3'}),
9530    'B'         : Item(),
9531    'mu'        : Item("This is the file 'mu'.\n"),
9532    'B/E'       : Item(),
9533    'B/E/alpha' : Item("This is the file 'alpha'.\n"),
9534    'B/E/beta'  : Item("This is the file 'beta'.\n"),
9535    'B/lambda'  : Item("This is the file 'lambda'.\n"),
9536    'B/F'       : Item(),
9537    'C'         : Item(),
9538    'D'         : Item(),
9539    'D/G'       : Item(),
9540    'D/G/pi'    : Item("This is the file 'pi'.\n"),
9541    'D/G/rho'   : Item("This is the file 'rho'.\n"),
9542    'D/G/tau'   : Item("This is the file 'tau'.\n"),
9543    'D/gamma'   : Item("This is the file 'gamma'.\n"),
9544    'D/H'       : Item(),
9545    'D/H/chi'   : Item("This is the file 'chi'.\n"),
9546    'D/H/psi'   : Item("New content"),
9547    'D/H/omega' : Item("This is the file 'omega'.\n"),
9548    })
9549  expected_A_COPY_skip = wc.State(A_COPY_path, { })
9550  svntest.actions.run_and_verify_merge(A_COPY_path, '2', '3',
9551                                       sbox.repo_url + '/A', None,
9552                                       expected_output,
9553                                       expected_mergeinfo_output,
9554                                       expected_elision_output,
9555                                       expected_A_COPY_disk,
9556                                       expected_A_COPY_status,
9557                                       expected_A_COPY_skip,
9558                                       check_props=True)
9559
9560  # Change 'A_COPY/mu'
9561  svntest.main.file_write(mu_COPY_path, "New content")
9562
9563  # Commit r7
9564  expected_output = wc.State(wc_dir, {
9565    'A_COPY'         : Item(verb='Sending'),
9566    'A_COPY/D/H/psi' : Item(verb='Sending'),
9567    'A_COPY/mu'      : Item(verb='Sending'),
9568    })
9569  wc_status.tweak('A_COPY', 'A_COPY/D/H/psi', 'A_COPY/mu', wc_rev=7)
9570  svntest.actions.run_and_verify_commit(wc_dir,
9571                                        expected_output,
9572                                        wc_status)
9573
9574  # Merge r7 back to the 'A'
9575  expected_output = wc.State(A_path, {
9576    'mu' : Item(status='U '),
9577    })
9578  expected_mergeinfo_output = wc.State(A_path, {
9579    '' : Item(status=' U'),
9580    })
9581  expected_elision_output = wc.State(A_path, {
9582    })
9583  expected_A_status = wc.State(A_path, {
9584    ''          : Item(status=' M', wc_rev=1),
9585    'B'         : Item(status='  ', wc_rev=1),
9586    'mu'        : Item(status='M ', wc_rev=1),
9587    'B/E'       : Item(status='  ', wc_rev=1),
9588    'B/E/alpha' : Item(status='  ', wc_rev=1),
9589    'B/E/beta'  : Item(status='  ', wc_rev=5),
9590    'B/lambda'  : Item(status='  ', wc_rev=1),
9591    'B/F'       : Item(status='  ', wc_rev=1),
9592    'C'         : Item(status='  ', wc_rev=1),
9593    'D'         : Item(status='  ', wc_rev=1),
9594    'D/G'       : Item(status='  ', wc_rev=1),
9595    'D/G/pi'    : Item(status='  ', wc_rev=1),
9596    'D/G/rho'   : Item(status='  ', wc_rev=4),
9597    'D/G/tau'   : Item(status='  ', wc_rev=1),
9598    'D/gamma'   : Item(status='  ', wc_rev=1),
9599    'D/H'       : Item(status='  ', wc_rev=1),
9600    'D/H/chi'   : Item(status='  ', wc_rev=1),
9601    'D/H/psi'   : Item(status='  ', wc_rev=3),
9602    'D/H/omega' : Item(status='  ', wc_rev=6),
9603    })
9604  expected_A_disk = wc.State('', {
9605    ''          : Item(props={SVN_PROP_MERGEINFO : '/A_COPY:7'}),
9606    'B'         : Item(),
9607    'mu'        : Item("New content"),
9608    'B/E'       : Item(),
9609    'B/E/alpha' : Item("This is the file 'alpha'.\n"),
9610    'B/E/beta'  : Item("New content"),
9611    'B/lambda'  : Item("This is the file 'lambda'.\n"),
9612    'B/F'       : Item(),
9613    'C'         : Item(),
9614    'D'         : Item(),
9615    'D/G'       : Item(),
9616    'D/G/pi'    : Item("This is the file 'pi'.\n"),
9617    'D/G/rho'   : Item("New content"),
9618    'D/G/tau'   : Item("This is the file 'tau'.\n"),
9619    'D/gamma'   : Item("This is the file 'gamma'.\n"),
9620    'D/H'       : Item(),
9621    'D/H/chi'   : Item("This is the file 'chi'.\n"),
9622    'D/H/psi'   : Item("New content"),
9623    'D/H/omega' : Item("New content"),
9624    })
9625  expected_A_skip = wc.State(A_path, {})
9626  svntest.actions.run_and_verify_merge(A_path, '6', '7',
9627                                       sbox.repo_url + '/A_COPY', None,
9628                                       expected_output,
9629                                       expected_mergeinfo_output,
9630                                       expected_elision_output,
9631                                       expected_A_disk,
9632                                       expected_A_status,
9633                                       expected_A_skip,
9634                                       [], True, False,
9635                                       '--allow-mixed-revisions', A_path)
9636
9637  # Revert all local mods
9638  svntest.actions.run_and_verify_svn(["Reverted '" + A_path + "'\n",
9639                                      "Reverted '" + mu_path + "'\n"],
9640                                     [], 'revert', '-R', wc_dir)
9641
9642  # Move 'A' to 'A_MOVED' and once again merge r7 from 'A_COPY', this time
9643  # to 'A_MOVED'.  This attempts to add the mergeinfo '/A:3' to
9644  # 'A_MOVED', but 'A_MOVED@3' is 'A', so again this mergeinfo is filtered
9645  # out, leaving the only the mergeinfo created from the merge itself:
9646  # '/A_COPY:7'.
9647  svntest.actions.run_and_verify_svn(['Committing transaction...\n',
9648                                      'Committed revision 8.\n'],
9649                                     [], 'move',
9650                                     sbox.repo_url + '/A',
9651                                     sbox.repo_url + '/A_MOVED',
9652                                     '-m', 'Copy A to A_MOVED')
9653  wc_status.remove('A', 'A/B', 'A/B/lambda', 'A/B/E', 'A/B/E/alpha',
9654    'A/B/E/beta', 'A/B/F', 'A/mu', 'A/C', 'A/D', 'A/D/gamma', 'A/D/G',
9655    'A/D/G/pi', 'A/D/G/rho', 'A/D/G/tau', 'A/D/H', 'A/D/H/chi',
9656    'A/D/H/omega', 'A/D/H/psi')
9657  wc_status.add({
9658    'A_MOVED'           : Item(),
9659    'A_MOVED/B'         : Item(),
9660    'A_MOVED/B/lambda'  : Item(),
9661    'A_MOVED/B/E'       : Item(),
9662    'A_MOVED/B/E/alpha' : Item(),
9663    'A_MOVED/B/E/beta'  : Item(),
9664    'A_MOVED/B/F'       : Item(),
9665    'A_MOVED/mu'        : Item(),
9666    'A_MOVED/C'         : Item(),
9667    'A_MOVED/D'         : Item(),
9668    'A_MOVED/D/gamma'   : Item(),
9669    'A_MOVED/D/G'       : Item(),
9670    'A_MOVED/D/G/pi'    : Item(),
9671    'A_MOVED/D/G/rho'   : Item(),
9672    'A_MOVED/D/G/tau'   : Item(),
9673    'A_MOVED/D/H'       : Item(),
9674    'A_MOVED/D/H/chi'   : Item(),
9675    'A_MOVED/D/H/omega' : Item(),
9676    'A_MOVED/D/H/psi'   : Item(),
9677    })
9678  wc_status.tweak(wc_rev=8, status='  ')
9679  wc_disk.remove('A', 'A/B', 'A/B/lambda', 'A/B/E', 'A/B/E/alpha',
9680    'A/B/E/beta', 'A/B/F', 'A/mu', 'A/C', 'A/D', 'A/D/gamma',
9681    'A/D/G', 'A/D/G/pi', 'A/D/G/rho', 'A/D/G/tau', 'A/D/H',
9682    'A/D/H/chi', 'A/D/H/omega', 'A/D/H/psi'    )
9683  wc_disk.add({
9684    'A_MOVED'           : Item(),
9685    'A_MOVED/B'         : Item(),
9686    'A_MOVED/B/lambda'  : Item("This is the file 'lambda'.\n"),
9687    'A_MOVED/B/E'       : Item(),
9688    'A_MOVED/B/E/alpha' : Item("This is the file 'alpha'.\n"),
9689    'A_MOVED/B/E/beta'  : Item("New content"),
9690    'A_MOVED/B/F'       : Item(),
9691    'A_MOVED/mu'        : Item("This is the file 'mu'.\n"),
9692    'A_MOVED/C'         : Item(),
9693    'A_MOVED/D'         : Item(),
9694    'A_MOVED/D/gamma'   : Item("This is the file 'gamma'.\n"),
9695    'A_MOVED/D/G'       : Item(),
9696    'A_MOVED/D/G/pi'    : Item("This is the file 'pi'.\n"),
9697    'A_MOVED/D/G/rho'   : Item("New content"),
9698    'A_MOVED/D/G/tau'   : Item("This is the file 'tau'.\n"),
9699    'A_MOVED/D/H'       : Item(),
9700    'A_MOVED/D/H/chi'   : Item("This is the file 'chi'.\n"),
9701    'A_MOVED/D/H/omega' : Item("New content"),
9702    'A_MOVED/D/H/psi'   : Item("New content"),
9703    })
9704  wc_disk.tweak('A_COPY/D/H/psi', 'A_COPY/mu', contents='New content')
9705  wc_disk.tweak('A_COPY', props={SVN_PROP_MERGEINFO : '/A:3'})
9706  expected_output = wc.State(wc_dir, {
9707    'A'                 : Item(status='D '),
9708    'A_MOVED'           : Item(status='A '),
9709    'A_MOVED/B'         : Item(status='A '),
9710    'A_MOVED/B/lambda'  : Item(status='A '),
9711    'A_MOVED/B/E'       : Item(status='A '),
9712    'A_MOVED/B/E/alpha' : Item(status='A '),
9713    'A_MOVED/B/E/beta'  : Item(status='A '),
9714    'A_MOVED/B/F'       : Item(status='A '),
9715    'A_MOVED/mu'        : Item(status='A '),
9716    'A_MOVED/C'         : Item(status='A '),
9717    'A_MOVED/D'         : Item(status='A '),
9718    'A_MOVED/D/gamma'   : Item(status='A '),
9719    'A_MOVED/D/G'       : Item(status='A '),
9720    'A_MOVED/D/G/pi'    : Item(status='A '),
9721    'A_MOVED/D/G/rho'   : Item(status='A '),
9722    'A_MOVED/D/G/tau'   : Item(status='A '),
9723    'A_MOVED/D/H'       : Item(status='A '),
9724    'A_MOVED/D/H/chi'   : Item(status='A '),
9725    'A_MOVED/D/H/omega' : Item(status='A '),
9726    'A_MOVED/D/H/psi'   : Item(status='A ')
9727    })
9728  svntest.actions.run_and_verify_update(wc_dir,
9729                                        expected_output,
9730                                        wc_disk,
9731                                        wc_status,
9732                                        check_props=True)
9733
9734  expected_output = wc.State(A_MOVED_path, {
9735    'mu' : Item(status='U '),
9736    })
9737  expected_mergeinfo_output = wc.State(A_MOVED_path, {
9738    '' : Item(status=' U'),
9739    })
9740  expected_elision_output = wc.State(A_MOVED_path, {
9741    })
9742  expected_A_status = wc.State(A_MOVED_path, {
9743    ''          : Item(status=' M', wc_rev=8),
9744    'B'         : Item(status='  ', wc_rev=8),
9745    'mu'        : Item(status='M ', wc_rev=8),
9746    'B/E'       : Item(status='  ', wc_rev=8),
9747    'B/E/alpha' : Item(status='  ', wc_rev=8),
9748    'B/E/beta'  : Item(status='  ', wc_rev=8),
9749    'B/lambda'  : Item(status='  ', wc_rev=8),
9750    'B/F'       : Item(status='  ', wc_rev=8),
9751    'C'         : Item(status='  ', wc_rev=8),
9752    'D'         : Item(status='  ', wc_rev=8),
9753    'D/G'       : Item(status='  ', wc_rev=8),
9754    'D/G/pi'    : Item(status='  ', wc_rev=8),
9755    'D/G/rho'   : Item(status='  ', wc_rev=8),
9756    'D/G/tau'   : Item(status='  ', wc_rev=8),
9757    'D/gamma'   : Item(status='  ', wc_rev=8),
9758    'D/H'       : Item(status='  ', wc_rev=8),
9759    'D/H/chi'   : Item(status='  ', wc_rev=8),
9760    'D/H/psi'   : Item(status='  ', wc_rev=8),
9761    'D/H/omega' : Item(status='  ', wc_rev=8),
9762    })
9763  # We can reuse expected_A_disk from above without change.
9764  svntest.actions.run_and_verify_merge(A_MOVED_path, '6', '7',
9765                                       sbox.repo_url + '/A_COPY', None,
9766                                       expected_output,
9767                                       expected_mergeinfo_output,
9768                                       expected_elision_output,
9769                                       expected_A_disk,
9770                                       expected_A_status,
9771                                       expected_A_skip,
9772                                       check_props=True)
9773
9774  # Revert all local mods
9775  svntest.actions.run_and_verify_svn(["Reverted '" + A_MOVED_path + "'\n",
9776                                      "Reverted '" + mu_MOVED_path + "'\n"],
9777                                     [], 'revert', '-R', wc_dir)
9778
9779  # Create a new 'A' unrelated to the old 'A' which was moved.  Then merge
9780  # r7 from 'A_COPY' to this new 'A'.  Since the new 'A' shares no history
9781  # with the mergeinfo 'A@3', the mergeinfo '/A:3' is added and when combined
9782  # with the mergeinfo created from the merge should result in
9783  # '/A:3\n/A_COPY:7'
9784  #
9785  # Create the new 'A' by exporting the old 'A@1'.
9786  expected_output = svntest.verify.UnorderedOutput(
9787      ["A    " + sbox.ospath('A') + "\n",
9788       "A    " + sbox.ospath('A/B') + "\n",
9789       "A    " + sbox.ospath('A/B/lambda') + "\n",
9790       "A    " + sbox.ospath('A/B/E') + "\n",
9791       "A    " + sbox.ospath('A/B/E/alpha') + "\n",
9792       "A    " + sbox.ospath('A/B/E/beta') + "\n",
9793       "A    " + sbox.ospath('A/B/F') + "\n",
9794       "A    " + sbox.ospath('A/mu') + "\n",
9795       "A    " + sbox.ospath('A/C') + "\n",
9796       "A    " + sbox.ospath('A/D') + "\n",
9797       "A    " + sbox.ospath('A/D/gamma') + "\n",
9798       "A    " + sbox.ospath('A/D/G') + "\n",
9799       "A    " + sbox.ospath('A/D/G/pi') + "\n",
9800       "A    " + sbox.ospath('A/D/G/rho') + "\n",
9801       "A    " + sbox.ospath('A/D/G/tau') + "\n",
9802       "A    " + sbox.ospath('A/D/H') + "\n",
9803       "A    " + sbox.ospath('A/D/H/chi') + "\n",
9804       "A    " + sbox.ospath('A/D/H/omega') + "\n",
9805       "A    " + sbox.ospath('A/D/H/psi') + "\n",
9806       "Exported revision 1.\n",]
9807       )
9808  svntest.actions.run_and_verify_svn(expected_output, [],
9809                                     'export', sbox.repo_url + '/A@1',
9810                                     A_path)
9811  expected_output = svntest.verify.UnorderedOutput(
9812      ["A         " + sbox.ospath('A') + "\n",
9813       "A         " + sbox.ospath('A/B') + "\n",
9814       "A         " + sbox.ospath('A/B/lambda') + "\n",
9815       "A         " + sbox.ospath('A/B/E') + "\n",
9816       "A         " + sbox.ospath('A/B/E/alpha') + "\n",
9817       "A         " + sbox.ospath('A/B/E/beta') + "\n",
9818       "A         " + sbox.ospath('A/B/F') + "\n",
9819       "A         " + sbox.ospath('A/mu') + "\n",
9820       "A         " + sbox.ospath('A/C') + "\n",
9821       "A         " + sbox.ospath('A/D') + "\n",
9822       "A         " + sbox.ospath('A/D/gamma') + "\n",
9823       "A         " + sbox.ospath('A/D/G') + "\n",
9824       "A         " + sbox.ospath('A/D/G/pi') + "\n",
9825       "A         " + sbox.ospath('A/D/G/rho') + "\n",
9826       "A         " + sbox.ospath('A/D/G/tau') + "\n",
9827       "A         " + sbox.ospath('A/D/H') + "\n",
9828       "A         " + sbox.ospath('A/D/H/chi') + "\n",
9829       "A         " + sbox.ospath('A/D/H/omega') + "\n",
9830       "A         " + sbox.ospath('A/D/H/psi') + "\n",]
9831      )
9832  svntest.actions.run_and_verify_svn(expected_output, [],
9833                                     'add', A_path)
9834  # Commit the new 'A' as r9
9835  expected_output = wc.State(wc_dir, {
9836    'A'           : Item(verb='Adding'),
9837    'A/B'         : Item(verb='Adding'),
9838    'A/mu'        : Item(verb='Adding'),
9839    'A/B/E'       : Item(verb='Adding'),
9840    'A/B/E/alpha' : Item(verb='Adding'),
9841    'A/B/E/beta'  : Item(verb='Adding'),
9842    'A/B/lambda'  : Item(verb='Adding'),
9843    'A/B/F'       : Item(verb='Adding'),
9844    'A/C'         : Item(verb='Adding'),
9845    'A/D'         : Item(verb='Adding'),
9846    'A/D/G'       : Item(verb='Adding'),
9847    'A/D/G/pi'    : Item(verb='Adding'),
9848    'A/D/G/rho'   : Item(verb='Adding'),
9849    'A/D/G/tau'   : Item(verb='Adding'),
9850    'A/D/gamma'   : Item(verb='Adding'),
9851    'A/D/H'       : Item(verb='Adding'),
9852    'A/D/H/chi'   : Item(verb='Adding'),
9853    'A/D/H/psi'   : Item(verb='Adding'),
9854    'A/D/H/omega' : Item(verb='Adding'),
9855    })
9856  wc_status.tweak(wc_rev=8)
9857  wc_status.add({
9858    'A'           : Item(wc_rev=9),
9859    'A/B'         : Item(wc_rev=9),
9860    'A/B/lambda'  : Item(wc_rev=9),
9861    'A/B/E'       : Item(wc_rev=9),
9862    'A/B/E/alpha' : Item(wc_rev=9),
9863    'A/B/E/beta'  : Item(wc_rev=9),
9864    'A/B/F'       : Item(wc_rev=9),
9865    'A/mu'        : Item(wc_rev=9),
9866    'A/C'         : Item(wc_rev=9),
9867    'A/D'         : Item(wc_rev=9),
9868    'A/D/gamma'   : Item(wc_rev=9),
9869    'A/D/G'       : Item(wc_rev=9),
9870    'A/D/G/pi'    : Item(wc_rev=9),
9871    'A/D/G/rho'   : Item(wc_rev=9),
9872    'A/D/G/tau'   : Item(wc_rev=9),
9873    'A/D/H'       : Item(wc_rev=9),
9874    'A/D/H/chi'   : Item(wc_rev=9),
9875    'A/D/H/omega' : Item(wc_rev=9),
9876    'A/D/H/psi'   : Item(wc_rev=9),
9877    })
9878  wc_status.tweak(status='  ')
9879  svntest.actions.run_and_verify_commit(wc_dir,
9880                                        expected_output,
9881                                        wc_status)
9882
9883  expected_output = wc.State(A_path, {
9884    'mu'      : Item(status='U '),
9885    'D/H/psi' : Item(status='U '),
9886    ''        : Item(status=' U'),
9887    })
9888  expected_mergeinfo_output = wc.State(A_path, {
9889    '' : Item(status=' G'),
9890    })
9891  expected_elision_output = wc.State(A_path, {
9892    })
9893  expected_A_status = wc.State(A_path, {
9894    ''          : Item(status=' M', wc_rev=9),
9895    'B'         : Item(status='  ', wc_rev=9),
9896    'mu'        : Item(status='M ', wc_rev=9),
9897    'B/E'       : Item(status='  ', wc_rev=9),
9898    'B/E/alpha' : Item(status='  ', wc_rev=9),
9899    'B/E/beta'  : Item(status='  ', wc_rev=9),
9900    'B/lambda'  : Item(status='  ', wc_rev=9),
9901    'B/F'       : Item(status='  ', wc_rev=9),
9902    'C'         : Item(status='  ', wc_rev=9),
9903    'D'         : Item(status='  ', wc_rev=9),
9904    'D/G'       : Item(status='  ', wc_rev=9),
9905    'D/G/pi'    : Item(status='  ', wc_rev=9),
9906    'D/G/rho'   : Item(status='  ', wc_rev=9),
9907    'D/G/tau'   : Item(status='  ', wc_rev=9),
9908    'D/gamma'   : Item(status='  ', wc_rev=9),
9909    'D/H'       : Item(status='  ', wc_rev=9),
9910    'D/H/chi'   : Item(status='  ', wc_rev=9),
9911    'D/H/psi'   : Item(status='M ', wc_rev=9),
9912    'D/H/omega' : Item(status='  ', wc_rev=9),
9913    })
9914  expected_A_disk = wc.State('', {
9915    ''          : Item(props={SVN_PROP_MERGEINFO : '/A:3\n/A_COPY:7'}),
9916    'B'         : Item(),
9917    'mu'        : Item("New content"),
9918    'B/E'       : Item(),
9919    'B/E/alpha' : Item("This is the file 'alpha'.\n"),
9920    'B/E/beta'  : Item("This is the file 'beta'.\n"),
9921    'B/lambda'  : Item("This is the file 'lambda'.\n"),
9922    'B/F'       : Item(),
9923    'C'         : Item(),
9924    'D'         : Item(),
9925    'D/G'       : Item(),
9926    'D/G/pi'    : Item("This is the file 'pi'.\n"),
9927    'D/G/rho'   : Item("This is the file 'rho'.\n"),
9928    'D/G/tau'   : Item("This is the file 'tau'.\n"),
9929    'D/gamma'   : Item("This is the file 'gamma'.\n"),
9930    'D/H'       : Item(),
9931    'D/H/chi'   : Item("This is the file 'chi'.\n"),
9932    'D/H/psi'   : Item("New content"),
9933    'D/H/omega' : Item("This is the file 'omega'.\n"),
9934    })
9935  expected_A_skip = wc.State(A_path, {})
9936  svntest.actions.run_and_verify_merge(A_path, '6', '7',
9937                                       sbox.repo_url + '/A_COPY', None,
9938                                       expected_output,
9939                                       expected_mergeinfo_output,
9940                                       expected_elision_output,
9941                                       expected_A_disk,
9942                                       expected_A_status,
9943                                       expected_A_skip,
9944                                       check_props=True)
9945
9946#----------------------------------------------------------------------
9947@SkipUnless(server_has_mergeinfo)
9948@Issue(3094)
9949def merge_range_predates_history(sbox):
9950  "merge range predates history"
9951
9952  sbox.build()
9953  wc_dir = sbox.wc_dir
9954
9955  iota_path = sbox.ospath('iota')
9956  trunk_file_path = sbox.ospath('trunk/file')
9957  trunk_url = sbox.repo_url + "/trunk"
9958  branches_url = sbox.repo_url + "/branches"
9959  branch_path = sbox.ospath('branches/branch')
9960  branch_file_path = sbox.ospath('branches/branch/file')
9961  branch_url = sbox.repo_url + "/branches/branch"
9962
9963  # Tweak a file and commit. (r2)
9964  svntest.main.file_append(iota_path, "More data.\n")
9965  sbox.simple_commit(message='tweak iota')
9966
9967  # Create our trunk and branches directory, and update working copy. (r3)
9968  svntest.main.run_svn(None, 'mkdir', trunk_url, branches_url,
9969                       '-m', 'add trunk and branches dirs')
9970  svntest.main.run_svn(None, 'up', wc_dir)
9971
9972  # Add a file to the trunk and commit. (r4)
9973  svntest.main.file_append(trunk_file_path, "This is the file 'file'.\n")
9974  svntest.main.run_svn(None, 'add', trunk_file_path)
9975  sbox.simple_commit(message='add trunk file')
9976
9977  # Branch trunk from r3, and update working copy. (r5)
9978  svntest.main.run_svn(None, 'cp', trunk_url, branch_url, '-r3',
9979                       '-m', 'branch trunk@2')
9980  svntest.main.run_svn(None, 'up', wc_dir)
9981
9982  # Now, try to merge trunk into the branch.  There should be one
9983  # outstanding change -- the addition of the file.
9984  expected_output = expected_merge_output([[4,5]],
9985                                          ['A    ' + branch_file_path + '\n',
9986                                           ' U   ' + branch_path + '\n'])
9987  svntest.actions.run_and_verify_svn(expected_output, [], 'merge',
9988                                     trunk_url, branch_path)
9989
9990#----------------------------------------------------------------------
9991@Issue(3623)
9992def foreign_repos(sbox):
9993  "merge from a foreign repository"
9994
9995  sbox.build()
9996  wc_dir = sbox.wc_dir
9997
9998  # Make a copy of this repository and associated working copy.  Both
9999  # should have nothing but a Greek tree in them, and the two
10000  # repository UUIDs should differ.
10001  sbox2 = sbox.clone_dependent(True)
10002  sbox2.build()
10003  wc_dir2 = sbox2.wc_dir
10004
10005  # Convenience variables for working copy paths.
10006  Z_path = sbox.ospath('A/D/G/Z')
10007  B_path = sbox.ospath('A/B')
10008  Q_path = sbox.ospath('Q')
10009  H_path = sbox.ospath('A/D/H')
10010  iota_path = sbox.ospath('iota')
10011  beta_path = sbox.ospath('A/B/E/beta')
10012  alpha_path = sbox.ospath('A/B/E/alpha')
10013  zeta_path = sbox.ospath('A/D/G/Z/zeta')
10014  fred_path = sbox.ospath('A/C/fred')
10015
10016  # Add new directories, with and without properties.
10017  svntest.main.run_svn(None, 'mkdir', Q_path, Z_path)
10018  svntest.main.run_svn(None, 'pset', 'foo', 'bar', Z_path)
10019
10020  # Add new files, with contents, with and without properties.
10021  zeta_contents = "This is the file 'zeta'.\n"
10022  fred_contents = "This is the file 'fred'.\n"
10023  svntest.main.file_append(zeta_path, zeta_contents)
10024  svntest.main.file_append(fred_path, fred_contents)
10025  svntest.main.run_svn(None, 'add', zeta_path, fred_path)
10026  svntest.main.run_svn(None, 'pset', 'foo', 'bar', fred_path)
10027
10028  # Modify existing files and directories.
10029  added_contents = "This is another line of text.\n"
10030  svntest.main.file_append(iota_path, added_contents)
10031  svntest.main.file_append(beta_path, added_contents)
10032  svntest.main.run_svn(None, 'pset', 'foo', 'bar', iota_path, B_path)
10033
10034  # Delete some stuff
10035  svntest.main.run_svn(None, 'delete', alpha_path, H_path)
10036
10037  # Commit up these changes.
10038  expected_output = wc.State(wc_dir, {
10039    'Q'            : Item(verb='Adding'),
10040    'A/D/G/Z'      : Item(verb='Adding'),
10041    'A/D/G/Z/zeta' : Item(verb='Adding'),
10042    'A/C/fred'     : Item(verb='Adding'),
10043    'iota'         : Item(verb='Sending'),
10044    'A/B'          : Item(verb='Sending'),
10045    'A/B/E/beta'   : Item(verb='Sending'),
10046    'A/B/E/alpha'  : Item(verb='Deleting'),
10047    'A/D/H'        : Item(verb='Deleting'),
10048    })
10049  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
10050  expected_status.add({
10051    'Q'            : Item(status='  ', wc_rev=2),
10052    'A/D/G/Z'      : Item(status='  ', wc_rev=2),
10053    'A/D/G/Z/zeta' : Item(status='  ', wc_rev=2),
10054    'A/C/fred'     : Item(status='  ', wc_rev=2),
10055    })
10056  expected_status.tweak('iota', 'A/B/E/beta', 'A/B', wc_rev=2)
10057  expected_status.remove('A/B/E/alpha', 'A/D/H', 'A/D/H/chi',
10058                         'A/D/H/psi', 'A/D/H/omega')
10059  expected_disk = svntest.main.greek_state.copy()
10060  expected_disk.add({
10061    'Q'            : Item(),
10062    'A/D/G/Z'      : Item(props={'foo':'bar'}),
10063    'A/D/G/Z/zeta' : Item(contents=zeta_contents),
10064    'A/C/fred'     : Item(contents=fred_contents,props={'foo':'bar'}),
10065    })
10066  expected_disk.remove('A/B/E/alpha', 'A/D/H', 'A/D/H/chi',
10067                       'A/D/H/psi', 'A/D/H/omega')
10068  expected_disk.tweak('iota',
10069                      contents=expected_disk.desc['iota'].contents
10070                      + added_contents,
10071                      props={'foo':'bar'})
10072  expected_disk.tweak('A/B', props={'foo':'bar'})
10073  expected_disk.tweak('A/B/E/beta',
10074                      contents=expected_disk.desc['A/B/E/beta'].contents
10075                      + added_contents)
10076  svntest.actions.run_and_verify_commit(wc_dir,
10077                                        expected_output,
10078                                        expected_status)
10079  svntest.actions.verify_disk(wc_dir, expected_disk, True)
10080
10081  # Now, merge our committed revision into a working copy of another
10082  # repository.  Not only should the merge succeed, but the results on
10083  # disk should match those in our first working copy.
10084
10085  ### TODO: Use run_and_verify_merge() ###
10086  svntest.main.run_svn(None, 'merge', '-c2', sbox.repo_url, wc_dir2)
10087  sbox2.simple_commit(message='Merge from foreign repo')
10088  svntest.actions.verify_disk(wc_dir2, expected_disk, True)
10089
10090  # Now, let's make a third checkout -- our second from the original
10091  # repository -- and make sure that all the data there is correct.
10092  # It should look just like the original EXPECTED_DISK.
10093  # This is a regression test for issue #3623 in which wc_dir2 had the
10094  # correct state but the committed state was wrong.
10095  wc_dir3 = sbox.add_wc_path('wc3')
10096  svntest.actions.run_and_verify_svn(None, [], 'checkout',
10097                                     sbox2.repo_url, wc_dir3)
10098  svntest.actions.verify_disk(wc_dir3, expected_disk, True)
10099
10100#----------------------------------------------------------------------
10101def foreign_repos_uuid(sbox):
10102  "verify uuid of items added via foreign repo merge"
10103
10104  sbox.build()
10105  wc_dir = sbox.wc_dir
10106  wc_uuid = svntest.actions.get_wc_uuid(wc_dir)
10107
10108  # Make a copy of this repository and associated working copy.  Both
10109  # should have nothing but a Greek tree in them, and the two
10110  # repository UUIDs should differ.
10111  sbox2 = sbox.clone_dependent(True)
10112  sbox2.build()
10113  wc_dir2 = sbox2.wc_dir
10114  wc2_uuid = svntest.actions.get_wc_uuid(wc_dir2)
10115
10116  # Convenience variables for working copy paths.
10117  zeta_path = sbox.ospath('A/D/G/zeta')
10118  Z_path = sbox.ospath('A/Z')
10119
10120  # Add new file and directory.
10121  zeta_contents = "This is the file 'zeta'.\n"
10122  svntest.main.file_append(zeta_path, zeta_contents)
10123  os.mkdir(Z_path)
10124  svntest.main.run_svn(None, 'add', zeta_path, Z_path)
10125
10126  # Commit up these changes.
10127  expected_output = wc.State(wc_dir, {
10128    'A/D/G/zeta' : Item(verb='Adding'),
10129    'A/Z'        : Item(verb='Adding'),
10130    })
10131  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
10132  expected_status.add({
10133    'A/D/G/zeta' : Item(status='  ', wc_rev=2),
10134    'A/Z'        : Item(status='  ', wc_rev=2),
10135    })
10136  expected_disk = svntest.main.greek_state.copy()
10137  expected_disk.add({
10138    'A/D/G/zeta' : Item(contents=zeta_contents),
10139    'A/Z'        : Item(),
10140    })
10141  svntest.actions.run_and_verify_commit(wc_dir,
10142                                        expected_output,
10143                                        expected_status)
10144  svntest.actions.verify_disk(wc_dir, expected_disk, True)
10145
10146  svntest.main.run_svn(None, 'merge', '-c2', sbox.repo_url, wc_dir2)
10147  sbox2.simple_commit(message='Merge from foreign repos')
10148
10149  # Run info to check the copied rev to make sure it's right
10150  zeta2_path = os.path.join(wc_dir2, 'A', 'D', 'G', 'zeta')
10151  expected_info = {"Path" : re.escape(zeta2_path), # escape backslashes
10152                   "URL" : sbox2.repo_url + "/A/D/G/zeta",
10153                   "Repository Root" : sbox2.repo_url,
10154                   "Repository UUID" : wc2_uuid,
10155                   "Revision" : "2",
10156                   "Node Kind" : "file",
10157                   "Schedule" : "normal",
10158                  }
10159  svntest.actions.run_and_verify_info([expected_info], zeta2_path)
10160
10161  # Run info to check the copied rev to make sure it's right
10162  Z2_path = os.path.join(wc_dir2, 'A', 'Z')
10163  expected_info = {"Path" : re.escape(Z2_path), # escape backslashes
10164                   "URL" : sbox2.repo_url + "/A/Z",
10165                   "Repository Root" : sbox2.repo_url,
10166                   "Repository UUID" : wc2_uuid,
10167                   "Revision" : "2",
10168                   "Node Kind" : "directory",
10169                   "Schedule" : "normal",
10170                  }
10171  svntest.actions.run_and_verify_info([expected_info], Z2_path)
10172
10173#----------------------------------------------------------------------
10174def foreign_repos_2_url(sbox):
10175  "2-url merge from a foreign repository"
10176
10177  sbox.build()
10178  wc_dir = sbox.wc_dir
10179
10180  # Make a copy of this repository and associated working copy.  Both
10181  # should have nothing but a Greek tree in them, and the two
10182  # repository UUIDs should differ.
10183  sbox2 = sbox.clone_dependent(True)
10184  sbox2.build()
10185  wc_dir2 = sbox2.wc_dir
10186
10187  # Convenience variables for working copy paths.
10188  Z_path = sbox.ospath('A/D/G/Z')
10189  Q_path = sbox.ospath('A/Q')
10190  H_path = sbox.ospath('A/D/H')
10191  beta_path = sbox.ospath('A/B/E/beta')
10192  alpha_path = sbox.ospath('A/B/E/alpha')
10193  zeta_path = sbox.ospath('A/D/G/Z/zeta')
10194  fred_path = sbox.ospath('A/C/fred')
10195
10196  # First, "tag" the current state of the repository.
10197  svntest.main.run_svn(None, 'copy', sbox.repo_url + '/A',
10198                       sbox.repo_url + '/A-tag1', '-m', 'tag1')
10199
10200  # Add new directories
10201  svntest.main.run_svn(None, 'mkdir', Q_path, Z_path)
10202
10203  # Add new files
10204  zeta_contents = "This is the file 'zeta'.\n"
10205  fred_contents = "This is the file 'fred'.\n"
10206  svntest.main.file_append(zeta_path, zeta_contents)
10207  svntest.main.file_append(fred_path, fred_contents)
10208  svntest.main.run_svn(None, 'add', zeta_path, fred_path)
10209
10210  # Modify existing files
10211  added_contents = "This is another line of text.\n"
10212  svntest.main.file_append(beta_path, added_contents)
10213
10214  # Delete some stuff
10215  svntest.main.run_svn(None, 'delete', alpha_path, H_path)
10216
10217  # Commit up these changes.
10218  expected_output = wc.State(wc_dir, {
10219    'A/Q'          : Item(verb='Adding'),
10220    'A/D/G/Z'      : Item(verb='Adding'),
10221    'A/D/G/Z/zeta' : Item(verb='Adding'),
10222    'A/C/fred'     : Item(verb='Adding'),
10223    'A/B/E/beta'   : Item(verb='Sending'),
10224    'A/B/E/alpha'  : Item(verb='Deleting'),
10225    'A/D/H'        : Item(verb='Deleting'),
10226    })
10227  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
10228  expected_status.add({
10229    'A/Q'          : Item(status='  ', wc_rev=3),
10230    'A/D/G/Z'      : Item(status='  ', wc_rev=3),
10231    'A/D/G/Z/zeta' : Item(status='  ', wc_rev=3),
10232    'A/C/fred'     : Item(status='  ', wc_rev=3),
10233    })
10234  expected_status.tweak('A/B/E/beta', wc_rev=3)
10235  expected_status.remove('A/B/E/alpha', 'A/D/H', 'A/D/H/chi',
10236                         'A/D/H/psi', 'A/D/H/omega')
10237  expected_disk = svntest.main.greek_state.copy()
10238  expected_disk.add({
10239    'A/Q'          : Item(),
10240    'A/D/G/Z'      : Item(),
10241    'A/D/G/Z/zeta' : Item(contents=zeta_contents),
10242    'A/C/fred'     : Item(contents=fred_contents),
10243    })
10244  expected_disk.remove('A/B/E/alpha', 'A/D/H', 'A/D/H/chi',
10245                       'A/D/H/psi', 'A/D/H/omega')
10246  expected_disk.tweak('A/B/E/beta',
10247                      contents=expected_disk.desc['A/B/E/beta'].contents
10248                      + added_contents)
10249  svntest.actions.run_and_verify_commit(wc_dir,
10250                                        expected_output,
10251                                        expected_status)
10252  svntest.actions.verify_disk(wc_dir, expected_disk, True)
10253
10254  # Now, "tag" the new state of the repository.
10255  svntest.main.run_svn(None, 'copy', sbox.repo_url + '/A',
10256                       sbox.repo_url + '/A-tag2', '-m', 'tag2')
10257
10258  # Now, merge across our "tags" (copies of /A) into the /A of a
10259  # working copy of another repository.  Not only should the merge
10260  # succeed, but the results on disk should match those in our first
10261  # working copy.
10262
10263  ### TODO: Use run_and_verify_merge() ###
10264  svntest.main.run_svn(None, 'merge', sbox.repo_url + '/A-tag1',
10265                       sbox.repo_url + '/A-tag2',
10266                       os.path.join(wc_dir2, 'A'))
10267  sbox2.simple_commit(message='Merge from foreign repos')
10268  svntest.actions.verify_disk(wc_dir2, expected_disk, True)
10269
10270#----------------------------------------------------------------------
10271@Issue(1962)
10272def merge_added_subtree(sbox):
10273  "merge added subtree"
10274
10275  # The result of a subtree added by copying
10276  # or merging an added subtree, should be the same on disk
10277  ### with the exception of mergeinfo?!
10278
10279  # test for issue 1962
10280  sbox.build()
10281  wc_dir = sbox.wc_dir
10282  url = sbox.repo_url
10283
10284  # make a branch of A
10285  # svn cp A A_COPY
10286  A_url = url + "/A"
10287  A_COPY_url = url + "/A_COPY"
10288  A_path = sbox.ospath('A')
10289
10290  svntest.actions.run_and_verify_svn(["Committing transaction...\n",
10291                                         "Committed revision 2.\n"], [],
10292                                     "cp", "-m", "", A_url, A_COPY_url)
10293  svntest.actions.run_and_verify_svn(["Committing transaction...\n",
10294                                         "Committed revision 3.\n"], [],
10295                                     "cp", "-m", "",
10296                                     A_COPY_url + '/D',
10297                                     A_COPY_url + '/D2')
10298  expected_output = wc.State(A_path, {
10299    'D2'        : Item(status='A '),
10300    'D2/gamma'  : Item(status='A '),
10301    'D2/H'      : Item(status='A '),
10302    'D2/H/chi'  : Item(status='A '),
10303    'D2/H/psi'  : Item(status='A '),
10304    'D2/H/omega': Item(status='A '),
10305    'D2/G'      : Item(status='A '),
10306    'D2/G/pi'   : Item(status='A '),
10307    'D2/G/rho'  : Item(status='A '),
10308    'D2/G/tau'  : Item(status='A ')
10309    })
10310
10311  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
10312  expected_status.add({
10313    'A/D2'        : Item(status='A ', copied='+', wc_rev='-'),
10314    'A/D2/gamma'  : Item(status='  ', copied='+', wc_rev='-'),
10315    'A/D2/H'      : Item(status='  ', copied='+', wc_rev='-'),
10316    'A/D2/H/chi'  : Item(status='  ', copied='+', wc_rev='-'),
10317    'A/D2/H/psi'  : Item(status='  ', copied='+', wc_rev='-'),
10318    'A/D2/H/omega': Item(status='  ', copied='+', wc_rev='-'),
10319    'A/D2/G'      : Item(status='  ', copied='+', wc_rev='-'),
10320    'A/D2/G/pi'   : Item(status='  ', copied='+', wc_rev='-'),
10321    'A/D2/G/rho'  : Item(status='  ', copied='+', wc_rev='-'),
10322    'A/D2/G/tau'  : Item(status='  ', copied='+', wc_rev='-')
10323    })
10324  expected_status.remove('', 'iota')
10325
10326  expected_skip = wc.State('', {})
10327  expected_disk = svntest.main.greek_state.subtree("A")
10328  dest_name = ''
10329  expected_disk.add({
10330    dest_name + 'D2'         : Item(),
10331    dest_name + 'D2/gamma'   : Item("This is the file 'gamma'.\n"),
10332    dest_name + 'D2/G'       : Item(),
10333    dest_name + 'D2/G/pi'    : Item("This is the file 'pi'.\n"),
10334    dest_name + 'D2/G/rho'   : Item("This is the file 'rho'.\n"),
10335    dest_name + 'D2/G/tau'   : Item("This is the file 'tau'.\n"),
10336    dest_name + 'D2/H'       : Item(),
10337    dest_name + 'D2/H/chi'   : Item("This is the file 'chi'.\n"),
10338    dest_name + 'D2/H/omega' : Item("This is the file 'omega'.\n"),
10339    dest_name + 'D2/H/psi'   : Item("This is the file 'psi'.\n")
10340    })
10341
10342  # Using the above information, verify a REPO->WC copy
10343  svntest.actions.run_and_verify_svn(None, [],
10344                                     "cp", A_COPY_url + '/D2',
10345                                     os.path.join(A_path, "D2"))
10346  svntest.actions.verify_disk(A_path, expected_disk)
10347  svntest.actions.run_and_verify_status(A_path, expected_status)
10348
10349  # Remove the copy artifacts
10350  svntest.actions.run_and_verify_svn(None, [],
10351                                     "revert", "-R", A_path)
10352  svntest.main.safe_rmtree(os.path.join(A_path, "D2"))
10353
10354  # Add merge-tracking differences between copying and merging
10355  # Verify a merge using the otherwise unchanged disk and status trees
10356  expected_status.tweak('A',status=' M')
10357  expected_mergeinfo_output = wc.State(A_path, {
10358    '' : Item(status=' U'),
10359    })
10360  expected_elision_output = wc.State(A_path, {
10361    })
10362  svntest.actions.run_and_verify_merge(A_path, 2, 3, A_COPY_url, None,
10363                                       expected_output,
10364                                       expected_mergeinfo_output,
10365                                       expected_elision_output,
10366                                       expected_disk,
10367                                       expected_status, expected_skip)
10368
10369#----------------------------------------------------------------------
10370# Issue #3138
10371@SkipUnless(server_has_mergeinfo)
10372@Issue(3138)
10373def merge_unknown_url(sbox):
10374  "merging an unknown url should return error"
10375
10376  sbox.build()
10377  wc_dir = sbox.wc_dir
10378
10379  # remove a path from the repo and commit.
10380  iota_path = sbox.ospath('iota')
10381  svntest.actions.run_and_verify_svn(None, [], 'rm', iota_path)
10382  svntest.actions.run_and_verify_svn(None, [],
10383                                     "ci", wc_dir, "-m", "log message")
10384
10385
10386  url = sbox.repo_url + "/iota"
10387  expected_err = ".*File not found.*iota.*|.*iota.*path not found.*"
10388  svntest.actions.run_and_verify_svn(None, expected_err,
10389                                     "merge", url, wc_dir)
10390
10391#----------------------------------------------------------------------
10392@SkipUnless(server_has_mergeinfo)
10393def reverse_merge_away_all_mergeinfo(sbox):
10394  "merges that remove all mergeinfo work"
10395
10396  sbox.build()
10397  wc_dir = sbox.wc_dir
10398  wc_disk, wc_status = set_up_branch(sbox)
10399
10400  # Some paths we'll care about
10401  A_COPY_H_path = sbox.ospath('A_COPY/D/H')
10402
10403  # Merge r4:8 from A/D/H into A_COPY/D/H.
10404  expected_output = wc.State(A_COPY_H_path, {
10405    'omega' : Item(status='U '),
10406    'psi'   : Item(status='U ')
10407    })
10408  expected_mergeinfo_output = wc.State(A_COPY_H_path, {
10409    '' : Item(status=' U'),
10410    })
10411  expected_elision_output = wc.State(A_COPY_H_path, {
10412    })
10413  expected_status = wc.State(A_COPY_H_path, {
10414    ''      : Item(status=' M', wc_rev=2),
10415    'psi'   : Item(status='M ', wc_rev=2),
10416    'omega' : Item(status='M ', wc_rev=2),
10417    'chi'   : Item(status='  ', wc_rev=2),
10418    })
10419  expected_disk = wc.State('', {
10420    ''      : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:3-6'}),
10421    'psi'   : Item("New content"),
10422    'omega' : Item("New content"),
10423    'chi'   : Item("This is the file 'chi'.\n"),
10424    })
10425  expected_skip = wc.State(A_COPY_H_path, { })
10426  svntest.actions.run_and_verify_merge(A_COPY_H_path, '2', '6',
10427                                       sbox.repo_url + '/A/D/H', None,
10428                                       expected_output,
10429                                       expected_mergeinfo_output,
10430                                       expected_elision_output,
10431                                       expected_disk,
10432                                       expected_status, expected_skip,
10433                                       check_props=True)
10434
10435  # Commit the merge as r7
10436  expected_output = wc.State(wc_dir, {
10437    'A_COPY/D/H'       : Item(verb='Sending'),
10438    'A_COPY/D/H/omega' : Item(verb='Sending'),
10439    'A_COPY/D/H/psi'   : Item(verb='Sending'),
10440    })
10441  wc_status.tweak('A_COPY/D/H', 'A_COPY/D/H/omega', 'A_COPY/D/H/psi',
10442                  wc_rev=7)
10443  svntest.actions.run_and_verify_commit(wc_dir,
10444                                        expected_output,
10445                                        wc_status)
10446
10447  # Now reverse merge r7 from itself, all mergeinfo should be removed.
10448  expected_output = wc.State(A_COPY_H_path, {
10449    ''      : Item(status=' U'),
10450    'omega' : Item(status='U '),
10451    'psi'   : Item(status='U ')
10452    })
10453  expected_mergeinfo_output = wc.State(A_COPY_H_path, {
10454    '' : Item(status=' G'),
10455    })
10456  expected_elision_output = wc.State(A_COPY_H_path, {
10457    '' : Item(status=' U'),
10458    })
10459  expected_status = wc.State(A_COPY_H_path, {
10460    ''      : Item(status=' M', wc_rev=7),
10461    'psi'   : Item(status='M ', wc_rev=7),
10462    'omega' : Item(status='M ', wc_rev=7),
10463    'chi'   : Item(status='  ', wc_rev=2),
10464    })
10465  expected_disk = wc.State('', {
10466    'psi'   : Item("This is the file 'psi'.\n"),
10467    'omega' : Item("This is the file 'omega'.\n"),
10468    'chi'   : Item("This is the file 'chi'.\n"),
10469    })
10470  expected_skip = wc.State(A_COPY_H_path, { })
10471  svntest.actions.run_and_verify_merge(A_COPY_H_path, '7', '6',
10472                                       sbox.repo_url + '/A_COPY/D/H', None,
10473                                       expected_output,
10474                                       expected_mergeinfo_output,
10475                                       expected_elision_output,
10476                                       expected_disk,
10477                                       expected_status, expected_skip,
10478                                       [],
10479                                       True, False, '--allow-mixed-revisions',
10480                                       A_COPY_H_path)
10481
10482#----------------------------------------------------------------------
10483# Issue #3138
10484# Another test for issue #3067: 'subtrees with intersecting mergeinfo,
10485# that don't exist at the start of a merge range shouldn't break the
10486# merge'.  Specifically see
10487# https://issues.apache.org/jira/browse/SVN-3067#desc5
10488@SkipUnless(server_has_mergeinfo)
10489@Issues(3138,3067,4217)
10490def dont_merge_revs_into_subtree_that_predate_it(sbox):
10491  "dont merge revs into a subtree that predate it"
10492
10493  #                              +-> merge -c7 A/D/H/nu@7 H_COPY/nu
10494  #                              | +-> merge -c2 A/D/H H_COPY
10495  #                              | | +-> merge A/D/H H_COPY
10496  #                              | | |
10497  # A/D/H      A----------------------
10498  #     +-psi  +-M-------------M------
10499  #     +-nu       A-D C---M-D
10500  # H_COPY               C---------G-G
10501  #     +-psi            +---------+-.
10502  #     +-nu             +-------G---.
10503  #            1 2 3 4 5 6 7 8 9 w w w
10504
10505  # Create our good 'ole greek tree.
10506  sbox.build()
10507  wc_dir = sbox.wc_dir
10508
10509  # Some paths we'll care about
10510  psi_path     = sbox.ospath('A/D/H/psi')
10511  nu_path      = sbox.ospath('A/D/H/nu')
10512  H_COPY_path  = sbox.ospath('H_COPY')
10513  nu_COPY_path = sbox.ospath('H_COPY/nu')
10514
10515  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
10516  expected_disk = svntest.main.greek_state.copy()
10517
10518  # Make a text mod to 'A/D/H/psi' and commit it as r2
10519  svntest.main.file_write(psi_path, "New content")
10520  expected_output = wc.State(wc_dir, {'A/D/H/psi' : Item(verb='Sending')})
10521  expected_status.tweak('A/D/H/psi', wc_rev=2)
10522  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
10523                                        expected_status)
10524  expected_disk.tweak('A/D/H/psi', contents="New content")
10525
10526  # Create 'A/D/H/nu' and commit it as r3.
10527  svntest.main.file_write(nu_path, "This is the file 'nu'.\n")
10528  svntest.actions.run_and_verify_svn(None, [], 'add', nu_path)
10529  expected_output = wc.State(wc_dir, {'A/D/H/nu' : Item(verb='Adding')})
10530  expected_status.add({'A/D/H/nu' : Item(status='  ', wc_rev=3)})
10531  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
10532                                        expected_status)
10533
10534  # Delete 'A/D/H/nu' and commit it as r4.
10535  svntest.actions.run_and_verify_svn(None, [], 'rm', nu_path)
10536  expected_output = wc.State(wc_dir, {'A/D/H/nu' : Item(verb='Deleting')})
10537  expected_status.remove('A/D/H/nu')
10538  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
10539                                        expected_status)
10540
10541  # Copy 'A/D/H/nu' from r3 and commit it as r5.
10542  svntest.actions.run_and_verify_svn(None, [], 'cp',
10543                                     sbox.repo_url + '/A/D/H/nu@3', nu_path)
10544  expected_output = wc.State(wc_dir, {'A/D/H/nu' : Item(verb='Adding')})
10545  expected_status.add({'A/D/H/nu' : Item(status='  ', wc_rev=5)})
10546  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
10547                                        expected_status)
10548
10549  # Copy 'A/D/H' to 'H_COPY' in r6.
10550  svntest.actions.run_and_verify_svn(['Committing transaction...\n',
10551                                      'Committed revision 6.\n'],
10552                                     [], 'copy',
10553                                     sbox.repo_url + "/A/D/H",
10554                                     sbox.repo_url + "/H_COPY",
10555                                     "-m", "Copy A/D/H to H_COPY")
10556  expected_status.add({
10557    "H_COPY"       : Item(),
10558    "H_COPY/chi"   : Item(),
10559    "H_COPY/omega" : Item(),
10560    "H_COPY/psi"   : Item(),
10561    "H_COPY/nu"    : Item()})
10562
10563  # Update to pull the previous copy into the WC
10564  svntest.main.run_svn(None, 'up', wc_dir)
10565  expected_status.tweak(status='  ', wc_rev=6)
10566
10567  # Make a text mod to 'A/D/H/nu' and commit it as r7.
10568  svntest.main.file_write(nu_path, "New content")
10569  expected_output = wc.State(wc_dir, {'A/D/H/nu' : Item(verb='Sending')})
10570  expected_status.tweak('A/D/H/nu', wc_rev=7)
10571  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
10572                                        expected_status)
10573
10574  # Remove A/D/H/nu and commit it as r8.
10575  # We do this deletion so that following cherry harvest has a *tough*
10576  # time to identify the line of history of /A/D/H/nu@HEAD.
10577  svntest.main.run_svn(None, 'rm', nu_path)
10578  expected_output = wc.State(wc_dir, {'A/D/H/nu' : Item(verb='Deleting')})
10579  expected_status.remove('A/D/H/nu')
10580  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
10581                                        expected_status)
10582
10583  # Make another text mod to 'A/D/H/psi' that can be merged to 'H_COPY'
10584  # during a cherry harvest and commit it as r9.
10585  svntest.main.file_write(psi_path, "Even *newer* content")
10586  expected_output = wc.State(wc_dir, {'A/D/H/psi' : Item(verb='Sending')})
10587  expected_status.tweak('A/D/H/psi', wc_rev=9)
10588  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
10589                                        expected_status)
10590  expected_disk.tweak('A/D/H/psi', contents="Even *newer* content")
10591
10592  # Update WC so elision occurs smoothly.
10593  svntest.main.run_svn(None, 'up', wc_dir)
10594  expected_status.tweak(status='  ', wc_rev=9)
10595
10596  # Merge r7 from 'A/D/H/nu' to 'H_COPY/nu'.
10597  svntest.actions.run_and_verify_svn(
10598    expected_merge_output([[7]],
10599                          ['U    ' + nu_COPY_path + '\n',
10600                           ' U   ' + nu_COPY_path + '\n']),
10601    [], 'merge', '-c7', sbox.repo_url + '/A/D/H/nu@7', nu_COPY_path)
10602
10603  # Cherry harvest all eligible revisions from 'A/D/H' to 'H_COPY'.
10604  #
10605  # This is where we see the problem described in
10606  # https://issues.apache.org/jira/browse/SVN-3067#desc5.
10607  #
10608  # Use run_and_verify_svn() because run_and_verify_merge*() require
10609  # explicit revision ranges.
10610
10611  expected_skip = wc.State(H_COPY_path, { })
10612  #Cherry pick r2 prior to cherry harvest.
10613  svntest.actions.run_and_verify_svn([], [], 'merge', '-c2',
10614                                     sbox.repo_url + '/A/D/H',
10615                                     H_COPY_path)
10616
10617  # H_COPY needs r6-9 applied while H_COPY/nu needs only 6,8-9.
10618  svntest.actions.run_and_verify_svn(
10619    expected_merge_output(
10620      [[7,9],  # Merge notification
10621       [6,9]], # Mergeinfo notification
10622               ['U    ' + os.path.join(H_COPY_path, "psi") + '\n',
10623                'D    ' + os.path.join(H_COPY_path, "nu") + '\n',
10624                ' U   ' + H_COPY_path + '\n',]),
10625    [], 'merge', sbox.repo_url + '/A/D/H', H_COPY_path, '--force')
10626
10627  # Check the status after the merge.
10628  expected_status.tweak('H_COPY', status=' M')
10629  expected_status.tweak('H_COPY/psi', status='M ')
10630  expected_status.tweak('H_COPY/nu', status='D ')
10631  svntest.actions.run_and_verify_status(wc_dir, expected_status)
10632  check_mergeinfo_recursively(wc_dir,
10633                              { H_COPY_path: '/A/D/H:6-9' })
10634
10635#----------------------------------------------------------------------
10636# Helper for merge_chokes_on_renamed_subtrees and
10637# subtrees_with_empty_mergeinfo.
10638def set_up_renamed_subtree(sbox):
10639  '''Starting with standard greek tree, make a text mod to A/D/H/psi
10640  as r2. Tweak A/D/H/omega and commit it at r3(We do this to create
10641  broken segment of history of A/D/H.
10642  *DO NOT SVN UPDATE*.
10643  Move A/D/H/psi to A/D/H/psi_moved as r4.  Copy A/D/H to H_COPY
10644  as r5.  Make a text mod to A/D/H/psi_moved and commit it at r6.
10645  Update the working copy and return the expected disk and status
10646  representing it'''
10647
10648  # Create our good 'ole greek tree.
10649  sbox.build()
10650  wc_dir = sbox.wc_dir
10651
10652  # Some paths we'll care about
10653  psi_path            = sbox.ospath('A/D/H/psi')
10654  omega_path            = sbox.ospath('A/D/H/omega')
10655  psi_moved_path      = sbox.ospath('A/D/H/psi_moved')
10656  psi_COPY_moved_path = sbox.ospath('H_COPY/psi_moved')
10657  H_COPY_path    = sbox.ospath('H_COPY')
10658
10659  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
10660  expected_disk = svntest.main.greek_state.copy()
10661
10662  # Make a text mod to 'A/D/H/psi' and commit it as r2
10663  svntest.main.file_write(psi_path, "New content")
10664  expected_output = wc.State(wc_dir, {'A/D/H/psi' : Item(verb='Sending')})
10665  expected_status.tweak('A/D/H/psi', wc_rev=2)
10666  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
10667                                        expected_status)
10668  expected_disk.tweak('A/D/H/psi', contents="New content")
10669
10670  # Make a text mod to 'A/D/H/omega' and commit it as r3
10671  svntest.main.file_write(omega_path, "New omega")
10672  expected_output = wc.State(wc_dir, {'A/D/H/omega' : Item(verb='Sending')})
10673  expected_status.tweak('A/D/H/omega', wc_rev=3)
10674  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
10675                                        expected_status)
10676  expected_disk.tweak('A/D/H/omega', contents="New omega")
10677
10678  # Move 'A/D/H/psi' to 'A/D/H/psi_moved' and commit it as r4.
10679  svntest.actions.run_and_verify_svn(None, [], 'move',
10680                                     psi_path, psi_moved_path)
10681  expected_output = wc.State(wc_dir, {
10682    'A/D/H/psi'       : Item(verb='Deleting'),
10683    'A/D/H/psi_moved' : Item(verb='Adding')
10684    })
10685  expected_status.add({'A/D/H/psi_moved' : Item(status='  ', wc_rev=4)})
10686  expected_status.remove('A/D/H/psi')
10687
10688  # Replicate old WC-to-WC move behavior where empty mergeinfo was set on
10689  # the move destination.  Pre 1.6 repositories might have mergeinfo like
10690  # this so we still want to test that the issue #3067 fixes tested by
10691  # merge_chokes_on_renamed_subtrees and subtrees_with_empty_mergeinfo
10692  # still work.
10693  svntest.actions.run_and_verify_svn(None, [], 'ps', SVN_PROP_MERGEINFO,
10694                                     "", psi_moved_path)
10695
10696  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
10697                                        expected_status)
10698
10699  # Copy 'A/D/H' to 'H_COPY' in r5.
10700  svntest.actions.run_and_verify_svn(['Committing transaction...\n',
10701                                      'Committed revision 5.\n'],
10702                                     [], 'copy',
10703                                     sbox.repo_url + "/A/D/H",
10704                                     sbox.repo_url + "/H_COPY",
10705                                     "-m", "Copy A/D/H to H_COPY")
10706  expected_status.add({
10707    "H_COPY"       : Item(),
10708    "H_COPY/chi"   : Item(),
10709    "H_COPY/omega" : Item(),
10710    "H_COPY/psi_moved"   : Item()})
10711
10712  # Update to pull the previous copy into the WC
10713  svntest.main.run_svn(None, 'up', wc_dir)
10714  expected_status.tweak(status='  ', wc_rev=5)
10715
10716  # Make a text mod to 'A/D/H/psi_moved' and commit it as r6
10717  svntest.main.file_write(psi_moved_path, "Even *Newer* content")
10718  expected_output = wc.State(wc_dir,
10719                             {'A/D/H/psi_moved' : Item(verb='Sending')})
10720  expected_status.tweak('A/D/H/psi_moved', wc_rev=6)
10721  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
10722                                        expected_status)
10723  expected_disk.remove('A/D/H/psi')
10724  expected_disk.add({
10725    'A/D/H/psi_moved' : Item("Even *Newer* content"),
10726    })
10727
10728  # Update for a uniform working copy before merging.
10729  svntest.main.run_svn(None, 'up', wc_dir)
10730  expected_status.tweak(status='  ', wc_rev=6)
10731
10732  return wc_dir, expected_disk, expected_status
10733
10734#----------------------------------------------------------------------
10735# Test for issue #3174: 'Merge algorithm chokes on subtrees needing
10736# special attention that have been renamed'
10737@SkipUnless(server_has_mergeinfo)
10738@Issue(3174)
10739def merge_chokes_on_renamed_subtrees(sbox):
10740  "merge fails with renamed subtrees with mergeinfo"
10741
10742  # Use helper to setup a renamed subtree.
10743  wc_dir, expected_disk, expected_status = set_up_renamed_subtree(sbox)
10744
10745  # Some paths we'll care about
10746  psi_COPY_moved_path = sbox.ospath('H_COPY/psi_moved')
10747
10748
10749  # Cherry harvest all available revsions from 'A/D/H/psi_moved' to
10750  # 'H_COPY/psi_moved'.
10751  #
10752  # Here is where issue #3174 appears, the merge fails with:
10753  # svn: svn: File not found: revision 3, path '/A/D/H/psi'
10754  svntest.actions.run_and_verify_svn(
10755    expected_merge_output([[5,6],[3,6]],
10756                          ['U    ' + psi_COPY_moved_path + '\n',
10757                           ' U   ' + psi_COPY_moved_path + '\n',
10758                           ' G   ' + psi_COPY_moved_path + '\n',],
10759                          elides=True),
10760    [], 'merge', sbox.repo_url + '/A/D/H/psi_moved',
10761    psi_COPY_moved_path)
10762
10763  expected_status.tweak('H_COPY/psi_moved', status='MM')
10764  svntest.actions.run_and_verify_status(wc_dir, expected_status)
10765
10766
10767#----------------------------------------------------------------------
10768# Issue #3157
10769@SkipUnless(server_has_mergeinfo)
10770@Issue(3157)
10771def dont_explicitly_record_implicit_mergeinfo(sbox):
10772  "don't explicitly record implicit mergeinfo"
10773
10774  sbox.build()
10775  wc_dir = sbox.wc_dir
10776
10777  A_path = sbox.ospath('A')
10778  A_copy_path = sbox.ospath('A_copy')
10779  A_copy2_path = sbox.ospath('A_copy2')
10780  A_copy_mu_path = sbox.ospath('A_copy/mu')
10781  A_copy2_mu_path = sbox.ospath('A_copy2/mu')
10782  nu_path = sbox.ospath('A/D/H/nu')
10783  nu_copy_path = sbox.ospath('A_copy/D/H/nu')
10784
10785  def _commit_and_update(rev, action):
10786    svntest.actions.run_and_verify_svn(None, [],
10787                                       'ci', '-m', 'r%d - %s' % (rev, action),
10788                                       sbox.wc_dir)
10789    svntest.main.run_svn(None, 'up', wc_dir)
10790
10791  # r2 - copy A to A_copy
10792  svntest.main.run_svn(None, 'cp', A_path, A_copy_path)
10793  _commit_and_update(2, "Copy A to A_copy.")
10794
10795  # r3 - tweak A_copy/mu
10796  svntest.main.file_append(A_copy_mu_path, "r3\n")
10797  _commit_and_update(3, "Edit A_copy/mu.")
10798
10799  # r4 - copy A_copy to A_copy2
10800  svntest.main.run_svn(None, 'cp', A_copy_path, A_copy2_path)
10801  _commit_and_update(4, "Copy A_copy to A_copy2.")
10802
10803  # r5 - tweak A_copy2/mu
10804  svntest.main.file_append(A_copy2_mu_path, "r5\n")
10805  _commit_and_update(5, "Edit A_copy2/mu.")
10806
10807  # Merge r5 from A_copy2/mu to A_copy/mu.
10808  #
10809  # run_and_verify_merge doesn't support merging to a file WCPATH
10810  # so use run_and_verify_svn.  Check the resulting mergeinfo with
10811  # a propget.
10812  ### TODO: We can use run_and_verify_merge() here now.
10813  svntest.actions.run_and_verify_svn(
10814    expected_merge_output([[5]], ['U    ' + A_copy_mu_path + '\n',
10815                                  ' U   ' + A_copy_mu_path + '\n']),
10816    [], 'merge', '-c5', sbox.repo_url + '/A_copy2/mu', A_copy_mu_path)
10817  check_mergeinfo_recursively(A_copy_mu_path,
10818                              { A_copy_mu_path: '/A_copy2/mu:5' })
10819
10820  # Now, merge A_copy2 (in full) back to A_copy.  This should result in
10821  # mergeinfo of '/A_copy2:4-5' on A_copy and '/A_copy2/mu:4-5' on A_copy/mu
10822  # and the latter should elide to the former.  Any revisions < 4 are part of
10823  # A_copy's natural history and should not be explicitly recorded.
10824  expected_output = wc.State(A_copy_path, {})
10825  expected_mergeinfo_output = wc.State(A_copy_path, {
10826    ''   : Item(status=' U'),
10827    })
10828  expected_elision_output = wc.State(A_copy_path, {
10829    })
10830  expected_disk = wc.State('', {
10831    ''          : Item(props={SVN_PROP_MERGEINFO : '/A_copy2:4-5'}),
10832    'mu'        : Item("This is the file 'mu'.\nr3\nr5\n",
10833                       props={SVN_PROP_MERGEINFO : '/A_copy2/mu:5'}),
10834    'B'         : Item(),
10835    'B/lambda'  : Item("This is the file 'lambda'.\n"),
10836    'B/E'       : Item(),
10837    'B/E/alpha' : Item("This is the file 'alpha'.\n"),
10838    'B/E/beta'  : Item("This is the file 'beta'.\n"),
10839    'B/F'       : Item(),
10840    'C'         : Item(),
10841    'D'         : Item(),
10842    'D/gamma'   : Item("This is the file 'gamma'.\n"),
10843    'D/H'       : Item(),
10844    'D/H/chi'   : Item("This is the file 'chi'.\n"),
10845    'D/H/psi'   : Item("This is the file 'psi'.\n"),
10846    'D/H/omega' : Item("This is the file 'omega'.\n"),
10847    'D/G'       : Item(),
10848    'D/G/pi'    : Item("This is the file 'pi'.\n"),
10849    'D/G/rho'   : Item("This is the file 'rho'.\n"),
10850    'D/G/tau'   : Item("This is the file 'tau'.\n"),
10851    })
10852  expected_status = wc.State(A_copy_path, {
10853    ''          : Item(status=' M'),
10854    'mu'        : Item(status='MM'),
10855    'B'         : Item(status='  '),
10856    'B/lambda'  : Item(status='  '),
10857    'B/E'       : Item(status='  '),
10858    'B/E/alpha' : Item(status='  '),
10859    'B/E/beta'  : Item(status='  '),
10860    'B/F'       : Item(status='  '),
10861    'C'         : Item(status='  '),
10862    'D'         : Item(status='  '),
10863    'D/gamma'   : Item(status='  '),
10864    'D/H'       : Item(status='  '),
10865    'D/H/chi'   : Item(status='  '),
10866    'D/H/psi'   : Item(status='  '),
10867    'D/H/omega' : Item(status='  '),
10868    'D/G'       : Item(status='  '),
10869    'D/G/pi'    : Item(status='  '),
10870    'D/G/rho'   : Item(status='  '),
10871    'D/G/tau'   : Item(status='  '),
10872    })
10873  expected_status.tweak(wc_rev=5)
10874  expected_skip = wc.State(A_copy_path, { })
10875  svntest.actions.run_and_verify_merge(A_copy_path, None, None,
10876                                       sbox.repo_url + '/A_copy2', None,
10877                                       expected_output,
10878                                       expected_mergeinfo_output,
10879                                       expected_elision_output,
10880                                       expected_disk,
10881                                       expected_status, expected_skip,
10882                                       check_props=True)
10883
10884  # Revert the previous merges and try a cherry harvest merge where
10885  # the subtree's natural history is a proper subset of the merge.
10886  svntest.actions.run_and_verify_svn(None, [], 'revert', '-R', wc_dir)
10887
10888  svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir)
10889  wc_status = svntest.actions.get_virginal_state(wc_dir, 5)
10890  wc_status.add({
10891    'A_copy'            : Item(),
10892    'A_copy/B'          : Item(),
10893    'A_copy/B/lambda'   : Item(),
10894    'A_copy/B/E'        : Item(),
10895    'A_copy/B/E/alpha'  : Item(),
10896    'A_copy/B/E/beta'   : Item(),
10897    'A_copy/B/F'        : Item(),
10898    'A_copy/mu'         : Item(),
10899    'A_copy/C'          : Item(),
10900    'A_copy/D'          : Item(),
10901    'A_copy/D/gamma'    : Item(),
10902    'A_copy/D/G'        : Item(),
10903    'A_copy/D/G/pi'     : Item(),
10904    'A_copy/D/G/rho'    : Item(),
10905    'A_copy/D/G/tau'    : Item(),
10906    'A_copy/D/H'        : Item(),
10907    'A_copy/D/H/chi'    : Item(),
10908    'A_copy/D/H/omega'  : Item(),
10909    'A_copy/D/H/psi'    : Item(),
10910    'A_copy2'           : Item(),
10911    'A_copy2/B'         : Item(),
10912    'A_copy2/B/lambda'  : Item(),
10913    'A_copy2/B/E'       : Item(),
10914    'A_copy2/B/E/alpha' : Item(),
10915    'A_copy2/B/E/beta'  : Item(),
10916    'A_copy2/B/F'       : Item(),
10917    'A_copy2/mu'        : Item(),
10918    'A_copy2/C'         : Item(),
10919    'A_copy2/D'         : Item(),
10920    'A_copy2/D/gamma'   : Item(),
10921    'A_copy2/D/G'       : Item(),
10922    'A_copy2/D/G/pi'    : Item(),
10923    'A_copy2/D/G/rho'   : Item(),
10924    'A_copy2/D/G/tau'   : Item(),
10925    'A_copy2/D/H'       : Item(),
10926    'A_copy2/D/H/chi'   : Item(),
10927    'A_copy2/D/H/omega' : Item(),
10928    'A_copy2/D/H/psi'   : Item(),
10929    })
10930  wc_status.tweak(status='  ', wc_rev=5)
10931
10932  # r6 - Add the file 'A/D/H/nu'.
10933  svntest.main.file_write(nu_path, "This is the file 'nu'.\n")
10934  svntest.actions.run_and_verify_svn(None, [], 'add', nu_path)
10935  expected_output = wc.State(wc_dir, {'A/D/H/nu' : Item(verb='Adding')})
10936  wc_status.add({'A/D/H/nu' : Item(status='  ', wc_rev=6)})
10937  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
10938                                        wc_status)
10939
10940  # r7 - Make a change to 'A/D/H/nu'.
10941  svntest.main.file_write(nu_path, "Nu content")
10942  expected_output = wc.State(wc_dir, {'A/D/H/nu' : Item(verb='Sending')})
10943  wc_status.tweak('A/D/H/nu', wc_rev=7)
10944  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
10945                                        wc_status)
10946
10947  # r8 - Merge r6 to 'A_copy'.
10948  expected_output = wc.State(A_copy_path, {
10949    'D/H/nu' : Item(status='A '),
10950    })
10951  expected_mergeinfo_output = wc.State(A_copy_path, {
10952    ''   : Item(status=' U'),
10953    })
10954  expected_elision_output = wc.State(A_copy_path, {
10955    })
10956  expected_A_copy_status = wc.State(A_copy_path, {
10957    ''          : Item(status=' M', wc_rev=5),
10958    'B'         : Item(status='  ', wc_rev=5),
10959    'mu'        : Item(status='  ', wc_rev=5),
10960    'B/E'       : Item(status='  ', wc_rev=5),
10961    'B/E/alpha' : Item(status='  ', wc_rev=5),
10962    'B/E/beta'  : Item(status='  ', wc_rev=5),
10963    'B/lambda'  : Item(status='  ', wc_rev=5),
10964    'B/F'       : Item(status='  ', wc_rev=5),
10965    'C'         : Item(status='  ', wc_rev=5),
10966    'D'         : Item(status='  ', wc_rev=5),
10967    'D/G'       : Item(status='  ', wc_rev=5),
10968    'D/G/pi'    : Item(status='  ', wc_rev=5),
10969    'D/G/rho'   : Item(status='  ', wc_rev=5),
10970    'D/G/tau'   : Item(status='  ', wc_rev=5),
10971    'D/gamma'   : Item(status='  ', wc_rev=5),
10972    'D/H'       : Item(status='  ', wc_rev=5),
10973    'D/H/chi'   : Item(status='  ', wc_rev=5),
10974    'D/H/psi'   : Item(status='  ', wc_rev=5),
10975    'D/H/omega' : Item(status='  ', wc_rev=5),
10976    'D/H/nu'    : Item(status='A ', wc_rev='-', copied='+'),
10977    })
10978  expected_A_copy_disk = wc.State('', {
10979    ''          : Item(props={SVN_PROP_MERGEINFO : '/A:6'}),
10980    'B'         : Item(),
10981    'mu'        : Item("This is the file 'mu'.\nr3\n"),
10982    'B/E'       : Item(),
10983    'B/E/alpha' : Item("This is the file 'alpha'.\n"),
10984    'B/E/beta'  : Item("This is the file 'beta'.\n"),
10985    'B/lambda'  : Item("This is the file 'lambda'.\n"),
10986    'B/F'       : Item(),
10987    'C'         : Item(),
10988    'D'         : Item(),
10989    'D/G'       : Item(),
10990    'D/G/pi'    : Item("This is the file 'pi'.\n"),
10991    'D/G/rho'   : Item("This is the file 'rho'.\n"),
10992    'D/G/tau'   : Item("This is the file 'tau'.\n"),
10993    'D/gamma'   : Item("This is the file 'gamma'.\n"),
10994    'D/H'       : Item(),
10995    'D/H/chi'   : Item("This is the file 'chi'.\n"),
10996    'D/H/psi'   : Item("This is the file 'psi'.\n"),
10997    'D/H/omega' : Item("This is the file 'omega'.\n"),
10998    'D/H/nu'    : Item("This is the file 'nu'.\n"),
10999    })
11000  expected_A_copy_skip = wc.State(A_copy_path, {})
11001  svntest.actions.run_and_verify_merge(A_copy_path, '5', '6',
11002                                       sbox.repo_url + '/A', None,
11003                                       expected_output,
11004                                       expected_mergeinfo_output,
11005                                       expected_elision_output,
11006                                       expected_A_copy_disk,
11007                                       expected_A_copy_status,
11008                                       expected_A_copy_skip,
11009                                       check_props=True)
11010  wc_status.add({'A_copy/D/H/nu' : Item(status='  ', wc_rev=8)})
11011  wc_status.tweak('A_copy', wc_rev=8)
11012  expected_output = wc.State(wc_dir, {
11013    'A_copy/D/H/nu' : Item(verb='Adding'),
11014    'A_copy'        : Item(verb='Sending'),
11015    })
11016  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
11017                                        wc_status)
11018
11019  # r9 - Merge r7 to 'A_copy/D/H/nu'.
11020  expected_skip = wc.State(nu_copy_path, { })
11021  # run_and_verify_merge doesn't support merging to a file WCPATH
11022  # so use run_and_verify_svn.
11023  ### TODO: We can use run_and_verify_merge() here now.
11024  svntest.actions.run_and_verify_svn(
11025    expected_merge_output([[7]],
11026                          ['U    ' + nu_copy_path + '\n',
11027                           ' G   ' + nu_copy_path + '\n',]),
11028    [], 'merge', '-c7', sbox.repo_url + '/A/D/H/nu', nu_copy_path)
11029  expected_output = wc.State(wc_dir, {'A_copy/D/H/nu' : Item(verb='Sending')})
11030  wc_status.tweak('A_copy/D/H/nu', wc_rev=9)
11031  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
11032                                        wc_status)
11033
11034  # Update WC
11035  svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir)
11036  wc_status.tweak(wc_rev=9)
11037
11038  # r10 - Make another change to 'A/D/H/nu'.
11039  svntest.main.file_write(nu_path, "Even nuer content")
11040  expected_output = wc.State(wc_dir, {'A/D/H/nu' : Item(verb='Sending')})
11041  wc_status.tweak('A/D/H/nu', wc_rev=10)
11042  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
11043                                        wc_status)
11044
11045  # Update WC
11046  svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir)
11047  wc_status.tweak(wc_rev=10)
11048
11049  # Now do a cherry harvest merge to 'A_copy'.
11050  expected_output = wc.State(A_copy_path, {
11051    'D/H/nu' : Item(status='U '),
11052    })
11053  expected_mergeinfo_output = wc.State(A_copy_path, {
11054    ''   : Item(status=' U'),
11055    'D/H/nu' : Item(status=' U'),
11056    })
11057  expected_elision_output = wc.State(A_copy_path, {
11058    })
11059  expected_A_copy_status = wc.State(A_copy_path, {
11060    ''          : Item(status=' M', wc_rev=10),
11061    'B'         : Item(status='  ', wc_rev=10),
11062    'mu'        : Item(status='  ', wc_rev=10),
11063    'B/E'       : Item(status='  ', wc_rev=10),
11064    'B/E/alpha' : Item(status='  ', wc_rev=10),
11065    'B/E/beta'  : Item(status='  ', wc_rev=10),
11066    'B/lambda'  : Item(status='  ', wc_rev=10),
11067    'B/F'       : Item(status='  ', wc_rev=10),
11068    'C'         : Item(status='  ', wc_rev=10),
11069    'D'         : Item(status='  ', wc_rev=10),
11070    'D/G'       : Item(status='  ', wc_rev=10),
11071    'D/G/pi'    : Item(status='  ', wc_rev=10),
11072    'D/G/rho'   : Item(status='  ', wc_rev=10),
11073    'D/G/tau'   : Item(status='  ', wc_rev=10),
11074    'D/gamma'   : Item(status='  ', wc_rev=10),
11075    'D/H'       : Item(status='  ', wc_rev=10),
11076    'D/H/chi'   : Item(status='  ', wc_rev=10),
11077    'D/H/psi'   : Item(status='  ', wc_rev=10),
11078    'D/H/omega' : Item(status='  ', wc_rev=10),
11079    'D/H/nu'    : Item(status='MM', wc_rev=10),
11080    })
11081  expected_A_copy_disk = wc.State('', {
11082    ''          : Item(props={SVN_PROP_MERGEINFO : '/A:2-10'}),
11083    'B'         : Item(),
11084    'mu'        : Item("This is the file 'mu'.\nr3\n"),
11085    'B/E'       : Item(),
11086    'B/E/alpha' : Item("This is the file 'alpha'.\n"),
11087    'B/E/beta'  : Item("This is the file 'beta'.\n"),
11088    'B/lambda'  : Item("This is the file 'lambda'.\n"),
11089    'B/F'       : Item(),
11090    'C'         : Item(),
11091    'D'         : Item(),
11092    'D/G'       : Item(),
11093    'D/G/pi'    : Item("This is the file 'pi'.\n"),
11094    'D/G/rho'   : Item("This is the file 'rho'.\n"),
11095    'D/G/tau'   : Item("This is the file 'tau'.\n"),
11096    'D/gamma'   : Item("This is the file 'gamma'.\n"),
11097    'D/H'       : Item(),
11098    'D/H/chi'   : Item("This is the file 'chi'.\n"),
11099    'D/H/psi'   : Item("This is the file 'psi'.\n"),
11100    'D/H/omega' : Item("This is the file 'omega'.\n"),
11101    'D/H/nu'    : Item("Even nuer content",
11102                       props={SVN_PROP_MERGEINFO : '/A/D/H/nu:6-10'}),
11103    })
11104  expected_A_copy_skip = wc.State(A_copy_path, {})
11105  svntest.actions.run_and_verify_merge(A_copy_path, None, None,
11106                                       sbox.repo_url + '/A', None,
11107                                       expected_output,
11108                                       expected_mergeinfo_output,
11109                                       expected_elision_output,
11110                                       expected_A_copy_disk,
11111                                       expected_A_copy_status,
11112                                       expected_A_copy_skip,
11113                                       check_props=True)
11114
11115#----------------------------------------------------------------------
11116# Test for issue where merging a change to a broken link fails
11117@SkipUnless(svntest.main.is_posix_os)
11118def merge_broken_link(sbox):
11119  "merge with broken symlinks in target"
11120
11121  # Create our good 'ole greek tree.
11122  sbox.build()
11123  wc_dir = sbox.wc_dir
11124  src_path = sbox.ospath('A/B/E')
11125  copy_path = sbox.ospath('A/B/E_COPY')
11126  link_path = os.path.join(src_path, 'beta_link')
11127
11128  os.symlink('beta_broken', link_path)
11129  svntest.main.run_svn(None, 'add', link_path)
11130  svntest.main.run_svn(None, 'commit', '-m', 'Create a broken link', link_path)
11131  svntest.main.run_svn(None, 'copy', src_path, copy_path)
11132  svntest.main.run_svn(None, 'commit', '-m', 'Copy the tree with the broken link',
11133                       copy_path)
11134  os.unlink(link_path)
11135  os.symlink('beta', link_path)
11136  svntest.main.run_svn(None, 'commit', '-m', 'Fix a broken link', link_path)
11137  svntest.actions.run_and_verify_svn(
11138    expected_merge_output([[4]],
11139                          ['U    ' + copy_path + '/beta_link\n',
11140                           ' U   ' + copy_path + '\n']),
11141    [], 'merge', '-c4', src_path, copy_path)
11142
11143#----------------------------------------------------------------------
11144# Test for issue #3199 'Subtree merges broken when required ranges
11145# don't intersect with merge target'
11146@SkipUnless(server_has_mergeinfo)
11147@Issue(3199)
11148def subtree_merges_dont_intersect_with_targets(sbox):
11149  "subtree ranges might not intersect with target"
11150
11151  sbox.build()
11152  wc_dir = sbox.wc_dir
11153
11154  # Make two branches to merge to.
11155  wc_disk, wc_status = set_up_branch(sbox, False, 2)
11156
11157  # Some paths we'll care about.
11158  A_COPY_path     = sbox.ospath('A_COPY')
11159  A_COPY_2_path   = sbox.ospath('A_COPY_2')
11160  H_COPY_2_path   = sbox.ospath('A_COPY_2/D/H')
11161  gamma_path      = sbox.ospath('A/D/gamma')
11162  psi_path        = sbox.ospath('A/D/H/psi')
11163  psi_COPY_path   = sbox.ospath('A_COPY/D/H/psi')
11164  gamma_COPY_path = sbox.ospath('A_COPY/D/gamma')
11165  psi_COPY_path   = sbox.ospath('A_COPY/D/H/psi')
11166  psi_COPY_2_path = sbox.ospath('A_COPY_2/D/H/psi')
11167  rho_COPY_2_path = sbox.ospath('A_COPY_2/D/G/rho')
11168
11169  # Make a tweak to A/D/gamma and A/D/H/psi in r8.
11170  svntest.main.file_write(gamma_path, "New content")
11171  svntest.main.file_write(psi_path, "Even newer content")
11172  expected_output = wc.State(wc_dir, {
11173    'A/D/gamma' : Item(verb='Sending'),
11174    'A/D/H/psi' : Item(verb='Sending'),
11175    })
11176  wc_status.tweak('A/D/gamma', 'A/D/H/psi', wc_rev=8)
11177  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
11178                                        wc_status)
11179  wc_disk.tweak('A/D/gamma', contents="New content")
11180  wc_disk.tweak('A/D/H/psi', contents="Even newer content")
11181
11182  # Update the WC.
11183  svntest.actions.run_and_verify_svn(exp_noop_up_out(8), [],
11184                                     'update', wc_dir)
11185  wc_status.tweak(wc_rev=8)
11186
11187  # Run a bunch of merges to setup the 2 branches with explicit
11188  # mergeinfo on each branch root and explicit mergeinfo on one subtree
11189  # of each root.  The mergeinfo should be such that:
11190  #
11191  #   1) On one branch: The mergeinfo on the root and the subtree do
11192  #      not intersect.
11193  #
11194  #   2) On the other branch: The mergeinfo on the root and subtree
11195  #      are each 'missing' and eligible ranges and these missing
11196  #      ranges do not intersect.
11197  #
11198  #   Note: We just use run_and_verify_svn(...'merge'...) here rather than
11199  #         run_and_verify_merge() because these types of simple merges are
11200  #         tested to death elsewhere and this is just setup for the "real"
11201  #         test.
11202  svntest.actions.run_and_verify_svn(None, [],
11203                                     'merge', '-c4',
11204                                     sbox.repo_url + '/A/D/H/psi',
11205                                     psi_COPY_path)
11206  svntest.actions.run_and_verify_svn(None, [],
11207                                     'merge', '-c8',
11208                                     sbox.repo_url + '/A',
11209                                     A_COPY_path)
11210  svntest.actions.run_and_verify_svn(None, [],
11211                                     'merge', '-c-8',
11212                                     sbox.repo_url + '/A/D/H/psi',
11213                                     psi_COPY_path)
11214  svntest.actions.run_and_verify_svn(None, [],
11215                                     'merge',
11216                                     sbox.repo_url + '/A',
11217                                     A_COPY_2_path)
11218  svntest.actions.run_and_verify_svn(None, [],
11219                                     'merge', '-c-5',
11220                                     sbox.repo_url + '/A',
11221                                     A_COPY_2_path)
11222  svntest.actions.run_and_verify_svn(None, [],
11223                                     'merge', '-c5', '-c-8',
11224                                     sbox.repo_url + '/A/D/H',
11225                                     H_COPY_2_path)
11226
11227  # Commit all the previous merges as r9.
11228  expected_output = wc.State(wc_dir, {
11229    'A_COPY'             : Item(verb='Sending'),
11230    'A_COPY/D/H/psi'     : Item(verb='Sending'),
11231    'A_COPY/D/gamma'     : Item(verb='Sending'),
11232    'A_COPY_2'           : Item(verb='Sending'),
11233    'A_COPY_2/B/E/beta'  : Item(verb='Sending'),
11234    'A_COPY_2/D/H'       : Item(verb='Sending'),
11235    'A_COPY_2/D/H/omega' : Item(verb='Sending'),
11236    'A_COPY_2/D/H/psi'   : Item(verb='Sending'),
11237    'A_COPY_2/D/gamma'   : Item(verb='Sending'),
11238    })
11239  wc_status.tweak('A_COPY',
11240                  'A_COPY/D/H/psi',
11241                  'A_COPY/D/gamma',
11242                  'A_COPY_2',
11243                  'A_COPY_2/B/E/beta',
11244                  'A_COPY_2/D/H',
11245                  'A_COPY_2/D/H/omega',
11246                  'A_COPY_2/D/H/psi',
11247                  'A_COPY_2/D/gamma',
11248                  wc_rev=9)
11249  svntest.actions.run_and_verify_commit(wc_dir,
11250                                        expected_output,
11251                                        wc_status)
11252
11253  # Update the WC.
11254  svntest.actions.run_and_verify_svn(exp_noop_up_out(9), [],
11255                                     'update', wc_dir)
11256
11257  # Make sure we have mergeinfo that meets the two criteria set out above.
11258  check_mergeinfo_recursively(wc_dir,
11259                              { # Criterion 1
11260                                A_COPY_path: '/A:8',
11261                                psi_COPY_path: '/A/D/H/psi:4',
11262                                # Criterion 2
11263                                A_COPY_2_path : '/A:3-4,6-8',
11264                                H_COPY_2_path : '/A/D/H:3-7' })
11265
11266  # Merging to the criterion 2 branch.
11267  #
11268  # Forward merge a range to a target with a subtree where the target
11269  # and subtree need different, non-intersecting revision ranges applied:
11270  # Merge r3:9 from A into A_COPY_2.
11271  #
11272  # The subtree A_COPY_2/D/H needs r8-9 applied (affecting A_COPY_2/D/H/psi)
11273  # while the target needs r5 (affecting A_COPY_2/D/G/rho) applied.  The
11274  # resulting mergeinfo on A_COPY_2 and A_COPY_2/D/H should be equivalent
11275  # and therefore elide to A_COPY_2.
11276  expected_output = wc.State(A_COPY_2_path, {
11277    'D/G/rho'   : Item(status='U '),
11278    'D/H/psi'   : Item(status='U '),
11279    })
11280  expected_mergeinfo_output = wc.State(A_COPY_2_path, {
11281    ''    : Item(status=' U'),
11282    'D/H' : Item(status=' U'),
11283    })
11284  expected_elision_output = wc.State(A_COPY_2_path, {
11285    'D/H' : Item(status=' U'),
11286    })
11287  expected_status = wc.State(A_COPY_2_path, {
11288    ''          : Item(status=' M', wc_rev=9),
11289    'B'         : Item(status='  ', wc_rev=9),
11290    'mu'        : Item(status='  ', wc_rev=9),
11291    'B/E'       : Item(status='  ', wc_rev=9),
11292    'B/E/alpha' : Item(status='  ', wc_rev=9),
11293    'B/E/beta'  : Item(status='  ', wc_rev=9),
11294    'B/lambda'  : Item(status='  ', wc_rev=9),
11295    'B/F'       : Item(status='  ', wc_rev=9),
11296    'C'         : Item(status='  ', wc_rev=9),
11297    'D'         : Item(status='  ', wc_rev=9),
11298    'D/G'       : Item(status='  ', wc_rev=9),
11299    'D/G/pi'    : Item(status='  ', wc_rev=9),
11300    'D/G/rho'   : Item(status='M ', wc_rev=9),
11301    'D/G/tau'   : Item(status='  ', wc_rev=9),
11302    'D/gamma'   : Item(status='  ', wc_rev=9),
11303    'D/H'       : Item(status=' M', wc_rev=9),
11304    'D/H/chi'   : Item(status='  ', wc_rev=9),
11305    'D/H/psi'   : Item(status='M ', wc_rev=9),
11306    'D/H/omega' : Item(status='  ', wc_rev=9),
11307    })
11308  expected_disk = wc.State('', {
11309    ''          : Item(props={SVN_PROP_MERGEINFO : '/A:3-9'}),
11310    'B'         : Item(),
11311    'mu'        : Item("This is the file 'mu'.\n"),
11312    'B/E'       : Item(),
11313    'B/E/alpha' : Item("This is the file 'alpha'.\n"),
11314    'B/E/beta'  : Item("New content"),
11315    'B/lambda'  : Item("This is the file 'lambda'.\n"),
11316    'B/F'       : Item(),
11317    'C'         : Item(),
11318    'D'         : Item(),
11319    'D/G'       : Item(),
11320    'D/G/pi'    : Item("This is the file 'pi'.\n"),
11321    'D/G/rho'   : Item("New content"),
11322    'D/G/tau'   : Item("This is the file 'tau'.\n"),
11323    'D/gamma'   : Item("New content"),
11324    'D/H'       : Item(),
11325    'D/H/chi'   : Item("This is the file 'chi'.\n"),
11326    'D/H/psi'   : Item("Even newer content"),
11327    'D/H/omega' : Item("New content"),
11328    })
11329  expected_skip = wc.State(A_COPY_2_path, {})
11330  svntest.actions.run_and_verify_merge(A_COPY_2_path, '3', '9',
11331                                       sbox.repo_url + '/A', None,
11332                                       expected_output,
11333                                       expected_mergeinfo_output,
11334                                       expected_elision_output,
11335                                       expected_disk,
11336                                       expected_status,
11337                                       expected_skip,
11338                                       check_props=True)
11339
11340  # Merging to the criterion 1 branch.
11341  #
11342  # Reverse merge a range to a target with a subtree where the target
11343  # and subtree need different, non-intersecting revision ranges
11344  # reversed: Merge r9:3 from A into A_COPY.
11345  #
11346  # The subtree A_COPY_2/D/H/psi needs r4 reversed, while the target needs
11347  # r8 (affecting A_COPY/D/gamma) reversed.  Since this reverses all merges
11348  # thus far to A_COPY, there should be *no* mergeinfo post merge.
11349  expected_output = wc.State(A_COPY_path, {
11350    'D/gamma'   : Item(status='U '),
11351    'D/H/psi'   : Item(status='U '),
11352    })
11353  expected_mergeinfo_output = wc.State(A_COPY_path, {
11354    ''        : Item(status=' U'),
11355    'D/H/psi' : Item(status=' U'),
11356    })
11357  expected_elision_output = wc.State(A_COPY_path, {
11358    ''        : Item(status=' U'),
11359    'D/H/psi' : Item(status=' U'),
11360    })
11361  expected_status = wc.State(A_COPY_path, {
11362    ''          : Item(status=' M', wc_rev=9),
11363    'B'         : Item(status='  ', wc_rev=9),
11364    'mu'        : Item(status='  ', wc_rev=9),
11365    'B/E'       : Item(status='  ', wc_rev=9),
11366    'B/E/alpha' : Item(status='  ', wc_rev=9),
11367    'B/E/beta'  : Item(status='  ', wc_rev=9),
11368    'B/lambda'  : Item(status='  ', wc_rev=9),
11369    'B/F'       : Item(status='  ', wc_rev=9),
11370    'C'         : Item(status='  ', wc_rev=9),
11371    'D'         : Item(status='  ', wc_rev=9),
11372    'D/G'       : Item(status='  ', wc_rev=9),
11373    'D/G/pi'    : Item(status='  ', wc_rev=9),
11374    'D/G/rho'   : Item(status='  ', wc_rev=9),
11375    'D/G/tau'   : Item(status='  ', wc_rev=9),
11376    'D/gamma'   : Item(status='M ', wc_rev=9),
11377    'D/H'       : Item(status='  ', wc_rev=9),
11378    'D/H/chi'   : Item(status='  ', wc_rev=9),
11379    'D/H/psi'   : Item(status='MM', wc_rev=9),
11380    'D/H/omega' : Item(status='  ', wc_rev=9),
11381    })
11382  expected_disk = wc.State('', {
11383    'B'         : Item(),
11384    'mu'        : Item("This is the file 'mu'.\n"),
11385    'B/E'       : Item(),
11386    'B/E/alpha' : Item("This is the file 'alpha'.\n"),
11387    'B/E/beta'  : Item("This is the file 'beta'.\n"),
11388    'B/lambda'  : Item("This is the file 'lambda'.\n"),
11389    'B/F'       : Item(),
11390    'C'         : Item(),
11391    'D'         : Item(),
11392    'D/G'       : Item(),
11393    'D/G/pi'    : Item("This is the file 'pi'.\n"),
11394    'D/G/rho'   : Item("This is the file 'rho'.\n"),
11395    'D/G/tau'   : Item("This is the file 'tau'.\n"),
11396    'D/gamma'   : Item("This is the file 'gamma'.\n"),
11397    'D/H'       : Item(),
11398    'D/H/chi'   : Item("This is the file 'chi'.\n"),
11399    'D/H/psi'   : Item("This is the file 'psi'.\n"),
11400    'D/H/omega' : Item("This is the file 'omega'.\n"),
11401    })
11402  expected_skip = wc.State(A_COPY_path, {})
11403  svntest.actions.run_and_verify_merge(A_COPY_path, '9', '3',
11404                                       sbox.repo_url + '/A', None,
11405                                       expected_output,
11406                                       expected_mergeinfo_output,
11407                                       expected_elision_output,
11408                                       expected_disk,
11409                                       expected_status,
11410                                       expected_skip,
11411                                       check_props=True)
11412
11413  # Test the notification portion of issue #3199.
11414  #
11415  # run_and_verify_merge() doesn't check the notification headers
11416  # so we need to repeat the previous two merges using
11417  # run_and_verify_svn(...'merge'...) and expected_merge_output().
11418  #
11419  ### TODO: Things are fairly ugly when it comes to testing the
11420  ###       merge notification headers.  run_and_verify_merge*()
11421  ###       just ignores the notifications and in the few places
11422  ###       we use expected_merge_output() the order of notifications
11423  ###       and paths are not considered.  In a perfect world we'd
11424  ###       have run_and_verify_merge() that addressed these
11425  ###       shortcomings (and allowed merges to file targets).
11426  #
11427  # Revert the previous merges.
11428  svntest.actions.run_and_verify_svn(None, [], 'revert', '-R', wc_dir)
11429
11430  # Repeat the forward merge
11431  expected_output = expected_merge_output(
11432    [[5],[8],[5,9]],
11433    ['U    %s\n' % (rho_COPY_2_path),
11434     'U    %s\n' % (psi_COPY_2_path),
11435     ' U   %s\n' % (H_COPY_2_path),
11436     ' U   %s\n' % (A_COPY_2_path),],
11437    elides=True)
11438  svntest.actions.run_and_verify_svn(expected_output,
11439                                     [], 'merge', '-r', '3:9',
11440                                     sbox.repo_url + '/A',
11441                                     A_COPY_2_path)
11442  # Repeat the reverse merge
11443  expected_output = expected_merge_output(
11444    [[-4],[-8],[8,4]],
11445    ['U    %s\n' % (gamma_COPY_path),
11446     'U    %s\n' % (psi_COPY_path),
11447     ' U   %s\n' % (A_COPY_path),
11448     ' U   %s\n' % (psi_COPY_path)],
11449    elides=True)
11450  svntest.actions.run_and_verify_svn(expected_output,
11451                                     [], 'merge', '-r', '9:3',
11452                                     sbox.repo_url + '/A',
11453                                     A_COPY_path)
11454
11455#----------------------------------------------------------------------
11456# Some more tests for issue #3067 'subtrees that don't exist at the start
11457# or end of a merge range shouldn't break the merge'
11458@Issue(3067)
11459@SkipUnless(server_has_mergeinfo)
11460def subtree_source_missing_in_requested_range(sbox):
11461  "subtree merge source might not exist"
11462
11463  sbox.build()
11464  wc_dir = sbox.wc_dir
11465
11466  # Make a branch to merge to.
11467  wc_disk, wc_status = set_up_branch(sbox, False, 1)
11468
11469  # Some paths we'll care about.
11470  psi_path        = sbox.ospath('A/D/H/psi')
11471  omega_path      = sbox.ospath('A/D/H/omega')
11472  A_COPY_path     = sbox.ospath('A_COPY')
11473  psi_COPY_path   = sbox.ospath('A_COPY/D/H/psi')
11474  omega_COPY_path = sbox.ospath('A_COPY/D/H/omega')
11475
11476  # r7 Delete A/D/H/psi.
11477  svntest.actions.run_and_verify_svn(None, [],
11478                                     'delete', psi_path)
11479  sbox.simple_commit(message='delete psi')
11480
11481  # r8 - modify A/D/H/omega.
11482  svntest.main.file_write(os.path.join(omega_path), "Even newer content")
11483  sbox.simple_commit(message='modify omega')
11484
11485  # r9 - Merge r3 to A_COPY/D/H/psi
11486  expected_output = expected_merge_output(
11487    [[3]], ['U    %s\n' % (psi_COPY_path),
11488            ' U   %s\n' % (psi_COPY_path),])
11489  svntest.actions.run_and_verify_svn(expected_output, [],
11490                                     'merge', '-c', '3',
11491                                     sbox.repo_url + '/A/D/H/psi@3',
11492                                     psi_COPY_path)
11493  sbox.simple_commit(message='merge r3 to A_COPY/D/H/psi')
11494
11495  # r10 - Merge r6 to A_COPY/D/H/omega.
11496  expected_output = expected_merge_output(
11497    [[6]], ['U    %s\n' % (omega_COPY_path),
11498            ' U   %s\n' % (omega_COPY_path),])
11499  svntest.actions.run_and_verify_svn(expected_output, [],
11500                                     'merge', '-c', '6',
11501                                     sbox.repo_url + '/A/D/H/omega',
11502                                     omega_COPY_path)
11503  sbox.simple_commit(message='merge r6 to A_COPY')
11504  svntest.actions.run_and_verify_svn(exp_noop_up_out(10), [], 'up',
11505                                     wc_dir)
11506
11507  # r11 - Merge r8 to A_COPY.
11508  expected_output = expected_merge_output(
11509    [[8]], ['U    %s\n' % (omega_COPY_path),
11510            ' U   %s\n' % (omega_COPY_path),
11511            ' U   %s\n' % (A_COPY_path)])
11512  svntest.actions.run_and_verify_svn(expected_output, [],
11513                                     'merge', '-c', '8',
11514                                     sbox.repo_url + '/A',
11515                                     A_COPY_path)
11516  # Repeat the merge using the --record-only option so A_COPY/D/H/psi gets
11517  # mergeinfo including 'A/D/H/psi:8', which doesn't exist.  Why?  Because
11518  # we are trying to create mergeinfo that will provoke an invalid editor
11519  # drive.  In 1.5-1.6 merge updated all subtrees, regardless of whether the
11520  # merge touched these subtrees.  This --record-only merge duplicates that
11521  # behavior, allowing us to test the relevant issue #3067 fixes.
11522  expected_output = expected_merge_output(
11523    [[8]], [' G   %s\n' % (omega_COPY_path),
11524            ' U   %s\n' % (psi_COPY_path),
11525            ' G   %s\n' % (A_COPY_path)])
11526  svntest.actions.run_and_verify_svn(expected_output, [],
11527                                     'merge', '-c', '8',
11528                                     sbox.repo_url + '/A',
11529                                     A_COPY_path, '--record-only')
11530  sbox.simple_commit(message='merge r8 to A_COPY/D/H/omega')
11531  svntest.actions.run_and_verify_svn(exp_noop_up_out(11), [], 'up',
11532                                     wc_dir)
11533
11534  # r12 - modify A/D/H/omega yet again.
11535  svntest.main.file_write(os.path.join(omega_path),
11536                          "Now with fabulous new content!")
11537  sbox.simple_commit(message='modify omega')
11538
11539  # r13 - Merge all available revs to A_COPY/D/H/omega.
11540  expected_output = expected_merge_output(
11541    [[9,12],[2,12]], ['U    %s\n' % (omega_COPY_path),
11542               ' U   %s\n' % (omega_COPY_path)])
11543  svntest.actions.run_and_verify_svn(expected_output, [],
11544                                     'merge',
11545                                     sbox.repo_url + '/A/D/H/omega',
11546                                     omega_COPY_path)
11547  sbox.simple_commit(message='cherry harvest to A_COPY/D/H/omega')
11548  svntest.actions.run_and_verify_svn(exp_noop_up_out(13), [], 'up',
11549                                     wc_dir)
11550
11551  # Check that svn:mergeinfo is as expected.
11552  check_mergeinfo_recursively(wc_dir,
11553                              { A_COPY_path: '/A:8',
11554                                omega_COPY_path: '/A/D/H/omega:2-12',
11555                                psi_COPY_path : '/A/D/H/psi:3,8' })
11556
11557  # Now test a reverse merge where part of the requested range postdates
11558  # a subtree's existence.  Merge -r12:1 to A_COPY.  This should revert
11559  # all of the merges done thus far.  The fact that A/D/H/psi no longer
11560  # exists after r7 shouldn't break the subtree merge into A_COPY/D/H/psi.
11561  # A_COPY/D/H/psi should simply have r3 reverse merged.  No paths under
11562  # in the tree rooted at A_COPY should have any explicit mergeinfo.
11563  expected_output = wc.State(A_COPY_path, {
11564    'D/H/omega' : Item(status='U '),
11565    'D/H/psi'   : Item(status='U '),
11566    'D/H/omega' : Item(status='G ', prev_status='G '),
11567    })
11568  expected_mergeinfo_output = wc.State(A_COPY_path, {
11569    ''          : Item(status=' U'),
11570    'D/H/psi'   : Item(status=' U'),
11571    'D/H/omega' : Item(status=' U'),
11572    })
11573  expected_elision_output = wc.State(A_COPY_path, {
11574    ''          : Item(status=' U'),
11575    'D/H/psi'   : Item(status=' U'),
11576    'D/H/omega' : Item(status=' U'),
11577    })
11578  expected_status = wc.State(A_COPY_path, {
11579    ''          : Item(status=' M', wc_rev=13),
11580    'B'         : Item(status='  ', wc_rev=13),
11581    'mu'        : Item(status='  ', wc_rev=13),
11582    'B/E'       : Item(status='  ', wc_rev=13),
11583    'B/E/alpha' : Item(status='  ', wc_rev=13),
11584    'B/E/beta'  : Item(status='  ', wc_rev=13),
11585    'B/lambda'  : Item(status='  ', wc_rev=13),
11586    'B/F'       : Item(status='  ', wc_rev=13),
11587    'C'         : Item(status='  ', wc_rev=13),
11588    'D'         : Item(status='  ', wc_rev=13),
11589    'D/G'       : Item(status='  ', wc_rev=13),
11590    'D/G/pi'    : Item(status='  ', wc_rev=13),
11591    'D/G/rho'   : Item(status='  ', wc_rev=13),
11592    'D/G/tau'   : Item(status='  ', wc_rev=13),
11593    'D/gamma'   : Item(status='  ', wc_rev=13),
11594    'D/H'       : Item(status='  ', wc_rev=13),
11595    'D/H/chi'   : Item(status='  ', wc_rev=13),
11596    'D/H/psi'   : Item(status='MM', wc_rev=13),
11597    'D/H/omega' : Item(status='MM', wc_rev=13),
11598    })
11599  expected_disk = wc.State('', {
11600    'B'         : Item(),
11601    'mu'        : Item("This is the file 'mu'.\n"),
11602    'B/E'       : Item(),
11603    'B/E/alpha' : Item("This is the file 'alpha'.\n"),
11604    'B/E/beta'  : Item("This is the file 'beta'.\n"),
11605    'B/lambda'  : Item("This is the file 'lambda'.\n"),
11606    'B/F'       : Item(),
11607    'C'         : Item(),
11608    'D'         : Item(),
11609    'D/G'       : Item(),
11610    'D/G/pi'    : Item("This is the file 'pi'.\n"),
11611    'D/G/rho'   : Item("This is the file 'rho'.\n"),
11612    'D/G/tau'   : Item("This is the file 'tau'.\n"),
11613    'D/gamma'   : Item("This is the file 'gamma'.\n"),
11614    'D/H'       : Item(),
11615    'D/H/chi'   : Item("This is the file 'chi'.\n"),
11616    'D/H/psi'   : Item("This is the file 'psi'.\n"),
11617    'D/H/omega' : Item("This is the file 'omega'.\n"),
11618    })
11619  expected_skip = wc.State(A_COPY_path, { })
11620  svntest.actions.run_and_verify_merge(A_COPY_path, '12', '1',
11621                                       sbox.repo_url + '/A', None,
11622                                       expected_output,
11623                                       expected_mergeinfo_output,
11624                                       expected_elision_output,
11625                                       expected_disk,
11626                                       expected_status,
11627                                       expected_skip,
11628                                       [], True, False)
11629
11630  # Revert the previous merge.
11631  svntest.actions.run_and_verify_svn(None, [],
11632                                     'revert', '-R', wc_dir)
11633  # Merge r12 to A_COPY and commit as r14.
11634  expected_output = wc.State(A_COPY_path, {})
11635  expected_mergeinfo_output = wc.State(A_COPY_path, {
11636    '' : Item(status=' U'),
11637    })
11638  expected_elision_output = wc.State(A_COPY_path, {
11639    })
11640  expected_status = wc.State(A_COPY_path, {
11641    ''          : Item(status=' M', wc_rev=13),
11642    'B'         : Item(status='  ', wc_rev=13),
11643    'mu'        : Item(status='  ', wc_rev=13),
11644    'B/E'       : Item(status='  ', wc_rev=13),
11645    'B/E/alpha' : Item(status='  ', wc_rev=13),
11646    'B/E/beta'  : Item(status='  ', wc_rev=13),
11647    'B/lambda'  : Item(status='  ', wc_rev=13),
11648    'B/F'       : Item(status='  ', wc_rev=13),
11649    'C'         : Item(status='  ', wc_rev=13),
11650    'D'         : Item(status='  ', wc_rev=13),
11651    'D/G'       : Item(status='  ', wc_rev=13),
11652    'D/G/pi'    : Item(status='  ', wc_rev=13),
11653    'D/G/rho'   : Item(status='  ', wc_rev=13),
11654    'D/G/tau'   : Item(status='  ', wc_rev=13),
11655    'D/gamma'   : Item(status='  ', wc_rev=13),
11656    'D/H'       : Item(status='  ', wc_rev=13),
11657    'D/H/chi'   : Item(status='  ', wc_rev=13),
11658    'D/H/psi'   : Item(status='  ', wc_rev=13),
11659    'D/H/omega' : Item(status='  ', wc_rev=13),
11660    })
11661  expected_disk = wc.State('', {
11662    ''          : Item(props={SVN_PROP_MERGEINFO : '/A:8,12'}),
11663    'B'         : Item(),
11664    'mu'        : Item("This is the file 'mu'.\n"),
11665    'B/E'       : Item(),
11666    'B/E/alpha' : Item("This is the file 'alpha'.\n"),
11667    'B/E/beta'  : Item("This is the file 'beta'.\n"),
11668    'B/lambda'  : Item("This is the file 'lambda'.\n"),
11669    'B/F'       : Item(),
11670    'C'         : Item(),
11671    'D'         : Item(),
11672    'D/G'       : Item(),
11673    'D/G/pi'    : Item("This is the file 'pi'.\n"),
11674    'D/G/rho'   : Item("This is the file 'rho'.\n"),
11675    'D/G/tau'   : Item("This is the file 'tau'.\n"),
11676    'D/gamma'   : Item("This is the file 'gamma'.\n"),
11677    'D/H'       : Item(),
11678    'D/H/chi'   : Item("This is the file 'chi'.\n"),
11679    'D/H/psi'   : Item("New content",
11680                       props={SVN_PROP_MERGEINFO : '/A/D/H/psi:3,8'}),
11681    'D/H/omega' : Item("Now with fabulous new content!",
11682                       props={SVN_PROP_MERGEINFO : '/A/D/H/omega:2-12'}),
11683    })
11684  expected_skip = wc.State(A_COPY_path, { })
11685  svntest.actions.run_and_verify_merge(A_COPY_path, '11', '12',
11686                                       sbox.repo_url + '/A', None,
11687                                       expected_output,
11688                                       expected_mergeinfo_output,
11689                                       expected_elision_output,
11690                                       expected_disk,
11691                                       expected_status,
11692                                       expected_skip,
11693                                       [], True, False)
11694  # As we did earlier, repeat the merge with the --record-only option to
11695  # preserve the old behavior of recording mergeinfo on every subtree, thus
11696  # allowing this test to actually test the issue #3067 fixes.
11697  expected_output = expected_merge_output(
11698    [[12]], ['U    %s\n' % (A_COPY_path),
11699             ' G   %s\n' % (A_COPY_path),
11700             ' U   %s\n' % (psi_COPY_path),
11701             ' U   %s\n' % (omega_COPY_path),])
11702  svntest.actions.run_and_verify_svn(expected_output, [],
11703                                     'merge', '-c', '12',
11704                                     sbox.repo_url + '/A',
11705                                     A_COPY_path, '--record-only')
11706  sbox.simple_commit(message='Merge r12 to A_COPY')
11707
11708  # Update A_COPY/D/H/rho back to r13 so it's mergeinfo doesn't include
11709  # r12.  Then merge a range, -r6:12 which should delete a subtree
11710  # (A_COPY/D/H/psi).
11711  svntest.actions.run_and_verify_svn(exp_noop_up_out(14), [], 'up',
11712                                     wc_dir)
11713  expected_output = wc.State(A_COPY_path, {
11714    'D/H/psi'   : Item(status='D '),
11715    })
11716  expected_mergeinfo_output = wc.State(A_COPY_path, {
11717    '' : Item(status=' U'),
11718    })
11719  expected_elision_output = wc.State(A_COPY_path, {
11720    })
11721  expected_status = wc.State(A_COPY_path, {
11722    ''          : Item(status=' M', wc_rev=14),
11723    'B'         : Item(status='  ', wc_rev=14),
11724    'mu'        : Item(status='  ', wc_rev=14),
11725    'B/E'       : Item(status='  ', wc_rev=14),
11726    'B/E/alpha' : Item(status='  ', wc_rev=14),
11727    'B/E/beta'  : Item(status='  ', wc_rev=14),
11728    'B/lambda'  : Item(status='  ', wc_rev=14),
11729    'B/F'       : Item(status='  ', wc_rev=14),
11730    'C'         : Item(status='  ', wc_rev=14),
11731    'D'         : Item(status='  ', wc_rev=14),
11732    'D/G'       : Item(status='  ', wc_rev=14),
11733    'D/G/pi'    : Item(status='  ', wc_rev=14),
11734    'D/G/rho'   : Item(status='  ', wc_rev=14),
11735    'D/G/tau'   : Item(status='  ', wc_rev=14),
11736    'D/gamma'   : Item(status='  ', wc_rev=14),
11737    'D/H'       : Item(status='  ', wc_rev=14),
11738    'D/H/chi'   : Item(status='  ', wc_rev=14),
11739    'D/H/psi'   : Item(status='D ', wc_rev=14),
11740    'D/H/omega' : Item(status='  ', wc_rev=14),
11741    })
11742  expected_disk = wc.State('', {
11743    ''          : Item(props={SVN_PROP_MERGEINFO : '/A:7-12'}),
11744    'B'         : Item(),
11745    'mu'        : Item("This is the file 'mu'.\n"),
11746    'B/E'       : Item(),
11747    'B/E/alpha' : Item("This is the file 'alpha'.\n"),
11748    'B/E/beta'  : Item("This is the file 'beta'.\n"),
11749    'B/lambda'  : Item("This is the file 'lambda'.\n"),
11750    'B/F'       : Item(),
11751    'C'         : Item(),
11752    'D'         : Item(),
11753    'D/G'       : Item(),
11754    'D/G/pi'    : Item("This is the file 'pi'.\n"),
11755    'D/G/rho'   : Item("This is the file 'rho'.\n"),
11756    'D/G/tau'   : Item("This is the file 'tau'.\n"),
11757    'D/gamma'   : Item("This is the file 'gamma'.\n"),
11758    'D/H'       : Item(),
11759    'D/H/chi'   : Item("This is the file 'chi'.\n"),
11760    'D/H/omega' : Item("Now with fabulous new content!",
11761                       props={SVN_PROP_MERGEINFO : '/A/D/H/omega:2-12'}),
11762    })
11763  expected_skip = wc.State(A_COPY_path, { })
11764  svntest.actions.run_and_verify_merge(A_COPY_path, '6', '12',
11765                                       sbox.repo_url + '/A', None,
11766                                       expected_output,
11767                                       expected_mergeinfo_output,
11768                                       expected_elision_output,
11769                                       expected_disk,
11770                                       expected_status,
11771                                       expected_skip,
11772                                       [], True, False)
11773
11774#----------------------------------------------------------------------
11775# Another test for issue #3067: 'subtrees that don't exist at the start
11776# or end of a merge range shouldn't break the merge'
11777#
11778# See https://issues.apache.org/jira/browse/SVN-3067#desc34
11779@Issue(3067)
11780@SkipUnless(server_has_mergeinfo)
11781def subtrees_with_empty_mergeinfo(sbox):
11782  "mergeinfo not set on subtree with empty mergeinfo"
11783
11784  # Use helper to setup a renamed subtree.
11785  wc_dir, expected_disk, expected_status = set_up_renamed_subtree(sbox)
11786
11787  # Some paths we'll care about
11788  H_COPY_path = sbox.ospath('H_COPY')
11789
11790  # Cherry harvest all available revsions from 'A/D/H' to 'H_COPY'.
11791  #
11792  # This should merge r4:6 from 'A/D/H' setting mergeinfo for r5-6
11793  # on both 'H_COPY' and 'H_COPY/psi_moved'.  But since the working copy
11794  # is at a uniform working revision, the latter's mergeinfo should
11795  # elide, leaving explicit mergeinfo only on the merge target.
11796  expected_output = wc.State(H_COPY_path, {
11797    'psi_moved' : Item(status='U ')
11798    })
11799  expected_mergeinfo_output = wc.State(H_COPY_path, {
11800    ''          : Item(status=' U'),
11801    'psi_moved' : Item(status=' U'),
11802    })
11803  expected_elision_output = wc.State(H_COPY_path, {
11804    'psi_moved' : Item(status=' U'),
11805    })
11806  expected_status = wc.State(H_COPY_path, {
11807    ''          : Item(status=' M', wc_rev=6), # mergeinfo set on target
11808    'psi_moved' : Item(status='MM', wc_rev=6), # mergeinfo elides
11809    'omega'     : Item(status='  ', wc_rev=6),
11810    'chi'       : Item(status='  ', wc_rev=6),
11811    })
11812  expected_disk = wc.State('', {
11813    ''          : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:5-6'}),
11814    'psi_moved' : Item("Even *Newer* content"), # mergeinfo elides
11815    'omega'     : Item("New omega"),
11816    'chi'       : Item("This is the file 'chi'.\n"),
11817    })
11818  expected_skip = wc.State(H_COPY_path, { })
11819
11820  svntest.actions.run_and_verify_merge(H_COPY_path, None, None,
11821                                       sbox.repo_url + '/A/D/H', None,
11822                                       expected_output,
11823                                       expected_mergeinfo_output,
11824                                       expected_elision_output,
11825                                       expected_disk,
11826                                       expected_status, expected_skip,
11827                                       check_props=True)
11828
11829#----------------------------------------------------------------------
11830# Test for issue #3240 'commits to subtrees added by merge
11831# corrupt working copy and repos'.
11832@SkipUnless(server_has_mergeinfo)
11833@Issue(3240)
11834def commit_to_subtree_added_by_merge(sbox):
11835  "commits to subtrees added by merge wreak havoc"
11836
11837  # Setup a standard greek tree in r1.
11838  sbox.build()
11839  wc_dir = sbox.wc_dir
11840
11841  # Some paths we'll care about
11842  N_path        = sbox.ospath('A/D/H/N')
11843  nu_path       = sbox.ospath('A/D/H/N/nu')
11844  nu_COPY_path  = sbox.ospath('A_COPY/D/H/N/nu')
11845  H_COPY_path   = sbox.ospath('A_COPY/D/H')
11846
11847  # Copy 'A' to 'A_COPY' in r2.
11848  wc_disk, wc_status = set_up_branch(sbox, True)
11849
11850  # Create a 'A/D/H/N' and 'A/D/H/N/nu', and commit this new
11851  # subtree as r3.
11852  os.mkdir(N_path)
11853  svntest.main.file_write(nu_path, "This is the file 'nu'.\n")
11854  svntest.actions.run_and_verify_svn(None, [], 'add', N_path)
11855  expected_output = wc.State(wc_dir,
11856                             {'A/D/H/N'    : Item(verb='Adding'),
11857                              'A/D/H/N/nu' : Item(verb='Adding')})
11858  wc_status.add({'A/D/H/N'    : Item(status='  ', wc_rev=3),
11859                 'A/D/H/N/nu' : Item(status='  ', wc_rev=3)})
11860  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
11861                                        wc_status)
11862
11863  # Merge r3 to 'A_COPY/D/H', creating A_COPY/D/H/N' and 'A_COPY/D/H/N/nu'.
11864  # Commit the merge as r4.
11865  expected_output = wc.State(H_COPY_path, {
11866    'N'    : Item(status='A '),
11867    'N/nu' : Item(status='A '),
11868    })
11869  expected_mergeinfo_output = wc.State(H_COPY_path, {
11870    '' : Item(status=' U'),
11871    })
11872  expected_elision_output = wc.State(H_COPY_path, {
11873    })
11874  expected_status = wc.State(H_COPY_path, {
11875    ''      : Item(status=' M', wc_rev=2),
11876    'psi'   : Item(status='  ', wc_rev=2),
11877    'omega' : Item(status='  ', wc_rev=2),
11878    'chi'   : Item(status='  ', wc_rev=2),
11879    'N'     : Item(status='A ', copied='+', wc_rev='-'),
11880    'N/nu'  : Item(status='  ', copied='+', wc_rev='-'),
11881    })
11882  expected_disk = wc.State('', {
11883    ''      : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:2-3'}),
11884    'psi'   : Item("This is the file 'psi'.\n"),
11885    'omega' : Item("This is the file 'omega'.\n"),
11886    'chi'   : Item("This is the file 'chi'.\n"),
11887    'N'     : Item(),
11888    'N/nu'  : Item("This is the file 'nu'.\n"),
11889    })
11890  expected_skip = wc.State(H_COPY_path, {})
11891  svntest.actions.run_and_verify_merge(H_COPY_path,
11892                                       None, None,
11893                                       sbox.repo_url + '/A/D/H', None,
11894                                       expected_output,
11895                                       expected_mergeinfo_output,
11896                                       expected_elision_output,
11897                                       expected_disk,
11898                                       expected_status, expected_skip,
11899                                       check_props=True)
11900  expected_output = wc.State(wc_dir, {
11901    'A_COPY/D/H'      : Item(verb='Sending'),
11902    'A_COPY/D/H/N'    : Item(verb='Adding'),
11903    })
11904  wc_status.add({'A_COPY/D/H/N'    : Item(status='  ', wc_rev=4),
11905                 'A_COPY/D/H/N/nu' : Item(status='  ', wc_rev=4)})
11906  wc_status.tweak('A_COPY/D/H', wc_rev=4)
11907  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
11908                                        wc_status)
11909
11910  # Make a text change to 'A_COPY/D/H/N/nu' and commit it as r5.  This
11911  # is the first place issue #3240 appears over DAV layers, and the
11912  # commit fails with an error like this:
11913  #   trunk>svn ci -m "" merge_tests-100
11914  #   Sending        merge_tests-100\A_COPY\D\H\N\nu
11915  #   Transmitting file data ...\..\..\subversion\libsvn_client\commit.c:919:
11916  #     (apr_err=20014)
11917  #   svn: Commit failed (details follow):
11918  #   ..\..\..\subversion\libsvn_ra_neon\merge.c:260: (apr_err=20014)
11919  #   svn: A MERGE response for '/svn-test-work/repositories/merge_tests-100/
11920  #     A/D/H/N/nu' is not a child of the destination
11921  #     ('/svn-test-work/repositories/merge_tests-100/A_COPY/D/H/N')
11922  svntest.main.file_write(nu_COPY_path, "New content")
11923  expected_output = wc.State(wc_dir,
11924                             {'A_COPY/D/H/N/nu' : Item(verb='Sending')})
11925  wc_status.tweak('A_COPY/D/H/N/nu', wc_rev=5)
11926  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
11927                                        wc_status)
11928  # The second place issue #3240 shows up is in the fact that the commit
11929  # *did* succeed, but the wrong path ('A/D/H/nu' rather than 'A_COPY/D/H/nu')
11930  # is affected.  We can see this by running an update; since we just
11931  # committed there shouldn't be any incoming changes.
11932  svntest.actions.run_and_verify_svn(exp_noop_up_out(5), [], 'up',
11933                                     wc_dir)
11934
11935
11936#----------------------------------------------------------------------
11937# Tests for merging the deletion of a node, where the node to be deleted
11938# is the same as or different from the node that was deleted.
11939
11940#----------------------------------------------------------------------
11941def del_identical_file(sbox):
11942  "merge tries to delete a file of identical content"
11943
11944  # Set up a standard greek tree in r1.
11945  sbox.build()
11946
11947  saved_cwd = os.getcwd()
11948  os.chdir(sbox.wc_dir)
11949  sbox.wc_dir = ''
11950
11951  # Set up a modification and deletion in the source branch.
11952  source = 'A/D/G'
11953  s_rev_orig = 1
11954  svn_modfile(source+"/tau")
11955  sbox.simple_commit(source)
11956  s_rev_mod = 2
11957  sbox.simple_rm(source+"/tau")
11958  sbox.simple_commit(source)
11959  s_rev_del = 3
11960
11961  # Make an identical copy, and merge a deletion to it.
11962  target = 'A/D/G2'
11963  svn_copy(s_rev_mod, source, target)
11964  sbox.simple_commit(target)
11965  # Should be deleted quietly.
11966  svn_merge(s_rev_del, source, target,
11967            ['D    %s\n' % local_path('A/D/G2/tau')])
11968
11969  # Make a differing copy, locally modify it so it's the same,
11970  # and merge a deletion to it.
11971  target = 'A/D/G3'
11972  svn_copy(s_rev_orig, source, target)
11973  sbox.simple_commit(target)
11974  svn_modfile(target+"/tau")
11975  # Should be deleted quietly.
11976  svn_merge(s_rev_del, source, target,
11977            ['D    %s\n' % local_path('A/D/G3/tau')])
11978
11979  os.chdir(saved_cwd)
11980
11981#----------------------------------------------------------------------
11982def del_sched_add_hist_file(sbox):
11983  "merge tries to delete identical sched-add file"
11984
11985  # Setup a standard greek tree in r1.
11986  sbox.build()
11987
11988  saved_cwd = os.getcwd()
11989  os.chdir(sbox.wc_dir)
11990  sbox.wc_dir = ''
11991
11992  # Set up a creation in the source branch.
11993  source = 'A/D/G'
11994  s_rev_orig = 1
11995  svn_mkfile(source+"/file")
11996  sbox.simple_commit(source)
11997  s_rev_add = 2
11998
11999  # Merge a creation, and delete by reverse-merging into uncommitted WC.
12000  target = 'A/D/G2'
12001  svn_copy(s_rev_orig, source, target)
12002  sbox.simple_commit(target)
12003  s_rev = 3
12004  svn_merge(s_rev_add, source, target,
12005            ['A    %s\n' % local_path('A/D/G2/file')])
12006  # Should be deleted quietly.
12007  svn_merge(-s_rev_add, source, target,
12008            ['D    %s\n' % local_path('A/D/G2/file')], elides=['A/D/G2'])
12009
12010  os.chdir(saved_cwd)
12011
12012#----------------------------------------------------------------------
12013@SkipUnless(server_has_mergeinfo)
12014def subtree_merges_dont_cause_spurious_conflicts(sbox):
12015  "subtree merges dont cause spurious conflicts"
12016
12017  # Fix a merge bug where previous merges are incorrectly reversed leading
12018  # to repeat merges and spurious conflicts.  These can occur when a subtree
12019  # needs a range M:N merged that is older than the ranges X:Y needed by the
12020  # merge target *and* there are changes in the merge source between N:X that
12021  # affect parts of the merge target other than the subtree.  An actual case
12022  # where our own epository encountered this problem is described here:
12023  # http://subversion.tigris.org/servlets/ReadMsg?listName=dev&msgNo=141832
12024
12025  sbox.build()
12026  wc_dir = sbox.wc_dir
12027
12028  # Some paths we'll care about
12029  rho_path      = sbox.ospath('A/D/G/rho')
12030  A_COPY_path   = sbox.ospath('A_COPY')
12031  psi_COPY_path = sbox.ospath('A_COPY/D/H/psi')
12032
12033  # Make a branch to merge to.
12034  wc_disk, wc_status = set_up_branch(sbox, False, 1)
12035
12036  # r7 Make a text change to A/D/G/rho.
12037  svntest.main.file_write(rho_path, "Newer content")
12038  expected_output = wc.State(wc_dir, {'A/D/G/rho' : Item(verb='Sending')})
12039  wc_status.tweak('A/D/G/rho', wc_rev=7)
12040  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
12041                                        wc_status)
12042  wc_disk.tweak('A/D/G/rho', contents="Newer content")
12043
12044  # r8 Make another text change to A/D/G/rho.
12045  svntest.main.file_write(rho_path, "Even *newer* content")
12046  expected_output = wc.State(wc_dir, {'A/D/G/rho' : Item(verb='Sending')})
12047  wc_status.tweak('A/D/G/rho', wc_rev=8)
12048  svntest.actions.run_and_verify_commit(wc_dir, expected_output, wc_status)
12049  wc_disk.tweak('A/D/G/rho', contents="Even *newer* content")
12050
12051  # Update the WC to allow full mergeinfo inheritance and elision.
12052  svntest.actions.run_and_verify_svn(exp_noop_up_out(8), [], 'up',
12053                                     wc_dir)
12054  wc_status.tweak(wc_rev=8)
12055
12056  # r9 Merge r0:7 from A to A_COPY, then create a subtree with differing
12057  # mergeinfo under A_COPY by reverse merging r3 from A_COPY/D/H/psi.
12058  expected_output = wc.State(A_COPY_path, {
12059    'B/E/beta'  : Item(status='U '),
12060    'D/G/rho'   : Item(status='U '),
12061    'D/H/omega' : Item(status='U '),
12062    'D/H/psi'   : Item(status='U '),
12063    })
12064  expected_mergeinfo_output = wc.State(A_COPY_path, {
12065    '' : Item(status=' U'),
12066    })
12067  expected_elision_output = wc.State(A_COPY_path, {
12068    })
12069  expected_status = wc.State(A_COPY_path, {
12070    ''          : Item(status=' M', wc_rev=8),
12071    'B'         : Item(status='  ', wc_rev=8),
12072    'mu'        : Item(status='  ', wc_rev=8),
12073    'B/E'       : Item(status='  ', wc_rev=8),
12074    'B/E/alpha' : Item(status='  ', wc_rev=8),
12075    'B/E/beta'  : Item(status='M ', wc_rev=8),
12076    'B/lambda'  : Item(status='  ', wc_rev=8),
12077    'B/F'       : Item(status='  ', wc_rev=8),
12078    'C'         : Item(status='  ', wc_rev=8),
12079    'D'         : Item(status='  ', wc_rev=8),
12080    'D/G'       : Item(status='  ', wc_rev=8),
12081    'D/G/pi'    : Item(status='  ', wc_rev=8),
12082    'D/G/rho'   : Item(status='M ', wc_rev=8),
12083    'D/G/tau'   : Item(status='  ', wc_rev=8),
12084    'D/gamma'   : Item(status='  ', wc_rev=8),
12085    'D/H'       : Item(status='  ', wc_rev=8),
12086    'D/H/chi'   : Item(status='  ', wc_rev=8),
12087    'D/H/psi'   : Item(status='M ', wc_rev=8),
12088    'D/H/omega' : Item(status='M ', wc_rev=8),
12089    })
12090  expected_disk = wc.State('', {
12091    ''          : Item(props={SVN_PROP_MERGEINFO : '/A:2-7'}),
12092    'B'         : Item(),
12093    'mu'        : Item("This is the file 'mu'.\n"),
12094    'B/E'       : Item(),
12095    'B/E/alpha' : Item("This is the file 'alpha'.\n"),
12096    'B/E/beta'  : Item("New content"),
12097    'B/lambda'  : Item("This is the file 'lambda'.\n"),
12098    'B/F'       : Item(),
12099    'C'         : Item(),
12100    'D'         : Item(),
12101    'D/G'       : Item(),
12102    'D/G/pi'    : Item("This is the file 'pi'.\n"),
12103    'D/G/rho'   : Item("Newer content"),
12104    'D/G/tau'   : Item("This is the file 'tau'.\n"),
12105    'D/gamma'   : Item("This is the file 'gamma'.\n"),
12106    'D/H'       : Item(),
12107    'D/H/chi'   : Item("This is the file 'chi'.\n"),
12108    'D/H/psi'   : Item("New content",),
12109    'D/H/omega' : Item("New content"),
12110    })
12111  expected_skip = wc.State(A_COPY_path, { })
12112  svntest.actions.run_and_verify_merge(A_COPY_path, '0', '7',
12113                                       sbox.repo_url + '/A', None,
12114                                       expected_output,
12115                                       expected_mergeinfo_output,
12116                                       expected_elision_output,
12117                                       expected_disk,
12118                                       expected_status, expected_skip,
12119                                       check_props=True)
12120  # run_and_verify_merge doesn't support merging to a file WCPATH
12121  # so use run_and_verify_svn.
12122  ### TODO: We can use run_and_verify_merge() here now.
12123  svntest.actions.run_and_verify_svn(expected_merge_output([[-3]],
12124                                       ['G    ' + psi_COPY_path + '\n',
12125                                        ' G   ' + psi_COPY_path + '\n']),
12126                                     [], 'merge', '-c-3',
12127                                     sbox.repo_url + '/A/D/H/psi',
12128                                     psi_COPY_path)
12129  # Commit the two merges.
12130  expected_output = svntest.wc.State(wc_dir, {
12131    'A_COPY' : Item(verb='Sending'),
12132    'A_COPY/B/E/beta'  : Item(verb='Sending'),
12133    'A_COPY/D/G/rho'   : Item(verb='Sending'),
12134    'A_COPY/D/H/psi'   : Item(verb='Sending'),
12135    'A_COPY/D/H/omega' : Item(verb='Sending'),
12136    })
12137  wc_status.tweak('A_COPY',
12138                  'A_COPY/B/E/beta',
12139                  'A_COPY/D/G/rho',
12140                  'A_COPY/D/H/psi',
12141                  'A_COPY/D/H/omega',
12142                  wc_rev=9)
12143  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
12144                                        wc_status)
12145
12146  # Update the WC to allow full mergeinfo inheritance and elision.
12147  svntest.actions.run_and_verify_svn(exp_noop_up_out(9), [], 'up',
12148                                     wc_dir)
12149  wc_status.tweak(wc_rev=9)
12150
12151  # r9 Merge all available revisions from A to A_COPY.
12152  #
12153  # This is where the bug revealed itself, instead of cleanly merging
12154  # just r3 and then r8-9, the first merge editor drive of r3 set A_COPY
12155  # to the state it was in r7, effectively reverting the merge committed
12156  # in r9.  So we saw unexpected merges to omega, rho, and beta, as they
12157  # are returned to their r7 state and then a conflict on rho as the editor
12158  # attempted to merge r8:
12159  #
12160  #   trunk>svn merge %url%/A merge_tests-104\A_COPY
12161  #   --- Merging r3 into 'merge_tests-104\A_COPY\D\H\psi':
12162  #   U    merge_tests-104\A_COPY\D\H\psi
12163  #   --- Merging r8 through r9 into 'merge_tests-104\A_COPY':
12164  #   U    merge_tests-104\A_COPY\D\H\omega
12165  #   U    merge_tests-104\A_COPY\D\G\rho
12166  #   U    merge_tests-104\A_COPY\B\E\beta
12167  #   Conflict discovered in 'merge_tests-104/A_COPY/D/G/rho'.
12168  #   Select: (p) postpone, (df) diff-full, (e) edit,
12169  #           (mc) mine-conflict, (tc) theirs-conflict,
12170  #           (s) show all options: p
12171  #   --- Merging r8 through r9 into 'merge_tests-104\A_COPY':
12172  #   C    merge_tests-104\A_COPY\D\G\rho
12173  expected_output = wc.State(A_COPY_path, {
12174    'D/G/rho'   : Item(status='U '),
12175    'D/H/psi'   : Item(status='U '),
12176    })
12177  expected_mergeinfo_output = wc.State(A_COPY_path, {
12178    ''        : Item(status=' U'),
12179    'D/H/psi' : Item(status=' U'),
12180    })
12181  expected_elision_output = wc.State(A_COPY_path, {
12182    'D/H/psi' : Item(status=' U'),
12183    })
12184  expected_status = wc.State(A_COPY_path, {
12185    ''          : Item(status=' M', wc_rev=9),
12186    'B'         : Item(status='  ', wc_rev=9),
12187    'mu'        : Item(status='  ', wc_rev=9),
12188    'B/E'       : Item(status='  ', wc_rev=9),
12189    'B/E/alpha' : Item(status='  ', wc_rev=9),
12190    'B/E/beta'  : Item(status='  ', wc_rev=9),
12191    'B/lambda'  : Item(status='  ', wc_rev=9),
12192    'B/F'       : Item(status='  ', wc_rev=9),
12193    'C'         : Item(status='  ', wc_rev=9),
12194    'D'         : Item(status='  ', wc_rev=9),
12195    'D/G'       : Item(status='  ', wc_rev=9),
12196    'D/G/pi'    : Item(status='  ', wc_rev=9),
12197    'D/G/rho'   : Item(status='M ', wc_rev=9),
12198    'D/G/tau'   : Item(status='  ', wc_rev=9),
12199    'D/gamma'   : Item(status='  ', wc_rev=9),
12200    'D/H'       : Item(status='  ', wc_rev=9),
12201    'D/H/chi'   : Item(status='  ', wc_rev=9),
12202    'D/H/psi'   : Item(status='MM', wc_rev=9),
12203    'D/H/omega' : Item(status='  ', wc_rev=9),
12204    })
12205  expected_disk = wc.State('', {
12206    ''          : Item(props={SVN_PROP_MERGEINFO : '/A:2-9'}),
12207    'B'         : Item(),
12208    'mu'        : Item("This is the file 'mu'.\n"),
12209    'B/E'       : Item(),
12210    'B/E/alpha' : Item("This is the file 'alpha'.\n"),
12211    'B/E/beta'  : Item("New content"),
12212    'B/lambda'  : Item("This is the file 'lambda'.\n"),
12213    'B/F'       : Item(),
12214    'C'         : Item(),
12215    'D'         : Item(),
12216    'D/G'       : Item(),
12217    'D/G/pi'    : Item("This is the file 'pi'.\n"),
12218    'D/G/rho'   : Item("Even *newer* content"),
12219    'D/G/tau'   : Item("This is the file 'tau'.\n"),
12220    'D/gamma'   : Item("This is the file 'gamma'.\n"),
12221    'D/H'       : Item(),
12222    'D/H/chi'   : Item("This is the file 'chi'.\n"),
12223    'D/H/psi'   : Item("New content"), # Mergeinfo elides to A_COPY
12224    'D/H/omega' : Item("New content"),
12225    })
12226  expected_skip = wc.State(A_COPY_path, { })
12227  svntest.actions.run_and_verify_merge(A_COPY_path, None, None,
12228                                       sbox.repo_url + '/A', None,
12229                                       expected_output,
12230                                       expected_mergeinfo_output,
12231                                       expected_elision_output,
12232                                       expected_disk,
12233                                       expected_status, expected_skip,
12234                                       check_props=True)
12235
12236#----------------------------------------------------------------------
12237# Test for yet another variant of issue #3067.
12238@Issue(3067)
12239@SkipUnless(server_has_mergeinfo)
12240def merge_target_and_subtrees_need_nonintersecting_ranges(sbox):
12241  "target and subtrees need nonintersecting revs"
12242
12243  sbox.build()
12244  wc_dir = sbox.wc_dir
12245
12246  # Some paths we'll care about
12247  nu_path          = sbox.ospath('A/D/G/nu')
12248  A_COPY_path      = sbox.ospath('A_COPY')
12249  nu_COPY_path     = sbox.ospath('A_COPY/D/G/nu')
12250  omega_COPY_path  = sbox.ospath('A_COPY/D/H/omega')
12251  beta_COPY_path   = sbox.ospath('A_COPY/B/E/beta')
12252  rho_COPY_path    = sbox.ospath('A_COPY/D/G/rho')
12253  psi_COPY_path    = sbox.ospath('A_COPY/D/H/psi')
12254
12255  # Make a branch to merge to.
12256  wc_disk, wc_status = set_up_branch(sbox, False, 1)
12257
12258  # Add file A/D/G/nu in r7.
12259  svntest.main.file_write(nu_path, "This is the file 'nu'.\n")
12260  svntest.actions.run_and_verify_svn(None, [], 'add', nu_path)
12261  expected_output = wc.State(wc_dir, {'A/D/G/nu' : Item(verb='Adding')})
12262  wc_status.add({'A/D/G/nu' : Item(status='  ', wc_rev=7)})
12263  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
12264                                        wc_status)
12265
12266  # Make a text mod to A/D/G/nu in r8.
12267  svntest.main.file_write(nu_path, "New content")
12268  expected_output = wc.State(wc_dir, {'A/D/G/nu' : Item(verb='Sending')})
12269  wc_status.tweak('A/D/G/nu', wc_rev=8)
12270  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
12271                                        wc_status)
12272
12273  # Do several merges to setup a situation where the merge
12274  # target and two of its subtrees need non-intersecting ranges
12275  # merged when doing a synch (a.k.a. cherry harvest) merge.
12276  #
12277  #   1) Merge -r0:7 from A to A_COPY.
12278  #
12279  #   2) Merge -c8 from A/D/G/nu to A_COPY/D/G/nu.
12280  #
12281  #   3) Merge -c-6 from A/D/H/omega to A_COPY/D/H/omega.
12282  #
12283  # Commit this group of merges as r9.  Since we already test these type
12284  # of merges to death we don't use run_and_verify_merge() on these
12285  # intermediate merges.
12286  svntest.actions.run_and_verify_svn(
12287    expected_merge_output([[2,7]],
12288                                ['U    ' + beta_COPY_path  + '\n',
12289                                 'A    ' + nu_COPY_path    + '\n',
12290                                 'U    ' + rho_COPY_path   + '\n',
12291                                 'U    ' + omega_COPY_path + '\n',
12292                                 'U    ' + psi_COPY_path   + '\n',
12293                                 ' U   ' + A_COPY_path     + '\n',]
12294                                ),
12295    [], 'merge', '-r0:7', sbox.repo_url + '/A', A_COPY_path)
12296  svntest.actions.run_and_verify_svn(
12297    expected_merge_output([[8]], ['U    ' + nu_COPY_path    + '\n',
12298                                        ' G   ' + nu_COPY_path    + '\n']),
12299    [], 'merge', '-c8', sbox.repo_url + '/A/D/G/nu', nu_COPY_path)
12300
12301  svntest.actions.run_and_verify_svn(
12302    expected_merge_output([[-6]], ['G    ' + omega_COPY_path    + '\n',
12303                                         ' G   ' + omega_COPY_path    + '\n']),
12304    [], 'merge', '-c-6', sbox.repo_url + '/A/D/H/omega', omega_COPY_path)
12305  wc_status.add({'A_COPY/D/G/nu' : Item(status='  ', wc_rev=9)})
12306  wc_status.tweak('A_COPY',
12307                  'A_COPY/B/E/beta',
12308                  'A_COPY/D/G/rho',
12309                  'A_COPY/D/H/omega',
12310                  'A_COPY/D/H/psi',
12311                  wc_rev=9)
12312  expected_output = wc.State(wc_dir, {
12313    'A_COPY'           : Item(verb='Sending'),
12314    'A_COPY/B/E/beta'  : Item(verb='Sending'),
12315    'A_COPY/D/G/rho'   : Item(verb='Sending'),
12316    'A_COPY/D/G/nu'    : Item(verb='Adding'),
12317    'A_COPY/D/H/omega' : Item(verb='Sending'),
12318    'A_COPY/D/H/psi'   : Item(verb='Sending'),
12319    })
12320  svntest.actions.run_and_verify_commit(wc_dir, expected_output, wc_status)
12321
12322  # Update the WC to allow full mergeinfo inheritance and elision.
12323  svntest.actions.run_and_verify_svn(exp_noop_up_out(9), [], 'up',
12324                                     wc_dir)
12325
12326  # Merge all available revisions from A to A_COPY, the merge logic
12327  # should handle this situation (no "svn: Working copy path 'D/G/nu'
12328  # does not exist in repository" errors!).  The mergeinfo on
12329  # A_COPY/D/H/omega elides to the root, but the mergeinfo on
12330  # A_COPY/D/G/nu, untouched by the merge, does not get updated so
12331  # does not elide.
12332  expected_output = wc.State(A_COPY_path, {
12333    'D/H/omega': Item(status='U '),
12334    })
12335  expected_mergeinfo_output = wc.State(A_COPY_path, {
12336    ''         : Item(status=' U'),
12337    'D/H/omega': Item(status=' U'),
12338    })
12339  expected_elision_output = wc.State(A_COPY_path, {
12340    'D/H/omega': Item(status=' U'),
12341    })
12342  expected_status = wc.State(A_COPY_path, {
12343    ''          : Item(status=' M', wc_rev=9),
12344    'B'         : Item(status='  ', wc_rev=9),
12345    'mu'        : Item(status='  ', wc_rev=9),
12346    'B/E'       : Item(status='  ', wc_rev=9),
12347    'B/E/alpha' : Item(status='  ', wc_rev=9),
12348    'B/E/beta'  : Item(status='  ', wc_rev=9),
12349    'B/lambda'  : Item(status='  ', wc_rev=9),
12350    'B/F'       : Item(status='  ', wc_rev=9),
12351    'C'         : Item(status='  ', wc_rev=9),
12352    'D'         : Item(status='  ', wc_rev=9),
12353    'D/G'       : Item(status='  ', wc_rev=9),
12354    'D/G/pi'    : Item(status='  ', wc_rev=9),
12355    'D/G/rho'   : Item(status='  ', wc_rev=9),
12356    'D/G/tau'   : Item(status='  ', wc_rev=9),
12357    'D/G/nu'    : Item(status='  ', wc_rev=9),
12358    'D/gamma'   : Item(status='  ', wc_rev=9),
12359    'D/H'       : Item(status='  ', wc_rev=9),
12360    'D/H/chi'   : Item(status='  ', wc_rev=9),
12361    'D/H/psi'   : Item(status='  ', wc_rev=9),
12362    'D/H/omega' : Item(status='MM', wc_rev=9),
12363    })
12364  expected_disk = wc.State('', {
12365    ''          : Item(props={SVN_PROP_MERGEINFO : '/A:2-9'}),
12366    'B'         : Item(),
12367    'mu'        : Item("This is the file 'mu'.\n"),
12368    'B/E'       : Item(),
12369    'B/E/alpha' : Item("This is the file 'alpha'.\n"),
12370    'B/E/beta'  : Item("New content"),
12371    'B/lambda'  : Item("This is the file 'lambda'.\n"),
12372    'B/F'       : Item(),
12373    'C'         : Item(),
12374    'D'         : Item(),
12375    'D/G'       : Item(),
12376    'D/G/pi'    : Item("This is the file 'pi'.\n"),
12377    'D/G/rho'   : Item("New content"),
12378    'D/G/tau'   : Item("This is the file 'tau'.\n"),
12379    'D/G/nu'    : Item("New content",
12380                       props={SVN_PROP_MERGEINFO : '/A/D/G/nu:2-8'}),
12381    'D/gamma'   : Item("This is the file 'gamma'.\n"),
12382    'D/H'       : Item(),
12383    'D/H/chi'   : Item("This is the file 'chi'.\n"),
12384    'D/H/psi'   : Item("New content"),
12385    'D/H/omega' : Item("New content"),
12386    })
12387  expected_skip = wc.State(A_COPY_path, { })
12388  svntest.actions.run_and_verify_merge(A_COPY_path, None, None,
12389                                       sbox.repo_url + '/A', None,
12390                                       expected_output,
12391                                       expected_mergeinfo_output,
12392                                       expected_elision_output,
12393                                       expected_disk,
12394                                       expected_status,
12395                                       expected_skip,
12396                                       check_props=True)
12397
12398#----------------------------------------------------------------------
12399# Part of this test is a regression test for issue #3250 "Repeated merging
12400# of conflicting properties fails".
12401@Issue(3250)
12402def merge_two_edits_to_same_prop(sbox):
12403  "merge two successive edits to the same property"
12404
12405  sbox.build()
12406  wc_dir = sbox.wc_dir
12407
12408  # Make a branch to merge to. (This is r6.)
12409  wc_disk, wc_status = set_up_branch(sbox, False, 1)
12410  initial_rev = 6
12411
12412  # Change into the WC dir for convenience
12413  was_cwd = os.getcwd()
12414  os.chdir(sbox.wc_dir)
12415  sbox.wc_dir = ''
12416  wc_disk.wc_dir = ''
12417  wc_status.wc_dir = ''
12418
12419  # Some paths we'll care about
12420  A_path           = "A"
12421  A_COPY_path      = "A_COPY"
12422  mu_path          = os.path.join(A_path, "mu")
12423  mu_COPY_path     = os.path.join(A_COPY_path, "mu")
12424
12425  # In the source, make two successive changes to the same property
12426  sbox.simple_propset('p', 'new-val-1', 'A/mu')
12427  sbox.simple_commit('A/mu')
12428  rev1 = initial_rev + 1
12429  sbox.simple_propset('p', 'new-val-2', 'A/mu')
12430  sbox.simple_commit('A/mu')
12431  rev2 = initial_rev + 2
12432
12433  # Merge the first change, then the second, to a target branch.
12434  svn_merge(rev1, A_path, A_COPY_path)
12435  svn_merge(rev2, A_path, A_COPY_path)
12436
12437  # Both changes should merge automatically: the second one should not
12438  # complain about the local mod which the first one caused. The starting
12439  # value in the target ("mine") for the second merge is exactly equal to
12440  # the merge-left source value.
12441
12442  # A merge-tracking version of this problem is when the merge-tracking
12443  # algorithm breaks a single requested merge into two phases because of
12444  # some other target within the same merge requiring only a part of the
12445  # revision range.
12446
12447  # ====================================================================
12448
12449  # We test issue #3250 here: that is, test that we can make two successive
12450  # conflicting changes to the same property on the same node (here a file;
12451  # in #3250 it was on a dir).
12452  #
12453  # ### But we no longer support merging into a node that's already in
12454  #     conflict, and the 'rev3' merge here has been tweaked to resolve
12455  #     the conflict, so it no longer tests the original #3250 scenario.
12456  #
12457  # Revert changes to branch wc
12458  svntest.actions.run_and_verify_svn(None, [],
12459                                     'revert', '--recursive', A_COPY_path)
12460
12461  # In the branch, make two successive changes to the same property
12462  sbox.simple_propset('p', 'new-val-3', 'A_COPY/mu')
12463  sbox.simple_commit('A_COPY/mu')
12464  rev3 = initial_rev + 3
12465  sbox.simple_propset('p', 'new-val-4', 'A_COPY/mu')
12466  sbox.simple_commit('A_COPY/mu')
12467  rev4 = initial_rev + 4
12468
12469  # Merge the two changes together to trunk.
12470  svn_merge([rev3, rev4], A_COPY_path, A_path, [
12471      " C   %s\n" % mu_path,
12472      ], prop_conflicts=1, args=['--allow-mixed-revisions'])
12473
12474  # Revert changes to trunk wc, to test next scenario of #3250
12475  svntest.actions.run_and_verify_svn(None, [],
12476                                     'revert', '--recursive', A_path)
12477
12478  # Merge the first change, then the second, to trunk.
12479  svn_merge(rev3, A_COPY_path, A_path, [
12480      " C   %s\n" % mu_path,
12481      "Resolved .* '%s'\n" % mu_path,
12482      ], prop_resolved=1,
12483      args=['--allow-mixed-revisions',
12484            '--accept=working'])
12485  svn_merge(rev4, A_COPY_path, A_path, [
12486      " C   %s\n" % mu_path,
12487      ], prop_conflicts=1, args=['--allow-mixed-revisions'])
12488
12489  os.chdir(was_cwd)
12490
12491#----------------------------------------------------------------------
12492def merge_an_eol_unification_and_set_svn_eol_style(sbox):
12493  "merge an EOL unification and set svn:eol-style"
12494  # In svn 1.5.2, merging the two changes between these three states:
12495  #   r1. inconsistent EOLs and no svn:eol-style
12496  #   r2. consistent EOLs and no svn:eol-style
12497  #   r3. consistent EOLs and svn:eol-style=native
12498  # fails if attempted as a single merge (e.g. "svn merge r1:3") though it
12499  # succeeds if attempted in two phases (e.g. "svn merge -c2,3").
12500
12501  sbox.build()
12502  wc_dir = sbox.wc_dir
12503
12504  # Make a branch to merge to. (This will be r6.)
12505  wc_disk, wc_status = set_up_branch(sbox, False, 1)
12506  initial_rev = 6
12507
12508  # Change into the WC dir for convenience
12509  was_cwd = os.getcwd()
12510  os.chdir(sbox.wc_dir)
12511  sbox.wc_dir = ''
12512  wc_disk.wc_dir = ''
12513  wc_status.wc_dir = ''
12514
12515  content1 = 'Line1\nLine2\r\n'  # write as 'binary' to get these exact EOLs
12516  content2 = 'Line1\nLine2\n'    # write as 'text' to get native EOLs in file
12517
12518  # In the source branch, create initial state and two successive changes.
12519  # Use binary mode to write the first file so no newline conversion occurs.
12520  svntest.main.file_write('A/mu', content1, 'wb')
12521  sbox.simple_commit('A/mu')
12522  rev1 = initial_rev + 1
12523  # Use text mode to write the second copy of the file to get native EOLs.
12524  svntest.main.file_write('A/mu', content2, 'w')
12525  sbox.simple_commit('A/mu')
12526  rev2 = initial_rev + 2
12527  sbox.simple_propset('svn:eol-style', 'native', 'A/mu')
12528  sbox.simple_commit('A/mu')
12529  rev3 = initial_rev + 3
12530
12531  # Merge the initial state (inconsistent EOLs) to the target branch.
12532  svn_merge(rev1, 'A', 'A_COPY')
12533  sbox.simple_commit('A_COPY')
12534
12535  # Merge the two changes together to the target branch.
12536  svn_merge([rev2, rev3], 'A', 'A_COPY',
12537            args=['--allow-mixed-revisions'])
12538
12539  # That merge should succeed.
12540  # Surprise: setting svn:eol-style='LF' instead of 'native' doesn't fail.
12541  # Surprise: if we don't merge the file's 'rev1' state first, it doesn't fail
12542  # nor even raise a conflict.
12543
12544#----------------------------------------------------------------------
12545@SkipUnless(server_has_mergeinfo)
12546def merge_adds_mergeinfo_correctly(sbox):
12547  "merge adds mergeinfo to subtrees correctly"
12548
12549  # A merge may add explicit mergeinfo to the subtree of a merge target
12550  # as a result of changes in the merge source.  These paths may have
12551  # inherited mergeinfo prior to the merge, if so the subtree should end up
12552  # with mergeinfo that reflects all of the following:
12553  #
12554  #  A) The mergeinfo added from the merge source
12555  #
12556  #  B) The mergeinfo the subtree inherited prior to the merge.
12557  #
12558  #  C) Mergeinfo describing the merge performed.
12559  #
12560  # See http://subversion.tigris.org/servlets/ReadMsg?listName=dev&msgNo=142460
12561
12562  sbox.build()
12563  wc_dir = sbox.wc_dir
12564
12565  # Setup a 'trunk' and two branches.
12566  wc_disk, wc_status = set_up_branch(sbox, False, 2)
12567
12568  # Some paths we'll care about
12569  A_COPY_path   = sbox.ospath('A_COPY')
12570  D_COPY_path   = sbox.ospath('A_COPY/D')
12571  A_COPY_2_path = sbox.ospath('A_COPY_2')
12572  D_COPY_2_path = sbox.ospath('A_COPY_2/D')
12573
12574  # Update working copy to allow full inheritance and elision.
12575  svntest.actions.run_and_verify_svn(exp_noop_up_out(7), [],
12576                                     'up', wc_dir)
12577  wc_status.tweak(wc_rev=7)
12578
12579  # Merge r5 from A to A_COPY and commit as r8.
12580  # This creates explicit mergeinfo on A_COPY of '/A:5'.
12581  expected_output = wc.State(A_COPY_path, {
12582    'D/G/rho': Item(status='U '),
12583    })
12584  expected_mergeinfo_output = wc.State(A_COPY_path, {
12585    '' : Item(status=' U'),
12586    })
12587  expected_elision_output = wc.State(A_COPY_path, {
12588    })
12589  expected_status = wc.State(A_COPY_path, {
12590    ''          : Item(status=' M', wc_rev=7),
12591    'B'         : Item(status='  ', wc_rev=7),
12592    'mu'        : Item(status='  ', wc_rev=7),
12593    'B/E'       : Item(status='  ', wc_rev=7),
12594    'B/E/alpha' : Item(status='  ', wc_rev=7),
12595    'B/E/beta'  : Item(status='  ', wc_rev=7),
12596    'B/lambda'  : Item(status='  ', wc_rev=7),
12597    'B/F'       : Item(status='  ', wc_rev=7),
12598    'C'         : Item(status='  ', wc_rev=7),
12599    'D'         : Item(status='  ', wc_rev=7),
12600    'D/G'       : Item(status='  ', wc_rev=7),
12601    'D/G/pi'    : Item(status='  ', wc_rev=7),
12602    'D/G/rho'   : Item(status='M ', wc_rev=7),
12603    'D/G/tau'   : Item(status='  ', wc_rev=7),
12604    'D/gamma'   : Item(status='  ', wc_rev=7),
12605    'D/H'       : Item(status='  ', wc_rev=7),
12606    'D/H/chi'   : Item(status='  ', wc_rev=7),
12607    'D/H/psi'   : Item(status='  ', wc_rev=7),
12608    'D/H/omega' : Item(status='  ', wc_rev=7),
12609    })
12610  expected_disk = wc.State('', {
12611    ''          : Item(props={SVN_PROP_MERGEINFO : '/A:5'}),
12612    'B'         : Item(),
12613    'mu'        : Item("This is the file 'mu'.\n"),
12614    'B/E'       : Item(),
12615    'B/E/alpha' : Item("This is the file 'alpha'.\n"),
12616    'B/E/beta'  : Item("This is the file 'beta'.\n"),
12617    'B/lambda'  : Item("This is the file 'lambda'.\n"),
12618    'B/F'       : Item(),
12619    'C'         : Item(),
12620    'D'         : Item(),
12621    'D/G'       : Item(),
12622    'D/G/pi'    : Item("This is the file 'pi'.\n"),
12623    'D/G/rho'   : Item("New content"),
12624    'D/G/tau'   : Item("This is the file 'tau'.\n"),
12625    'D/gamma'   : Item("This is the file 'gamma'.\n"),
12626    'D/H'       : Item(),
12627    'D/H/chi'   : Item("This is the file 'chi'.\n"),
12628    'D/H/psi'   : Item("This is the file 'psi'.\n"),
12629    'D/H/omega' : Item("This is the file 'omega'.\n"),
12630    })
12631  expected_skip = wc.State(A_COPY_path, { })
12632  svntest.actions.run_and_verify_merge(A_COPY_path, '4', '5',
12633                                       sbox.repo_url + '/A', None,
12634                                       expected_output,
12635                                       expected_mergeinfo_output,
12636                                       expected_elision_output,
12637                                       expected_disk,
12638                                       expected_status,
12639                                       expected_skip,
12640                                       check_props=True)
12641  wc_status.tweak('A_COPY',
12642                  'A_COPY/D/G/rho',
12643                  wc_rev=8)
12644  expected_output = wc.State(wc_dir, {
12645    'A_COPY'           : Item(verb='Sending'),
12646    'A_COPY/D/G/rho'   : Item(verb='Sending'),
12647    })
12648  svntest.actions.run_and_verify_commit(wc_dir, expected_output, wc_status)
12649
12650  # Merge r7 from A/D to A_COPY_2/D and commit as r9.
12651  # This creates explicit mergeinfo on A_COPY_2/D of '/A/D:7'.
12652  expected_output = wc.State(D_COPY_2_path, {
12653    'H/omega': Item(status='U '),
12654    })
12655  expected_mergeinfo_output = wc.State(D_COPY_2_path, {
12656    '' : Item(status=' U'),
12657    })
12658  expected_elision_output = wc.State(D_COPY_2_path, {
12659    })
12660  expected_status = wc.State(D_COPY_2_path, {
12661    ''          : Item(status=' M', wc_rev=7),
12662    'G'       : Item(status='  ', wc_rev=7),
12663    'G/pi'    : Item(status='  ', wc_rev=7),
12664    'G/rho'   : Item(status='  ', wc_rev=7),
12665    'G/tau'   : Item(status='  ', wc_rev=7),
12666    'gamma'   : Item(status='  ', wc_rev=7),
12667    'H'       : Item(status='  ', wc_rev=7),
12668    'H/chi'   : Item(status='  ', wc_rev=7),
12669    'H/psi'   : Item(status='  ', wc_rev=7),
12670    'H/omega' : Item(status='M ', wc_rev=7),
12671    })
12672  expected_disk = wc.State('', {
12673    ''          : Item(props={SVN_PROP_MERGEINFO : '/A/D:7'}),
12674    'G'       : Item(),
12675    'G/pi'    : Item("This is the file 'pi'.\n"),
12676    'G/rho'   : Item("This is the file 'rho'.\n"),
12677    'G/tau'   : Item("This is the file 'tau'.\n"),
12678    'gamma'   : Item("This is the file 'gamma'.\n"),
12679    'H'       : Item(),
12680    'H/chi'   : Item("This is the file 'chi'.\n"),
12681    'H/psi'   : Item("This is the file 'psi'.\n"),
12682    'H/omega' : Item("New content"),
12683    })
12684  expected_skip = wc.State(A_COPY_path, { })
12685  svntest.actions.run_and_verify_merge(D_COPY_2_path, '6', '7',
12686                                       sbox.repo_url + '/A/D', None,
12687                                       expected_output,
12688                                       expected_mergeinfo_output,
12689                                       expected_elision_output,
12690                                       expected_disk,
12691                                       expected_status,
12692                                       expected_skip,
12693                                       check_props=True)
12694  wc_status.tweak('A_COPY_2/D',
12695                  'A_COPY_2/D/H/omega',
12696                  wc_rev=9)
12697  expected_output = wc.State(wc_dir, {
12698    'A_COPY_2/D'         : Item(verb='Sending'),
12699    'A_COPY_2/D/H/omega' : Item(verb='Sending'),
12700    })
12701  svntest.actions.run_and_verify_commit(wc_dir, expected_output, wc_status)
12702
12703  # Merge r9 from A_COPY_2 to A_COPY.  A_COPY/D gets the explicit mergeinfo
12704  # '/A/D/:7' added from r9.  But it prior to the merge it inherited '/A/D:5'
12705  # from A_COPY, so this should be present in its explicit mergeinfo.  Lastly,
12706  # the mergeinfo describing this merge '/A_COPY_2:9' should also be present
12707  # in A_COPY's explicit mergeinfo.
12708    # Update working copy to allow full inheritance and elision.
12709  svntest.actions.run_and_verify_svn(exp_noop_up_out(9), [],
12710                                     'up', wc_dir)
12711  expected_output = wc.State(A_COPY_path, {
12712    'D'        : Item(status=' U'),
12713    'D/H/omega': Item(status='U '),
12714    })
12715  expected_mergeinfo_output = wc.State(A_COPY_path, {
12716    ''  : Item(status=' U'),
12717    'D' : Item(status=' G'),
12718    })
12719  expected_elision_output = wc.State(A_COPY_path, {
12720    })
12721  expected_status = wc.State(A_COPY_path, {
12722    ''          : Item(status=' M', wc_rev=9),
12723    'B'         : Item(status='  ', wc_rev=9),
12724    'mu'        : Item(status='  ', wc_rev=9),
12725    'B/E'       : Item(status='  ', wc_rev=9),
12726    'B/E/alpha' : Item(status='  ', wc_rev=9),
12727    'B/E/beta'  : Item(status='  ', wc_rev=9),
12728    'B/lambda'  : Item(status='  ', wc_rev=9),
12729    'B/F'       : Item(status='  ', wc_rev=9),
12730    'C'         : Item(status='  ', wc_rev=9),
12731    'D'         : Item(status=' M', wc_rev=9),
12732    'D/G'       : Item(status='  ', wc_rev=9),
12733    'D/G/pi'    : Item(status='  ', wc_rev=9),
12734    'D/G/rho'   : Item(status='  ', wc_rev=9),
12735    'D/G/tau'   : Item(status='  ', wc_rev=9),
12736    'D/gamma'   : Item(status='  ', wc_rev=9),
12737    'D/H'       : Item(status='  ', wc_rev=9),
12738    'D/H/chi'   : Item(status='  ', wc_rev=9),
12739    'D/H/psi'   : Item(status='  ', wc_rev=9),
12740    'D/H/omega' : Item(status='M ', wc_rev=9),
12741    })
12742  expected_disk = wc.State('', {
12743    ''          : Item(props={SVN_PROP_MERGEINFO : '/A:5\n/A_COPY_2:9'}),
12744    'B'         : Item(),
12745    'mu'        : Item("This is the file 'mu'.\n"),
12746    'B/E'       : Item(),
12747    'B/E/alpha' : Item("This is the file 'alpha'.\n"),
12748    'B/E/beta'  : Item("This is the file 'beta'.\n"),
12749    'B/lambda'  : Item("This is the file 'lambda'.\n"),
12750    'B/F'       : Item(),
12751    'C'         : Item(),
12752    'D'         : Item(props={SVN_PROP_MERGEINFO : '/A/D:5,7\n/A_COPY_2/D:9'}),
12753    'D/G'       : Item(),
12754    'D/G/pi'    : Item("This is the file 'pi'.\n"),
12755    'D/G/rho'   : Item("New content"),
12756    'D/G/tau'   : Item("This is the file 'tau'.\n"),
12757    'D/gamma'   : Item("This is the file 'gamma'.\n"),
12758    'D/H'       : Item(),
12759    'D/H/chi'   : Item("This is the file 'chi'.\n"),
12760    'D/H/psi'   : Item("This is the file 'psi'.\n"),
12761    'D/H/omega' : Item("New content"),
12762    })
12763  expected_skip = wc.State(A_COPY_path, { })
12764  svntest.actions.run_and_verify_merge(A_COPY_path, '8', '9',
12765                                       sbox.repo_url + '/A_COPY_2', None,
12766                                       expected_output,
12767                                       expected_mergeinfo_output,
12768                                       expected_elision_output,
12769                                       expected_disk,
12770                                       expected_status,
12771                                       expected_skip,
12772                                       check_props=True)
12773
12774  # Revert and repeat the above merge, but this time create some
12775  # uncommitted mergeinfo on A_COPY/D, this should not cause a write
12776  # lock error as was seen in http://subversion.tigris.org/
12777  # ds/viewMessage.do?dsForumId=462&dsMessageId=103945
12778  svntest.actions.run_and_verify_svn(None, [],
12779                                     'revert', '-R', wc_dir)
12780  svntest.actions.run_and_verify_svn(None, [],
12781                                     'ps', SVN_PROP_MERGEINFO, '',
12782                                     D_COPY_path)
12783  expected_output = wc.State(A_COPY_path, {
12784    'D'        : Item(status=' G'), # Merged with local svn:mergeinfo
12785    'D/H/omega': Item(status='U '),
12786    })
12787  expected_mergeinfo_output = wc.State(A_COPY_path, {
12788    ''  : Item(status=' U'),
12789    'D' : Item(status=' G'),
12790    })
12791  expected_elision_output = wc.State(A_COPY_path, {
12792    })
12793  svntest.actions.run_and_verify_merge(A_COPY_path, '8', '9',
12794                                       sbox.repo_url + '/A_COPY_2', None,
12795                                       expected_output,
12796                                       expected_mergeinfo_output,
12797                                       expected_elision_output,
12798                                       expected_disk,
12799                                       expected_status,
12800                                       expected_skip,
12801                                       check_props=True)
12802
12803#----------------------------------------------------------------------
12804@SkipUnless(server_has_mergeinfo)
12805def natural_history_filtering(sbox):
12806  "natural history filtering permits valid mergeinfo"
12807
12808  # While filtering self-referential mergeinfo (e.g. natural history) that
12809  # a merge tries to add to a target, we may encounter contiguous revision
12810  # ranges that describe *both* natural history and valid mergeinfo.  The
12811  # former should be filtered, but the latter allowed and recorded on the
12812  # target.  See
12813  # http://subversion.tigris.org/servlets/ReadMsg?listName=dev&msgNo=142777.
12814  #
12815  # To set up a situation where this can occur we'll do the following:
12816  #
12817  #     trunk   -1-----3-4-5-6-------8----------- A
12818  #                \           \       \
12819  #     branch1     2-----------\-------9-------- A_COPY
12820  #                              \        \
12821  #     branch2                   7--------10---- A_COPY_2
12822  #
12823  #   1) Create a 'trunk'.
12824  #
12825  #   2) Copy 'trunk' to 'branch1'.
12826  #
12827  #   3) Make some changes under 'trunk'.
12828  #
12829  #   4) Copy 'trunk' to 'branch2'.
12830  #
12831  #   5) Make some more changes under 'trunk'.
12832  #
12833  #   6) Merge all available revisions from 'trunk' to 'branch1' and commit.
12834  #
12835  #   7) Merge all available revisions from 'branch1' to 'branch2'.
12836  #      'branch2' should have explicit merginfo for both 'branch1' *and* for
12837  #      the revisions on 'trunk' which occurred after 'branch2' was copied as
12838  #      these are not part of 'branch2's natural history.
12839
12840  def path_join(head, tail):
12841    if not head: return tail
12842    if not tail: return head
12843    return head + '/' + tail
12844
12845  def greek_file_item(path):
12846    if path[-1:].islower():
12847      basename = re.sub('.*/', '', path)
12848      return Item("This is the file '" + basename + "'.\n")
12849    return Item()
12850
12851  A_paths = [
12852    "",
12853    "B",
12854    "B/lambda",
12855    "B/E",
12856    "B/E/alpha",
12857    "B/E/beta",
12858    "B/F",
12859    "mu",
12860    "C",
12861    "D",
12862    "D/gamma",
12863    "D/G",
12864    "D/G/pi",
12865    "D/G/rho",
12866    "D/G/tau",
12867    "D/H",
12868    "D/H/chi",
12869    "D/H/omega",
12870    "D/H/psi",
12871    ]
12872
12873  sbox.build()
12874  wc_dir = sbox.wc_dir
12875
12876  # Some paths we'll care about
12877  A_COPY_path   = sbox.ospath('A_COPY')
12878  A_COPY_2_path = sbox.ospath('A_COPY_2')
12879  chi_path      = sbox.ospath('A/D/H/chi')
12880
12881  # r1-r6: Setup a 'trunk' (A) and a 'branch' (A_COPY).
12882  wc_disk, wc_status = set_up_branch(sbox, False, 1)
12883
12884  # r7: Make a second 'branch': Copy A to A_COPY_2
12885  expected = svntest.verify.UnorderedOutput(
12886    [ "A         " + sbox.ospath(path_join("A_COPY_2", p)) + "\n"
12887      for p in A_paths ])
12888  wc_status.add(
12889    { path_join("A_COPY_2", p) : Item(status='  ', wc_rev=7)
12890      for p in A_paths })
12891  wc_disk.add(
12892    { path_join("A_COPY_2", p) :
12893        Item("New content") if p in ['B/E/beta', 'D/G/rho', 'D/H/chi', 'D/H/psi']
12894                            else greek_file_item(p)
12895      for p in A_paths })
12896  svntest.actions.run_and_verify_svn(expected, [], 'copy',
12897                                     sbox.repo_url + "/A",
12898                                     A_COPY_2_path)
12899  expected_output = wc.State(wc_dir, {"A_COPY_2" : Item(verb='Adding')})
12900  svntest.actions.run_and_verify_commit(wc_dir,
12901                                        expected_output,
12902                                        wc_status)
12903
12904  # r8: Make a text change under A, to A/D/H/chi.
12905  svntest.main.file_write(chi_path, "New content")
12906  expected_output = wc.State(wc_dir, {'A/D/H/chi' : Item(verb='Sending')})
12907  wc_status.tweak('A/D/H/chi', wc_rev=8)
12908  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
12909                                        wc_status)
12910  wc_disk.tweak('A/D/H/psi', contents="New content")
12911
12912  # r9: Merge all available revisions from A to A_COPY.  But first
12913  # update working copy to allow full inheritance and elision.
12914  svntest.actions.run_and_verify_svn(exp_noop_up_out(8), [],
12915                                     'up', wc_dir)
12916  wc_status.tweak(wc_rev=8)
12917  expected_output = wc.State(A_COPY_path, {
12918    'B/E/beta' : Item(status='U '),
12919    'D/G/rho'  : Item(status='U '),
12920    'D/H/chi'  : Item(status='U '),
12921    'D/H/psi'  : Item(status='U '),
12922    'D/H/omega': Item(status='U '),
12923    })
12924  expected_mergeinfo_output = wc.State(A_COPY_path, {
12925    '' : Item(status=' U'),
12926    })
12927  expected_elision_output = wc.State(A_COPY_path, {
12928    })
12929  expected_status = wc.State(A_COPY_path, {
12930    ''          : Item(status=' M', wc_rev=8),
12931    'B'         : Item(status='  ', wc_rev=8),
12932    'mu'        : Item(status='  ', wc_rev=8),
12933    'B/E'       : Item(status='  ', wc_rev=8),
12934    'B/E/alpha' : Item(status='  ', wc_rev=8),
12935    'B/E/beta'  : Item(status='M ', wc_rev=8),
12936    'B/lambda'  : Item(status='  ', wc_rev=8),
12937    'B/F'       : Item(status='  ', wc_rev=8),
12938    'C'         : Item(status='  ', wc_rev=8),
12939    'D'         : Item(status='  ', wc_rev=8),
12940    'D/G'       : Item(status='  ', wc_rev=8),
12941    'D/G/pi'    : Item(status='  ', wc_rev=8),
12942    'D/G/rho'   : Item(status='M ', wc_rev=8),
12943    'D/G/tau'   : Item(status='  ', wc_rev=8),
12944    'D/gamma'   : Item(status='  ', wc_rev=8),
12945    'D/H'       : Item(status='  ', wc_rev=8),
12946    'D/H/chi'   : Item(status='M ', wc_rev=8),
12947    'D/H/psi'   : Item(status='M ', wc_rev=8),
12948    'D/H/omega' : Item(status='M ', wc_rev=8),
12949    })
12950  expected_disk = wc.State('', {
12951    ''          : Item(props={SVN_PROP_MERGEINFO : '/A:2-8'}),
12952    'B'         : Item(),
12953    'mu'        : Item("This is the file 'mu'.\n"),
12954    'B/E'       : Item(),
12955    'B/E/alpha' : Item("This is the file 'alpha'.\n"),
12956    'B/E/beta'  : Item("New content"),
12957    'B/lambda'  : Item("This is the file 'lambda'.\n"),
12958    'B/F'       : Item(),
12959    'C'         : Item(),
12960    'D'         : Item(),
12961    'D/G'       : Item(),
12962    'D/G/pi'    : Item("This is the file 'pi'.\n"),
12963    'D/G/rho'   : Item("New content"),
12964    'D/G/tau'   : Item("This is the file 'tau'.\n"),
12965    'D/gamma'   : Item("This is the file 'gamma'.\n"),
12966    'D/H'       : Item(),
12967    'D/H/chi'   : Item("New content"),
12968    'D/H/psi'   : Item("New content"),
12969    'D/H/omega' : Item("New content"),
12970    })
12971  expected_skip = wc.State(A_COPY_path, { })
12972  svntest.actions.run_and_verify_merge(A_COPY_path, None, None,
12973                                       sbox.repo_url + '/A', None,
12974                                       expected_output,
12975                                       expected_mergeinfo_output,
12976                                       expected_elision_output,
12977                                       expected_disk,
12978                                       expected_status,
12979                                       expected_skip,
12980                                       check_props=True)
12981  wc_status.tweak('A_COPY',
12982                  'A_COPY/B/E/beta',
12983                  'A_COPY/D/G/rho',
12984                  'A_COPY/D/H/chi',
12985                  'A_COPY/D/H/psi',
12986                  'A_COPY/D/H/omega',
12987                  wc_rev=9)
12988  expected_output = wc.State(wc_dir, {
12989    'A_COPY'           : Item(verb='Sending'),
12990    'A_COPY/B/E/beta'  : Item(verb='Sending'),
12991    'A_COPY/D/G/rho'   : Item(verb='Sending'),
12992    'A_COPY/D/H/chi'   : Item(verb='Sending'),
12993    'A_COPY/D/H/psi'   : Item(verb='Sending'),
12994    'A_COPY/D/H/omega' : Item(verb='Sending'),
12995    })
12996  svntest.actions.run_and_verify_commit(wc_dir, expected_output, wc_status)
12997
12998  # Again update the working copy to allow full inheritance and elision.
12999  svntest.actions.run_and_verify_svn(exp_noop_up_out(9), [],
13000                                     'up', wc_dir)
13001  wc_status.tweak(wc_rev=9)
13002
13003  # Merge all available revisions from A_COPY to A_COPY_2.  The mergeinfo on
13004  # A_COPY_2 should reflect both the merge of revisions 2-9 from A_COPY *and*
13005  # revisions 7-8 from A.  Reivisions 2-6 from A should not be part of the
13006  # explicit mergeinfo on A_COPY_2 as they are already part of its natural
13007  # history.
13008  expected_output = wc.State(A_COPY_2_path, {
13009    ''         : Item(status=' U'),
13010    'D/H/chi'  : Item(status='U '),
13011    })
13012  expected_mergeinfo_output = wc.State(A_COPY_2_path, {
13013    '' : Item(status=' G'),
13014    })
13015  expected_elision_output = wc.State(A_COPY_2_path, {
13016    })
13017  expected_status = wc.State(A_COPY_2_path, {
13018    ''          : Item(status=' M', wc_rev=9),
13019    'B'         : Item(status='  ', wc_rev=9),
13020    'mu'        : Item(status='  ', wc_rev=9),
13021    'B/E'       : Item(status='  ', wc_rev=9),
13022    'B/E/alpha' : Item(status='  ', wc_rev=9),
13023    'B/E/beta'  : Item(status='  ', wc_rev=9),
13024    'B/lambda'  : Item(status='  ', wc_rev=9),
13025    'B/F'       : Item(status='  ', wc_rev=9),
13026    'C'         : Item(status='  ', wc_rev=9),
13027    'D'         : Item(status='  ', wc_rev=9),
13028    'D/G'       : Item(status='  ', wc_rev=9),
13029    'D/G/pi'    : Item(status='  ', wc_rev=9),
13030    'D/G/rho'   : Item(status='  ', wc_rev=9),
13031    'D/G/tau'   : Item(status='  ', wc_rev=9),
13032    'D/gamma'   : Item(status='  ', wc_rev=9),
13033    'D/H'       : Item(status='  ', wc_rev=9),
13034    'D/H/chi'   : Item(status='M ', wc_rev=9),
13035    'D/H/psi'   : Item(status='  ', wc_rev=9),
13036    'D/H/omega' : Item(status='  ', wc_rev=9),
13037    })
13038  expected_disk = wc.State('', {
13039    ''          : Item(props={SVN_PROP_MERGEINFO : '/A:7-8\n/A_COPY:2-9'}),
13040    'B'         : Item(),
13041    'mu'        : Item("This is the file 'mu'.\n"),
13042    'B/E'       : Item(),
13043    'B/E/alpha' : Item("This is the file 'alpha'.\n"),
13044    'B/E/beta'  : Item("New content"),
13045    'B/lambda'  : Item("This is the file 'lambda'.\n"),
13046    'B/F'       : Item(),
13047    'C'         : Item(),
13048    'D'         : Item(),
13049    'D/G'       : Item(),
13050    'D/G/pi'    : Item("This is the file 'pi'.\n"),
13051    'D/G/rho'   : Item("New content"),
13052    'D/G/tau'   : Item("This is the file 'tau'.\n"),
13053    'D/gamma'   : Item("This is the file 'gamma'.\n"),
13054    'D/H'       : Item(),
13055    'D/H/chi'   : Item("New content"),
13056    'D/H/psi'   : Item("New content"),
13057    'D/H/omega' : Item("New content"),
13058    })
13059  expected_skip = wc.State(A_COPY_path, { })
13060  svntest.actions.run_and_verify_merge(A_COPY_2_path, None, None,
13061                                       sbox.repo_url + '/A_COPY', None,
13062                                       expected_output,
13063                                       expected_mergeinfo_output,
13064                                       expected_elision_output,
13065                                       expected_disk,
13066                                       expected_status,
13067                                       expected_skip,
13068                                       check_props=True)
13069
13070#----------------------------------------------------------------------
13071@SkipUnless(server_has_mergeinfo)
13072@Issue(3067)
13073def subtree_gets_changes_even_if_ultimately_deleted(sbox):
13074  "subtree gets changes even if ultimately deleted"
13075
13076  # merge_tests.py 101 'merge tries to delete a file of identical content'
13077  # demonstrates how a file can be deleted by a merge if the file is identical
13078  # to the file deleted in the merge source.  If the file differs then it
13079  # should be 'skipped' as a tree-conflict.  But suppose the file has
13080  # mergeinfo such that the requested merge should bring the file into a state
13081  # identical to the deleted source *before* attempting to delete it.  Then the
13082  # file should get those changes first and then be deleted rather than skipped.
13083  #
13084  # This problem, as discussed here,
13085  # http://subversion.tigris.org/servlets/ReadMsg?listName=dev&msgNo=141533,
13086  # is only nominally a tree conflict issue.  More accurately this is yet
13087  # another issue #3067 problem, in that the merge target has a subtree which
13088  # doesn't exist in part of the requested merge range.
13089
13090  # r1: Create a greek tree.
13091  sbox.build()
13092  wc_dir = sbox.wc_dir
13093
13094  # Some paths we'll care about
13095  H_COPY_path   = sbox.ospath('A_COPY/D/H')
13096  psi_path      = sbox.ospath('A/D/H/psi')
13097  psi_COPY_path = sbox.ospath('A_COPY/D/H/psi')
13098
13099  # r2 - r6: Copy A to A_COPY and then make some text changes under A.
13100  set_up_branch(sbox)
13101
13102  # r7: Make an additional text mod to A/D/H/psi.
13103  svntest.main.file_write(psi_path, "Even newer content")
13104  sbox.simple_commit(message='mod psi')
13105
13106  # r8: Delete A/D/H/psi.
13107  svntest.actions.run_and_verify_svn(None, [],
13108                                     'delete', psi_path)
13109  sbox.simple_commit(message='delete psi')
13110
13111  # Update WC before merging so mergeinfo elision and inheritance
13112  # occur smoothly.
13113  svntest.main.run_svn(None, 'up', wc_dir)
13114
13115  # r9: Merge r3,7 from A/D/H to A_COPY/D/H, then reverse merge r7 from
13116  # A/D/H/psi to A_COPY/D/H/psi.
13117  expected_output = wc.State(H_COPY_path, {
13118    'psi' : Item(status='G ', prev_status='U '), # Touched twice
13119    })
13120  expected_mergeinfo_output = wc.State(H_COPY_path, {
13121    '' : Item(status=' G', prev_status=' U'),
13122    })
13123  expected_elision_output = wc.State(H_COPY_path, {
13124    })
13125  expected_status = wc.State(H_COPY_path, {
13126    ''      : Item(status=' M', wc_rev=8),
13127    'psi'   : Item(status='M ', wc_rev=8),
13128    'omega' : Item(status='  ', wc_rev=8),
13129    'chi'   : Item(status='  ', wc_rev=8),
13130    })
13131  expected_disk = wc.State('', {
13132    ''      : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:3,7'}),
13133    'psi'   : Item("Even newer content"),
13134    'omega' : Item("This is the file 'omega'.\n"),
13135    'chi'   : Item("This is the file 'chi'.\n"),
13136    })
13137  expected_skip = wc.State(H_COPY_path, { })
13138
13139  svntest.actions.run_and_verify_merge(H_COPY_path, None, None,
13140                                       sbox.repo_url + '/A/D/H', None,
13141                                       expected_output,
13142                                       expected_mergeinfo_output,
13143                                       expected_elision_output,
13144                                       expected_disk,
13145                                       expected_status, expected_skip,
13146                                       [], True, False,
13147                                       '-c3,7', H_COPY_path)
13148  svntest.actions.run_and_verify_svn(
13149    expected_merge_output([[-7]],
13150                          ['G    ' + psi_COPY_path + '\n',
13151                           ' G   ' + psi_COPY_path + '\n',]),
13152    [], 'merge', '-c-7', sbox.repo_url + '/A/D/H/psi@7', psi_COPY_path)
13153  sbox.simple_commit(message='merge -c3,7 from A/D/H,' \
13154                             'reverse merge -c-7 from A/D/H/psi')
13155
13156  # Merge all available revisions from A/D/H to A_COPY/D/H.  This merge
13157  # ultimately tries to delete A_COPY/D/H/psi, but first it should merge
13158  # r7 to A_COPY/D/H/psi, since that is one of the available revisions.
13159  # Then when merging the deletion of A_COPY/D/H/psi in r8 the file will
13160  # be identical to the deleted source A/D/H/psi and the deletion will
13161  # succeed.
13162  #
13163  # Update WC before merging so mergeinfo elision and inheritance
13164  # occur smoothly.
13165  svntest.main.run_svn(None, 'up', wc_dir)
13166  expected_output = wc.State(H_COPY_path, {
13167    'omega' : Item(status='U '),
13168    'psi'   : Item(status='D ', prev_status='U '),
13169    })
13170  expected_mergeinfo_output = wc.State(H_COPY_path, {
13171    '' : Item(status=' U'),
13172    })
13173  expected_elision_output = wc.State(H_COPY_path, {
13174    })
13175  expected_status = wc.State(H_COPY_path, {
13176    ''      : Item(status=' M', wc_rev=9),
13177    'psi'   : Item(status='D ', wc_rev=9),
13178    'omega' : Item(status='M ', wc_rev=9),
13179    'chi'   : Item(status='  ', wc_rev=9),
13180    })
13181  expected_disk = wc.State('', {
13182    ''      : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:2-9'}),
13183    'omega' : Item("New content"),
13184    'chi'   : Item("This is the file 'chi'.\n"),
13185    })
13186  expected_skip = wc.State(H_COPY_path, { })
13187
13188  svntest.actions.run_and_verify_merge(H_COPY_path, None, None,
13189                                       sbox.repo_url + '/A/D/H', None,
13190                                       expected_output,
13191                                       expected_mergeinfo_output,
13192                                       expected_elision_output,
13193                                       expected_disk,
13194                                       expected_status, expected_skip,
13195                                       [], True, False)
13196
13197#----------------------------------------------------------------------
13198@SkipUnless(server_has_mergeinfo)
13199def no_self_referential_filtering_on_added_path(sbox):
13200  "no self referential filtering on added path"
13201
13202  sbox.build()
13203  wc_dir = sbox.wc_dir
13204
13205  # Some paths we'll care about
13206  C_COPY_path   = sbox.ospath('A_COPY/C')
13207  A_path        = sbox.ospath('A')
13208  C_path        = sbox.ospath('A/C')
13209  A_COPY_2_path = sbox.ospath('A_COPY_2')
13210
13211  # r1-r7: Setup a 'trunk' and two 'branches'.
13212  wc_disk, wc_status = set_up_branch(sbox, False, 2)
13213
13214  # r8: Make a prop change on A_COPY/C.
13215  svntest.actions.run_and_verify_svn(["property 'propname' set on '" +
13216                                      C_COPY_path + "'\n"], [],
13217                                     'ps', 'propname', 'propval',
13218                                     C_COPY_path)
13219  expected_output = svntest.wc.State(wc_dir,
13220                                     {'A_COPY/C' : Item(verb='Sending')})
13221  wc_status.tweak('A_COPY/C', wc_rev=8)
13222  wc_disk.tweak("A_COPY/C",
13223                props={'propname' : 'propval'})
13224  svntest.actions.run_and_verify_commit(wc_dir, expected_output, wc_status)
13225
13226  # r9: Merge r8 from A_COPY to A.
13227  #
13228  # Update first to avoid an out of date error.
13229  svntest.actions.run_and_verify_svn(exp_noop_up_out(8), [], 'up',
13230                                     wc_dir)
13231  wc_status.tweak(wc_rev=8)
13232  svntest.actions.run_and_verify_svn(
13233    expected_merge_output([[8]],
13234                          [' U   ' + C_path + '\n',
13235                           ' U   ' + A_path + '\n',]),
13236    [], 'merge', '-c8', sbox.repo_url + '/A_COPY', A_path)
13237  expected_output = svntest.wc.State(wc_dir,
13238                                     {'A'   : Item(verb='Sending'),
13239                                      'A/C' : Item(verb='Sending')})
13240  wc_status.tweak('A', 'A/C', wc_rev=9)
13241  svntest.actions.run_and_verify_commit(wc_dir, expected_output, wc_status)
13242
13243  wc_disk.tweak("A/C",
13244                props={'propname' : 'propval'})
13245  wc_disk.tweak("A",
13246                props={SVN_PROP_MERGEINFO : '/A_COPY:8'})
13247
13248  # r10: Move A/C to A/C_MOVED.
13249  svntest.actions.run_and_verify_svn(['Committing transaction...\n',
13250                                      'Committed revision 10.\n'],
13251                                     [], 'move',
13252                                     sbox.repo_url + '/A/C',
13253                                     sbox.repo_url + '/A/C_MOVED',
13254                                     '-m', 'Copy A/C to A/C_MOVED')
13255  svntest.actions.run_and_verify_svn(None, [], 'up',
13256                                     wc_dir)
13257
13258  # Now try to merge all available revisions from A to A_COPY_2.
13259  # This should try to add the directory A_COPY_2/C_MOVED which has
13260  # explicit mergeinfo.  This should not break self-referential mergeinfo
13261  # filtering logic...in fact there is no reason to even attempt such
13262  # filtering since the file is *new*.
13263
13264  expected_output = wc.State(A_COPY_2_path, {
13265    ''          : Item(status=' U'),
13266    'B/E/beta'  : Item(status='U '),
13267    'D/G/rho'   : Item(status='U '),
13268    'D/H/psi'   : Item(status='U '),
13269    'D/H/omega' : Item(status='U '),
13270    'C'         : Item(status='D '),
13271    'C_MOVED'   : Item(status='A '),
13272    })
13273  # Why is C_MOVED notified as ' G' rather than ' U'?  C_MOVED was
13274  # added by the merge and there is only a single editor drive, so
13275  # how can any prop changes be merged to it?  The answer is that
13276  # the merge code does some quiet housekeeping, merging C_MOVED's
13277  # inherited mergeinfo into its incoming mergeinfo, see
13278  # https://issues.apache.org/jira/browse/SVN-4309
13279  # This test is not covering issue #4309 so we let the current
13280  # behavior pass.
13281  expected_mergeinfo_output = wc.State(A_COPY_2_path, {
13282    ''        : Item(status=' G'),
13283    'C_MOVED' : Item(status=' G'),
13284    })
13285  expected_elision_output = wc.State(A_COPY_2_path, {
13286    })
13287  expected_A_COPY_2_status = wc.State(A_COPY_2_path, {
13288    ''          : Item(status=' M', wc_rev=10),
13289    'B'         : Item(status='  ', wc_rev=10),
13290    'mu'        : Item(status='  ', wc_rev=10),
13291    'B/E'       : Item(status='  ', wc_rev=10),
13292    'B/E/alpha' : Item(status='  ', wc_rev=10),
13293    'B/E/beta'  : Item(status='M ', wc_rev=10),
13294    'B/lambda'  : Item(status='  ', wc_rev=10),
13295    'B/F'       : Item(status='  ', wc_rev=10),
13296    'C'         : Item(status='D ', wc_rev=10),
13297    'C_MOVED'   : Item(status='A ', wc_rev='-', copied='+'),
13298    'D'         : Item(status='  ', wc_rev=10),
13299    'D/G'       : Item(status='  ', wc_rev=10),
13300    'D/G/pi'    : Item(status='  ', wc_rev=10),
13301    'D/G/rho'   : Item(status='M ', wc_rev=10),
13302    'D/G/tau'   : Item(status='  ', wc_rev=10),
13303    'D/gamma'   : Item(status='  ', wc_rev=10),
13304    'D/H'       : Item(status='  ', wc_rev=10),
13305    'D/H/chi'   : Item(status='  ', wc_rev=10),
13306    'D/H/psi'   : Item(status='M ', wc_rev=10),
13307    'D/H/omega' : Item(status='M ', wc_rev=10),
13308    })
13309  expected_A_COPY_2_disk = wc.State('', {
13310    ''          : Item(props={SVN_PROP_MERGEINFO : '/A:3-10\n/A_COPY:8'}),
13311    'B'         : Item(),
13312    'mu'        : Item("This is the file 'mu'.\n"),
13313    'B/E'       : Item(),
13314    'B/E/alpha' : Item("This is the file 'alpha'.\n"),
13315    'B/E/beta'  : Item("New content"),
13316    'B/lambda'  : Item("This is the file 'lambda'.\n"),
13317    'B/F'       : Item(),
13318  # What's up with the mergeinfo
13319    'C_MOVED'   : Item(props={SVN_PROP_MERGEINFO : '/A/C_MOVED:10\n' +
13320                              '/A_COPY/C:8\n' +
13321                              '/A_COPY/C_MOVED:8',
13322                              'propname' : 'propval'}),
13323    'D'         : Item(),
13324    'D/G'       : Item(),
13325    'D/G/pi'    : Item("This is the file 'pi'.\n"),
13326    'D/G/rho'   : Item("New content"),
13327    'D/G/tau'   : Item("This is the file 'tau'.\n"),
13328    'D/gamma'   : Item("This is the file 'gamma'.\n"),
13329    'D/H'       : Item(),
13330    'D/H/chi'   : Item("This is the file 'chi'.\n"),
13331    'D/H/psi'   : Item("New content"),
13332    'D/H/omega' : Item("New content"),
13333    })
13334  expected_A_COPY_2_skip = wc.State(A_COPY_2_path, { })
13335  svntest.actions.run_and_verify_merge(A_COPY_2_path, None, None,
13336                                       sbox.repo_url + '/A', None,
13337                                       expected_output,
13338                                       expected_mergeinfo_output,
13339                                       expected_elision_output,
13340                                       expected_A_COPY_2_disk,
13341                                       expected_A_COPY_2_status,
13342                                       expected_A_COPY_2_skip,
13343                                       check_props=True)
13344
13345#----------------------------------------------------------------------
13346# Test for issue #3324
13347# https://issues.apache.org/jira/browse/SVN-3324
13348@Issue(3324)
13349@SkipUnless(server_has_mergeinfo)
13350def merge_range_prior_to_rename_source_existence(sbox):
13351  "merge prior to rename src existence still dels src"
13352
13353  # Replicate a merge bug found while synching up a feature branch on the
13354  # Subversion repository with trunk.  See r874121 of
13355  # http://svn.apache.org/repos/asf/subversion/branches/ignore-mergeinfo, in which
13356  # a move was merged to the target, but the delete half of the move
13357  # didn't occur.
13358
13359  sbox.build()
13360  wc_dir = sbox.wc_dir
13361
13362  # Some paths we'll care about
13363  nu_path         = sbox.ospath('A/D/H/nu')
13364  nu_moved_path   = sbox.ospath('A/D/H/nu_moved')
13365  A_path          = sbox.ospath('A')
13366  alpha_path      = sbox.ospath('A/B/E/alpha')
13367  A_COPY_path     = sbox.ospath('A_COPY')
13368  A_COPY_2_path   = sbox.ospath('A_COPY_2')
13369  B_COPY_path     = sbox.ospath('A_COPY/B')
13370  B_COPY_2_path   = sbox.ospath('A_COPY_2/B')
13371  alpha_COPY_path = sbox.ospath('A_COPY/B/E/alpha')
13372  beta_COPY_path  = sbox.ospath('A_COPY/B/E/beta')
13373  gamma_COPY_path = sbox.ospath('A_COPY/D/gamma')
13374  rho_COPY_path   = sbox.ospath('A_COPY/D/G/rho')
13375  omega_COPY_path = sbox.ospath('A_COPY/D/H/omega')
13376  psi_COPY_path   = sbox.ospath('A_COPY/D/H/psi')
13377  nu_COPY_path    = sbox.ospath('A_COPY/D/H/nu')
13378
13379  # Setup our basic 'trunk' and 'branch':
13380  # r2 - Copy A to A_COPY
13381  # r3 - Copy A to A_COPY_2
13382  # r4 - Text change to A/D/H/psi
13383  # r5 - Text change to A/D/G/rho
13384  # r6 - Text change to A/B/E/beta
13385  # r7 - Text change to A/D/H/omega
13386  wc_disk, wc_status = set_up_branch(sbox, False, 2)
13387
13388  # r8 - Text change to A/B/E/alpha
13389  svntest.main.file_write(alpha_path, "New content")
13390  wc_status.tweak('A/B/E/alpha', wc_rev=8)
13391  svntest.actions.run_and_verify_svn(None, [], 'ci', '-m',
13392                                     'Text change', wc_dir)
13393
13394  # r9 - Add the file A/D/H/nu and make another change to A/B/E/alpha.
13395  svntest.main.file_write(alpha_path, "Even newer content")
13396  svntest.main.file_write(nu_path, "This is the file 'nu'.\n")
13397  svntest.actions.run_and_verify_svn(None, [], 'add', nu_path)
13398  expected_output = wc.State(wc_dir,
13399                             {'A/D/H/nu'    : Item(verb='Adding'),
13400                              'A/B/E/alpha' : Item(verb='Sending')})
13401  wc_status.add({'A/D/H/nu' : Item(status='  ', wc_rev=9)})
13402  wc_status.tweak('A/B/E/alpha', wc_rev=9)
13403  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
13404                                        wc_status)
13405
13406  # r10 - Merge all available revisions (i.e. -r1:9) from A to A_COPY.
13407  svntest.actions.run_and_verify_svn(exp_noop_up_out(9), [], 'up',
13408                                     wc_dir)
13409  wc_status.tweak(wc_rev=9)
13410  svntest.actions.run_and_verify_svn(
13411    expected_merge_output([[2,9]],
13412                          ['A    ' + nu_COPY_path  + '\n',
13413                           'U    ' + alpha_COPY_path + '\n',
13414                           'U    ' + beta_COPY_path  + '\n',
13415                           'U    ' + rho_COPY_path   + '\n',
13416                           'U    ' + omega_COPY_path + '\n',
13417                           'U    ' + psi_COPY_path   + '\n',
13418                           ' U   ' + A_COPY_path     + '\n',]),
13419    [], 'merge', sbox.repo_url + '/A', A_COPY_path)
13420  expected_output = wc.State(wc_dir,
13421                             {'A_COPY'           : Item(verb='Sending'),
13422                              'A_COPY/D/H/nu'    : Item(verb='Adding'),
13423                              'A_COPY/B/E/alpha' : Item(verb='Sending'),
13424                              'A_COPY/B/E/beta'  : Item(verb='Sending'),
13425                              'A_COPY/D/G/rho'   : Item(verb='Sending'),
13426                              'A_COPY/D/H/omega' : Item(verb='Sending'),
13427                              'A_COPY/D/H/psi'   : Item(verb='Sending')})
13428  wc_status.tweak('A_COPY',
13429                  'A_COPY/B/E/alpha',
13430                  'A_COPY/B/E/beta',
13431                  'A_COPY/D/G/rho',
13432                  'A_COPY/D/H/omega',
13433                  'A_COPY/D/H/psi',
13434                  wc_rev=10)
13435  wc_status.add({'A_COPY/D/H/nu' : Item(status='  ', wc_rev=10)})
13436  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
13437                                        wc_status)
13438
13439  # r11 - Reverse merge -r9:1 from A/B to A_COPY/B
13440  svntest.actions.run_and_verify_svn(exp_noop_up_out(10), [], 'up',
13441                                     wc_dir)
13442  wc_status.tweak(wc_rev=10)
13443  svntest.actions.run_and_verify_svn(
13444    expected_merge_output([[9,2]], ['U    ' + alpha_COPY_path + '\n',
13445                                    'U    ' + beta_COPY_path  + '\n',
13446                                    ' G   ' + B_COPY_path     + '\n',]),
13447    [], 'merge', sbox.repo_url + '/A/B', B_COPY_path, '-r9:1')
13448  expected_output = wc.State(wc_dir,
13449                             {'A_COPY/B'         : Item(verb='Sending'),
13450                              'A_COPY/B/E/alpha' : Item(verb='Sending'),
13451                              'A_COPY/B/E/beta'  : Item(verb='Sending')})
13452  wc_status.tweak('A_COPY/B',
13453                  'A_COPY/B/E/alpha',
13454                  'A_COPY/B/E/beta',
13455                  wc_rev=11)
13456  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
13457                                        wc_status)
13458
13459  # r12 - Move A/D/H/nu to A/D/H/nu_moved
13460  svntest.actions.run_and_verify_svn(["Committing transaction...\n",
13461                                            "Committed revision 12.\n"], [],
13462                                     'move', sbox.repo_url + '/A/D/H/nu',
13463                                     sbox.repo_url + '/A/D/H/nu_moved',
13464                                     '-m', 'Move nu to nu_moved')
13465  expected_output = svntest.verify.UnorderedOutput(
13466    ["Updating '%s':\n" % (wc_dir),
13467     "D    " + nu_path + "\n",
13468     "A    " + nu_moved_path + "\n",
13469     "Updated to revision 12.\n"],
13470    )
13471  svntest.actions.run_and_verify_svn(expected_output,
13472                                     [], 'up', wc_dir)
13473
13474  # Now merge -r7:12 from A to A_COPY.
13475  # A_COPY needs only -r10:12, which amounts to the rename of nu.
13476  # The subtree A_COPY/B needs the entire range -r7:12 because of
13477  # the reverse merge we performed in r11; the only operative change
13478  # here is the text mod to alpha made in r9.
13479  #
13480  # This merge previously failed because the delete half of the A_COPY/D/H/nu
13481  # to A_COPY/D/H/nu_moved move was reported in the notifications, but didn't
13482  # actually happen.
13483  expected_output = wc.State(A_COPY_path, {
13484    'B/E/alpha'    : Item(status='U '),
13485    'D/H/nu'       : Item(status='D '),
13486    'D/H/nu_moved' : Item(status='A '),
13487    })
13488  expected_mergeinfo_output = wc.State(A_COPY_path, {
13489    ''  : Item(status=' U'),
13490    'B' : Item(status=' U'),
13491    })
13492  expected_elision_output = wc.State(A_COPY_path, {
13493    })
13494  expected_status = wc.State(A_COPY_path, {
13495    ''             : Item(status=' M', wc_rev=12),
13496    'B'            : Item(status=' M', wc_rev=12),
13497    'mu'           : Item(status='  ', wc_rev=12),
13498    'B/E'          : Item(status='  ', wc_rev=12),
13499    'B/E/alpha'    : Item(status='M ', wc_rev=12),
13500    'B/E/beta'     : Item(status='  ', wc_rev=12),
13501    'B/lambda'     : Item(status='  ', wc_rev=12),
13502    'B/F'          : Item(status='  ', wc_rev=12),
13503    'C'            : Item(status='  ', wc_rev=12),
13504    'D'            : Item(status='  ', wc_rev=12),
13505    'D/G'          : Item(status='  ', wc_rev=12),
13506    'D/G/pi'       : Item(status='  ', wc_rev=12),
13507    'D/G/rho'      : Item(status='  ', wc_rev=12),
13508    'D/G/tau'      : Item(status='  ', wc_rev=12),
13509    'D/gamma'      : Item(status='  ', wc_rev=12),
13510    'D/H'          : Item(status='  ', wc_rev=12),
13511    'D/H/nu'       : Item(status='D ', wc_rev=12),
13512    'D/H/nu_moved' : Item(status='A ', wc_rev='-', copied='+'),
13513    'D/H/chi'      : Item(status='  ', wc_rev=12),
13514    'D/H/psi'      : Item(status='  ', wc_rev=12),
13515    'D/H/omega'    : Item(status='  ', wc_rev=12),
13516    })
13517  expected_disk = wc.State('', {
13518    ''             : Item(props={SVN_PROP_MERGEINFO : '/A:2-12'}),
13519    'mu'           : Item("This is the file 'mu'.\n"),
13520    'B'            : Item(props={SVN_PROP_MERGEINFO : '/A/B:8-12'}),
13521    'B/E'          : Item(),
13522    'B/E/alpha'    : Item("Even newer content"),
13523    'B/E/beta'     : Item("This is the file 'beta'.\n"),
13524    'B/lambda'     : Item("This is the file 'lambda'.\n"),
13525    'B/F'          : Item(),
13526    'C'            : Item(),
13527    'D'            : Item(),
13528    'D/G'          : Item(),
13529    'D/G/pi'       : Item("This is the file 'pi'.\n"),
13530    'D/G/rho'      : Item("New content"),
13531    'D/G/tau'      : Item("This is the file 'tau'.\n"),
13532    'D/gamma'      : Item("This is the file 'gamma'.\n"),
13533    'D/H'          : Item(),
13534    'D/H/nu_moved' : Item("This is the file 'nu'.\n"),
13535    'D/H/chi'      : Item("This is the file 'chi'.\n"),
13536    'D/H/psi'      : Item("New content"),
13537    'D/H/omega'    : Item("New content"),
13538    })
13539  expected_skip = wc.State(A_COPY_path, {})
13540  svntest.actions.run_and_verify_merge(A_COPY_path, 7, 12,
13541                                       sbox.repo_url + '/A', None,
13542                                       expected_output,
13543                                       expected_mergeinfo_output,
13544                                       expected_elision_output,
13545                                       expected_disk,
13546                                       expected_status,
13547                                       expected_skip,
13548                                       check_props=True)
13549  svntest.actions.run_and_verify_svn(None, [], 'ci', '-m',
13550                                     'Merge -r7:12 from A to A_COPY', wc_dir)
13551
13552  # Now run a similar scenario as above on the second branch, but with
13553  # a reverse merge this time.
13554  #
13555  # r14 - Merge all available revisions from A/B to A_COPY_B and then merge
13556  # -r2:9 from A to A_COPY_2.  Among other things, this adds A_COPY_2/D/H/nu
13557  # and leaves us with mergeinfo on the A_COPY_2 branch of:
13558  #
13559  #   Properties on 'A_COPY_2':
13560  #     svn:mergeinfo
13561  #       /A:3-9
13562  #   Properties on 'A_COPY_2\B':
13563  #     svn:mergeinfo
13564  #       /A/B:3-13
13565  svntest.actions.run_and_verify_svn(exp_noop_up_out(13), [], 'up',
13566                                     wc_dir)
13567  svntest.actions.run_and_verify_svn(None, # Don't check stdout, we test this
13568                                           # type of merge to death elsewhere.
13569                                     [], 'merge', sbox.repo_url + '/A/B',
13570                                     B_COPY_2_path)
13571  svntest.actions.run_and_verify_svn(None,[], 'merge', '-r', '2:9',
13572                                     sbox.repo_url + '/A', A_COPY_2_path)
13573  svntest.actions.run_and_verify_svn(
13574    None, [], 'ci', '-m',
13575    'Merge all from A/B to A_COPY_2/B\nMerge -r2:9 from A to A_COPY_2',
13576    wc_dir)
13577  svntest.actions.run_and_verify_svn(exp_noop_up_out(14), [], 'up',
13578                                     wc_dir)
13579
13580  # Now reverse merge -r13:7 from A to A_COPY_2.
13581  #
13582  # Recall:
13583  #
13584  #   >svn log -r8:13 ^/A -v
13585  #   ------------------------------------------------------------------------
13586  #   r8 | jrandom | 2010-10-14 11:25:59 -0400 (Thu, 14 Oct 2010) | 1 line
13587  #   Changed paths:
13588  #      M /A/B/E/alpha
13589  #
13590  #   Text change
13591  #   ------------------------------------------------------------------------
13592  #   r9 | jrandom | 2010-10-14 11:25:59 -0400 (Thu, 14 Oct 2010) | 1 line
13593  #   Changed paths:
13594  #      M /A/B/E/alpha
13595  #      A /A/D/H/nu
13596  #
13597  #   log msg
13598  #   ------------------------------------------------------------------------
13599  #   r12 | jrandom | 2010-10-14 11:26:01 -0400 (Thu, 14 Oct 2010) | 1 line
13600  #   Changed paths:
13601  #      D /A/D/H/nu
13602  #      A /A/D/H/nu_moved (from /A/D/H/nu:11)
13603  #
13604  #   Move nu to nu_moved
13605  #   ------------------------------------------------------------------------
13606  #
13607  # We can only reverse merge changes from the explicit mergeinfo or
13608  # natural history of a target, but since all of these changes intersect with
13609  # the target's explicit mergeinfo (including subtrees), all should be
13610  # reverse merged, including the deletion of A_COPY/D/H/nu.  Like the forward
13611  # merge performed earlier, this test previously failed when A_COPY/D/H/nu
13612  # was reported as deleted, but still remained as a versioned item in the WC.
13613  expected_output = wc.State(A_COPY_2_path, {
13614    'B/E/alpha'    : Item(status='U '),
13615    'D/H/nu'       : Item(status='D '),
13616    })
13617  expected_mergeinfo_output = wc.State(A_COPY_2_path, {
13618    ''  : Item(status=' U'),
13619    'B' : Item(status=' U'),
13620    })
13621  expected_elision_output = wc.State(A_COPY_2_path, {
13622    'B' : Item(status=' U'),
13623    })
13624  expected_status = wc.State(A_COPY_2_path, {
13625    ''             : Item(status=' M'),
13626    'B'            : Item(status=' M'),
13627    'mu'           : Item(status='  '),
13628    'B/E'          : Item(status='  '),
13629    'B/E/alpha'    : Item(status='M '),
13630    'B/E/beta'     : Item(status='  '),
13631    'B/lambda'     : Item(status='  '),
13632    'B/F'          : Item(status='  '),
13633    'C'            : Item(status='  '),
13634    'D'            : Item(status='  '),
13635    'D/G'          : Item(status='  '),
13636    'D/G/pi'       : Item(status='  '),
13637    'D/G/rho'      : Item(status='  '),
13638    'D/G/tau'      : Item(status='  '),
13639    'D/gamma'      : Item(status='  '),
13640    'D/H'          : Item(status='  '),
13641    'D/H/nu'       : Item(status='D '),
13642    'D/H/chi'      : Item(status='  '),
13643    'D/H/psi'      : Item(status='  '),
13644    'D/H/omega'    : Item(status='  '),
13645    })
13646  expected_status.tweak(wc_rev=14)
13647  expected_disk = wc.State('', {
13648    ''             : Item(props={SVN_PROP_MERGEINFO : '/A:3-7'}),
13649    'mu'           : Item("This is the file 'mu'.\n"),
13650    'B'            : Item(),
13651    'B/E'          : Item(),
13652    'B/E/alpha'    : Item("This is the file 'alpha'.\n"),
13653    'B/E/beta'     : Item("New content"),
13654    'B/lambda'     : Item("This is the file 'lambda'.\n"),
13655    'B/F'          : Item(),
13656    'C'            : Item(),
13657    'D'            : Item(),
13658    'D/G'          : Item(),
13659    'D/G/pi'       : Item("This is the file 'pi'.\n"),
13660    'D/G/rho'      : Item("New content"),
13661    'D/G/tau'      : Item("This is the file 'tau'.\n"),
13662    'D/gamma'      : Item("This is the file 'gamma'.\n"),
13663    'D/H'          : Item(),
13664    'D/H/chi'      : Item("This is the file 'chi'.\n"),
13665    'D/H/psi'      : Item("New content"),
13666    'D/H/omega'    : Item("New content"),
13667    })
13668  expected_skip = wc.State(A_COPY_path, {})
13669  svntest.actions.run_and_verify_merge(A_COPY_2_path, 13, 7,
13670                                       sbox.repo_url + '/A', None,
13671                                       expected_output,
13672                                       expected_mergeinfo_output,
13673                                       expected_elision_output,
13674                                       expected_disk,
13675                                       expected_status,
13676                                       expected_skip,
13677                                       [], True, True)
13678
13679#----------------------------------------------------------------------
13680def set_up_natural_history_gap(sbox):
13681  '''Starting with standard greek tree, do the following:
13682    r2 - A/D/H/psi
13683    r3 - A/D/G/rho
13684    r4 - A/B/E/beta
13685    r5 - A/D/H/omega
13686    r6 - Delete A
13687    r7 - "Resurrect" A, by copying A@2 to A
13688    r8 - Copy A to A_COPY
13689    r9 - Text mod to A/D/gamma
13690  Lastly it updates the WC to r9.
13691  All text mods set file contents to "New content".
13692  Return (expected_disk, expected_status).'''
13693
13694  # r1: Create a standard greek tree.
13695  sbox.build()
13696  wc_dir = sbox.wc_dir
13697
13698  # r2-5: Make some changes under 'A' (no branches yet).
13699  wc_disk, wc_status = set_up_branch(sbox, False, 0)
13700
13701  # Some paths we'll care about.
13702  A_COPY_path = sbox.ospath('A_COPY')
13703  gamma_path  = sbox.ospath('A/D/gamma')
13704
13705  # r6: Delete 'A'
13706  exit_code, out, err = svntest.actions.run_and_verify_svn(
13707    ["Committing transaction...\n",
13708           "Committed revision 6.\n"], [],
13709    'delete', sbox.repo_url + '/A', '-m', 'Delete A')
13710
13711  # r7: Resurrect 'A' by copying 'A@2' to 'A'.
13712  exit_code, out, err = svntest.actions.run_and_verify_svn(
13713    ["Committing transaction...\n",
13714           "Committed revision 7.\n"], [],
13715    'copy', sbox.repo_url + '/A@2', sbox.repo_url + '/A',
13716    '-m', 'Resurrect A from A@2')
13717
13718  # r8: Branch the resurrected 'A' to 'A_COPY'.
13719  exit_code, out, err = svntest.actions.run_and_verify_svn(
13720    ["Committing transaction...\n",
13721           "Committed revision 8.\n"], [],
13722    'copy', sbox.repo_url + '/A', sbox.repo_url + '/A_COPY',
13723    '-m', 'Copy A to A_COPY')
13724
13725  # Update to bring all the repos side changes down.
13726  exit_code, out, err = svntest.actions.run_and_verify_svn(None, [],
13727                                                           'up', wc_dir)
13728  wc_status.add({
13729      "A_COPY/B"         : Item(status='  '),
13730      "A_COPY/B/lambda"  : Item(status='  '),
13731      "A_COPY/B/E"       : Item(status='  '),
13732      "A_COPY/B/E/alpha" : Item(status='  '),
13733      "A_COPY/B/E/beta"  : Item(status='  '),
13734      "A_COPY/B/F"       : Item(status='  '),
13735      "A_COPY/mu"        : Item(status='  '),
13736      "A_COPY/C"         : Item(status='  '),
13737      "A_COPY/D"         : Item(status='  '),
13738      "A_COPY/D/gamma"   : Item(status='  '),
13739      "A_COPY/D/G"       : Item(status='  '),
13740      "A_COPY/D/G/pi"    : Item(status='  '),
13741      "A_COPY/D/G/rho"   : Item(status='  '),
13742      "A_COPY/D/G/tau"   : Item(status='  '),
13743      "A_COPY/D/H"       : Item(status='  '),
13744      "A_COPY/D/H/chi"   : Item(status='  '),
13745      "A_COPY/D/H/omega" : Item(status='  '),
13746      "A_COPY/D/H/psi"   : Item(status='  '),
13747      "A_COPY"           : Item(status='  ')})
13748  wc_status.tweak(wc_rev=8)
13749
13750  # r9: Make a text change to 'A/D/gamma'.
13751  svntest.main.file_write(gamma_path, "New content")
13752  expected_output = wc.State(wc_dir, {'A/D/gamma' : Item(verb='Sending')})
13753  wc_status.tweak('A/D/gamma', wc_rev=9)
13754
13755  # Update the WC to a uniform revision.
13756  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
13757                                        wc_status)
13758  svntest.actions.run_and_verify_svn(exp_noop_up_out(9), [],
13759                                     'up', wc_dir)
13760  return wc_disk, wc_status
13761
13762#----------------------------------------------------------------------
13763@SkipUnless(server_has_mergeinfo)
13764def dont_merge_gaps_in_history(sbox):
13765  "mergeinfo aware merges ignore natural history gaps"
13766
13767  ## See http://svn.haxx.se/dev/archive-2008-11/0618.shtml ##
13768
13769  wc_dir = sbox.wc_dir
13770
13771  # Create a branch with gaps in its natural history.
13772  set_up_natural_history_gap(sbox)
13773
13774  # Some paths we'll care about.
13775  A_COPY_path = sbox.ospath('A_COPY')
13776
13777  # Now merge all available changes from 'A' to 'A_COPY'.  The only
13778  # available revisions are r8 and r9.  Only r9 effects the source/target
13779  # so this merge should change 'A/D/gamma' from r9.  The fact that 'A_COPY'
13780  # has 'broken' natural history, i.e.
13781  #
13782  #  /A:2,7      <-- Recall 'A@7' was copied from 'A@2'.
13783  #  /A_COPY:8-9
13784  #
13785  # should have no impact, but currently this fact is causing a failure:
13786  #
13787  #  >svn merge %url127%/A merge_tests-127\A_COPY
13788  #  ..\..\..\subversion\libsvn_repos\reporter.c:1162: (apr_err=160005)
13789  #  svn: Target path '/A' does not exist.
13790  expected_output = wc.State(A_COPY_path, {
13791    'D/gamma' : Item(status='U '),
13792    })
13793  expected_mergeinfo_output = wc.State(A_COPY_path, {
13794    '' : Item(status=' U'),
13795    })
13796  expected_elision_output = wc.State(A_COPY_path, {
13797    })
13798  expected_status = wc.State(A_COPY_path, {
13799    ''          : Item(status=' M'),
13800    'B'         : Item(status='  '),
13801    'mu'        : Item(status='  '),
13802    'B/E'       : Item(status='  '),
13803    'B/E/alpha' : Item(status='  '),
13804    'B/E/beta'  : Item(status='  '),
13805    'B/lambda'  : Item(status='  '),
13806    'B/F'       : Item(status='  '),
13807    'C'         : Item(status='  '),
13808    'D'         : Item(status='  '),
13809    'D/G'       : Item(status='  '),
13810    'D/G/pi'    : Item(status='  '),
13811    'D/G/rho'   : Item(status='  '),
13812    'D/G/tau'   : Item(status='  '),
13813    'D/gamma'   : Item(status='M '),
13814    'D/H'       : Item(status='  '),
13815    'D/H/chi'   : Item(status='  '),
13816    'D/H/psi'   : Item(status='  '),
13817    'D/H/omega' : Item(status='  '),
13818    })
13819  expected_status.tweak(wc_rev=9)
13820  expected_disk = wc.State('', {
13821    ''          : Item(props={SVN_PROP_MERGEINFO : '/A:8-9'}),
13822    'B'         : Item(),
13823    'mu'        : Item("This is the file 'mu'.\n"),
13824    'B/E'       : Item(),
13825    'B/E/alpha' : Item("This is the file 'alpha'.\n"),
13826    'B/E/beta'  : Item("This is the file 'beta'.\n"),
13827    'B/lambda'  : Item("This is the file 'lambda'.\n"),
13828    'B/F'       : Item(),
13829    'C'         : Item(),
13830    'D'         : Item(),
13831    'D/G'       : Item(),
13832    'D/G/pi'    : Item("This is the file 'pi'.\n"),
13833    'D/G/rho'   : Item("This is the file 'rho'.\n"),
13834    'D/G/tau'   : Item("This is the file 'tau'.\n"),
13835    'D/gamma'   : Item("New content"),
13836    'D/H'       : Item(),
13837    'D/H/chi'   : Item("This is the file 'chi'.\n"),
13838    'D/H/psi'   : Item("New content"),
13839    'D/H/omega' : Item("This is the file 'omega'.\n"),
13840    })
13841  expected_skip = wc.State(A_COPY_path, { })
13842  svntest.actions.run_and_verify_merge(A_COPY_path, None, None,
13843                                       sbox.repo_url + '/A', None,
13844                                       expected_output,
13845                                       expected_mergeinfo_output,
13846                                       expected_elision_output,
13847                                       expected_disk,
13848                                       expected_status,
13849                                       expected_skip,
13850                                       check_props=True)
13851
13852#----------------------------------------------------------------------
13853# Test for issue #3432 'Merge can record mergeinfo from natural history
13854# gaps'.  See https://issues.apache.org/jira/browse/SVN-3432
13855@Issue(3432)
13856@SkipUnless(server_has_mergeinfo)
13857def handle_gaps_in_implicit_mergeinfo(sbox):
13858  "correctly consider natural history gaps"
13859
13860  wc_dir = sbox.wc_dir
13861
13862  # Create a branch with gaps in its natural history.
13863  #
13864  # r1--------r2--------r3--------r4--------r5--------r6
13865  # Add 'A'   edit      edit      edit      edit      Delete A
13866  #           psi       rho       beta      omega
13867  #           |
13868  #           V
13869  #           r7--------r9----------------->
13870  #           Rez 'A'   edit
13871  #           |         gamma
13872  #           |
13873  #           V
13874  #           r8--------------------------->
13875  #           Copy 'A@7' to
13876  #           'A_COPY'
13877  #
13878  expected_disk, expected_status = set_up_natural_history_gap(sbox)
13879
13880  # Some paths we'll care about.
13881  A_COPY_path = sbox.ospath('A_COPY')
13882
13883  # Merge r4 to 'A_COPY' from A@4, which is *not* part of A_COPY's history.
13884  expected_output = wc.State(A_COPY_path, {
13885    'B/E/beta' : Item(status='U '),
13886    })
13887  expected_mergeinfo_output = wc.State(A_COPY_path, {
13888    '' : Item(status=' U'),
13889    })
13890  expected_elision_output = wc.State(A_COPY_path, {
13891    })
13892  expected_status = wc.State(A_COPY_path, {
13893    ''          : Item(status=' M'),
13894    'B'         : Item(status='  '),
13895    'mu'        : Item(status='  '),
13896    'B/E'       : Item(status='  '),
13897    'B/E/alpha' : Item(status='  '),
13898    'B/E/beta'  : Item(status='M '),
13899    'B/lambda'  : Item(status='  '),
13900    'B/F'       : Item(status='  '),
13901    'C'         : Item(status='  '),
13902    'D'         : Item(status='  '),
13903    'D/G'       : Item(status='  '),
13904    'D/G/pi'    : Item(status='  '),
13905    'D/G/rho'   : Item(status='  '),
13906    'D/G/tau'   : Item(status='  '),
13907    'D/gamma'   : Item(status='  '),
13908    'D/H'       : Item(status='  '),
13909    'D/H/chi'   : Item(status='  '),
13910    'D/H/psi'   : Item(status='  '),
13911    'D/H/omega' : Item(status='  '),
13912    })
13913  expected_status.tweak(wc_rev=9)
13914  expected_disk = wc.State('', {
13915    ''          : Item(props={SVN_PROP_MERGEINFO : '/A:4'}),
13916    'B'         : Item(),
13917    'mu'        : Item("This is the file 'mu'.\n"),
13918    'B/E'       : Item(),
13919    'B/E/alpha' : Item("This is the file 'alpha'.\n"),
13920    'B/E/beta'  : Item("New content"), # From the merge of A@4
13921    'B/lambda'  : Item("This is the file 'lambda'.\n"),
13922    'B/F'       : Item(),
13923    'C'         : Item(),
13924    'D'         : Item(),
13925    'D/G'       : Item(),
13926    'D/G/pi'    : Item("This is the file 'pi'.\n"),
13927    'D/G/rho'   : Item("This is the file 'rho'.\n"),
13928    'D/G/tau'   : Item("This is the file 'tau'.\n"),
13929    'D/gamma'   : Item("This is the file 'gamma'.\n"),
13930    'D/H'       : Item(),
13931    'D/H/chi'   : Item("This is the file 'chi'.\n"),
13932    'D/H/psi'   : Item("New content"), # From A@2
13933    'D/H/omega' : Item("This is the file 'omega'.\n"),
13934    })
13935  expected_skip = wc.State(A_COPY_path, { })
13936  svntest.actions.run_and_verify_merge(A_COPY_path, 3, 4,
13937                                       sbox.repo_url + '/A@4', None,
13938                                       expected_output,
13939                                       expected_mergeinfo_output,
13940                                       expected_elision_output,
13941                                       expected_disk,
13942                                       expected_status,
13943                                       expected_skip,
13944                                       check_props=True)
13945
13946  # Now reverse merge -r9:2 from 'A@HEAD' to 'A_COPY'.  This should be
13947  # a no-op since the only operative change made on 'A@HEAD' between r2:9
13948  # is the text mod to 'A/D/gamma' made in r9, but since that was after
13949  # 'A_COPY' was copied from 'A 'and that change was never merged, we don't
13950  # try to reverse merge it.
13951  #
13952  # Also, the mergeinfo recorded by the previous merge, i.e. '/A:4', should
13953  # *not* be removed!  A@4 is not on the same line of history as 'A@9'.
13954  expected_output = wc.State(A_COPY_path, {})
13955  expected_mergeinfo_output = wc.State(A_COPY_path, {
13956    '' : Item(status=' G'),
13957    })
13958  svntest.actions.run_and_verify_merge(A_COPY_path, 9, 2,
13959                                       sbox.repo_url + '/A', None,
13960                                       expected_output,
13961                                       expected_mergeinfo_output,
13962                                       expected_elision_output,
13963                                       expected_disk,
13964                                       expected_status,
13965                                       expected_skip,
13966                                       check_props=True)
13967
13968  # Now merge all available revisions from 'A' to 'A_COPY'.
13969  # The mergeinfo '/A:4' on 'A_COPY' should have no impact on this merge
13970  # since it refers to another line of history.  Since 'A_COPY' was copied
13971  # from 'A@7' the only available revisions are r8 and r9.
13972  expected_output = wc.State(A_COPY_path, {
13973    'D/gamma' : Item(status='U '),
13974    })
13975  expected_status.tweak('D/gamma', status='M ')
13976  expected_disk.tweak('D/gamma', contents='New content')
13977  expected_disk.tweak('', props={SVN_PROP_MERGEINFO : '/A:4,8-9'})
13978  svntest.actions.run_and_verify_merge(A_COPY_path, None, None,
13979                                       sbox.repo_url + '/A', None,
13980                                       expected_output,
13981                                       expected_mergeinfo_output,
13982                                       expected_elision_output,
13983                                       expected_disk,
13984                                       expected_status,
13985                                       expected_skip,
13986                                       check_props=True)
13987
13988#----------------------------------------------------------------------
13989# Test for issue #3323 'Mergeinfo deleted by a merge should disappear'
13990@Issue(3323)
13991@SkipUnless(server_has_mergeinfo)
13992def mergeinfo_deleted_by_a_merge_should_disappear(sbox):
13993  "mergeinfo deleted by a merge should disappear"
13994
13995
13996  # r1: Create a greek tree.
13997  sbox.build()
13998  wc_dir = sbox.wc_dir
13999
14000  # Some paths we'll care about
14001  D_COPY_path   = sbox.ospath('A_COPY/D')
14002  A_COPY_path   = sbox.ospath('A_COPY')
14003  A_COPY_2_path = sbox.ospath('A_COPY_2')
14004
14005  # r2 - r6: Copy A to A_COPY and then make some text changes under A.
14006  wc_disk, wc_status = set_up_branch(sbox)
14007
14008  # r7: Merge all available revisions from A/D to A_COPY/D, this creates
14009  #     mergeinfo on A_COPY/D.
14010  svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir)
14011  svntest.actions.run_and_verify_svn(None, # Don't check stdout, we test this
14012                                           # type of merge to death elsewhere.
14013                                     [], 'merge', sbox.repo_url + '/A/D',
14014                                     D_COPY_path)
14015  svntest.actions.run_and_verify_svn(
14016    None, [], 'ci', '-m',
14017    'Merge all available revisions from A/D to A_COPY/D', wc_dir)
14018
14019  # r8: Copy A_COPY to A_COPY_2, this carries the mergeinf on A_COPY/D
14020  #     to A_COPY_2/D.
14021  svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir)
14022  svntest.actions.run_and_verify_svn(None,[],
14023                                     'copy', A_COPY_path, A_COPY_2_path)
14024  svntest.actions.run_and_verify_svn(None, [], 'ci', '-m',
14025                                     'Copy A_COPY to A_COPY_2', wc_dir)
14026
14027  # r9: Propdel the mergeinfo on A_COPY/D.
14028  svntest.actions.run_and_verify_svn(None,[],
14029                                     'pd', SVN_PROP_MERGEINFO, D_COPY_path)
14030  svntest.actions.run_and_verify_svn(None, [], 'ci', '-m',
14031                                     'Propdel the mergeinfo on A_COPY/D',
14032                                     wc_dir)
14033
14034  # r10: Merge r5 from A to A_COPY_2 so the latter gets some explicit
14035  #      mergeinfo.
14036  svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir)
14037  svntest.actions.run_and_verify_svn(None, [], 'merge', '-c5',
14038                                     sbox.repo_url + '/A', A_COPY_2_path)
14039  svntest.actions.run_and_verify_svn(None, [], 'ci', '-m',
14040                                     'Merge r5 from A to A_COPY_2', wc_dir)
14041
14042  # Now merge r9 from A_COPY to A_COPY_2.  Since the merge itself cleanly
14043  # removes all explicit mergeinfo from A_COPY_2/D, we should not set any
14044  # mergeinfo on that subtree describing the merge.
14045  svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir)
14046  expected_output = wc.State(A_COPY_2_path, {
14047    'D' : Item(status=' U'),
14048    })
14049  expected_mergeinfo_output = wc.State(A_COPY_2_path, {
14050    ''  : Item(status=' U'),
14051    })
14052  expected_elision_output = wc.State(A_COPY_2_path, {
14053    })
14054  expected_status = wc.State(A_COPY_2_path, {
14055    ''          : Item(status=' M'),
14056    'B'         : Item(status='  '),
14057    'mu'        : Item(status='  '),
14058    'B/E'       : Item(status='  '),
14059    'B/E/alpha' : Item(status='  '),
14060    'B/E/beta'  : Item(status='  '),
14061    'B/lambda'  : Item(status='  '),
14062    'B/F'       : Item(status='  '),
14063    'C'         : Item(status='  '),
14064    'D'         : Item(status=' M'),
14065    'D/G'       : Item(status='  '),
14066    'D/G/pi'    : Item(status='  '),
14067    'D/G/rho'   : Item(status='  '),
14068    'D/G/tau'   : Item(status='  '),
14069    'D/gamma'   : Item(status='  '),
14070    'D/H'       : Item(status='  '),
14071    'D/H/chi'   : Item(status='  '),
14072    'D/H/psi'   : Item(status='  '),
14073    'D/H/omega' : Item(status='  '),
14074    })
14075  expected_status.tweak(wc_rev=10)
14076  expected_disk = wc.State('', {
14077    ''          : Item(props={SVN_PROP_MERGEINFO : '/A:5\n/A_COPY:9'}),
14078    'B'         : Item(),
14079    'mu'        : Item("This is the file 'mu'.\n"),
14080    'B/E'       : Item(),
14081    'B/E/alpha' : Item("This is the file 'alpha'.\n"),
14082    'B/E/beta'  : Item("New content"),
14083    'B/lambda'  : Item("This is the file 'lambda'.\n"),
14084    'B/F'       : Item(),
14085    'C'         : Item(),
14086    'D'         : Item(),
14087    'D/G'       : Item(),
14088    'D/G/pi'    : Item("This is the file 'pi'.\n"),
14089    'D/G/rho'   : Item("New content"),
14090    'D/G/tau'   : Item("This is the file 'tau'.\n"),
14091    'D/gamma'   : Item("This is the file 'gamma'.\n"),
14092    'D/H'       : Item(),
14093    'D/H/chi'   : Item("This is the file 'chi'.\n"),
14094    'D/H/psi'   : Item("New content"),
14095    'D/H/omega' : Item("New content"),
14096    })
14097  expected_skip = wc.State(A_COPY_path, { })
14098  svntest.actions.run_and_verify_merge(A_COPY_2_path, '8', '9',
14099                                       sbox.repo_url + '/A_COPY', None,
14100                                       expected_output,
14101                                       expected_mergeinfo_output,
14102                                       expected_elision_output,
14103                                       expected_disk,
14104                                       expected_status,
14105                                       expected_skip,
14106                                       check_props=True)
14107
14108#----------------------------------------------------------------------
14109# File merge optimization caused segfault during noop file merge
14110# when multiple ranges are eligible for merge, see
14111# http://svn.haxx.se/dev/archive-2009-05/0363.shtml
14112@SkipUnless(server_has_mergeinfo)
14113def noop_file_merge(sbox):
14114  "noop file merge does not segfault"
14115
14116  # r1: Create a greek tree.
14117  sbox.build()
14118  wc_dir = sbox.wc_dir
14119
14120  # Some paths we'll care about
14121  A_COPY_path    = sbox.ospath('A_COPY')
14122  beta_COPY_path = sbox.ospath('A_COPY/B/E/beta')
14123  chi_COPY_path  = sbox.ospath('A_COPY/D/H/chi')
14124
14125  # r2 - r6: Copy A to A_COPY and then make some text changes under A.
14126  wc_disk, wc_status = set_up_branch(sbox)
14127
14128  # Merge r5 from A to A_COPY and commit as r7.  This will split the
14129  # eligible ranges to be merged to A_COPY/D/H/chi into two discrete
14130  # sets: r1-4 and r5-HEAD
14131  svntest.actions.run_and_verify_svn(
14132    expected_merge_output([[5]],
14133                          ['U    ' + beta_COPY_path + '\n',
14134                           ' U   ' + A_COPY_path    + '\n',]),
14135    [], 'merge', '-c5', sbox.repo_url + '/A', A_COPY_path)
14136  svntest.actions.run_and_verify_svn(None, [], 'commit', '-m',
14137                                     'Merge r5 from A to A_COPY',
14138                                     wc_dir)
14139
14140  # Update working copy to allow full inheritance and elision.
14141  svntest.actions.run_and_verify_svn(exp_noop_up_out(7), [],
14142                                     'up', wc_dir)
14143
14144  # Merge all available revisions from A/D/H/chi to A_COPY/D/H/chi.
14145  # There are no operative changes in the source, so this should
14146  # not produce any output other than mergeinfo updates on
14147  # A_COPY/D/H/chi.  This is where the segfault occurred.
14148  svntest.actions.run_and_verify_svn(None, [], 'merge',
14149                                     sbox.repo_url + '/A/D/H/chi',
14150                                     chi_COPY_path)
14151  svntest.actions.run_and_verify_svn([' M      ' + chi_COPY_path + '\n'],
14152                                     [], 'st', chi_COPY_path)
14153  svntest.actions.run_and_verify_svn(['/A/D/H/chi:2-7\n'],
14154                                     [], 'pg', SVN_PROP_MERGEINFO,
14155                                     chi_COPY_path)
14156
14157#----------------------------------------------------------------------
14158@SkipUnless(server_has_mergeinfo)
14159@Issue(2690)
14160def copy_then_replace_via_merge(sbox):
14161  "copy then replace via merge"
14162  # Testing issue #2690 with deleted/added/replaced files and subdirs.
14163
14164  sbox.build()
14165  wc_dir = sbox.wc_dir
14166  j = os.path.join
14167
14168  A = j(wc_dir, 'A')
14169  AJ = j(wc_dir, 'A', 'J')
14170  AJK = j(AJ, 'K')
14171  AJL = j(AJ, 'L')
14172  AJM = j(AJ, 'M')
14173  AJ_sigma = j(AJ, 'sigma')
14174  AJ_theta = j(AJ, 'theta')
14175  AJ_omega = j(AJ, 'omega')
14176  AJK_zeta = j(AJK, 'zeta')
14177  AJL_zeta = j(AJL, 'zeta')
14178  AJM_zeta = j(AJM, 'zeta')
14179  branch = j(wc_dir, 'branch')
14180  branch_J = j(wc_dir, 'branch', 'J')
14181  url_A = sbox.repo_url + '/A'
14182  url_branch = sbox.repo_url + '/branch'
14183
14184  # Create a branch.
14185  main.run_svn(None, 'cp', url_A, url_branch, '-m', 'create branch') # r2
14186
14187  # Create a tree J in A.
14188  os.makedirs(AJK)
14189  os.makedirs(AJL)
14190  main.file_append(AJ_sigma, 'new text')
14191  main.file_append(AJ_theta, 'new text')
14192  main.file_append(AJK_zeta, 'new text')
14193  main.file_append(AJL_zeta, 'new text')
14194  main.run_svn(None, 'add', AJ)
14195  sbox.simple_commit(message='create tree J') # r3
14196  main.run_svn(None, 'up', wc_dir)
14197
14198  # Copy J to the branch via merge
14199  main.run_svn(None, 'merge', url_A, branch)
14200  sbox.simple_commit(message='merge to branch') # r4
14201  main.run_svn(None, 'up', wc_dir)
14202
14203  # In A, replace J with a slightly different tree
14204  main.run_svn(None, 'rm', AJ)
14205  sbox.simple_commit(message='rm AJ') # r5
14206  main.run_svn(None, 'up', wc_dir)
14207
14208  os.makedirs(AJL)
14209  os.makedirs(AJM)
14210  main.file_append(AJ_theta, 'really new text')
14211  main.file_append(AJ_omega, 'really new text')
14212  main.file_append(AJL_zeta, 'really new text')
14213  main.file_append(AJM_zeta, 'really new text')
14214  main.run_svn(None, 'add', AJ)
14215  sbox.simple_commit(message='create tree J again') # r6
14216  main.run_svn(None, 'up', wc_dir)
14217
14218  # Run merge to replace /branch/J in one swell foop.
14219  main.run_svn(None, 'merge', url_A, branch)
14220
14221  # Check status:
14222  #   sigma and K are deleted (not copied!)
14223  #   theta and L are replaced (deleted then copied-here)
14224  #   omega and M are copied-here
14225  expected_status = wc.State(branch_J, {
14226    ''          : Item(status='R ', copied='+', wc_rev='-'),
14227    'sigma'     : Item(status='D ', wc_rev=6),
14228    'K'         : Item(status='D ', wc_rev=6),
14229    'K/zeta'    : Item(status='D ', wc_rev=6),
14230    'theta'     : Item(status='  ', copied='+', wc_rev='-'),
14231    'L'         : Item(status='  ', copied='+', wc_rev='-'),
14232    'L/zeta'    : Item(status='  ', copied='+', wc_rev='-'),
14233    'omega'     : Item(status='  ', copied='+', wc_rev='-'),
14234    'M'         : Item(status='  ', copied='+', wc_rev='-'),
14235    'M/zeta'    : Item(status='  ', copied='+', wc_rev='-'),
14236    })
14237  actions.run_and_verify_status(branch_J, expected_status)
14238
14239  # Update and commit, just to make sure the WC isn't busted.
14240  main.run_svn(None, 'up', branch_J)
14241  expected_output = wc.State(branch_J, {
14242    ''          : Item(verb='Replacing'),
14243    })
14244  expected_status = wc.State(branch_J, {
14245    ''          : Item(status='  ', wc_rev=7),
14246    'theta'     : Item(status='  ', wc_rev=7),
14247    'L'         : Item(status='  ', wc_rev=7),
14248    'L/zeta'    : Item(status='  ', wc_rev=7),
14249    'omega'     : Item(status='  ', wc_rev=7),
14250    'M'         : Item(status='  ', wc_rev=7),
14251    'M/zeta'    : Item(status='  ', wc_rev=7),
14252    })
14253  actions.run_and_verify_commit(branch_J,
14254                                expected_output,
14255                                expected_status)
14256
14257#----------------------------------------------------------------------
14258@SkipUnless(server_has_mergeinfo)
14259def record_only_merge(sbox):
14260  "record only merge applies mergeinfo diffs"
14261
14262  sbox.build()
14263  wc_dir = sbox.wc_dir
14264  wc_disk, wc_status = set_up_branch(sbox)
14265
14266  # Some paths we'll care about
14267  nu_path         = sbox.ospath('A/C/nu')
14268  A_COPY_path     = sbox.ospath('A_COPY')
14269  A2_path         = sbox.ospath('A2')
14270  Z_path          = sbox.ospath('A/B/Z')
14271  Z_COPY_path     = sbox.ospath('A_COPY/B/Z')
14272  rho_COPY_path   = sbox.ospath('A_COPY/D/G/rho')
14273  omega_COPY_path = sbox.ospath('A_COPY/D/H/omega')
14274  H_COPY_path     = sbox.ospath('A_COPY/D/H')
14275  nu_COPY_path    = sbox.ospath('A_COPY/C/nu')
14276
14277  # r7 - Copy the branch A_COPY@2 to A2 and update the WC.
14278  svntest.actions.run_and_verify_svn(None, [],
14279                                     'copy', A_COPY_path, A2_path)
14280  svntest.actions.run_and_verify_svn(None, [],
14281                                     'commit', '-m', 'Branch the branch',
14282                                     wc_dir)
14283  # r8 - Add A/C/nu and A/B/Z.
14284  # Add a new file with mergeinfo in the foreign repos.
14285  svntest.main.file_write(nu_path, "This is the file 'nu'.\n")
14286  svntest.actions.run_and_verify_svn(None, [], 'add', nu_path)
14287  svntest.actions.run_and_verify_svn(None, [], 'mkdir', Z_path)
14288  svntest.actions.run_and_verify_svn(None, [],
14289                                     'commit', '-m', 'Add subtrees',
14290                                     wc_dir)
14291
14292  # r9 - Edit A/C/nu and add a random property on A/B/Z.
14293  svntest.main.file_write(nu_path, "New content.\n")
14294  svntest.actions.run_and_verify_svn(None, [],
14295                                     'ps', 'propname', 'propval', Z_path)
14296  svntest.actions.run_and_verify_svn(None, [],
14297                                     'commit', '-m', 'Subtree changes',
14298                                     wc_dir)
14299
14300  # r10 - Merge r8 from A to A_COPY.
14301  svntest.actions.run_and_verify_svn(exp_noop_up_out(9), [], 'up',
14302                                     wc_dir)
14303  svntest.actions.run_and_verify_svn(expected_merge_output(
14304                                       [[8]],
14305                                       ['A    ' + Z_COPY_path + '\n',
14306                                        'A    ' + nu_COPY_path + '\n',
14307                                        ' U   ' + A_COPY_path + '\n',]),
14308                                     [], 'merge', '-c8',
14309                                     sbox.repo_url + '/A',
14310                                     A_COPY_path)
14311  svntest.actions.run_and_verify_svn(None, [],
14312                                     'commit', '-m', 'Root merge of r8',
14313                                     wc_dir)
14314
14315  # r11 - Do several subtree merges:
14316  #
14317  #   r4 from A/D/G/rho to A_COPY/D/G/rho
14318  #   r6 from A/D/H to A_COPY/D/H
14319  #   r9 from A/C/nu to A_COPY/C/nu
14320  #   r9 from A/B/Z to A_COPY/B/Z
14321  svntest.actions.run_and_verify_svn(expected_merge_output(
14322                                       [[4]],
14323                                       ['U    ' + rho_COPY_path + '\n',
14324                                        ' U   ' + rho_COPY_path + '\n',]),
14325                                     [], 'merge', '-c4',
14326                                     sbox.repo_url + '/A/D/G/rho',
14327                                     rho_COPY_path)
14328  svntest.actions.run_and_verify_svn(
14329    expected_merge_output([[6]],
14330                          ['U    ' + omega_COPY_path + '\n',
14331                           ' U   ' + H_COPY_path + '\n',]),
14332    [], 'merge', '-c6', sbox.repo_url + '/A/D/H', H_COPY_path)
14333  svntest.actions.run_and_verify_svn(expected_merge_output(
14334                                       [[9]],
14335                                       ['U    ' + nu_COPY_path + '\n',
14336                                        ' G   ' + nu_COPY_path + '\n',]),
14337                                     [], 'merge', '-c9',
14338                                     sbox.repo_url + '/A/C/nu',
14339                                     nu_COPY_path)
14340  svntest.actions.run_and_verify_svn(expected_merge_output(
14341                                       [[9]],
14342                                       [' U   ' + Z_COPY_path + '\n',
14343                                        ' G   ' + Z_COPY_path + '\n']),
14344                                     [], 'merge', '-c9',
14345                                     sbox.repo_url + '/A/B/Z',
14346                                     Z_COPY_path)
14347  svntest.actions.run_and_verify_svn(None, [],
14348                                     'commit', '-m', 'Several subtree merges',
14349                                     wc_dir)
14350
14351  svntest.actions.run_and_verify_svn(exp_noop_up_out(11), [], 'up',
14352                                     wc_dir)
14353
14354  # Now do a --record-only merge of r10 and r11 from A_COPY to A2.
14355  #
14356  # We only expect svn:mergeinfo changes to be applied to existing paths:
14357  #
14358  # From r10 the mergeinfo '/A:r8' is recorded on A_COPY.
14359  #
14360  # From r11 the mergeinfo of '/A/D/G/rho:r4' is recorded on A_COPY/D/G/rho
14361  # and the mergeinfo of '/A/D/H:r6' is recorded on A_COPY/D/H.  Rev 8 should
14362  # also be recorded on both subtrees because explicit mergeinfo must be
14363  # complete.
14364  #
14365  # The mergeinfo describing the merge source itself, '/A_COPY:10-11' should
14366  # also be recorded on the root and the two subtrees.
14367  #
14368  # The mergeinfo changes from r10 to A_COPY/C/nu and A_COPY/B/Z cannot be
14369  # applied because the corresponding paths don't exist under A2; this should
14370  # not cause any problems.
14371  expected_output = wc.State(A2_path, {
14372    ''        : Item(status=' U'),
14373    'D/G/rho' : Item(status=' U'),
14374    'D/H'     : Item(status=' U'),
14375    })
14376  expected_mergeinfo_output = wc.State(A2_path, {
14377    ''        : Item(status=' G'),
14378    'D/H'     : Item(status=' G'),
14379    'D/G/rho' : Item(status=' G'),
14380    })
14381  expected_elision_output = wc.State(A2_path, {
14382    })
14383  expected_disk = wc.State('', {
14384    ''          : Item(props={SVN_PROP_MERGEINFO : '/A:8\n/A_COPY:10-11'}),
14385    'mu'        : Item("This is the file 'mu'.\n"),
14386    'B'         : Item(),
14387    'B/lambda'  : Item("This is the file 'lambda'.\n"),
14388    'B/E'       : Item(),
14389    'B/E/alpha' : Item("This is the file 'alpha'.\n"),
14390    'B/E/beta'  : Item("This is the file 'beta'.\n"),
14391    'B/F'       : Item(),
14392    'C'         : Item(),
14393    'D'         : Item(),
14394    'D/gamma'   : Item("This is the file 'gamma'.\n"),
14395    'D/H'       : Item(props={SVN_PROP_MERGEINFO :
14396                              '/A/D/H:6,8\n/A_COPY/D/H:10-11'}),
14397    'D/H/chi'   : Item("This is the file 'chi'.\n"),
14398    'D/H/psi'   : Item("This is the file 'psi'.\n"),
14399    'D/H/omega' : Item("This is the file 'omega'.\n"),
14400    'D/G'       : Item(),
14401    'D/G/pi'    : Item("This is the file 'pi'.\n"),
14402    'D/G/rho'   : Item("This is the file 'rho'.\n",
14403                       props={SVN_PROP_MERGEINFO :
14404                              '/A/D/G/rho:4,8\n/A_COPY/D/G/rho:10-11'}),
14405    'D/G/tau'   : Item("This is the file 'tau'.\n"),
14406    })
14407  expected_status = wc.State(A2_path, {
14408    ''          : Item(status=' M'),
14409    'mu'        : Item(status='  '),
14410    'B'         : Item(status='  '),
14411    'B/lambda'  : Item(status='  '),
14412    'B/E'       : Item(status='  '),
14413    'B/E/alpha' : Item(status='  '),
14414    'B/E/beta'  : Item(status='  '),
14415    'B/F'       : Item(status='  '),
14416    'C'         : Item(status='  '),
14417    'D'         : Item(status='  '),
14418    'D/gamma'   : Item(status='  '),
14419    'D/H'       : Item(status=' M'),
14420    'D/H/chi'   : Item(status='  '),
14421    'D/H/psi'   : Item(status='  '),
14422    'D/H/omega' : Item(status='  '),
14423    'D/G'       : Item(status='  '),
14424    'D/G/pi'    : Item(status='  '),
14425    'D/G/rho'   : Item(status=' M'),
14426    'D/G/tau'   : Item(status='  '),
14427    })
14428  expected_status.tweak(wc_rev=11)
14429  expected_skip = wc.State('', { })
14430  svntest.actions.run_and_verify_merge(A2_path, '9', '11',
14431                                       sbox.repo_url + '/A_COPY', None,
14432                                       expected_output,
14433                                       expected_mergeinfo_output,
14434                                       expected_elision_output,
14435                                       expected_disk,
14436                                       expected_status,
14437                                       expected_skip,
14438                                       [], True, False,
14439                                       '--record-only', A2_path)
14440
14441#----------------------------------------------------------------------
14442# Test for issue #3514 'svn merge --accept [ base | theirs-full ]
14443# doesn't work'
14444@Issue(3514)
14445def merge_automatic_conflict_resolution(sbox):
14446  "automatic conflict resolutions work with merge"
14447
14448  sbox.build()
14449  wc_dir = sbox.wc_dir
14450  wc_disk, wc_status = set_up_branch(sbox)
14451
14452
14453  # Some paths we'll care about
14454  A_COPY_path   = sbox.ospath('A_COPY')
14455  psi_COPY_path = sbox.ospath('A_COPY/D/H/psi')
14456
14457  # r7 - Make a change on A_COPY that will conflict with r3 on A
14458  svntest.main.file_write(psi_COPY_path, "BASE.\n")
14459  svntest.actions.run_and_verify_svn(None, [],
14460                                     'commit', '-m', 'log msg', wc_dir)
14461
14462  # Set up our base expectations, we'll tweak accordingly for each option.
14463  expected_status = wc.State(A_COPY_path, {
14464    ''          : Item(status=' M', wc_rev=2),
14465    'B'         : Item(status='  ', wc_rev=2),
14466    'mu'        : Item(status='  ', wc_rev=2),
14467    'B/E'       : Item(status='  ', wc_rev=2),
14468    'B/E/alpha' : Item(status='  ', wc_rev=2),
14469    'B/E/beta'  : Item(status='  ', wc_rev=2),
14470    'B/lambda'  : Item(status='  ', wc_rev=2),
14471    'B/F'       : Item(status='  ', wc_rev=2),
14472    'C'         : Item(status='  ', wc_rev=2),
14473    'D'         : Item(status='  ', wc_rev=2),
14474    'D/G'       : Item(status='  ', wc_rev=2),
14475    'D/G/pi'    : Item(status='  ', wc_rev=2),
14476    'D/G/rho'   : Item(status='  ', wc_rev=2),
14477    'D/G/tau'   : Item(status='  ', wc_rev=2),
14478    'D/gamma'   : Item(status='  ', wc_rev=2),
14479    'D/H'       : Item(status='  ', wc_rev=2),
14480    'D/H/chi'   : Item(status='  ', wc_rev=2),
14481    'D/H/psi'   : Item(status='  ', wc_rev=7),
14482    'D/H/omega' : Item(status='  ', wc_rev=2),
14483    })
14484  expected_disk = wc.State('', {
14485    ''          : Item(props={SVN_PROP_MERGEINFO : '/A:3'}),
14486    'B'         : Item(),
14487    'mu'        : Item("This is the file 'mu'.\n"),
14488    'B/E'       : Item(),
14489    'B/E/alpha' : Item("This is the file 'alpha'.\n"),
14490    'B/E/beta'  : Item("This is the file 'beta'.\n"),
14491    'B/lambda'  : Item("This is the file 'lambda'.\n"),
14492    'B/F'       : Item(),
14493    'C'         : Item(),
14494    'D'         : Item(),
14495    'D/G'       : Item(),
14496    'D/G/pi'    : Item("This is the file 'pi'.\n"),
14497    'D/G/rho'   : Item("This is the file 'rho'.\n"),
14498    'D/G/tau'   : Item("This is the file 'tau'.\n"),
14499    'D/gamma'   : Item("This is the file 'gamma'.\n"),
14500    'D/H'       : Item(),
14501    'D/H/chi'   : Item("This is the file 'chi'.\n"),
14502    'D/H/psi'   : Item("This is the file 'psi'.\n"),
14503    'D/H/omega' : Item("This is the file 'omega'.\n"),
14504    })
14505  expected_skip = wc.State(A_COPY_path, { })
14506
14507  # Test --accept postpone
14508  expected_output = wc.State(A_COPY_path, {'D/H/psi' : Item(status='C ')})
14509  expected_mergeinfo_output = wc.State(A_COPY_path, {
14510    '' : Item(status=' U'),
14511    })
14512  expected_elision_output = wc.State(A_COPY_path, {
14513    })
14514  expected_disk.tweak('D/H/psi', contents="<<<<<<< .working\n"
14515                      "BASE.\n"
14516                      "||||||| .merge-left.r2\n"
14517                      "This is the file 'psi'.\n"
14518                      "=======\n"
14519                      "New content>>>>>>> .merge-right.r3\n")
14520  expected_status.tweak('D/H/psi', status='C ')
14521  psi_conflict_support_files = ["psi\.working",
14522                                "psi\.merge-right\.r3",
14523                                "psi\.merge-left\.r2"]
14524  svntest.actions.run_and_verify_merge(A_COPY_path, '2', '3',
14525                                       sbox.repo_url + '/A', None,
14526                                       expected_output,
14527                                       expected_mergeinfo_output,
14528                                       expected_elision_output,
14529                                       expected_disk,
14530                                       expected_status,
14531                                       expected_skip,
14532                                       [], True, True,
14533                                       '--accept', 'postpone',
14534                                       '--allow-mixed-revisions',
14535                                       A_COPY_path,
14536                                       extra_files=
14537                                        list(psi_conflict_support_files))
14538  svntest.actions.run_and_verify_svn(None, [],
14539                                     'revert', '--recursive', wc_dir)
14540
14541  # Test --accept mine-conflict and mine-full
14542  ### TODO: Also test that the output has a 'Resolved' line for this path.
14543  expected_output = wc.State(A_COPY_path, {'D/H/psi' : Item(status='C ')})
14544  expected_disk.tweak('D/H/psi', contents="BASE.\n")
14545  expected_status.tweak('D/H/psi', status='  ')
14546  svntest.actions.run_and_verify_merge(A_COPY_path, '2', '3',
14547                                       sbox.repo_url + '/A', None,
14548                                       expected_output,
14549                                       expected_mergeinfo_output,
14550                                       expected_elision_output,
14551                                       expected_disk,
14552                                       expected_status,
14553                                       expected_skip,
14554                                       [], True, False,
14555                                       '--accept', 'mine-conflict',
14556                                       '--allow-mixed-revisions',
14557                                       A_COPY_path)
14558  svntest.actions.run_and_verify_svn(None, [],
14559                                     'revert', '--recursive', wc_dir)
14560  svntest.actions.run_and_verify_merge(A_COPY_path, '2', '3',
14561                                       sbox.repo_url + '/A', None,
14562                                       expected_output,
14563                                       expected_mergeinfo_output,
14564                                       expected_elision_output,
14565                                       expected_disk,
14566                                       expected_status,
14567                                       expected_skip,
14568                                       [], True, False,
14569                                       '--accept', 'mine-full',
14570                                       '--allow-mixed-revisions',
14571                                       A_COPY_path)
14572  svntest.actions.run_and_verify_svn(None, [],
14573                                     'revert', '--recursive', wc_dir)
14574
14575  # Test --accept theirs-conflict and theirs-full
14576  ### TODO: Also test that the output has a 'Resolved' line for this path.
14577  expected_output = wc.State(A_COPY_path, {'D/H/psi' : Item(status='C ')})
14578  expected_disk.tweak('D/H/psi', contents="New content")
14579  expected_status.tweak('D/H/psi', status='M ')
14580  svntest.actions.run_and_verify_merge(A_COPY_path, '2', '3',
14581                                       sbox.repo_url + '/A', None,
14582                                       expected_output,
14583                                       expected_mergeinfo_output,
14584                                       expected_elision_output,
14585                                       expected_disk,
14586                                       expected_status,
14587                                       expected_skip,
14588                                       [], True, False,
14589                                       '--accept', 'theirs-conflict',
14590                                       '--allow-mixed-revisions',
14591                                       A_COPY_path)
14592  svntest.actions.run_and_verify_svn(None, [],
14593                                     'revert', '--recursive', wc_dir)
14594  svntest.actions.run_and_verify_merge(A_COPY_path, '2', '3',
14595                                       sbox.repo_url + '/A', None,
14596                                       expected_output,
14597                                       expected_mergeinfo_output,
14598                                       expected_elision_output,
14599                                       expected_disk,
14600                                       expected_status,
14601                                       expected_skip,
14602                                       [], True, False,
14603                                       '--accept', 'theirs-full',
14604                                       '--allow-mixed-revisions',
14605                                       A_COPY_path)
14606  svntest.actions.run_and_verify_svn(None, [],
14607                                     'revert', '--recursive', wc_dir)
14608  # Test --accept base
14609  ### TODO: Also test that the output has a 'Resolved' line for this path.
14610  expected_output = wc.State(A_COPY_path, {'D/H/psi' : Item(status='C ')})
14611  expected_elision_output = wc.State(A_COPY_path, {
14612    })
14613  expected_disk.tweak('D/H/psi', contents="This is the file 'psi'.\n")
14614  expected_status.tweak('D/H/psi', status='M ')
14615  svntest.actions.run_and_verify_merge(A_COPY_path, '2', '3',
14616                                       sbox.repo_url + '/A', None,
14617                                       expected_output,
14618                                       expected_mergeinfo_output,
14619                                       expected_elision_output,
14620                                       expected_disk,
14621                                       expected_status,
14622                                       expected_skip,
14623                                       [], True, False,
14624                                       '--accept', 'base',
14625                                       '--allow-mixed-revisions',
14626                                       A_COPY_path)
14627
14628#----------------------------------------------------------------------
14629# Test for issue #3440 'Skipped paths get incorrect override mergeinfo
14630# during merge'.
14631@SkipUnless(server_has_mergeinfo)
14632@Issue(3440)
14633def skipped_files_get_correct_mergeinfo(sbox):
14634  "skipped files get correct mergeinfo set"
14635
14636  sbox.build()
14637  wc_dir = sbox.wc_dir
14638
14639  # Some paths we'll care about
14640  A_COPY_path   = sbox.ospath('A_COPY')
14641  H_COPY_path   = sbox.ospath('A_COPY/D/H')
14642  psi_COPY_path = sbox.ospath('A_COPY/D/H/psi')
14643  psi_path      = sbox.ospath('A/D/H/psi')
14644
14645  # Setup our basic 'trunk' and 'branch':
14646  # r2 - Copy A to A_COPY
14647  # r3 - Text change to A/D/H/psi
14648  # r4 - Text change to A/D/G/rho
14649  # r5 - Text change to A/B/E/beta
14650  # r6 - Text change to A/D/H/omega
14651  wc_disk, wc_status = set_up_branch(sbox, False, 1)
14652
14653  # r7 Make another text change to A/D/H/psi
14654  svntest.main.file_write(psi_path, "Even newer content")
14655  expected_output = wc.State(wc_dir, {'A/D/H/psi' : Item(verb='Sending')})
14656  svntest.main.run_svn(None, 'commit', '-m', 'another change to A/D/H/psi',
14657                       wc_dir)
14658
14659  # Merge r3 from A to A_COPY, this will create explicit mergeinfo of
14660  # '/A:3' on A_COPY.  Commit this merge as r8.
14661  svntest.actions.run_and_verify_svn(
14662    expected_merge_output([[3]],
14663                          ['U    ' + psi_COPY_path + '\n',
14664                           ' U   ' + A_COPY_path + '\n',]),
14665    [], 'merge', '-c3', sbox.repo_url + '/A', A_COPY_path)
14666  svntest.main.run_svn(None, 'commit', '-m', 'initial merge', wc_dir)
14667
14668  # Update WC to uniform revision and then set the depth on A_COPY/D/H to
14669  # empty.  Then merge all available revisions from A to A_COPY.
14670  # A_COPY/D/H/psi and A_COPY/D/H/omega are not present due to their
14671  # parent's depth and should be reported as skipped.  A_COPY/D/H should
14672  # get explicit mergeinfo set on it reflecting what it previously inherited
14673  # from A_COPY after the first merge, i.e. '/A/D/H:3', plus non-inheritable
14674  # mergeinfo describing what was done during this merge,
14675  # i.e. '/A/D/H:2*,4-8*'.
14676  #
14677  # Issue #3440 occurred when empty mergeinfo was set on A_COPY/D/H, making
14678  # it appear that r3 was never merged.
14679  svntest.actions.run_and_verify_svn(exp_noop_up_out(8), [],
14680                                     'up', wc_dir)
14681  svntest.actions.run_and_verify_svn(None, [],
14682                                     'up', '--set-depth=empty', H_COPY_path)
14683  expected_status = wc.State(A_COPY_path, {
14684    ''          : Item(status=' M'),
14685    'B'         : Item(status='  '),
14686    'mu'        : Item(status='  '),
14687    'B/E'       : Item(status='  '),
14688    'B/E/alpha' : Item(status='  '),
14689    'B/E/beta'  : Item(status='M '),
14690    'B/lambda'  : Item(status='  '),
14691    'B/F'       : Item(status='  '),
14692    'C'         : Item(status='  '),
14693    'D'         : Item(status='  '),
14694    'D/G'       : Item(status='  '),
14695    'D/G/pi'    : Item(status='  '),
14696    'D/G/rho'   : Item(status='M '),
14697    'D/G/tau'   : Item(status='  '),
14698    'D/gamma'   : Item(status='  '),
14699    'D/H'       : Item(status=' M'),
14700    })
14701  expected_status.tweak(wc_rev=8)
14702  expected_disk = wc.State('', {
14703    ''          : Item(props={SVN_PROP_MERGEINFO : '/A:2-8'}),
14704    'B'         : Item(),
14705    'mu'        : Item("This is the file 'mu'.\n"),
14706    'B/E'       : Item(),
14707    'B/E/alpha' : Item("This is the file 'alpha'.\n"),
14708    'B/E/beta'  : Item("New content"),
14709    'B/lambda'  : Item("This is the file 'lambda'.\n"),
14710    'B/F'       : Item(),
14711    'C'         : Item(),
14712    'D'         : Item(),
14713    'D/G'       : Item(),
14714    'D/G/pi'    : Item("This is the file 'pi'.\n"),
14715    'D/G/rho'   : Item("New content"),
14716    'D/G/tau'   : Item("This is the file 'tau'.\n"),
14717    'D/gamma'   : Item("This is the file 'gamma'.\n"),
14718    'D/H'       : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:2*,3,4-8*'}),
14719    })
14720  expected_skip = wc.State(
14721    A_COPY_path,
14722    {'D/H/psi'   : Item(verb='Skipped missing target'),
14723     'D/H/omega' : Item(verb='Skipped missing target')})
14724  expected_output = wc.State(A_COPY_path,
14725                             {'B/E/beta'  : Item(status='U '),
14726                              'D/G/rho'   : Item(status='U ')})
14727  expected_mergeinfo_output = wc.State(A_COPY_path, {
14728    ''    : Item(status=' U'),
14729    'D/H' : Item(status=' G'), # ' G' because override mergeinfo gets set
14730                               # on this, the root of a 'missing' subtree.
14731    })
14732  expected_elision_output = wc.State(A_COPY_path, {
14733    })
14734  svntest.actions.run_and_verify_merge(A_COPY_path, None, None,
14735                                       sbox.repo_url + '/A', None,
14736                                       expected_output,
14737                                       expected_mergeinfo_output,
14738                                       expected_elision_output,
14739                                       expected_disk,
14740                                       expected_status,
14741                                       expected_skip,
14742                                       [], True, True)
14743
14744#----------------------------------------------------------------------
14745# Test for issue #3115 'Case only renames resulting from merges don't
14746# work or break the WC on case-insensitive file systems'.
14747@Issue(3115)
14748def committed_case_only_move_and_revert(sbox):
14749  "committed case only move causes revert to fail"
14750
14751  sbox.build()
14752  wc_dir = sbox.wc_dir
14753  wc_disk, wc_status = set_up_branch(sbox, True)
14754
14755  # Some paths we'll care about
14756  A_COPY_path = sbox.ospath('A_COPY')
14757
14758  # r3: A case-only file rename on the server
14759  svntest.actions.run_and_verify_svn(['Committing transaction...\n',
14760                                      'Committed revision 3.\n'],
14761                                     [], 'move',
14762                                     sbox.repo_url + '/A/mu',
14763                                     sbox.repo_url + '/A/MU',
14764                                     '-m', 'Move A/mu to A/MU')
14765
14766  # Now merge that rename into the WC
14767  expected_output = wc.State(A_COPY_path, {
14768    'mu' : Item(status='D '),
14769    'MU' : Item(status='A '),
14770    })
14771  expected_mergeinfo_output = wc.State(A_COPY_path, {
14772    '' : Item(status=' U'),
14773    })
14774  expected_elision_output = wc.State(A_COPY_path, {
14775    })
14776  expected_status = wc.State(A_COPY_path, {
14777    ''          : Item(status=' M', wc_rev=2),
14778    'B'         : Item(status='  ', wc_rev=2),
14779    'mu'        : Item(status='D ', wc_rev=2),
14780    'MU'        : Item(status='A ', wc_rev='-', copied='+'),
14781    'B/E'       : Item(status='  ', wc_rev=2),
14782    'B/E/alpha' : Item(status='  ', wc_rev=2),
14783    'B/E/beta'  : Item(status='  ', wc_rev=2),
14784    'B/lambda'  : Item(status='  ', wc_rev=2),
14785    'B/F'       : Item(status='  ', wc_rev=2),
14786    'C'         : Item(status='  ', wc_rev=2),
14787    'D'         : Item(status='  ', wc_rev=2),
14788    'D/G'       : Item(status='  ', wc_rev=2),
14789    'D/G/pi'    : Item(status='  ', wc_rev=2),
14790    'D/G/rho'   : Item(status='  ', wc_rev=2),
14791    'D/G/tau'   : Item(status='  ', wc_rev=2),
14792    'D/gamma'   : Item(status='  ', wc_rev=2),
14793    'D/H'       : Item(status='  ', wc_rev=2),
14794    'D/H/chi'   : Item(status='  ', wc_rev=2),
14795    'D/H/psi'   : Item(status='  ', wc_rev=2),
14796    'D/H/omega' : Item(status='  ', wc_rev=2),
14797    })
14798  expected_disk = wc.State('', {
14799    ''          : Item(props={SVN_PROP_MERGEINFO : '/A:3'}),
14800    'B'         : Item(),
14801    'MU'        : Item("This is the file 'mu'.\n"),
14802    'B/E'       : Item(),
14803    'B/E/alpha' : Item("This is the file 'alpha'.\n"),
14804    'B/E/beta'  : Item("This is the file 'beta'.\n"),
14805    'B/lambda'  : Item("This is the file 'lambda'.\n"),
14806    'B/F'       : Item(),
14807    'C'         : Item(),
14808    'D'         : Item(),
14809    'D/G'       : Item(),
14810    'D/G/pi'    : Item("This is the file 'pi'.\n"),
14811    'D/G/rho'   : Item("This is the file 'rho'.\n"),
14812    'D/G/tau'   : Item("This is the file 'tau'.\n"),
14813    'D/gamma'   : Item("This is the file 'gamma'.\n"),
14814    'D/H'       : Item(),
14815    'D/H/chi'   : Item("This is the file 'chi'.\n"),
14816    'D/H/psi'   : Item("This is the file 'psi'.\n"),
14817    'D/H/omega' : Item("This is the file 'omega'.\n"),
14818    })
14819  expected_skip = wc.State(A_COPY_path, { })
14820  svntest.actions.run_and_verify_merge(A_COPY_path, '2', '3',
14821                                       sbox.repo_url + '/A', None,
14822                                       expected_output,
14823                                       expected_mergeinfo_output,
14824                                       expected_elision_output,
14825                                       expected_disk,
14826                                       expected_status,
14827                                       expected_skip,
14828                                       [], True, False)
14829
14830  # Commit the merge
14831  expected_output = svntest.wc.State(wc_dir, {
14832    'A_COPY'    : Item(verb='Sending'),
14833    'A_COPY/mu' : Item(verb='Deleting'),
14834    'A_COPY/MU' : Item(verb='Adding'),
14835    })
14836  wc_status.tweak('A_COPY', wc_rev=4)
14837  wc_status.remove('A_COPY/mu')
14838  wc_status.add({'A_COPY/MU': Item(status='  ', wc_rev=4)})
14839
14840  svntest.actions.run_and_verify_commit(wc_dir, expected_output, wc_status)
14841
14842  # In issue #3115 the WC gets corrupted and any subsequent revert
14843  # attempts fail with this error:
14844  #  svn.exe revert -R "svn-test-work\working_copies\merge_tests-139"
14845  #  ..\..\..\subversion\svn\revert-cmd.c:81: (apr_err=2)
14846  #  ..\..\..\subversion\libsvn_client\revert.c:167: (apr_err=2)
14847  #  ..\..\..\subversion\libsvn_client\revert.c:103: (apr_err=2)
14848  #  ..\..\..\subversion\libsvn_wc\adm_ops.c:2232: (apr_err=2)
14849  #  ..\..\..\subversion\libsvn_wc\adm_ops.c:2232: (apr_err=2)
14850  #  ..\..\..\subversion\libsvn_wc\adm_ops.c:2232: (apr_err=2)
14851  #  ..\..\..\subversion\libsvn_wc\adm_ops.c:2176: (apr_err=2)
14852  #  ..\..\..\subversion\libsvn_wc\adm_ops.c:2053: (apr_err=2)
14853  #  ..\..\..\subversion\libsvn_wc\adm_ops.c:1869: (apr_err=2)
14854  #  ..\..\..\subversion\libsvn_wc\workqueue.c:520: (apr_err=2)
14855  #  ..\..\..\subversion\libsvn_wc\workqueue.c:490: (apr_err=2)
14856  #  svn: Error restoring text for 'C:\SVN\src-trunk\Debug\subversion\tests
14857  #    \cmdline\svn-test-work\working_copies\merge_tests-139\A_COPY\MU'
14858  svntest.actions.run_and_verify_svn([], [], 'revert', '-R', wc_dir)
14859
14860  # r5: A case-only directory rename on the server
14861  svntest.actions.run_and_verify_svn(['Committing transaction...\n',
14862                                      'Committed revision 5.\n'],
14863                                     [], 'move',
14864                                     sbox.repo_url + '/A/C',
14865                                     sbox.repo_url + '/A/c',
14866                                     '-m', 'Move A/C to A/c')
14867  expected_output = wc.State(A_COPY_path, {
14868    'C' : Item(status='D '),
14869    'c' : Item(status='A '),
14870    })
14871  expected_elision_output = wc.State(A_COPY_path, {
14872    })
14873  expected_disk.tweak('', props={SVN_PROP_MERGEINFO : '/A:3,5'})
14874  expected_disk.add({'c' : Item()})
14875  expected_disk.remove('C')
14876  expected_status.tweak('MU', status='  ', wc_rev=4, copied=None)
14877  expected_status.remove('mu')
14878  expected_status.tweak('C', status='D ')
14879  expected_status.tweak('', wc_rev=4)
14880  expected_status.add({'c' : Item(status='A ', copied='+', wc_rev='-')})
14881  # This merge succeeds. It used to leave a strange state, added with
14882  # history but missing:
14883  #
14884  #   M      merge_tests-139\A_COPY
14885  #  !  +    merge_tests-139\A_COPY\c
14886  #  R  +    merge_tests-139\A_COPY\C
14887  svntest.actions.run_and_verify_merge(A_COPY_path, '4', '5',
14888                                       sbox.repo_url + '/A', None,
14889                                       expected_output,
14890                                       expected_mergeinfo_output,
14891                                       expected_elision_output,
14892                                       expected_disk,
14893                                       expected_status,
14894                                       expected_skip,
14895                                       [], True, False,
14896                                       '--allow-mixed-revisions', A_COPY_path)
14897
14898#----------------------------------------------------------------------
14899# This is a test for issue #3221 'Unable to merge into working copy of
14900# deleted branch'.
14901@SkipUnless(server_has_mergeinfo)
14902@Issue(3221)
14903def merge_into_wc_for_deleted_branch(sbox):
14904  "merge into WC of deleted branch should work"
14905
14906  sbox.build()
14907  wc_dir = sbox.wc_dir
14908
14909  # Copy 'A' to 'A_COPY' then make some changes under 'A'
14910  wc_disk, wc_status = set_up_branch(sbox)
14911
14912  # Some paths we'll care about
14913  A_COPY_path = sbox.ospath('A_COPY')
14914  gamma_path  = sbox.ospath('A/D/gamma')
14915
14916  # r7 - Delete the branch on the repository, obviously it still
14917  # exists in our WC.
14918  svntest.actions.run_and_verify_svn(None, [],
14919                                     'delete', sbox.repo_url + '/A_COPY',
14920                                     '-m', 'Delete A_COPY directly in repos')
14921
14922  # r8 - Make another change under 'A'.
14923  svntest.main.file_write(gamma_path, "Content added after A_COPY deleted")
14924  expected_output = wc.State(wc_dir, {'A/D/gamma' : Item(verb='Sending')})
14925  svntest.main.run_svn(None, 'commit',
14926                       '-m', 'Change made on A after A_COPY was deleted',
14927                       wc_dir)
14928
14929  # Now merge all available revisions from A to A_COPY:
14930  expected_output = wc.State(A_COPY_path, {
14931    'B/E/beta'  : Item(status='U '),
14932    'D/G/rho'   : Item(status='U '),
14933    'D/H/omega' : Item(status='U '),
14934    'D/H/psi'   : Item(status='U '),
14935    'D/gamma'   : Item(status='U '),
14936    })
14937  expected_mergeinfo_output = wc.State(A_COPY_path, {
14938    '' : Item(status=' U'),
14939    })
14940  expected_elision_output = wc.State(A_COPY_path, {
14941    })
14942  expected_status = wc.State(A_COPY_path, {
14943    ''          : Item(status=' M'),
14944    'B'         : Item(status='  '),
14945    'mu'        : Item(status='  '),
14946    'B/E'       : Item(status='  '),
14947    'B/E/alpha' : Item(status='  '),
14948    'B/E/beta'  : Item(status='M '),
14949    'B/lambda'  : Item(status='  '),
14950    'B/F'       : Item(status='  '),
14951    'C'         : Item(status='  '),
14952    'D'         : Item(status='  '),
14953    'D/G'       : Item(status='  '),
14954    'D/G/pi'    : Item(status='  '),
14955    'D/G/rho'   : Item(status='M '),
14956    'D/G/tau'   : Item(status='  '),
14957    'D/gamma'   : Item(status='M '),
14958    'D/H'       : Item(status='  '),
14959    'D/H/chi'   : Item(status='  '),
14960    'D/H/psi'   : Item(status='M '),
14961    'D/H/omega' : Item(status='M '),
14962    })
14963  expected_status.tweak(wc_rev=2)
14964  expected_disk = wc.State('', {
14965    ''          : Item(props={SVN_PROP_MERGEINFO : '/A:2-8'}),
14966    'B'         : Item(),
14967    'mu'        : Item("This is the file 'mu'.\n"),
14968    'B/E'       : Item(),
14969    'B/E/alpha' : Item("This is the file 'alpha'.\n"),
14970    'B/E/beta'  : Item("New content"),
14971    'B/lambda'  : Item("This is the file 'lambda'.\n"),
14972    'B/F'       : Item(),
14973    'C'         : Item(),
14974    'D'         : Item(),
14975    'D/G'       : Item(),
14976    'D/G/pi'    : Item("This is the file 'pi'.\n"),
14977    'D/G/rho'   : Item("New content"),
14978    'D/G/tau'   : Item("This is the file 'tau'.\n"),
14979    'D/gamma'   : Item("Content added after A_COPY deleted"),
14980    'D/H'       : Item(),
14981    'D/H/chi'   : Item("This is the file 'chi'.\n"),
14982    'D/H/psi'   : Item("New content"),
14983    'D/H/omega' : Item("New content"),
14984    })
14985  expected_skip = wc.State(A_COPY_path, { })
14986  # Issue #3221: Previously this merge failed with:
14987  #   ..\..\..\subversion\svn\util.c:900: (apr_err=160013)
14988  #   ..\..\..\subversion\libsvn_client\merge.c:9383: (apr_err=160013)
14989  #   ..\..\..\subversion\libsvn_client\merge.c:8029: (apr_err=160013)
14990  #   ..\..\..\subversion\libsvn_client\merge.c:7577: (apr_err=160013)
14991  #   ..\..\..\subversion\libsvn_client\merge.c:4132: (apr_err=160013)
14992  #   ..\..\..\subversion\libsvn_client\merge.c:3312: (apr_err=160013)
14993  #   ..\..\..\subversion\libsvn_client\ra.c:659: (apr_err=160013)
14994  #   ..\..\..\subversion\libsvn_repos\rev_hunt.c:696: (apr_err=160013)
14995  #   ..\..\..\subversion\libsvn_repos\rev_hunt.c:539: (apr_err=160013)
14996  #   ..\..\..\subversion\libsvn_fs_fs\tree.c:2818: (apr_err=160013)
14997  #   svn: File not found: revision 8, path '/A_COPY'
14998  svntest.actions.run_and_verify_merge(A_COPY_path, None, None,
14999                                       sbox.repo_url + '/A', None,
15000                                       expected_output,
15001                                       expected_mergeinfo_output,
15002                                       expected_elision_output,
15003                                       expected_disk,
15004                                       expected_status,
15005                                       expected_skip,
15006                                       check_props=True)
15007
15008#----------------------------------------------------------------------
15009def foreign_repos_del_and_props(sbox):
15010  "merge del and ps variants from a foreign repos"
15011
15012  sbox.build()
15013  wc_dir = sbox.wc_dir
15014  wc2_dir = sbox.add_wc_path('wc2')
15015
15016  (r2_path, r2_url) = sbox.add_repo_path('fgn')
15017  svntest.main.create_repos(r2_path)
15018
15019  svntest.actions.run_and_verify_svn(None, [], 'checkout',
15020                                     r2_url, wc2_dir)
15021
15022  svntest.actions.run_and_verify_svn(None, [], 'propset',
15023                                      'svn:eol-style', 'native',
15024                                      sbox.ospath('iota'))
15025
15026  svntest.actions.run_and_verify_svn(None, [], 'cp',
15027                                      sbox.ospath('A/D'),
15028                                      sbox.ospath('D'))
15029
15030  svntest.actions.run_and_verify_svn(None, [], 'rm',
15031                                      sbox.ospath('A/D'),
15032                                      sbox.ospath('D/G'))
15033
15034  new_file = sbox.ospath('new-file')
15035  svntest.main.file_write(new_file, 'new-file')
15036  svntest.actions.run_and_verify_svn(None, [], 'add', new_file)
15037
15038  svntest.actions.run_and_verify_svn(None, [], 'propset',
15039                                      'svn:eol-style', 'native', new_file)
15040
15041  svntest.actions.run_and_verify_svn(None, [], 'commit', wc_dir,
15042                                      '-m', 'changed')
15043
15044  svntest.actions.run_and_verify_svn(None, [], 'merge',
15045                                      sbox.repo_url, wc2_dir,
15046                                      '-r', '0:1')
15047
15048  expected_status = svntest.actions.get_virginal_state(wc2_dir, 0)
15049  expected_status.tweak(status='A ')
15050  expected_status.add(
15051     {
15052        ''                  : Item(status='  ', wc_rev='0'),
15053     })
15054  svntest.actions.run_and_verify_status(wc2_dir, expected_status)
15055
15056  expected_status = svntest.actions.get_virginal_state(wc2_dir, 1)
15057
15058  svntest.actions.run_and_verify_svn(None, [], 'commit', wc2_dir,
15059                                     '-m', 'Merged r1')
15060
15061  svntest.actions.run_and_verify_svn(None, [], 'merge',
15062                                      sbox.repo_url, wc2_dir,
15063                                      '-r', '1:2', '--allow-mixed-revisions')
15064
15065  expected_status.tweak('A/D', 'A/D/G', 'A/D/G/rho', 'A/D/G/tau', 'A/D/G/pi',
15066                         'A/D/gamma', 'A/D/H', 'A/D/H/psi', 'A/D/H/omega',
15067                         'A/D/H/chi', status='D ')
15068  expected_status.tweak(wc_rev='1')
15069  expected_status.tweak('', wc_rev='0')
15070  expected_status.tweak('iota', status=' M')
15071
15072  expected_status.add(
15073     {
15074        'new-file'          : Item(status='A ', wc_rev='0'),
15075        'D'                 : Item(status='A ', wc_rev='0'),
15076        'D/H'               : Item(status='A ', wc_rev='0'),
15077        'D/H/omega'         : Item(status='A ', wc_rev='0'),
15078        'D/H/psi'           : Item(status='A ', wc_rev='0'),
15079        'D/H/chi'           : Item(status='A ', wc_rev='0'),
15080        'D/gamma'           : Item(status='A ', wc_rev='0'),
15081     })
15082
15083  svntest.actions.run_and_verify_status(wc2_dir, expected_status)
15084
15085  expected_output = ["Properties on '%s':\n" % (os.path.join(wc2_dir, 'iota')),
15086                     "  svn:eol-style\n",
15087                     "Properties on '%s':\n" % (os.path.join(wc2_dir, 'new-file')),
15088                     "  svn:eol-style\n" ]
15089  svntest.actions.run_and_verify_svn(expected_output, [], 'proplist',
15090                                     os.path.join(wc2_dir, 'iota'),
15091                                     os.path.join(wc2_dir, 'new-file'))
15092
15093#----------------------------------------------------------------------
15094# Test for issue #3642 'immediate depth merges don't create proper subtree
15095# mergeinfo'. See https://issues.apache.org/jira/browse/SVN-3642
15096@Issue(3642)
15097def immediate_depth_merge_creates_minimal_subtree_mergeinfo(sbox):
15098  "no spurious mergeinfo from immediate depth merges"
15099
15100  sbox.build()
15101  wc_dir = sbox.wc_dir
15102  wc_disk, wc_status = set_up_branch(sbox)
15103
15104  B_path      = sbox.ospath('A/B')
15105  B_COPY_path = sbox.ospath('A_COPY/B')
15106
15107
15108  svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir)
15109
15110  # Merge -c5 from A/B to A_COPY/B at --depth immediates.
15111  # This should create only the minimum subtree mergeinfo
15112  # required to describe the merge.  This means that A_COPY/B/E gets
15113  # non-inheritable mergeinfo for r5, because a full depth merge would
15114  # affect that subtree.  The other child of the merge target, A_COPY/B/F
15115  # would never be affected by r5, so it doesn't need any explicit
15116  # mergeinfo.
15117  expected_output = wc.State(B_COPY_path, {})
15118  expected_mergeinfo_output = wc.State(B_COPY_path, {
15119    ''  : Item(status=' U'),
15120    'E' : Item(status=' U'),  # A_COPY/B/E would be affected by r5 if the
15121                              # merge was at infinite depth, so it needs
15122                              # non-inheritable override mergeinfo.
15123    #'F' : Item(status=' U'), No override mergeinfo, r5 is
15124    #                         inoperative on this child.
15125    })
15126  expected_elision_output = wc.State(B_COPY_path, {
15127    })
15128  expected_status = wc.State(B_COPY_path, {
15129    ''        : Item(status=' M'),
15130    'F'       : Item(status='  '),
15131    'E'       : Item(status=' M'),
15132    'E/alpha' : Item(status='  '),
15133    'E/beta'  : Item(status='  '),
15134    'lambda'  : Item(status='  '),
15135
15136    })
15137  expected_status.tweak(wc_rev=6)
15138  expected_disk = wc.State('', {
15139    ''        : Item(props={SVN_PROP_MERGEINFO : '/A/B:5'}),
15140    'E'       : Item(props={SVN_PROP_MERGEINFO : '/A/B/E:5*'}),
15141    'E/alpha' : Item("This is the file 'alpha'.\n"),
15142    'E/beta'  : Item("This is the file 'beta'.\n"),
15143    'F'       : Item(),
15144    'lambda'  : Item("This is the file 'lambda'.\n")
15145    })
15146  expected_skip = wc.State(B_COPY_path, { })
15147  svntest.actions.run_and_verify_merge(B_COPY_path, '4', '5',
15148                                       sbox.repo_url + '/A/B', None,
15149                                       expected_output,
15150                                       expected_mergeinfo_output,
15151                                       expected_elision_output,
15152                                       expected_disk,
15153                                       expected_status,
15154                                       expected_skip,
15155                                       [], True, True,
15156                                       '--depth', 'immediates',
15157                                       B_COPY_path)
15158
15159#----------------------------------------------------------------------
15160# Test for issue #3646 'cyclic --record-only merges create self-referential
15161# mergeinfo'
15162@SkipUnless(server_has_mergeinfo)
15163@Issue(3646)
15164def record_only_merge_creates_self_referential_mergeinfo(sbox):
15165  "merge creates self referential mergeinfo"
15166
15167  # Given a copy of trunk@M to branch, committed in r(M+1), if we
15168  # --record-only merge the branch back to trunk with no revisions
15169  # specified, then trunk gets self-referential mergeinfo recorded
15170  # reflecting its entire natural history.
15171
15172  # Setup a standard greek tree in r1.
15173  sbox.build()
15174  wc_dir = sbox.wc_dir
15175
15176  # Some paths we'll care about
15177  mu_path       = sbox.ospath('A/mu')
15178  A_path        = sbox.ospath('A')
15179  A_branch_path = sbox.ospath('A-branch')
15180
15181  # Make a change to A/mu in r2.
15182  svntest.main.file_write(mu_path, "Trunk edit\n")
15183  svntest.actions.run_and_verify_svn(None, [], 'ci', '-m', 'trunk edit',
15184                                     wc_dir)
15185  svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir)
15186  # Copy A to A-branch in r3
15187  svntest.actions.run_and_verify_svn(None, [],
15188                                     'copy', A_path, A_branch_path)
15189  svntest.actions.run_and_verify_svn(None, [], 'ci',
15190                                     '-m', 'Branch A to A-branch', wc_dir)
15191  svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir)
15192
15193  # Merge A-branch back to A.  This should record the mergeinfo '/A-branch:3'
15194  # on A.
15195  expected_output = wc.State(A_path, {})
15196  expected_mergeinfo_output = wc.State(A_path, {
15197    '' : Item(status=' U'),
15198    })
15199  expected_elision_output = wc.State(A_path, {})
15200  expected_A_status = wc.State(A_path, {
15201    ''          : Item(status=' M'),
15202    'B'         : Item(status='  '),
15203    'mu'        : Item(status='  '),
15204    'B/E'       : Item(status='  '),
15205    'B/E/alpha' : Item(status='  '),
15206    'B/E/beta'  : Item(status='  '),
15207    'B/lambda'  : Item(status='  '),
15208    'B/F'       : Item(status='  '),
15209    'C'         : Item(status='  '),
15210    'D'         : Item(status='  '),
15211    'D/G'       : Item(status='  '),
15212    'D/G/pi'    : Item(status='  '),
15213    'D/G/rho'   : Item(status='  '),
15214    'D/G/tau'   : Item(status='  '),
15215    'D/gamma'   : Item(status='  '),
15216    'D/H'       : Item(status='  '),
15217    'D/H/chi'   : Item(status='  '),
15218    'D/H/psi'   : Item(status='  '),
15219    'D/H/omega' : Item(status='  '),
15220    })
15221  expected_A_status.tweak(wc_rev=3)
15222  expected_A_disk = wc.State('', {
15223    ''          : Item(props={SVN_PROP_MERGEINFO : '/A-branch:3'}),
15224    'B'         : Item(),
15225    'mu'        : Item("Trunk edit\n"),
15226    'B/E'       : Item(),
15227    'B/E/alpha' : Item("This is the file 'alpha'.\n"),
15228    'B/E/beta'  : Item("This is the file 'beta'.\n"),
15229    'B/lambda'  : Item("This is the file 'lambda'.\n"),
15230    'B/F'       : Item(),
15231    'C'         : Item(),
15232    'D'         : Item(),
15233    'D/G'       : Item(),
15234    'D/G/pi'    : Item("This is the file 'pi'.\n"),
15235    'D/G/rho'   : Item("This is the file 'rho'.\n"),
15236    'D/G/tau'   : Item("This is the file 'tau'.\n"),
15237    'D/gamma'   : Item("This is the file 'gamma'.\n"),
15238    'D/H'       : Item(),
15239    'D/H/chi'   : Item("This is the file 'chi'.\n"),
15240    'D/H/psi'   : Item("This is the file 'psi'.\n"),
15241    'D/H/omega' : Item("This is the file 'omega'.\n"),
15242    })
15243  expected_A_skip = wc.State(A_path, {})
15244  svntest.actions.run_and_verify_merge(A_path, None, None,
15245                                       sbox.repo_url + '/A-branch', None,
15246                                       expected_output,
15247                                       expected_mergeinfo_output,
15248                                       expected_elision_output,
15249                                       expected_A_disk,
15250                                       expected_A_status,
15251                                       expected_A_skip,
15252                                       [], True, True,
15253                                       '--record-only', A_path)
15254
15255#----------------------------------------------------------------------
15256# Test for issue #3657 'dav update report handler in skelta mode can cause
15257# spurious conflicts'.
15258@Issue(3657)
15259def dav_skelta_mode_causes_spurious_conflicts(sbox):
15260  "dav skelta mode can cause spurious conflicts"
15261
15262  sbox.build()
15263  wc_dir = sbox.wc_dir
15264
15265  # Some paths we'll care about
15266  mu_path       = sbox.ospath('A/mu')
15267  A_path        = sbox.ospath('A')
15268  C_path        = sbox.ospath('A/C')
15269  A_branch_path = sbox.ospath('A-branch')
15270  C_branch_path = sbox.ospath('A-branch/C')
15271
15272  # r2 - Set some initial properties:
15273  #
15274  #  'dir-prop'='value1' on A/C.
15275  #  'svn:eol-style'='native' on A/mu.
15276  svntest.actions.run_and_verify_svn(None, [],
15277                                     'ps', 'dir-prop', 'initial-val',
15278                                     C_path)
15279  svntest.actions.run_and_verify_svn(None, [],
15280                                     'ps', 'svn:eol-style', 'native',
15281                                     mu_path)
15282  svntest.actions.run_and_verify_svn(None, [],
15283                                     'ci', '-m', 'Set some properties',
15284                                     wc_dir)
15285
15286  # r3 - Branch 'A' to 'A-branch':
15287  svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir)
15288  svntest.actions.run_and_verify_svn(None, [],
15289                                     'copy', A_path, A_branch_path)
15290  svntest.actions.run_and_verify_svn(None, [],
15291                                     'ci', '-m', 'Create a branch of A',
15292                                     wc_dir)
15293
15294  # r4 - Make a text mod to 'A/mu' and add new props to 'A/mu' and 'A/C':
15295  svntest.main.file_write(mu_path, "The new mu!\n")
15296  svntest.actions.run_and_verify_svn(None, [],
15297                                     'ps', 'prop-name', 'prop-val', mu_path)
15298  svntest.actions.run_and_verify_svn(None, [],
15299                                     'ps', 'another-dir-prop', 'initial-val',
15300                                     C_path)
15301  svntest.actions.run_and_verify_svn(None, [], 'ci', '-m',
15302                                     'Edit a file and make some prop changes',
15303                                     wc_dir)
15304
15305  # r5 - Modify the sole property on 'A-branch/C':
15306  svntest.actions.run_and_verify_svn(None, [],
15307                                     'ps', 'dir-prop', 'branch-val',
15308                                     C_branch_path)
15309  svntest.actions.run_and_verify_svn(None, [],
15310                                     'ci', '-m', 'prop mod on branch', wc_dir)
15311
15312  # Now merge r4 from 'A' to 'A-branch'.
15313  #
15314  # Previously this failed over ra_neon and ra_serf on Windows:
15315  #
15316  #   >svn merge ^^/A A-branch -c4
15317  #   Conflict discovered in 'C:/SVN/src-trunk/Debug/subversion/tests/cmdline
15318  #     /svn-test-work/working_copies/merge_tests-110/A-branch/mu'.
15319  #   Select: (p) postpone, (df) diff-full, (e) edit,
15320  #           (mc) mine-conflict, (tc) theirs-conflict,
15321  #           (s) show all options: p
15322  #   --- Merging r4 into 'A-branch':
15323  #   CU   A-branch\mu
15324  #   Conflict for property 'another-dir-prop' discovered on 'C:/SVN/src-trunk
15325  #     /Debug/subversion/tests/cmdline/svn-test-work/working_copies/
15326  #     merge_tests-110/A-branch/C'.
15327  #   Select: (p) postpone,
15328  #           (mf) mine-full, (tf) theirs-full,
15329  #           (s) show all options: p
15330  #    C   A-branch\C
15331  #   --- Recording mergeinfo for merge of r4 into 'A-branch':
15332  #    U   A-branch
15333  #   Summary of conflicts:
15334  #     Text conflicts: 1
15335  #     Property conflicts: 1
15336  svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir)
15337  expected_output = wc.State(A_branch_path, {
15338    'mu' : Item(status='UU'),
15339    'C'  : Item(status=' U'),
15340    })
15341  expected_mergeinfo_output = wc.State(A_branch_path, {
15342    ''   : Item(status=' U'),
15343    })
15344  expected_elision_output = wc.State(A_branch_path, {})
15345  expected_status = wc.State(A_branch_path, {
15346    ''          : Item(status=' M'),
15347    'B'         : Item(status='  '),
15348    'mu'        : Item(status='MM'),
15349    'B/E'       : Item(status='  '),
15350    'B/E/alpha' : Item(status='  '),
15351    'B/E/beta'  : Item(status='  '),
15352    'B/lambda'  : Item(status='  '),
15353    'B/F'       : Item(status='  '),
15354    'C'         : Item(status=' M'),
15355    'D'         : Item(status='  '),
15356    'D/G'       : Item(status='  '),
15357    'D/G/pi'    : Item(status='  '),
15358    'D/G/rho'   : Item(status='  '),
15359    'D/G/tau'   : Item(status='  '),
15360    'D/gamma'   : Item(status='  '),
15361    'D/H'       : Item(status='  '),
15362    'D/H/chi'   : Item(status='  '),
15363    'D/H/psi'   : Item(status='  '),
15364    'D/H/omega' : Item(status='  '),
15365    })
15366  expected_status.tweak(wc_rev=5)
15367  expected_disk = wc.State('', {
15368    ''          : Item(props={SVN_PROP_MERGEINFO :
15369                              '/A:4'}),
15370    'B'         : Item(),
15371    'mu'        : Item("The new mu!\n",
15372                       props={'prop-name' : 'prop-val',
15373                              'svn:eol-style' : 'native'}),
15374    'B/E'       : Item(),
15375    'B/E/alpha' : Item("This is the file 'alpha'.\n"),
15376    'B/E/beta'  : Item("This is the file 'beta'.\n"),
15377    'B/lambda'  : Item("This is the file 'lambda'.\n"),
15378    'B/F'       : Item(),
15379    'C'         : Item(props={'dir-prop' : 'branch-val',
15380                              'another-dir-prop' : 'initial-val'}),
15381    'D'         : Item(),
15382    'D/G'       : Item(),
15383    'D/G/pi'    : Item("This is the file 'pi'.\n"),
15384    'D/G/rho'   : Item("This is the file 'rho'.\n"),
15385    'D/G/tau'   : Item("This is the file 'tau'.\n"),
15386    'D/gamma'   : Item("This is the file 'gamma'.\n"),
15387    'D/H'       : Item(),
15388    'D/H/chi'   : Item("This is the file 'chi'.\n"),
15389    'D/H/psi'   : Item("This is the file 'psi'.\n"),
15390    'D/H/omega' : Item("This is the file 'omega'.\n"),
15391    })
15392  expected_skip = wc.State(A_branch_path, {})
15393  svntest.actions.run_and_verify_merge(A_branch_path, 3, 4,
15394                                       sbox.repo_url + '/A',
15395                                       None,
15396                                       expected_output,
15397                                       expected_mergeinfo_output,
15398                                       expected_elision_output,
15399                                       expected_disk,
15400                                       expected_status,
15401                                       expected_skip,
15402                                       [], True, True)
15403
15404
15405#----------------------------------------------------------------------
15406def merge_into_locally_added_file(sbox):
15407  "merge into locally added file"
15408
15409  sbox.build()
15410  wc_dir = sbox.wc_dir
15411
15412  # Some paths we'll care about
15413  pi_path = sbox.ospath("A/D/G/pi")
15414  new_path = sbox.ospath("A/D/G/new")
15415
15416  shutil.copy(pi_path, new_path)
15417  svntest.main.file_append(pi_path, "foo\n")
15418  sbox.simple_commit() # r2
15419
15420  sbox.simple_add('A/D/G/new')
15421
15422  expected_output = wc.State(wc_dir, {
15423    'A/D/G/new' : Item(status='G '),
15424    })
15425  expected_mergeinfo_output = wc.State(wc_dir, {
15426    'A/D/G/new'   : Item(status=' U'),
15427    })
15428  expected_elision_output = wc.State(wc_dir, {})
15429  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
15430  expected_status.add({ 'A/D/G/new' : Item(status='A ', wc_rev=0)})
15431  expected_status.tweak('A/D/G/pi', wc_rev=2)
15432  expected_disk = svntest.main.greek_state.copy()
15433  expected_disk.tweak('A/D/G/pi', contents="This is the file 'pi'.\nfoo\n")
15434  expected_disk.add({'A/D/G/new' : Item("This is the file 'pi'.\nfoo\n",
15435                     props={SVN_PROP_MERGEINFO : '/A/D/G/pi:2'})})
15436  expected_skip = wc.State(wc_dir, {})
15437
15438  svntest.actions.run_and_verify_merge(wc_dir, '1', '2',
15439                                       sbox.repo_url + '/A/D/G/pi', None,
15440                                       expected_output,
15441                                       expected_mergeinfo_output,
15442                                       expected_elision_output,
15443                                       expected_disk,
15444                                       expected_status,
15445                                       expected_skip,
15446                                       [], True, True,
15447                                       new_path)
15448  sbox.simple_commit()
15449
15450#----------------------------------------------------------------------
15451def merge_into_locally_added_directory(sbox):
15452  "merge into locally added directory"
15453
15454  sbox.build()
15455  wc_dir = sbox.wc_dir
15456
15457  # Some paths we'll care about
15458  G_path = sbox.ospath("A/D/G")
15459  pi_path = sbox.ospath("A/D/G/pi")
15460  new_dir_path = sbox.ospath("A/D/new_dir")
15461
15462  svntest.main.file_append_binary(pi_path, "foo\n")
15463  sbox.simple_commit() # r2
15464
15465  os.mkdir(new_dir_path)
15466  svntest.main.file_append_binary(os.path.join(new_dir_path, 'pi'),
15467                                  "This is the file 'pi'.\n")
15468  svntest.main.file_append_binary(os.path.join(new_dir_path, 'rho'),
15469                                  "This is the file 'rho'.\n")
15470  svntest.main.file_append_binary(os.path.join(new_dir_path, 'tau'),
15471                                  "This is the file 'tau'.\n")
15472  sbox.simple_add('A/D/new_dir')
15473
15474  expected_output = wc.State(wc_dir, {
15475    'A/D/new_dir/pi' : Item(status='G '),
15476    })
15477  expected_mergeinfo_output = wc.State(wc_dir, {
15478    'A/D/new_dir'   : Item(status=' U'),
15479    })
15480  expected_elision_output = wc.State(wc_dir, {})
15481  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
15482  expected_status.add({ 'A/D/new_dir' : Item(status='A ', wc_rev=0)})
15483  expected_status.add({ 'A/D/new_dir/pi' : Item(status='A ', wc_rev=0)})
15484  expected_status.add({ 'A/D/new_dir/rho' : Item(status='A ', wc_rev=0)})
15485  expected_status.add({ 'A/D/new_dir/tau' : Item(status='A ', wc_rev=0)})
15486  expected_status.tweak('A/D/G/pi', wc_rev=2)
15487  expected_disk = svntest.main.greek_state.copy()
15488  expected_disk.tweak('A/D/G/pi', contents="This is the file 'pi'.\nfoo\n")
15489  expected_disk.add({'A/D/new_dir' :
15490                       Item(props={SVN_PROP_MERGEINFO : '/A/D/G:2'})})
15491  expected_disk.add({'A/D/new_dir/pi' :
15492                     Item(contents="This is the file 'pi'.\nfoo\n")})
15493  expected_disk.add({'A/D/new_dir/rho' :
15494                     Item(contents="This is the file 'rho'.\n")})
15495  expected_disk.add({'A/D/new_dir/tau' :
15496                     Item(contents="This is the file 'tau'.\n")})
15497  expected_skip = wc.State(wc_dir, {})
15498
15499  svntest.actions.run_and_verify_merge(wc_dir, '1', '2',
15500                                       sbox.repo_url + '/A/D/G', None,
15501                                       expected_output,
15502                                       expected_mergeinfo_output,
15503                                       expected_elision_output,
15504                                       expected_disk,
15505                                       expected_status,
15506                                       expected_skip,
15507                                       [], True, True,
15508                                       new_dir_path)
15509  sbox.simple_commit()
15510
15511#----------------------------------------------------------------------
15512# Test for issue #2915 'Handle mergeinfo for subtrees missing due to removal
15513# by non-svn command'
15514@SkipUnless(server_has_mergeinfo)
15515@Issue(2915)
15516def merge_with_os_deleted_subtrees(sbox):
15517  "merge tracking fails if target missing subtrees"
15518
15519  # r1: Create a greek tree.
15520  sbox.build()
15521  wc_dir = sbox.wc_dir
15522
15523  # r2 - r6: Copy A to A_COPY and then make some text changes under A.
15524  set_up_branch(sbox)
15525
15526  # Some paths we'll care about
15527  A_COPY_path   = sbox.ospath('A_COPY')
15528  C_COPY_path   = sbox.ospath('A_COPY/C')
15529  psi_COPY_path = sbox.ospath('A_COPY/D/H/psi')
15530  mu_COPY_path  = sbox.ospath('A_COPY/mu')
15531  G_COPY_path   = sbox.ospath('A_COPY/D/G')
15532
15533  # Remove several subtrees from disk.
15534  svntest.main.safe_rmtree(C_COPY_path)
15535  svntest.main.safe_rmtree(G_COPY_path)
15536  os.remove(psi_COPY_path)
15537  os.remove(mu_COPY_path)
15538
15539  # Be sure the regex paths are properly escaped on Windows, see the
15540  # note about "The Backslash Plague" in expected_merge_output().
15541  if sys.platform == 'win32':
15542    re_sep = '\\\\'
15543  else:
15544    re_sep = os.sep
15545
15546  # Common part of the expected error message for all cases we will test.
15547  err_re = "svn: E195016: Merge tracking not allowed with missing subtrees; " + \
15548           "try restoring these items first:"                        + \
15549           "|(\n)"                                                   + \
15550           "|" + svntest.main.stack_trace_regexp
15551
15552  # Case 1: Infinite depth merge into infinite depth WC target.
15553  # Every missing subtree under the target should be reported as missing.
15554  missing = "|(.*A_COPY" + re_sep + "mu\n)"                                + \
15555            "|(.*A_COPY" + re_sep + "D" + re_sep + "G\n)"                  + \
15556            "|(.*A_COPY" + re_sep + "C\n)"                                 + \
15557            "|(.*A_COPY" + re_sep + "D" + re_sep + "H" + re_sep + "psi\n)"
15558  exit_code, out, err = svntest.actions.run_and_verify_svn(
15559    [], svntest.verify.AnyOutput,
15560    'merge', sbox.repo_url + '/A', A_COPY_path)
15561  svntest.verify.verify_outputs("Merge failed but not in the way expected",
15562                                err, None, err_re + missing, None,
15563                                True) # Match *all* lines of stderr
15564
15565  # Case 2: Immediates depth merge into infinite depth WC target.
15566  # Only the two immediate children of the merge target should be reported
15567  # as missing.
15568  missing = "|(.*A_COPY" + re_sep + "mu\n)" + \
15569            "|(.*A_COPY" + re_sep + "C\n)"
15570  exit_code, out, err = svntest.actions.run_and_verify_svn(
15571    [], svntest.verify.AnyOutput,
15572    'merge', sbox.repo_url + '/A', A_COPY_path, '--depth=immediates')
15573  svntest.verify.verify_outputs("Merge failed but not in the way expected",
15574                                err, None, err_re + missing, None, True)
15575
15576  # Case 3: Files depth merge into infinite depth WC target.
15577  # Only the single file child of the merge target should be reported
15578  # as missing.
15579  missing = "|(.*A_COPY" + re_sep + "mu\n)"
15580  exit_code, out, err = svntest.actions.run_and_verify_svn(
15581    [], svntest.verify.AnyOutput,
15582    'merge', sbox.repo_url + '/A', A_COPY_path, '--depth=files')
15583  svntest.verify.verify_outputs("Merge failed but not in the way expected",
15584                                err, None, err_re + missing, None, True)
15585
15586  # Case 4: Empty depth merge into infinite depth WC target.
15587  # Only the...oh, wait, the target is present and that is as deep
15588  # as the merge goes, so this merge should succeed!
15589  svntest.actions.run_and_verify_svn(
15590    svntest.verify.AnyOutput, [], 'merge', sbox.repo_url + '/A',
15591    A_COPY_path, '--depth=empty')
15592
15593#----------------------------------------------------------------------
15594# Test for issue #3668 'inheritance can result in self-referential
15595# mergeinfo' and issue #3669 'inheritance can result in mergeinfo
15596# describing nonexistent sources'
15597@Issue(3668,3669)
15598@XFail()
15599def no_self_referential_or_nonexistent_inherited_mergeinfo(sbox):
15600  "don't inherit bogus mergeinfo"
15601
15602  # r1: Create a greek tree.
15603  sbox.build()
15604  wc_dir = sbox.wc_dir
15605
15606  # r2 - r6: Copy A to A_COPY and then make some text changes under A.
15607  set_up_branch(sbox, nbr_of_branches=1)
15608
15609  # Some paths we'll care about
15610  nu_path      = sbox.ospath('A/C/nu')
15611  nu_COPY_path = sbox.ospath('A_COPY/C/nu')
15612  J_path       = sbox.ospath('A/D/J')
15613  J_COPY_path  = sbox.ospath('A_COPY/D/J')
15614  zeta_path    = sbox.ospath('A/D/J/zeta')
15615  A_COPY_path  = sbox.ospath('A_COPY')
15616
15617  # r7 - Add the file A/C/nu
15618  svntest.main.file_write(nu_path, "This is the file 'nu'.\n")
15619  svntest.actions.run_and_verify_svn(None, [], 'add', nu_path)
15620  svntest.actions.run_and_verify_svn(None, [], 'commit',
15621                                     '-m', 'Add file', wc_dir)
15622
15623  # r8 - Sync merge A to A_COPY
15624  svntest.actions.run_and_verify_svn(
15625    svntest.verify.AnyOutput, [], 'merge', sbox.repo_url + '/A',
15626    A_COPY_path)
15627  svntest.actions.run_and_verify_svn(None, [], 'commit',
15628                                     '-m', 'Sync A_COPY with A', wc_dir)
15629
15630  # r9 - Add the subtree A/D/J
15631  #                      A/D/J/zeta
15632  svntest.actions.run_and_verify_svn(None, [], 'mkdir', J_path)
15633  svntest.main.file_write(zeta_path, "This is the file 'zeta'.\n")
15634  svntest.actions.run_and_verify_svn(None, [], 'add', zeta_path)
15635  svntest.actions.run_and_verify_svn(None, [], 'commit',
15636                                     '-m', 'Add subtree', wc_dir)
15637
15638  # Update the WC in preparation for merges.
15639  svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir)
15640
15641  # r10 - Sync merge A to A_COPY
15642  svntest.actions.run_and_verify_svn(
15643    svntest.verify.AnyOutput, [], 'merge', sbox.repo_url + '/A',
15644    A_COPY_path)
15645  svntest.actions.run_and_verify_svn(None, [], 'commit',
15646                                     '-m', 'Sync A_COPY with A', wc_dir)
15647
15648  # r11 - Text changes to A/C/nu and A/D/J/zeta.
15649  svntest.main.file_write(nu_path, "This is the EDITED file 'nu'.\n")
15650  svntest.main.file_write(zeta_path, "This is the EDITED file 'zeta'.\n")
15651  svntest.actions.run_and_verify_svn(None, [], 'commit',
15652                                     '-m', 'Edit added files', wc_dir)
15653
15654  # Update the WC in preparation for merges.
15655  svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir)
15656
15657  # This test is marked as XFail because the following two merges
15658  # create mergeinfo with both non-existent path-revs and self-referential
15659  # mergeinfo.
15660  #
15661  # Merge all available revisions from A/C/nu to A_COPY/C/nu.
15662  # The target has no explicit mergeinfo of its own but inherits mergeinfo
15663  # from A_COPY.  A_COPY has the mergeinfo '/A:2-9' so the naive mergeinfo
15664  # A_COPY/C/nu inherits is '/A/C/nu:2-9'.  However, '/A/C/nu:2-6' don't
15665  # actually exist (issue #3669) and '/A/C/nu:7-8' is self-referential
15666  # (issue #3668).  Neither of these should be present in the resulting
15667  # mergeinfo for A_COPY/C/nu, only '/A/C/nu:8-11'
15668  expected_output = wc.State(nu_COPY_path, {
15669    '' : Item(status='U '),
15670    })
15671  expected_mergeinfo_output = wc.State(nu_COPY_path, {
15672    '' : Item(status=' G'),
15673    })
15674  expected_elision_output = wc.State(nu_COPY_path, {
15675    })
15676  expected_status = wc.State(nu_COPY_path, {
15677    '' : Item(status='MM', wc_rev=11),
15678    })
15679  expected_disk = wc.State('', {
15680    '' : Item(props={SVN_PROP_MERGEINFO : '/A/C/nu:8-11'}),
15681    })
15682  expected_skip = wc.State(nu_COPY_path, { })
15683  svntest.actions.run_and_verify_merge(nu_COPY_path, None, None,
15684                                       sbox.repo_url + '/A/C/nu', None,
15685                                       expected_output,
15686                                       expected_mergeinfo_output,
15687                                       expected_elision_output,
15688                                       expected_disk,
15689                                       expected_status,
15690                                       expected_skip,
15691                                       check_props=True)
15692
15693  # Merge all available revisions from A/D/J to A_COPY/D/J.  Like the
15694  # previous merge, the target should not have any non-existent ('/A/D/J:2-8')
15695  # or self-referential mergeinfo ('/A/D/J:9') recorded on it post-merge.
15696  expected_output = wc.State(J_COPY_path, {
15697    'zeta' : Item(status='U '),
15698    })
15699  expected_mergeinfo_output = wc.State(J_COPY_path, {
15700    '' : Item(status=' G'),
15701    })
15702  expected_elision_output = wc.State(J_COPY_path, {
15703    })
15704  expected_status = wc.State(J_COPY_path, {
15705    ''     : Item(status=' M', wc_rev=11),
15706    'zeta' : Item(status='M ', wc_rev=11),
15707    })
15708  expected_disk = wc.State('', {
15709    ''     : Item(props={SVN_PROP_MERGEINFO : '/A/D/J:10-11'}),
15710    'zeta' : Item("This is the EDITED file 'zeta'.\n")
15711    })
15712  expected_skip = wc.State(J_COPY_path, { })
15713  svntest.actions.run_and_verify_merge(J_COPY_path, None, None,
15714                                       sbox.repo_url + '/A/D/J', None,
15715                                       expected_output,
15716                                       expected_mergeinfo_output,
15717                                       expected_elision_output,
15718                                       expected_disk,
15719                                       expected_status,
15720                                       expected_skip,
15721                                       check_props=True)
15722
15723#----------------------------------------------------------------------
15724# Test for issue #3756 'subtree merge can inherit invalid working mergeinfo',
15725# issue #3668 'inheritance can result in self-referential mergeinfo', and
15726# issue #3669 'inheritance can result in mergeinfo describing nonexistent
15727# sources'.
15728@XFail()
15729@Issue(3756,3668,3669)
15730def subtree_merges_inherit_invalid_working_mergeinfo(sbox):
15731  "don't inherit bogus working mergeinfo"
15732
15733  # r1: Create a greek tree.
15734  sbox.build()
15735  wc_dir = sbox.wc_dir
15736
15737  # r2 - r6: Copy A to A_COPY and then make some text changes under A.
15738  set_up_branch(sbox, nbr_of_branches=1)
15739
15740  # Some paths we'll care about
15741  nu_path      = sbox.ospath('A/C/nu')
15742  nu_COPY_path = sbox.ospath('A_COPY/C/nu')
15743  A_COPY_path  = sbox.ospath('A_COPY')
15744
15745  # r7 - Add the file A/C/nu
15746  svntest.main.file_write(nu_path, "This is the file 'nu'.\n")
15747  svntest.actions.run_and_verify_svn(None, [], 'add', nu_path)
15748  svntest.actions.run_and_verify_svn(None, [], 'commit',
15749                                     '-m', 'Add file', wc_dir)
15750
15751  # r8 Merge c7 from A to A_COPY.
15752  svntest.actions.run_and_verify_svn(
15753    svntest.verify.AnyOutput, [], 'merge', sbox.repo_url + '/A',
15754    A_COPY_path, '-c7')
15755  svntest.actions.run_and_verify_svn(None, [], 'commit',
15756                                     '-m', 'Merge subtree file addition',
15757                                     wc_dir)
15758
15759  # r9 - A text change to A/C/nu.
15760  svntest.main.file_write(nu_path, "This is the EDITED file 'nu'.\n")
15761  svntest.actions.run_and_verify_svn(None, [], 'commit',
15762                                     '-m', 'Edit added file', wc_dir)
15763
15764  # Update the WC in preparation for merges.
15765  svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir)
15766
15767  # Now do two merges.  The first, r3 to the root of the branch A_COPY.
15768  # This creates working mergeinfo '/A:3,7' on A_COPY.  Then do a subtree
15769  # file merge of r9 from A/C/nu to A_COPY/C/nu.  Since the target has no
15770  # explicit mergeinfo, the mergeinfo set to record the merge of r9 should
15771  # include the mergeinfo inherited from A_COPY.  *But* that raw inherited
15772  # mergeinfo, '/A/C/nu:3,7' is wholly invalid: '/A/C/nu:3' simply doesn't
15773  # exist in the repository and '/A/C/nu:7' is self-referential.  So the
15774  # resulting mergeinfo on 'A_COPY/C/nu' should be only '/A/C/nu:9'.
15775  #
15776  # Currently this test is marked as XFail because the resulting mergeinfo is
15777  # '/A/C/nu:3,7,9' and thus includes a non-existent path-rev.
15778  svntest.actions.run_and_verify_svn(
15779    svntest.verify.AnyOutput, [], 'merge', sbox.repo_url + '/A',
15780    A_COPY_path, '-c3')
15781  svntest.actions.run_and_verify_svn(
15782    svntest.verify.AnyOutput, [], 'merge', sbox.repo_url + '/A/C/nu',
15783    nu_COPY_path, '-c9')
15784  svntest.actions.run_and_verify_svn(
15785    '/A/C/nu:9', [], 'pg', SVN_PROP_MERGEINFO, nu_COPY_path)
15786
15787
15788#----------------------------------------------------------------------
15789# Test for issue #3686 'executable flag not correctly set on merge'
15790# See https://issues.apache.org/jira/browse/SVN-3686
15791@Issue(3686)
15792@SkipUnless(server_has_mergeinfo)
15793@SkipUnless(svntest.main.is_posix_os)
15794def merge_change_to_file_with_executable(sbox):
15795  "executable flag is maintained during binary merge"
15796
15797  # Scenario: When merging a change to a binary file with the 'svn:executable'
15798  # property set, the file is not marked as 'executable'. After commit, the
15799  # executable bit is set correctly.
15800  sbox.build()
15801  wc_dir = sbox.wc_dir
15802  trunk_url = sbox.repo_url + '/A/B/E'
15803
15804  alpha_path = sbox.ospath('A/B/E/alpha')
15805  beta_path = sbox.ospath('A/B/E/beta')
15806
15807  # Force one of the files to be a binary type
15808  svntest.actions.run_and_verify_svn2(None,
15809                                      binary_mime_type_on_text_file_warning, 0,
15810                                     'propset', 'svn:mime-type',
15811                                     'application/octet-stream',
15812                                     alpha_path)
15813
15814  # Set the 'svn:executable' property on both files
15815  svntest.actions.run_and_verify_svn(None, [],
15816                                     'propset', 'svn:executable', 'ON',
15817                                     beta_path)
15818
15819  svntest.actions.run_and_verify_svn(None, [],
15820                                     'propset', 'svn:executable', 'ON',
15821                                     alpha_path)
15822
15823  # Verify the executable bit has been set before committing
15824  if not os.access(alpha_path, os.X_OK):
15825    raise svntest.Failure("alpha not marked as executable before commit")
15826  if not os.access(beta_path, os.X_OK):
15827    raise svntest.Failure("beta is not marked as executable before commit")
15828
15829  # Commit change (r2)
15830  sbox.simple_commit()
15831
15832  # Verify the executable bit has remained after committing
15833  if not os.access(alpha_path, os.X_OK):
15834    raise svntest.Failure("alpha not marked as executable before commit")
15835  if not os.access(beta_path, os.X_OK):
15836    raise svntest.Failure("beta is not marked as executable before commit")
15837
15838  # Create the branch
15839  svntest.actions.run_and_verify_svn(None, [], 'cp',
15840                                     trunk_url,
15841                                     sbox.repo_url + '/branch',
15842                                     '-m', "Creating the Branch")
15843
15844  # Modify the files + commit (r3)
15845  svntest.main.file_append(alpha_path, 'appended alpha text')
15846  svntest.main.file_append(beta_path, 'appended beta text')
15847  sbox.simple_commit()
15848
15849  # Re-root the WC at the branch
15850  svntest.main.safe_rmtree(wc_dir)
15851  svntest.actions.run_and_verify_svn(None, [], 'checkout',
15852                                     sbox.repo_url + '/branch', wc_dir)
15853
15854  # Recalculate the paths
15855  alpha_path = sbox.ospath('alpha')
15856  beta_path = sbox.ospath('beta')
15857
15858  expected_output = wc.State(wc_dir, {
15859    'beta'              : Item(status='U '),
15860    'alpha'             : Item(status='U '),
15861    })
15862  expected_mergeinfo_output = wc.State(wc_dir, {
15863    ''  : Item(status=' U')
15864    })
15865  expected_elision_output = wc.State(wc_dir, {
15866    })
15867  expected_disk = wc.State('', {
15868    '.'                 : Item(props={'svn:mergeinfo':'/A/B/E:3-4'}),
15869    'alpha' : Item(contents="This is the file 'alpha'.\nappended alpha text",
15870                   props={'svn:executable':'*',
15871                          'svn:mime-type':'application/octet-stream'}),
15872    'beta' : Item(contents="This is the file 'beta'.\nappended beta text",
15873                  props={"svn:executable" : '*'}),
15874    })
15875  expected_status = wc.State(wc_dir, {
15876    ''                  : Item(status=' M', wc_rev='4'),
15877    'alpha'             : Item(status='M ', wc_rev='4'),
15878    'beta'              : Item(status='M ', wc_rev='4'),
15879    })
15880  expected_skip = wc.State(wc_dir, { })
15881
15882  # Merge the changes across
15883  svntest.actions.run_and_verify_merge(wc_dir, None, None,
15884                                       trunk_url, None,
15885                                       expected_output,
15886                                       expected_mergeinfo_output,
15887                                       expected_elision_output,
15888                                       expected_disk,
15889                                       expected_status,
15890                                       expected_skip,
15891                                       [], True, True)
15892
15893
15894  # Verify the executable bit has been set
15895  if not os.access(alpha_path, os.X_OK):
15896    raise svntest.Failure("alpha is not marked as executable after merge")
15897  if not os.access(beta_path, os.X_OK):
15898    raise svntest.Failure("beta is not marked as executable after merge")
15899
15900  # Commit (r4)
15901  sbox.simple_commit()
15902
15903  # Verify the executable bit has been set
15904  if not os.access(alpha_path, os.X_OK):
15905    raise svntest.Failure("alpha is not marked as executable after commit")
15906  if not os.access(beta_path, os.X_OK):
15907    raise svntest.Failure("beta is not marked as executable after commit")
15908
15909def dry_run_merge_conflicting_binary(sbox):
15910  "dry run shouldn't resolve conflicts"
15911
15912  # This test-case is to showcase the regression caused by
15913  # r1075802. Here is the link to the relevant discussion:
15914  # http://svn.haxx.se/dev/archive-2011-03/0145.shtml
15915
15916  sbox.build()
15917  wc_dir = sbox.wc_dir
15918  # Add a binary file to the project
15919  theta_contents = open(os.path.join(sys.path[0], "theta.bin"), 'rb').read()
15920  # Write PNG file data into 'A/theta'.
15921  theta_path = sbox.ospath('A/theta')
15922  svntest.main.file_write(theta_path, theta_contents, 'wb')
15923
15924  svntest.main.run_svn(None, 'add', theta_path)
15925
15926  # Commit the new binary file, creating revision 2.
15927  expected_output = svntest.wc.State(wc_dir, {
15928    'A/theta' : Item(verb='Adding  (bin)'),
15929    })
15930  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
15931  expected_status.add({
15932    'A/theta' : Item(status='  ', wc_rev=2),
15933    })
15934  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
15935                                        expected_status)
15936
15937  # Make the "other" working copy
15938  other_wc = sbox.add_wc_path('other')
15939  svntest.actions.duplicate_dir(wc_dir, other_wc)
15940
15941  # Change the binary file in first working copy, commit revision 3.
15942  svntest.main.file_append(theta_path, "some extra junk")
15943  expected_output = wc.State(wc_dir, {
15944    'A/theta' : Item(verb='Sending'),
15945    })
15946  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
15947  expected_status.add({
15948    'A/theta' : Item(status='  ', wc_rev=3),
15949    })
15950  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
15951                                        expected_status)
15952
15953  # In second working copy, append different content to the binary
15954  # and attempt to 'svn merge -r 2:3'.
15955  # We should see a conflict during the merge.
15956  other_theta_path = os.path.join(other_wc, 'A', 'theta')
15957  svntest.main.file_append(other_theta_path, "some other junk")
15958  expected_output = wc.State(other_wc, {
15959    'A/theta' : Item(status='C '),
15960    })
15961  expected_mergeinfo_output = wc.State(other_wc, {
15962    '' : Item(status=' U'),
15963    })
15964  expected_elision_output = wc.State(other_wc, {
15965    })
15966  expected_disk = svntest.main.greek_state.copy()
15967  expected_disk.add({
15968    ''        : Item(props={SVN_PROP_MERGEINFO : '/:3'}),
15969    'A/theta' : Item(theta_contents + b"some other junk",
15970                     props={'svn:mime-type' : 'application/octet-stream'}),
15971    })
15972
15973  # verify content of base(left) file
15974  expected_disk.add({
15975  'A/theta.merge-left.r2' :
15976    Item(contents = theta_contents )
15977  })
15978  # verify content of theirs(right) file
15979  expected_disk.add({
15980  'A/theta.merge-right.r3' :
15981    Item(contents= theta_contents + b"some extra junk")
15982  })
15983
15984  expected_status = svntest.actions.get_virginal_state(other_wc, 1)
15985  expected_status.add({
15986    ''        : Item(status=' M', wc_rev=1),
15987    'A/theta' : Item(status='C ', wc_rev=2),
15988    })
15989  expected_skip = wc.State('', { })
15990
15991  svntest.actions.run_and_verify_merge(other_wc, '2', '3',
15992                                       sbox.repo_url, None,
15993                                       expected_output,
15994                                       expected_mergeinfo_output,
15995                                       expected_elision_output,
15996                                       expected_disk,
15997                                       expected_status,
15998                                       expected_skip,
15999                                       [], True, True,
16000                                       '--allow-mixed-revisions',
16001                                       other_wc)
16002
16003#----------------------------------------------------------------------
16004@Issue(3857)
16005def foreign_repos_prop_conflict(sbox):
16006  "prop conflict from foreign repos merge"
16007
16008  sbox.build()
16009  wc_dir = sbox.wc_dir
16010
16011  # Create a second repository and working copy with the original
16012  # greek tree.
16013  repo_dir = sbox.repo_dir
16014  other_repo_dir, other_repo_url = sbox.add_repo_path("other")
16015  other_wc_dir = sbox.add_wc_path("other")
16016  svntest.main.copy_repos(repo_dir, other_repo_dir, 1, 1)
16017  svntest.actions.run_and_verify_svn(None, [], 'co', other_repo_url,
16018                                     other_wc_dir)
16019
16020  # Add properties in the first repos and commit.
16021  sbox.simple_propset('red', 'rojo', 'A/D/G')
16022  sbox.simple_propset('yellow', 'amarillo', 'A/D/G')
16023  svntest.actions.run_and_verify_svn(None, [],
16024                                     'ci', '-m', 'spenglish', wc_dir)
16025
16026  # Tweak properties in the first repos and commit.
16027  sbox.simple_propset('red', 'rosso', 'A/D/G')
16028  sbox.simple_propset('yellow', 'giallo', 'A/D/G')
16029  svntest.actions.run_and_verify_svn(None, [],
16030                                     'ci', '-m', 'engtalian', wc_dir)
16031
16032  # Now, merge the propchange to the *second* working copy.
16033  expected_output = [' C   %s\n' % (os.path.join(other_wc_dir,
16034                                                 "A", "D", "G"))]
16035  expected_output = expected_merge_output([[3]], expected_output, True,
16036                                          prop_conflicts=1)
16037  svntest.actions.run_and_verify_svn(expected_output,
16038                                     [], 'merge', '-c3',
16039                                     sbox.repo_url,
16040                                     other_wc_dir)
16041
16042#----------------------------------------------------------------------
16043# Test for issue #3975 'adds with explicit mergeinfo don't get mergeinfo
16044# describing merge which added them'
16045@Issue(3975)
16046@SkipUnless(server_has_mergeinfo)
16047def merge_adds_subtree_with_mergeinfo(sbox):
16048  "merge adds subtree with mergeinfo"
16049
16050  sbox.build()
16051  os.chdir(sbox.wc_dir)
16052  sbox.wc_dir = ''
16053  wc_dir = sbox.wc_dir
16054  wc_disk, wc_status = set_up_branch(sbox, False, 2)
16055
16056  A_path       = sbox.ospath('A')
16057  nu_path      = sbox.ospath('A/C/nu')
16058  nu_COPY_path = sbox.ospath('A_COPY/C/nu')
16059  A_COPY2_path = sbox.ospath('A_COPY_2')
16060
16061  # r8 - Add the file A_COPY/C/nu.
16062  svntest.main.file_write(nu_COPY_path, "This is the file 'nu'.\n")
16063  svntest.actions.run_and_verify_svn(None, [], 'add', nu_COPY_path)
16064  svntest.actions.run_and_verify_svn(None, [], 'ci', '-m',
16065                                     'Add a file on the A_COPY branch',
16066                                     wc_dir)
16067
16068  # r9 - Cherry pick r8 from A_COPY to A.
16069  svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir)
16070  svntest.actions.run_and_verify_svn(None, [], 'merge',
16071                                     sbox.repo_url + '/A_COPY',
16072                                     A_path, '-c8')
16073  svntest.actions.run_and_verify_svn(None, [], 'ci', '-m',
16074                                     'Merge r8 from A_COPY to A', wc_dir)
16075
16076  # r10 - Make a modification to A_COPY/C/nu
16077  svntest.main.file_append(nu_COPY_path,
16078                           "More work on the A_COPY branch.\n")
16079  svntest.actions.run_and_verify_svn(None, [], 'ci', '-m',
16080                                     'Some work on the A_COPY branch', wc_dir)
16081
16082  # r9 - Cherry pick r10 from A_COPY/C/nu to A/C/nu.  Make some
16083  # changes to A/C/nu before committing the merge.
16084  svntest.actions.run_and_verify_svn(None, [], 'merge',
16085                                     sbox.repo_url + '/A_COPY/C/nu',
16086                                     nu_path, '-c10')
16087  svntest.main.file_append(nu_path, "A faux conflict resolution.\n")
16088  svntest.actions.run_and_verify_svn(None, [], 'ci', '-m',
16089                                     'Merge r8 from A_COPY to A', wc_dir)
16090
16091  # Sync merge A to A_COPY_2
16092  svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir)
16093  expected_output = wc.State(A_COPY2_path, {
16094    'B/E/beta'  : Item(status='U '),
16095    'C/nu'      : Item(status='A '),
16096    'D/G/rho'   : Item(status='U '),
16097    'D/H/omega' : Item(status='U '),
16098    'D/H/psi'   : Item(status='U '),
16099    ''          : Item(status=' U'),
16100    })
16101  expected_mergeinfo_output = wc.State(A_COPY2_path, {
16102    ''     : Item(status=' G'),
16103    'C/nu' : Item(status=' U'),
16104    })
16105  expected_elision_output = wc.State(A_COPY2_path, {
16106    })
16107  expected_status = wc.State(A_COPY2_path, {
16108    ''          : Item(status=' M'),
16109    'B'         : Item(status='  '),
16110    'mu'        : Item(status='  '),
16111    'B/E'       : Item(status='  '),
16112    'B/E/alpha' : Item(status='  '),
16113    'B/E/beta'  : Item(status='M '),
16114    'B/lambda'  : Item(status='  '),
16115    'B/F'       : Item(status='  '),
16116    'C'         : Item(status='  '),
16117    'C/nu'      : Item(status='A ', copied='+'),
16118    'D'         : Item(status='  '),
16119    'D/G'       : Item(status='  '),
16120    'D/G/pi'    : Item(status='  '),
16121    'D/G/rho'   : Item(status='M '),
16122    'D/G/tau'   : Item(status='  '),
16123    'D/gamma'   : Item(status='  '),
16124    'D/H'       : Item(status='  '),
16125    'D/H/chi'   : Item(status='  '),
16126    'D/H/psi'   : Item(status='M '),
16127    'D/H/omega' : Item(status='M '),
16128    })
16129  expected_status.tweak(wc_rev=11)
16130  expected_status.tweak('C/nu', wc_rev='-')
16131  expected_disk = wc.State('', {
16132    ''          : Item(props={SVN_PROP_MERGEINFO : '/A:3-11\n/A_COPY:8'}),
16133    'B'         : Item(),
16134    'mu'        : Item("This is the file 'mu'.\n"),
16135    'B/E'       : Item(),
16136    'B/E/alpha' : Item("This is the file 'alpha'.\n"),
16137    'B/E/beta'  : Item("New content"),
16138    'B/lambda'  : Item("This is the file 'lambda'.\n"),
16139    'B/F'       : Item(),
16140    'C'         : Item(),
16141    # C/nu will pick up the mergeinfo A_COPY/C/nu:8 which is self-referential.
16142    # This is issue #3668 'inheritance can result in self-referential
16143    # mergeinfo', but we'll allow it in this test since issue #3668 is
16144    # tested elsewhere and is not the point of *this* test.
16145    'C/nu'      : Item("This is the file 'nu'.\n" \
16146                       "More work on the A_COPY branch.\n" \
16147                       "A faux conflict resolution.\n",
16148                       props={SVN_PROP_MERGEINFO :
16149                              '/A/C/nu:9-11\n/A_COPY/C/nu:8,10'}),
16150    'D'         : Item(),
16151    'D/G'       : Item(),
16152    'D/G/pi'    : Item("This is the file 'pi'.\n"),
16153    'D/G/rho'   : Item("New content"),
16154    'D/G/tau'   : Item("This is the file 'tau'.\n"),
16155    'D/gamma'   : Item("This is the file 'gamma'.\n"),
16156    'D/H'       : Item(),
16157    'D/H/chi'   : Item("This is the file 'chi'.\n"),
16158    'D/H/psi'   : Item("New content"),
16159    'D/H/omega' : Item("New content"),
16160    })
16161  expected_skip = wc.State('.', { })
16162  svntest.actions.run_and_verify_merge(A_COPY2_path, None, None,
16163                                       sbox.repo_url + '/A', None,
16164                                       expected_output,
16165                                       expected_mergeinfo_output,
16166                                       expected_elision_output,
16167                                       expected_disk,
16168                                       expected_status,
16169                                       expected_skip,
16170                                       check_props=True)
16171
16172#----------------------------------------------------------------------
16173# A test for issue #3978 'reverse merge which adds subtree fails'.
16174@Issue(3978,4057)
16175@SkipUnless(server_has_mergeinfo)
16176def reverse_merge_adds_subtree(sbox):
16177  "reverse merge adds subtree"
16178
16179  sbox.build()
16180  os.chdir(sbox.wc_dir)
16181  sbox.wc_dir = ''
16182  wc_dir = sbox.wc_dir
16183  wc_disk, wc_status = set_up_branch(sbox)
16184
16185  A_path       = sbox.ospath('A')
16186  chi_path     = sbox.ospath('A/D/H/chi')
16187  A_COPY_path  = sbox.ospath('A_COPY')
16188  H_COPY_path  = sbox.ospath('A_COPY/D/H')
16189
16190  # r7 - Delete A\D\H\chi
16191  svntest.actions.run_and_verify_svn(None, [], 'delete', chi_path)
16192  svntest.actions.run_and_verify_svn(None, [], 'ci', '-m',
16193                                     'Delete a file', wc_dir)
16194
16195  # r8 - Merge r7 from A to A_COPY
16196  svntest.actions.run_and_verify_svn(None, [], 'merge',
16197                                     sbox.repo_url + '/A',
16198                                     A_COPY_path, '-c7')
16199  svntest.actions.run_and_verify_svn(None, [], 'ci', '-m',
16200                                     'Cherry-pick r7 from A to A_COPY', wc_dir)
16201
16202  # r9 - File depth sync merge from A/D/H to A_COPY/D/H/
16203  # This shallow merge does not create non-inheritable mergeinfo because of
16204  # the issue #4057 fix; all subtrees affected by the diff are present, so
16205  # non-inheritable mergeinfo is not required.
16206  svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir)
16207  svntest.actions.run_and_verify_svn(None, [], 'merge',
16208                                     sbox.repo_url + '/A/D/H',
16209                                     H_COPY_path, '--depth', 'files')
16210  svntest.actions.run_and_verify_svn(None, [], 'ci', '-m',
16211                                     'Cherry-pick r7 from A to A_COPY', wc_dir)
16212
16213  # Reverse merge r7 from A to A_COPY
16214  #
16215  # Prior to the issue #3978 fix this merge failed with an assertion:
16216  #
16217  # >svn merge ^/A A_COPY -c-7
16218  # --- Reverse-merging r7 into 'A_COPY\D\H':
16219  # A    A_COPY\D\H\chi
16220  # --- Recording mergeinfo for reverse merge of r7 into 'A_COPY':
16221  #  U   A_COPY
16222  # --- Recording mergeinfo for reverse merge of r7 into 'A_COPY\D\H':
16223  #  U   A_COPY\D\H
16224  # ..\..\..\subversion\svn\util.c:913: (apr_err=200020)
16225  # ..\..\..\subversion\libsvn_client\merge.c:10990: (apr_err=200020)
16226  # ..\..\..\subversion\libsvn_client\merge.c:10944: (apr_err=200020)
16227  # ..\..\..\subversion\libsvn_client\merge.c:10944: (apr_err=200020)
16228  # ..\..\..\subversion\libsvn_client\merge.c:10914: (apr_err=200020)
16229  # ..\..\..\subversion\libsvn_client\merge.c:8928: (apr_err=200020)
16230  # ..\..\..\subversion\libsvn_client\merge.c:7850: (apr_err=200020)
16231  # ..\..\..\subversion\libsvn_client\mergeinfo.c:120: (apr_err=200020)
16232  # ..\..\..\subversion\libsvn_wc\props.c:2472: (apr_err=200020)
16233  # ..\..\..\subversion\libsvn_wc\props.c:2247: (apr_err=200020)
16234  # ..\..\..\subversion\libsvn_wc\props.c:2576: (apr_err=200020)
16235  # ..\..\..\subversion\libsvn_subr\mergeinfo.c:705: (apr_err=200020)
16236  # svn: E200020: Could not parse mergeinfo string '-7'
16237  # ..\..\..\subversion\libsvn_subr\mergeinfo.c:688: (apr_err=200022)
16238  # ..\..\..\subversion\libsvn_subr\mergeinfo.c:607: (apr_err=200022)
16239  # ..\..\..\subversion\libsvn_subr\mergeinfo.c:504: (apr_err=200022)
16240  # ..\..\..\subversion\libsvn_subr\kitchensink.c:57: (apr_err=200022)
16241  # svn: E200022: Negative revision number found parsing '-7'
16242  svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir)
16243  expected_output = wc.State(A_COPY_path, {
16244    'D/H/chi' : Item(status='A '),
16245    })
16246  expected_mergeinfo_output = wc.State(A_COPY_path, {
16247    ''    : Item(status=' U'),
16248    'D/H' : Item(status=' U'),
16249    })
16250  expected_elision_output = wc.State(A_COPY_path, {
16251    '' : Item(status=' U'),
16252    })
16253  expected_status = wc.State(A_COPY_path, {
16254    ''          : Item(status=' M'),
16255    'B'         : Item(status='  '),
16256    'mu'        : Item(status='  '),
16257    'B/E'       : Item(status='  '),
16258    'B/E/alpha' : Item(status='  '),
16259    'B/E/beta'  : Item(status='  '),
16260    'B/lambda'  : Item(status='  '),
16261    'B/F'       : Item(status='  '),
16262    'C'         : Item(status='  '),
16263    'D'         : Item(status='  '),
16264    'D/G'       : Item(status='  '),
16265    'D/G/pi'    : Item(status='  '),
16266    'D/G/rho'   : Item(status='  '),
16267    'D/G/tau'   : Item(status='  '),
16268    'D/gamma'   : Item(status='  '),
16269    'D/H'       : Item(status=' M'),
16270    'D/H/chi'   : Item(status='A ', copied='+'),
16271    'D/H/psi'   : Item(status='  '),
16272    'D/H/omega' : Item(status='  '),
16273    })
16274  expected_status.tweak(wc_rev=9)
16275  expected_status.tweak('D/H/chi', wc_rev='-')
16276  expected_disk = wc.State('', {
16277    'B'         : Item(),
16278    'mu'        : Item("This is the file 'mu'.\n"),
16279    'B/E'       : Item(),
16280    'B/E/alpha' : Item("This is the file 'alpha'.\n"),
16281    'B/E/beta'  : Item("This is the file 'beta'.\n"),
16282    'B/lambda'  : Item("This is the file 'lambda'.\n"),
16283    'B/F'       : Item(),
16284    'C'         : Item(),
16285    'D'         : Item(),
16286    'D/G'       : Item(),
16287    'D/G/pi'    : Item("This is the file 'pi'.\n"),
16288    'D/G/rho'   : Item("This is the file 'rho'.\n"),
16289    'D/G/tau'   : Item("This is the file 'tau'.\n"),
16290    'D/gamma'   : Item("This is the file 'gamma'.\n"),
16291    'D/H'       : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:2-6,8'}),
16292    'D/H/chi'   : Item("This is the file 'chi'.\n"),
16293    'D/H/psi'   : Item("New content"),
16294    'D/H/omega' : Item("New content"),
16295    })
16296  expected_skip = wc.State('.', { })
16297  svntest.actions.run_and_verify_merge(A_COPY_path, 7, 6,
16298                                       sbox.repo_url + '/A', None,
16299                                       expected_output,
16300                                       expected_mergeinfo_output,
16301                                       expected_elision_output,
16302                                       expected_disk,
16303                                       expected_status,
16304                                       expected_skip,
16305                                       check_props=True)
16306
16307#----------------------------------------------------------------------
16308# A test for issue #3989 'merge which deletes file with native eol-style
16309# raises spurious tree conflict'.
16310@Issue(3989)
16311@SkipUnless(server_has_mergeinfo)
16312def merged_deletion_causes_tree_conflict(sbox):
16313  "merged deletion causes spurious tree conflict"
16314
16315  sbox.build()
16316  os.chdir(sbox.wc_dir)
16317  sbox.wc_dir = ''
16318  wc_dir = sbox.wc_dir
16319
16320  A_path        = sbox.ospath('A')
16321  psi_path      = sbox.ospath('A/D/H/psi')
16322  H_branch_path = sbox.ospath('branch/D/H')
16323
16324  # r2 - Set svn:eol-style native on A/D/H/psi
16325  svntest.actions.run_and_verify_svn(None, [], 'ps', 'svn:eol-style',
16326                                     'native', psi_path)
16327  svntest.actions.run_and_verify_svn(None, [], 'ci', '-m',
16328                                     'Set eol-style native on a path',
16329                                     wc_dir)
16330
16331  # r3 - Branch ^/A to ^/branch
16332  svntest.actions.run_and_verify_svn(None, [], 'copy',
16333                                     sbox.repo_url + '/A',
16334                                     sbox.repo_url + '/branch',
16335                                     '-m', 'Copy ^/A to ^/branch')
16336  svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir)
16337
16338  # r4 - Delete A/D/H/psi
16339  svntest.actions.run_and_verify_svn(None, [], 'delete', psi_path)
16340  svntest.actions.run_and_verify_svn(None, [], 'ci', '-m',
16341                                     'Delete a a path with native eol-style',
16342                                     wc_dir)
16343
16344  # Sync merge ^/A/D/H to branch/D/H.
16345  #
16346  # branch/D/H/psi is, ignoring differences caused by svn:eol-style, identical
16347  # to ^/A/D/H/psi when the latter was deleted, so the deletion should merge
16348  # cleanly.
16349  svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir)
16350  expected_output = wc.State(H_branch_path, {
16351    'psi' : Item(status='D '),
16352    })
16353  expected_mergeinfo_output = wc.State(H_branch_path, {
16354    ''    : Item(status=' U'),
16355    })
16356  expected_elision_output = wc.State(H_branch_path, {})
16357  expected_status = wc.State(H_branch_path, {
16358    ''      : Item(status=' M'),
16359    'chi'   : Item(status='  '),
16360    'psi'   : Item(status='D '),
16361    'omega' : Item(status='  '),
16362    })
16363  expected_status.tweak(wc_rev=4)
16364  expected_disk = wc.State('', {
16365    ''      : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:3-4'}),
16366    'chi'   : Item("This is the file 'chi'.\n"),
16367    'omega' : Item("This is the file 'omega'.\n"),
16368    })
16369  expected_skip = wc.State('.', { })
16370  svntest.actions.run_and_verify_merge(H_branch_path, None, None,
16371                                       sbox.repo_url + '/A/D/H', None,
16372                                       expected_output,
16373                                       expected_mergeinfo_output,
16374                                       expected_elision_output,
16375                                       expected_disk,
16376                                       expected_status,
16377                                       expected_skip,
16378                                       check_props=True)
16379
16380#----------------------------------------------------------------------
16381# A test for issue #3976 'record-only merges which add new subtree mergeinfo
16382# don't record mergeinfo describing merge'.
16383@Issue(3976)
16384@SkipUnless(server_has_mergeinfo)
16385def record_only_merge_adds_new_subtree_mergeinfo(sbox):
16386  "record only merge adds new subtree mergeinfo"
16387
16388  sbox.build()
16389  os.chdir(sbox.wc_dir)
16390  sbox.wc_dir = ''
16391  wc_dir = sbox.wc_dir
16392  wc_disk, wc_status = set_up_branch(sbox)
16393
16394  psi_path      = sbox.ospath('A/D/H/psi')
16395  psi_COPY_path = sbox.ospath('A_COPY/D/H/psi')
16396  H_COPY2_path  = sbox.ospath('A_COPY_2/D/H')
16397
16398  # r7 - Copy ^/A_COPY to ^/A_COPY_2
16399  svntest.actions.run_and_verify_svn(None, [],
16400                                     'copy', '-m', 'copy A_COPY to A_COPY_2',
16401                                     sbox.repo_url + '/A_COPY',
16402                                     sbox.repo_url + '/A_COPY_2')
16403
16404  # r8 - Set a property on A/D/H/psi.  It doesn't matter what property
16405  # we use, just as long as we have a change that can be merged independently
16406  # of the text change to A/D/H/psi in r3.
16407  svntest.main.run_svn(None, 'propset', 'svn:eol-style', 'native', psi_path)
16408  svntest.main.run_svn(None, 'commit', '-m', 'set svn:eol-style', wc_dir)
16409
16410  # r9 - Merge r3 from ^/A/D/H/psi to A_COPY/D/H/psi.
16411  svntest.actions.run_and_verify_svn(None, [], 'merge',
16412                                     sbox.repo_url + '/A/D/H/psi',
16413                                     psi_COPY_path, '-c3')
16414  svntest.main.run_svn(None, 'commit', '-m', 'Subtree merge', wc_dir)
16415
16416  # r10 - Merge r8 from ^/A/D/H/psi to A_COPY/D/H/psi.
16417  svntest.actions.run_and_verify_svn(None, [], 'merge',
16418                                     sbox.repo_url + '/A/D/H/psi',
16419                                     psi_COPY_path, '-c8')
16420  svntest.main.run_svn(None, 'commit', '-m', 'Subtree merge', wc_dir)
16421
16422  # Merge r10 from ^/A_COPY/D/H to A_COPY_2/D/H.  This should leave
16423  # A_COPY_2/D/H/psi with three new property additions:
16424  #
16425  #   1) The 'svn:eol-style=native' from r10 via r8.
16426  #
16427  #   2) The mergeinfo '/A/D/H/psi:8' from r10.
16428  #
16429  #   3) The mergeinfo '/A_COPY/D/H/psi:10' describing the merge itself.
16430  svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir)
16431  expected_output = wc.State(H_COPY2_path, {
16432    'psi' : Item(status=' U'),
16433    })
16434  expected_mergeinfo_output = wc.State(H_COPY2_path, {
16435    ''    : Item(status=' U'),
16436    'psi' : Item(status=' G'),
16437    })
16438  expected_elision_output = wc.State(H_COPY2_path, {})
16439  expected_status = wc.State(H_COPY2_path, {
16440    ''      : Item(status=' M'),
16441    'chi'   : Item(status='  '),
16442    'psi'   : Item(status=' M'),
16443    'omega' : Item(status='  '),
16444    })
16445  expected_status.tweak(wc_rev=10)
16446  expected_disk = wc.State('', {
16447    ''      : Item(props={SVN_PROP_MERGEINFO : '/A_COPY/D/H:10'}),
16448    'psi'   : Item("This is the file 'psi'.\n",
16449                   props={SVN_PROP_MERGEINFO :
16450                          '/A/D/H/psi:8\n/A_COPY/D/H/psi:10',
16451                          'svn:eol-style' : 'native'}),
16452    'chi'   : Item("This is the file 'chi'.\n"),
16453    'omega' : Item("This is the file 'omega'.\n"),
16454    })
16455  expected_skip = wc.State('.', { })
16456  svntest.actions.run_and_verify_merge(H_COPY2_path, 9, 10,
16457                                       sbox.repo_url + '/A_COPY/D/H', None,
16458                                       expected_output,
16459                                       expected_mergeinfo_output,
16460                                       expected_elision_output,
16461                                       expected_disk,
16462                                       expected_status,
16463                                       expected_skip,
16464                                       check_props=True)
16465
16466
16467#----------------------------------------------------------------------
16468# Test for issue #4056 "don't record non-inheritable mergeinfo if missing
16469# subtrees are not touched by the full-depth diff".
16470@Issue(4056)
16471@SkipUnless(server_has_mergeinfo)
16472def unnecessary_noninheritable_mergeinfo_missing_subtrees(sbox):
16473  "missing subtrees untouched by infinite depth merge"
16474
16475  B_branch_path = sbox.ospath('branch/B')
16476
16477  # Setup a simple branch to which
16478  expected_output, expected_mergeinfo_output, expected_elision_output, \
16479    expected_status, expected_disk, expected_skip = \
16480    noninheritable_mergeinfo_test_set_up(sbox)
16481
16482  # Create a shallow merge target; set depth of branch/B to files.
16483  svntest.main.run_svn(None, 'up', '--set-depth=files', B_branch_path)
16484  expected_status.remove('E', 'E/alpha', 'E/beta', 'F')
16485  expected_disk.remove('E', 'E/alpha', 'E/beta', 'F')
16486
16487  # Merge r3 from ^/A/B to branch/B
16488  #
16489  # Merge is smart enough to realize that despite the shallow merge target,
16490  # the diff can only affect branch/B/lambda, which is still present, so there
16491  # is no need to record non-inheritable mergeinfo on the target
16492  # or any subtree mergeinfo whatsoever:
16493  #
16494  #   >svn pg svn:mergeinfo -vR
16495  #   Properties on 'branch\B':
16496  #     svn:mergeinfo
16497  #       /A/B:3 <-- Nothing was skipped, so doesn't need
16498  #                  to be non-inheritable.
16499  svntest.actions.run_and_verify_merge(B_branch_path,
16500                                       '2', '3',
16501                                       sbox.repo_url + '/A/B', None,
16502                                       expected_output,
16503                                       expected_mergeinfo_output,
16504                                       expected_elision_output,
16505                                       expected_disk,
16506                                       expected_status,
16507                                       expected_skip,
16508                                       [], True, True,
16509                                       B_branch_path)
16510
16511#----------------------------------------------------------------------
16512# Test for issue #4057 "don't record non-inheritable mergeinfo in shallow
16513# merge if entire diff is within requested depth".
16514@Issue(4057)
16515@SkipUnless(server_has_mergeinfo)
16516def unnecessary_noninheritable_mergeinfo_shallow_merge(sbox):
16517  "shallow merge reaches all necessary subtrees"
16518
16519  B_branch_path = sbox.ospath('branch/B')
16520  E_path        = sbox.ospath('A/B/E')
16521
16522  # Setup a simple branch to which
16523  expected_output, expected_mergeinfo_output, expected_elision_output, \
16524    expected_status, expected_disk, expected_skip = \
16525    noninheritable_mergeinfo_test_set_up(sbox)
16526
16527  # Merge r3 from ^/A/B to branch/B at operational depth=files
16528  #
16529  # Previously this failed because merge wasn't smart enough to
16530  # realize that despite being a shallow merge, the diff can
16531  # only affect branch/B/lambda, which is within the specified
16532  # depth, so there is no need to record non-inheritable mergeinfo
16533  # or subtree mergeinfo:
16534  #
16535  #   >svn pg svn:mergeinfo -vR
16536  #   Properties on 'branch\B':
16537  #     svn:mergeinfo
16538  #       /A/B:3* <-- Should be inheritable
16539  #   Properties on 'branch\B\lambda':
16540  #     svn:mergeinfo
16541  #       /A/B/lambda:3 <-- Not necessary
16542  expected_skip = wc.State(B_branch_path, {})
16543  svntest.actions.run_and_verify_merge(B_branch_path, '2', '3',
16544                                       sbox.repo_url + '/A/B', None,
16545                                       expected_output,
16546                                       expected_mergeinfo_output,
16547                                       expected_elision_output,
16548                                       expected_disk,
16549                                       expected_status,
16550                                       expected_skip,
16551                                       [], True, True,
16552                                       '--depth', 'files', B_branch_path)
16553
16554  # Revert the merge and then make a prop change to A/B/E in r4.
16555  svntest.actions.run_and_verify_svn(None, [],
16556                                     'revert', '--recursive', sbox.wc_dir)
16557  svntest.actions.run_and_verify_svn(["property 'prop:name' set on '" +
16558                                      E_path + "'\n"], [], 'ps',
16559                                     'prop:name', 'propval', E_path)
16560  svntest.actions.run_and_verify_svn(None, [],
16561                                     'ci', '-m', 'A new property on a dir',
16562                                     sbox.wc_dir)
16563  svntest.actions.run_and_verify_svn(None, [],
16564                                     'up', sbox.wc_dir)
16565
16566  # Merge r4 from ^/A/B to branch/B at operational depth=immediates
16567  #
16568  # Previously this failed because the mergetracking logic didn't realize
16569  # that despite being a shallow merge, the diff only affected branch/B/E,
16570  # which was within the specified depth, so there was no need to record
16571  # non-inheritable mergeinfo or subtree mergeinfo:
16572  #
16573  #   >svn pg svn:mergeinfo -vR
16574  #   Properties on 'branch\B':
16575  #     svn:mergeinfo
16576  #       /A/B:4* <-- Should be inheritable
16577  #   Properties on 'branch\B\E':
16578  #     svn:mergeinfo
16579  #       /A/B/E:4 <-- Not necessary
16580  expected_output = wc.State(B_branch_path, {
16581    'E' : Item(status=' U'),
16582    })
16583  expected_mergeinfo_output = wc.State(B_branch_path, {
16584    ''  : Item(status=' U'),
16585    'E' : Item(status=' U'),
16586    })
16587  expected_elision_output = wc.State(B_branch_path, {
16588    'E' : Item(status=' U'),
16589    })
16590  expected_status = wc.State(B_branch_path, {
16591    ''        : Item(status=' M'),
16592    'lambda'  : Item(status='  '),
16593    'E'       : Item(status=' M'),
16594    'E/alpha' : Item(status='  '),
16595    'E/beta'  : Item(status='  '),
16596    'F'       : Item(status='  '),
16597    })
16598  expected_status.tweak(wc_rev='4')
16599  expected_disk = wc.State('', {
16600    ''          : Item(props={SVN_PROP_MERGEINFO : '/A/B:4'}),
16601    'lambda'  : Item("This is the file 'lambda'.\n"),
16602    'E'       : Item(props={'prop:name' : 'propval'}),
16603    'E/alpha' : Item("This is the file 'alpha'.\n"),
16604    'E/beta'  : Item("This is the file 'beta'.\n"),
16605    'F'       : Item(),
16606    })
16607  svntest.actions.run_and_verify_merge(B_branch_path, '3', '4',
16608                                       sbox.repo_url + '/A/B', None,
16609                                       expected_output,
16610                                       expected_mergeinfo_output,
16611                                       expected_elision_output,
16612                                       expected_disk,
16613                                       expected_status,
16614                                       expected_skip,
16615                                       [], True, True,
16616                                       '--depth', 'immediates', B_branch_path)
16617
16618#----------------------------------------------------------------------
16619# Test for issue #4132, "merge of replaced source asserts".
16620# The original use-case is the following merges, which both asserted:
16621#    svn merge -cr1295005 ^/subversion/trunk@1295000 ../src
16622#    svn merge -cr1295004 ^/subversion/trunk/@r1295004 ../src
16623@Issue(4132)
16624def svnmucc_abuse_1(sbox):
16625  "svnmucc: merge a replacement"
16626
16627  sbox.build()
16628  os.chdir(sbox.wc_dir)
16629  sbox.wc_dir = ''
16630  wc_dir = sbox.wc_dir
16631
16632  ## Using A/ as our trunk, since one cannot replace the root.
16633
16634  ## r2: open a branch
16635  sbox.simple_repo_copy('A', 'A_COPY')
16636
16637  ## r3: padding (to make the revnums-mod-10 match)
16638  sbox.simple_repo_copy('iota', 'padding')
16639
16640  ## r4: trunk: accidental change
16641  sbox.simple_append('A/mu', 'accidental change')
16642  sbox.simple_commit()
16643
16644  ## r5: fail to revert it
16645  svntest.actions.run_and_verify_svnmucc(None, [],
16646                                         '-m', 'r5',
16647                                         '-U', sbox.repo_url,
16648                                         'rm', 'A',
16649                                         'cp', 'HEAD', 'A', 'A')
16650
16651  ## r6: really revert it
16652  svntest.actions.run_and_verify_svnmucc(None, [],
16653                                         '-m', 'r6',
16654                                         '-U', sbox.repo_url,
16655                                         'rm', 'A',
16656                                         'cp', '3', 'A', 'A')
16657
16658  ## Attempt to merge that.
16659  # This used to assert:
16660  #   --- Recording mergeinfo for merge of r5 into \
16661  #     'svn-test-work/working_copies/merge_tests-125/A_COPY':
16662  #   subversion/libsvn_subr/mergeinfo.c:1172: (apr_err=235000)
16663  #   svn: E235000: In file 'subversion/libsvn_subr/mergeinfo.c' \
16664  #     line 1172: assertion failed (IS_VALID_FORWARD_RANGE(first))
16665  #
16666  # Then, prior to the fix asserted this way:
16667  #
16668  #   >svn merge -c5 ^/A@r5 A_COPY
16669  #   subversion\libsvn_client\merge.c:4871: (apr_err=235000)
16670  #   svn: E235000: In file 'subversion\libsvn_client\merge.c'
16671  #     line 4871: assertion failed (*gap_start < *gap_end)
16672  sbox.simple_update()
16673  svntest.main.run_svn(None, 'merge', '-c', 'r5', '^/A@r5',
16674                       sbox.ospath('A_COPY'))
16675
16676#----------------------------------------------------------------------
16677# Test for issue #4138 'replacement in merge source not notified correctly'.
16678@SkipUnless(server_has_mergeinfo)
16679@Issue(4138)
16680def merge_source_with_replacement(sbox):
16681  "replacement in merge source not notified correctly"
16682
16683  sbox.build()
16684  os.chdir(sbox.wc_dir)
16685  sbox.wc_dir = ''
16686  wc_dir = sbox.wc_dir
16687
16688  # Some paths we'll care about.
16689  A_path          = sbox.ospath('A')
16690  omega_path      = sbox.ospath('A/D/H/omega')
16691  A_COPY_path     = sbox.ospath('A_COPY')
16692  beta_COPY_path  = sbox.ospath('A_COPY/B/E/beta')
16693  psi_COPY_path   = sbox.ospath('A_COPY/D/H/psi')
16694  rho_COPY_path   = sbox.ospath('A_COPY/D/G/rho')
16695  omega_COPY_path = sbox.ospath('A_COPY/D/H/omega')
16696
16697  # branch A@1 to A_COPY in r2, then make a few edits under A in r3-6:
16698  wc_disk, wc_status = set_up_branch(sbox)
16699
16700  # r7 Delete A, replace it with A@5, effectively reverting the change
16701  # made to A/D/H/omega in r6:
16702  svntest.main.run_svn(None, 'up', wc_dir)
16703  svntest.main.run_svn(None, 'del', A_path)
16704  svntest.main.run_svn(None, 'copy', sbox.repo_url + '/A@5', A_path)
16705  sbox.simple_commit(message='Replace A with older version of itself')
16706
16707  # r8: Make an edit to A/D/H/omega:
16708  svntest.main.file_write(omega_path, "New content for 'omega'.\n")
16709  sbox.simple_commit(message='file edit')
16710
16711  # Update and sync merge ^/A to A_COPY.
16712  #
16713  #         text  text  text  text              text
16714  #         edit  edit  edit  edit              edit
16715  #         psi   rho   beta  omega             omega
16716  #  A@r1---r3----r4----r5----r6---X       r7---r8--------->
16717  #    |                 |                 ^          |
16718  #    |                 v                 |          |
16719  #    |                 +---replacement---+          |
16720  #   copy                                            |
16721  #    |                                          sync-merge
16722  #    |                                              |
16723  #    v                                              v
16724  #    r2---A_COPY----------------------------------------->
16725  svntest.main.run_svn(None, 'up', wc_dir)
16726  # This test previously failed because the merge notifications make it look
16727  # like r6 from ^/A was merged and recorded:
16728  #
16729  #   >svn merge ^^/A A_COPY
16730  #   --- Merging r2 through r5 into 'A_COPY':
16731  #   U    A_COPY\B\E\beta
16732  #   U    A_COPY\D\G\rho
16733  #   U    A_COPY\D\H\psi
16734  #   --- Recording mergeinfo for merge of r2 through r5 into 'A_COPY':
16735  #    U   A_COPY
16736  #   --- Merging r6 through r8 into 'A_COPY':
16737  #   U    A_COPY\D\H\omega
16738  #   --- Recording mergeinfo for merge of r6 through r8 into 'A_COPY':
16739  #   G   A_COPY
16740  expected_output = expected_merge_output(
16741    [[2,5],[7,8]],
16742    ['U    ' + beta_COPY_path  + '\n',
16743     'U    ' + rho_COPY_path   + '\n',
16744     'U    ' + omega_COPY_path + '\n',
16745     'U    ' + psi_COPY_path   + '\n',
16746     ' U   ' + A_COPY_path     + '\n',
16747     ' G   ' + A_COPY_path     + '\n',])
16748  svntest.actions.run_and_verify_svn(expected_output, [],
16749                                     'merge', sbox.repo_url + '/A',
16750                                     A_COPY_path)
16751
16752  # Misleading notifications are one thing, incorrect mergeinfo is quite
16753  # another.
16754  svntest.actions.run_and_verify_svn([A_COPY_path + ' - /A:2-5,7-8\n'],
16755                                     [], 'pg', SVN_PROP_MERGEINFO,
16756                                     '-R', A_COPY_path)
16757
16758  # Commit the above merge and then reverse merge it.  Again r6 is not
16759  # being merged and should not be part of the notifications.
16760  sbox.simple_commit()
16761  sbox.simple_update()
16762  expected_output = expected_merge_output(
16763    [[5,2],[8,7]],
16764    ['U    ' + beta_COPY_path  + '\n',
16765     'U    ' + rho_COPY_path   + '\n',
16766     'U    ' + omega_COPY_path + '\n',
16767     'U    ' + psi_COPY_path   + '\n',
16768     ' U   ' + A_COPY_path     + '\n',
16769     ' G   ' + A_COPY_path     + '\n',],
16770    elides=True)
16771  svntest.actions.run_and_verify_svn(expected_output, [],
16772                                     'merge', sbox.repo_url + '/A',
16773                                     A_COPY_path, '-r8:1')
16774
16775#----------------------------------------------------------------------
16776# Test for issue #4144 'Reverse merge with replace in source applies
16777# diffs in forward order'.
16778@SkipUnless(server_has_mergeinfo)
16779@Issue(4144)
16780def reverse_merge_with_rename(sbox):
16781  "reverse merge applies revs in reverse order"
16782
16783  sbox.build()
16784  os.chdir(sbox.wc_dir)
16785  sbox.wc_dir = ''
16786  wc_dir = sbox.wc_dir
16787
16788  # Some paths we'll care about.
16789  A_path          = sbox.ospath('A')
16790  omega_path      = sbox.ospath('trunk/D/H/omega')
16791  A_COPY_path     = sbox.ospath('A_COPY')
16792  beta_COPY_path  = sbox.ospath('A_COPY/B/E/beta')
16793  psi_COPY_path   = sbox.ospath('A_COPY/D/H/psi')
16794  rho_COPY_path   = sbox.ospath('A_COPY/D/G/rho')
16795  omega_COPY_path = sbox.ospath('A_COPY/D/H/omega')
16796
16797  # branch A@1 to A_COPY in r2, then make a few edits under A in r3-6:
16798  wc_disk, wc_status = set_up_branch(sbox)
16799
16800  # r7 - Rename ^/A to ^/trunk.
16801  svntest.actions.run_and_verify_svn(['Committing transaction...\n',
16802                                      'Committed revision 7.\n'],
16803                                     [], 'move',
16804                                     sbox.repo_url + '/A',
16805                                     sbox.repo_url + '/trunk',
16806                                     '-m', "Rename 'A' to 'trunk'")
16807  svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir)
16808
16809  # r8 - Make and edit to trunk/D/H/omega (which was also edited in r6).
16810  svntest.main.file_write(omega_path, "Edit 'omega' on trunk.\n")
16811  sbox.simple_commit(message='Another omega edit')
16812
16813  # r9 - Sync merge ^/trunk to A_COPY.
16814  svntest.actions.run_and_verify_svn(None, # Don't check stdout, we test this
16815                                           # type of merge to death elsewhere.
16816                                     [], 'merge', sbox.repo_url + '/trunk',
16817                                     A_COPY_path)
16818  sbox.simple_commit(message='Sync A_COPY with ^/trunk')
16819  svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir)
16820
16821  # Reverse merge -r9:1 from ^/trunk to A_COPY.  This should return
16822  # A_COPY to the same state it had prior to the sync merge in r2.
16823  #
16824  # This currently fails because the Subversion tries to reverse merge
16825  # -r6:1 first, then -r8:6, causing a spurious conflict on omega:
16826  #
16827  #   >svn merge ^/trunk A_COPY -r9:1 --accept=postpone
16828  #   --- Reverse-merging r6 through r2 into 'A_COPY':
16829  #   U    A_COPY\B\E\beta
16830  #   U    A_COPY\D\G\rho
16831  #   C    A_COPY\D\H\omega
16832  #   U    A_COPY\D\H\psi
16833  #   --- Recording mergeinfo for reverse merge of r6 through r2 into 'A_COPY':
16834  #    U   A_COPY
16835  #   Summary of conflicts:
16836  #     Text conflicts: 1
16837  #   ..\..\..\subversion\svn\util.c:913: (apr_err=155015)
16838  #   ..\..\..\subversion\libsvn_client\merge.c:10848: (apr_err=155015)
16839  #   ..\..\..\subversion\libsvn_client\merge.c:10812: (apr_err=155015)
16840  #   ..\..\..\subversion\libsvn_client\merge.c:8984: (apr_err=155015)
16841  #   ..\..\..\subversion\libsvn_client\merge.c:4728: (apr_err=155015)
16842  #   svn: E155015: One or more conflicts were produced while merging r6:1
16843  #   into 'C:\SVN\src-trunk-4\Debug\subversion\tests\cmdline\svn-test-work
16844  #   \working_copies\merge_tests-127\A_COPY' -- resolve all conflicts and
16845  #   rerun the merge to apply the remaining unmerged revisions
16846  expected_output = expected_merge_output(
16847    [[8,7],[6,2]],
16848    ['U    ' + beta_COPY_path  + '\n',
16849    'U    ' + rho_COPY_path   + '\n',
16850    'U    ' + omega_COPY_path + '\n',
16851    'G    ' + omega_COPY_path + '\n',
16852    'U    ' + psi_COPY_path   + '\n',
16853    ' U   ' + A_COPY_path     + '\n',
16854    ' G   ' + A_COPY_path     + '\n',], elides=True)
16855  svntest.actions.run_and_verify_svn(expected_output, [],
16856                                     'merge', sbox.repo_url + '/trunk',
16857                                     A_COPY_path, '-r9:1')
16858
16859#----------------------------------------------------------------------
16860# Test for issue #4166 'multiple merge editor drives which add then
16861# delete a subtree fail'.
16862@SkipUnless(server_has_mergeinfo)
16863@Issue(4166)
16864def merge_adds_then_deletes_subtree(sbox):
16865  "merge adds then deletes subtree"
16866
16867  sbox.build()
16868  os.chdir(sbox.wc_dir)
16869  sbox.wc_dir = ''
16870  wc_dir = sbox.wc_dir
16871
16872  # Some paths we'll care about.
16873  A_path         = sbox.ospath('A')
16874  nu_path        = sbox.ospath('A/C/nu')
16875  C_branch_path  = sbox.ospath('branch/C')
16876  nu_branch_path = sbox.ospath('branch/C/nu')
16877
16878  # Make a branch.
16879  svntest.actions.run_and_verify_svn(None, [], 'copy',
16880                                     sbox.repo_url + '/A',
16881                                     sbox.repo_url + '/branch',
16882                                     '-m', 'Make a branch.')
16883
16884  # On the branch parent: Add a file in r3 and then delete it in r4.
16885  svntest.main.file_write(nu_path, "This is the file 'nu'.\n")
16886  svntest.actions.run_and_verify_svn(None, [], 'add', nu_path)
16887  svntest.actions.run_and_verify_svn(None, [], 'ci', wc_dir,
16888                                     '-m', 'Add a file')
16889  svntest.actions.run_and_verify_svn(None, [], 'delete', nu_path)
16890  svntest.actions.run_and_verify_svn(None, [], 'ci', wc_dir,
16891                                     '-m', 'Delete a file')
16892
16893  # Merge r3 and r4 from ^/A/C to branch/C as part of one merge
16894  # command, but as separate editor drives, i.e. 'c3,4 vs. -r2:4.
16895  # These should be equivalent but the former was failing with:
16896  #
16897  #   >svn merge ^/A/C branch\C -c3,4
16898  #   --- Merging r3 into 'branch\C':
16899  #   A    branch\C\nu
16900  #   --- Recording mergeinfo for merge of r3 into 'branch\C':
16901  #    U   branch\C
16902  #   --- Merging r4 into 'branch\C':
16903  #   D    branch\C\nu
16904  #   --- Recording mergeinfo for merge of r4 into 'branch\C':
16905  #    G   branch\C
16906  #   ..\..\..\subversion\svn\util.c:913: (apr_err=155010)
16907  #   ..\..\..\subversion\libsvn_client\merge.c:10873: (apr_err=155010)
16908  #   ..\..\..\subversion\libsvn_client\merge.c:10837: (apr_err=155010)
16909  #   ..\..\..\subversion\libsvn_client\merge.c:8994: (apr_err=155010)
16910  #   ..\..\..\subversion\libsvn_client\merge.c:7923: (apr_err=155010)
16911  #   ..\..\..\subversion\libsvn_client\mergeinfo.c:257: (apr_err=155010)
16912  #   ..\..\..\subversion\libsvn_client\mergeinfo.c:97: (apr_err=155010)
16913  #   ..\..\..\subversion\libsvn_wc\props.c:2003: (apr_err=155010)
16914  #   ..\..\..\subversion\libsvn_wc\props.c:2024: (apr_err=155010)
16915  #   ..\..\..\subversion\libsvn_wc\wc_db.c:11473: (apr_err=155010)
16916  #   ..\..\..\subversion\libsvn_wc\wc_db.c:7247: (apr_err=155010)
16917  #   ..\..\..\subversion\libsvn_wc\wc_db.c:7232: (apr_err=155010)
16918  #   svn: E155010: The node 'C:\SVN\src-trunk\Debug\subversion\tests
16919  #   \cmdline\svn-test-work\working_copies\merge_tests-128\branch\C\nu'
16920  #   was not found.
16921  svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir)
16922  svntest.actions.run_and_verify_svn(
16923    expected_merge_output([[3],[4]],
16924                          ['A    ' + nu_branch_path + '\n',
16925                           'D    ' + nu_branch_path + '\n',
16926                           ' U   ' + C_branch_path + '\n',
16927                           ' G   ' + C_branch_path + '\n',]),
16928    [], 'merge', '-c3,4', sbox.repo_url + '/A/C', C_branch_path)
16929
16930#----------------------------------------------------------------------
16931# Test for issue #4169 'added subtrees with non-inheritable mergeinfo
16932# cause spurious subtree mergeinfo'.
16933@SkipUnless(server_has_mergeinfo)
16934@Issue(4169)
16935def merge_with_added_subtrees_with_mergeinfo(sbox):
16936  "merge with added subtrees with mergeinfo"
16937
16938  sbox.build()
16939  os.chdir(sbox.wc_dir)
16940  sbox.wc_dir = ''
16941  wc_dir = sbox.wc_dir
16942
16943  # Some paths we'll care about.
16944  A_path      = sbox.ospath('A')
16945  Y_path      = sbox.ospath('A/C/X/Y')
16946  Z_path      = sbox.ospath('A/C/X/Y/Z')
16947  nu_path     = sbox.ospath('A/C/X/Y/Z/nu')
16948  A_COPY_path = sbox.ospath('A_COPY')
16949  Y_COPY_path = sbox.ospath('A_COPY/C/X/Y')
16950  W_COPY_path = sbox.ospath('A_COPY/C/X/Y/Z/W')
16951  A_COPY2_path = sbox.ospath('A_COPY_2')
16952
16953  # Make two branches of ^/A and then make a few edits under A in r4-7:
16954  wc_disk, wc_status = set_up_branch(sbox, nbr_of_branches=2)
16955
16956  # r8 - Add a subtree under A.
16957  svntest.actions.run_and_verify_svn(None, [], 'mkdir', '--parents',
16958                                     Z_path)
16959  svntest.main.file_write(nu_path, "This is the file 'nu'.\n")
16960  svntest.actions.run_and_verify_svn(None, [], 'add', nu_path)
16961  svntest.actions.run_and_verify_svn(None, [], 'ci', wc_dir,
16962                                     '-m', 'Add a subtree on our "trunk"')
16963
16964  # r9 - Sync ^/A to the first branch A_COPY.
16965  svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir)
16966  svntest.actions.run_and_verify_svn(None, [], 'merge',
16967                                     sbox.repo_url + '/A', A_COPY_path)
16968  svntest.actions.run_and_verify_svn(None, [], 'ci', wc_dir,
16969                                     '-m', 'Sync ^/A to ^/A_COPY')
16970
16971  # r10 - Make some edits on the first branch.
16972  svntest.actions.run_and_verify_svn(None, [], 'ps', 'branch-prop-foo',
16973                                     'bar', Y_COPY_path)
16974  svntest.actions.run_and_verify_svn(None, [], 'mkdir', W_COPY_path)
16975  svntest.actions.run_and_verify_svn(None, [], 'ci', wc_dir,
16976                                     '-m', 'Make some edits on "branch 1"')
16977
16978  # r11 - Cherry-pick r10 on the first branch back to A, but
16979  # do so at depth=empty so non-inheritable mergeinfo is created.
16980  svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir)
16981  svntest.actions.run_and_verify_svn(None, [],
16982                                     'merge', '-c10', '--depth=empty',
16983                                     sbox.repo_url + '/A_COPY/C/X/Y', Y_path)
16984  svntest.actions.run_and_verify_svn(
16985    None, [], 'ci', wc_dir,
16986    '-m', 'Depth empty subtree cherry pick from "branch 1" to "trunk"')
16987
16988  # Sync ^/A to the second branch A_COPY_2.
16989  #
16990  # Previously this failed because spurious mergeinfo was created on
16991  # A_COPY_2/C/X/Y/Z:
16992  #
16993  #   >svn merge ^^/A A_COPY_2
16994  #   --- Merging r3 through r11 into 'A_COPY_2':
16995  #   U    A_COPY_2\B\E\beta
16996  #   A    A_COPY_2\C\X
16997  #   A    A_COPY_2\C\X\Y
16998  #   A    A_COPY_2\C\X\Y\Z
16999  #   A    A_COPY_2\C\X\Y\Z\nu
17000  #   U    A_COPY_2\D\G\rho
17001  #   U    A_COPY_2\D\H\omega
17002  #   U    A_COPY_2\D\H\psi
17003  #   --- Recording mergeinfo for merge of r3 through r11 into 'A_COPY_2':
17004  #    U   A_COPY_2
17005  #   --- Recording mergeinfo for merge of r3 through r11 into 'A_COPY_2\C\X\Y':
17006  #    G   A_COPY_2\C\X\Y
17007  #    vvvvvvvvvvvvvvvvvvvv
17008  #    U   A_COPY_2\C\X\Y\Z
17009  #    ^^^^^^^^^^^^^^^^^^^^
17010  #
17011  #   >svn pl -vR A_COPY_2
17012  #   Properties on 'A_COPY_2':
17013  #     svn:mergeinfo
17014  #       /A:3-11
17015  #   Properties on 'A_COPY_2\C\X\Y':
17016  #     branch-prop-foo
17017  #       bar
17018  #     svn:mergeinfo
17019  #       /A/C/X/Y:8-11
17020  #       /A_COPY/C/X/Y:10*
17021  #   vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
17022  #   Properties on 'A_COPY_2\C\X\Y\Z':
17023  #     svn:mergeinfo
17024  #       /A/C/X/Y/Z:8-11
17025  #   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
17026  svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir)
17027  expected_output = wc.State(A_COPY2_path, {
17028    'B/E/beta'   : Item(status='U '),
17029    'D/G/rho'    : Item(status='U '),
17030    'D/H/omega'  : Item(status='U '),
17031    'D/H/psi'    : Item(status='U '),
17032    'C/X'        : Item(status='A '),
17033    'C/X/Y'      : Item(status='A '),
17034    'C/X/Y/Z'    : Item(status='A '),
17035    'C/X/Y/Z/nu' : Item(status='A '),
17036    })
17037  expected_mergeinfo_output = wc.State(A_COPY2_path, {
17038    ''      : Item(status=' U'),
17039    'C/X/Y' : Item(status=' U'), # Added with explicit mergeinfo
17040    })
17041  expected_elision_output = wc.State(A_COPY2_path, {
17042    })
17043  expected_status = wc.State(A_COPY2_path, {
17044    ''           : Item(status=' M', wc_rev=11),
17045    'B'          : Item(status='  ', wc_rev=11),
17046    'mu'         : Item(status='  ', wc_rev=11),
17047    'B/E'        : Item(status='  ', wc_rev=11),
17048    'B/E/alpha'  : Item(status='  ', wc_rev=11),
17049    'B/E/beta'   : Item(status='M ', wc_rev=11),
17050    'B/lambda'   : Item(status='  ', wc_rev=11),
17051    'B/F'        : Item(status='  ', wc_rev=11),
17052    'C'          : Item(status='  ', wc_rev=11),
17053    'C/X'        : Item(status='A ', wc_rev='-', copied='+'),
17054    'C/X/Y'      : Item(status=' M', wc_rev='-', copied='+'),
17055    'C/X/Y/Z'    : Item(status='  ', wc_rev='-', copied='+'),
17056    'C/X/Y/Z/nu' : Item(status='  ', wc_rev='-', copied='+'),
17057    'D'          : Item(status='  ', wc_rev=11),
17058    'D/G'        : Item(status='  ', wc_rev=11),
17059    'D/G/pi'     : Item(status='  ', wc_rev=11),
17060    'D/G/rho'    : Item(status='M ', wc_rev=11),
17061    'D/G/tau'    : Item(status='  ', wc_rev=11),
17062    'D/gamma'    : Item(status='  ', wc_rev=11),
17063    'D/H'        : Item(status='  ', wc_rev=11),
17064    'D/H/chi'    : Item(status='  ', wc_rev=11),
17065    'D/H/psi'    : Item(status='M ', wc_rev=11),
17066    'D/H/omega'  : Item(status='M ', wc_rev=11),
17067    })
17068  expected_disk = wc.State('', {
17069    ''           : Item(props={SVN_PROP_MERGEINFO : '/A:3-11'}),
17070    'B'          : Item(),
17071    'mu'         : Item("This is the file 'mu'.\n"),
17072    'B/E'        : Item(),
17073    'B/E/alpha'  : Item("This is the file 'alpha'.\n"),
17074    'B/E/beta'   : Item("New content"),
17075    'B/lambda'   : Item("This is the file 'lambda'.\n"),
17076    'B/F'        : Item(),
17077    'C'          : Item(),
17078    'C/X'        : Item(),
17079    'C/X/Y'      : Item(props={
17080      SVN_PROP_MERGEINFO : '/A/C/X/Y:8-11\n/A_COPY/C/X/Y:10*',
17081      'branch-prop-foo'  : 'bar'}),
17082    'C/X/Y/Z'    : Item(),
17083    'C/X/Y/Z/nu' : Item("This is the file 'nu'.\n"),
17084    'D'          : Item(),
17085    'D/G'        : Item(),
17086    'D/G/pi'     : Item("This is the file 'pi'.\n"),
17087    'D/G/rho'    : Item("New content"),
17088    'D/G/tau'    : Item("This is the file 'tau'.\n"),
17089    'D/gamma'    : Item("This is the file 'gamma'.\n"),
17090    'D/H'        : Item(),
17091    'D/H/chi'    : Item("This is the file 'chi'.\n"),
17092    'D/H/psi'    : Item("New content"),
17093    'D/H/omega'  : Item("New content"),
17094    })
17095  expected_skip = wc.State(A_COPY_path, { })
17096  svntest.actions.run_and_verify_merge(A_COPY2_path, None, None,
17097                                       sbox.repo_url + '/A', None,
17098                                       expected_output,
17099                                       expected_mergeinfo_output,
17100                                       expected_elision_output,
17101                                       expected_disk,
17102                                       expected_status,
17103                                       expected_skip,
17104                                       check_props=True)
17105
17106#----------------------------------------------------------------------
17107@SkipUnless(server_has_mergeinfo)
17108def merge_with_externals_with_mergeinfo(sbox):
17109  "merge with externals with mergeinfo"
17110
17111  sbox.build()
17112  os.chdir(sbox.wc_dir)
17113  sbox.wc_dir = ''
17114  wc_dir = sbox.wc_dir
17115
17116  # Some paths we'll care about.
17117  A_path = sbox.ospath('A')
17118  A_COPY_path = sbox.ospath('A_COPY')
17119  file_external_path = sbox.ospath('A/file-external')
17120  mu_COPY_path = sbox.ospath('A_COPY/mu')
17121  mu_path = sbox.ospath('A/mu')
17122
17123  # Make a branch of ^/A and then make a few edits under A in r3-6:
17124  wc_disk, wc_status = set_up_branch(sbox)
17125
17126  svntest.main.file_write(mu_COPY_path, "branch edit")
17127  svntest.actions.run_and_verify_svn(None, [], 'ci', '-m',
17128                                     'file edit on the branch', wc_dir)
17129  svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir)
17130
17131  # Create a file external under 'A' and set some bogus mergeinfo
17132  # on it (the fact that this mergeinfo is bogus has no bearing on
17133  # this test).
17134  svntest.actions.run_and_verify_svn(None, [], 'propset',
17135                                     'svn:externals',
17136                                     '^/iota file-external', A_path)
17137  svntest.actions.run_and_verify_svn(None, [], 'ci', '-m',
17138                                     'set file external', wc_dir)
17139  svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir)
17140  svntest.actions.run_and_verify_svn(None, [], 'ps', SVN_PROP_MERGEINFO,
17141                                     "/bogus-mergeinfo:5", file_external_path)
17142  svntest.actions.run_and_verify_svn(None, [], 'ci', '-m',
17143                                     'set mergeinfo on file external',
17144                                     file_external_path)
17145
17146  # Sync merge ^/A to A_COPY and then reintegrate A_COPY back to A.
17147  svntest.actions.run_and_verify_svn(None, [], 'merge',
17148                                     sbox.repo_url + '/A', A_COPY_path)
17149  svntest.actions.run_and_verify_svn(None, [], 'ci', '-m',
17150                                     'sync merge', wc_dir)
17151  # This was segfaulting, see
17152  # http://svn.haxx.se/dev/archive-2012-10/0364.shtml
17153  svntest.actions.run_and_verify_svn(
17154    expected_merge_output(None,
17155                          ['U    ' + mu_path + '\n',
17156                           ' U   ' + A_path  + '\n'],
17157                          two_url=True),
17158    [], 'merge', '--reintegrate', sbox.repo_url + '/A_COPY',
17159    A_path)
17160
17161#----------------------------------------------------------------------
17162# Test merging 'binary' files with keyword expansion enabled.
17163# Tests issue #4221 'Trivial merge of a binary file with svn:keywords
17164# raises a conflict', among other cases.
17165@SkipUnless(server_has_mergeinfo)
17166@Issue(4221)
17167def merge_binary_file_with_keywords(sbox):
17168  "merge binary file with keywords"
17169
17170  sbox.build()
17171  os.chdir(sbox.wc_dir)
17172  sbox.wc_dir = ''
17173
17174  # Some binary files, and some binary files that will become text files.
17175  # 'mod_src' means a content change on the branch (the merge source);
17176  # 'mod_tgt' means a content change on the original (the merge target);
17177  # 'to_txt' means svn:mime-type removed on the branch (the merge source).
17178  file_mod_both           = 'A/B/E/alpha'
17179  file_mod_src            = 'A/D/G/pi'
17180  file_mod_tgt            = 'A/D/G/rho'
17181  file_mod_none           = 'A/D/G/tau'
17182  file_mod_both_to_txt    = 'A/B/E/beta'
17183  file_mod_src_to_txt     = 'A/D/H/chi'
17184  file_mod_tgt_to_txt     = 'A/D/H/psi'
17185  file_mod_none_to_txt    = 'A/D/H/omega'
17186  files_bin = [ file_mod_both, file_mod_src, file_mod_tgt, file_mod_none ]
17187  files_txt = [ file_mod_both_to_txt, file_mod_src_to_txt,
17188                file_mod_tgt_to_txt, file_mod_none_to_txt ]
17189  files = files_bin + files_txt
17190
17191  # make some 'binary' files with keyword expansion enabled
17192  for f in files:
17193    svntest.main.file_append(sbox.ospath(f), "With $Revision: $ keyword.\n")
17194    svntest.main.run_svn(binary_mime_type_on_text_file_warning,
17195                         'propset', 'svn:mime-type',
17196                         'application/octet-stream', sbox.ospath(f))
17197    sbox.simple_propset('svn:keywords', 'Revision', f)
17198  sbox.simple_commit()
17199
17200  # branch the files
17201  sbox.simple_repo_copy('A', 'A2')
17202  sbox.simple_update()
17203
17204  # Modify the branched (source) and/or original (target) versions. Remove
17205  # the svn:mime-type from the 'to_txt' files on the branch.
17206  # The original bug in issue #4221 gave a conflict if we modified either
17207  # version or neither (using a single-file merge test case).
17208  for f in [ file_mod_both, file_mod_both_to_txt,
17209             file_mod_src, file_mod_src_to_txt ]:
17210    f_branch = 'A2' + f[1:]
17211    svntest.main.file_append(sbox.ospath(f_branch), "Incoming mod.\n")
17212  for f in [ file_mod_both, file_mod_both_to_txt,
17213             file_mod_tgt, file_mod_tgt_to_txt ]:
17214    svntest.main.file_append(sbox.ospath(f), "Mod on merge target.\n")
17215  for f in files_txt:
17216    f_branch = 'A2' + f[1:]
17217    sbox.simple_propdel('svn:mime-type', f_branch)
17218  sbox.simple_commit()
17219  sbox.simple_update()
17220
17221  # merge back
17222  svntest.actions.run_and_verify_svn(
17223    expected_merge_output([[3,4]],
17224                          ['C    ' + sbox.ospath(file_mod_both) + '\n',
17225                           'U    ' + sbox.ospath(file_mod_src) + '\n',
17226                          #'     ' + sbox.ospath(file_mod_tgt) + '\n',
17227                          #'     ' + sbox.ospath(file_mod_none) + '\n',
17228                           'CU   ' + sbox.ospath(file_mod_both_to_txt) + '\n',
17229                           'UU   ' + sbox.ospath(file_mod_src_to_txt) + '\n',
17230                           ' U   ' + sbox.ospath(file_mod_tgt_to_txt) + '\n',
17231                           ' U   ' + sbox.ospath(file_mod_none_to_txt) + '\n',
17232                           ' U   A\n'],
17233                          text_conflicts=2),
17234    [], 'merge', '^/A2', 'A')
17235
17236#----------------------------------------------------------------------
17237# Test for issue #4155 'Merge conflict text of expanded keyword incorrect
17238# when svn:keyword property value removed'. Failed in 1.7.0 through 1.7.8.
17239@SkipUnless(server_has_mergeinfo)
17240@Issue(4155)
17241def merge_conflict_when_keywords_removed(sbox):
17242  "merge conflict when keywords removed"
17243
17244  sbox.build()
17245  os.chdir(sbox.wc_dir)
17246  sbox.wc_dir = ''
17247
17248  # make a file with keyword expansion enabled
17249  svntest.main.file_write('A/keyfile', "$Date$ $Revision$\n")
17250  sbox.simple_add('A/keyfile')
17251  sbox.simple_propset('svn:keywords', 'Date Revision', 'A/keyfile')
17252  sbox.simple_commit()
17253  sbox.simple_update()
17254
17255  # branch the file
17256  sbox.simple_repo_copy('A', 'A2')
17257  sbox.simple_update()
17258
17259  #
17260  svntest.main.file_append('A/keyfile', " some changes\n")
17261  sbox.simple_commit()
17262
17263  # sync merge
17264  svntest.actions.run_and_verify_svn(
17265    expected_merge_output([[3,4]],
17266                          ['U    '+ sbox.ospath('A2/keyfile') + '\n',
17267                           ' U   A2\n']),
17268    [], 'merge', '^/A', 'A2')
17269  sbox.simple_commit()
17270  sbox.simple_update()
17271
17272  # modify the original version: disable those KW & enable 'Id'
17273  sbox.simple_propset('svn:keywords', 'Id', 'A/keyfile')
17274  svntest.main.file_append('A/keyfile', "$Id$\n")
17275  sbox.simple_commit()
17276
17277  # sync merge again
17278  svntest.actions.run_and_verify_svn(
17279    expected_merge_output([[5,6]],
17280                          ['UU   ' + sbox.ospath('A2/keyfile') + '\n',
17281                           ' U   A2\n']),
17282    [], 'merge', '--accept=postpone', '^/A', 'A2')
17283
17284@SkipUnless(server_has_mergeinfo)
17285@Issue(4139, 3274, 3503)
17286def merge_target_selection(sbox):
17287  "merge target selection handling"
17288
17289  sbox.build()
17290  os.chdir(sbox.wc_dir)
17291  sbox.wc_dir = ''
17292
17293  # r2
17294  sbox.simple_mkdir('dir')
17295  sbox.simple_add_text('\1\2\3\4\5', 'dir/binary-file')
17296  sbox.simple_add_text('abcde', 'dir/text-file')
17297  sbox.simple_commit()
17298
17299  # r3
17300  sbox.simple_copy('dir', 'branch')
17301  sbox.simple_commit()
17302
17303  # r4
17304  svntest.main.file_write(sbox.ospath('dir/binary-file'),
17305                          '\9\8\7\6\5\4\3\2\1')
17306  sbox.simple_commit()
17307
17308  sbox.simple_update()
17309
17310  os.chdir(sbox.ospath('branch'))
17311
17312  # Merge the directory (no target)
17313  expected_output = [
17314    '--- Merging r4 into \'.\':\n',
17315    'U    binary-file\n',
17316    '--- Recording mergeinfo for merge of r4 into \'.\':\n',
17317    ' U   .\n',
17318  ]
17319  svntest.actions.run_and_verify_svn(expected_output, [],
17320                                     'merge', '^/dir', '-c', '4')
17321
17322  svntest.main.run_svn(None, 'revert', '-R', '.')
17323
17324  # Merge the file (no target)
17325  expected_output = [
17326    '--- Merging r4 into \'binary-file\':\n',
17327    'U    binary-file\n',
17328    '--- Recording mergeinfo for merge of r4 into \'binary-file\':\n',
17329    ' U   binary-file\n',
17330  ]
17331  svntest.actions.run_and_verify_svn(expected_output, [],
17332                                     'merge', '^/dir/binary-file', '-c', '4')
17333
17334  svntest.main.run_svn(None, 'revert', '-R', '.')
17335
17336  # Merge the directory (explicit target)
17337  expected_output = [
17338    '--- Merging r4 into \'.\':\n',
17339    'U    binary-file\n',
17340    '--- Recording mergeinfo for merge of r4 into \'.\':\n',
17341    ' U   .\n',
17342  ]
17343  svntest.actions.run_and_verify_svn(expected_output, [],
17344                                     'merge', '^/dir', '-c', '4', '.')
17345
17346  svntest.main.run_svn(None, 'revert', '-R', '.')
17347
17348  # Merge the file (explicit target)
17349  expected_output = [
17350    '--- Merging r4 into \'binary-file\':\n',
17351    'U    binary-file\n',
17352    '--- Recording mergeinfo for merge of r4 into \'binary-file\':\n',
17353    ' U   binary-file\n',
17354  ]
17355  svntest.actions.run_and_verify_svn(expected_output, [],
17356                                     'merge', '^/dir/binary-file', '-c', '4', 'binary-file')
17357
17358  svntest.main.run_svn(None, 'revert', '-R', '.')
17359
17360  # Merge the file (wrong target)
17361  expected_output = [
17362    '--- Merging r4 into \'.\':\n',
17363    '   C .\n',
17364    '--- Recording mergeinfo for merge of r4 into \'.\':\n',
17365    ' U   .\n',
17366  ] + svntest.main.summary_of_conflicts(tree_conflicts=1)
17367  svntest.actions.run_and_verify_svn(expected_output, [],
17368                                     'merge', '^/dir/binary-file',
17369                                     '-c', '4', '.', '--accept', 'postpone')
17370
17371  svntest.main.run_svn(None, 'revert', '-R', '.')
17372
17373  # Merge the dir (wrong target)
17374  expected_output = [
17375    '--- Merging r4 into \'binary-file\':\n',
17376    '   C %s\n' % os.path.join('binary-file'),
17377    '--- Recording mergeinfo for merge of r4 into \'binary-file\':\n',
17378    ' U   binary-file\n',
17379  ] + svntest.main.summary_of_conflicts(tree_conflicts=1)
17380  svntest.actions.run_and_verify_svn(expected_output, [],
17381                                     'merge', '^/dir', '-c', '4', 'binary-file',
17382                                     '--accept', 'postpone')
17383
17384@SkipUnless(server_has_mergeinfo)
17385@Issue(3405) # seems to be the wrong issue number
17386def merge_properties_on_adds(sbox):
17387  "merged directory properties are added"
17388
17389  sbox.build()
17390  os.chdir(sbox.wc_dir)
17391  sbox.wc_dir = ''
17392
17393  sbox.simple_copy('A/D/G', 'G')
17394
17395  sbox.simple_mkdir('A/D/G/M')
17396  sbox.simple_mkdir('A/D/G/M/N')
17397  sbox.simple_add_text('QQ', 'A/D/G/file', 'A/D/G/M/file')
17398  sbox.simple_propset('key', 'value',
17399                      'A/D/G/M', 'A/D/G/file', 'A/D/G/M/N', 'A/D/G/M/file')
17400  sbox.simple_commit()
17401  sbox.simple_update()
17402
17403  svntest.actions.run_and_verify_svn(None, [],
17404                                     'merge', '^/A/D/G', sbox.ospath('G'))
17405
17406  expected_output = svntest.verify.UnorderedOutput([
17407    'Properties on \'%s\':\n' % sbox.ospath('G'),
17408     '  svn:mergeinfo\n',
17409     'Properties on \'%s\':\n' % sbox.ospath('G/M'),
17410     '  key\n',
17411     'Properties on \'%s\':\n' % sbox.ospath('G/file'),
17412     '  key\n',
17413     'Properties on \'%s\':\n' % sbox.ospath('G/M/N'),
17414     '  key\n',
17415     'Properties on \'%s\':\n' % sbox.ospath('G/M/file'),
17416     '  key\n',
17417  ])
17418  svntest.actions.run_and_verify_svn(expected_output, [],
17419                                     'proplist', '-R', sbox.ospath('G'))
17420
17421  expected_output = svntest.verify.UnorderedOutput([
17422     'Properties on \'%s\':\n' % sbox.ospath('G/M'),
17423     '  key\n',
17424     'Properties on \'%s\':\n' % sbox.ospath('G/file'),
17425     '  key\n',
17426     'Properties on \'%s\':\n' % sbox.ospath('G/M/N'),
17427     '  key\n',
17428     'Properties on \'%s\':\n' % sbox.ospath('G/M/file'),
17429     '  key\n',
17430  ])
17431
17432  # I merged the tree, which should include history but only the files have
17433  # the properties stored in PRISTINE. All directories have the properties
17434  # as local changes in ACTUAL.
17435  svntest.actions.run_and_verify_svn(expected_output, [],
17436                                     'proplist', '-R', sbox.ospath('G'),
17437                                     '-r', 'BASE')
17438
17439  # Note that this is not a regression. This has been the case since 1.0.
17440  # ### We just made status, update and merge handle this without users
17441  # ### knowing about this limitation.
17442
17443  # ### My guess is that the base merge support on svn_wc_merge_props()
17444  # ### was originally designed to resolve this problem, but I can't
17445  # ### find a released version where this was actually implemented.
17446
17447  # For fun, also check the status: 'svn status' suppresses the M from AM.
17448
17449  # G = sbox.ospath('G')
17450  #
17451  # expected_status = wc.State('G', {
17452  #   ''           : Item(status=' M', wc_rev='2'),
17453  #   'pi'         : Item(status='  ', wc_rev='2'),
17454  #   'tau'        : Item(status='  ', wc_rev='2'),
17455  #   'file'       : Item(status='A ', copied='+', wc_rev='-'), # Copied, no changes
17456  #   'M'          : Item(status='A ', copied='+', wc_rev='-'), # Copied, changes
17457  #   'M/file'     : Item(status='  ', copied='+', wc_rev='-'), # Copied, no changes
17458  #   'M/N'        : Item(status=' M', copied='+', wc_rev='-'), # Local changes
17459  #   'rho'        : Item(status='  ', wc_rev='2'),
17460  # })
17461  # svntest.actions.run_and_verify_status(G, expected_status)
17462
17463
17464# ======================================================================
17465# Functions for parsing mergeinfo
17466
17467def parse_changes_list(changes_string):
17468  """Parse a string containing a list of revision numbers in the form
17469     of the '--change' command-line argument (e.g. '1,3,-5,7-10').
17470     Return a list of elements of the form [[1], [3], [-5], [7,10]].
17471  """
17472  rev_ranges = []
17473  for rr in changes_string.split(','):
17474    if '-' in rr[1:]:
17475      revs = rr.split('-')
17476      rev_ranges.append([int(revs[0]), int(revs[1])])
17477    else:
17478      rev_ranges.append([int(rr)])
17479  return rev_ranges
17480
17481def parse_rev_args(arg_list):
17482  """Return a list of [rX:rY] or [rZ] elements representing ARG_LIST
17483     whose elements are strings in the form '-rX:Y' or '-cZ,X-Y,...'.
17484  """
17485  rev_ranges = []
17486  for arg in arg_list:
17487    kind = arg[:2]
17488    val = arg[2:]
17489    if kind == '-r':
17490      if ':' in val:
17491        revs = map(int, val.split(':'))
17492        if revs[0] < revs[1]:
17493          rev_ranges.append([revs[0] + 1, revs[1]])
17494        else:
17495          rev_ranges.append([revs[0], revs[1] + 1])
17496      else:
17497        rev_ranges.append([int(val)])
17498    elif kind == '-c':
17499      rev_ranges.extend(parse_changes_list(val))
17500    else:
17501      raise ValueError("revision arg '" + arg + "' in '" + arg_list +
17502                       "' does not start with -r or -c")
17503  return rev_ranges
17504
17505class RangeList(list):
17506  """Represents of a list of revision ranges, as a list of one- or
17507     two-element lists, each of the form [X] meaning "--revision (X-1):X"
17508     or [X,Y] meaning "--revision (X-1):Y".
17509  """
17510  def __init__(self, arg):
17511    """
17512    """
17513    self.as_given = arg
17514    if isinstance(arg, str):
17515      list.__init__(self, parse_changes_list(arg))
17516    elif isinstance(arg, list):
17517      list.__init__(self, parse_rev_args(arg))
17518    else:
17519      raise ValueError("RangeList needs a string or a list, not '" + str(arg) + "'")
17520
17521def expected_merge_output2(tgt_ospath,
17522                           recorded_ranges,
17523                           merged_ranges=None,
17524                           prop_conflicts=0,
17525                           prop_resolved=0):
17526  """Return an ExpectedOutput instance corresponding to the expected
17527     output of a merge into TGT_OSPATH, with one 'recording
17528     mergeinfo...' notification per specified revision range in
17529     RECORDED_RANGES and one 'merging...' notification per revision
17530     range in MERGED_RANGES.
17531
17532     RECORDED_RANGES is a mergeinfo-string or a RangeList.
17533
17534     MERGED_RANGES is a list of mergeinfo-strings or a list of
17535     RangeLists.  If None, it means [[r] for r in RECORDED_RANGES].
17536  """
17537  # Convert RECORDED_RANGES to a RangeList.
17538  if isinstance(recorded_ranges, str):
17539    recorded_ranges = RangeList(recorded_ranges)
17540  # Convert MERGED_RANGES to a list of RangeLists.
17541  if merged_ranges is None:
17542    merged_ranges = [[r] for r in recorded_ranges]
17543  elif len(merged_ranges) > 0 and isinstance(merged_ranges[0], str):
17544    # List of mergeinfo-strings => list of rangelists
17545    merged_ranges = [RangeList(r) for r in merged_ranges]
17546
17547  status_letters_re = (prop_conflicts or prop_resolved) and ' [UC]' or ' U'
17548  status_letters_mi = ' [UG]'
17549  lines = []
17550  for i, rr in enumerate(recorded_ranges):
17551    # Merging ...
17552    for sr in merged_ranges[i]:
17553      revstart = sr[0]
17554      revend = len(sr) > 1 and sr[1] or None
17555      lines += [svntest.main.merge_notify_line(revstart, revend,
17556                                               target=tgt_ospath)]
17557      lines += [status_letters_re + '   ' + re.escape(tgt_ospath) + '\n']
17558    # Recording mergeinfo ...
17559    revstart = rr[0]
17560    revend = len(rr) > 1 and rr[1] or None
17561    lines += [svntest.main.mergeinfo_notify_line(revstart, revend,
17562                                                 target=tgt_ospath)]
17563    lines += [status_letters_mi + '   ' + re.escape(tgt_ospath) + '\n']
17564
17565  # Summary of conflicts
17566  lines += svntest.main.summary_of_conflicts(prop_conflicts=prop_conflicts,
17567                                             prop_resolved=prop_resolved,
17568                                             as_regex=True)
17569
17570  # The 'match_all=False' is because we also expect some
17571  # 'Resolved conflicted state of ...' lines.
17572  return RegexListOutput(lines, match_all=False)
17573
17574def expected_out_and_err(tgt_ospath,
17575                           recorded_ranges,
17576                           merged_ranges=None,
17577                           prop_conflicts=0,
17578                           prop_resolved=0,
17579                           expect_error=True):
17580  """Return a tuple (expected_out, expected_err) giving the expected
17581     output and expected error output for a merge into TGT_OSPATH. See
17582     expected_merge_output2() for details of RECORDED_RANGES and
17583     MERGED_RANGES and PROP_CONFLICTS.  EXPECT_ERROR should be true iff
17584     we expect the merge to abort with an error about conflicts being
17585     raised.
17586  """
17587  expected_out = expected_merge_output2(tgt_ospath, recorded_ranges,
17588                                        merged_ranges,
17589                                        prop_conflicts, prop_resolved)
17590  if expect_error:
17591    expected_err = RegexListOutput([
17592                     '^svn: E155015: .* conflicts were produced .* into$',
17593                     "^'.*" + re.escape(tgt_ospath) + "' --$",
17594                     '^resolve all conflicts .* remaining$',
17595                     '^unmerged revisions$'],
17596                     match_all=False)
17597  else:
17598    expected_err = []
17599
17600  return expected_out, expected_err
17601
17602def check_mergeinfo(expected_mergeinfo, tgt_ospath):
17603  """Read the mergeinfo on TGT_OSPATH; verify that it matches
17604     EXPECTED_MERGEINFO (list of lines).
17605  """
17606  svntest.actions.run_and_verify_svn(
17607    expected_mergeinfo, [], 'pg', SVN_PROP_MERGEINFO, tgt_ospath)
17608
17609def simple_merge(src_path, tgt_ospath, rev_args):
17610  """Merge from ^/SRC_PATH to TGT_OSPATH using revision arguments REV_ARGS
17611     (list of '-r...' or '-c...' strings); expect a single-target merge
17612     with no conflicts or errors.
17613  """
17614  rev_ranges = RangeList(rev_args)
17615
17616  expected_out = expected_merge_output(rev_ranges,
17617                                       [' U   ' + tgt_ospath + '\n',
17618                                        ' [UG]   ' + tgt_ospath + '\n'],
17619                                       target=tgt_ospath)
17620  src_url = '^/' + src_path
17621  svntest.actions.run_and_verify_svn(
17622    expected_out, [],
17623    'merge', src_url, tgt_ospath, '--accept', 'postpone', *rev_args)
17624
17625@SkipUnless(server_has_mergeinfo)
17626@Issue(4306)
17627# Test for issue #4306 'multiple editor drive file merges record wrong
17628# mergeinfo during conflicts'
17629def conflict_aborted_mergeinfo_described_partial_merge(sbox):
17630  "conflicted split merge can be repeated"
17631
17632  sbox.build()
17633  os.chdir(sbox.wc_dir)
17634  sbox.wc_dir = ''
17635
17636  trunk = 'A'
17637  branch = 'A2'
17638  file = 'mu'
17639  dir = 'B'
17640  trunk_file = 'A/mu'
17641  trunk_dir = 'A/B'
17642
17643  # r2: initial state
17644  for rev in range(4, 11):
17645    sbox.simple_propset('prop-' + str(rev), 'Old pval ' + str(rev),
17646                        trunk_file, trunk_dir)
17647  sbox.simple_commit()
17648
17649  # r3: branch
17650  sbox.simple_copy(trunk, branch)
17651  sbox.simple_commit()
17652
17653  zero_rev = 3
17654
17655  def edit_file_or_dir(path, rev, val):
17656    """Make a local edit to the file at PATH."""
17657    sbox.simple_propset('prop-' + str(rev), val + ' pval ' + str(rev), path)
17658
17659  # r4 through r10: simple edits
17660  for rev in range(4, 11):
17661    edit_file_or_dir(trunk_file, rev, 'Edited')
17662    edit_file_or_dir(trunk_dir, rev, 'Edited')
17663    sbox.simple_commit()
17664
17665  # r14: merge some changes to the branch so that later merges will be split
17666  svntest.actions.run_and_verify_svn(None, [], 'merge', '-c5,9',
17667                                     '^/' + trunk, sbox.ospath(branch),
17668                                     '--accept', 'theirs-conflict')
17669  sbox.simple_commit()
17670  sbox.simple_update()
17671
17672  def revert_branch():
17673    svntest.actions.run_and_verify_svn(None, [], 'revert', '-R',
17674                                       sbox.ospath(branch))
17675
17676  def try_merge(relpath, conflict_rev, rev_args,
17677                expected_out_err, expected_mi):
17678    """Revert RELPATH in the branch; make a change that will conflict
17679       with CONFLICT_REV if not None; merge RELPATH in the trunk
17680       to RELPATH in the branch using revision arguments REV_ARGS (list of
17681       '-r...' or '-c...' strings).
17682
17683       EXPECTED_OUT_ERR_MI is a tuple: (expected_out, expected_err,
17684       expected_mi).  EXPECTED_OUT and EXPECTED_ERR are instances of
17685       ExpectedOutput.
17686
17687       Expect to find mergeinfo EXPECTED_MI if not None.  EXPECTED_MI is
17688       a single mergeinfo-string.
17689    """
17690    src_path = trunk + '/' + relpath
17691    tgt_path = branch + '/' + relpath
17692    tgt_ospath = sbox.ospath(tgt_path)
17693
17694    expected_out, expected_err = expected_out_err
17695
17696    revert_branch()
17697
17698    # Arrange for the merge to conflict at CONFLICT_REV.
17699    if conflict_rev:
17700      edit_file_or_dir(tgt_path, conflict_rev, 'Conflict')
17701
17702    src_url = '^/' + src_path
17703    svntest.actions.run_and_verify_svn(
17704                      expected_out, expected_err,
17705                      'merge', src_url, tgt_ospath, '--accept', 'postpone',
17706                      *rev_args)
17707
17708    if expected_mi is not None:
17709      expected_mergeinfo = ['/' + src_path + ':' + expected_mi + '\n']
17710      check_mergeinfo(expected_mergeinfo, tgt_ospath)
17711
17712  # In a mergeinfo-aware merge, each specified revision range is split
17713  # internally into sub-ranges, to avoid any already-merged revisions.
17714  #
17715  # From white-box inspection, we see there are code paths that treat
17716  # the last specified range and the last sub-range specially.  The
17717  # first specified range or sub-range is not treated specially in terms
17718  # of the code paths, although it might be in terms of data flow.
17719  #
17720  # We test merges that raise a conflict in the first and last sub-range
17721  # of the first and last specified range.
17722
17723  for target in [file, dir]:
17724
17725    tgt_ospath = sbox.ospath(branch + '/' + target)
17726
17727    # First test: Merge "everything" to the branch.
17728    #
17729    # This merge is split into three sub-ranges: r3-4, r6-8, r10-head.
17730    # We have arranged that the merge will raise a conflict in the first
17731    # sub-range.  Since we are postponing conflict resolution, the merge
17732    # should stop after the first sub-range, allowing us to resolve and
17733    # repeat the merge at which point the next sub-range(s) can be merged.
17734    # The mergeinfo on the target then should only reflect that the first
17735    # sub-range (r3-4) has been merged.
17736    #
17737    # Previously the merge failed after merging only r3-4 (as it should)
17738    # but mergeinfo for the whole range was recorded, preventing subsequent
17739    # repeat merges from applying the rest of the source changes.
17740    expect = expected_out_and_err(tgt_ospath,
17741                                  '3-4', ['3-4'],
17742                                  prop_conflicts=1)
17743    try_merge(target, 4, [], expect, '3-5,9')
17744
17745    # Try a multiple-range merge that raises a conflict in the
17746    # first sub-range in the first specified range;
17747    expect = expected_out_and_err(tgt_ospath,
17748                                  '4', ['4'],
17749                                  prop_conflicts=1)
17750    try_merge(target, 4, ['-c4-6,8-10'], expect, '4-5,9')
17751    # last sub-range in the first specified range;
17752    expect = expected_out_and_err(tgt_ospath,
17753                                  '4-6', ['4,6'],
17754                                  prop_conflicts=1)
17755    try_merge(target, 6, ['-c4-6,8-10'], expect, '4-6,9')
17756    # first sub-range in the last specified range;
17757    expect = expected_out_and_err(tgt_ospath,
17758                                  '4-6,8', ['4,6', '8'],
17759                                  prop_conflicts=1)
17760    try_merge(target, 8, ['-c4-6,8-10'], expect, '4-6,8-9')
17761    # last sub-range in the last specified range.
17762    # (Expect no error, because 'svn merge' does not throw an error if
17763    # there is no more merging to do when a conflict occurs.)
17764    expect = expected_out_and_err(tgt_ospath,
17765                                  '4-6,8-10', ['4,6', '8,10'],
17766                                  prop_conflicts=1, expect_error=False)
17767    try_merge(target, 10, ['-c4-6,8-10'], expect, '4-6,8-10')
17768
17769    # Try similar merges but involving ranges in reverse order.
17770    expect = expected_out_and_err(tgt_ospath,
17771                                  '8', ['8'],
17772                                  prop_conflicts=1)
17773    try_merge(target, 8,  ['-c8-10,4-6'], expect, '5,8-9')
17774    expect = expected_out_and_err(tgt_ospath,
17775                                  '8-10', ['8,10'],
17776                                  prop_conflicts=1)
17777    try_merge(target, 10, ['-c8-10,4-6'], expect, '5,8-10')
17778    expect = expected_out_and_err(tgt_ospath,
17779                                  '8-10,4', ['8,10', '4'],
17780                                  prop_conflicts=1)
17781    try_merge(target, 4,  ['-c8-10,4-6'], expect, '4-5,8-10')
17782    expect = expected_out_and_err(tgt_ospath,
17783                                  '8-10,4-6', ['8,10', '4,6'],
17784                                  prop_conflicts=1, expect_error=False)
17785    try_merge(target, 6,  ['-c8-10,4-6'], expect, '4-6,8-10')
17786
17787    # Try some reverse merges, with ranges in forward and reverse order.
17788    #
17789    # Reverse merges start with all source changes merged except 5 and 9.
17790    revert_branch()
17791    simple_merge(trunk + '/' + target, sbox.ospath(branch + '/' + target),
17792                 ['-c-5,-9,4,6-8,10'])
17793    sbox.simple_commit()
17794    sbox.simple_update()
17795
17796    expect = expected_out_and_err(tgt_ospath,
17797                                  '6-4,10-8', ['-6,-4', '-10,-8'],
17798                                  expect_error=False)
17799    try_merge(target, None, ['-r6:3', '-r10:7'], expect, '7')
17800    expect = expected_out_and_err(tgt_ospath,
17801                                  '-6', ['-6'],
17802                                  prop_conflicts=1)
17803    try_merge(target, 6,  ['-r6:3', '-r10:7'], expect, '4,7-8,10')
17804    expect = expected_out_and_err(tgt_ospath,
17805                                  '6-4', ['-6,-4'],
17806                                  prop_conflicts=1)
17807    try_merge(target, 4,  ['-r6:3', '-r10:7'], expect, '7-8,10')
17808    expect = expected_out_and_err(tgt_ospath,
17809                                  '6-4,-10', ['-6,-4', '-10'],
17810                                  prop_conflicts=1)
17811    try_merge(target, 10, ['-r6:3', '-r10:7'], expect, '7-8')
17812    expect = expected_out_and_err(tgt_ospath,
17813                                  '6-4,10-8', ['-6,-4', '-10,-8'],
17814                                  prop_conflicts=1, expect_error=False)
17815    try_merge(target, 8,  ['-r6:3', '-r10:7'], expect, '7')
17816
17817@SkipUnless(server_has_mergeinfo)
17818@Issue(4310)
17819# Test for issue #4310 "each editor drive gets its own notification
17820# during 'svn merge'"
17821def multiple_editor_drive_merge_notifications(sbox):
17822  "each editor drive gets its own notification"
17823
17824  sbox.build()
17825  os.chdir(sbox.wc_dir)
17826  sbox.wc_dir = ''
17827
17828  iota_branch_path = sbox.ospath('iota-copy')
17829  C_branch_path = sbox.ospath('branch')
17830
17831  # Branch a file and a directory:
17832
17833  # r2
17834  sbox.simple_copy('iota', 'iota-copy')
17835  sbox.simple_commit()
17836
17837  # r3
17838  sbox.simple_copy('A/C', 'branch')
17839  sbox.simple_commit()
17840
17841  # r4-8 - Set five non-conflicting properties on the branch parents.
17842  for i in range(0,5):
17843    sbox.simple_propset('foo' + str(i) , 'bar', 'iota')
17844    sbox.simple_propset('foo' + str(i) , 'bar', 'A/C')
17845    sbox.simple_commit()
17846
17847  # Cherry pick merge r5 and r6 to each branch and commit.
17848  svntest.actions.run_and_verify_svn(None, [], 'merge', '^/iota',
17849                                     '-c', '5,7', iota_branch_path)
17850  svntest.actions.run_and_verify_svn(None, [], 'merge', '^/A/C',
17851                                     '-c', '5,7', C_branch_path)
17852  sbox.simple_commit()
17853
17854  # Now auto merge all eligible revisions to each branch.
17855  # First the directory target:
17856  #
17857  # TODO: We don't use run_and_verify_merge here because it has limitations
17858  # re checking the merge notification headers -- which need to be improved
17859  # at some point.
17860  svntest.actions.run_and_verify_svn(
17861    ["--- Merging r2 through r4 into '" + C_branch_path + "':\n",
17862     " U   " + C_branch_path + "\n",
17863     "--- Merging r6 into '" + C_branch_path + "':\n",
17864     " U   " + C_branch_path + "\n",
17865     "--- Merging r8 through r9 into '" + C_branch_path + "':\n",
17866     " U   " + C_branch_path + "\n",
17867     "--- Recording mergeinfo for merge of r2 through r9 into '" +
17868     C_branch_path + "':\n",
17869     " U   " + C_branch_path + "\n"],
17870    [], 'merge', sbox.repo_url + '/A/C', C_branch_path)
17871
17872  # Then the file target:
17873  # Previously this failed because only the first range notification was
17874  # printed:
17875  #
17876  #   >svn merge ^/iota iota-copy
17877  #   --- Merging r2 through r4 into 'iota-copy':
17878  #    U   iota-copy
17879  #    U   iota-copy
17880  #    U   iota-copy
17881  #   --- Recording mergeinfo for merge of r2 through r9 into 'iota-copy':
17882  #    U   iota-copy
17883  #
17884  # This is what we expect:
17885  #
17886  #   --- Merging r2 through r4 into 'iota-copy':
17887  #    U   iota-copy
17888  #   --- Merging r6 into 'iota-copy': <-- 2nd editor drive
17889  #    U   iota-copy
17890  #   --- Merging r8 through r9 into 'iota-copy': <-- 3rd editor drive
17891  #    U   iota-copy
17892  #   --- Recording mergeinfo for merge of r2 through r9 into 'iota-copy':
17893  #    U   iota-copy
17894  svntest.actions.run_and_verify_svn(
17895    ["--- Merging r2 through r4 into '" + iota_branch_path + "':\n",
17896     " U   " + iota_branch_path + "\n",
17897     "--- Merging r6 into '" + iota_branch_path + "':\n",
17898     " U   " + iota_branch_path + "\n",
17899     "--- Merging r8 through r9 into '" + iota_branch_path + "':\n",
17900     " U   " + iota_branch_path + "\n",
17901     "--- Recording mergeinfo for merge of r2 through r9 into '" +
17902     iota_branch_path + "':\n",
17903     " U   " + iota_branch_path + "\n"],
17904    [], 'merge', sbox.repo_url + '/iota', iota_branch_path)
17905
17906#----------------------------------------------------------------------
17907@SkipUnless(server_has_mergeinfo)
17908@Issue(4317)
17909# Test for issue #4317 "redundant notifications in single editor drive merge".
17910def single_editor_drive_merge_notifications(sbox):
17911  "single editor drive merge notifications"
17912  sbox.build()
17913  os.chdir(sbox.wc_dir)
17914  sbox.wc_dir = ''
17915  wc_dir = sbox.wc_dir
17916
17917  A_copy_path = sbox.ospath('A_COPY')
17918  D_copy_path = sbox.ospath('A_COPY/D')
17919  psi_copy_path = sbox.ospath('A_COPY/D/H/psi')
17920  omega_copy_path = sbox.ospath('A_COPY/D/H/omega')
17921  beta_copy_path = sbox.ospath('A_COPY/B/E/beta')
17922
17923  # r2 - r6: Copy A to A_COPY and then make some text changes under A.
17924  set_up_branch(sbox)
17925
17926  # r7 - Subtree merge
17927  svntest.actions.run_and_verify_svn(None, [], 'merge', '^/A/D',
17928                                     '-c4', D_copy_path)
17929  sbox.simple_commit()
17930  sbox.simple_update()
17931
17932  # Previously this failed because of redundant merge notifications
17933  # for r4-7:
17934  #
17935  #   >svn merge ^/A A_COPY
17936  #   --- Merging r2 through r3 into 'A_COPY\D':
17937  #   U    A_COPY\D\H\psi
17938  #   --- Merging r5 through r7 into 'A_COPY\D':
17939  #   U    A_COPY\D\H\omega
17940  #   --- Merging r4 through r7 into 'A_COPY':
17941  #   U    A_COPY\B\E\beta
17942  #   --- Recording mergeinfo for merge of r2 through r7 into 'A_COPY':
17943  #    U   A_COPY
17944  #   --- Recording mergeinfo for merge of r2 through r7 into 'A_COPY\D':
17945  #    U   A_COPY\D
17946  #   --- Eliding mergeinfo from 'A_COPY\D':
17947  #    U   A_COPY\D
17948  #
17949  # The order of 'beta' and 'omega' can vary, so use UnorderedOutput.  This
17950  # raises the possibility that the test could spuriously pass if the 'U'pdate
17951  # notifications aren't grouped with the correct headers, but that's not what
17952  # is being tested here.
17953  expected_output = svntest.verify.UnorderedOutput(
17954    ["--- Merging r2 through r3 into '" + A_copy_path + "':\n",
17955     "U    " + psi_copy_path + "\n",
17956     "--- Merging r4 through r7 into '" + A_copy_path + "':\n",
17957     "U    " + omega_copy_path + "\n",
17958     "U    " + beta_copy_path + "\n",
17959     "--- Recording mergeinfo for merge of r2 through r7 into '" +
17960     A_copy_path + "':\n",
17961     " U   " + A_copy_path + "\n",
17962     "--- Recording mergeinfo for merge of r2 through r7 into '" +
17963     D_copy_path + "':\n",
17964     " U   " + D_copy_path + "\n",
17965     "--- Eliding mergeinfo from '" + D_copy_path + "':\n",
17966     " U   " + D_copy_path + "\n"])
17967  svntest.actions.run_and_verify_svn(expected_output, [], 'merge',
17968                                     sbox.repo_url + '/A', A_copy_path)
17969
17970  # r8 and r9 - Commit and do reverse subtree merge.
17971  sbox.simple_commit()
17972  sbox.simple_update()
17973  svntest.actions.run_and_verify_svn(None, [], 'merge', '^/A/D',
17974                                     '-c-4', D_copy_path)
17975  sbox.simple_commit()
17976
17977  # Now try a reverse merge.  There should only be one notification for
17978  # r7-5:
17979  sbox.simple_update()
17980  expected_output = svntest.verify.UnorderedOutput(
17981    ["--- Reverse-merging r7 through r5 into '" + A_copy_path + "':\n",
17982     "U    " + beta_copy_path + "\n",
17983     "U    " + omega_copy_path + "\n",
17984     "--- Reverse-merging r4 through r3 into '" + A_copy_path + "':\n",
17985     "U    " + psi_copy_path + "\n",
17986     "--- Recording mergeinfo for reverse merge of r7 through r3 into '" +
17987     A_copy_path + "':\n",
17988     " U   " + A_copy_path + "\n",
17989     "--- Recording mergeinfo for reverse merge of r7 through r3 into '" +
17990     D_copy_path + "':\n",
17991     " U   " + D_copy_path + "\n",
17992     "--- Eliding mergeinfo from '" + D_copy_path + "':\n",
17993     " U   " + D_copy_path + "\n"])
17994  svntest.actions.run_and_verify_svn(expected_output, [], 'merge',
17995                                     '-r9:2', sbox.repo_url + '/A',
17996                                     A_copy_path)
17997
17998@SkipUnless(server_has_mergeinfo)
17999@Issue(4316)  # 'Merge errors out after resolving conflicts'
18000# Very similar to conflict_aborted_mergeinfo_described_partial_merge()
18001# (test number 135), except here we tell the merge to resolve the
18002# conflicts that are generated part way through a multi-revision-range
18003# merge, and we expect it to continue with the rest of the merge.
18004def conflicted_split_merge_with_resolve(sbox):
18005  "conflicted split merge with resolve"
18006
18007  sbox.build()
18008  os.chdir(sbox.wc_dir)
18009  sbox.wc_dir = ''
18010
18011  trunk = 'A'
18012  branch = 'A2'
18013  file = 'mu'
18014  dir = 'B'
18015  trunk_file = 'A/mu'
18016  trunk_dir = 'A/B'
18017
18018  # r2: initial state
18019  for rev in range(4, 11):
18020    sbox.simple_propset('prop-' + str(rev), 'Old pval ' + str(rev),
18021                        trunk_file, trunk_dir)
18022  sbox.simple_commit()
18023
18024  # r3: branch
18025  sbox.simple_update()
18026  sbox.simple_copy(trunk, branch)
18027  sbox.simple_commit()
18028
18029  zero_rev = 3
18030
18031  def edit_file_or_dir(path, rev, val):
18032    """Make a local edit to the file at PATH."""
18033    sbox.simple_propset('prop-' + str(rev), val + ' pval ' + str(rev), path)
18034
18035  # r4 through r10: simple edits
18036  for rev in range(4, 11):
18037    edit_file_or_dir(trunk_file, rev, 'Edited')
18038    edit_file_or_dir(trunk_dir, rev, 'Edited')
18039    sbox.simple_commit()
18040
18041  # r14: merge some changes to the branch so that later merges will be split
18042  svntest.actions.run_and_verify_svn(None, [], 'merge', '-c5,9',
18043                                     '^/' + trunk, sbox.ospath(branch),
18044                                     '--accept', 'theirs-conflict')
18045  sbox.simple_commit()
18046  sbox.simple_update()
18047
18048  def revert_branch():
18049    svntest.actions.run_and_verify_svn(None, [], 'revert', '-R',
18050                                       sbox.ospath(branch))
18051
18052  def try_merge(relpath, conflict_rev, rev_args,
18053                expected_out_err, expected_mi):
18054    """Revert RELPATH in the branch; make a change that will conflict
18055       with CONFLICT_REV if not None; merge RELPATH in the trunk
18056       to RELPATH in the branch using revision arguments REV_ARGS (list of
18057       '-r...' or '-c...' strings).
18058
18059       EXPECTED_OUT_ERR_MI is a tuple: (expected_out, expected_err,
18060       expected_mi).  EXPECTED_OUT and EXPECTED_ERR are instances of
18061       ExpectedOutput.
18062
18063       Expect to find mergeinfo EXPECTED_MI if not None.  EXPECTED_MI is
18064       a single mergeinfo-string.
18065    """
18066    src_path = trunk + '/' + relpath
18067    tgt_path = branch + '/' + relpath
18068    tgt_ospath = sbox.ospath(tgt_path)
18069
18070    expected_out, expected_err = expected_out_err
18071
18072    revert_branch()
18073
18074    # Arrange for the merge to conflict at CONFLICT_REV.
18075    if conflict_rev:
18076      edit_file_or_dir(tgt_path, conflict_rev, 'Conflict')
18077
18078    src_url = '^/' + src_path + '@11'
18079    svntest.actions.run_and_verify_svn(
18080                      expected_out, expected_err,
18081                      'merge', src_url, tgt_ospath, '--accept', 'mine-full',
18082                      *rev_args)
18083
18084    if expected_mi is not None:
18085      expected_mergeinfo = ['/' + src_path + ':' + expected_mi + '\n']
18086      check_mergeinfo(expected_mergeinfo, tgt_ospath)
18087
18088  # In a mergeinfo-aware merge, each specified revision range is split
18089  # internally into sub-ranges, to avoid any already-merged revisions.
18090  #
18091  # From white-box inspection, we see there are code paths that treat
18092  # the last specified range and the last sub-range specially.  The
18093  # first specified range or sub-range is not treated specially in terms
18094  # of the code paths, although it might be in terms of data flow.
18095  #
18096  # We test merges that raise a conflict in the first and last sub-range
18097  # of the first and last specified range.
18098
18099  for target in [file, dir]:
18100
18101    tgt_ospath = sbox.ospath(branch + '/' + target)
18102
18103    # First test: Merge "everything" to the branch.
18104    #
18105    # This merge is split into three sub-ranges: r3-4, r6-8, r10-head.
18106    # We have arranged that the merge will raise a conflict in the first
18107    # sub-range.  Since we are postponing conflict resolution, the merge
18108    # should stop after the first sub-range, allowing us to resolve and
18109    # repeat the merge at which point the next sub-range(s) can be merged.
18110    # The mergeinfo on the target then should only reflect that the first
18111    # sub-range (r3-4) has been merged.
18112    expect = expected_out_and_err(tgt_ospath,
18113                                  '3-4,6-11',
18114                                  ['3-4', '6-8,10-11'],
18115                                  prop_resolved=1, expect_error=False)
18116    try_merge(target, 4, [], expect, '3-11')
18117
18118    # Try a multiple-range merge that raises a conflict in the
18119    # first sub-range in the first specified range;
18120    expect = expected_out_and_err(tgt_ospath,
18121                                  '4,6,8-10',
18122                                  ['4', '6', '8,10'],
18123                                  prop_resolved=1, expect_error=False)
18124    try_merge(target, 4, ['-c4-6,8-10'], expect, '4-6,8-10')
18125    # last sub-range in the first specified range;
18126    expect = expected_out_and_err(tgt_ospath,
18127                                  '4-6,8-10', ['4,6', '8,10'],
18128                                  prop_resolved=1, expect_error=False)
18129    try_merge(target, 6, ['-c4-6,8-10'], expect, '4-6,8-10')
18130    # first sub-range in the last specified range;
18131    expect = expected_out_and_err(tgt_ospath,
18132                                  '4-6,8,10',
18133                                  ['4,6', '8', '10'],
18134                                  prop_resolved=1, expect_error=False)
18135    try_merge(target, 8, ['-c4-6,8-10'], expect, '4-6,8-10')
18136    # last sub-range in the last specified range.
18137    # (Expect no error, because 'svn merge' does not throw an error if
18138    # there is no more merging to do when a conflict occurs.)
18139    expect = expected_out_and_err(tgt_ospath,
18140                                  '4-6,8-10', ['4,6', '8,10'],
18141                                  prop_resolved=1, expect_error=False)
18142    try_merge(target, 10, ['-c4-6,8-10'], expect, '4-6,8-10')
18143
18144    # Try similar merges but involving ranges in reverse order.
18145    expect = expected_out_and_err(tgt_ospath,
18146                                  '8', ['8'],
18147                                  prop_resolved=1, expect_error=False)
18148    try_merge(target, 8,  ['-c8-10,4-6'], expect, '4-6,8-10')
18149    expect = expected_out_and_err(tgt_ospath,
18150                                  '8-10', ['8,10'],
18151                                  prop_resolved=1, expect_error=False)
18152    try_merge(target, 10, ['-c8-10,4-6'], expect, '4-6,8-10')
18153    expect = expected_out_and_err(tgt_ospath,
18154                                  '8-10,4', ['8,10', '4'],
18155                                  prop_resolved=1, expect_error=False)
18156    try_merge(target, 4,  ['-c8-10,4-6'], expect, '4-6,8-10')
18157    expect = expected_out_and_err(tgt_ospath,
18158                                  '8-10,4-6', ['8,10', '4,6'],
18159                                  prop_resolved=1, expect_error=False)
18160    try_merge(target, 6,  ['-c8-10,4-6'], expect, '4-6,8-10')
18161
18162    # Try some reverse merges, with ranges in forward and reverse order.
18163    #
18164    # Reverse merges start with all source changes merged except 5 and 9.
18165    revert_branch()
18166    simple_merge(trunk + '/' + target, sbox.ospath(branch + '/' + target),
18167                 ['-c-5,-9,4,6-8,10'])
18168    sbox.simple_commit()
18169    sbox.simple_update()
18170
18171    expect = expected_out_and_err(tgt_ospath,
18172                                  '6-4,10-8', ['-6,-4', '-10,-8'],
18173                                  expect_error=False)
18174    try_merge(target, None, ['-r6:3', '-r10:7'], expect, '7')
18175    expect = expected_out_and_err(tgt_ospath,
18176                                  '-6,-4,10-8',
18177                                  ['-6', '-4', '-10,-8'],
18178                                  prop_resolved=1, expect_error=False)
18179    try_merge(target, 6,  ['-r6:3', '-r10:7'], expect, '7')
18180    expect = expected_out_and_err(tgt_ospath,
18181                                  '6-4,10-8', ['-6,-4', '-10,-8'],
18182                                  prop_resolved=1, expect_error=False)
18183    try_merge(target, 4,  ['-r6:3', '-r10:7'], expect, '7')
18184    expect = expected_out_and_err(tgt_ospath,
18185                                  '6-4,-10,-8',
18186                                  ['-6,-4', '-10', '-8'],
18187                                  prop_resolved=1, expect_error=False)
18188    try_merge(target, 10, ['-r6:3', '-r10:7'], expect, '7')
18189    expect = expected_out_and_err(tgt_ospath,
18190                                  '6-4,10-8', ['-6,-4', '-10,-8'],
18191                                  prop_resolved=1, expect_error=False)
18192    try_merge(target, 8,  ['-r6:3', '-r10:7'], expect, '7')
18193
18194#----------------------------------------------------------------------
18195# Test for issue 4367 'merge to shallow WC, repeat merge to infinite
18196# depth WC is broken'.
18197@SkipUnless(server_has_mergeinfo)
18198@Issues(4367)
18199def merge_to_empty_target_merge_to_infinite_target(sbox):
18200  "repeat merge to infinite depth WC conflicts"
18201
18202  sbox.build()
18203  wc_dir = sbox.wc_dir
18204  wc_disk, wc_status = set_up_branch(sbox, branch_only=True)
18205  A_COPY_path = sbox.ospath('A_COPY')
18206  C_COPY_path = sbox.ospath('A_COPY/C')
18207  E_path = sbox.ospath('A/B/E')
18208  J_path = sbox.ospath('A/C/J')
18209  K_path = sbox.ospath('A/C/J/K')
18210  nu1_path = sbox.ospath('A/C/J/nu1')
18211  nu2_path = sbox.ospath('A/C/J/K/nu2')
18212  L_path = sbox.ospath('A/B/L')
18213  nu3_path = sbox.ospath('A/B/L/nu3')
18214
18215  B1_path = sbox.ospath('A/B/B1')
18216  B1a_path = sbox.ospath('A/B/B1/B1a')
18217  test1_path = sbox.ospath('A/B/B1/test.txt')
18218  test2_path = sbox.ospath('A/B/B1/B1a/test.txt')
18219
18220  C1_path = sbox.ospath('A/C/C1')
18221  test3_path = sbox.ospath('A/C/C1/test.txt')
18222
18223  # r3 - Add some subtrees:
18224  #   A /A/B/B1
18225  #   A /A/B/B1/B1a
18226  #   A /A/B/B1/B1a/test.txt
18227  #   A /A/B/B1/test.txt
18228  svntest.main.run_svn(None, 'mkdir', B1_path)
18229  svntest.main.run_svn(None, 'mkdir', B1a_path)
18230  svntest.main.file_append(test1_path, "New file.\n")
18231  svntest.main.file_append(test2_path, "New file.\n")
18232  svntest.main.run_svn(None, 'add', test1_path, test2_path)
18233  sbox.simple_commit()
18234
18235  # r4 - Add some another subtree.
18236  #   A /A/C/C1
18237  #   A /A/C/C1/test.txt
18238  svntest.main.run_svn(None, 'mkdir', C1_path)
18239  svntest.main.file_append(test3_path, "New file.\n")
18240  svntest.main.run_svn(None, 'add', test3_path)
18241  sbox.simple_commit()
18242
18243  # r5 - Delete part of the subtree added in r3.
18244  #  D /A/B/B1/B1a
18245  svntest.main.run_svn(None, 'del', B1a_path)
18246  sbox.simple_commit()
18247
18248  # r6 - Set depth of A_COPY to empty, merge all available revs from ^/A.
18249  svntest.actions.run_and_verify_svn(None, [], 'up',
18250                                     '--set-depth=empty', A_COPY_path)
18251  svntest.actions.run_and_verify_svn(None, [], 'up',
18252                                     '--set-depth=infinity', C_COPY_path)
18253  svntest.actions.run_and_verify_svn(None, [], 'merge', '^/A',
18254                                     A_COPY_path)
18255  sbox.simple_commit()
18256
18257  # Update A_COPY back to depth infinity and retry the prior merge.
18258  svntest.actions.run_and_verify_svn(None, [], 'up',
18259                                     '--set-depth=infinity', A_COPY_path)
18260
18261  expected_output = wc.State(A_COPY_path, {
18262    'B/B1'              : Item(status='A '),
18263    'B/B1/test.txt'     : Item(status='A '),
18264    'B/B1/B1a'          : Item(status='D ', prev_status='A '),
18265    'B/B1/B1a/test.txt' : Item(status='A '),
18266    })
18267  expected_mergeinfo_output = wc.State(A_COPY_path, {
18268    ''  : Item(status=' U'),
18269    'B' : Item(status=' G'),
18270    })
18271  expected_elision_output = wc.State(A_COPY_path, {
18272    'B' : Item(status=' U'),
18273    })
18274  expected_status = wc.State(A_COPY_path, {
18275    ''                  : Item(status=' M'),
18276    'B'                 : Item(status='  '),
18277    'mu'                : Item(status='  '),
18278    'B/B1'              : Item(status='A ', copied='+'),
18279    'B/B1/test.txt'     : Item(status='  ', copied='+'),
18280    'B/B1/B1a'          : Item(status='D ', copied='+'),
18281    'B/B1/B1a/test.txt' : Item(status='D ', copied='+'),
18282    'B/E'               : Item(status='  '),
18283    'B/E/alpha'         : Item(status='  '),
18284    'B/E/beta'          : Item(status='  '),
18285    'B/lambda'          : Item(status='  '),
18286    'B/F'               : Item(status='  '),
18287    'C'                 : Item(status='  '),
18288    'C/C1'              : Item(status='  '),
18289    'C/C1/test.txt'     : Item(status='  '),
18290    'D'                 : Item(status='  '),
18291    'D/G'               : Item(status='  '),
18292    'D/G/pi'            : Item(status='  '),
18293    'D/G/rho'           : Item(status='  '),
18294    'D/G/tau'           : Item(status='  '),
18295    'D/gamma'           : Item(status='  '),
18296    'D/H'               : Item(status='  '),
18297    'D/H/chi'           : Item(status='  '),
18298    'D/H/psi'           : Item(status='  '),
18299    'D/H/omega'         : Item(status='  '),
18300    })
18301  expected_status.tweak(wc_rev=6)
18302  expected_status.tweak('B/B1', 'B/B1/test.txt', 'B/B1/B1a',
18303                        'B/B1/B1a/test.txt', wc_rev='-')
18304  expected_disk = wc.State('', {
18305    ''              : Item(props={SVN_PROP_MERGEINFO : '/A:2-6'}),
18306    'B'             : Item(),
18307    'mu'            : Item("This is the file 'mu'.\n"),
18308    'B/B1'          : Item(),
18309    'B/B1/test.txt' : Item("New file.\n"),
18310    'B/E'           : Item(),
18311    'B/E/alpha'     : Item("This is the file 'alpha'.\n"),
18312    'B/E/beta'      : Item("This is the file 'beta'.\n"),
18313    'B/lambda'      : Item("This is the file 'lambda'.\n"),
18314    'B/F'           : Item(),
18315    'C'             : Item(props={SVN_PROP_MERGEINFO : '/A/C:2-5'}),
18316    'C/C1'          : Item(),
18317    'C/C1/test.txt' : Item("New file.\n"),
18318    'D'             : Item(),
18319    'D/G'           : Item(),
18320    'D/G/pi'        : Item("This is the file 'pi'.\n"),
18321    'D/G/rho'       : Item("This is the file 'rho'.\n"),
18322    'D/G/tau'       : Item("This is the file 'tau'.\n"),
18323    'D/gamma'       : Item("This is the file 'gamma'.\n"),
18324    'D/H'           : Item(),
18325    'D/H/chi'       : Item("This is the file 'chi'.\n"),
18326    'D/H/psi'       : Item("This is the file 'psi'.\n"),
18327    'D/H/omega'     : Item("This is the file 'omega'.\n"),
18328    })
18329  expected_skip = wc.State(A_COPY_path, { })
18330  svntest.actions.run_and_verify_merge(A_COPY_path, None, None,
18331                                       sbox.repo_url + '/A', None,
18332                                       expected_output,
18333                                       expected_mergeinfo_output,
18334                                       expected_elision_output,
18335                                       expected_disk,
18336                                       expected_status,
18337                                       expected_skip,
18338                                       [], True, False)
18339
18340  # Commit the merge.
18341  #sbox.simple_commit()
18342
18343def conflict_naming(sbox):
18344  "verify conflict file naming"
18345
18346  sbox.build()
18347  wc_dir = sbox.wc_dir
18348  sbox.simple_append('file.txt', 'This is the initial content\n')
18349  sbox.simple_add('file.txt')
18350  sbox.simple_commit()
18351
18352  sbox.simple_append('file.txt', 'This is the new content\n', truncate=True)
18353  sbox.simple_commit()
18354
18355  sbox.simple_append('file.txt', 'This is conflicting content\n', truncate=True)
18356
18357  # Update - no preserve ext
18358  expected_status = svntest.actions.get_virginal_state(wc_dir, 2)
18359  expected_disk = svntest.main.greek_state.copy()
18360  expected_output = svntest.wc.State(wc_dir, {
18361    'file.txt' : Item(status='C ')
18362  })
18363  expected_status.add({
18364    'file.txt' : Item(status='C ', wc_rev='2')
18365  })
18366
18367  expected_disk.add({
18368    'file.txt.r3'       : Item(contents="This is the new content\n"),
18369    'file.txt.r2'       : Item(contents="This is the initial content\n"),
18370    'file.txt'          : Item(contents="<<<<<<< .mine\n" \
18371                               "This is conflicting content\n" \
18372                               "||||||| .r3\n" \
18373                               "This is the new content\n" \
18374                               "=======\n" \
18375                               "This is the initial content\n" \
18376                               ">>>>>>> .r2\n"),
18377    'file.txt.mine'     : Item(contents="This is conflicting content\n"),
18378  })
18379  svntest.actions.run_and_verify_update(wc_dir,
18380                                        expected_output, expected_disk,
18381                                        expected_status,
18382                                        [], False,
18383                                        wc_dir, '-r', '2')
18384
18385  sbox.simple_revert('file.txt')
18386  sbox.simple_update('', revision=3)
18387  sbox.simple_append('file.txt', 'This is conflicting content\n', truncate=True)
18388
18389  # Update - preserve ext
18390  expected_status = svntest.actions.get_virginal_state(wc_dir, 2)
18391  expected_disk = svntest.main.greek_state.copy()
18392  expected_output = svntest.wc.State(wc_dir, {
18393    'file.txt' : Item(status='C ')
18394  })
18395  expected_status.add({
18396    'file.txt' : Item(status='C ', wc_rev='2')
18397  })
18398
18399  expected_disk.add({
18400    'file.txt.r3.txt'   : Item(contents="This is the new content\n"),
18401    'file.txt.r2.txt'   : Item(contents="This is the initial content\n"),
18402    'file.txt'          : Item(contents="<<<<<<< .mine.txt\n" \
18403                               "This is conflicting content\n" \
18404                               "||||||| .r3.txt\n" \
18405                               "This is the new content\n" \
18406                               "=======\n" \
18407                               "This is the initial content\n" \
18408                               ">>>>>>> .r2.txt\n"),
18409    'file.txt.mine.txt' : Item(contents="This is conflicting content\n"),
18410  })
18411  svntest.actions.run_and_verify_update(
18412                      wc_dir,
18413                      expected_output, expected_disk, expected_status,
18414                      [], False,
18415                      wc_dir, '-r', '2',
18416                      '--config-option',
18417                      'config:miscellany:preserved-conflict-file-exts=' +
18418                      'c txt h')
18419
18420  sbox.simple_revert('file.txt')
18421  sbox.simple_update('', revision=3)
18422  sbox.simple_append('file.txt', 'This is conflicting content\n', truncate=True)
18423
18424  # Merge - no preserve ext
18425  expected_status = svntest.actions.get_virginal_state(wc_dir, 3)
18426  expected_disk = svntest.main.greek_state.copy()
18427  expected_status.add({
18428    'file.txt' : Item(status='C ', wc_rev='3')
18429  })
18430  expected_disk.add({
18431    'file.txt.merge-left.r3' : Item(contents="This is the new content\n"),
18432    'file.txt.merge-right.r2': Item(contents="This is the initial content\n"),
18433    'file.txt'               : Item(contents="<<<<<<< .working\n" \
18434                                    "This is conflicting content\n" \
18435                                    "||||||| .merge-left.r3\n" \
18436                                    "This is the new content\n" \
18437                                    "=======\n" \
18438                                    "This is the initial content\n" \
18439                                    ">>>>>>> .merge-right.r2\n"),
18440    'file.txt.working'       : Item(contents="This is conflicting content\n"),
18441  })
18442
18443  svntest.actions.run_and_verify_svn(None, [],
18444                                     'merge', '-c-3', '^/', sbox.ospath(''))
18445  svntest.actions.run_and_verify_status(wc_dir, expected_status)
18446  svntest.actions.verify_disk(wc_dir, expected_disk)
18447
18448  sbox.simple_revert('file.txt')
18449  sbox.simple_append('file.txt', 'This is conflicting content\n', truncate=True)
18450
18451  # Merge - preserve ext
18452  expected_status = svntest.actions.get_virginal_state(wc_dir, 3)
18453  expected_disk = svntest.main.greek_state.copy()
18454  expected_status.add({
18455    'file.txt' : Item(status='C ', wc_rev='3')
18456  })
18457  expected_disk.add({
18458    'file.txt.merge-left.r3.txt' : Item(contents="This is the new content\n"),
18459    'file.txt.merge-right.r2.txt': Item(contents="This is the initial content\n"),
18460    'file.txt'                   : Item(contents="<<<<<<< .working.txt\n" \
18461                                        "This is conflicting content\n" \
18462                                        "||||||| .merge-left.r3.txt\n" \
18463                                        "This is the new content\n" \
18464                                        "=======\n" \
18465                                        "This is the initial content\n" \
18466                                        ">>>>>>> .merge-right.r2.txt\n"),
18467    'file.txt.working.txt'       : Item(contents="This is conflicting content\n"),
18468  })
18469
18470  svntest.actions.run_and_verify_svn(
18471                           None, [],
18472                           'merge', '-c-3', '^/', sbox.ospath(''),
18473                           '--config-option',
18474                           'config:miscellany:preserved-conflict-file-exts=' +
18475                           'c txt h')
18476  svntest.actions.run_and_verify_status(wc_dir, expected_status)
18477  svntest.actions.verify_disk(wc_dir, expected_disk)
18478
18479def merge_dir_delete_force(sbox):
18480  "merge a directory delete with --force"
18481
18482  sbox.build()
18483
18484  sbox.simple_rm('A/D/G')
18485  sbox.simple_commit() # r2
18486
18487  sbox.simple_update(revision=1)
18488
18489  # Just merging r2 on r1 succeeds
18490  svntest.actions.run_and_verify_svn(None, [],
18491                                     'merge', '-c2', '^/', sbox.wc_dir,
18492                                     '--ignore-ancestry')
18493
18494  # Bring working copy to r1 again
18495  svntest.actions.run_and_verify_svn(None, [],
18496                                     'revert', '-R', sbox.wc_dir)
18497
18498  # But when using --force this same merge caused a segfault in 1.8.0-1.8.8
18499  svntest.actions.run_and_verify_svn(None, [],
18500                                     'merge', '-c2', '^/', sbox.wc_dir,
18501                                     '--ignore-ancestry', '--force')
18502
18503# Issue #4859: Merge removing a folder with non-inheritable mergeinfo ->
18504# E155023: can't set properties: invalid status for updating properties
18505@Issue(4859)
18506def merge_deleted_folder_with_mergeinfo(sbox):
18507  "merge deleted folder with mergeinfo"
18508
18509  sbox.build()
18510
18511  was_cwd = os.getcwd()
18512  os.chdir(sbox.wc_dir)
18513  sbox.wc_dir = ''
18514
18515  # Some non-inheritable mergeinfo
18516  sbox.simple_propset('svn:mergeinfo', '/A/C:1*', 'A/D')
18517  sbox.simple_commit() # r2
18518
18519  # Branching
18520  sbox.simple_repo_copy('A', 'branch_A')  # r3
18521  sbox.simple_update()
18522
18523  # On branch, remove a folder that has non-inheritable mergeinfo
18524  sbox.simple_rm('branch_A/D')
18525  sbox.simple_commit() # r4
18526
18527  sbox.simple_update()
18528
18529  # A merge that removes that folder
18530  # (merge revision 4 only from 'branch_A' to 'A')
18531  expected_output = wc.State(sbox.ospath(''), {
18532    'A/D'    : Item(status='D '),
18533    })
18534  expected_mergeinfo_output = wc.State(sbox.ospath(''), {
18535    'A'      : Item(status=' U'),
18536    })
18537  expected_status = svntest.actions.get_virginal_state(sbox.ospath('A'), 4).subtree('A')
18538  expected_status.add({ '': Item(status=' M', wc_rev=4) })
18539  expected_status.tweak_some(
18540    lambda path, item: [True] if path.split('/')[0] == 'D' else [],
18541    status='D ')
18542  svntest.actions.run_and_verify_merge(sbox.ospath('A'), 3, 4,
18543                                       '^/branch_A', None,
18544                                       expected_output,
18545                                       expected_mergeinfo_output,
18546                                       None,
18547                                       None,
18548                                       expected_status,
18549                                       wc.State('', {}),
18550                                       [],
18551                                       check_props=False,
18552                                       dry_run=False  # as dry run is broken
18553                                       )
18554
18555  os.chdir(was_cwd)
18556
18557# Issue #4859: Merge removing a folder with non-inheritable mergeinfo ->
18558# E155023: can't set properties: invalid status for updating properties
18559#
18560# In this test we split the merge into two separate operable parts, a
18561# delete followed later by an add, to check it will set the mergeinfo on the
18562# subtree paths if the deleted folder is later replaced within the same
18563# overall merge.
18564@Issue(4859)
18565def merge_deleted_folder_with_mergeinfo_2(sbox):
18566  "merge deleted folder with mergeinfo 2"
18567
18568  sbox.build()
18569
18570  was_cwd = os.getcwd()
18571  os.chdir(sbox.wc_dir)
18572  sbox.wc_dir = ''
18573
18574  # Some non-inheritable mergeinfo
18575  sbox.simple_propset('svn:mergeinfo', '/A/C:1*', 'A/D')
18576  sbox.simple_commit() # r2
18577
18578  # Branching
18579  sbox.simple_repo_copy('A', 'branch_A')  # r3
18580  sbox.simple_update()
18581
18582  # On branch, remove a folder that has non-inheritable mergeinfo
18583  sbox.simple_rm('branch_A/D')
18584  sbox.simple_commit() # r4
18585
18586  # A commit that we don't want to merge from the branch, to split the merge
18587  # into two separate operable parts.
18588  sbox.simple_mkdir('branch_A/IgnoreThis')
18589  sbox.simple_commit() # r5
18590
18591  # On branch, replace the deleted folder with a new one, with mergeinfo,
18592  # to check we don't omit setting mergeinfo on this.
18593  sbox.simple_mkdir('branch_A/D')
18594  sbox.simple_propset('svn:mergeinfo', '/branch_B/C:1*', 'branch_A/D')
18595  sbox.simple_mkdir('branch_A/D/G', 'branch_A/D/G2')
18596  sbox.simple_propset('svn:mergeinfo', '/branch_B/C/G:1*', 'branch_A/D/G')
18597  sbox.simple_propset('svn:mergeinfo', '/branch_B/C/G2:1*', 'branch_A/D/G2')
18598  sbox.simple_commit() # r6
18599
18600  sbox.simple_propset('svn:mergeinfo', '/branch_A:5', 'A')
18601  sbox.simple_commit() # r7
18602
18603  sbox.simple_update()
18604
18605  # A merge that removes that folder
18606  expected_output = wc.State(sbox.ospath(''), {
18607    'A/D'    : Item(status='A ', prev_status='D '),
18608    'A/D/G'  : Item(status='A '),
18609    'A/D/G2' : Item(status='A '),
18610    })
18611  # verify that mergeinfo is set/changed on A/D, A/D/G, A/D/G2.
18612  expected_mergeinfo_output = wc.State(sbox.ospath(''), {
18613    'A'      : Item(status=' U'),
18614    'A/D'    : Item(status=' G'),
18615    'A/D/G'  : Item(status=' G'),
18616    'A/D/G2' : Item(status=' G'),
18617    })
18618  expected_status = svntest.actions.get_virginal_state(sbox.ospath('A'), 7).subtree('A')
18619  expected_status.tweak_some(
18620    lambda path, item: [True] if path.split('/')[0] == 'D' else [],
18621    status='D ')
18622  expected_status.add({
18623    ''     : Item(status=' M', wc_rev=7),
18624    'D'    : Item(status='RM', copied='+', wc_rev='-'),
18625    'D/G'  : Item(status=' M', copied='+', wc_rev='-'),
18626    'D/G2' : Item(status=' M', copied='+', wc_rev='-'),
18627    })
18628  svntest.actions.run_and_verify_merge(sbox.ospath('A'), None, None,
18629                                       '^/branch_A', None,
18630                                       expected_output,
18631                                       expected_mergeinfo_output,
18632                                       None,
18633                                       None,
18634                                       expected_status,
18635                                       wc.State('', {}),
18636                                       [],
18637                                       check_props=False,
18638                                       dry_run=False  # as dry run is broken
18639                                       )
18640
18641  # verify that mergeinfo is set/changed on A/D, A/D/G, A/D/G2.
18642
18643  # NOTE: When writing out multi-line prop values in svn:* props, the
18644  # client converts to local encoding and local eol style.
18645  # Therefore, the expected output must contain the right kind of eoln
18646  # strings. That's why we use os.linesep in the tests below, not just
18647  # plain '\n'.
18648
18649  expected_mergeinfo = [
18650    ('A',       ['/branch_A:3-7']),
18651    ('A/D',     ['/branch_A/D:5-7'+os.linesep, '/branch_B/C:1*']),
18652    ('A/D/G',   ['/branch_A/D/G:5-7'+os.linesep, '/branch_B/C/G:1*']),
18653    ('A/D/G2',  ['/branch_A/D/G2:5-7'+os.linesep, '/branch_B/C/G2:1*']),
18654    ]
18655  for path, mergeinfo in expected_mergeinfo:
18656    svntest.actions.check_prop('svn:mergeinfo', sbox.ospath(path),
18657                               [m.encode() for m in mergeinfo])
18658
18659  os.chdir(was_cwd)
18660
18661########################################################################
18662# Run the tests
18663
18664
18665# list all tests here, starting with None:
18666test_list = [ None,
18667              textual_merges_galore,
18668              add_with_history,
18669              simple_property_merges,
18670              merge_with_implicit_target_using_r,
18671              merge_with_implicit_target_using_c,
18672              merge_with_implicit_target_and_revs,
18673              merge_similar_unrelated_trees,
18674              merge_with_prev,
18675              merge_binary_file,
18676              merge_one_file_using_r,
18677              merge_one_file_using_c,
18678              merge_one_file_using_implicit_revs,
18679              merge_record_only,
18680              merge_in_new_file_and_diff,
18681              merge_skips_obstructions,
18682              merge_into_missing,
18683              dry_run_adds_file_with_prop,
18684              merge_binary_with_common_ancestry,
18685              merge_funny_chars_on_path,
18686              merge_keyword_expansions,
18687              merge_prop_change_to_deleted_target,
18688              merge_file_with_space_in_its_name,
18689              merge_dir_branches,
18690              safe_property_merge,
18691              property_merge_from_branch,
18692              property_merge_undo_redo,
18693              cherry_pick_text_conflict,
18694              merge_file_replace,
18695              merge_dir_replace,
18696              merge_dir_and_file_replace,
18697              merge_file_replace_to_mixed_rev_wc,
18698              merge_ignore_whitespace,
18699              merge_ignore_eolstyle,
18700              merge_conflict_markers_matching_eol,
18701              merge_eolstyle_handling,
18702              avoid_repeated_merge_using_inherited_merge_info,
18703              avoid_repeated_merge_on_subtree_with_merge_info,
18704              obey_reporter_api_semantics_while_doing_subtree_merges,
18705              mergeinfo_inheritance,
18706              mergeinfo_elision,
18707              mergeinfo_inheritance_and_discontinuous_ranges,
18708              merge_to_target_with_copied_children,
18709              merge_to_switched_path,
18710              merge_to_path_with_switched_children,
18711              merge_with_implicit_target_file,
18712              empty_mergeinfo,
18713              prop_add_to_child_with_mergeinfo,
18714              foreign_repos_does_not_update_mergeinfo,
18715              avoid_reflected_revs,
18716              update_loses_mergeinfo,
18717              merge_loses_mergeinfo,
18718              single_file_replace_style_merge_capability,
18719              merge_to_out_of_date_target,
18720              merge_with_depth_files,
18721              merge_away_subtrees_noninheritable_ranges,
18722              merge_to_sparse_directories,
18723              merge_old_and_new_revs_from_renamed_dir,
18724              merge_with_child_having_different_rev_ranges_to_merge,
18725              merge_old_and_new_revs_from_renamed_file,
18726              merge_with_auto_rev_range_detection,
18727              cherry_picking,
18728              propchange_of_subdir_raises_conflict,
18729              reverse_merge_prop_add_on_child,
18730              merge_target_with_non_inheritable_mergeinfo,
18731              self_reverse_merge,
18732              ignore_ancestry_and_mergeinfo,
18733              merge_from_renamed_branch_fails_while_avoiding_repeat_merge,
18734              merge_source_normalization_and_subtree_merges,
18735              new_subtrees_should_not_break_merge,
18736              dont_add_mergeinfo_from_own_history,
18737              merge_range_predates_history,
18738              foreign_repos,
18739              foreign_repos_uuid,
18740              foreign_repos_2_url,
18741              merge_added_subtree,
18742              merge_unknown_url,
18743              reverse_merge_away_all_mergeinfo,
18744              dont_merge_revs_into_subtree_that_predate_it,
18745              merge_chokes_on_renamed_subtrees,
18746              dont_explicitly_record_implicit_mergeinfo,
18747              merge_broken_link,
18748              subtree_merges_dont_intersect_with_targets,
18749              subtree_source_missing_in_requested_range,
18750              subtrees_with_empty_mergeinfo,
18751              commit_to_subtree_added_by_merge,
18752              del_identical_file,
18753              del_sched_add_hist_file,
18754              subtree_merges_dont_cause_spurious_conflicts,
18755              merge_target_and_subtrees_need_nonintersecting_ranges,
18756              merge_two_edits_to_same_prop,
18757              merge_an_eol_unification_and_set_svn_eol_style,
18758              merge_adds_mergeinfo_correctly,
18759              natural_history_filtering,
18760              subtree_gets_changes_even_if_ultimately_deleted,
18761              no_self_referential_filtering_on_added_path,
18762              merge_range_prior_to_rename_source_existence,
18763              dont_merge_gaps_in_history,
18764              mergeinfo_deleted_by_a_merge_should_disappear,
18765              noop_file_merge,
18766              handle_gaps_in_implicit_mergeinfo,
18767              copy_then_replace_via_merge,
18768              record_only_merge,
18769              merge_automatic_conflict_resolution,
18770              skipped_files_get_correct_mergeinfo,
18771              committed_case_only_move_and_revert,
18772              merge_into_wc_for_deleted_branch,
18773              foreign_repos_del_and_props,
18774              immediate_depth_merge_creates_minimal_subtree_mergeinfo,
18775              record_only_merge_creates_self_referential_mergeinfo,
18776              dav_skelta_mode_causes_spurious_conflicts,
18777              merge_into_locally_added_file,
18778              merge_into_locally_added_directory,
18779              merge_with_os_deleted_subtrees,
18780              no_self_referential_or_nonexistent_inherited_mergeinfo,
18781              subtree_merges_inherit_invalid_working_mergeinfo,
18782              merge_change_to_file_with_executable,
18783              dry_run_merge_conflicting_binary,
18784              foreign_repos_prop_conflict,
18785              merge_adds_subtree_with_mergeinfo,
18786              reverse_merge_adds_subtree,
18787              merged_deletion_causes_tree_conflict,
18788              record_only_merge_adds_new_subtree_mergeinfo,
18789              unnecessary_noninheritable_mergeinfo_missing_subtrees,
18790              unnecessary_noninheritable_mergeinfo_shallow_merge,
18791              svnmucc_abuse_1,
18792              merge_source_with_replacement,
18793              reverse_merge_with_rename,
18794              merge_adds_then_deletes_subtree,
18795              merge_with_added_subtrees_with_mergeinfo,
18796              merge_with_externals_with_mergeinfo,
18797              merge_binary_file_with_keywords,
18798              merge_conflict_when_keywords_removed,
18799              merge_target_selection,
18800              merge_properties_on_adds,
18801              conflict_aborted_mergeinfo_described_partial_merge,
18802              multiple_editor_drive_merge_notifications,
18803              single_editor_drive_merge_notifications,
18804              conflicted_split_merge_with_resolve,
18805              merge_to_empty_target_merge_to_infinite_target,
18806              conflict_naming,
18807              merge_dir_delete_force,
18808              merge_deleted_folder_with_mergeinfo,
18809              merge_deleted_folder_with_mergeinfo_2,
18810             ]
18811
18812if __name__ == '__main__':
18813  svntest.main.run_tests(test_list)
18814  # NOTREACHED
18815
18816
18817### End of file.
18818