You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by hw...@apache.org on 2012/01/13 22:40:38 UTC
svn commit: r1231318 [30/35] - in /subversion/branches/revprop-packing: ./
build/ build/ac-macros/ build/generator/ build/generator/templates/
build/win32/ contrib/client-side/emacs/ contrib/server-side/mod_dontdothat/
notes/ notes/http-and-webdav/ not...
Modified: subversion/branches/revprop-packing/subversion/tests/cmdline/log_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/revprop-packing/subversion/tests/cmdline/log_tests.py?rev=1231318&r1=1231317&r2=1231318&view=diff
==============================================================================
--- subversion/branches/revprop-packing/subversion/tests/cmdline/log_tests.py (original)
+++ subversion/branches/revprop-packing/subversion/tests/cmdline/log_tests.py Fri Jan 13 21:40:26 2012
@@ -34,6 +34,7 @@ from svntest import wc
from svntest.main import server_has_mergeinfo
from svntest.main import SVN_PROP_MERGEINFO
from merge_tests import set_up_branch
+from diff_tests import make_diff_header, make_no_diff_deleted_header
# (abbreviation)
Skip = svntest.testcase.Skip_deco
@@ -178,7 +179,8 @@ def guarantee_repos_and_wc(sbox):
to test the code"""
svntest.main.file_write(msg_file, msg)
svntest.main.file_append(iota_path, "8")
- svntest.main.file_append(rho_path, "8")
+ svntest.main.file_append(rho_path, "88") # More than one char so libmagic
+ # treats it as text.
svntest.main.run_svn(None, 'add', rho_path)
svntest.main.run_svn(None,
'ci', '-F', msg_file)
@@ -424,7 +426,7 @@ class SVNLogParseError(Exception):
pass
-def parse_log_output(log_lines):
+def parse_log_output(log_lines, with_diffs=False):
"""Return a log chain derived from LOG_LINES.
A log chain is a list of hashes; each hash represents one log
message, in the order it appears in LOG_LINES (the first log
@@ -438,6 +440,7 @@ def parse_log_output(log_lines):
'date' ===> string
'msg' ===> string (the log message itself)
'lines' ===> number (so that it may be checked against rev)
+
If LOG_LINES contains changed-path information, then the hash
also contains
@@ -451,7 +454,11 @@ def parse_log_output(log_lines):
'reverse_merges' ===> list of reverse-merging revisions that resulted
in this log being part of the list of messages.
- """
+
+ If LOG_LINES contains diffs and WITH_DIFFS=True, then the hash also contains
+
+ 'diff_lines' ===> list of strings (diffs)
+ """
# Here's some log output to look at while writing this function:
@@ -566,6 +573,20 @@ def parse_log_output(log_lines):
for line in log_lines[0:lines]:
msg += line
del log_lines[0:lines]
+
+ # Maybe accumulate a diff.
+ # If there is a diff, there is a blank line before and after it.
+ if with_diffs and len(log_lines) >= 2 and log_lines[0] == '\n':
+ log_lines.pop(0)
+ diff_lines = []
+ while len(log_lines) and log_lines[0] != msg_separator:
+ diff_lines.append(log_lines.pop(0))
+ if diff_lines[-1] == '\n':
+ diff_lines.pop()
+ else:
+ raise SVNLogParseError("no blank line after diff in log")
+ this_item['diff_lines'] = diff_lines
+
elif this_line == msg_separator:
if this_item:
this_item['msg'] = msg
@@ -676,6 +697,42 @@ def check_log_chain(chain, revlist, path
missing_revs, chain)
+def parse_diff(output):
+ """Return a set containing the various diff bits, broken up by file."""
+
+ diff_set = []
+ current_diff = []
+ for line in output:
+ if line.startswith('Index: ') and current_diff:
+ diff_set.append(current_diff)
+ current_diff = []
+ current_diff.append(line)
+ diff_set.append(current_diff)
+
+ return diff_set
+
+
+def setify(diff_list):
+ """Take a list of lists and make it a set of tuples."""
+ s = set()
+ for diff in diff_list:
+ s.add(tuple(diff))
+ return s
+
+
+def compare_diff_output(expected_diffs, output):
+ """Compare the diffs in EXPECTED_DIFFS (which is a Python set) with the
+ text in OUTPUT, remembering that there is no canonical ordering for diffs."""
+
+ diffs = parse_diff(output)
+ diffs = setify(diffs)
+ expected_diffs = setify(expected_diffs)
+
+ if diffs.issubset(expected_diffs) and diffs.issuperset(expected_diffs):
+ return
+
+ raise svntest.Failure("Diffs not equal")
+
######################################################################
# Tests
@@ -2028,6 +2085,124 @@ def log_on_nonexistent_path_and_valid_re
svntest.actions.run_and_verify_svn(None, None, expected_error,
'log', '-q', bad_path_default_rev)
+#----------------------------------------------------------------------
+# Test for issue #4022 'svn log -g interprets change in inherited mergeinfo
+# due to move as a merge'.
+@Issue(4022)
+def merge_sensitive_log_copied_path_inherited_mergeinfo(sbox):
+ "log -g on copied path with inherited mergeinfo"
+
+ sbox.build()
+ wc_dir = sbox.wc_dir
+ wc_disk, wc_status = set_up_branch(sbox, branch_only=True)
+
+ A_path = os.path.join(wc_dir, 'A')
+ gamma_COPY_path = os.path.join(wc_dir, 'A_COPY', 'D', 'gamma')
+ old_gamma_path = os.path.join(wc_dir, 'A', 'D', 'gamma')
+ new_gamma_path = os.path.join(wc_dir, 'A', 'C', 'gamma')
+
+ # r3 - Modify a file (A_COPY/D/gamma) on the branch
+ svntest.main.file_write(gamma_COPY_path, "Branch edit.\n")
+ svntest.main.run_svn(None, 'ci', '-m', 'Branch edit', wc_dir)
+
+ # r4 - Reintegrate A_COPY to A
+ svntest.main.run_svn(None, 'up', wc_dir)
+ svntest.main.run_svn(None, 'merge', '--reintegrate',
+ sbox.repo_url + '/A_COPY', A_path)
+ svntest.main.run_svn(None, 'ci', '-m', 'Reintegrate A_COPY to A', wc_dir)
+
+ # r5 - Move file modified by reintegrate (A/D/gamma to A/C/gamma).
+ svntest.main.run_svn(None, 'move', old_gamma_path, new_gamma_path)
+ svntest.main.run_svn(None, 'ci', '-m', 'Move file', wc_dir)
+
+ # 'svn log -g --stop-on-copy ^/A/C/gamma' hould return *only* r5
+ # Previously this test failed because the change in gamma's inherited
+ # mergeinfo between r4 and r5, due to the move, was understood as a merge:
+ #
+ # >svn log -v -g --stop-on-copy ^^/A/C/gamma
+ # ------------------------------------------------------------------------
+ # r5 | jrandom | 2011-10-11 14:37:57 -0700 (Tue, 11 Oct 2011) | 1 line #
+ # Changed paths:
+ # A /A/C/gamma (from /A/D/gamma:4)
+ # D /A/D/gamma
+ #
+ # Move file
+ # ------------------------------------------------------------------------
+ # r3 | jrandom | 2011-10-11 14:37:56 -0700 (Tue, 11 Oct 2011) | 1 line
+ # Changed paths:
+ # M /A_COPY/D/gamma
+ # Reverse merged via: r5
+ #
+ # Branch edit
+ # ------------------------------------------------------------------------
+ # r2 | jrandom | 2011-10-11 14:37:56 -0700 (Tue, 11 Oct 2011) | 1 line
+ # Changed paths:
+ # A /A_COPY (from /A:1)
+ # Reverse merged via: r5
+ #
+ # log msg
+ # ------------------------------------------------------------------------
+ expected_merges = {5 : []}
+ svntest.main.run_svn(None, 'up', wc_dir)
+ exit_code, out, err = svntest.actions.run_and_verify_svn(
+ None, None, [], 'log', '-g', '--stop-on-copy',
+ sbox.repo_url + '/A/C/gamma')
+ log_chain = parse_log_output(out)
+ check_merge_results(log_chain, expected_merges)
+
+#----------------------------------------------------------------------
+def log_diff(sbox):
+ "'svn log --diff'"
+
+ guarantee_repos_and_wc(sbox)
+
+ was_cwd = os.getcwd()
+ os.chdir(sbox.wc_dir)
+
+ exit_code, output, err = svntest.actions.run_and_verify_svn(None, None, [],
+ 'log', '--diff')
+ os.chdir(was_cwd)
+
+ for line in output:
+ if line.startswith('Index:'):
+ break
+ else:
+ raise SVNLogParseError("no diffs found in log output")
+
+ # After a copy, a log of the copy destination used to fail because the
+ # diff tried to use the head-revision URL with the old revision numbers
+ # without using the correct peg revision.
+
+ sbox.simple_copy('A', 'A2')
+ sbox.simple_commit()
+
+ os.chdir(sbox.wc_dir)
+ exit_code, output, err = svntest.actions.run_and_verify_svn(None, None, [],
+ 'log', '--diff',
+ '-r10:8', 'A2')
+ os.chdir(was_cwd)
+
+ r9diff = [ make_no_diff_deleted_header('A2/B/E/alpha', 8, 9),
+ make_diff_header('A2/B/E/beta', 'revision 8', 'revision 9')
+ + [ "@@ -1 +1,2 @@\n",
+ " This is the file 'beta'.\n",
+ "+9\n",
+ "\ No newline at end of file\n",
+ ]
+ ]
+ r8diff = [ make_diff_header('A2/D/G/rho', 'revision 0', 'revision 8')
+ + [ "@@ -0,0 +1 @@\n",
+ "+88\n",
+ "\ No newline at end of file\n",
+ ]
+ ]
+ log_chain = parse_log_output(output, with_diffs=True)
+ if len(log_chain) != 3:
+ raise SVNLogParseError("%d logs found, 3 expected" % len(log_chain))
+ compare_diff_output(r9diff, log_chain[1]['diff_lines'])
+ compare_diff_output(r8diff, log_chain[2]['diff_lines'])
+
+
########################################################################
# Run the tests
@@ -2067,6 +2242,8 @@ test_list = [ None,
merge_sensitive_log_ignores_cyclic_merges,
log_with_unrelated_peg_and_operative_revs,
log_on_nonexistent_path_and_valid_rev,
+ merge_sensitive_log_copied_path_inherited_mergeinfo,
+ log_diff,
]
if __name__ == '__main__':
Modified: subversion/branches/revprop-packing/subversion/tests/cmdline/merge_authz_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/revprop-packing/subversion/tests/cmdline/merge_authz_tests.py?rev=1231318&r1=1231317&r2=1231318&view=diff
==============================================================================
--- subversion/branches/revprop-packing/subversion/tests/cmdline/merge_authz_tests.py (original)
+++ subversion/branches/revprop-packing/subversion/tests/cmdline/merge_authz_tests.py Fri Jan 13 21:40:26 2012
@@ -73,7 +73,10 @@ from svntest.actions import inject_confl
# This is *not* a full test of issue #2829, see also merge_tests.py,
# search for "2829". This tests the problem where a merge adds a path
# with a missing sibling and so needs its own explicit mergeinfo.
-@Issues(2893,2997,2829)
+#
+# #4056 - Don't record non-inheritable mergeinfo if missing subtrees are not
+# touched by the full-depth diff
+@Issues(2893,2997,2829,4056)
@SkipUnless(svntest.main.server_has_mergeinfo)
@Skip(svntest.main.is_ra_type_file)
def mergeinfo_and_skipped_paths(sbox):
@@ -393,10 +396,54 @@ def mergeinfo_and_skipped_paths(sbox):
# Merge -r7:9 to the restricted WC's A_COPY_2/D/H.
#
+ # r9 adds a path, 'A_COPY_2/D/H/zeta', which has a missing sibling 'psi',
+ # but since 'psi' is untouched by the merge it isn't skipped, and since it
+ # isn't skipped, its parent 'A_COPY_2/D/H' won't get non-inheritable
+ # mergeinfo set on it to describe the merge, so none of the parent's
+ # children will get explicit mergeinfo -- see issue #4056.
+ expected_output = wc.State(A_COPY_2_H_path, {
+ 'omega' : Item(status='U '),
+ 'zeta' : Item(status='A '),
+ })
+ expected_mergeinfo_output = wc.State(A_COPY_2_H_path, {
+ '' : Item(status=' U'),
+ 'omega' : Item(status=' U'),
+ })
+ expected_elision_output = wc.State(A_COPY_2_H_path, {
+ 'omega' : Item(status=' U'),
+ })
+ expected_status = wc.State(A_COPY_2_H_path, {
+ '' : Item(status=' M', wc_rev=8),
+ 'chi' : Item(status=' ', wc_rev=8),
+ 'omega' : Item(status='M ', wc_rev=8),
+ 'zeta' : Item(status='A ', copied='+', wc_rev='-'),
+ })
+ expected_disk = wc.State('', {
+ '' : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:8-9'}),
+ 'omega' : Item("New content"),
+ 'chi' : Item("This is the file 'chi'.\n"),
+ 'zeta' : Item("This is the file 'zeta'.\n"),
+ })
+ expected_skip = wc.State(A_COPY_2_H_path, {})
+ svntest.actions.run_and_verify_merge(A_COPY_2_H_path, '7', '9',
+ sbox.repo_url + '/A/D/H', None,
+ expected_output,
+ expected_mergeinfo_output,
+ expected_elision_output,
+ expected_disk,
+ expected_status,
+ expected_skip,
+ None, None, None, None,
+ None, 1, 0)
+
+ # Merge -r4:9 to the restricted WC's A_COPY_2/D/H.
+ #
# r9 adds a path, 'A_COPY_2/D/H/zeta', which has a parent with
- # non-inheritable mergeinfo (due to the fact 'A_COPY_2/D/H/psi' is missing).
- # 'A_COPY_2/D/H/zeta' must therefore get its own explicit mergeinfo from
- # this merge.
+ # non-inheritable mergeinfo (due to the fact 'A_COPY_2/D/H/psi' is missing
+ # and skipped). 'A_COPY_2/D/H/zeta' must therefore get its own explicit
+ # mergeinfo from this merge.
+ svntest.actions.run_and_verify_svn(None, None, [], 'revert', '--recursive',
+ wc_restricted)
expected_output = wc.State(A_COPY_2_H_path, {
'omega' : Item(status='U '),
'zeta' : Item(status='A '),
@@ -415,15 +462,17 @@ def mergeinfo_and_skipped_paths(sbox):
'zeta' : Item(status='A ', copied='+', wc_rev='-'),
})
expected_disk = wc.State('', {
- '' : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:8-9*'}),
+ '' : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:5-9*'}),
'omega' : Item("New content",
- props={SVN_PROP_MERGEINFO : '/A/D/H/omega:8-9'}),
+ props={SVN_PROP_MERGEINFO : '/A/D/H/omega:5-9'}),
'chi' : Item("This is the file 'chi'.\n"),
'zeta' : Item("This is the file 'zeta'.\n",
props={SVN_PROP_MERGEINFO : '/A/D/H/zeta:9'}),
})
- expected_skip = wc.State(A_COPY_2_H_path, {})
- svntest.actions.run_and_verify_merge(A_COPY_2_H_path, '7', '9',
+ expected_skip = wc.State(A_COPY_2_H_path, {
+ 'psi' : Item(),
+ })
+ svntest.actions.run_and_verify_merge(A_COPY_2_H_path, '4', '9',
sbox.repo_url + '/A/D/H', None,
expected_output,
expected_mergeinfo_output,
Modified: subversion/branches/revprop-packing/subversion/tests/cmdline/merge_reintegrate_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/revprop-packing/subversion/tests/cmdline/merge_reintegrate_tests.py?rev=1231318&r1=1231317&r2=1231318&view=diff
==============================================================================
--- subversion/branches/revprop-packing/subversion/tests/cmdline/merge_reintegrate_tests.py (original)
+++ subversion/branches/revprop-packing/subversion/tests/cmdline/merge_reintegrate_tests.py Fri Jan 13 21:40:26 2012
@@ -2435,7 +2435,7 @@ def reintegrate_replaced_source(sbox):
# Using cherrypick merges, simulate a series of sync merges from A to
# A_COPY with a replace of A_COPY along the way.
#
- # r6 - Merge r3 from A to A_COPY
+ # r7 - Merge r3 from A to A_COPY
svntest.main.run_svn(None, 'up', wc_dir)
svntest.main.run_svn(None, 'merge', sbox.repo_url + '/A', A_COPY_path,
'-c3')
Modified: subversion/branches/revprop-packing/subversion/tests/cmdline/merge_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/revprop-packing/subversion/tests/cmdline/merge_tests.py?rev=1231318&r1=1231317&r2=1231318&view=diff
==============================================================================
--- subversion/branches/revprop-packing/subversion/tests/cmdline/merge_tests.py (original)
+++ subversion/branches/revprop-packing/subversion/tests/cmdline/merge_tests.py Fri Jan 13 21:40:26 2012
@@ -49,15 +49,31 @@ from svntest.actions import make_conflic
from svntest.actions import inject_conflict_into_expected_state
def expected_merge_output(rev_ranges, additional_lines=None, foreign=False,
- elides=False, two_url=False):
+ elides=False, two_url=False, target=None,
+ text_conflicts=0, prop_conflicts=0, tree_conflicts=0):
"""Generate an (inefficient) regex representing the expected merge
- output and mergeinfo notifications from REV_RANGES (a list of 'range' lists
- of the form [start, end] or [single_rev] --> [single_rev - 1, single_rev]),
- and ADDITIONAL_LINES (a list of strings). If REV_RANGES is None then only
- the standard notification for a 3-way merge is expected. If ELIDES is true
- add to the regex an expression representing elision notification. If TWO_URL
- us true tweak the regex to expect the appropriate mergeinfo notification
- for a 3-way merge."""
+ output and mergeinfo notifications from REV_RANGES and ADDITIONAL_LINES.
+
+ REV_RANGES is a list of revision ranges for which mergeinfo is being
+ recorded. Each range is of the form [start, end] (where both START and
+ END are inclusive, unlike in '-rX:Y') or the form [single_rev] (which is
+ like '-c SINGLE_REV'). If REV_RANGES is None then only the standard
+ notification for a 3-way merge is expected.
+
+ ADDITIONAL_LINES is a list of strings to match the other lines of output;
+ these are basically regular expressions except that backslashes will be
+ escaped herein.
+
+ If ELIDES is true, add to the regex an expression representing elision
+ notification. If TWO_URL is true, tweak the regex to expect the
+ appropriate mergeinfo notification for a 3-way merge.
+
+ TARGET is the local path to the target, as it should appear in
+ notifications; if None, it is not checked.
+
+ TEXT_CONFLICTS, PROP_CONFLICTS and TREE_CONFLICTS specify the number of
+ each kind of conflict to expect."""
+
if rev_ranges is None:
lines = [svntest.main.merge_notify_line(None, None, False, foreign)]
else:
@@ -69,8 +85,8 @@ def expected_merge_output(rev_ranges, ad
else:
end_rev = None
lines += [svntest.main.merge_notify_line(start_rev, end_rev,
- True, foreign)]
- lines += [svntest.main.mergeinfo_notify_line(start_rev, end_rev)]
+ True, foreign, target)]
+ lines += [svntest.main.mergeinfo_notify_line(start_rev, end_rev, target)]
if (elides):
lines += ["--- Eliding mergeinfo from .*\n"]
@@ -94,6 +110,16 @@ def expected_merge_output(rev_ranges, ad
if sys.platform == 'win32' and additional_lines != None:
additional_lines = additional_lines.replace("\\", "\\\\")
lines.append(str(additional_lines))
+
+ if text_conflicts or prop_conflicts or tree_conflicts:
+ lines.append("Summary of conflicts:\n")
+ if text_conflicts:
+ lines.append(" Text conflicts: %d\n" % text_conflicts)
+ if prop_conflicts:
+ lines.append(" Property conflicts: %d\n" % prop_conflicts)
+ if tree_conflicts:
+ lines.append(" Tree conflicts: %d\n" % tree_conflicts)
+
return "|".join(lines)
def check_mergeinfo_recursively(root_path, subpaths_mergeinfo):
@@ -835,7 +861,8 @@ def merge_similar_unrelated_trees(sbox):
#----------------------------------------------------------------------
def merge_one_file_helper(sbox, arg_flav, record_only = 0):
- "ARG_FLAV is one of 'r' (revision range) or 'c' (single change)."
+ """ARG_FLAV is one of 'r' (revision range) or 'c' (single change) or
+ '*' (no revision specified)."""
if arg_flav not in ('r', 'c', '*'):
raise svntest.Failure("Unrecognized flavor of merge argument")
@@ -972,9 +999,13 @@ def merge_record_only(sbox):
merge_one_file_helper(sbox, 'r', 1)
#----------------------------------------------------------------------
-# This is a regression for the enhancement added in issue #785.
+# This is a regression test for the enhancement added in issue #785 "add
+# friendly enhancement to 'svn merge'", which is about inferring that
+# the default target of "svn merge [-r...] FILE" should not be "." but
+# rather should be "FILE".
def merge_with_implicit_target_helper(sbox, arg_flav):
- "ARG_FLAV is one of 'r' (revision range) or 'c' (single change)."
+ """ARG_FLAV is one of 'r' (revision range) or 'c' (single change) or
+ '*' (no revision specified)."""
if arg_flav not in ('r', 'c', '*'):
raise svntest.Failure("Unrecognized flavor of merge argument")
@@ -2439,7 +2470,7 @@ def set_up_dir_replace(sbox):
# A merge that replaces a directory
# Tests for Issue #2144 and Issue #2607
@SkipUnless(server_has_mergeinfo)
-@Issue(2144)
+@Issue(2144,2607)
def merge_dir_replace(sbox):
"merge a replacement of a directory"
@@ -5573,7 +5604,7 @@ def merge_to_switched_path(sbox):
# 3188: Mergeinfo on switched targets/subtrees should
# elide to repos
@SkipUnless(server_has_mergeinfo)
-@Issue(2823,2839,3187,3188)
+@Issue(2823,2839,3187,3188,4056)
def merge_to_path_with_switched_children(sbox):
"merge to path with switched children"
@@ -5668,18 +5699,18 @@ def merge_to_path_with_switched_children
'omega' : Item(status=' U')
})
expected_elision_output = wc.State(A_COPY_H_path, {
+ 'omega' : Item(status=' U')
})
expected_status = wc.State(A_COPY_H_path, {
'' : Item(status=' M', wc_rev=8),
'psi' : Item(status=' ', wc_rev=8, switched='S'),
- 'omega' : Item(status='MM', wc_rev=8),
+ 'omega' : Item(status='M ', wc_rev=8),
'chi' : Item(status=' ', wc_rev=8),
})
expected_disk = wc.State('', {
- '' : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:8*'}),
+ '' : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:8'}),
'psi' : Item("This is the file 'psi'.\n"),
- 'omega' : Item("New content",
- props={SVN_PROP_MERGEINFO : '/A/D/H/omega:8'}),
+ 'omega' : Item("New content"),
'chi' : Item("This is the file 'chi'.\n"),
})
expected_skip = wc.State(A_COPY_H_path, { })
@@ -5713,7 +5744,7 @@ def merge_to_path_with_switched_children
'' : Item(status=' M', wc_rev=8),
'H' : Item(status=' M', wc_rev=8),
'H/chi' : Item(status=' ', wc_rev=8),
- 'H/omega' : Item(status='MM', wc_rev=8),
+ 'H/omega' : Item(status='M ', wc_rev=8),
'H/psi' : Item(status=' ', wc_rev=8, switched='S'),
'G' : Item(status=' M', wc_rev=8, switched='S'),
'G/pi' : Item(status=' ', wc_rev=8),
@@ -5723,10 +5754,9 @@ def merge_to_path_with_switched_children
})
expected_disk_D = wc.State('', {
'' : Item(props={SVN_PROP_MERGEINFO : '/A/D:6*'}),
- 'H' : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:8*'}),
+ 'H' : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:8'}),
'H/chi' : Item("This is the file 'chi'.\n"),
- 'H/omega' : Item("New content",
- props={SVN_PROP_MERGEINFO : '/A/D/H/omega:8'}),
+ 'H/omega' : Item("New content"),
'H/psi' : Item("This is the file 'psi'.\n",),
'G' : Item(props={SVN_PROP_MERGEINFO : '/A/D/G:6*'}),
'G/pi' : Item("This is the file 'pi'.\n"),
@@ -5760,10 +5790,10 @@ def merge_to_path_with_switched_children
})
expected_elision_output = wc.State(A_COPY_D_path, {
})
- expected_disk_D.tweak('', props={SVN_PROP_MERGEINFO : '/A/D:5-6*'})
- expected_disk_D.tweak('H', props={SVN_PROP_MERGEINFO : '/A/D/H:5*,8*'})
+ expected_disk_D.tweak('', props={SVN_PROP_MERGEINFO : '/A/D:5,6*'})
+ expected_disk_D.tweak('H', props={SVN_PROP_MERGEINFO : '/A/D/H:5*,8'})
expected_disk_D.tweak('H/psi', contents="New content",
- props={SVN_PROP_MERGEINFO :'/A/D/H/psi:5'})
+ props={SVN_PROP_MERGEINFO :'/A/D/H/psi:5,8'})
expected_status_D.tweak('H/psi', status='MM')
svntest.actions.run_and_verify_merge(A_COPY_D_path, '4', '5',
sbox.repo_url + '/A/D', None,
@@ -5804,7 +5834,7 @@ def merge_to_path_with_switched_children
'D/H' : Item(status=' M', wc_rev=8),
'D/H/chi' : Item(status=' ', wc_rev=8),
'D/H/psi' : Item(status='MM', wc_rev=8, switched='S'),
- 'D/H/omega' : Item(status='MM', wc_rev=8),
+ 'D/H/omega' : Item(status='M ', wc_rev=8),
})
expected_disk = wc.State('', {
'' : Item(props={SVN_PROP_MERGEINFO : '/A:5-8'}),
@@ -5816,19 +5846,18 @@ def merge_to_path_with_switched_children
'B/lambda' : Item("This is the file 'lambda'.\n"),
'B/F' : Item(),
'C' : Item(),
- 'D' : Item(props={SVN_PROP_MERGEINFO : '/A/D:5-6*'}),
+ 'D' : Item(props={SVN_PROP_MERGEINFO : '/A/D:5,6*'}),
'D/G' : Item(props={SVN_PROP_MERGEINFO : '/A/D/G:6*'}),
'D/G/pi' : Item("This is the file 'pi'.\n"),
'D/G/rho' : Item("New content",
props={SVN_PROP_MERGEINFO : '/A/D/G/rho:6'}),
'D/G/tau' : Item("This is the file 'tau'.\n"),
'D/gamma' : Item("This is the file 'gamma'.\n"),
- 'D/H' : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:5*,8*'}),
+ 'D/H' : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:5*,8'}),
'D/H/chi' : Item("This is the file 'chi'.\n"),
'D/H/psi' : Item("New content",
- props={SVN_PROP_MERGEINFO : '/A/D/H/psi:5'}),
- 'D/H/omega' : Item("New content",
- props={SVN_PROP_MERGEINFO : '/A/D/H/omega:8'}),
+ props={SVN_PROP_MERGEINFO : '/A/D/H/psi:5,8'}),
+ 'D/H/omega' : Item("New content"),
})
expected_skip = wc.State(A_COPY_path, { })
svntest.actions.run_and_verify_merge(A_COPY_path, '4', '8',
@@ -5839,7 +5868,6 @@ def merge_to_path_with_switched_children
expected_disk,
expected_status, expected_skip,
None, None, None, None, None, 1)
-
# Commit changes thus far.
expected_output = svntest.wc.State(wc_dir, {
'A_COPY' : Item(verb='Sending'),
@@ -5866,17 +5894,16 @@ def merge_to_path_with_switched_children
wc_disk.tweak("A_COPY/B/E/beta",
contents="New content")
wc_disk.tweak("A_COPY/D",
- props={SVN_PROP_MERGEINFO : '/A/D:5-6*'})
+ props={SVN_PROP_MERGEINFO : '/A/D:5,6*'})
wc_disk.tweak("A_COPY/D/G",
props={SVN_PROP_MERGEINFO : '/A/D/G:6*'})
wc_disk.tweak("A_COPY/D/G/rho",
contents="New content",
props={SVN_PROP_MERGEINFO : '/A/D/G/rho:6'})
wc_disk.tweak("A_COPY/D/H",
- props={SVN_PROP_MERGEINFO : '/A/D/H:5*,8*'})
+ props={SVN_PROP_MERGEINFO : '/A/D/H:5*,8'})
wc_disk.tweak("A_COPY/D/H/omega",
- contents="New content",
- props={SVN_PROP_MERGEINFO : '/A/D/H/omega:8'})
+ contents="New content")
wc_disk.tweak("A_COPY_2", props={})
svntest.actions.run_and_verify_switch(sbox.wc_dir, A_COPY_psi_path,
sbox.repo_url + "/A_COPY/D/H/psi",
@@ -5915,8 +5942,7 @@ def merge_to_path_with_switched_children
expected_disk = wc.State('', {
'' : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:5-8'}),
'psi' : Item("New content"),
- 'omega' : Item("New content",
- props={SVN_PROP_MERGEINFO : '/A/D/H/omega:8'}),
+ 'omega' : Item("New content"),
'chi' : Item("This is the file 'chi'.\n"),
})
expected_skip = wc.State(A_COPY_H_path, { })
@@ -5966,14 +5992,11 @@ def merge_to_path_with_switched_children
expected_status_D.tweak('H/psi', wc_rev=10, switched=None)
expected_status_D.tweak('H/omega', wc_rev=9)
expected_status_D.tweak('G', 'G/rho', switched='S', wc_rev=9)
- expected_disk_D.tweak('', props={SVN_PROP_MERGEINFO : '/A/D:5-6*,10*',
+ expected_disk_D.tweak('', props={SVN_PROP_MERGEINFO : '/A/D:5,6*,10',
"prop:name" : "propval"})
expected_disk_D.tweak('G/rho',
props={SVN_PROP_MERGEINFO : '/A/D/G/rho:6'})
expected_disk_D.tweak('H', props={SVN_PROP_MERGEINFO : '/A/D/H:5-8'})
-
- expected_disk_D.tweak('H/omega',
- props={SVN_PROP_MERGEINFO : '/A/D/H/omega:8'})
expected_disk_D.tweak('H/psi', contents="New content", props={})
svntest.actions.run_and_verify_merge(A_COPY_D_path, '9', '10',
sbox.repo_url + '/A/D', None,
@@ -6034,7 +6057,6 @@ def merge_to_path_with_switched_children
'D/G' : Item(status=' U'),
'D/G/rho' : Item(status=' U'),
'D/H' : Item(status=' U'),
- 'D/H/omega' : Item(status=' U'),
})
expected_elision_output = wc.State(A_COPY_path, {
'' : Item(status=' U'),
@@ -6042,7 +6064,6 @@ def merge_to_path_with_switched_children
'D/G' : Item(status=' U'),
'D/G/rho' : Item(status=' U'),
'D/H' : Item(status=' U'),
- 'D/H/omega' : Item(status=' U'),
})
expected_status = wc.State(A_COPY_path, {
'' : Item(status=' M', wc_rev=10),
@@ -6063,7 +6084,7 @@ def merge_to_path_with_switched_children
'D/H' : Item(status=' M', wc_rev=10),
'D/H/chi' : Item(status=' ', wc_rev=10),
'D/H/psi' : Item(status='M ', wc_rev=10),
- 'D/H/omega' : Item(status='MM', wc_rev=10),
+ 'D/H/omega' : Item(status='M ', wc_rev=10),
})
expected_disk = wc.State('', {
'B' : Item(),
@@ -7260,7 +7281,7 @@ def merge_with_depth_files(sbox):
#
# Test issue #3407 'Shallow merges incorrectly set mergeinfo on children'.
@SkipUnless(server_has_mergeinfo)
-@Issues(2976,3392,3407)
+@Issues(2976,3392,3407,4057)
def merge_away_subtrees_noninheritable_ranges(sbox):
"subtrees can lose non-inheritable ranges"
@@ -7579,8 +7600,9 @@ def merge_away_subtrees_noninheritable_r
svntest.actions.run_and_verify_svn(None, None, [], 'revert', '-R', wc_dir)
svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
- # Merge r8 from A/D/H to A_COPY_D/H at depth empty, creating non-inheritable
- # mergeinfo on the target. Commit this merge as r13.
+ # Merge r8 from A/D/H to A_COPY_D/H at depth empty. Since r8 affects only
+ # A_COPY/D/H itself, the resulting mergeinfo is inheritabled. Commit this
+ # merge as r13.
expected_output = wc.State(H_COPY_2_path, {
'' : Item(status=' U'),
})
@@ -7596,7 +7618,7 @@ def merge_away_subtrees_noninheritable_r
'chi' : Item(status=' ', wc_rev=12),
})
expected_disk = wc.State('', {
- '' : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:8*',
+ '' : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:8',
"prop:name" : "propval"}),
'psi' : Item("This is the file 'psi'.\n"),
'omega' : Item("This is the file 'omega'.\n"),
@@ -9308,6 +9330,7 @@ def ignore_ancestry_and_mergeinfo(sbox):
#----------------------------------------------------------------------
@SkipUnless(server_has_mergeinfo)
+@Issue(3032)
def merge_from_renamed_branch_fails_while_avoiding_repeat_merge(sbox):
"merge from renamed branch"
#Copy A/C to A/COPY_C results in r2.
@@ -9693,7 +9716,7 @@ def new_subtrees_should_not_break_merge(
'D/H/psi' : Item("This is the file 'psi'.\n"),
'D/H/omega' : Item("New content"),
'D/H/nu' : Item("New content",
- props={SVN_PROP_MERGEINFO : '/A/D/H/nu:8'}),
+ props={SVN_PROP_MERGEINFO : '/A/D/H/nu:7-8'}),
})
expected_skip = wc.State(A_COPY_path, { })
svntest.actions.run_and_verify_merge(A_COPY_path, '4', '6',
@@ -9739,7 +9762,7 @@ def new_subtrees_should_not_break_merge(
'H/psi' : Item("This is the file 'psi'.\n"),
'H/omega' : Item("This is the file 'omega'.\n"),
'H/nu' : Item("New content",
- props={SVN_PROP_MERGEINFO : '/A/D/H/nu:8'}),
+ props={SVN_PROP_MERGEINFO : '/A/D/H/nu:7-8'}),
})
expected_skip = wc.State(D_COPY_path, { })
svntest.actions.run_and_verify_merge(D_COPY_path, '6', '5',
@@ -9811,7 +9834,7 @@ def new_subtrees_should_not_break_merge(
'D/H/psi' : Item("This is the file 'psi'.\n"),
'D/H/omega' : Item("New content"),
'D/H/nu' : Item("New content",
- props={SVN_PROP_MERGEINFO : '/A/D/H/nu:8'}),
+ props={SVN_PROP_MERGEINFO : '/A/D/H/nu:7-8'}),
})
expected_skip = wc.State(A_COPY_path, { })
svntest.actions.run_and_verify_merge(A_COPY_path, '5', '6',
@@ -12508,24 +12531,46 @@ def svn_copy(s_rev, path1, path2):
svntest.actions.run_and_verify_svn(None, None, [], 'copy', '--parents',
'-r', s_rev, path1, path2)
-def svn_merge(rev_spec, source, target, exp_out=None, *args):
- """Merge a single change from path 'source' to path 'target'.
- SRC_CHANGE_NUM is either a number (to cherry-pick that specific change)
- or a command-line option revision range string such as '-r10:20'.
- *ARGS are additional arguments passed to svn merge."""
+def svn_merge(rev_range, source, target, lines=None, elides=[],
+ text_conflicts=0, prop_conflicts=0, tree_conflicts=0, args=[]):
+ """Merge a single change from path SOURCE to path TARGET and verify the
+ output and that there is no error. (The changes made are not verified.)
+
+ REV_RANGE is either a number (to cherry-pick that specific change) or a
+ two-element list [X,Y] to pick the revision range '-r(X-1):Y'.
+
+ LINES is a list of regular expressions to match other lines of output; if
+ LINES is 'None' then match all normal (non-conflicting) merges.
+
+ ELIDES is a list of paths on which mergeinfo elision should be reported.
+
+ TEXT_CONFLICTS, PROP_CONFLICTS and TREE_CONFLICTS specify the number of
+ each kind of conflict to expect.
+
+ ARGS are additional arguments passed to svn merge."""
+
source = local_path(source)
target = local_path(target)
- if isinstance(rev_spec, int):
- rev_spec = '-c' + str(rev_spec)
- if exp_out is None:
- target_re = re.escape(target)
- exp_1 = "--- Merging r.* into '" + target_re + ".*':"
- exp_2 = "(A |D |[UG] | [UG]|[UG][UG]) " + target_re + ".*"
- exp_3 = "--- Recording mergeinfo for merge of r.* into '" + \
- target_re + ".*':"
- exp_out = svntest.verify.RegexOutput(exp_1 + "|" + exp_2 + "|" + exp_3)
+ elides = [local_path(p) for p in elides]
+ if isinstance(rev_range, int):
+ mi_rev_range = [rev_range]
+ rev_arg = '-c' + str(rev_range)
+ else:
+ mi_rev_range = rev_range
+ rev_arg = '-r' + str(rev_range[0] - 1) + ':' + str(rev_range[1])
+ if lines is None:
+ lines = ["(A |D |[UG] | [UG]|[UG][UG]) " + target + ".*\n"]
+ else:
+ # Expect mergeinfo on the target; caller must supply matches for any
+ # subtree mergeinfo paths.
+ lines.append(" [UG] " + target + "\n")
+ exp_out = expected_merge_output([mi_rev_range], lines, target=target,
+ elides=elides,
+ text_conflicts=text_conflicts,
+ prop_conflicts=prop_conflicts,
+ tree_conflicts=tree_conflicts)
svntest.actions.run_and_verify_svn(None, exp_out, [],
- 'merge', rev_spec, source, target, *args)
+ 'merge', rev_arg, source, target, *args)
#----------------------------------------------------------------------
# Tests for merging the deletion of a node, where the node to be deleted
@@ -12557,7 +12602,8 @@ def del_identical_file(sbox):
svn_copy(s_rev_mod, source, target)
sbox.simple_commit(target)
# Should be deleted quietly.
- svn_merge(s_rev_del, source, target, '--- Merging|D |--- Recording| U')
+ svn_merge(s_rev_del, source, target,
+ ['D %s\n' % local_path('A/D/G2/tau')])
# Make a differing copy, locally modify it so it's the same,
# and merge a deletion to it.
@@ -12566,7 +12612,8 @@ def del_identical_file(sbox):
sbox.simple_commit(target)
svn_modfile(target+"/tau")
# Should be deleted quietly.
- svn_merge(s_rev_del, source, target, '--- Merging|D |--- Recording| U')
+ svn_merge(s_rev_del, source, target,
+ ['D %s\n' % local_path('A/D/G3/tau')])
os.chdir(saved_cwd)
@@ -12593,10 +12640,11 @@ def del_sched_add_hist_file(sbox):
svn_copy(s_rev_orig, source, target)
sbox.simple_commit(target)
s_rev = 3
- svn_merge(s_rev_add, source, target, '--- Merging|A |--- Recording| U')
+ svn_merge(s_rev_add, source, target,
+ ['A %s\n' % local_path('A/D/G2/file')])
# Should be deleted quietly.
svn_merge(-s_rev_add, source, target,
- '--- Reverse-merging|D |--- Recording| U| G|--- Eliding')
+ ['D %s\n' % local_path('A/D/G2/file')], elides=['A/D/G2'])
os.chdir(saved_cwd)
@@ -12890,12 +12938,6 @@ def merge_target_and_subtrees_need_nonin
None, expected_merge_output([[8]], ['U ' + nu_COPY_path + '\n',
' G ' + nu_COPY_path + '\n']),
[], 'merge', '-c8', sbox.repo_url + '/A/D/G/nu', nu_COPY_path)
- # Replicate pre 1.7 merge behavior where self-referential mergeinfo
- # could be inherited, this keeps the original intent of this test intact,
- # see http://subversion.tigris.org/issues/show_bug.cgi?id=3669#desc8
- svntest.actions.run_and_verify_svn(None, None, [], 'ps',
- SVN_PROP_MERGEINFO, '/A/D/G/nu:2-8',
- nu_COPY_path)
svntest.actions.run_and_verify_svn(
None, expected_merge_output([[-6]], ['G ' + omega_COPY_path + '\n',
@@ -13057,15 +13099,9 @@ def merge_two_edits_to_same_prop(sbox):
rev4 = initial_rev + 4
# Merge the two changes together to source.
- svn_merge('-r'+str(rev3-1)+':'+str(rev4), A_COPY_path, A_path, [
- "--- Merging r9 through r10 into '%s':\n" % A_path,
+ svn_merge([rev3, rev4], A_COPY_path, A_path, [
" C %s\n" % mu_path,
- "--- Recording mergeinfo for merge of r9 through r10 into '%s':\n" \
- % A_path,
- " U A\n",
- "Summary of conflicts:\n",
- " Property conflicts: 1\n"],
- '--allow-mixed-revisions')
+ ], prop_conflicts=1, args=['--allow-mixed-revisions'])
# Revert changes to source wc, to test next scenario of #3250
svntest.actions.run_and_verify_svn(None, None, [],
@@ -13073,21 +13109,11 @@ def merge_two_edits_to_same_prop(sbox):
# Merge the first change, then the second, to source.
svn_merge(rev3, A_COPY_path, A_path, [
- "--- Merging r9 into '%s':\n" % A_path,
" C %s\n" % mu_path,
- "--- Recording mergeinfo for merge of r9 into '%s':\n" % A_path,
- " U A\n",
- "Summary of conflicts:\n",
- " Property conflicts: 1\n"],
- '--allow-mixed-revisions')
+ ], prop_conflicts=1, args=['--allow-mixed-revisions'])
svn_merge(rev4, A_COPY_path, A_path, [
- "--- Merging r10 into '%s':\n" % A_path,
" C %s\n" % mu_path,
- "--- Recording mergeinfo for merge of r10 into '%s':\n" % A_path,
- " G A\n",
- "Summary of conflicts:\n",
- " Property conflicts: 1\n"],
- '--allow-mixed-revisions')
+ ], prop_conflicts=1, args=['--allow-mixed-revisions'])
os.chdir(was_cwd)
@@ -13136,8 +13162,8 @@ def merge_an_eol_unification_and_set_svn
sbox.simple_commit('A_COPY')
# Merge the two changes together to the target branch.
- svn_merge('-r'+str(rev1)+':'+str(rev3), 'A', 'A_COPY', None,
- '--allow-mixed-revisions')
+ svn_merge([rev2, rev3], 'A', 'A_COPY',
+ args=['--allow-mixed-revisions'])
# That merge should succeed.
# Surprise: setting svn:eol-style='LF' instead of 'native' doesn't fail.
@@ -16242,7 +16268,8 @@ def merge_with_os_deleted_subtrees(sbox)
# Test for issue #3668 'inheritance can result in self-referential
# mergeinfo' and issue #3669 'inheritance can result in mergeinfo
# describing nonexistent sources'
-@Issue(3668)
+@Issue(3668,3669)
+@XFail()
def no_self_referential_or_nonexistent_inherited_mergeinfo(sbox):
"don't inherit bogus mergeinfo"
@@ -16303,6 +16330,10 @@ def no_self_referential_or_nonexistent_i
# Update the WC in preparation for merges.
svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
+ # This test is marked as XFail because the following two merges
+ # create mergeinfo with both non-existent path-revs and self-referential
+ # mergeinfo.
+ #
# Merge all available revisions from A/C/nu to A_COPY/C/nu.
# The target has no explicit mergeinfo of its own but inherits mergeinfo
# from A_COPY. A_COPY has the mergeinfo '/A:2-9' so the naive mergeinfo
@@ -16680,11 +16711,9 @@ def foreign_repos_prop_conflict(sbox):
# Now, merge the propchange to the *second* working copy.
expected_output = [' C %s\n' % (os.path.join(other_wc_dir,
- "A", "D", "G")),
- 'Summary of conflicts:\n',
- ' Property conflicts: 1\n',
- ]
- expected_output = expected_merge_output([[3]], expected_output, True)
+ "A", "D", "G"))]
+ expected_output = expected_merge_output([[3]], expected_output, True,
+ prop_conflicts=1)
svntest.actions.run_and_verify_svn(None,
expected_output,
[], 'merge', '-c3',
@@ -16788,11 +16817,15 @@ def merge_adds_subtree_with_mergeinfo(sb
'B/lambda' : Item("This is the file 'lambda'.\n"),
'B/F' : Item(),
'C' : Item(),
+ # C/nu will pick up the mergeinfo A_COPY/C/nu:8 which is self-referential.
+ # This is issue #3668 'inheritance can result in self-referential
+ # mergeinfo', but we'll allow it in this test since issue #3668 is
+ # tested elsewhere and is not the point of *this* test.
'C/nu' : Item("This is the file 'nu'.\n" \
"More work on the A_COPY branch.\n" \
"A faux conflict resolution.\n",
props={SVN_PROP_MERGEINFO :
- '/A/C/nu:9-11\n/A_COPY/C/nu:10'}),
+ '/A/C/nu:9-11\n/A_COPY/C/nu:8,10'}),
'D' : Item(),
'D/G' : Item(),
'D/G/pi' : Item("This is the file 'pi'.\n"),
@@ -16818,7 +16851,7 @@ def merge_adds_subtree_with_mergeinfo(sb
#----------------------------------------------------------------------
# A test for issue #3978 'reverse merge which adds subtree fails'.
-@Issue(3978)
+@Issue(3978,4057)
@SkipUnless(server_has_mergeinfo)
def reverse_merge_adds_subtree(sbox):
"reverse merge adds subtree"
@@ -16845,6 +16878,9 @@ def reverse_merge_adds_subtree(sbox):
'Cherry-pick r7 from A to A_COPY', wc_dir)
# r9 - File depth sync merge from A/D/H to A_COPY/D/H/
+ # This shallow merge does not create non-inheritable mergeinfo because of
+ # the issue #4057 fix; all subtrees affected by the diff are present, so
+ # non-inheritable mergeinfo is not required.
svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
svntest.actions.run_and_verify_svn(None, None, [], 'merge',
sbox.repo_url + '/A/D/H',
@@ -16882,7 +16918,6 @@ def reverse_merge_adds_subtree(sbox):
# ..\..\..\subversion\libsvn_subr\kitchensink.c:57: (apr_err=200022)
# svn: E200022: Negative revision number found parsing '-7'
svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
- svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
expected_output = wc.State(A_COPY_path, {
'D/H/chi' : Item(status='A '),
})
@@ -16931,12 +16966,10 @@ def reverse_merge_adds_subtree(sbox):
'D/G/rho' : Item("This is the file 'rho'.\n"),
'D/G/tau' : Item("This is the file 'tau'.\n"),
'D/gamma' : Item("This is the file 'gamma'.\n"),
- 'D/H' : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:2-6*,8*'}),
+ 'D/H' : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:2-6,8'}),
'D/H/chi' : Item("This is the file 'chi'.\n"),
- 'D/H/psi' : Item("New content",
- props={SVN_PROP_MERGEINFO : '/A/D/H/psi:2-8'}),
- 'D/H/omega' : Item("New content",
- props={SVN_PROP_MERGEINFO : '/A/D/H/omega:2-8'}),
+ 'D/H/psi' : Item("New content"),
+ 'D/H/omega' : Item("New content"),
})
expected_skip = wc.State('.', { })
svntest.actions.run_and_verify_merge(A_COPY_path, 7, 6,
@@ -17107,6 +17140,215 @@ def record_only_merge_adds_new_subtree_m
None, None, None, None,
None, 1, False)
+#----------------------------------------------------------------------
+# Setup helper for issue #4056 and issue #4057 tests.
+def noninheritable_mergeinfo_test_set_up(sbox):
+ '''Starting with standard greek tree, copy 'A' to 'branch' in r2 and
+ then made a file edit to A/B/lambda in r3.
+ Return (expected_output, expected_mergeinfo_output, expected_elision_output,
+ expected_status, expected_disk, expected_skip) for a merge of
+ r3 from ^/A/B to branch/B.'''
+
+ sbox.build()
+ wc_dir = sbox.wc_dir
+
+ lambda_path = os.path.join(wc_dir, 'A', 'B', 'lambda')
+ B_branch_path = os.path.join(wc_dir, 'branch', 'B')
+
+ # r2 - Branch ^/A to ^/branch.
+ svntest.main.run_svn(None, 'copy', sbox.repo_url + '/A',
+ sbox.repo_url + '/branch', '-m', 'make a branch')
+
+ # r3 - Make an edit to A/B/lambda.
+ svntest.main.file_write(lambda_path, "trunk edit.\n")
+ svntest.main.run_svn(None, 'commit', '-m', 'file edit', wc_dir)
+ svntest.main.run_svn(None, 'up', wc_dir)
+
+ expected_output = wc.State(B_branch_path, {
+ 'lambda' : Item(status='U '),
+ })
+ expected_mergeinfo_output = wc.State(B_branch_path, {
+ '' : Item(status=' U'),
+ 'lambda' : Item(status=' U'),
+ })
+ expected_elision_output = wc.State(B_branch_path, {
+ 'lambda' : Item(status=' U'),
+ })
+ expected_status = wc.State(B_branch_path, {
+ '' : Item(status=' M'),
+ 'lambda' : Item(status='M '),
+ 'E' : Item(status=' '),
+ 'E/alpha' : Item(status=' '),
+ 'E/beta' : Item(status=' '),
+ 'F' : Item(status=' '),
+ })
+ expected_status.tweak(wc_rev='3')
+ expected_disk = wc.State('', {
+ '' : Item(props={SVN_PROP_MERGEINFO : '/A/B:3'}),
+ 'lambda' : Item("trunk edit.\n"),
+ 'E' : Item(),
+ 'E/alpha' : Item("This is the file 'alpha'.\n"),
+ 'E/beta' : Item("This is the file 'beta'.\n"),
+ 'F' : Item(),
+ })
+ expected_skip = wc.State(B_branch_path, {})
+
+ return expected_output, expected_mergeinfo_output, expected_elision_output, \
+ expected_status, expected_disk, expected_skip
+
+
+#----------------------------------------------------------------------
+# Test for issue #4056 "don't record non-inheritable mergeinfo if missing
+# subtrees are not touched by the full-depth diff".
+@Issue(4056)
+@SkipUnless(server_has_mergeinfo)
+def unnecessary_noninheritable_mergeinfo_missing_subtrees(sbox):
+ "missing subtrees untouched by infinite depth merge"
+
+ B_branch_path = os.path.join(sbox.wc_dir, 'branch', 'B')
+
+ # Setup a simple branch to which
+ expected_output, expected_mergeinfo_output, expected_elision_output, \
+ expected_status, expected_disk, expected_skip = \
+ noninheritable_mergeinfo_test_set_up(sbox)
+
+ # Create a shallow merge target; set depth of branch/B to files.
+ svntest.main.run_svn(None, 'up', '--set-depth=files', B_branch_path)
+ expected_status.remove('E', 'E/alpha', 'E/beta', 'F')
+ expected_disk.remove('E', 'E/alpha', 'E/beta', 'F')
+
+ # Merge r3 from ^/A/B to branch/B
+ #
+ # Merge is smart enough to realize that despite the shallow merge target,
+ # the diff can only affect branch/B/lambda, which is still present, so there
+ # is no need to record non-inheritable mergeinfo on the target
+ # or any subtree mergeinfo whatsoever:
+ #
+ # >svn pg svn:mergeinfo -vR
+ # Properties on 'branch\B':
+ # svn:mergeinfo
+ # /A/B:3 <-- Nothing was skipped, so doesn't need
+ # to be non-inheritable.
+ svntest.actions.run_and_verify_merge(B_branch_path,
+ '2', '3',
+ sbox.repo_url + '/A/B', None,
+ expected_output,
+ expected_mergeinfo_output,
+ expected_elision_output,
+ expected_disk,
+ expected_status,
+ expected_skip,
+ None, None, None, None, None, 1, 1,
+ B_branch_path)
+
+#----------------------------------------------------------------------
+# Test for issue #4057 "don't record non-inheritable mergeinfo in shallow
+# merge if entire diff is within requested depth".
+@Issue(4057)
+@SkipUnless(server_has_mergeinfo)
+def unnecessary_noninheritable_mergeinfo_shallow_merge(sbox):
+ "shallow merge reaches all necessary subtrees"
+
+ B_branch_path = os.path.join(sbox.wc_dir, 'branch', 'B')
+ E_path = os.path.join(sbox.wc_dir, 'A', 'B', 'E')
+
+ # Setup a simple branch to which
+ expected_output, expected_mergeinfo_output, expected_elision_output, \
+ expected_status, expected_disk, expected_skip = \
+ noninheritable_mergeinfo_test_set_up(sbox)
+
+ # Merge r3 from ^/A/B to branch/B at operational depth=files
+ #
+ # Previously this failed because merge wasn't smart enough to
+ # realize that despite being a shallow merge, the diff can
+ # only affect branch/B/lambda, which is within the specified
+ # depth, so there is no need to record non-inheritable mergeinfo
+ # or subtree mergeinfo:
+ #
+ # >svn pg svn:mergeinfo -vR
+ # Properties on 'branch\B':
+ # svn:mergeinfo
+ # /A/B:3* <-- Should be inheritable
+ # Properties on 'branch\B\lambda':
+ # svn:mergeinfo
+ # /A/B/lambda:3 <-- Not necessary
+ expected_skip = wc.State(B_branch_path, {})
+ svntest.actions.run_and_verify_merge(B_branch_path, '2', '3',
+ sbox.repo_url + '/A/B', None,
+ expected_output,
+ expected_mergeinfo_output,
+ expected_elision_output,
+ expected_disk,
+ expected_status,
+ expected_skip,
+ None, None, None, None, None, 1, 1,
+ '--depth', 'files', B_branch_path)
+
+ # Revert the merge and then make a prop change to A/B/E in r4.
+ svntest.actions.run_and_verify_svn(None, None, [],
+ 'revert', '--recursive', sbox.wc_dir)
+ svntest.actions.run_and_verify_svn(None,
+ ["property 'prop:name' set on '" +
+ E_path + "'\n"], [], 'ps',
+ 'prop:name', 'propval', E_path)
+ svntest.actions.run_and_verify_svn(None, None, [],
+ 'ci', '-m', 'A new property on a dir',
+ sbox.wc_dir)
+ svntest.actions.run_and_verify_svn(None, None, [],
+ 'up', sbox.wc_dir)
+
+ # Merge r4 from ^/A/B to branch/B at operational depth=immediates
+ #
+ # Previously this failed because the mergetracking logic didn't realize
+ # that despite being a shallow merge, the diff only affected branch/B/E,
+ # which was within the specified depth, so there was no need to record
+ # non-inheritable mergeinfo or subtree mergeinfo:
+ #
+ # >svn pg svn:mergeinfo -vR
+ # Properties on 'branch\B':
+ # svn:mergeinfo
+ # /A/B:4* <-- Should be inheritable
+ # Properties on 'branch\B\E':
+ # svn:mergeinfo
+ # /A/B/E:4 <-- Not necessary
+ expected_output = wc.State(B_branch_path, {
+ 'E' : Item(status=' U'),
+ })
+ expected_mergeinfo_output = wc.State(B_branch_path, {
+ '' : Item(status=' U'),
+ 'E' : Item(status=' U'),
+ })
+ expected_elision_output = wc.State(B_branch_path, {
+ 'E' : Item(status=' U'),
+ })
+ expected_status = wc.State(B_branch_path, {
+ '' : Item(status=' M'),
+ 'lambda' : Item(status=' '),
+ 'E' : Item(status=' M'),
+ 'E/alpha' : Item(status=' '),
+ 'E/beta' : Item(status=' '),
+ 'F' : Item(status=' '),
+ })
+ expected_status.tweak(wc_rev='4')
+ expected_disk = wc.State('', {
+ '' : Item(props={SVN_PROP_MERGEINFO : '/A/B:4'}),
+ 'lambda' : Item("This is the file 'lambda'.\n"),
+ 'E' : Item(props={'prop:name' : 'propval'}),
+ 'E/alpha' : Item("This is the file 'alpha'.\n"),
+ 'E/beta' : Item("This is the file 'beta'.\n"),
+ 'F' : Item(),
+ })
+ svntest.actions.run_and_verify_merge(B_branch_path, '3', '4',
+ sbox.repo_url + '/A/B', None,
+ expected_output,
+ expected_mergeinfo_output,
+ expected_elision_output,
+ expected_disk,
+ expected_status,
+ expected_skip,
+ None, None, None, None, None, 1, 1,
+ '--depth', 'immediates', B_branch_path)
+
########################################################################
# Run the tests
@@ -17235,6 +17477,8 @@ test_list = [ None,
reverse_merge_adds_subtree,
merged_deletion_causes_tree_conflict,
record_only_merge_adds_new_subtree_mergeinfo,
+ unnecessary_noninheritable_mergeinfo_missing_subtrees,
+ unnecessary_noninheritable_mergeinfo_shallow_merge,
]
if __name__ == '__main__':
Modified: subversion/branches/revprop-packing/subversion/tests/cmdline/merge_tree_conflict_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/revprop-packing/subversion/tests/cmdline/merge_tree_conflict_tests.py?rev=1231318&r1=1231317&r2=1231318&view=diff
==============================================================================
--- subversion/branches/revprop-packing/subversion/tests/cmdline/merge_tree_conflict_tests.py (original)
+++ subversion/branches/revprop-packing/subversion/tests/cmdline/merge_tree_conflict_tests.py Fri Jan 13 21:40:26 2012
@@ -46,6 +46,7 @@ from svntest.main import server_has_merg
from merge_tests import set_up_branch
from merge_tests import svn_copy
from merge_tests import svn_merge
+from merge_tests import expected_merge_output
#----------------------------------------------------------------------
@SkipUnless(server_has_mergeinfo)
@@ -680,25 +681,16 @@ def del_differing_file(sbox):
'newprop', 'v', target+"/pi")
dir_D = os.path.join('A','D')
- dir_G2 = os.path.join(dir_D, 'G2')
tau = os.path.join(dir_D,'G2','tau')
pi = os.path.join(dir_D, 'G2', 'pi')
# Should complain and "skip" it.
svn_merge(s_rev_tau, source, target, [
- "--- Merging r2 into '%s':\n" % dir_G2,
- " C %s\n" % tau,
- "--- Recording mergeinfo for merge of r2 into '%s':\n" % (dir_G2),
- " U %s\n" % (dir_G2),
- "Summary of conflicts:\n",
- " Tree conflicts: 1\n"])
+ " C %s\n" % tau, # merge
+ ], tree_conflicts=1)
svn_merge(s_rev_pi, source, target, [
- "--- Merging r3 into '%s':\n" % dir_G2,
- " C %s\n" % pi,
- "--- Recording mergeinfo for merge of r3 into '%s':\n" % (dir_G2),
- " G %s\n" % (dir_G2),
- "Summary of conflicts:\n",
- " Tree conflicts: 1\n"])
+ " C %s\n" % pi, # merge
+ ], tree_conflicts=1)
# Copy a file, modify it, commit, and merge a deletion to it.
@@ -710,26 +702,17 @@ def del_differing_file(sbox):
sbox.simple_commit(target)
- dir_G3 = os.path.join(dir_D, 'G3')
tau = os.path.join(dir_D,'G3','tau')
pi = os.path.join(dir_D, 'G3', 'pi')
# Should complain and "skip" it.
svn_merge(s_rev_tau, source, target, [
- "--- Merging r2 into '%s':\n" % dir_G3,
" C %s\n" % tau,
- "--- Recording mergeinfo for merge of r2 into '%s':\n" % (dir_G3),
- " U %s\n" % (dir_G3),
- "Summary of conflicts:\n",
- " Tree conflicts: 1\n"])
+ ], tree_conflicts=1)
svn_merge(s_rev_pi, source, target, [
- "--- Merging r3 into '%s':\n" % dir_G3,
" C %s\n" % pi,
- "--- Recording mergeinfo for merge of r3 into '%s':\n" % (dir_G3),
- " G %s\n" % (dir_G3),
- "Summary of conflicts:\n",
- " Tree conflicts: 1\n"])
+ ], tree_conflicts=1)
os.chdir(saved_cwd)
@@ -1738,18 +1721,15 @@ def merge_replace_causes_tree_conflict(s
'propname', 'propval', A_D_H)
# svn merge $URL/A $URL/branch A
- expected_stdout = verify.UnorderedOutput([
- "--- Merging differences between repository URLs into '" + A + "':\n",
+ expected_stdout = expected_merge_output(None, [
+ # merge
' C ' + A_B_E + '\n',
' C ' + A_mu + '\n',
' C ' + A_D_G_pi + '\n',
' C ' + A_D_H + '\n',
- "--- Recording mergeinfo for merge between repository URLs into '" \
- + A + "':\n",
+ # mergeinfo
' U ' + A + '\n',
- 'Summary of conflicts:\n',
- ' Tree conflicts: 4\n',
- ])
+ ], target=A, two_url=True, tree_conflicts=4)
actions.run_and_verify_svn2('OUTPUT', expected_stdout, [], 0, 'merge',
url_A, url_branch, A)
@@ -1829,15 +1809,10 @@ def merge_replace_causes_tree_conflict2(
### A file-with-file replacement onto a deleted file.
# svn merge $URL/A/mu $URL/branch/mu A/mu
- expected_stdout = verify.UnorderedOutput([
- "--- Merging differences between repository URLs into '" + A + "':\n",
- ' C ' + A_mu + '\n',
- "--- Recording mergeinfo for merge between repository URLs into '" +
- A + "':\n",
- " U " + A + "\n",
- 'Summary of conflicts:\n',
- ' Tree conflicts: 1\n',
- ])
+ expected_stdout = expected_merge_output(None, [
+ ' C ' + A_mu + '\n', # merge
+ " U " + A + "\n", # mergeinfo
+ ], target=A, two_url=True, tree_conflicts=1)
actions.run_and_verify_svn2('OUTPUT', expected_stdout, [], 0, 'merge',
url_A, url_branch, A, '--depth=files')
@@ -1853,15 +1828,10 @@ def merge_replace_causes_tree_conflict2(
### A dir-with-dir replacement onto a deleted directory.
# svn merge $URL/A/B $URL/branch/B A/B
- expected_stdout = verify.UnorderedOutput([
- "--- Merging differences between repository URLs into '" + A_B + "':\n",
- ' C ' + A_B_E + '\n',
- "--- Recording mergeinfo for merge between repository URLs into '" +
- A_B + "':\n",
- " U " + A_B + "\n",
- 'Summary of conflicts:\n',
- ' Tree conflicts: 1\n',
- ])
+ expected_stdout = expected_merge_output(None, [
+ ' C ' + A_B_E + '\n', # merge
+ " U " + A_B + "\n", # mergeinfo
+ ], target=A_B, two_url=True, tree_conflicts=1)
actions.run_and_verify_svn2('OUTPUT', expected_stdout, [], 0, 'merge',
url_A_B, url_branch_B, A_B)
@@ -1877,16 +1847,11 @@ def merge_replace_causes_tree_conflict2(
### A dir-with-file replacement onto a deleted directory.
# svn merge --depth=immediates $URL/A/D $URL/branch/D A/D
- expected_stdout = verify.UnorderedOutput([
- "--- Merging differences between repository URLs into '" + A_D + "':\n",
- ' C ' + A_D_H + '\n',
- "--- Recording mergeinfo for merge between repository URLs into '" +
- A_D + "':\n",
- " U " + A_D + "\n",
+ expected_stdout = expected_merge_output(None, [
+ ' C ' + A_D_H + '\n', # merge
+ " U " + A_D + "\n", # mergeinfo
" U " + A_D_G + "\n",
- 'Summary of conflicts:\n',
- ' Tree conflicts: 1\n',
- ])
+ ], target=A_D, two_url=True, tree_conflicts=1)
actions.run_and_verify_svn2('OUTPUT', expected_stdout, [], 0, 'merge',
'--depth=immediates', url_A_D, url_branch_D, A_D)
@@ -1902,20 +1867,9 @@ def merge_replace_causes_tree_conflict2(
### A file-with-dir replacement onto a deleted file.
# svn merge $URL/A/D/G $URL/branch/D/G A/D/G
- expected_stdout = verify.UnorderedOutput([
- "--- Merging differences between repository URLs into '" + A_D_G +
- "':\n",
- ' C ' + A_D_G_pi + '\n',
- "--- Recording mergeinfo for merge between repository URLs into '" +
- A_D_G + "':\n",
- "--- Eliding mergeinfo from '" + A_D_G_pi + "':\n",
- " U " + A_D_G_pi + "\n",
- "--- Eliding mergeinfo from '" + A_D_G_pi + "':\n",
- " U " + A_D_G_pi + "\n",
- " G " + A_D_G + "\n",
- 'Summary of conflicts:\n',
- ' Tree conflicts: 1\n',
- ])
+ expected_stdout = expected_merge_output(None, [
+ ' C ' + A_D_G_pi + '\n', # merge
+ ], target=A_D_G, elides=[A_D_G_pi, A_D_G], two_url=True, tree_conflicts=1)
actions.run_and_verify_svn2('OUTPUT', expected_stdout, [], 0, 'merge',
url_A_D_G, url_branch_D_G, A_D_G)
@@ -1940,6 +1894,77 @@ def merge_replace_causes_tree_conflict2(
actions.run_and_verify_svn2('OUTPUT', expected_stdout, [], 0, 'st',
'--depth=empty', path)
+#----------------------------------------------------------------------
+# Test for issue #4011 'merge of replacement on local delete fails'
+@SkipUnless(server_has_mergeinfo)
+@Issue(4011)
+@XFail()
+def merge_replace_on_del_fails(sbox):
+ "merge replace on local delete fails"
+
+ sbox.build()
+ wc_dir = sbox.wc_dir
+
+ C_path = os.path.join(wc_dir, 'A', 'C')
+ branch_path = os.path.join(wc_dir, 'branch')
+ C_branch_path = os.path.join(wc_dir, 'branch', 'C')
+
+ # r2 - Copy ^/A to ^/branch
+ svntest.actions.run_and_verify_svn(None, None, [], 'copy',
+ sbox.repo_url + '/A',
+ sbox.repo_url + '/branch',
+ '-m', 'Create a branch')
+
+ # r3 - Replace A/C
+ svntest.actions.run_and_verify_svn(None, None, [], 'del', C_path)
+ svntest.actions.run_and_verify_svn(None, None, [], 'mkdir', C_path)
+ svntest.actions.run_and_verify_svn(None, None, [], 'ci',
+ '-m', 'Replace A/C', wc_dir)
+
+ # r4 - Delete branch/C
+ svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
+ svntest.actions.run_and_verify_svn(None, None, [], 'del', C_branch_path)
+ svntest.actions.run_and_verify_svn(None, None, [], 'ci',
+ '-m', 'Delete branch/C', wc_dir)
+
+ # Sync merge ^/A to branch
+ svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
+ expected_stdout = expected_merge_output([[2,4]], [
+ ' C ' + C_branch_path + '\n', # merge
+ ' U ' + branch_path + '\n', # mergeinfo
+ ], target=branch_path, tree_conflicts=1)
+ # This currently fails with:
+ #
+ # >svn merge ^/A branch
+ # ..\..\..\subversion\svn\util.c:913: (apr_err=155010)
+ # ..\..\..\subversion\libsvn_client\merge.c:11349: (apr_err=155010)
+ # ..\..\..\subversion\libsvn_client\merge.c:11303: (apr_err=155010)
+ # ..\..\..\subversion\libsvn_client\merge.c:11303: (apr_err=155010)
+ # ..\..\..\subversion\libsvn_client\merge.c:11273: (apr_err=155010)
+ # ..\..\..\subversion\libsvn_client\merge.c:9287: (apr_err=155010)
+ # ..\..\..\subversion\libsvn_client\merge.c:8870: (apr_err=155010)
+ # ..\..\..\subversion\libsvn_client\merge.c:5349: (apr_err=155010)
+ # ..\..\..\subversion\libsvn_repos\reporter.c:1430: (apr_err=155010)
+ # ..\..\..\subversion\libsvn_client\ra.c:247: (apr_err=155010)
+ # ..\..\..\subversion\libsvn_repos\reporter.c:1269: (apr_err=155010)
+ # ..\..\..\subversion\libsvn_repos\reporter.c:1205: (apr_err=155010)
+ # ..\..\..\subversion\libsvn_repos\reporter.c:920: (apr_err=155010)
+ # ..\..\..\subversion\libsvn_delta\cancel.c:120: (apr_err=155010)
+ # ..\..\..\subversion\libsvn_delta\cancel.c:120: (apr_err=155010)
+ # ..\..\..\subversion\libsvn_client\repos_diff.c:710: (apr_err=155010)
+ # ..\..\..\subversion\libsvn_client\merge.c:2234: (apr_err=155010)
+ # ..\..\..\subversion\libsvn_wc\adm_ops.c:1069: (apr_err=155010)
+ # ..\..\..\subversion\libsvn_wc\adm_ops.c:956: (apr_err=155010)
+ # ..\..\..\subversion\libsvn_wc\update_editor.c:5036: (apr_err=155010)
+ # ..\..\..\subversion\libsvn_wc\wc_db.c:6985: (apr_err=155010)
+ # ..\..\..\subversion\libsvn_wc\wc_db.c:6929: (apr_err=155010)
+ # ..\..\..\subversion\libsvn_wc\wc_db.c:6920: (apr_err=155010)
+ # svn: E155010: The node 'C:\SVN\src-trunk\Debug\subversion\tests\
+ # cmdline\svn-test-work\working_copies\merge_tree_conflict_tests-24\
+ # branch\C' was not found.
+ actions.run_and_verify_svn2('OUTPUT', expected_stdout, [], 0, 'merge',
+ sbox.repo_url + '/A', branch_path)
+
########################################################################
# Run the tests
@@ -1969,6 +1994,7 @@ test_list = [ None,
tree_conflicts_merge_del_onto_missing,
merge_replace_causes_tree_conflict,
merge_replace_causes_tree_conflict2,
+ merge_replace_on_del_fails,
]
if __name__ == '__main__':
Modified: subversion/branches/revprop-packing/subversion/tests/cmdline/mergeinfo_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/revprop-packing/subversion/tests/cmdline/mergeinfo_tests.py?rev=1231318&r1=1231317&r2=1231318&view=diff
==============================================================================
--- subversion/branches/revprop-packing/subversion/tests/cmdline/mergeinfo_tests.py (original)
+++ subversion/branches/revprop-packing/subversion/tests/cmdline/mergeinfo_tests.py Fri Jan 13 21:40:26 2012
@@ -68,8 +68,11 @@ def no_mergeinfo(sbox):
"'mergeinfo' on a URL that lacks mergeinfo"
sbox.build(create_wc=False)
+ sbox.simple_repo_copy('A', 'A2')
svntest.actions.run_and_verify_mergeinfo(adjust_error_for_server_version(""),
- [], sbox.repo_url, sbox.repo_url)
+ [],
+ sbox.repo_url + '/A',
+ sbox.repo_url + '/A2')
def mergeinfo(sbox):
"'mergeinfo' on a path with mergeinfo"
@@ -77,41 +80,65 @@ def mergeinfo(sbox):
sbox.build()
wc_dir = sbox.wc_dir
+ # make a branch 'A2'
+ sbox.simple_repo_copy('A', 'A2') # r2
+ # make a change in branch 'A'
+ sbox.simple_mkdir('A/newdir')
+ sbox.simple_commit() # r3
+ sbox.simple_update()
+
# Dummy up some mergeinfo.
- svntest.actions.run_and_verify_svn(None, None, [], 'ps', SVN_PROP_MERGEINFO,
- '/:1', wc_dir)
+ svntest.actions.run_and_verify_svn(None, None, [],
+ 'ps', SVN_PROP_MERGEINFO, '/A:3',
+ sbox.ospath('A2'))
svntest.actions.run_and_verify_mergeinfo(adjust_error_for_server_version(""),
- ['1'], sbox.repo_url, wc_dir)
+ ['3'],
+ sbox.repo_url + '/A',
+ sbox.ospath('A2'))
@SkipUnless(server_has_mergeinfo)
def explicit_mergeinfo_source(sbox):
"'mergeinfo' with source selection"
+ # The idea is the target has mergeinfo pertaining to two or more different
+ # source branches and we're asking about just one of them.
+
sbox.build()
- wc_dir = sbox.wc_dir
- H_path = os.path.join(wc_dir, 'A', 'D', 'H')
- H2_path = os.path.join(wc_dir, 'A', 'D', 'H2')
- B_url = sbox.repo_url + '/A/B'
- B_path = os.path.join(wc_dir, 'A', 'B')
- G_url = sbox.repo_url + '/A/D/G'
- G_path = os.path.join(wc_dir, 'A', 'D', 'G')
- H2_url = sbox.repo_url + '/A/D/H2'
- # Make a copy, and dummy up some mergeinfo.
- mergeinfo = '/A/B:1\n/A/D/G:1\n'
- svntest.actions.set_prop(SVN_PROP_MERGEINFO, mergeinfo, H_path)
- svntest.main.run_svn(None, "cp", H_path, H2_path)
- svntest.main.run_svn(None, "ci", "-m", "r2", wc_dir)
+ def url(relpath):
+ return sbox.repo_url + '/' + relpath
+ def path(relpath):
+ return sbox.ospath(relpath)
+
+ B = 'A/B'
+
+ # make some branches
+ B2 = 'A/B2'
+ B3 = 'A/B3'
+ sbox.simple_repo_copy(B, B2) # r2
+ sbox.simple_repo_copy(B, B3) # r3
+ sbox.simple_update()
+
+ # make changes in the branches
+ sbox.simple_mkdir('A/B2/newdir')
+ sbox.simple_commit() # r4
+ sbox.simple_mkdir('A/B3/newdir')
+ sbox.simple_commit() # r5
+
+ # Put dummy mergeinfo on branch root
+ mergeinfo = '/A/B2:2-5\n/A/B3:2-5\n'
+ sbox.simple_propset(SVN_PROP_MERGEINFO, mergeinfo, B)
+ sbox.simple_commit()
# Check using each of our recorded merge sources (as paths and URLs).
svntest.actions.run_and_verify_mergeinfo(adjust_error_for_server_version(""),
- ['1'], B_url, H_path)
+ ['2', '4'], url(B2), path(B))
svntest.actions.run_and_verify_mergeinfo(adjust_error_for_server_version(""),
- ['1'], B_path, H_path)
+ ['2', '4'], path(B2), path(B))
svntest.actions.run_and_verify_mergeinfo(adjust_error_for_server_version(""),
- ['1'], G_url, H_path)
+ ['3', '5'], url(B3), path(B))
svntest.actions.run_and_verify_mergeinfo(adjust_error_for_server_version(""),
- ['1'], G_path, H_path)
+ ['3', '5'], path(B3), path(B))
@SkipUnless(server_has_mergeinfo)
def mergeinfo_non_source(sbox):
@@ -624,6 +651,109 @@ def wc_target_inherits_mergeinfo_from_re
test_svn_mergeinfo_4_way(D_COPY_path)
test_svn_mergeinfo_4_way(subtree_wc)
+#----------------------------------------------------------------------
+# A test for issue 3791 'svn mergeinfo shows natural history of added
+# subtrees as eligible'.
+@Issue(3791)
+@SkipUnless(server_has_mergeinfo)
+def natural_history_is_not_eligible_nor_merged(sbox):
+ "natural history is not eligible nor merged"
+
+ sbox.build()
+ wc_dir = sbox.wc_dir
+ wc_disk, wc_status = set_up_branch(sbox)
+
+ nu_path = os.path.join(wc_dir, 'A', 'C', 'nu')
+ A_COPY_path = os.path.join(wc_dir, 'A_COPY')
+ nu_COPY_path = os.path.join(wc_dir, 'A_COPY', 'C', 'nu')
+
+ # r7 - Add a new file A/C/nu
+ svntest.main.file_write(nu_path, "This is the file 'nu'.\n")
+ svntest.actions.run_and_verify_svn(None, None, [], 'add', nu_path)
+ svntest.actions.run_and_verify_svn(None, None, [], 'ci',
+ '-m', 'Add a file', wc_dir)
+
+ # r8 - Sync merge ^/A to A_COPY
+ svntest.actions.run_and_verify_svn(None, None, [], 'merge',
+ sbox.repo_url + '/A', A_COPY_path)
+ svntest.actions.run_and_verify_svn(None, None, [], 'ci',
+ '-m', 'Add a file', wc_dir)
+
+ # r9 - Modify the file added in r7
+ svntest.main.file_write(nu_path, "Modification to file 'nu'.\n")
+ svntest.actions.run_and_verify_svn(None, None, [], 'ci',
+ '-m', 'Modify added file', wc_dir)
+
+ # r10 - Merge ^/A/C/nu to A_COPY/C/nu, creating subtree mergeinfo.
+ svntest.actions.run_and_verify_svn(None, None, [], 'merge',
+ sbox.repo_url + '/A/C/nu', nu_COPY_path)
+ svntest.actions.run_and_verify_svn(None, None, [], 'ci',
+ '-m', 'Add a file', wc_dir)
+ svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
+
+ # We've effectively merged everything from ^/A to A_COPY, check
+ # that svn mergeinfo -R agrees.
+ #
+ # First check if there are eligible revisions, there should be none.
+ svntest.actions.run_and_verify_mergeinfo(
+ adjust_error_for_server_version(''),
+ [], sbox.repo_url + '/A',
+ A_COPY_path, '--show-revs', 'eligible', '-R')
+
+ # Now check that all operative revisions show as merged.
+ svntest.actions.run_and_verify_mergeinfo(
+ adjust_error_for_server_version(''),
+ ['3','4','5','6','7','9'], sbox.repo_url + '/A',
+ A_COPY_path, '--show-revs', 'merged', '-R')
+
+#----------------------------------------------------------------------
+# A test for issue 4050 "'svn mergeinfo' always considers non-inheritable
+# ranges as partially merged".
+@Issue(4050)
+@SkipUnless(server_has_mergeinfo)
+def noninheritabled_mergeinfo_not_always_eligible(sbox):
+ "noninheritabled mergeinfo not always eligible"
+
+ sbox.build()
+ wc_dir = sbox.wc_dir
+
+ A_path = os.path.join(wc_dir, 'A')
+ branch_path = os.path.join(wc_dir, 'branch')
+
+ # r2 - Branch ^/A to ^/branch.
+ svntest.main.run_svn(None, 'copy', sbox.repo_url + '/A',
+ sbox.repo_url + '/branch', '-m', 'make a branch')
+
+ # r3 - Make prop edit to A.
+ svntest.main.run_svn(None, 'ps', 'prop', 'val', A_path)
+ svntest.main.run_svn(None, 'commit', '-m', 'file edit', wc_dir)
+ svntest.main.run_svn(None, 'up', wc_dir)
+
+ # r4 - Merge r3 from ^/A to branch at depth=empty.
+ svntest.actions.run_and_verify_svn(None, None, [], 'merge',
+ sbox.repo_url + '/A', branch_path,
+ '-c3', '--depth=empty')
+ # Forcibly set non-inheritable mergeinfo to replicate the pre-1.8 behavior,
+ # where prior to the fix for issue #4057, non-inheritable mergeinfo was
+ # unconditionally set for merges with shallow operational depths.
+ svntest.actions.run_and_verify_svn(None, None, [],
+ 'propset', SVN_PROP_MERGEINFO,
+ '/A:3*\n', branch_path)
+ svntest.main.run_svn(None, 'commit', '-m', 'shallow merge', wc_dir)
+
+ # Now check that r3 is reported as fully merged from ^/A to ^/branch
+ # and does not show up all when asking for eligible revs.
+ svntest.actions.run_and_verify_mergeinfo(
+ adjust_error_for_server_version(''),
+ ['3'], sbox.repo_url + '/A', sbox.repo_url + '/branch',
+ '--show-revs', 'merged', '-R')
+ # Likewise r3 shows up as partially eligible when asking about
+ # for --show-revs=eligible.
+ svntest.actions.run_and_verify_mergeinfo(
+ adjust_error_for_server_version(''),
+ [], sbox.repo_url + '/A', sbox.repo_url + '/branch',
+ '--show-revs', 'eligible', '-R')
+
########################################################################
# Run the tests
@@ -639,6 +769,8 @@ test_list = [ None,
recursive_mergeinfo,
mergeinfo_on_pegged_wc_path,
wc_target_inherits_mergeinfo_from_repos,
+ natural_history_is_not_eligible_nor_merged,
+ noninheritabled_mergeinfo_not_always_eligible,
]
if __name__ == '__main__':