~ubuntu-branches/debian/sid/subversion/sid

« back to all changes in this revision

Viewing changes to subversion/tests/cmdline/merge_automatic_tests.py

  • Committer: Package Import Robot
  • Author(s): James McCoy, Peter Samuelson, James McCoy
  • Date: 2014-01-12 19:48:33 UTC
  • mfrom: (0.2.10)
  • Revision ID: package-import@ubuntu.com-20140112194833-w3axfwksn296jn5x
Tags: 1.8.5-1
[ Peter Samuelson ]
* New upstream release.  (Closes: #725787) Rediff patches:
  - Remove apr-abi1 (applied upstream), rename apr-abi2 to apr-abi
  - Remove loosen-sqlite-version-check (shouldn't be needed)
  - Remove java-osgi-metadata (applied upstream)
  - svnmucc prompts for a changelog if none is provided. (Closes: #507430)
  - Remove fix-bdb-version-detection, upstream uses "apu-config --dbm-libs"
  - Remove ruby-test-wc (applied upstream)
  - Fix “svn diff -r N file” when file has svn:mime-type set.
    (Closes: #734163)
  - Support specifying an encoding for mod_dav_svn's environment in which
    hooks are run.  (Closes: #601544)
  - Fix ordering of “svnadmin dump” paths with certain APR versions.
    (Closes: #687291)
  - Provide a better error message when authentication fails with an
    svn+ssh:// URL.  (Closes: #273874)
  - Updated Polish translations.  (Closes: #690815)

[ James McCoy ]
* Remove all traces of libneon, replaced by libserf.
* patches/sqlite_3.8.x_workaround: Upstream fix for wc-queries-test test
  failurse.
* Run configure with --with-apache-libexecdir, which allows removing part of
  patches/rpath.
* Re-enable auth-test as upstream has fixed the problem of picking up
  libraries from the environment rather than the build tree.
  (Closes: #654172)
* Point LD_LIBRARY_PATH at the built auth libraries when running the svn
  command during the build.  (Closes: #678224)
* Add a NEWS entry describing how to configure mod_dav_svn to understand
  UTF-8.  (Closes: #566148)
* Remove ancient transitional package, libsvn-ruby.
* Enable compatibility with Sqlite3 versions back to Wheezy.
* Enable hardening flags.  (Closes: #734918)
* patches/build-fixes: Enable verbose build logs.
* Build against the default ruby version.  (Closes: #722393)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#!/usr/bin/env python
 
2
#
 
3
#  merge_automatic_tests.py:  testing "automatic merge" scenarios
 
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
 
28
import shutil, sys, re, os
 
29
import time
 
30
 
 
31
# Our testing module
 
32
import svntest
 
33
from svntest import main, wc, verify, actions
 
34
 
 
35
# (abbreviation)
 
36
Item = wc.StateItem
 
37
Skip = svntest.testcase.Skip_deco
 
38
SkipUnless = svntest.testcase.SkipUnless_deco
 
39
XFail = svntest.testcase.XFail_deco
 
40
Issues = svntest.testcase.Issues_deco
 
41
Issue = svntest.testcase.Issue_deco
 
42
Wimp = svntest.testcase.Wimp_deco
 
43
 
 
44
from svntest.main import SVN_PROP_MERGEINFO
 
45
from svntest.main import server_has_mergeinfo
 
46
from merge_tests import local_path
 
47
from merge_tests import expected_merge_output
 
48
from merge_tests import svn_merge
 
49
from merge_tests import set_up_branch
 
50
 
 
51
#----------------------------------------------------------------------
 
52
 
 
53
# Merging scenarios to test
 
54
#
 
55
#   Merge once
 
56
#
 
57
#     A (--?---
 
58
#       (    \
 
59
#     B (--?--x
 
60
#
 
61
#   Merge twice in same direction
 
62
#
 
63
#     A (--o-----?---
 
64
#       (    \     \
 
65
#     B (--o--x--?--x
 
66
#
 
67
#   Merge to and fro
 
68
#
 
69
#     A (--o-----?--x
 
70
#       (    \     /
 
71
#     B (--o--x--?---
 
72
#
 
73
#     A (--o-----o-----?--x
 
74
#       (    \     \     /
 
75
#     B (--o--x--o--x--?---
 
76
#
 
77
#     A (--o-----o--x--?--x
 
78
#       (    \     /     /
 
79
#     B (--o--x--o-----?---
 
80
#
 
81
#     A (--o-----o--x--?---
 
82
#       (    \     /     \
 
83
#     B (--o--x--o-----?--x
 
84
#
 
85
#   Merge with cherry-picks
 
86
#   (This set of six cases represents all of the topologically distinct
 
87
#   scenarios involving one cherry-pick between two automatic merges.)
 
88
#
 
89
#     Cherry1, fwd
 
90
#     A (--o-----o-[o]----o---
 
91
#       (    \        \     \
 
92
#     B (--o--x--?-----c-----x
 
93
#
 
94
#     Cherry2, fwd
 
95
#     A (--o-----?-----c--o---
 
96
#       (    \        /     \
 
97
#     B (--o--x--o-[o]-------x
 
98
#
 
99
#     Cherry3, fwd
 
100
#     A (--o-----?-------c--o----
 
101
#       (    \_____     /     \
 
102
#       (          \   /       \
 
103
#     B (--o--o-[o]-x-/---------x
 
104
#                 \__/
 
105
#
 
106
#     Cherry1, back
 
107
#     A (--o-----o-[o]-------x
 
108
#       (    \        \     /
 
109
#     B (--o--x--?-----c--o---
 
110
#
 
111
#     Cherry2, back
 
112
#     A (--o-----?-----c-----x
 
113
#       (    \        /     /
 
114
#     B (--o--x--o-[o]----o---
 
115
#
 
116
#     Cherry3, back
 
117
#     A (--o-----?-------c------x
 
118
#       (    \_____     /      /
 
119
#       (          \   /      /
 
120
#     B (--o--o-[o]-x-/-----o----
 
121
#                 \__/
 
122
#
 
123
#   Criss-cross merge
 
124
#
 
125
#     A (--o--?--x--?----
 
126
#       (    \ /     \
 
127
#       (     X       \
 
128
#       (    / \       \
 
129
#     B (--o--?--x--?---x
 
130
#
 
131
#   Subtree mergeinfo
 
132
#
 
133
#     subtree to, fro
 
134
#     A (--o-o-o-o---------x
 
135
#       ( \         \     /
 
136
#       (  \         \   /
 
137
#     B (   o--o------s--
 
138
#
 
139
#     merge to, reverse cherry subtree to, merge to
 
140
#     A (--o-o-o-o------------------
 
141
#       ( \         \        \     \
 
142
#       (  \         \        \     \
 
143
#     B (   o--o------x-------rcs----x
 
144
#
 
145
#   Sparse WC
 
146
#
 
147
#     ...
 
148
#
 
149
#   Mixed-rev WC
 
150
#
 
151
#     ...
 
152
#
 
153
#
 
154
# Key to diagrams:
 
155
#
 
156
#   o   - an original change
 
157
#   ?   - an original change or no-op (test both)
 
158
#   x   - a branch root merge
 
159
#   c   - a cherry-pick merge
 
160
#   [o] - source range of a cherry-pick merge
 
161
#   s   - a subtree merge
 
162
#   r   - reverse merge
 
163
 
 
164
 
 
165
########################################################################
 
166
 
 
167
def assert_equal(a, b):
 
168
  """Assert that two generic Python objects are equal.  If not, raise an
 
169
     exception giving their values.  Rationale:  During debugging, it's
 
170
     easier to see what's wrong if we see the values rather than just
 
171
     an indication that the assertion failed."""
 
172
  if a != b:
 
173
    raise Exception("assert_equal failed: a = (%s), b = (%s)" % (a, b))
 
174
 
 
175
def logical_changes_in_branch(sbox, branch):
 
176
  """Return the set of logical changes that are actually in branch BRANCH
 
177
     (at its current working version), by examining the state of the
 
178
     branch files and directories rather than its mergeinfo.
 
179
 
 
180
     Each logical change is described by its branch and revision number
 
181
     as a string such as 'A1'."""
 
182
  changes = set()
 
183
  for propname in sbox.simple_proplist(branch + '/D').keys():
 
184
    if propname.startswith('prop-'):
 
185
      changes.add(propname[5:])
 
186
  return changes
 
187
 
 
188
def get_mergeinfo_change(sbox, target):
 
189
  """Return a list of revision numbers representing the mergeinfo change
 
190
  on TARGET (working version against base).  Non-recursive."""
 
191
  exit, out, err = actions.run_and_verify_svn(None, None, [],
 
192
                                              'diff', '--depth=empty',
 
193
                                              sbox.ospath(target))
 
194
  merged_revs = []
 
195
  for line in out:
 
196
    match = re.match(r'   Merged /(\w+):r(.*)', line)
 
197
    if match:
 
198
      for r_range in match.group(2).split(','):
 
199
        if '-' in r_range:
 
200
          r_start, r_end = r_range.split('-')
 
201
        else:
 
202
          r_start = r_end = r_range
 
203
        merged_revs += range(int(r_start), int(r_end) + 1)
 
204
  return merged_revs
 
205
 
 
206
def get_3ways_from_output(output):
 
207
  """Scan the list of lines OUTPUT for indications of 3-way merges.
 
208
     Return a list of (base, source-right) tuples."""
 
209
  ### Problem: test suite strips debugging output within run_and_verify_...()
 
210
  ### so we don't see it here.  And relying on debug output is a temporary
 
211
  ### measure only.  Better to access svn_client_find_automatic_merge()
 
212
  ### directly, via bindings?
 
213
 
 
214
  merges = []
 
215
  for line in output:
 
216
    print "## " + line,
 
217
    # Extract "A1" from a line like "DBG: merge.c:11336: base  svn://.../A@1"
 
218
    match = re.search(r'merge\.c:.* base .* /(\w+)@([0-9-]+)', line)
 
219
    if match:
 
220
      base = match.group(1) + match.group(2)
 
221
    match = re.search(r'merge\.c:.* right.* /(\w+)@([0-9-]+)', line)
 
222
    if match:
 
223
      right = match.group(1) + match.group(2)
 
224
      assert base is not None
 
225
      merges.append((base, right))
 
226
      base = None
 
227
  return merges
 
228
 
 
229
def make_branches(sbox):
 
230
  """Make branches A and B."""
 
231
  sbox.build()
 
232
  sbox.simple_copy('A', 'B')
 
233
  sbox.simple_commit()
 
234
  os.chdir(sbox.wc_dir)
 
235
  sbox.wc_dir = ''
 
236
 
 
237
def modify_branch(sbox, branch, number, conflicting=False):
 
238
  """Commit a modification to branch BRANCH. The actual modification depends
 
239
     on NUMBER.  If CONFLICTING=True, the change will be of a kind that
 
240
     conflicts with any other change that has CONFLICTING=True.  We don't
 
241
     modify (properties on) the branch root node itself, to make it easier
 
242
     for the tests to distinguish mergeinfo changes from these mods."""
 
243
  uniq = branch + str(number)  # something like 'A1' or 'B2'
 
244
  if conflicting:
 
245
    sbox.simple_propset('conflict', uniq, branch + '/C')
 
246
  else:
 
247
    # Make some changes.  We add a property, which we will read later in
 
248
    # logical_changes_in_branch() to check that the correct logical
 
249
    # changes were merged.  We add a file, so that we will notice if
 
250
    # Subversion tries to merge this same logical change into a branch
 
251
    # that already has it (it will raise a tree conflict).
 
252
    sbox.simple_propset('prop-' + uniq, uniq, branch + '/D')
 
253
    sbox.simple_copy(branch + '/mu', branch + '/mu-' + uniq)
 
254
  sbox.simple_commit()
 
255
 
 
256
def expected_automatic_merge_output(target, expect_3ways):
 
257
  """Calculate the expected output."""
 
258
 
 
259
  # (This is rather specific to the current implementation.)
 
260
 
 
261
  # Match a notification for each rev-range.
 
262
  if expect_3ways:
 
263
    rev_ranges = []
 
264
    for base, right in expect_3ways:
 
265
      if base[0] == right[0]:
 
266
        base_rev = int(base[1:])
 
267
        right_rev = int(right[1:])
 
268
        rev_ranges += [(base_rev + 1, right_rev)];
 
269
  else:
 
270
    rev_ranges = None
 
271
 
 
272
  # Match any content modifications; but not of the root of the branch
 
273
  # because we don't intentionally modify the branch root node in most
 
274
  # tests and we don't want to accidentally overlook a mergeinfo change.
 
275
  lines = ["(A |D |[UG] | [UG]|[UG][UG])   " + target + os.path.sep + ".*\n"]
 
276
 
 
277
  # Match mergeinfo changes.  (### Subtrees are not yet supported here.)
 
278
  lines += [" [UG]   " + target + "\n"]
 
279
 
 
280
  # At the moment, the automatic merge code sometimes says 'Merging
 
281
  # differences between repository URLs' and sometimes 'Merging r3 through
 
282
  # r5', but it's not trivial to predict which, so expect either form.
 
283
  lines += ["--- Merging .* into '%s':\n" % (target,),
 
284
            "--- Recording mergeinfo for merge .* into '%s':\n" % (target,)]
 
285
 
 
286
  return expected_merge_output(rev_ranges, lines, target=target)
 
287
 
 
288
def automatic_merge(sbox, source, target, args=[],
 
289
                    expect_changes=None, expect_mi=None, expect_3ways=None):
 
290
  """Do a complete, automatic merge from path SOURCE to path TARGET, and
 
291
  commit.  Verify the output and that there is no error.
 
292
  ### TODO: Verify the changes made.
 
293
 
 
294
  ARGS are additional arguments passed to svn merge."""
 
295
 
 
296
  source = local_path(source)
 
297
  target = local_path(target)
 
298
 
 
299
  # First, update the WC target because mixed-rev is not fully supported.
 
300
  sbox.simple_update(target)
 
301
 
 
302
  before_changes = logical_changes_in_branch(sbox, target)
 
303
 
 
304
  exp_out = expected_automatic_merge_output(target, expect_3ways)
 
305
  exit, out, err = svntest.actions.run_and_verify_svn(None, exp_out, [],
 
306
                                     'merge',
 
307
                                     '^/' + source, target,
 
308
                                     *args)
 
309
 
 
310
  if expect_changes is not None:
 
311
    after_changes = logical_changes_in_branch(sbox, target)
 
312
    merged_changes = after_changes - before_changes
 
313
    assert_equal(merged_changes, set(expect_changes))
 
314
    reversed_changes = before_changes - after_changes
 
315
    assert_equal(reversed_changes, set())
 
316
 
 
317
  if expect_mi is not None:
 
318
    actual_mi_change = get_mergeinfo_change(sbox, target)
 
319
    assert_equal(actual_mi_change, expect_mi)
 
320
 
 
321
  if expect_3ways is not None:
 
322
    ### actual_3ways = get_3ways_from_output(out)
 
323
    ### assert_equal(actual_3ways, expect_3ways)
 
324
    pass
 
325
 
 
326
  sbox.simple_commit()
 
327
 
 
328
def three_way_merge(base_node, source_right_node):
 
329
  return (base_node, source_right_node)
 
330
 
 
331
def three_way_merge_no_op(base_node, source_right_node):
 
332
  return (base_node, source_right_node)
 
333
 
 
334
def cherry_pick(sbox, rev, source, target):
 
335
  """Cherry-pick merge revision REV from branch SOURCE to branch TARGET
 
336
  (both WC-relative paths), and commit."""
 
337
  sbox.simple_update(target)
 
338
  svn_merge(rev, source, target)
 
339
  sbox.simple_commit()
 
340
 
 
341
no_op_commit__n = 0
 
342
def no_op_commit(sbox):
 
343
  """Commit a new revision that does not affect the branches under test."""
 
344
 
 
345
  global no_op_commit__n
 
346
  sbox.simple_propset('foo', str(no_op_commit__n), 'iota')
 
347
  no_op_commit__n += 1
 
348
  sbox.simple_commit('iota')
 
349
 
 
350
 
 
351
#----------------------------------------------------------------------
 
352
 
 
353
def init_mod_merge_mod(sbox, mod_6, mod_7):
 
354
  """Modify both branches, merge A -> B, optionally modify again.
 
355
     MOD_6 is True to modify A in r6, MOD_7 is True to modify B in r7,
 
356
     otherwise make no-op commits for r6 and/or r7."""
 
357
 
 
358
  #   A (--o------?-
 
359
  #     (     \
 
360
  #   B (---o--x---?
 
361
  #     2  34  5  67
 
362
 
 
363
  make_branches(sbox)
 
364
  modify_branch(sbox, 'A', 3)
 
365
  modify_branch(sbox, 'B', 4)
 
366
 
 
367
  automatic_merge(sbox, 'A', 'B',
 
368
                  expect_changes=['A3'],
 
369
                  expect_mi=[2, 3, 4],
 
370
                  expect_3ways=[three_way_merge('A1', 'A4')])
 
371
 
 
372
  if mod_6:
 
373
    modify_branch(sbox, 'A', 6)
 
374
  else:
 
375
    no_op_commit(sbox)  # r6
 
376
 
 
377
  if mod_7:
 
378
    modify_branch(sbox, 'B', 7)
 
379
  else:
 
380
    no_op_commit(sbox)  # r7
 
381
 
 
382
########################################################################
 
383
 
 
384
# Merge once
 
385
 
 
386
@SkipUnless(server_has_mergeinfo)
 
387
def merge_once_1(sbox):
 
388
  """merge_once_1"""
 
389
 
 
390
  #   A (------
 
391
  #     (    \
 
392
  #   B (-----x
 
393
  #     2 34  5
 
394
 
 
395
  make_branches(sbox)
 
396
  no_op_commit(sbox)  # r3
 
397
  no_op_commit(sbox)  # r4
 
398
 
 
399
  automatic_merge(sbox, 'A', 'B',
 
400
                  expect_changes=[],
 
401
                  expect_mi=[2, 3, 4],
 
402
                  expect_3ways=[three_way_merge_no_op('A1', 'A4')])
 
403
 
 
404
@SkipUnless(server_has_mergeinfo)
 
405
def merge_once_2(sbox):
 
406
  """merge_once_2"""
 
407
 
 
408
  #   A (-o----
 
409
  #     (    \
 
410
  #   B (-----x
 
411
  #     2 34  5
 
412
 
 
413
  make_branches(sbox)
 
414
  modify_branch(sbox, 'A', 3)
 
415
  no_op_commit(sbox)  # r4
 
416
 
 
417
  automatic_merge(sbox, 'A', 'B',
 
418
                  expect_changes=['A3'],
 
419
                  expect_mi=[2, 3, 4],
 
420
                  expect_3ways=[three_way_merge('A1', 'A4')])
 
421
 
 
422
@SkipUnless(server_has_mergeinfo)
 
423
def merge_once_3(sbox):
 
424
  """merge_once_3"""
 
425
 
 
426
  #   A (------
 
427
  #     (    \
 
428
  #   B (--o--x
 
429
  #     2 34  5
 
430
 
 
431
  make_branches(sbox)
 
432
  no_op_commit(sbox)  # r3
 
433
  modify_branch(sbox, 'B', 4)
 
434
 
 
435
  automatic_merge(sbox, 'A', 'B',
 
436
                  expect_changes=[],
 
437
                  expect_mi=[2, 3, 4],
 
438
                  expect_3ways=[three_way_merge_no_op('A1', 'A4')])
 
439
 
 
440
@SkipUnless(server_has_mergeinfo)
 
441
def merge_once_4(sbox):
 
442
  """merge_once_4"""
 
443
 
 
444
  #   A (-o----
 
445
  #     (    \
 
446
  #   B (--o--x
 
447
  #     2 34  5
 
448
 
 
449
  make_branches(sbox)
 
450
  modify_branch(sbox, 'A', 3)
 
451
  modify_branch(sbox, 'B', 4)
 
452
 
 
453
  automatic_merge(sbox, 'A', 'B',
 
454
                  expect_changes=['A3'],
 
455
                  expect_mi=[2, 3, 4],
 
456
                  expect_3ways=[three_way_merge('A1', 'A4')])
 
457
 
 
458
#----------------------------------------------------------------------
 
459
 
 
460
# Merge twice in same direction
 
461
 
 
462
@SkipUnless(server_has_mergeinfo)
 
463
def merge_twice_same_direction_1(sbox):
 
464
  """merge_twice_same_direction_1"""
 
465
 
 
466
  #   A (--o-----------
 
467
  #     (     \      \
 
468
  #   B (---o--x------x
 
469
  #     2  34  5  67  8
 
470
 
 
471
  init_mod_merge_mod(sbox, mod_6=False, mod_7=False)
 
472
 
 
473
  automatic_merge(sbox, 'A', 'B',
 
474
                  expect_changes=[],
 
475
                  expect_mi=[5, 6, 7],
 
476
                  expect_3ways=[three_way_merge_no_op('A4', 'A7')])
 
477
 
 
478
@SkipUnless(server_has_mergeinfo)
 
479
def merge_twice_same_direction_2(sbox):
 
480
  """merge_twice_same_direction_2"""
 
481
 
 
482
  #   A (--o------o----
 
483
  #     (     \      \
 
484
  #   B (---o--x---o--x
 
485
  #     2  34  5  67  8
 
486
 
 
487
  init_mod_merge_mod(sbox, mod_6=True, mod_7=True)
 
488
 
 
489
  automatic_merge(sbox, 'A', 'B',
 
490
                  expect_changes=['A6'],
 
491
                  expect_mi=[5, 6, 7],
 
492
                  expect_3ways=[three_way_merge('A4', 'A7')])
 
493
 
 
494
#----------------------------------------------------------------------
 
495
 
 
496
#   Merge to and fro
 
497
 
 
498
@SkipUnless(server_has_mergeinfo)
 
499
def merge_to_and_fro_1_1(sbox):
 
500
  """merge_to_and_fro_1_1"""
 
501
 
 
502
  #   A (--o----------x
 
503
  #     (     \      /
 
504
  #   B (---o--x-------
 
505
  #     2  34  5  67  8
 
506
 
 
507
  init_mod_merge_mod(sbox, mod_6=False, mod_7=False)
 
508
 
 
509
  automatic_merge(sbox, 'B', 'A',
 
510
                  expect_changes=['B4'],
 
511
                  expect_mi=[2, 3, 4, 5, 6, 7],
 
512
                  expect_3ways=[three_way_merge('A4', 'B7')])
 
513
 
 
514
@SkipUnless(server_has_mergeinfo)
 
515
def merge_to_and_fro_1_2(sbox):
 
516
  """merge_to_and_fro_1_2"""
 
517
 
 
518
  #   A (--o------o---x
 
519
  #     (     \      /
 
520
  #   B (---o--x---o---
 
521
  #     2  34  5  67  8
 
522
 
 
523
  init_mod_merge_mod(sbox, mod_6=True, mod_7=True)
 
524
 
 
525
  automatic_merge(sbox, 'B', 'A',
 
526
                  expect_changes=['B4', 'B7'],
 
527
                  expect_mi=[2, 3, 4, 5, 6, 7],
 
528
                  expect_3ways=[three_way_merge('A4', 'B7')])
 
529
 
 
530
def init_merge_to_and_fro_2(sbox, mod_9, mod_10):
 
531
  """Set up branches A and B for the merge_to_and_fro_2 scenarios.
 
532
     MOD_9 is True to modify A in r9, MOD_10 is True to modify B in r10,
 
533
     otherwise make no-op commits for r9 and/or r10."""
 
534
 
 
535
  #   A (--o------o------?-
 
536
  #     (     \      \
 
537
  #   B (---o--x---o--x---?
 
538
  #     2  34  5  67  8--90
 
539
 
 
540
  init_mod_merge_mod(sbox, mod_6=True, mod_7=True)
 
541
 
 
542
  automatic_merge(sbox, 'A', 'B',
 
543
                  expect_changes=['A6'],
 
544
                  expect_mi=[5, 6, 7],
 
545
                  expect_3ways=[three_way_merge('A4', 'A7')])
 
546
 
 
547
  if mod_9:
 
548
    modify_branch(sbox, 'A', 9)
 
549
  else:
 
550
    no_op_commit(sbox)  # r9
 
551
 
 
552
  if mod_10:
 
553
    modify_branch(sbox, 'B', 10)
 
554
  else:
 
555
    no_op_commit(sbox)  # r10
 
556
 
 
557
@SkipUnless(server_has_mergeinfo)
 
558
def merge_to_and_fro_2_1(sbox):
 
559
  """merge_to_and_fro_2_1"""
 
560
 
 
561
  #   A (--o------o----------x
 
562
  #     (     \      \      /
 
563
  #   B (---o--x---o--x-------
 
564
  #     2  34  5  67  8  90  1
 
565
 
 
566
  init_merge_to_and_fro_2(sbox, mod_9=False, mod_10=False)
 
567
 
 
568
  automatic_merge(sbox, 'B', 'A',
 
569
                  expect_changes=['B4', 'B7'],
 
570
                  expect_mi=[2, 3, 4, 5, 6, 7, 8, 9, 10],
 
571
                  expect_3ways=[three_way_merge('A7', 'B10')])
 
572
 
 
573
@SkipUnless(server_has_mergeinfo)
 
574
def merge_to_and_fro_2_2(sbox):
 
575
  """merge_to_and_fro_2_2"""
 
576
 
 
577
  #   A (--o------o------o---x
 
578
  #     (     \      \      /
 
579
  #   B (---o--x---o--x---o---
 
580
  #     2  34  5  67  8  90  1
 
581
 
 
582
  init_merge_to_and_fro_2(sbox, mod_9=True, mod_10=True)
 
583
 
 
584
  automatic_merge(sbox, 'B', 'A',
 
585
                  expect_changes=['B4', 'B7', 'B10'],
 
586
                  expect_mi=[2, 3, 4, 5, 6, 7, 8, 9, 10],
 
587
                  expect_3ways=[three_way_merge('A7', 'B10')])
 
588
 
 
589
def init_merge_to_and_fro_3(sbox, mod_9, mod_10):
 
590
  """Set up branches A and B for the merge_to_and_fro_3/4 scenarios.
 
591
     MOD_9 is True to modify A in r9, MOD_10 is True to modify B in r10,
 
592
     otherwise make no-op commits for r9 and/or r10."""
 
593
 
 
594
  #   A (--o------o---x--?-
 
595
  #     (     \      /
 
596
  #   B (---o--x---o------?
 
597
  #     2  34  5  67  8  90
 
598
 
 
599
  init_mod_merge_mod(sbox, mod_6=True, mod_7=True)
 
600
 
 
601
  automatic_merge(sbox, 'B', 'A',
 
602
                  expect_changes=['B4', 'B7'],
 
603
                  expect_mi=[2, 3, 4, 5, 6, 7],
 
604
                  expect_3ways=[three_way_merge('A4', 'B7')])
 
605
 
 
606
  if mod_9:
 
607
    modify_branch(sbox, 'A', 9)
 
608
  else:
 
609
    no_op_commit(sbox)  # r9
 
610
 
 
611
  if mod_10:
 
612
    modify_branch(sbox, 'B', 10)
 
613
  else:
 
614
    no_op_commit(sbox)  # r10
 
615
 
 
616
@SkipUnless(server_has_mergeinfo)
 
617
def merge_to_and_fro_3_1(sbox):
 
618
  """merge_to_and_fro_3_1"""
 
619
 
 
620
  #   A (--o------o---x------x
 
621
  #     (     \      /      /
 
622
  #   B (---o--x---o----------
 
623
  #     2  34  5  67  8  90  1
 
624
 
 
625
  init_merge_to_and_fro_3(sbox, mod_9=False, mod_10=False)
 
626
 
 
627
  automatic_merge(sbox, 'B', 'A',
 
628
                  expect_changes=[],
 
629
                  expect_mi=[8, 9, 10],
 
630
                  expect_3ways=[three_way_merge_no_op('B7', 'B10')])
 
631
 
 
632
@SkipUnless(server_has_mergeinfo)
 
633
def merge_to_and_fro_3_2(sbox):
 
634
  """merge_to_and_fro_3_2"""
 
635
 
 
636
  #   A (--o------o---x--o---x
 
637
  #     (     \      /      /
 
638
  #   B (---o--x---o------o---
 
639
  #     2  34  5  67  8  90  1
 
640
 
 
641
  init_merge_to_and_fro_3(sbox, mod_9=True, mod_10=True)
 
642
 
 
643
  automatic_merge(sbox, 'B', 'A',
 
644
                  expect_changes=['B10'],
 
645
                  expect_mi=[8, 9, 10],
 
646
                  expect_3ways=[three_way_merge('B7', 'B10')])
 
647
 
 
648
@SkipUnless(server_has_mergeinfo)
 
649
def merge_to_and_fro_4_1(sbox):
 
650
  """merge_to_and_fro_4_1"""
 
651
 
 
652
  #   A (--o------o---x-------
 
653
  #     (     \      /      \
 
654
  #   B (---o--x---o---------x
 
655
  #     2  34  5  67  8  90  1
 
656
 
 
657
  init_merge_to_and_fro_3(sbox, mod_9=False, mod_10=False)
 
658
 
 
659
  automatic_merge(sbox, 'A', 'B',
 
660
                  expect_changes=['A6'],
 
661
                  expect_mi=[5, 6, 7, 8, 9, 10],
 
662
                  expect_3ways=[three_way_merge_no_op('B7', 'A10')])
 
663
 
 
664
@SkipUnless(server_has_mergeinfo)
 
665
def merge_to_and_fro_4_2(sbox):
 
666
  """merge_to_and_fro_4_2"""
 
667
 
 
668
  #   A (--o------o---x--o----
 
669
  #     (     \      /      \
 
670
  #   B (---o--x---o------o--x
 
671
  #     2  34  5  67  8  90  1
 
672
 
 
673
  init_merge_to_and_fro_3(sbox, mod_9=True, mod_10=True)
 
674
 
 
675
  automatic_merge(sbox, 'A', 'B',
 
676
                  expect_changes=['A6', 'A9'],
 
677
                  expect_mi=[5, 6, 7, 8, 9, 10],
 
678
                  expect_3ways=[three_way_merge('B7', 'A10')])
 
679
 
 
680
#----------------------------------------------------------------------
 
681
 
 
682
# Cherry-pick scenarios
 
683
 
 
684
@SkipUnless(server_has_mergeinfo)
 
685
def cherry1_fwd(sbox):
 
686
  """cherry1_fwd"""
 
687
 
 
688
  #   A (--o------o--[o]----o---
 
689
  #     (     \         \     \
 
690
  #   B (---o--x---------c-----x
 
691
  #     2  34  5  67  8  9  0  1
 
692
 
 
693
  init_mod_merge_mod(sbox, mod_6=True, mod_7=False)
 
694
  modify_branch(sbox, 'A', 8)
 
695
  cherry_pick(sbox, 8, 'A', 'B')
 
696
  modify_branch(sbox, 'A', 10)
 
697
 
 
698
  automatic_merge(sbox, 'A', 'B',
 
699
                  expect_changes=['A6', 'A10'],  # and NOT A8
 
700
                  expect_mi=[5, 6, 7, 9, 10],
 
701
                  expect_3ways=[three_way_merge('A4', 'A7'),
 
702
                                three_way_merge('A8', 'A10')])
 
703
 
 
704
@SkipUnless(server_has_mergeinfo)
 
705
@XFail()
 
706
@Issue(4255)
 
707
def cherry2_fwd(sbox):
 
708
  """cherry2_fwd"""
 
709
 
 
710
  #   A (--o-------------c--o---
 
711
  #     (     \         /     \
 
712
  #   B (---o--x---o-[o]-------x
 
713
  #     2  34  5  67  8  9  0  1
 
714
 
 
715
  init_mod_merge_mod(sbox, mod_6=False, mod_7=True)
 
716
  modify_branch(sbox, 'B', 8)
 
717
  cherry_pick(sbox, 8, 'B', 'A')
 
718
  modify_branch(sbox, 'A', 10)
 
719
 
 
720
  automatic_merge(sbox, 'A', 'B',
 
721
                  expect_changes=['A10'],  # and NOT A9
 
722
                  expect_mi=[5, 6, 7, 8, 9, 10],
 
723
                  expect_3ways=[three_way_merge('A9', 'A10')])
 
724
 
 
725
@SkipUnless(server_has_mergeinfo)
 
726
@XFail()
 
727
@Issue(4255)
 
728
def cherry3_fwd(sbox):
 
729
  """cherry3_fwd"""
 
730
 
 
731
  #   A (--o--------------c--o----
 
732
  #     (          \     /     \
 
733
  #     (           \   /       \
 
734
  #   B (---o--o-[o]-x-/---------x
 
735
  #                \__/
 
736
  #     2  34  5  6  7    8  9   0
 
737
 
 
738
  make_branches(sbox)
 
739
  modify_branch(sbox, 'A', 3)
 
740
  modify_branch(sbox, 'B', 4)
 
741
  modify_branch(sbox, 'B', 5)
 
742
  modify_branch(sbox, 'B', 6)
 
743
 
 
744
  automatic_merge(sbox, 'A', 'B',
 
745
                  expect_changes=['A3'],
 
746
                  expect_mi=[2, 3, 4, 5, 6],
 
747
                  expect_3ways=[three_way_merge('A1', 'A6')])
 
748
 
 
749
  cherry_pick(sbox, 6, 'B', 'A')
 
750
  modify_branch(sbox, 'A', 9)
 
751
 
 
752
  automatic_merge(sbox, 'A', 'B',
 
753
                  expect_changes=['A9'],  # and NOT A8
 
754
                  expect_mi=[7, 8, 9],
 
755
                  expect_3ways=[three_way_merge('A8', 'A9')])
 
756
 
 
757
#----------------------------------------------------------------------
 
758
# Automatic merges ignore subtree mergeinfo during reintegrate.
 
759
@SkipUnless(server_has_mergeinfo)
 
760
@Issue(4258)
 
761
def subtree_to_and_fro(sbox):
 
762
  "reintegrate considers source subtree mergeinfo"
 
763
 
 
764
  #   A      (-----o-o-o-o------------x
 
765
  #          ( \            \        /
 
766
  #          (  \            \      /
 
767
  #   A_COPY (   o---------o--s--o--
 
768
  #              2 3 4 5 6 7  8  9
 
769
 
 
770
  # Some paths we'll care about.
 
771
  A_COPY_gamma_path = sbox.ospath('A_COPY/D/gamma')
 
772
  psi_path = sbox.ospath('A/D/H/psi')
 
773
  A_COPY_D_path = sbox.ospath('A_COPY/D')
 
774
  A_path = sbox.ospath('A')
 
775
 
 
776
  sbox.build()
 
777
  wc_dir = sbox.wc_dir
 
778
 
 
779
  # Setup a simple 'trunk & branch': Copy ^/A to ^/A_COPY in r2 and then
 
780
  # make a few edits under A in r3-6 (edits r3, r4, r6 are under subtree 'D'):
 
781
  wc_disk, wc_status = set_up_branch(sbox)
 
782
 
 
783
  # r7 - Edit a file on the branch.
 
784
  svntest.main.file_write(A_COPY_gamma_path, "Branch edit to 'gamma'.\n")
 
785
  svntest.actions.run_and_verify_svn(None, None, [], 'ci', wc_dir,
 
786
                                     '-m', 'Edit a file on our branch')
 
787
 
 
788
  # r8 - Do a subtree sync merge from ^/A/D to A_COPY/D.
 
789
  # Note that among other things this changes A_COPY/D/H/psi.
 
790
  svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
 
791
  svntest.actions.run_and_verify_svn(None, None, [], 'merge',
 
792
                                     sbox.repo_url + '/A/D', A_COPY_D_path)
 
793
  svntest.actions.run_and_verify_svn(None, None, [], 'ci', wc_dir,
 
794
                                     '-m', 'Automatic subtree merge')
 
795
 
 
796
  # r9 - Make an edit to A/D/H/psi.
 
797
  svntest.main.file_write(psi_path, "Trunk Edit to 'psi'.\n")
 
798
  svntest.actions.run_and_verify_svn(None, None, [], 'ci', wc_dir,
 
799
                                     '-m', 'Edit a file on our trunk')
 
800
 
 
801
  # Now reintegrate ^/A_COPY back to A.  Prior to issue #4258's fix, the
 
802
  # the subtree merge to A_COPY/D just looks like any other branch edit and
 
803
  # was not considered a merge.  So the changes which exist on A/D and were
 
804
  # merged to A_COPY/D, were merged *back* to A, resulting in a conflict:
 
805
  #
 
806
  #   C:\...\working_copies\merge_automatic_tests-18>svn merge ^^/A_COPY A
 
807
  #   DBG: merge.c:11461: base on source: ^/A@1
 
808
  #   DBG: merge.c:11462: base on target: ^/A@1
 
809
  #   DBG: merge.c:11567: yca   ^/A@1
 
810
  #   DBG: merge.c:11568: base  ^/A@1
 
811
  #   DBG: merge.c:11571: right ^/A_COPY@8
 
812
  #   Conflict discovered in file 'A\D\H\psi'.
 
813
  #   Select: (p) postpone, (df) diff-full, (e) edit,
 
814
  #           (mc) mine-conflict, (tc) theirs-conflict,
 
815
  #           (s) show all options: p
 
816
  #   --- Merging r2 through r9 into 'A':
 
817
  #   C    A\D\H\psi
 
818
  #   U    A\D\gamma
 
819
  #   --- Recording mergeinfo for merge of r2 through r9 into 'A':
 
820
  #    U   A
 
821
  #   Summary of conflicts:
 
822
  #     Text conflicts: 1
 
823
  svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
 
824
  exit_code, out, err = svntest.actions.run_and_verify_svn(
 
825
    None, [], svntest.verify.AnyOutput,
 
826
    'merge', sbox.repo_url + '/A_COPY', A_path)
 
827
 
 
828
  # Better to produce the same warning that explicitly using the
 
829
  # --reintegrate option would produce:
 
830
  svntest.verify.verify_outputs("Automatic Reintegrate failed, but not "
 
831
                                "in the way expected",
 
832
                                err, None,
 
833
                                "(svn: E195016: Reintegrate can only be used if "
 
834
                                "revisions 2 through 9 were previously "
 
835
                                "merged from .*/A to the reintegrate source, "
 
836
                                "but this is not the case:\n)"
 
837
                                "|(  A_COPY\n)"
 
838
                                "|(    Missing ranges: /A:5\n)"
 
839
                                "|(\n)"
 
840
                                "|" + svntest.main.stack_trace_regexp,
 
841
                                None,
 
842
                                True) # Match *all* lines of stdout
 
843
 
 
844
#----------------------------------------------------------------------
 
845
# Automatic merges ignore subtree mergeinfo gaps older than the last rev
 
846
# synced to the target root.
 
847
@SkipUnless(server_has_mergeinfo)
 
848
def merge_to_reverse_cherry_subtree_to_merge_to(sbox):
 
849
  "sync merge considers target subtree mergeinfo"
 
850
 
 
851
  #   A (--o-o-o-o------------------
 
852
  #     ( \         \        \     \
 
853
  #     (  \         \        \     \
 
854
  #   B (   o--o------x-------rc-----x
 
855
 
 
856
  # Some paths we'll care about.
 
857
  A_COPY_path = sbox.ospath('A_COPY')
 
858
  A_COPY_B_path = sbox.ospath('A_COPY/B')
 
859
  A_COPY_beta_path = sbox.ospath('A_COPY/B/E/beta')
 
860
 
 
861
  sbox.build()
 
862
  wc_dir = sbox.wc_dir
 
863
 
 
864
  # Setup a simple 'trunk & branch': Copy ^/A to ^/A_COPY in r2 and then
 
865
  # make a few edits under A in r3-6:
 
866
  wc_disk, wc_status = set_up_branch(sbox)
 
867
 
 
868
  # Sync merge ^/A to A_COPY, then reverse merge r5 from ^/A/B to A_COPY/B.
 
869
  # This results in mergeinfo on the target which makes it appear that the
 
870
  # branch is synced up to r6, but the subtree mergeinfo on A_COPY/B reveals
 
871
  # that r5 has not been merged to that subtree:
 
872
  #
 
873
  #   Properties on 'A_COPY':
 
874
  #     svn:mergeinfo
 
875
  #       /A:2-6
 
876
  #   Properties on 'A_COPY\B':
 
877
  #     svn:mergeinfo
 
878
  #       /A/B:2-4,6
 
879
  svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
 
880
  svntest.actions.run_and_verify_svn(None, None, [], 'merge',
 
881
                                     sbox.repo_url + '/A', A_COPY_path)
 
882
  svntest.actions.run_and_verify_svn(None, None, [], 'merge', '-c-5',
 
883
                                     sbox.repo_url + '/A/B',
 
884
                                     A_COPY_B_path)
 
885
  svntest.actions.run_and_verify_svn(None, None, [], 'ci', wc_dir, '-m',
 
886
                                     'sync merge and reverse subtree merge')
 
887
 
 
888
  # Try an automatic sync merge from ^/A to A_COPY.  Revision 5 should be
 
889
  # merged to A_COPY/B as its subtree mergeinfo reveals that rev is missing,
 
890
  # like so:
 
891
  #
 
892
  #   >svn merge ^/A A_COPY
 
893
  #   --- Merging r5 into 'A_COPY\B':
 
894
  #   U    A_COPY\B\E\beta
 
895
  #   --- Recording mergeinfo for merge of r5 through r7 into 'A_COPY':
 
896
  #    U   A_COPY
 
897
  #   --- Recording mergeinfo for merge of r5 through r7 into 'A_COPY\B':
 
898
  #    U   A_COPY\B
 
899
  #   --- Eliding mergeinfo from 'A_COPY\B':
 
900
  #    U   A_COPY\B
 
901
  #
 
902
  # But the merge ignores the subtree mergeinfo and considers
 
903
  # only the mergeinfo on the target itself (and thus is a no-op but for
 
904
  # the mergeinfo change on the root of the merge target):
 
905
  #
 
906
  #   >svn merge ^/A A_COPY
 
907
  #   --- Recording mergeinfo for merge of r7 into 'A_COPY':
 
908
  #    U   A_COPY
 
909
  #
 
910
  #   >svn diff
 
911
  #   Index: A_COPY
 
912
  #   ===================================================================
 
913
  #   --- A_COPY      (revision 7)
 
914
  #   +++ A_COPY      (working copy)
 
915
  #
 
916
  #   Property changes on: A_COPY
 
917
  #   ___________________________________________________________________
 
918
  #   Modified: svn:mergeinfo
 
919
  #      Merged /A:r7
 
920
  svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
 
921
  expected_output = wc.State(A_COPY_path, {
 
922
    'B/E/beta'   : Item(status='U '),
 
923
    })
 
924
  expected_mergeinfo_output = wc.State(A_COPY_path, {
 
925
    ''  : Item(status=' U'),
 
926
    'B' : Item(status=' U'),
 
927
    })
 
928
  expected_elision_output = wc.State(A_COPY_path, {
 
929
    'B' : Item(status=' U'),
 
930
    })
 
931
  expected_status = wc.State(A_COPY_path, {
 
932
    ''           : Item(status=' M'),
 
933
    'B'          : Item(status=' M'),
 
934
    'mu'         : Item(status='  '),
 
935
    'B/E'        : Item(status='  '),
 
936
    'B/E/alpha'  : Item(status='  '),
 
937
    'B/E/beta'   : Item(status='M '),
 
938
    'B/lambda'   : Item(status='  '),
 
939
    'B/F'        : Item(status='  '),
 
940
    'C'          : Item(status='  '),
 
941
    'D'          : Item(status='  '),
 
942
    'D/G'        : Item(status='  '),
 
943
    'D/G/pi'     : Item(status='  '),
 
944
    'D/G/rho'    : Item(status='  '),
 
945
    'D/G/tau'    : Item(status='  '),
 
946
    'D/gamma'    : Item(status='  '),
 
947
    'D/H'        : Item(status='  '),
 
948
    'D/H/chi'    : Item(status='  '),
 
949
    'D/H/psi'    : Item(status='  '),
 
950
    'D/H/omega'  : Item(status='  '),
 
951
    })
 
952
  expected_status.tweak(wc_rev='7')
 
953
  expected_disk = wc.State('', {
 
954
    ''           : Item(props={SVN_PROP_MERGEINFO : '/A:2-7'}),
 
955
    'B'          : Item(),
 
956
    'mu'         : Item("This is the file 'mu'.\n"),
 
957
    'B/E'        : Item(),
 
958
    'B/E/alpha'  : Item("This is the file 'alpha'.\n"),
 
959
    'B/E/beta'   : Item("New content"),
 
960
    'B/lambda'   : Item("This is the file 'lambda'.\n"),
 
961
    'B/F'        : Item(),
 
962
    'C'          : Item(),
 
963
    'D'          : Item(),
 
964
    'D/G'        : Item(),
 
965
    'D/G/pi'     : Item("This is the file 'pi'.\n"),
 
966
    'D/G/rho'    : Item("New content"),
 
967
    'D/G/tau'    : Item("This is the file 'tau'.\n"),
 
968
    'D/gamma'    : Item("This is the file 'gamma'.\n"),
 
969
    'D/H'        : Item(),
 
970
    'D/H/chi'    : Item("This is the file 'chi'.\n"),
 
971
    'D/H/psi'    : Item("New content"),
 
972
    'D/H/omega'  : Item("New content"),
 
973
    })
 
974
  expected_skip = wc.State(A_COPY_path, { })
 
975
  svntest.actions.run_and_verify_merge(A_COPY_path, None, None,
 
976
                                       sbox.repo_url + '/A', None,
 
977
                                       expected_output,
 
978
                                       expected_mergeinfo_output,
 
979
                                       expected_elision_output,
 
980
                                       expected_disk,
 
981
                                       expected_status,
 
982
                                       expected_skip,
 
983
                                       None, None, None, None,
 
984
                                       None, 1, 0, A_COPY_path)
 
985
 
 
986
#----------------------------------------------------------------------
 
987
# Automatic merges should notice ancestory for replaced files
 
988
@SkipUnless(server_has_mergeinfo)
 
989
def merge_replacement(sbox):
 
990
  "notice ancestory for replaced files"
 
991
 
 
992
  A_path = sbox.ospath('A')
 
993
  A_COPY_path = sbox.ospath('A_copy')
 
994
  A_COPY_mu_path = sbox.ospath('A_copy/mu')
 
995
 
 
996
  sbox.build()
 
997
  wc_dir = sbox.wc_dir
 
998
  sbox.simple_copy('A', 'A_copy')
 
999
  # Commit as r2
 
1000
  sbox.simple_commit()
 
1001
 
 
1002
  sbox.simple_rm('A_copy/B/lambda')
 
1003
  sbox.simple_copy('A_copy/D/gamma', 'A_copy/B/lambda')
 
1004
 
 
1005
  sbox.simple_rm('A_copy/mu')
 
1006
  svntest.main.file_write(A_COPY_mu_path, "Branch edit to 'mu'.\n")
 
1007
  sbox.simple_add('A_copy/mu')
 
1008
 
 
1009
  # Commit as r3
 
1010
  sbox.simple_commit()
 
1011
 
 
1012
  expected_output = wc.State(A_path, {
 
1013
    'B/lambda'   : Item(status='R '),
 
1014
    'mu'         : Item(status='R '),
 
1015
    })
 
1016
  expected_mergeinfo_output = wc.State(A_path, {
 
1017
    ''  : Item(status=' U'),
 
1018
    })
 
1019
  expected_elision_output = wc.State(A_path, {
 
1020
    })
 
1021
 
 
1022
  expected_status = wc.State(A_path, {
 
1023
    ''           : Item(status=' M', wc_rev='1'),
 
1024
    'B'          : Item(status='  ', wc_rev='1'),
 
1025
    'mu'         : Item(status='R ', copied='+', wc_rev='-'),
 
1026
    'B/E'        : Item(status='  ', wc_rev='1'),
 
1027
    'B/E/alpha'  : Item(status='  ', wc_rev='1'),
 
1028
    'B/E/beta'   : Item(status='  ', wc_rev='1'),
 
1029
    'B/lambda'   : Item(status='R ', copied='+', wc_rev='-'),
 
1030
    'B/F'        : Item(status='  ', wc_rev='1'),
 
1031
    'C'          : Item(status='  ', wc_rev='1'),
 
1032
    'D'          : Item(status='  ', wc_rev='1'),
 
1033
    'D/G'        : Item(status='  ', wc_rev='1'),
 
1034
    'D/G/pi'     : Item(status='  ', wc_rev='1'),
 
1035
    'D/G/rho'    : Item(status='  ', wc_rev='1'),
 
1036
    'D/G/tau'    : Item(status='  ', wc_rev='1'),
 
1037
    'D/gamma'    : Item(status='  ', wc_rev='1'),
 
1038
    'D/H'        : Item(status='  ', wc_rev='1'),
 
1039
    'D/H/chi'    : Item(status='  ', wc_rev='1'),
 
1040
    'D/H/psi'    : Item(status='  ', wc_rev='1'),
 
1041
    'D/H/omega'  : Item(status='  ', wc_rev='1'),
 
1042
    })
 
1043
 
 
1044
  expected_disk = wc.State('', {
 
1045
    ''           : Item(props={SVN_PROP_MERGEINFO : '/A_copy:2-3'}),
 
1046
    'B'          : Item(),
 
1047
    'mu'         : Item("Branch edit to 'mu'.\n"),
 
1048
    'B/E'        : Item(),
 
1049
    'B/E/alpha'  : Item("This is the file 'alpha'.\n"),
 
1050
    'B/E/beta'   : Item("This is the file 'beta'.\n"),
 
1051
    'B/lambda'   : Item("This is the file 'gamma'.\n"),
 
1052
    'B/F'        : Item(),
 
1053
    'C'          : Item(),
 
1054
    'D'          : Item(),
 
1055
    'D/G'        : Item(),
 
1056
    'D/G/pi'     : Item("This is the file 'pi'.\n"),
 
1057
    'D/G/rho'    : Item("This is the file 'rho'.\n"),
 
1058
    'D/G/tau'    : Item("This is the file 'tau'.\n"),
 
1059
    'D/gamma'    : Item("This is the file 'gamma'.\n"),
 
1060
    'D/H'        : Item(),
 
1061
    'D/H/chi'    : Item("This is the file 'chi'.\n"),
 
1062
    'D/H/psi'    : Item("This is the file 'psi'.\n"),
 
1063
    'D/H/omega'  : Item("This is the file 'omega'.\n"),
 
1064
    })
 
1065
 
 
1066
  expected_skip = wc.State(A_COPY_path, { })
 
1067
 
 
1068
  svntest.actions.run_and_verify_merge(A_path, None, None,
 
1069
                                       sbox.repo_url + '/A_copy', None,
 
1070
                                       expected_output,
 
1071
                                       expected_mergeinfo_output,
 
1072
                                       expected_elision_output,
 
1073
                                       expected_disk,
 
1074
                                       expected_status,
 
1075
                                       expected_skip,
 
1076
                                       None, None, None, None,
 
1077
                                       None, 1, 0, A_path)
 
1078
 
 
1079
@SkipUnless(server_has_mergeinfo)
 
1080
@Issue(4313)
 
1081
 
 
1082
# Test for issue #4313 'replaced merges source causes assertion during
 
1083
# automatic merge'
 
1084
def auto_merge_handles_replacements_in_merge_source(sbox):
 
1085
  "automerge handles replacements in merge source"
 
1086
 
 
1087
  sbox.build()
 
1088
 
 
1089
  A_path = sbox.ospath('A')
 
1090
  branch1_path = sbox.ospath('branch-1')
 
1091
  branch2_path = sbox.ospath('branch-2')
 
1092
 
 
1093
  # r2 - Make two branches.
 
1094
  sbox.simple_copy('A', 'branch-1')
 
1095
  sbox.simple_copy('A', 'branch-2')
 
1096
  sbox.simple_commit()
 
1097
  sbox.simple_update()
 
1098
 
 
1099
  # r3 - Replace 'A' with 'branch-1'.
 
1100
  svntest.main.run_svn(None, 'del', A_path)
 
1101
  svntest.main.run_svn(None, 'copy', branch1_path, A_path)
 
1102
  sbox.simple_commit()
 
1103
  sbox.simple_update()
 
1104
 
 
1105
  # Merge^/A to branch-2, it should be a no-op but for mergeinfo changes,
 
1106
  # but it *should* work.  Previously this failed because automatic merges
 
1107
  # weren't adhering to the merge source normalization rules, resulting in
 
1108
  # this assertion:
 
1109
  #
 
1110
  #   >svn merge ^/A branch-2
 
1111
  #   ..\..\..\subversion\libsvn_client\merge.c:4568: (apr_err=235000)
 
1112
  #   svn: E235000: In file '..\..\..\subversion\libsvn_client\merge.c'
 
1113
  #     line 4568: assertion failed (apr_hash_count(implicit_src_mergeinfo)
 
1114
  #     == 1)
 
1115
  #
 
1116
  #   This application has requested the Runtime to terminate it in an
 
1117
  #   unusual way.
 
1118
  #   Please contact the application's support team for more information.
 
1119
  svntest.actions.run_and_verify_svn(
 
1120
    None,
 
1121
    ["--- Recording mergeinfo for merge of r2 into '" + branch2_path + "':\n",
 
1122
     " U   " + branch2_path + "\n",
 
1123
     "--- Recording mergeinfo for merge of r3 into '" + branch2_path + "':\n",
 
1124
     " G   " + branch2_path + "\n"],
 
1125
    [], 'merge', sbox.repo_url + '/A', branch2_path)
 
1126
 
 
1127
# Test for issue #4329 'automatic merge uses reintegrate type merge if
 
1128
# source is fully synced'
 
1129
@SkipUnless(server_has_mergeinfo)
 
1130
@Issue(4329)
 
1131
def effective_sync_results_in_reintegrate(sbox):
 
1132
  "an effectively synced branch gets reintegrated"
 
1133
 
 
1134
  sbox.build()
 
1135
 
 
1136
  iota_path = sbox.ospath('iota')
 
1137
  A_path = sbox.ospath('A')
 
1138
  psi_path = sbox.ospath('A/D/H/psi')
 
1139
  mu_path = sbox.ospath('A/mu')
 
1140
  branch_path = sbox.ospath('branch')
 
1141
  psi_branch_path = sbox.ospath('branch/D/H/psi')
 
1142
 
 
1143
  # r2 - Make a branch.
 
1144
  sbox.simple_copy('A', 'branch')
 
1145
  sbox.simple_commit()
 
1146
 
 
1147
  # r3 - An edit to a file on the trunk.
 
1148
  sbox.simple_append('A/mu', "Trunk edit to 'mu'\n", True)
 
1149
  sbox.simple_commit()
 
1150
 
 
1151
  # r4 - An edit to a file on the branch
 
1152
  sbox.simple_append('branch/D/H/psi', "Branch edit to 'psi'\n", True)
 
1153
  sbox.simple_commit()
 
1154
 
 
1155
  # r5 - Effectively sync all changes on trunk to the branch.  We do this
 
1156
  # not via an automatic sync merge, but with a cherry pick that effectively
 
1157
  # merges the same changes (i.e. r3).
 
1158
  sbox.simple_update()
 
1159
  cherry_pick(sbox, 3, A_path, branch_path)
 
1160
 
 
1161
  # r6 - Make another edit to the file on the trunk.
 
1162
  sbox.simple_append('A/mu', "2nd trunk edit to 'mu'\n", True)
 
1163
  sbox.simple_commit()
 
1164
 
 
1165
  # Now try an explicit --reintegrate merge from ^/branch to A.
 
1166
  # This should work because since the resolution of
 
1167
  # http://subversion.tigris.org/issues/show_bug.cgi?id=3577
 
1168
  # if B is *effectively* synced with A, then B can be reintegrated
 
1169
  # to A.
 
1170
  sbox.simple_update()
 
1171
  expected_output = [
 
1172
    "--- Merging differences between repository URLs into '" +
 
1173
    A_path + "':\n",
 
1174
    "U    " + psi_path + "\n",
 
1175
    "--- Recording mergeinfo for merge between repository URLs into '" +
 
1176
    A_path + "':\n",
 
1177
    " U   " + A_path + "\n"]
 
1178
  svntest.actions.run_and_verify_svn(None, expected_output, [], 'merge',
 
1179
                                     sbox.repo_url + '/branch', A_path,
 
1180
                                     '--reintegrate')
 
1181
 
 
1182
  # Revert the merge and try it again, this time without the --reintegrate
 
1183
  # option.  The merge should still work with the same results.
 
1184
  #
 
1185
  # Previously this failed because the reintegrate code path is not followed,
 
1186
  # rather the automatic merge attempts a sync style merge of the yca (^/A@1)
 
1187
  # through the HEAD of the branch (^/branch@7).  This results in a spurious
 
1188
  # conflict on A/mu as the edit made in r3 is reapplied.
 
1189
  #
 
1190
  # >svn merge ^/branch A
 
1191
  # --- Merging r2 through r6 into 'A':
 
1192
  # C    A\mu
 
1193
  # U    A\D\H\psi
 
1194
  # --- Recording mergeinfo for merge of r2 through r6 into 'A':
 
1195
  #  U   A
 
1196
  # Summary of conflicts:
 
1197
  #   Text conflicts: 1
 
1198
  # Conflict discovered in file 'A\mu'.
 
1199
  # Select: (p) postpone, (df) diff-full, (e) edit, (m) merge,
 
1200
  #         (mc) mine-conflict, (tc) theirs-conflict, (s) show all options: p
 
1201
  svntest.actions.run_and_verify_svn(None, None, [], 'revert', A_path, '-R')
 
1202
  svntest.actions.run_and_verify_svn(None, expected_output, [], 'merge',
 
1203
                                     sbox.repo_url + '/branch', A_path)
 
1204
 
 
1205
########################################################################
 
1206
# Run the tests
 
1207
 
 
1208
 
 
1209
# list all tests here, starting with None:
 
1210
test_list = [ None,
 
1211
              merge_once_1,
 
1212
              merge_once_2,
 
1213
              merge_once_3,
 
1214
              merge_once_4,
 
1215
              merge_twice_same_direction_1,
 
1216
              merge_twice_same_direction_2,
 
1217
              merge_to_and_fro_1_1,
 
1218
              merge_to_and_fro_1_2,
 
1219
              merge_to_and_fro_2_1,
 
1220
              merge_to_and_fro_2_2,
 
1221
              merge_to_and_fro_3_1,
 
1222
              merge_to_and_fro_3_2,
 
1223
              merge_to_and_fro_4_1,
 
1224
              merge_to_and_fro_4_2,
 
1225
              cherry1_fwd,
 
1226
              cherry2_fwd,
 
1227
              cherry3_fwd,
 
1228
              subtree_to_and_fro,
 
1229
              merge_to_reverse_cherry_subtree_to_merge_to,
 
1230
              merge_replacement,
 
1231
              auto_merge_handles_replacements_in_merge_source,
 
1232
              effective_sync_results_in_reintegrate,
 
1233
             ]
 
1234
 
 
1235
if __name__ == '__main__':
 
1236
  svntest.main.run_tests(test_list)
 
1237
  # NOTREACHED
 
1238
 
 
1239
 
 
1240
### End of file.