blob: fccf5fba360f61a3f54fa7a3176bd460568cefcb [file] [log] [blame]
#!/usr/bin/env python
#
# mergeinfo_tests.py: testing Merge Tracking reporting
#
# Subversion is a tool for revision control.
# See http://subversion.apache.org for more information.
#
# ====================================================================
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
######################################################################
# General modules
import shutil, sys, re, os
# Our testing module
import svntest
from svntest import wc
# (abbreviation)
Item = wc.StateItem
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
from svntest.main import SVN_PROP_MERGEINFO
from svntest.main import server_has_mergeinfo
# Get a couple merge helpers
from svntest.mergetrees import set_up_branch
from svntest.mergetrees import expected_merge_output
def adjust_error_for_server_version(expected_err):
"Return the expected error regexp appropriate for the server version."
if server_has_mergeinfo():
return expected_err
else:
return ".*Retrieval of mergeinfo unsupported by '.+'"
######################################################################
# Tests
#
# Each test must return on success or raise on failure.
#----------------------------------------------------------------------
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 + '/A',
sbox.repo_url + '/A2',
"--show-revs=merged")
@SkipUnless(server_has_mergeinfo)
def mergeinfo(sbox):
"'mergeinfo' on a path with mergeinfo"
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, [],
'ps', SVN_PROP_MERGEINFO, '/A:3',
sbox.ospath('A2'))
svntest.actions.run_and_verify_mergeinfo(adjust_error_for_server_version(""),
['3'],
sbox.repo_url + '/A',
sbox.ospath('A2'),
"--show-revs=merged")
@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()
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(""),
['2', '4'], url(B2), path(B),
"--show-revs=merged")
svntest.actions.run_and_verify_mergeinfo(adjust_error_for_server_version(""),
['2', '4'], path(B2), path(B),
"--show-revs=merged")
svntest.actions.run_and_verify_mergeinfo(adjust_error_for_server_version(""),
['3', '5'], url(B3), path(B),
"--show-revs=merged")
svntest.actions.run_and_verify_mergeinfo(adjust_error_for_server_version(""),
['3', '5'], path(B3), path(B),
"--show-revs=merged")
@SkipUnless(server_has_mergeinfo)
def mergeinfo_non_source(sbox):
"'mergeinfo' with uninteresting source selection"
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)
# Check on a source we haven't "merged" from.
svntest.actions.run_and_verify_mergeinfo(adjust_error_for_server_version(""),
[], H2_url, H_path,
"--show-revs=merged")
#----------------------------------------------------------------------
# Issue #3138
@SkipUnless(server_has_mergeinfo)
@Issue(3138)
def mergeinfo_on_unknown_url(sbox):
"mergeinfo of an unknown url should return error"
sbox.build()
wc_dir = sbox.wc_dir
# remove a path from the repo and commit.
iota_path = os.path.join(wc_dir, 'iota')
svntest.actions.run_and_verify_svn(None, [], 'rm', iota_path)
svntest.actions.run_and_verify_svn(None, [],
"ci", wc_dir, "-m", "log message")
url = sbox.repo_url + "/iota"
expected_err = adjust_error_for_server_version(".*File not found.*iota.*|"
".*iota.*path not found.*")
svntest.actions.run_and_verify_svn(None, expected_err,
"mergeinfo", "--show-revs", "eligible",
url, wc_dir)
# Test for issue #3126 'svn mergeinfo shows too few or too many
# eligible revisions'. Specifically
# https://issues.apache.org/jira/browse/SVN-3126#desc5.
@SkipUnless(server_has_mergeinfo)
@Issue(3126)
def non_inheritable_mergeinfo(sbox):
"non-inheritable mergeinfo shows as merged"
sbox.build()
wc_dir = sbox.wc_dir
expected_disk, expected_status = set_up_branch(sbox)
# Some paths we'll care about
A_COPY_path = os.path.join(wc_dir, "A_COPY")
D_COPY_path = os.path.join(wc_dir, "A_COPY", "D")
rho_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "G", "rho")
# Update the WC, then merge r4 from A to A_COPY and r6 from A to A_COPY
# at --depth empty and commit the merges as r7.
svntest.actions.run_and_verify_svn(exp_noop_up_out(6), [], 'up',
wc_dir)
expected_status.tweak(wc_rev=6)
svntest.actions.run_and_verify_svn(
expected_merge_output([[4]],
['U ' + rho_COPY_path + '\n',
' U ' + A_COPY_path + '\n',]),
[], 'merge', '-c4',
sbox.repo_url + '/A',
A_COPY_path)
svntest.actions.run_and_verify_svn(
expected_merge_output([[6]], ' G ' + A_COPY_path + '\n'),
[], 'merge', '-c6',
sbox.repo_url + '/A',
A_COPY_path, '--depth', 'empty')
expected_output = wc.State(wc_dir, {
'A_COPY' : Item(verb='Sending'),
'A_COPY/D/G/rho' : Item(verb='Sending'),
})
expected_status.tweak('A_COPY', 'A_COPY/D/G/rho', wc_rev=7)
svntest.actions.run_and_verify_commit(wc_dir, expected_output,
expected_status)
# Update the WC a last time to ensure full inheritance.
svntest.actions.run_and_verify_svn(exp_noop_up_out(7), [], 'up',
wc_dir)
# Despite being non-inheritable, r6 should still show as merged to A_COPY
# and not eligible for merging.
svntest.actions.run_and_verify_mergeinfo(adjust_error_for_server_version(""),
['4','6*'],
sbox.repo_url + '/A',
A_COPY_path,
'--show-revs', 'merged')
svntest.actions.run_and_verify_mergeinfo(adjust_error_for_server_version(""),
['3','5','6*'],
sbox.repo_url + '/A',
A_COPY_path,
'--show-revs', 'eligible')
# But if we drop down to A_COPY/D, r6 should show as eligible because it
# was only merged into A_COPY, no deeper.
svntest.actions.run_and_verify_mergeinfo(adjust_error_for_server_version(""),
['4'],
sbox.repo_url + '/A/D',
D_COPY_path,
'--show-revs', 'merged')
svntest.actions.run_and_verify_mergeinfo(adjust_error_for_server_version(""),
['3','6'],
sbox.repo_url + '/A/D',
D_COPY_path,
'--show-revs', 'eligible')
# Test for -R option with svn mergeinfo subcommand.
#
# Test for issue #3242 'Subversion demands unnecessary access to parent
# directories of operations'
@Issue(3242)
@SkipUnless(server_has_mergeinfo)
def recursive_mergeinfo(sbox):
"test svn mergeinfo -R"
sbox.build()
wc_dir = sbox.wc_dir
expected_disk, expected_status = set_up_branch(sbox)
# Some paths we'll care about
A_path = os.path.join(wc_dir, "A")
A_COPY_path = os.path.join(wc_dir, "A_COPY")
B_COPY_path = os.path.join(wc_dir, "A_COPY", "B")
C_COPY_path = os.path.join(wc_dir, "A_COPY", "C")
rho_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "G", "rho")
H_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "H")
F_COPY_path = os.path.join(wc_dir, "A_COPY", "B", "F")
omega_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "H", "omega")
beta_COPY_path = os.path.join(wc_dir, "A_COPY", "B", "E", "beta")
A2_path = os.path.join(wc_dir, "A2")
nu_path = os.path.join(wc_dir, "A2", "B", "F", "nu")
nu_COPY_path = os.path.join(wc_dir, "A_COPY", "B", "F", "nu")
nu2_path = os.path.join(wc_dir, "A2", "C", "nu2")
# Rename A to A2 in r7.
svntest.actions.run_and_verify_svn(exp_noop_up_out(6), [], 'up', wc_dir)
svntest.actions.run_and_verify_svn(None, [],
'ren', A_path, A2_path)
svntest.actions.run_and_verify_svn(None, [],
'ci', wc_dir, '-m', 'rename A to A2')
# Add the files A/B/F/nu and A/C/nu2 and commit them as r8.
svntest.main.file_write(nu_path, "A new file.\n")
svntest.main.file_write(nu2_path, "Another new file.\n")
svntest.main.run_svn(None, "add", nu_path, nu2_path)
svntest.actions.run_and_verify_svn(None, [],
'ci', wc_dir, '-m', 'Add 2 new files')
# Do several merges to create varied subtree mergeinfo
# Merge r4 from A2 to A_COPY at depth empty
svntest.actions.run_and_verify_svn(exp_noop_up_out(8), [], 'up',
wc_dir)
svntest.actions.run_and_verify_svn(
expected_merge_output([[4]], ' U ' + A_COPY_path + '\n'),
[], 'merge', '-c4', '--depth', 'empty',
sbox.repo_url + '/A2',
A_COPY_path)
# Merge r6 from A2/D/H to A_COPY/D/H
svntest.actions.run_and_verify_svn(
expected_merge_output([[6]],
['U ' + omega_COPY_path + '\n',
' G ' + H_COPY_path + '\n']),
[], 'merge', '-c6',
sbox.repo_url + '/A2/D/H',
H_COPY_path)
# Merge r5 from A2 to A_COPY
svntest.actions.run_and_verify_svn(
expected_merge_output([[5]],
['U ' + beta_COPY_path + '\n',
' G ' + A_COPY_path + '\n',
' G ' + B_COPY_path + '\n',
' U ' + B_COPY_path + '\n',], # Elision
elides=True),
[], 'merge', '-c5',
sbox.repo_url + '/A2',
A_COPY_path)
# Reverse merge -r5 from A2/C to A_COPY/C leaving empty mergeinfo on
# A_COPY/C.
svntest.actions.run_and_verify_svn(
expected_merge_output([[-5]],
' G ' + C_COPY_path + '\n'),
[], 'merge', '-c-5',
sbox.repo_url + '/A2/C', C_COPY_path)
# Merge r8 from A2/B/F to A_COPY/B/F
svntest.actions.run_and_verify_svn(
expected_merge_output([[8]],
['A ' + nu_COPY_path + '\n',
' G ' + F_COPY_path + '\n']),
[], 'merge', '-c8',
sbox.repo_url + '/A2/B/F',
F_COPY_path)
# Commit everything this far as r9
svntest.actions.run_and_verify_svn(None, [],
'ci', wc_dir, '-m', 'Many merges')
svntest.actions.run_and_verify_svn(exp_noop_up_out(9), [], 'up',
wc_dir)
# Test svn mergeinfo -R / --depth infinity.
# Asking for eligible revisions from A2 to A_COPY should show:
#
# r3 - Was never merged.
#
# r4 - Was merged at depth empty, so while there is mergeinfo for the
# revision, the actual text change to A_COPY/D/G/rho hasn't yet
# happened.
#
# r8* - Was only partially merged to the subtree at A_COPY/B/F. The
# addition of A_COPY/C/nu2 is still outstanding.
svntest.actions.run_and_verify_mergeinfo(adjust_error_for_server_version(""),
['3', '4*', '8*'],
sbox.repo_url + '/A2',
sbox.repo_url + '/A_COPY',
'--show-revs', 'eligible', '-R')
# Do the same as above, but test that we can request the revisions
# in reverse order.
svntest.actions.run_and_verify_mergeinfo(adjust_error_for_server_version(""),
['8*', '4*', '3'],
sbox.repo_url + '/A2',
sbox.repo_url + '/A_COPY',
'--show-revs', 'eligible', '-R',
'-r', '9:0')
# Asking for merged revisions from A2 to A_COPY should show:
#
# r4* - Was merged at depth empty, so while there is mergeinfo for the
# revision, the actual text change to A_COPY/D/G/rho hasn't yet
# happened.
#
# r5 - Was merged at depth infinity to the root of the 'branch', so it
# should show as fully merged.
#
# r6 - This was a subtree merge, but since the subtree A_COPY/D/H was
# the ancestor of the only change made in r6 it is considered
# fully merged.
#
# r8* - Was only partially merged to the subtree at A_COPY/B/F. The
# addition of A_COPY/C/nu2 is still outstanding.
svntest.actions.run_and_verify_mergeinfo(adjust_error_for_server_version(""),
['4*', '5', '6', '8*'],
A2_path,
A_COPY_path,
'--show-revs', 'merged',
'--depth', 'infinity')
# Do the same as above, but test that we can request the revisions
# in reverse order.
svntest.actions.run_and_verify_mergeinfo(adjust_error_for_server_version(""),
['8*', '6', '5', '4*'],
A2_path,
A_COPY_path,
'--show-revs', 'merged',
'--depth', 'infinity',
'-r', '9:0')
# A couple tests of problems found with initial issue #3242 fixes.
# We should be able to check for the merged revs from a URL to a URL
# when the latter has explicit mergeinfo...
svntest.actions.run_and_verify_mergeinfo(
adjust_error_for_server_version(''), ['6'],
sbox.repo_url + '/A2/D/H',
sbox.repo_url + '/A_COPY/D/H',
'--show-revs', 'merged')
# ...and when the latter has inherited mergeinfo.
svntest.actions.run_and_verify_mergeinfo(
adjust_error_for_server_version(''), ['6'],
sbox.repo_url + '/A2/D/H/omega',
sbox.repo_url + '/A_COPY/D/H/omega',
'--show-revs', 'merged')
# Test for issue #3180 'svn mergeinfo ignores peg rev for WC target'.
@SkipUnless(server_has_mergeinfo)
def mergeinfo_on_pegged_wc_path(sbox):
"svn mergeinfo on pegged working copy target"
sbox.build()
wc_dir = sbox.wc_dir
expected_disk, expected_status = set_up_branch(sbox)
# Some paths we'll care about
A_path = os.path.join(wc_dir, "A")
A_COPY_path = os.path.join(wc_dir, "A_COPY")
psi_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "H", "psi")
omega_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "H", "omega")
beta_COPY_path = os.path.join(wc_dir, "A_COPY", "B", "E", "beta")
# Do a couple merges
#
# r7 - Merge -c3,6 from A to A_COPY.
svntest.actions.run_and_verify_svn(
expected_merge_output([[3],[6]],
['U ' + psi_COPY_path + '\n',
'U ' + omega_COPY_path + '\n',
' U ' + A_COPY_path + '\n',
' G ' + A_COPY_path + '\n',]),
[], 'merge', '-c3,6', sbox.repo_url + '/A', A_COPY_path)
svntest.actions.run_and_verify_svn(None, [],
'ci', wc_dir,
'-m', 'Merge r3 and r6')
# r8 - Merge -c5 from A to A_COPY.
svntest.actions.run_and_verify_svn(
expected_merge_output([[5]],
['U ' + beta_COPY_path + '\n',
' U ' + A_COPY_path + '\n']),
[], 'merge', '-c5', '--allow-mixed-revisions',
sbox.repo_url + '/A', A_COPY_path)
svntest.actions.run_and_verify_svn(None, [],
'ci', wc_dir,
'-m', 'Merge r5')
# Ask for merged and eligible revisions to A_COPY pegged at various values.
# Prior to issue #3180 fix the peg revision was ignored.
#
# A_COPY pegged to non-existent revision
svntest.actions.run_and_verify_mergeinfo(
adjust_error_for_server_version('.*No such revision 99'),
[], A_path, A_COPY_path + '@99', '--show-revs', 'merged')
# A_COPY@BASE
svntest.actions.run_and_verify_mergeinfo(
adjust_error_for_server_version(''),
['3','5','6'], A_path, A_COPY_path + '@BASE', '--show-revs', 'merged')
# A_COPY@HEAD
svntest.actions.run_and_verify_mergeinfo(
adjust_error_for_server_version(''),
['3','5','6'], A_path, A_COPY_path + '@HEAD', '--show-revs', 'merged')
# A_COPY@4 (Prior to any merges)
svntest.actions.run_and_verify_mergeinfo(
adjust_error_for_server_version(''),
[], A_path, A_COPY_path + '@4', '--show-revs', 'merged')
# A_COPY@COMMITTED (r8)
svntest.actions.run_and_verify_mergeinfo(
adjust_error_for_server_version(''),
['3','5','6'], A_path, A_COPY_path + '@COMMITTED', '--show-revs',
'merged')
# A_COPY@PREV (r7)
svntest.actions.run_and_verify_mergeinfo(
adjust_error_for_server_version(''),
['3', '6'], A_path, A_COPY_path + '@PREV', '--show-revs', 'merged')
# A_COPY@BASE
svntest.actions.run_and_verify_mergeinfo(
adjust_error_for_server_version(''),
['4'], A_path, A_COPY_path + '@BASE', '--show-revs', 'eligible')
# A_COPY@HEAD
svntest.actions.run_and_verify_mergeinfo(
adjust_error_for_server_version(''),
['4'], A_path, A_COPY_path + '@HEAD', '--show-revs', 'eligible')
# A_COPY@4 (Prior to any merges)
svntest.actions.run_and_verify_mergeinfo(
adjust_error_for_server_version(''),
['3', '4', '5', '6'], A_path, A_COPY_path + '@4', '--show-revs', 'eligible')
# A_COPY@COMMITTED (r8)
svntest.actions.run_and_verify_mergeinfo(
adjust_error_for_server_version(''),
['4'], A_path, A_COPY_path + '@COMMITTED', '--show-revs',
'eligible')
# A_COPY@PREV (r7)
svntest.actions.run_and_verify_mergeinfo(
adjust_error_for_server_version(''),
['4', '5'], A_path, A_COPY_path + '@PREV', '--show-revs', 'eligible')
#----------------------------------------------------------------------
# A test for issue 3986 'svn_client_mergeinfo_log API is broken'.
@Issue(3986)
@SkipUnless(server_has_mergeinfo)
def wc_target_inherits_mergeinfo_from_repos(sbox):
"wc target inherits mergeinfo from repos"
sbox.build()
wc_dir = sbox.wc_dir
wc_disk, wc_status = set_up_branch(sbox, nbr_of_branches=2)
A_COPY_path = os.path.join(wc_dir, 'A_COPY')
rho_COPY_path = os.path.join(wc_dir, 'A_COPY', 'D', 'G', 'rho')
gamma_2_path = os.path.join(wc_dir, 'A_COPY_2', 'D', 'gamma')
tau_path = os.path.join(wc_dir, 'A', 'D', 'G', 'tau')
D_COPY_path = os.path.join(wc_dir, 'A_COPY', 'D')
# Merge -c5 ^/A/D/G/rho A_COPY\D\G\rho
# Merge -c7 ^/A A_COPY
# Commit as r8
#
# This gives us some explicit mergeinfo on the "branch" root and
# one of its subtrees:
#
# Properties on 'A_COPY\D\G\rho':
# svn:mergeinfo
# /A/D/G/rho:5
# Properties on 'A_COPY':
# svn:mergeinfo
# /A:7
svntest.actions.run_and_verify_svn(None, [], 'merge',
sbox.repo_url + '/A/D/G/rho',
rho_COPY_path, '-c5')
svntest.actions.run_and_verify_svn(None, [], 'merge',
sbox.repo_url + '/A',
A_COPY_path, '-c7')
svntest.actions.run_and_verify_svn(None, [], 'ci', '-m',
'Cherrypicks to branch subtree and root',
wc_dir)
# Checkout a new wc rooted at ^/A_COPY/D.
subtree_wc = sbox.add_wc_path('D_COPY')
svntest.actions.run_and_verify_svn(None, [], 'co',
sbox.repo_url + '/A_COPY/D',
subtree_wc)
# Check the merged and eligible revisions both recursively and
# non-recursively.
# Eligible : Non-recursive
svntest.actions.run_and_verify_mergeinfo(
adjust_error_for_server_version(''),
['4','5'], sbox.repo_url + '/A/D', subtree_wc,
'--show-revs', 'eligible')
# Eligible : Recursive
svntest.actions.run_and_verify_mergeinfo(
adjust_error_for_server_version(''),
['4'], sbox.repo_url + '/A/D', subtree_wc,
'--show-revs', 'eligible', '-R')
# Merged : Non-recursive
svntest.actions.run_and_verify_mergeinfo(
adjust_error_for_server_version(''),
['7'], sbox.repo_url + '/A/D', subtree_wc,
'--show-revs', 'merged')
# Merged : Recursive
svntest.actions.run_and_verify_mergeinfo(
adjust_error_for_server_version(''),
['5','7'], sbox.repo_url + '/A/D', subtree_wc,
'--show-revs', 'merged', '-R')
# Test that intersecting revisions in the 'svn mergeinfo' target
# from one source don't show up as merged when asking about a different
# source.
#
# In r9 make a change that effects two branches:
svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir)
svntest.main.file_write(gamma_2_path, "New content.\n")
svntest.main.file_write(tau_path, "New content.\n")
svntest.actions.run_and_verify_svn(None, [], 'ci', '-m',
'Make changes under both A and A_COPY_2',
wc_dir)
# In r10 merge r9 from A_COPY_2 to A_COPY.
#
# This gives us this mergeinfo:
#
# Properties on 'A_COPY':
# svn:mergeinfo
# /A:7
# /A_COPY_2:9
# Properties on 'A_COPY\D\G\rho':
# svn:mergeinfo
# /A/D/G/rho:5
svntest.actions.run_and_verify_svn(None, [], 'merge',
sbox.repo_url + '/A_COPY_2',
A_COPY_path, '-c9')
svntest.actions.run_and_verify_svn(None, [], 'ci', '-m',
'Merge r8 from A_COPY_2 to A_COPY',
wc_dir)
def test_svn_mergeinfo_4_way(wc_target):
# Eligible : Non-recursive
svntest.actions.run_and_verify_mergeinfo(
adjust_error_for_server_version(''),
['4','5','9'], sbox.repo_url + '/A/D', wc_target,
'--show-revs', 'eligible')
# Eligible : Recursive
svntest.actions.run_and_verify_mergeinfo(
adjust_error_for_server_version(''),
['4','9'], sbox.repo_url + '/A/D', wc_target,
'--show-revs', 'eligible', '-R')
# Merged : Non-recursive
svntest.actions.run_and_verify_mergeinfo(
adjust_error_for_server_version(''),
['7'], sbox.repo_url + '/A/D', wc_target,
'--show-revs', 'merged')
# Merged : Recursive
svntest.actions.run_and_verify_mergeinfo(
adjust_error_for_server_version(''),
['5','7'], sbox.repo_url + '/A/D', wc_target,
'--show-revs', 'merged', '-R')
# Test while the target is the full WC and then with the subtree WC:
svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir)
svntest.actions.run_and_verify_svn(None, [], 'up', subtree_wc)
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, [], 'add', nu_path)
svntest.actions.run_and_verify_svn(None, [], 'ci',
'-m', 'Add a file', wc_dir)
# r8 - Sync merge ^/A to A_COPY
svntest.actions.run_and_verify_svn(None, [], 'merge',
sbox.repo_url + '/A', A_COPY_path)
svntest.actions.run_and_verify_svn(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, [], '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, [], 'merge',
sbox.repo_url + '/A/C/nu', nu_COPY_path)
svntest.actions.run_and_verify_svn(None, [], 'ci',
'-m', 'Add a file', wc_dir)
svntest.actions.run_and_verify_svn(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 noninheritable_mergeinfo_not_always_eligible(sbox):
"noninheritable 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, [], '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, [],
'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')
@SkipUnless(server_has_mergeinfo)
@Issue(4301)
def mergeinfo_local_move(sbox):
"'mergeinfo' on a locally moved path"
sbox.build()
wc_dir = sbox.wc_dir
sbox.simple_move('A', 'A2')
svntest.actions.run_and_verify_svn(None, [],
'mergeinfo', sbox.repo_url + '/A',
sbox.ospath('A2'))
@SkipUnless(server_has_mergeinfo)
@Issue(4582)
def no_mergeinfo_on_tree_conflict_victim(sbox):
"do not record mergeinfo on tree conflict victims"
sbox.build()
# Create a branch of A called A_copy
sbox.simple_copy('A', 'A_copy')
sbox.simple_commit()
# Add a new directory and file on both branches
sbox.simple_mkdir('A/dir')
sbox.simple_add_text('new file', 'A/dir/f')
sbox.simple_commit()
sbox.simple_mkdir('A_copy/dir')
sbox.simple_add_text('new file', 'A_copy/dir/f')
sbox.simple_commit()
# Run a merge from A to A_copy
expected_output = wc.State(sbox.ospath('A_copy'), {
'dir' : Item(status=' ', treeconflict='C'),
'dir/f' : Item(status=' ', treeconflict='A'),
})
expected_mergeinfo_output = wc.State(sbox.ospath('A_copy'), {
'' : Item(status=' U'),
})
expected_elision_output = wc.State(sbox.ospath('A_copy'), {
})
expected_disk = svntest.wc.State('', {
'C' : Item(),
'B/E/beta' : Item(contents="This is the file 'beta'.\n"),
'B/E/alpha' : Item(contents="This is the file 'alpha'.\n"),
'B/lambda' : Item(contents="This is the file 'lambda'.\n"),
'B/F' : Item(),
'D/H/omega' : Item(contents="This is the file 'omega'.\n"),
'D/H/psi' : Item(contents="This is the file 'psi'.\n"),
'D/H/chi' : Item(contents="This is the file 'chi'.\n"),
'D/G/tau' : Item(contents="This is the file 'tau'.\n"),
'D/G/pi' : Item(contents="This is the file 'pi'.\n"),
'D/G/rho' : Item(contents="This is the file 'rho'.\n"),
'D/gamma' : Item(contents="This is the file 'gamma'.\n"),
'dir/f' : Item(contents="new file"),
'mu' : Item(contents="This is the file 'mu'.\n"),
})
# The merge will create an add vs add tree conflict on A_copy/dir
expected_status = svntest.wc.State(sbox.ospath('A_copy'), {
'' : Item(status=' M', wc_rev='4'),
'D' : Item(status=' ', wc_rev='4'),
'D/G' : Item(status=' ', wc_rev='4'),
'D/G/pi' : Item(status=' ', wc_rev='4'),
'D/G/rho' : Item(status=' ', wc_rev='4'),
'D/G/tau' : Item(status=' ', wc_rev='4'),
'D/H' : Item(status=' ', wc_rev='4'),
'D/H/psi' : Item(status=' ', wc_rev='4'),
'D/H/omega' : Item(status=' ', wc_rev='4'),
'D/H/chi' : Item(status=' ', wc_rev='4'),
'D/gamma' : Item(status=' ', wc_rev='4'),
'B' : Item(status=' ', wc_rev='4'),
'B/F' : Item(status=' ', wc_rev='4'),
'B/E' : Item(status=' ', wc_rev='4'),
'B/E/alpha' : Item(status=' ', wc_rev='4'),
'B/E/beta' : Item(status=' ', wc_rev='4'),
'B/lambda' : Item(status=' ', wc_rev='4'),
'C' : Item(status=' ', wc_rev='4'),
'dir' : Item(status=' ', treeconflict='C', wc_rev='4'),
'dir/f' : Item(status=' ', wc_rev='4'),
'mu' : Item(status=' ', wc_rev='4'),
})
expected_skip = wc.State('', { })
sbox.simple_update('A_copy')
svntest.actions.run_and_verify_merge(sbox.ospath('A_copy'),
None, None, # rev1, rev2
'^/A',
None, # URL2
expected_output,
expected_mergeinfo_output,
expected_elision_output,
expected_disk,
expected_status,
expected_skip)
# Resolve the tree conflict by accepting the working copy state left
# behind by the merge. This preserves the line of history of A_copy/dir,
# which originated on the branch 'A_copy', rather than replacing it with
# Jthe line f history of A/dir which originated on branch 'A'
svntest.actions.run_and_verify_resolve([sbox.ospath('A_copy/dir')],
'--accept', 'working',
sbox.ospath('A_copy/dir'))
sbox.simple_commit('A_copy')
# Now try to merge the 'A_copy' branch back to 'A"
expected_output = wc.State(sbox.ospath('A'), {
'dir' : Item(status='R '), # changes line of history of A/dir
'dir/f' : Item(status='A '),
})
expected_mergeinfo_output = wc.State(sbox.ospath('A'), {
'' : Item(status=' U'),
})
expected_elision_output = wc.State(sbox.ospath('A'), {
})
expected_disk = svntest.wc.State('', {
'C' : Item(),
'B/E/beta' : Item(contents="This is the file 'beta'.\n"),
'B/E/alpha' : Item(contents="This is the file 'alpha'.\n"),
'B/F' : Item(),
'B/lambda' : Item(contents="This is the file 'lambda'.\n"),
'D/H/omega' : Item(contents="This is the file 'omega'.\n"),
'D/H/psi' : Item(contents="This is the file 'psi'.\n"),
'D/H/chi' : Item(contents="This is the file 'chi'.\n"),
'D/G/tau' : Item(contents="This is the file 'tau'.\n"),
'D/G/pi' : Item(contents="This is the file 'pi'.\n"),
'D/G/rho' : Item(contents="This is the file 'rho'.\n"),
'D/gamma' : Item(contents="This is the file 'gamma'.\n"),
'dir/f' : Item(contents="new file"),
'mu' : Item(contents="This is the file 'mu'.\n"),
})
expected_status = svntest.wc.State(sbox.ospath('A'), {
'' : Item(status=' M', wc_rev='5'),
'dir' : Item(status='R ', copied='+', wc_rev='-'),
'dir/f' : Item(status=' ', copied='+', wc_rev='-'),
'D' : Item(status=' ', wc_rev='5'),
'D/H' : Item(status=' ', wc_rev='5'),
'D/H/chi' : Item(status=' ', wc_rev='5'),
'D/H/omega' : Item(status=' ', wc_rev='5'),
'D/H/psi' : Item(status=' ', wc_rev='5'),
'D/G' : Item(status=' ', wc_rev='5'),
'D/G/pi' : Item(status=' ', wc_rev='5'),
'D/G/rho' : Item(status=' ', wc_rev='5'),
'D/G/tau' : Item(status=' ', wc_rev='5'),
'D/gamma' : Item(status=' ', wc_rev='5'),
'B' : Item(status=' ', wc_rev='5'),
'B/E' : Item(status=' ', wc_rev='5'),
'B/E/beta' : Item(status=' ', wc_rev='5'),
'B/E/alpha' : Item(status=' ', wc_rev='5'),
'B/lambda' : Item(status=' ', wc_rev='5'),
'B/F' : Item(status=' ', wc_rev='5'),
'mu' : Item(status=' ', wc_rev='5'),
'C' : Item(status=' ', wc_rev='5'),
})
expected_skip = wc.State('', { })
sbox.simple_update('A')
svntest.actions.run_and_verify_merge(sbox.ospath('A'),
None, None, # rev1, rev2
'^/A_copy',
None, # URL2
expected_output,
expected_mergeinfo_output,
expected_elision_output,
expected_disk,
expected_status,
expected_skip)
sbox.simple_commit('A')
########################################################################
# Run the tests
# Note that mergeinfo --log is tested in log_tests.py
# list all tests here, starting with None:
test_list = [ None,
no_mergeinfo,
mergeinfo,
explicit_mergeinfo_source,
mergeinfo_non_source,
mergeinfo_on_unknown_url,
non_inheritable_mergeinfo,
recursive_mergeinfo,
mergeinfo_on_pegged_wc_path,
wc_target_inherits_mergeinfo_from_repos,
natural_history_is_not_eligible_nor_merged,
noninheritable_mergeinfo_not_always_eligible,
mergeinfo_local_move,
no_mergeinfo_on_tree_conflict_victim,
]
if __name__ == '__main__':
svntest.main.run_tests(test_list)
# NOTREACHED