import re, os, sys
import svntest
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
Skip = svntest.testcase.Skip_deco
SkipUnless = svntest.testcase.SkipUnless_deco
XFail = svntest.testcase.XFail_deco
Issues = svntest.testcase.Issues_deco
Issue = svntest.testcase.Issue_deco
Wimp = svntest.testcase.Wimp_deco
exp_noop_up_out = svntest.actions.expected_noop_update_output
max_revision = 0
msg_separator = '------------------------------------' \
+ '------------------------------------\n'
Item = svntest.wc.StateItem
def guarantee_repos_and_wc(sbox):
"Make a repos and wc, commit max_revision revs."
global max_revision
sbox.build()
wc_path = sbox.wc_dir
msg_file=os.path.join(sbox.repo_dir, 'log-msg')
msg_file=os.path.abspath(msg_file)
was_cwd = os.getcwd()
os.chdir(wc_path)
iota_path = os.path.join('iota')
mu_path = os.path.join('A', 'mu')
B_path = os.path.join('A', 'B')
omega_path = os.path.join('A', 'D', 'H', 'omega')
pi_path = os.path.join('A', 'D', 'G', 'pi')
rho_path = os.path.join('A', 'D', 'G', 'rho')
alpha_path = os.path.join('A', 'B', 'E', 'alpha')
beta_path = os.path.join('A', 'B', 'E', 'beta')
psi_path = os.path.join('A', 'D', 'H', 'psi')
epsilon_path = os.path.join('A', 'C', 'epsilon')
msg=""" Log message for revision 2
but with multiple lines
to test the code"""
svntest.main.file_write(msg_file, msg)
svntest.main.file_append(iota_path, "2")
svntest.main.run_svn(None,
'ci', '-F', msg_file)
svntest.main.run_svn(None,
'up')
svntest.main.file_append(omega_path, "3")
svntest.main.file_append(pi_path, "3")
svntest.main.file_append(rho_path, "3")
svntest.main.file_append(alpha_path, "3")
svntest.main.run_svn(None,
'ci', '-m', "Log message for revision 3")
svntest.main.run_svn(None,
'up')
msg=""" Log message for revision 4
but with multiple lines
to test the code"""
svntest.main.file_write(msg_file, msg)
svntest.main.file_append(iota_path, "4")
svntest.main.file_append(epsilon_path, "4")
svntest.main.run_svn(None, 'add', epsilon_path)
svntest.main.run_svn(None,
'ci', '-F', msg_file)
svntest.main.run_svn(None,
'up')
svntest.main.file_append(epsilon_path, "5")
svntest.main.run_svn(None, 'rm', rho_path)
svntest.main.run_svn(None,
'ci', '-m', "Log message for revision 5")
svntest.main.run_svn(None,
'up')
msg=""" Log message for revision 6
but with multiple lines
to test the code"""
svntest.main.file_write(msg_file, msg)
svntest.main.run_svn(None, 'ps', 'blue', 'azul', B_path)
svntest.main.file_append(psi_path, "6")
svntest.main.run_svn(None,
'ci', '-F', msg_file)
svntest.main.run_svn(None,
'up')
svntest.main.file_append(mu_path, "7")
svntest.main.run_svn(None, 'ps', 'red', 'burgundy', mu_path)
svntest.main.run_svn(None,
'ci', '-m', "Log message for revision 7")
svntest.main.run_svn(None,
'up')
msg=""" Log message for revision 8
but with multiple lines
to test the code"""
svntest.main.file_write(msg_file, msg)
svntest.main.file_append(iota_path, "8")
svntest.main.file_append(rho_path, "88") svntest.main.run_svn(None, 'add', rho_path)
svntest.main.run_svn(None,
'ci', '-F', msg_file)
svntest.main.run_svn(None,
'up')
svntest.main.file_append(beta_path, "9")
svntest.main.run_svn(None, 'rm', alpha_path)
svntest.main.run_svn(None,
'ci', '-m', "Log message for revision 9")
svntest.main.run_svn(None,
'up')
max_revision = 9
os.chdir(was_cwd)
expected_status = svntest.actions.get_virginal_state(wc_path, 9)
expected_status.remove('A/B/E/alpha')
expected_status.add({
'A/C/epsilon' : Item(status=' ', wc_rev=9),
})
expected_status.tweak('A/B', 'A/mu', status=' ')
svntest.actions.run_and_verify_status(wc_path, expected_status)
def merge_history_repos(sbox):
"""Make a repos with varied and interesting merge history, similar
to the repos found at: log_tests_data/merge_history_dump.png"""
upsilon_path = os.path.join('A', 'upsilon')
omicron_path = os.path.join('blocked', 'omicron')
branch_a = os.path.join('branches', 'a')
branch_b = os.path.join('branches', 'b')
branch_c = os.path.join('branches', 'c')
svntest.main.safe_rmtree(sbox.repo_dir, 1)
svntest.main.safe_rmtree(sbox.wc_dir, 1)
svntest.main.create_repos(sbox.repo_dir)
svntest.actions.run_and_verify_svn(None, None, [], "co", sbox.repo_url,
sbox.wc_dir)
was_cwd = os.getcwd()
os.chdir(sbox.wc_dir)
svntest.main.run_svn(None, 'mkdir', 'trunk')
svntest.main.run_svn(None, 'mkdir', 'tags')
svntest.main.run_svn(None, 'mkdir', 'branches')
svntest.main.run_svn(None, 'ci', '-m',
'Add trunk/tags/branches structure.')
svntest.main.greek_state.write_to_disk('trunk')
svntest.main.run_svn(None, 'add', os.path.join('trunk', 'A'),
os.path.join('trunk', 'iota'))
svntest.main.run_svn(None, 'ci', '-m',
'Import greek tree into trunk.')
svntest.main.run_svn(None, 'up')
svntest.main.run_svn(None, 'cp', 'trunk', branch_a)
svntest.main.run_svn(None, 'ci', '-m',
'Create branches/a from trunk.',
'--username', svntest.main.wc_author2)
svntest.main.file_append_binary(os.path.join(branch_a, 'iota'),
"'A' has changed a bit.\n")
svntest.main.file_append_binary(os.path.join(branch_a, 'A', 'mu'),
"Don't forget to look at 'upsilon', too.")
svntest.main.file_write(os.path.join(branch_a, upsilon_path),
"This is the file 'upsilon'.\n", "wb")
svntest.main.run_svn(None, 'add',
os.path.join(branch_a, upsilon_path))
svntest.main.run_svn(None, 'ci', '-m',
"Add the file 'upsilon', and change some other files.")
svntest.main.run_svn(None, 'cp', 'trunk', branch_c)
svntest.main.run_svn(None, 'ci', '-m',
'Create branches/c from trunk.',
'--username', svntest.main.wc_author2)
os.chdir('trunk')
svntest.main.run_svn(None, 'merge', os.path.join('..', branch_a) + '@HEAD')
svntest.main.run_svn(None, 'ci', '-m',
'Merged branches/a to trunk.',
'--username', svntest.main.wc_author2)
os.chdir('..')
svntest.main.run_svn(None, 'mkdir', os.path.join(branch_a, 'blocked'))
svntest.main.file_write(os.path.join(branch_a, omicron_path),
"This is the file 'omicron'.\n")
svntest.main.run_svn(None, 'add',
os.path.join(branch_a, omicron_path))
svntest.main.run_svn(None, 'ci', '-m',
"Add omicron to branches/a. " +
"It will be blocked from merging in r8.")
os.chdir('trunk')
svntest.main.run_svn(None, 'merge', '--allow-mixed-revisions',
'--record-only', '-r6:7',
os.path.join('..', branch_a))
svntest.main.run_svn(None, 'ci', '-m',
"Block r7 from merging to trunk.",
'--username', svntest.main.wc_author2)
os.chdir('..')
svntest.main.file_write(os.path.join('trunk', 'A', 'mu'),
"This is the file 'mu'.\n" +
"Don't forget to look at 'upsilon', as well.", "wb")
svntest.main.run_svn(None, 'ci', '-m',
"Wording change in mu.")
svntest.main.run_svn(None, 'up')
svntest.main.run_svn(None, 'cp', 'trunk', branch_b)
svntest.main.run_svn(None, 'ci', '-m',
"Create branches/b from trunk",
'--username', svntest.main.wc_author2)
svntest.main.file_append_binary(os.path.join(branch_a, upsilon_path),
"There is also the file 'xi'.")
svntest.main.file_write(os.path.join(branch_a, 'A', 'xi'),
"This is the file 'xi'.\n", "wb")
svntest.main.run_svn(None, 'add',
os.path.join(branch_a, 'A', 'xi'))
svntest.main.file_write(os.path.join(branch_a, 'iota'),
"This is the file 'iota'.\n" +
"'A' has changed a bit, with 'upsilon', and 'xi'.",
"wb")
svntest.main.run_svn(None, 'ci', '-m',
"Added 'xi' to branches/a, made a few other changes.")
os.chdir(branch_b)
svntest.main.run_svn(None, 'merge', os.path.join('..', 'a') + '@HEAD')
svntest.main.run_svn(None, 'ci', '-m',
"Merged branches/a to branches/b.",
'--username', svntest.main.wc_author2)
os.chdir(os.path.join('..', '..'))
svntest.main.file_append_binary(os.path.join(branch_b, 'A', 'D', 'gamma'),
"Watch out for the rays!")
svntest.main.run_svn(None, 'ci', '-m',
"Modify 'gamma' on branches/b.")
os.chdir('trunk')
svntest.main.run_svn(None, 'merge', os.path.join('..', branch_b) + '@HEAD')
svntest.main.run_svn(None, 'ci', '-m',
"Merged branches/b to trunk.",
'--username', svntest.main.wc_author2)
os.chdir('..')
os.chdir(branch_c)
svntest.main.run_svn(None, 'merge',
os.path.join('..', '..', 'trunk') + '@HEAD')
svntest.main.run_svn(None, 'ci', '-m',
"Bring branches/c up to date with trunk.",
'--username', svntest.main.wc_author2)
os.chdir(os.path.join('..', '..'))
svntest.main.file_append_binary(os.path.join(branch_c, 'A', 'mu'),
"\nThis is yet more content in 'mu'.")
svntest.main.run_svn(None, 'ci', '-m',
"Modify 'mu' on branches/c.")
os.chdir('trunk')
svntest.main.run_svn(None, 'merge', '--allow-mixed-revisions',
os.path.join('..', branch_c) + '@HEAD')
svntest.main.file_write(os.path.join('A', 'mu'),
"This is the file 'mu'.\n" +
"Don't forget to look at 'upsilon', as well.\n" +
"This is yet more content in 'mu'.",
"wb")
svntest.actions.run_and_verify_resolved([os.path.join('A', 'mu'),
os.path.join('A', 'xi'),
os.path.join('A', 'upsilon')])
svntest.main.run_svn(None, 'ci', '-m',
"Merge branches/c to trunk, " +
"resolving a conflict in 'mu'.",
'--username', svntest.main.wc_author2)
os.chdir('..')
os.chdir(was_cwd)
class SVNLogParseError(Exception):
pass
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
message in the data is also the first element of the list, and so
on).
Each hash contains the following keys/values:
'revision' ===> number
'author' ===> string
'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
'paths' ===> list of tuples of the form (X, PATH), where X is the
first column of verbose output, and PATH is the affected path.
If LOG_LINES contains merge result information, then the hash also contains
'merges' ===> list of forward-merging revisions that resulted in this
log being part of the list of messages.
'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)
"""
header_re = re.compile('^r([0-9]+) \| ' \
+ '([^|]*) \| ([^|]*) \| ([0-9]+) lines?')
chain = []
log_lines = [line for line in log_lines if not line.startswith('DBG:')]
this_item = None
while True:
try:
this_line = log_lines.pop(0)
except IndexError:
return chain
match = header_re.search(this_line)
if match and match.groups():
is_result = 0
is_result_reverse = 0
this_item = {}
this_item['revision'] = int(match.group(1))
this_item['author'] = match.group(2)
this_item['date'] = match.group(3)
lines = int(match.group(4))
this_item['lines'] = lines
next_line = log_lines.pop(0)
if next_line.strip() == 'Changed paths:':
paths = []
path_line = log_lines.pop(0).strip()
while (path_line != ''
and path_line[0:6] != 'Merged'
and path_line[0:14] != 'Reverse merged'):
paths.append( (path_line[0], path_line[2:]) )
path_line = log_lines.pop(0).strip()
this_item['paths'] = paths
if path_line[0:6] == 'Merged':
is_result = 1
result_line = path_line
elif path_line[0:14] == 'Reverse merged':
is_result_reverse = 1
result_line = path_line
elif next_line[0:6] == 'Merged':
is_result = 1
result_line = next_line.strip()
elif next_line[0:14] == 'Reverse merged':
is_result_reverse = 1
result_line = next_line.strip()
if is_result:
merges = []
prefix_len = len('Merged via: ')
for rev_str in result_line[prefix_len:].split(','):
merges.append(int(rev_str.strip()[1:]))
this_item['merges'] = merges
log_lines.pop(0)
if is_result_reverse:
reverse_merges = []
prefix_len = len('Reverse merged via: ')
for rev_str in result_line[prefix_len:].split(','):
reverse_merges.append(int(rev_str.strip()[1:]))
this_item['reverse_merges'] = reverse_merges
log_lines.pop(0)
msg = ''
for line in log_lines[0:lines]:
msg += line
del log_lines[0:lines]
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
chain.append(this_item)
else: print(this_line)
raise SVNLogParseError("trailing garbage after log message")
return chain
class SVNUnexpectedLogs(svntest.Failure):
"Exception raised if a set of log messages doesn't meet expectations."
def __init__(self, msg, chain, field_selector = 'revision'):
"""Stores the log chain for later use. FIELD_SELECTOR indicates
which individual field to display when turning the exception into
text."""
svntest.Failure.__init__(self, msg)
self.chain = chain
self.field_selector = field_selector
def __str__(self):
msg = svntest.Failure.__str__(self)
if self.chain:
chain_data = list(self.chain)
for i in range(0, len(self.chain)):
chain_data[i] = self.chain[i][self.field_selector]
msg = msg + ': Actual %s list was %s' % (self.field_selector, chain_data)
return msg
def check_log_chain(chain, revlist, path_counts=[]):
"""Verify that log chain CHAIN contains the right log messages for
revisions START to END (see documentation for parse_log_output() for
more about log chains).
Do nothing if the log chain's messages run from revision START to END
and each log message contains a line of the form
'Log message for revision N'
where N is the revision number of that commit. Verify that
author and date are present and look sane, but don't check them too
carefully.
Also verify that even numbered commit messages have three lines.
If the length of PATH_COUNTS is greater than zero, make sure that each
log has that number of paths.
Raise an error if anything looks wrong.
"""
nbr_expected = len(revlist)
if len(chain) != nbr_expected:
raise SVNUnexpectedLogs('Number of elements in log chain and revision ' +
'list %s not equal' % revlist, chain)
if path_counts and len(path_counts) != nbr_expected:
raise SVNUnexpectedLogs('Number of elements in log chain and path ' +
'counts %s not equal' % path_counts, chain)
missing_revs = []
for i in range(0, nbr_expected):
expect_rev = revlist[i]
log_item = chain[i]
saw_rev = log_item['revision']
date = log_item['date']
author = log_item['author']
msg = log_item['msg']
if expect_rev != saw_rev:
missing_revs.append(expect_rev)
continue
date_re = re.compile('[0-9]+')
if not date_re.search(date):
raise SVNUnexpectedLogs('Malformed date', chain, 'date')
author_re = re.compile('[a-zA-Z]+')
if (not (author_re.search(author)
or author == ''
or author == '(no author)')):
raise SVNUnexpectedLogs('Malformed author', chain, 'author')
if (saw_rev % 2 == 0 and log_item['lines'] != 3):
raise SVNUnexpectedLogs('Malformed log line counts', chain, 'lines')
pattern = 'Log message for revision ' + repr(saw_rev)
msg_re = re.compile(pattern)
if not msg_re.search(msg):
raise SVNUnexpectedLogs("Malformed log message, expected '%s'" % msg,
chain)
if path_counts:
if (not 'paths' in log_item) or (not log_item['paths']):
raise SVNUnexpectedLogs("No changed path information", chain)
if path_counts[i] != len(log_item['paths']):
raise SVNUnexpectedLogs("Changed paths counts not equal for " +
"revision %d" % (i + 1), chain)
nbr_missing_revs = len(missing_revs)
if nbr_missing_revs > 0:
raise SVNUnexpectedLogs('Unable to find expected revision(s) %s' %
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")
def plain_log(sbox):
"'svn log', no args, top of wc"
guarantee_repos_and_wc(sbox)
os.chdir(sbox.wc_dir)
exit_code, output, err = svntest.actions.run_and_verify_svn(None, None, [],
'log')
log_chain = parse_log_output(output)
check_log_chain(log_chain, list(range(max_revision, 1 - 1, -1)))
def log_with_empty_repos(sbox):
"'svn log' on an empty repository"
svntest.main.safe_rmtree(sbox.repo_dir, 1)
svntest.main.create_repos(sbox.repo_dir)
svntest.actions.run_and_verify_svn(None, None, [],
'log',
sbox.repo_url)
def log_where_nothing_changed(sbox):
"'svn log -rN some_dir_unchanged_in_N'"
sbox.build()
rho_path = os.path.join(sbox.wc_dir, 'A', 'D', 'G', 'rho')
svntest.main.file_append(rho_path, "some new material in rho")
svntest.actions.run_and_verify_svn(None, None, [],
'ci', '-m',
'log msg', rho_path)
H_path = os.path.join(sbox.wc_dir, 'A', 'D', 'H')
svntest.actions.run_and_verify_svn(None, None, [],
'log', '-r', '2', H_path)
def log_to_revision_zero(sbox):
"'svn log -v -r 1:0 wc_root'"
sbox.build(read_only = True)
svntest.actions.run_and_verify_svn(None, None, [],
'log', '-v',
'-r', '1:0', sbox.wc_dir)
def log_with_path_args(sbox):
"'svn log', with args, top of wc"
guarantee_repos_and_wc(sbox)
os.chdir(sbox.wc_dir)
exit_code, output, err = svntest.actions.run_and_verify_svn(
None, None, [],
'log', sbox.repo_url, 'A/D/G', 'A/D/H')
log_chain = parse_log_output(output)
check_log_chain(log_chain, [8, 6, 5, 3, 1])
def dynamic_revision(sbox):
"'svn log -r COMMITTED' of dynamic/local WC rev"
guarantee_repos_and_wc(sbox)
os.chdir(sbox.wc_dir)
revprops = [{'svn:author': 'jrandom',
'svn:date': '', 'svn:log': 'Log message for revision 9'}]
for rev in ('HEAD', 'BASE', 'COMMITTED'):
svntest.actions.run_and_verify_log_xml(expected_revprops=revprops,
args=['-r', rev])
revprops[0]['svn:log'] = ('Log message for revision 8\n'
' but with multiple lines\n'
' to test the code')
svntest.actions.run_and_verify_log_xml(expected_revprops=revprops,
args=['-r', 'PREV'])
def log_wc_with_peg_revision(sbox):
"'svn log wc_target@N'"
guarantee_repos_and_wc(sbox)
my_path = os.path.join(sbox.wc_dir, "A", "B", "E", "beta") + "@8"
exit_code, output, err = svntest.actions.run_and_verify_svn(None, None, [],
'log', my_path)
check_log_chain(parse_log_output(output), [1])
def url_missing_in_head(sbox):
"'svn log target@N' when target removed from HEAD"
guarantee_repos_and_wc(sbox)
my_url = sbox.repo_url + "/A/B/E/alpha" + "@8"
exit_code, output, err = svntest.actions.run_and_verify_svn(None, None, [],
'log', my_url)
check_log_chain(parse_log_output(output), [3, 1])
def log_through_copyfrom_history(sbox):
"'svn log TGT' with copyfrom history"
sbox.build()
wc_dir = sbox.wc_dir
msg_file=os.path.join(sbox.repo_dir, 'log-msg')
msg_file=os.path.abspath(msg_file)
mu_path = os.path.join(wc_dir, 'A', 'mu')
mu2_path = os.path.join(wc_dir, 'A', 'mu2')
mu_URL = sbox.repo_url + '/A/mu'
mu2_URL = sbox.repo_url + '/A/mu2'
msg2=""" Log message for revision 2
but with multiple lines
to test the code"""
msg4=""" Log message for revision 4
but with multiple lines
to test the code"""
msg6=""" Log message for revision 6
but with multiple lines
to test the code"""
svntest.main.file_write(msg_file, msg2)
svntest.main.file_append(mu_path, "2")
svntest.actions.run_and_verify_svn(None, None, [],
'ci', wc_dir,
'-F', msg_file)
svntest.main.file_append(mu2_path, "this is mu2")
svntest.actions.run_and_verify_svn(None, None, [], 'add', mu2_path)
svntest.actions.run_and_verify_svn(None, None, [],
'ci', wc_dir,
'-m', "Log message for revision 3")
svntest.actions.run_and_verify_svn(None, None, [], 'rm', mu2_path)
svntest.main.file_write(msg_file, msg4)
svntest.actions.run_and_verify_svn(None, None, [],
'ci', wc_dir,
'-F', msg_file)
svntest.main.file_append(mu_path, "5")
svntest.actions.run_and_verify_svn(None, None, [],
'ci', wc_dir,
'-m', "Log message for revision 5")
svntest.main.file_write(msg_file, msg6)
svntest.actions.run_and_verify_svn(None, None, [],
'cp', '-r', '5', mu_URL, mu2_URL,
'-F', msg_file)
svntest.actions.run_and_verify_svn(None, None, [],
'up', wc_dir)
exit_code, output, err = svntest.actions.run_and_verify_svn(None, None, [],
'log', mu2_path)
log_chain = parse_log_output(output)
check_log_chain(log_chain, [6, 5, 2, 1])
exit_code, output, err = svntest.actions.run_and_verify_svn(None, None, [],
'log', mu2_URL)
log_chain = parse_log_output(output)
check_log_chain(log_chain, [6, 5, 2, 1])
peg_mu2_path = mu2_path + "@3"
exit_code, output, err = svntest.actions.run_and_verify_svn(None, None, [],
'log', '-r', '3',
peg_mu2_path)
log_chain = parse_log_output(output)
check_log_chain(log_chain, [3])
peg_mu2_URL = mu2_URL + "@3"
exit_code, output, err = svntest.actions.run_and_verify_svn(None, None, [],
'log', '-r', '3',
peg_mu2_URL)
log_chain = parse_log_output(output)
check_log_chain(log_chain, [3])
exit_code, output, err = svntest.actions.run_and_verify_svn(None, None, [],
'log', '-r', '2',
mu2_path)
log_chain = parse_log_output(output)
check_log_chain(log_chain, [2])
exit_code, output, err = svntest.actions.run_and_verify_svn(None, None, [],
'log', '-r', '2',
mu2_URL)
log_chain = parse_log_output(output)
check_log_chain(log_chain, [2])
def escape_control_chars(sbox):
"mod_dav_svn must escape invalid XML control chars"
dump_str = """SVN-fs-dump-format-version: 2
UUID: ffcae364-69ee-0310-a980-ca5f10462af2
Revision-number: 0
Prop-content-length: 56
Content-length: 56
K 8
svn:date
V 27
2005-01-24T10:09:21.759592Z
PROPS-END
Revision-number: 1
Prop-content-length: 128
Content-length: 128
K 7
svn:log
V 100
This msg contains a Ctrl-T (\x14) and a Ctrl-I (\t).
The former might be escaped, but the latter never.
K 10
svn:author
V 7
jrandom
K 8
svn:date
V 27
2005-01-24T10:09:22.012524Z
PROPS-END
"""
svntest.actions.load_repo(sbox, dump_str=dump_str)
URL = sbox.repo_url
exit_code, output, errput = svntest.actions.run_and_verify_svn(
None, None, [], 'log', URL)
match_unescaped_ctrl_re = "This msg contains a Ctrl-T \(.\) " \
"and a Ctrl-I \(\t\)\."
match_escaped_ctrl_re = "^This msg contains a Ctrl-T \(\?\\\\020\) " \
"and a Ctrl-I \(\t\)\."
matched = None
for line in output:
if re.match(match_unescaped_ctrl_re, line) \
or re.match(match_escaped_ctrl_re, line):
matched = 1
if not matched:
raise svntest.Failure("log message not transmitted properly:" +
str(output) + "\n" + "error: " + str(errput))
def log_xml_empty_date(sbox):
"svn log --xml must not print empty date elements"
sbox.build()
svntest.actions.enable_revprop_changes(sbox.repo_dir)
date_re = re.compile('<date');
exit_code, output, errput = svntest.actions.run_and_verify_svn(
None, None, [], 'log', '--xml', '-r1', sbox.wc_dir)
matched = 0
for line in output:
if date_re.search(line):
matched = 1
if not matched:
raise svntest.Failure("log contains no date element")
svntest.actions.run_and_verify_svn(None, None, [],
'pdel', '--revprop', '-r1', 'svn:date',
sbox.wc_dir)
exit_code, output, errput = svntest.actions.run_and_verify_svn(
None, None, [], 'log', '--xml', '-r1', sbox.wc_dir)
for line in output:
if date_re.search(line):
raise svntest.Failure("log contains date element when svn:date is empty")
def log_limit(sbox):
"svn log --limit"
guarantee_repos_and_wc(sbox)
exit_code, out, err = svntest.actions.run_and_verify_svn(None, None, [],
'log',
'--limit', '2',
sbox.repo_url)
log_chain = parse_log_output(out)
check_log_chain(log_chain, [9, 8])
exit_code, out, err = svntest.actions.run_and_verify_svn(None, None, [],
'log',
'--limit', '2',
sbox.repo_url,
'A/B')
log_chain = parse_log_output(out)
check_log_chain(log_chain, [9, 6])
exit_code, out, err = svntest.actions.run_and_verify_svn(
None, None, [],
'log', '--limit', '2', '--revision', '2:HEAD', sbox.repo_url, 'A/B')
log_chain = parse_log_output(out)
check_log_chain(log_chain, [3, 6])
exit_code, out, err = svntest.actions.run_and_verify_svn(
None, None, [],
'log', '-l', '2', '--revision', '1', sbox.repo_url, 'A/B')
log_chain = parse_log_output(out)
check_log_chain(log_chain, [1])
must_be_positive = ".*Argument to --limit must be positive.*"
svntest.actions.run_and_verify_svn(None, None, must_be_positive,
'log', '--limit', '0', '--revision', '1',
sbox.repo_url, 'A/B')
svntest.actions.run_and_verify_svn(None, None, must_be_positive,
'log', '--limit', '-1', '--revision', '1',
sbox.repo_url, 'A/B')
def log_base_peg(sbox):
"run log on an @BASE target"
guarantee_repos_and_wc(sbox)
target = os.path.join(sbox.wc_dir, 'A', 'B', 'E', 'beta') + '@BASE'
exit_code, out, err = svntest.actions.run_and_verify_svn(None, None, [],
'log', target)
log_chain = parse_log_output(out)
check_log_chain(log_chain, [9, 1])
svntest.actions.run_and_verify_svn(None, None, [], 'update', '-r', '1',
sbox.wc_dir)
exit_code, out, err = svntest.actions.run_and_verify_svn(None, None, [],
'log', target)
log_chain = parse_log_output(out)
check_log_chain(log_chain, [1])
def log_verbose(sbox):
"run log with verbose output"
guarantee_repos_and_wc(sbox)
exit_code, output, err = svntest.actions.run_and_verify_svn(None, None, [],
'log', '-v',
sbox.wc_dir)
log_chain = parse_log_output(output)
path_counts = [2, 2, 1, 2, 2, 2, 4, 1, 20]
check_log_chain(log_chain, list(range(max_revision, 1 - 1, -1)), path_counts)
def log_parser(sbox):
"meta-test for the log parser"
logs = ['''------------------------------------------------------------------------
r24 | chuck | 2007-04-30 10:18:01 -0500 (Mon, 16 Apr 2007) | 1 line
Changed paths:
M /trunk/death-ray.c
M /trunk/frobnicator/frapnalyzer.c
Merge r12 and r14 from branch to trunk.
------------------------------------------------------------------------
r14 | bob | 2007-04-16 18:50:29 -0500 (Mon, 16 Apr 2007) | 1 line
Changed paths:
M /trunk/death-ray.c
Merged via: r24
Remove inadvertent changes to Death-Ray-o-Matic introduced in r12.
------------------------------------------------------------------------
r12 | alice | 2007-04-16 19:02:48 -0500 (Mon, 16 Apr 2007) | 1 line
Changed paths:
M /trunk/frobnicator/frapnalyzer.c
M /trunk/death-ray.c
Merged via: r24
Fix frapnalyzer bug in frobnicator.
------------------------------------------------------------------------''',
'''------------------------------------------------------------------------
r24 | chuck | 2007-04-30 10:18:01 -0500 (Mon, 16 Apr 2007) | 1 line
Merge r12 and r14 from branch to trunk.
------------------------------------------------------------------------
r14 | bob | 2007-04-16 18:50:29 -0500 (Mon, 16 Apr 2007) | 1 line
Merged via: r24
Remove inadvertent changes to Death-Ray-o-Matic introduced in r12.
------------------------------------------------------------------------
r12 | alice | 2007-04-16 19:02:48 -0500 (Mon, 16 Apr 2007) | 1 line
Merged via: r24
Fix frapnalyzer bug in frobnicator.
------------------------------------------------------------------------
r10 | alice | 2007-04-16 19:02:28 -0500 (Mon, 16 Apr 2007) | 1 line
Merged via: r12, r24
Fix frapnalyzer documentation.
------------------------------------------------------------------------
r9 | bob | 2007-04-16 19:01:48 -0500 (Mon, 16 Apr 2007) | 1 line
Merged via: r12, r24
Whitespace fixes. No functional change.
------------------------------------------------------------------------''',
'''------------------------------------------------------------------------
r5 | kfogel | Tue 6 Nov 2001 17:18:19 | 1 line
Log message for revision 5.
------------------------------------------------------------------------
r4 | kfogel | Tue 6 Nov 2001 17:18:18 | 3 lines
Log message for revision 4
but with multiple lines
to test the code.
------------------------------------------------------------------------
r3 | kfogel | Tue 6 Nov 2001 17:18:17 | 1 line
Log message for revision 3.
------------------------------------------------------------------------''',
]
for log in logs:
log_chain = parse_log_output([line+"\n" for line in log.split("\n")])
def check_merge_results(log_chain, expected_merges=None,
expected_reverse_merges=None):
'''Check LOG_CHAIN to see if the log information contains 'Merged via'
and/or 'Reverse Merged via' information indicated by EXPECTED_MERGES and
EXPECTED_REVERSE_MERGES respectively. EXPECTED_MERGES and
EXPECTED_REVERSE_MERGES are dictionaries whose keys are the merged
revisions, and whose values are the merging revisions.'''
for log in log_chain:
if not ((expected_merges and log['revision'] in expected_merges)
or (expected_reverse_merges
and log['revision'] in expected_reverse_merges)):
raise SVNUnexpectedLogs("Found unexpected revision %d" %
log['revision'], log_chain)
if expected_merges:
for rev in expected_merges:
try:
log = [x for x in log_chain if x['revision'] == rev][0]
if 'merges' in log.keys():
actual = log['merges']
else:
actual = []
expected = expected_merges[rev]
if actual != expected:
raise SVNUnexpectedLogs(("Merging revisions in rev %d not " +
"correct; expecting %s, found %s") %
(rev, str(expected), str(actual)), log_chain)
except IndexError:
raise SVNUnexpectedLogs("Merged revision '%d' missing" % rev,
log_chain)
if expected_reverse_merges:
for rev in expected_reverse_merges:
try:
log = [x for x in log_chain if x['revision'] == rev][0]
if 'reverse_merges' in log.keys():
actual = log['reverse_merges']
else:
actual = []
expected = expected_reverse_merges[rev]
if actual != expected:
raise SVNUnexpectedLogs(("Reverse merging revisions in rev %d not " +
"correct; expecting %s, found %s") %
(rev, str(expected), str(actual)), log_chain)
except IndexError:
raise SVNUnexpectedLogs("Reverse merged revision '%d' missing" % rev,
log_chain)
@SkipUnless(server_has_mergeinfo)
def merge_sensitive_log_single_revision(sbox):
"test 'svn log -g' on a single revision"
merge_history_repos(sbox)
wc_dir = sbox.wc_dir
TRUNK_path = os.path.join(wc_dir, "trunk")
BRANCH_B_path = os.path.join(wc_dir, "branches", "b")
saved_cwd = os.getcwd()
expected_merges = {
14 : [],
13 : [14],
12 : [14],
11 : [14, 12],
10 : [14],
}
os.chdir(TRUNK_path)
exit_code, output, err = svntest.actions.run_and_verify_svn(None, None, [],
'log', '-g',
'-r14')
log_chain = parse_log_output(output)
check_merge_results(log_chain, expected_merges)
exit_code, output, err = svntest.actions.run_and_verify_svn(None, None, [],
'log', '-g',
'--limit', '1',
'-r14:1')
log_chain = parse_log_output(output)
check_merge_results(log_chain, expected_merges)
os.chdir(saved_cwd)
expected_merges = {
12 : [],
11 : [12],
}
exit_code, output, err = svntest.actions.run_and_verify_svn(None, None, [],
'log', '-g',
'-r12',
BRANCH_B_path)
log_chain = parse_log_output(output)
check_merge_results(log_chain, expected_merges)
exit_code, output, err = svntest.actions.run_and_verify_svn(None, None, [],
'log', '-g',
'--limit', '1',
'-r12:1',
BRANCH_B_path)
log_chain = parse_log_output(output)
check_merge_results(log_chain, expected_merges)
@SkipUnless(server_has_mergeinfo)
def merge_sensitive_log_branching_revision(sbox):
"test 'svn log -g' on a branching revision"
merge_history_repos(sbox)
wc_dir = sbox.wc_dir
BRANCH_B_path = os.path.join(wc_dir, "branches", "b")
exit_code, output, err = svntest.actions.run_and_verify_svn(None, None, [],
'log', '-g',
'-r10',
BRANCH_B_path)
log_chain = parse_log_output(output)
expected_merges = {
10 : [],
}
check_merge_results(log_chain, expected_merges)
@SkipUnless(server_has_mergeinfo)
def merge_sensitive_log_non_branching_revision(sbox):
"test 'svn log -g' on a non-branching revision"
merge_history_repos(sbox)
TRUNK_path = os.path.join(sbox.wc_dir, "trunk")
exit_code, output, err = svntest.actions.run_and_verify_svn(None, None, [],
'log', '-g',
'-r6',
TRUNK_path)
log_chain = parse_log_output(output)
expected_merges = {
6 : [],
4 : [6],
3 : [6],
}
check_merge_results(log_chain, expected_merges)
@SkipUnless(server_has_mergeinfo)
def merge_sensitive_log_added_path(sbox):
"test 'svn log -g' a path added before merge"
merge_history_repos(sbox)
XI_path = os.path.join(sbox.wc_dir, "trunk", "A", "xi")
exit_code, output, err = svntest.actions.run_and_verify_svn(None, None, [],
'log', '-g',
XI_path)
log_chain = parse_log_output(output)
expected_merges = {
14 : [],
12 : [],
11 : [],
}
check_merge_results(log_chain, expected_merges)
revprops = [{'svn:author': 'jconstant', 'svn:date': '',
'svn:log': 'Merged branches/b to trunk.'},
{'svn:author': 'jconstant', 'svn:date': '',
'svn:log': 'Merged branches/a to branches/b.'},
{'svn:author': 'jrandom', 'svn:date': '',
'svn:log': "Added 'xi' to branches/a,"
' made a few other changes.'}]
svntest.actions.run_and_verify_log_xml(expected_revprops=revprops,
args=['-g', XI_path])
def log_single_change(sbox):
"test log -c for a single change"
guarantee_repos_and_wc(sbox)
repo_url = sbox.repo_url
exit_code, output, err = svntest.actions.run_and_verify_svn(None, None, [],
'log', '-c',
4, repo_url)
log_chain = parse_log_output(output)
check_log_chain(log_chain, [4])
def log_changes_range(sbox):
"test log -c on range of changes"
guarantee_repos_and_wc(sbox)
repo_url = sbox.repo_url
exit_code, output, err = svntest.actions.run_and_verify_svn(None, None, [],
'log', '-c',
'2-5', repo_url)
log_chain = parse_log_output(output)
check_log_chain(log_chain, [2, 3, 4, 5])
def log_changes_list(sbox):
"test log -c on comma-separated list of changes"
guarantee_repos_and_wc(sbox)
repo_url = sbox.repo_url
exit_code, output, err = svntest.actions.run_and_verify_svn(None, None, [],
'log', '-c',
'2,5,7',
repo_url)
log_chain = parse_log_output(output)
check_log_chain(log_chain, [2, 5, 7])
def log_changes_complex(sbox):
"test log -c on complex set of ranges"
guarantee_repos_and_wc(sbox)
repo_url = sbox.repo_url
exit_code, output, err = svntest.actions.run_and_verify_svn(None, None, [],
'log', '-c',
'2,5-3,-8,6-7', repo_url)
log_chain = parse_log_output(output)
check_log_chain(log_chain, [2, 5, 4, 3, 8, 6, 7])
def only_one_wc_path(sbox):
"svn log of two wc paths is disallowed"
sbox.build(read_only = True)
os.chdir(sbox.wc_dir)
svntest.actions.run_and_verify_log_xml(
expected_stderr=('.*When specifying working copy paths,'
' only one target may be given'),
args=['A/mu', 'iota'])
def retrieve_revprops(sbox):
"test revprop retrieval"
sbox.build()
svntest.actions.enable_revprop_changes(sbox.repo_dir)
author = 'jrandom'
msg1 = 'Log message for revision 1.'
msg2 = 'Log message for revision 2.'
custom_name = 'retrieve_revprops'
custom_value = 'foo bar'
wc_dir = sbox.wc_dir
cwd = os.getcwd()
os.chdir(wc_dir)
svntest.main.file_append(os.path.join('A', 'D', 'H', 'omega'), "new otext")
os.chdir(cwd)
omega_path = os.path.join(wc_dir, 'A', 'D', 'H', 'omega')
expected_output = svntest.wc.State(wc_dir, {
'A/D/H/omega' : Item(verb='Sending'),
})
expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
expected_status.tweak('A/D/H/omega', wc_rev=2, status=' ')
svntest.actions.run_and_verify_commit(wc_dir,
expected_output,
expected_status,
None,
'-m', msg2,
omega_path)
os.chdir(wc_dir)
svntest.actions.run_and_verify_svn(
None, None, [], 'ps', '--revprop', '-r1', custom_name, custom_value, sbox.repo_url)
svntest.actions.run_and_verify_svn(
None, None, [], 'ps', '--revprop', '-r2', custom_name, custom_value, sbox.repo_url)
svntest.actions.run_and_verify_log_xml(
expected_stderr=(".*cannot assign with 'with-revprop' option"
" \(drop the '='\)"),
args=['--with-revprop=foo=bar'])
svntest.actions.run_and_verify_log_xml(
expected_revprops=[{'svn:author': author, 'svn:date': '', 'svn:log': msg1}],
args=['-r1'])
svntest.actions.run_and_verify_log_xml(
expected_revprops=[{'svn:author': author, 'svn:date': '', 'svn:log': msg1},
{'svn:author': author, 'svn:date': '', 'svn:log': msg2}])
svntest.actions.run_and_verify_log_xml(
expected_revprops=[{'svn:author': author, 'svn:date': ''}],
args=['-q', '-r1'])
svntest.actions.run_and_verify_log_xml(
expected_revprops=[{'svn:date': '', 'svn:log': msg1}],
args=['-r1', '--with-revprop=svn:date', '--with-revprop', 'svn:log',
'--with-revprop', 'nosuchprop'])
svntest.actions.run_and_verify_log_xml(
expected_revprops=[{'svn:author': author, 'svn:date': '',
'svn:log': msg1, custom_name: custom_value}],
args=['-r1', '--with-all-revprops'])
svntest.actions.run_and_verify_log_xml(
expected_revprops=[{'svn:author': author, 'svn:date': '',
'svn:log': msg1, custom_name: custom_value},
{'svn:author': author, 'svn:date': '',
'svn:log': msg2, custom_name: custom_value}],
args=['--with-all-revprops'])
svntest.actions.run_and_verify_log_xml(
expected_revprops=[{custom_name: custom_value}],
args=['-r1', '--with-revprop', custom_name])
@Issue(2866)
def log_xml_with_bad_data(sbox):
"log --xml escapes non-utf8 data"
svntest.actions.load_repo(sbox, os.path.join(os.path.dirname(sys.argv[0]),
'log_tests_data',
'xml-invalid-chars.dump'))
r0_props = {
'svn:date' : '',
'svn:log' : 'After the colon are a space, 3 bad chars, '
+ '2 good chars, and a period: '
+ '?\\021?\\022?\\017\t\n.' }
svntest.actions.run_and_verify_log_xml(
expected_revprops=(r0_props,), args=[sbox.repo_url])
@SkipUnless(server_has_mergeinfo)
@Issue(3172)
def merge_sensitive_log_target_with_bogus_mergeinfo(sbox):
"'svn log -g target_with_bogus_mergeinfo'"
sbox.build()
wc_path = sbox.wc_dir
B_copied_path = os.path.join(wc_path, 'A', 'B-copied')
B_new_path = os.path.join(wc_path, 'A', 'B-new')
B_path = os.path.join(wc_path, 'A', 'B')
C_path = os.path.join(wc_path, 'A', 'C')
D_path = os.path.join(wc_path, 'A', 'D')
svntest.main.run_svn(None, 'cp', B_path, B_copied_path)
svntest.main.run_svn(None, 'ps', SVN_PROP_MERGEINFO, '/A/B-copied:1', C_path)
svntest.main.run_svn(None, 'mkdir', B_new_path)
svntest.main.run_svn(None, 'ps', SVN_PROP_MERGEINFO, '/A/B-new:1', D_path)
svntest.main.run_svn(None, 'ci', '-m', 'setting bogus mergeinfo', wc_path)
svntest.actions.run_and_verify_svn(None, None, [], 'log', '-g', C_path)
svntest.actions.run_and_verify_svn(None, None, [], 'log', '-g', D_path)
@SkipUnless(server_has_mergeinfo)
@Issue(3235)
def merge_sensitive_log_added_mergeinfo_replaces_inherited(sbox):
"log -g and explicit mergeinfo replacing inherited"
sbox.build()
wc_dir = sbox.wc_dir
wc_disk, wc_status = set_up_branch(sbox)
D_COPY_path = os.path.join(wc_dir, "A_COPY", "D")
H_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "H")
expected_output = wc.State(D_COPY_path, {
'H/psi' : Item(status='U '),
'G/rho' : Item(status='U '),
'H/omega' : Item(status='U '),
})
expected_mergeinfo_output = wc.State(D_COPY_path, {
'' : Item(status=' U'),
})
expected_elision_output = wc.State(D_COPY_path, {
})
expected_status = wc.State(D_COPY_path, {
'' : Item(status=' M', wc_rev=2),
'G' : Item(status=' ', wc_rev=2),
'G/pi' : Item(status=' ', wc_rev=2),
'G/rho' : Item(status='M ', wc_rev=2),
'G/tau' : Item(status=' ', wc_rev=2),
'H' : Item(status=' ', wc_rev=2),
'H/chi' : Item(status=' ', wc_rev=2),
'H/psi' : Item(status='M ', wc_rev=2),
'H/omega' : Item(status='M ', wc_rev=2),
'gamma' : Item(status=' ', wc_rev=2),
})
expected_disk = wc.State('', {
'' : Item(props={SVN_PROP_MERGEINFO : '/A/D:2-6'}),
'G' : Item(),
'G/pi' : Item("This is the file 'pi'.\n"),
'G/rho' : Item("New content"),
'G/tau' : Item("This is the file 'tau'.\n"),
'H' : Item(),
'H/chi' : Item("This is the file 'chi'.\n"),
'H/psi' : Item("New content"),
'H/omega' : Item("New content"),
'gamma' : Item("This is the file 'gamma'.\n")
})
expected_skip = wc.State(D_COPY_path, { })
svntest.actions.run_and_verify_merge(D_COPY_path, None, None,
sbox.repo_url + '/A/D', None,
expected_output,
expected_mergeinfo_output,
expected_elision_output,
expected_disk,
expected_status,
expected_skip,
None, None, None, None,
None, 1)
expected_output = svntest.wc.State(wc_dir, {
'A_COPY/D' : Item(verb='Sending'),
'A_COPY/D/G/rho' : Item(verb='Sending'),
'A_COPY/D/H/omega' : Item(verb='Sending'),
'A_COPY/D/H/psi' : Item(verb='Sending'),
})
wc_status.tweak('A_COPY/D',
'A_COPY/D/G/rho',
'A_COPY/D/H/omega',
'A_COPY/D/H/psi',
wc_rev=7)
svntest.actions.run_and_verify_commit(wc_dir, expected_output, wc_status,
None, wc_dir)
wc_disk.tweak("A_COPY/D",
props={SVN_PROP_MERGEINFO : '/A/D:2-6'})
wc_disk.tweak("A_COPY/D/G/rho", "A_COPY/D/H/omega", "A_COPY/D/H/psi",
contents="New content")
svntest.actions.run_and_verify_svn(None,
exp_noop_up_out(7), [],
'up', wc_dir)
wc_status.tweak(wc_rev=7)
expected_output = wc.State(H_COPY_path, {
'psi' : Item(status='U ')
})
expected_mergeinfo_output = wc.State(H_COPY_path, {
'' : Item(status=' G'),
})
expected_elision_output = wc.State(H_COPY_path, {
})
expected_status = wc.State(H_COPY_path, {
'' : Item(status=' M', wc_rev=7),
'psi' : Item(status='M ', wc_rev=7),
'omega' : Item(status=' ', wc_rev=7),
'chi' : Item(status=' ', wc_rev=7),
})
expected_disk = wc.State('', {
'' : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:2,4-6'}),
'psi' : Item("This is the file 'psi'.\n"),
'omega' : Item("New content"),
'chi' : Item("This is the file 'chi'.\n"),
})
expected_skip = wc.State(H_COPY_path, { })
svntest.actions.run_and_verify_merge(H_COPY_path, '3', '2',
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)
expected_output = svntest.wc.State(wc_dir, {
'A_COPY/D/H' : Item(verb='Sending'),
'A_COPY/D/H/psi' : Item(verb='Sending'),
})
wc_status.tweak('A_COPY/D/H',
'A_COPY/D/H/psi',
wc_rev=8)
svntest.actions.run_and_verify_commit(wc_dir, expected_output, wc_status,
None, wc_dir)
wc_disk.tweak("A_COPY/D/H",
props={SVN_PROP_MERGEINFO : '/A/D:2,4-6'})
wc_disk.tweak("A_COPY/D/G/rho", "A_COPY/D/H/omega", "A_COPY/D/H/psi",
contents="New content")
def run_log_g_r8(log_target):
expected_merges = {
8 : []}
expected_reverse_merges = {
3 : [8]}
exit_code, output, err = svntest.actions.run_and_verify_svn(None, None,
[],
'log', '-g',
'-r8',
log_target)
log_chain = parse_log_output(output)
check_merge_results(log_chain, expected_merges, expected_reverse_merges)
run_log_g_r8(wc_dir)
run_log_g_r8(os.path.join(wc_dir, "A_COPY"))
run_log_g_r8(os.path.join(wc_dir, "A_COPY", "D"))
run_log_g_r8(os.path.join(wc_dir, "A_COPY", "D", "H"))
run_log_g_r8(os.path.join(wc_dir, "A_COPY", "D", "H", "psi"))
@SkipUnless(server_has_mergeinfo)
@Issue(3285)
def merge_sensitive_log_propmod_merge_inheriting_path(sbox):
"log -g and simple propmod to merge-inheriting path"
sbox.build()
wc_dir = sbox.wc_dir
wc_disk, wc_status = set_up_branch(sbox)
A_path = os.path.join(wc_dir, 'A')
A_COPY_path = os.path.join(wc_dir, 'A_COPY')
A_COPY_psi_path = os.path.join(wc_dir, 'A_COPY', 'D', 'H', 'psi')
svntest.main.run_svn(None, 'up', wc_dir)
svntest.main.run_svn(None, 'merge', '-r2:6', A_path, A_COPY_path)
svntest.main.run_svn(None, 'ci', '-m', 'Merge changes from A.', wc_dir)
svntest.main.run_svn(None, 'up', wc_dir)
svntest.main.run_svn(None, 'propset', 'foo', 'bar', A_COPY_psi_path)
svntest.main.run_svn(None, 'ci', '-m',
'Set property "foo" to "bar" on A_COPY/D/H/psi', wc_dir)
svntest.main.run_svn(None, 'up', wc_dir)
def run_log_g_r7(log_target):
expected_merges = {
7 : [],
6 : [7],
5 : [7],
4 : [7],
3 : [7],
}
exit_code, output, err = svntest.actions.run_and_verify_svn(
None, None, [], 'log', '-g', '-r7', log_target)
log_chain = parse_log_output(output)
check_merge_results(log_chain, expected_merges)
run_log_g_r7(wc_dir)
run_log_g_r7(A_COPY_path)
def run_log_g_r8(log_target):
expected_merges = { 8 : [] }
exit_code, output, err = svntest.actions.run_and_verify_svn(
None, None, [], 'log', '-g', '-r8', log_target)
log_chain = parse_log_output(output)
check_merge_results(log_chain, expected_merges)
run_log_g_r8(wc_dir)
run_log_g_r8(A_COPY_path)
run_log_g_r8(A_COPY_psi_path)
def log_of_local_copy(sbox):
"svn log on an uncommitted copy"
guarantee_repos_and_wc(sbox)
C_path = os.path.join(sbox.wc_dir, "A", "C")
C_moved_path = os.path.join(sbox.wc_dir, "A", "C_MOVED")
psi_path = os.path.join(sbox.wc_dir, "A", "D", "H", "psi")
psi_moved_path = os.path.join(sbox.wc_dir, "A", "D", "H", "psi_moved")
exit_code, C_log_out, err = svntest.actions.run_and_verify_svn(
None, None, [], 'log', '-v', C_path)
exit_code, psi_log_out, err = svntest.actions.run_and_verify_svn(
None, None, [], 'log', '-v', psi_path)
svntest.actions.run_and_verify_svn(None, None, [], 'mv',
C_path, C_moved_path)
svntest.actions.run_and_verify_svn(None, None, [], 'mv',
psi_path, psi_moved_path)
exit_code, C_moved_log_out, err = svntest.actions.run_and_verify_svn(
None, None, [], 'log', '-v', C_moved_path)
exit_code, psi_moved_log_out, err = svntest.actions.run_and_verify_svn(
None, None, [], 'log', '-v', psi_moved_path)
if C_log_out != C_moved_log_out:
raise svntest.Failure("Log on uncommitted move destination '%s' " \
"differs from that on move source '%s'"
% (C_moved_path, C_path))
if psi_log_out != psi_moved_log_out:
raise svntest.Failure("Log on uncommitted move destination '%s' " \
"differs from that on move source '%s'"
% (psi_moved_path, psi_path))
@SkipUnless(server_has_mergeinfo)
@Issue(3176)
def merge_sensitive_log_reverse_merges(sbox):
"log -g differentiates forward and reverse merges"
sbox.build()
wc_dir = sbox.wc_dir
wc_disk, wc_status = set_up_branch(sbox)
A_path = os.path.join(wc_dir, 'A')
A_COPY_path = os.path.join(wc_dir, 'A_COPY')
D_COPY_path = os.path.join(wc_dir, 'A_COPY', 'D')
svntest.main.run_svn(None, 'up', wc_dir)
svntest.main.run_svn(None, 'merge', '-c3,5', A_path, A_COPY_path)
svntest.main.run_svn(None, 'ci', '-m', 'Merge -c3,5 from A to A_COPY',
wc_dir)
svntest.main.run_svn(None, 'up', wc_dir)
svntest.main.run_svn(None, 'merge', '-c-3,4,-5,6', A_path, A_COPY_path)
svntest.main.run_svn(None, 'ci', '-m', 'Merge -c-3,-5,4,6 from A to A_COPY',
wc_dir)
svntest.main.run_svn(None, 'up', wc_dir)
exit_code, out, err = svntest.actions.run_and_verify_svn(None, None, [],
'log', '-g', '-r8',
A_COPY_path)
log_chain = parse_log_output(out)
expected_merges = {
8 : [],
6 : [8],
4 : [8],
}
expected_reverse_merges = {
5 : [8],
3 : [8],
}
check_merge_results(log_chain, expected_merges, expected_reverse_merges)
exit_code, out, err = svntest.actions.run_and_verify_svn(None, None, [],
'log', '-g', '-r8',
D_COPY_path)
log_chain = parse_log_output(out)
expected_reverse_merges = {
3 : [8],
}
check_merge_results(log_chain, expected_merges, expected_reverse_merges)
@SkipUnless(server_has_mergeinfo)
@Issue(3650)
def merge_sensitive_log_ignores_cyclic_merges(sbox):
"log -g should ignore cyclic merges"
sbox.build()
wc_dir = sbox.wc_dir
wc_disk, wc_status = set_up_branch(sbox)
A_path = os.path.join(wc_dir, 'A')
X_path = os.path.join(wc_dir, 'A', 'C', 'X')
kappa_path = os.path.join(wc_dir, 'A', 'C', 'X', 'kappa')
chi_path = os.path.join(wc_dir, 'A', 'D', 'H', 'chi')
A_COPY_path = os.path.join(wc_dir, 'A_COPY')
mu_COPY_path = os.path.join(wc_dir, 'A_COPY', 'mu')
tau_COPY_path = os.path.join(wc_dir, 'A_COPY', 'D', 'G', 'tau')
Z_COPY_path = os.path.join(wc_dir, 'A_COPY', 'C', 'Z')
nu_COPY_path = os.path.join(wc_dir, 'A_COPY', 'C', 'Z', 'nu')
svntest.main.file_write(mu_COPY_path, "Branch edit.\n")
svntest.main.run_svn(None, 'ci', '-m', 'Branch edit', wc_dir)
svntest.main.file_write(chi_path, "Trunk edit.\n")
svntest.main.file_write(tau_COPY_path, "Branch edit.\n")
svntest.main.run_svn(None, 'ci', '-m', 'Branch and trunk edits in one rev',
wc_dir)
svntest.main.run_svn(None, 'up', wc_dir)
svntest.main.run_svn(None, 'merge', sbox.repo_url + '/A', A_COPY_path)
svntest.main.run_svn(None, 'ci', '-m', 'Sync merge A to A_COPY', wc_dir)
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)
svntest.main.run_svn(None, 'up', wc_dir)
svntest.main.run_svn(None, 'merge', sbox.repo_url + '/A', A_COPY_path)
svntest.main.run_svn(None, 'ci', '-m',
'--record-only merge r10 from A to A_COPY', wc_dir)
svntest.main.run_svn(None, 'mkdir', Z_COPY_path)
svntest.main.file_write(nu_COPY_path, "A new branch file.\n")
svntest.main.run_svn(None, 'add', nu_COPY_path)
svntest.main.run_svn(None, 'ci', '-m', 'Branch edit: Add a subtree', wc_dir)
svntest.main.run_svn(None, 'mkdir', X_path)
svntest.main.file_write(kappa_path, "A new trunk file.\n")
svntest.main.run_svn(None, 'add', kappa_path)
svntest.main.run_svn(None, 'ci', '-m', 'Trunk edit: Add a subtree', wc_dir)
svntest.main.run_svn(None, 'up', wc_dir)
svntest.main.run_svn(None, 'up', wc_dir)
svntest.main.run_svn(None, 'merge', sbox.repo_url + '/A', A_COPY_path)
svntest.main.run_svn(None, 'ci', '-m', 'Sync merge A to A_COPY', wc_dir)
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', '2nd reintegrate of A_COPY to A',
wc_dir)
expected_merges = {
15 : [],
14 : [15],
13 : [],
12 : [15],
11 : [15],
10 : [],
9 : [15,11],
8 : [15,11,9],
7 : [15,11],
6 : [],
5 : [],
4 : [],
3 : [],
2 : [15,11],
1 : [],
}
svntest.main.run_svn(None, 'up', wc_dir)
exit_code, out, err = svntest.actions.run_and_verify_svn(None, None, [],
'log', '-g',
A_path)
log_chain = parse_log_output(out)
check_merge_results(log_chain, expected_merges)
@Issue(3931,3936)
def log_with_unrelated_peg_and_operative_revs(sbox):
"log with unrelated peg and operative rev targets"
guarantee_repos_and_wc(sbox)
target = sbox.repo_url + '/A/D/G/rho@2'
expected_error = ".*(File|path) not found.*"
svntest.actions.run_and_verify_svn(None, None, expected_error,
'log', '-r', '6:7', target)
svntest.actions.run_and_verify_svn(None, None, expected_error,
'log', '-r', '7:6', target)
expected_error = ".*Unable to find repository location for.*"
svntest.actions.run_and_verify_svn(None, None, expected_error,
'log', '-r', '2:9', target)
svntest.actions.run_and_verify_svn(None, None, expected_error,
'log', '-r', '9:2', target)
expected_error = ".*Unable to find repository location for.*"
svntest.actions.run_and_verify_svn(None, None, expected_error,
'log', '-r', '2:HEAD', target)
svntest.actions.run_and_verify_svn(None, None, expected_error,
'log', '-r', 'HEAD:2', target)
@Issue(3937)
def log_on_nonexistent_path_and_valid_rev(sbox):
"log on nonexistent path does not error out"
sbox.build(create_wc=False)
real_path_real_rev = sbox.repo_url + '/A@1'
real_path_bad_rev = sbox.repo_url + '/A@99'
bad_url_bad_rev = sbox.repo_url + '/Z@99'
bad_path_real_rev = sbox.repo_url + '/Z@1'
bad_path_default_rev = sbox.repo_url + '/Z'
svntest.actions.run_and_verify_svn(None, None, [],
'log', '-q', real_path_real_rev)
expected_error = ".*No such revision 99*"
svntest.actions.run_and_verify_svn(None, None, expected_error,
'log', '-q', real_path_bad_rev)
svntest.actions.run_and_verify_svn(None, None, expected_error,
'log', '-q', bad_url_bad_rev)
expected_error = ".*not found.*"
svntest.actions.run_and_verify_svn(None, None, expected_error,
'log', '-q', bad_path_real_rev)
svntest.actions.run_and_verify_svn(None, None, expected_error,
'log', '-q', bad_path_default_rev)
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")
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'])
@Issue(4153)
def log_diff_moved(sbox):
"log --diff on moved file"
sbox.build()
sbox.simple_move('A/mu', 'A/mu2')
svntest.main.file_append(sbox.ospath('A/mu2'), "now mu2\n")
sbox.simple_commit()
sbox.simple_move('A/mu2', 'A/mu3')
svntest.main.file_append(sbox.ospath('A/mu3'), "now mu3\n")
sbox.simple_commit()
mu_at_1 = sbox.repo_url + '/A/mu@1'
mu3_at_3 = sbox.repo_url + '/A/mu3@3'
r1diff = [make_diff_header('mu', 'revision 0', 'revision 1')
+ ["@@ -0,0 +1 @@\n",
"+This is the file 'mu'.\n"]]
r2diff = [make_diff_header('mu',
'.../mu)\t(revision 1',
'.../mu2)\t(revision 2')
+ ["@@ -1 +1,2 @@\n",
" This is the file 'mu'.\n",
"+now mu2\n"]]
r3diff = [make_diff_header('mu2',
'.../mu2)\t(revision 2',
'.../mu3)\t(revision 3')
+ ["@@ -1,2 +1,3 @@\n",
" This is the file 'mu'.\n",
" now mu2\n",
"+now mu3\n"]]
exit_code, output, err = svntest.actions.run_and_verify_svn(None, None, [],
'log', '--diff',
mu_at_1)
log_chain = parse_log_output(output, with_diffs=True)
if len(log_chain) != 1:
raise SVNLogParseError("%d logs found, 1 expected" % len(log_chain))
compare_diff_output(r1diff, log_chain[0]['diff_lines'])
exit_code, output, err = svntest.actions.run_and_verify_svn(None, None, [],
'log', '--diff',
'-r3', mu3_at_3)
log_chain = parse_log_output(output, with_diffs=True)
if len(log_chain) != 1:
raise SVNLogParseError("%d logs found, 1 expected" % len(log_chain))
compare_diff_output(r3diff, log_chain[0]['diff_lines'])
exit_code, output, err = svntest.actions.run_and_verify_svn(None, None, [],
'log', '--diff',
'-r3:2', mu3_at_3)
log_chain = parse_log_output(output, with_diffs=True)
if len(log_chain) != 2:
raise SVNLogParseError("%d logs found, 2 expected" % len(log_chain))
compare_diff_output(r3diff, log_chain[0]['diff_lines'])
compare_diff_output(r2diff, log_chain[1]['diff_lines'])
exit_code, output, err = svntest.actions.run_and_verify_svn(None, None, [],
'log', '--diff',
mu3_at_3)
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(r3diff, log_chain[0]['diff_lines'])
compare_diff_output(r2diff, log_chain[1]['diff_lines'])
compare_diff_output(r1diff, log_chain[2]['diff_lines'])
test_list = [ None,
plain_log,
log_with_empty_repos,
log_where_nothing_changed,
log_to_revision_zero,
dynamic_revision,
log_with_path_args,
log_wc_with_peg_revision,
url_missing_in_head,
log_through_copyfrom_history,
escape_control_chars,
log_xml_empty_date,
log_limit,
log_base_peg,
log_verbose,
log_parser,
merge_sensitive_log_single_revision,
merge_sensitive_log_branching_revision,
merge_sensitive_log_non_branching_revision,
merge_sensitive_log_added_path,
log_single_change,
log_changes_range,
log_changes_list,
log_changes_complex,
only_one_wc_path,
retrieve_revprops,
log_xml_with_bad_data,
merge_sensitive_log_target_with_bogus_mergeinfo,
merge_sensitive_log_added_mergeinfo_replaces_inherited,
merge_sensitive_log_propmod_merge_inheriting_path,
log_of_local_copy,
merge_sensitive_log_reverse_merges,
merge_sensitive_log_ignores_cyclic_merges,
log_with_unrelated_peg_and_operative_revs,
log_on_nonexistent_path_and_valid_rev,
log_diff,
log_diff_moved,
]
if __name__ == '__main__':
svntest.main.run_tests(test_list)