blob: f71ec7380a07ff33e88fe3a83f045f17adce3722 [file] [log] [blame]
#!/usr/bin/env python
#
# switch_tests.py: testing `svn switch'.
#
# 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, re, os
# Our testing module
import svntest
from svntest import verify, actions, main, deeptrees
# (abbreviation)
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
Item = svntest.wc.StateItem
from svntest.main import SVN_PROP_MERGEINFO, server_has_mergeinfo
from svntest.deeptrees import do_routine_switching, commit_routine_switching, \
get_routine_disk_state, get_routine_status_state
######################################################################
# Tests
#
#----------------------------------------------------------------------
def routine_switching(sbox):
"test some basic switching operations"
sbox.build(read_only = True)
# Setup (and verify) some switched things
do_routine_switching(sbox.wc_dir, sbox.repo_url, 1)
#----------------------------------------------------------------------
def commit_switched_things(sbox):
"commits after some basic switching operations"
sbox.build()
wc_dir = sbox.wc_dir
# Setup some switched things (don't bother verifying)
do_routine_switching(wc_dir, sbox.repo_url, 0)
# Commit some stuff (and verify)
commit_routine_switching(wc_dir, 1)
#----------------------------------------------------------------------
def full_update(sbox):
"update wc that contains switched things"
sbox.build()
wc_dir = sbox.wc_dir
# Setup some switched things (don't bother verifying)
do_routine_switching(wc_dir, sbox.repo_url, 0)
# Copy wc_dir to a backup location
wc_backup = sbox.add_wc_path('backup')
svntest.actions.duplicate_dir(wc_dir, wc_backup)
# Commit some stuff (don't bother verifying)
commit_routine_switching(wc_backup, 0)
# Some convenient path variables
iota_path = sbox.ospath('iota')
gamma_path = sbox.ospath('A/D/gamma')
Bpi_path = sbox.ospath('A/B/pi')
BZ_path = sbox.ospath('A/B/Z')
Bzeta_path = sbox.ospath('A/B/Z/zeta')
Gpi_path = sbox.ospath('A/D/G/pi')
GZ_path = sbox.ospath('A/D/G/Z')
Gzeta_path = sbox.ospath('A/D/G/Z/zeta')
# Create expected output tree for an update of wc_backup.
expected_output = svntest.wc.State(wc_dir, {
'iota' : Item(status='U '),
'A/D/gamma' : Item(status='U '),
'A/B/pi' : Item(status='U '),
'A/B/Z' : Item(status='A '),
'A/B/Z/zeta' : Item(status='A '),
'A/D/G/pi' : Item(status='U '),
'A/D/G/Z' : Item(status='A '),
'A/D/G/Z/zeta' : Item(status='A '),
})
# Create expected disk tree for the update
expected_disk = get_routine_disk_state(wc_dir)
expected_disk.tweak('iota', contents="This is the file 'gamma'.\napple")
expected_disk.tweak('A/D/gamma', contents="This is the file 'gamma'.\napple")
expected_disk.tweak('A/B/pi', contents="This is the file 'pi'.\nmelon")
expected_disk.tweak('A/D/G/pi', contents="This is the file 'pi'.\nmelon")
expected_disk.add({
'A/B/Z' : Item(),
'A/B/Z/zeta' : Item(contents="This is the file 'zeta'.\n"),
'A/D/G/Z' : Item(),
'A/D/G/Z/zeta' : Item(contents="This is the file 'zeta'.\n"),
})
# Create expected status tree for the update.
expected_status = get_routine_status_state(wc_dir)
expected_status.tweak(wc_rev=2)
expected_status.add({
'A/D/G/Z' : Item(status=' ', wc_rev=2),
'A/D/G/Z/zeta' : Item(status=' ', wc_rev=2),
'A/B/Z' : Item(status=' ', wc_rev=2),
'A/B/Z/zeta' : Item(status=' ', wc_rev=2),
})
expected_status.tweak('iota', 'A/B', switched='S')
svntest.actions.run_and_verify_update(wc_dir,
expected_output,
expected_disk,
expected_status)
#----------------------------------------------------------------------
def full_rev_update(sbox):
"reverse update wc that contains switched things"
sbox.build()
wc_dir = sbox.wc_dir
# Setup some switched things (don't bother verifying)
do_routine_switching(wc_dir, sbox.repo_url, 0)
# Commit some stuff (don't bother verifying)
commit_routine_switching(wc_dir, 0)
# Update to HEAD (tested elsewhere)
svntest.main.run_svn(None, 'up', wc_dir)
# Some convenient path variables
iota_path = sbox.ospath('iota')
gamma_path = sbox.ospath('A/D/gamma')
Bpi_path = sbox.ospath('A/B/pi')
BZ_path = sbox.ospath('A/B/Z')
Gpi_path = sbox.ospath('A/D/G/pi')
GZ_path = sbox.ospath('A/D/G/Z')
# Now, reverse update, back to the pre-commit state.
expected_output = svntest.wc.State(wc_dir, {
'iota' : Item(status='U '),
'A/D/gamma' : Item(status='U '),
'A/B/pi' : Item(status='U '),
'A/B/Z' : Item(status='D '),
'A/D/G/pi' : Item(status='U '),
'A/D/G/Z' : Item(status='D '),
})
# Create expected disk tree
expected_disk = get_routine_disk_state(wc_dir)
# Create expected status
expected_status = get_routine_status_state(wc_dir)
expected_status.tweak('iota', 'A/B', switched='S')
svntest.actions.run_and_verify_update(wc_dir,
expected_output,
expected_disk,
expected_status,
[], True,
'-r', '1', wc_dir)
#----------------------------------------------------------------------
def update_switched_things(sbox):
"update switched wc things to HEAD"
sbox.build()
wc_dir = sbox.wc_dir
# Setup some switched things (don't bother verifying)
do_routine_switching(wc_dir, sbox.repo_url, 0)
# Copy wc_dir to a backup location
wc_backup = sbox.add_wc_path('backup')
svntest.actions.duplicate_dir(wc_dir, wc_backup)
# Commit some stuff (don't bother verifying)
commit_routine_switching(wc_backup, 0)
# Some convenient path variables
iota_path = sbox.ospath('iota')
B_path = sbox.ospath('A/B')
# Create expected output tree for an update of wc_backup.
expected_output = svntest.wc.State(wc_dir, {
'iota' : Item(status='U '),
'A/B/pi' : Item(status='U '),
'A/B/Z' : Item(status='A '),
'A/B/Z/zeta' : Item(status='A '),
})
# Create expected disk tree for the update
expected_disk = get_routine_disk_state(wc_dir)
expected_disk.tweak('iota', contents="This is the file 'gamma'.\napple")
expected_disk.tweak('A/B/pi', contents="This is the file 'pi'.\nmelon")
expected_disk.add({
'A/B/Z' : Item(),
'A/B/Z/zeta' : Item("This is the file 'zeta'.\n"),
})
# Create expected status tree for the update.
expected_status = get_routine_status_state(wc_dir)
expected_status.tweak('iota', 'A/B', switched='S')
expected_status.tweak('A/B', 'A/B/pi', 'A/B/rho', 'A/B/tau', 'iota',
wc_rev=2)
expected_status.add({
'A/B/Z' : Item(status=' ', wc_rev=2),
'A/B/Z/zeta' : Item(status=' ', wc_rev=2),
})
svntest.actions.run_and_verify_update(wc_dir,
expected_output,
expected_disk,
expected_status,
[], False,
B_path,
iota_path)
#----------------------------------------------------------------------
def rev_update_switched_things(sbox):
"reverse update switched wc things to an older rev"
sbox.build()
wc_dir = sbox.wc_dir
# Setup some switched things (don't bother verifying)
do_routine_switching(wc_dir, sbox.repo_url, 0)
# Commit some stuff (don't bother verifying)
commit_routine_switching(wc_dir, 0)
# Some convenient path variables
iota_path = sbox.ospath('iota')
B_path = sbox.ospath('A/B')
# Update to HEAD (tested elsewhere)
svntest.main.run_svn(None, 'up', wc_dir)
# Now, reverse update, back to the pre-commit state.
expected_output = svntest.wc.State(wc_dir, {
'iota' : Item(status='U '),
'A/B/pi' : Item(status='U '),
'A/B/Z' : Item(status='D '),
})
# Create expected disk tree
expected_disk = get_routine_disk_state(wc_dir)
expected_disk.tweak('A/D/gamma', contents="This is the file 'gamma'.\napple")
expected_disk.tweak('A/D/G/pi', contents="This is the file 'pi'.\nmelon")
expected_disk.add({
'A/D/G/Z' : Item(),
'A/D/G/Z/zeta' : Item("This is the file 'zeta'.\n"),
})
# Create expected status tree for the update.
expected_status = get_routine_status_state(wc_dir)
expected_status.tweak(wc_rev=2)
expected_status.tweak('iota', 'A/B', switched='S')
expected_status.tweak('A/B', 'A/B/pi', 'A/B/rho', 'A/B/tau', 'iota',
wc_rev=1)
expected_status.add({
'A/D/G/Z' : Item(status=' ', wc_rev=2),
'A/D/G/Z/zeta' : Item(status=' ', wc_rev=2),
})
svntest.actions.run_and_verify_update(wc_dir,
expected_output,
expected_disk,
expected_status,
[], True,
'-r', '1',
B_path,
iota_path)
#----------------------------------------------------------------------
def log_switched_file(sbox):
"show logs for a switched file"
sbox.build()
wc_dir = sbox.wc_dir
# Setup some switched things (don't bother verifying)
do_routine_switching(wc_dir, sbox.repo_url, 0)
# edit and commit switched file 'iota'
iota_path = sbox.ospath('iota')
svntest.main.run_svn(None, 'ps', 'x', 'x', iota_path)
svntest.main.run_svn(None,
'ci', '-m',
'set prop on switched iota',
iota_path)
# log switched file 'iota'
exit_code, output, error = svntest.main.run_svn(None, 'log', iota_path)
for line in output:
if line.find("set prop on switched iota") != -1:
break
else:
raise svntest.Failure
#----------------------------------------------------------------------
def delete_subdir(sbox):
"switch that deletes a sub-directory"
sbox.build()
wc_dir = sbox.wc_dir
A_path = sbox.ospath('A')
A_url = sbox.repo_url + '/A'
A2_url = sbox.repo_url + '/A2'
A2_B_F_url = sbox.repo_url + '/A2/B/F'
svntest.actions.run_and_verify_svn(['Committing transaction...\n',
'Committed revision 2.\n'], [],
'cp', '-m', 'make copy', A_url, A2_url)
svntest.actions.run_and_verify_svn(['Committing transaction...\n',
'Committed revision 3.\n'], [],
'rm', '-m', 'delete subdir', A2_B_F_url)
expected_output = svntest.wc.State(wc_dir, {
'A/B/F' : Item(status='D '),
})
expected_disk = svntest.main.greek_state.copy()
expected_disk.remove('A/B/F')
expected_status = svntest.actions.get_virginal_state(wc_dir, 3)
expected_status.tweak('A', switched='S')
expected_status.remove('A/B/F')
expected_status.tweak('', 'iota', wc_rev=1)
# Used to fail with a 'directory not locked' error for A/B/F
svntest.actions.run_and_verify_switch(wc_dir, A_path, A2_url,
expected_output,
expected_disk,
expected_status,
[], False,
'--ignore-ancestry')
#----------------------------------------------------------------------
# Issue 1532: Switch a file to a dir: can't switch it back to the file
@XFail()
@Issue(1532)
def file_dir_file(sbox):
"switch a file to a dir and back to the file"
sbox.build(read_only = True)
wc_dir = sbox.wc_dir
file_path = sbox.ospath('iota')
file_url = sbox.repo_url + '/iota'
dir_url = sbox.repo_url + '/A/C'
svntest.actions.run_and_verify_svn(None, [], 'switch',
'--ignore-ancestry', dir_url, file_path)
if not os.path.isdir(file_path):
raise svntest.Failure
# The reason the following switch currently fails is that the node
# is determined to be a 'root', because it is switched against its parent.
# In this specific case the switch editor is designed to be rooted on the node
# itself instead of its ancestor. If you would use sbox.ospath('A') for
# file_path the switch works both ways.
svntest.actions.run_and_verify_svn(None, [], 'switch',
'--ignore-ancestry', file_url, file_path)
if not os.path.isfile(file_path):
raise svntest.Failure
#----------------------------------------------------------------------
# Issue 1751: "svn switch --non-recursive" does not switch existing files,
# and generates the wrong URL for new files.
def nonrecursive_switching(sbox):
"non-recursive switch"
sbox.build()
wc1_dir = sbox.wc_dir
wc2_dir = os.path.join(wc1_dir, 'wc2')
# "Trunk" will be the existing dir "A/", with existing file "mu".
# "Branch" will be the new dir "branch/version1/", with added file "newfile".
# "wc1" will hold the whole repository (including trunk and branch).
# "wc2" will hold the "trunk" and then be switched to the "branch".
# It is irrelevant that wc2 is located on disk as a sub-directory of wc1.
trunk_url = sbox.repo_url + '/A'
branch_url = sbox.repo_url + '/branch'
version1_url = branch_url + '/version1'
wc1_new_file = os.path.join(wc1_dir, 'branch', 'version1', 'newfile')
wc2_new_file = os.path.join(wc2_dir, 'newfile')
wc2_mu_file = os.path.join(wc2_dir, 'mu')
wc2_B_dir = os.path.join(wc2_dir, 'B')
wc2_C_dir = os.path.join(wc2_dir, 'C')
wc2_D_dir = os.path.join(wc2_dir, 'D')
# Check out the trunk as "wc2"
svntest.main.run_svn(None, 'co', trunk_url, wc2_dir)
# Make a branch, and add a new file, in "wc_dir" and repository
svntest.main.run_svn(None,
'mkdir', '-m', '', branch_url)
svntest.main.run_svn(None,
'cp', '-m', '', trunk_url, version1_url)
svntest.main.run_svn(None,
'up', wc1_dir)
svntest.main.file_append(wc1_new_file, "This is the file 'newfile'.\n")
svntest.main.run_svn(None, 'add', wc1_new_file)
sbox.simple_commit()
# Try to switch "wc2" to the branch (non-recursively)
svntest.actions.run_and_verify_svn(None, [], 'switch', '-N',
'--ignore-ancestry', version1_url, wc2_dir)
# Check the URLs of the (not switched) directories.
expected_infos = [
{ 'URL' : '.*/A/B$' },
{ 'URL' : '.*/A/C$' },
{ 'URL' : '.*/A/D$' },
]
svntest.actions.run_and_verify_info(expected_infos,
wc2_B_dir, wc2_C_dir, wc2_D_dir)
# Check the URLs of the switched files.
# ("svn status -u" might be a better check: it fails when newfile's URL
# is bad, and shows "S" when mu's URL is wrong.)
# mu: not switched
expected_infos = [
{ 'URL' : '.*/branch/version1/mu$' },
{ 'URL' : '.*/branch/version1/newfile$' }, # newfile: wrong URL
]
svntest.actions.run_and_verify_info(expected_infos,
wc2_mu_file, wc2_new_file)
#----------------------------------------------------------------------
def failed_anchor_is_target(sbox):
"anchor=target, try to replace a local-mod file"
sbox.build()
wc_dir = sbox.wc_dir
# Set up a switch from dir H, containing locally-modified file 'psi',
# to dir G, containing a directory 'psi'. Expect a tree conflict.
# Make a directory 'G/psi' in the repository.
G_url = sbox.repo_url + '/A/D/G'
G_psi_url = G_url + '/psi'
svntest.actions.run_and_verify_svn(['Committing transaction...\n',
'Committed revision 2.\n'], [],
'mkdir', '-m', 'log msg', G_psi_url)
# Modify the file 'H/psi' locally.
H_path = sbox.ospath('A/D/H')
psi_path = os.path.join(H_path, 'psi')
svntest.main.file_append(psi_path, "more text")
# This switch raises a tree conflict on 'psi', because of the local mods.
svntest.actions.run_and_verify_svn(svntest.verify.AnyOutput, [],
'switch', '--ignore-ancestry',
G_url, H_path)
expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
expected_status.tweak('A/D/H', switched='S', wc_rev=2)
expected_status.tweak('A/D/H/psi', status='R ', copied='+',
wc_rev='-', treeconflict='C')
expected_status.remove('A/D/H/chi', 'A/D/H/omega')
expected_status.add({
'A/D/H/pi' : Item(status=' ', wc_rev=2),
'A/D/H/tau' : Item(status=' ', wc_rev=2),
'A/D/H/rho' : Item(status=' ', wc_rev=2),
})
svntest.actions.run_and_verify_status(wc_dir, expected_status)
# There was a bug whereby the failed switch left the wrong URL in
# the target directory H. Check for that.
expected_infos = [
{ 'URL' : '.*' + G_url + '$' },
]
svntest.actions.run_and_verify_info(expected_infos, H_path)
# Resolve tree conflict at psi.
svntest.actions.run_and_verify_resolved([psi_path])
# The switch should now be complete.
### Instead of "treeconflict=None" which means "don't check", we should
# check "treeconflict=' '" but the test suite doesn't do the right thing.
expected_status.tweak('A/D/H/psi', treeconflict=None)
svntest.actions.run_and_verify_status(wc_dir, expected_status)
#----------------------------------------------------------------------
# Issue #1826 - svn switch temporarily drops invalid URLs into the entries
# files (which become not-temporary if the switch fails).
def bad_intermediate_urls(sbox):
"bad intermediate urls in use"
sbox.build()
wc_dir = sbox.wc_dir
url = sbox.repo_url
A = sbox.ospath('A')
A_Z = sbox.ospath('A/Z')
url_A_C = url + '/A/C'
url_A_C_A = url + '/A/C/A'
url_A_C_A_Z = url + '/A/C/A/Z'
# We'll be switching our working copy to (a modified) A/C in the Greek tree.
# First, make an extra subdirectory in C to match one in the root, plus
# another one inside of that.
svntest.actions.run_and_verify_svn(['Committing transaction...\n',
'Committed revision 2.\n'], [],
'mkdir', '-m', 'log msg',
url_A_C_A, url_A_C_A_Z)
# Now, we'll drop a conflicting path under the root.
svntest.main.file_append(A_Z, 'Look, Mom, a ... tree conflict.')
#svntest.factory.make(sbox, """
# svn switch url/A/C wc_dir
# # svn info A
# # check that we can recover from the tree conflict
# rm A/Z
# svn up
# """)
#exit(0)
# svn switch url/A/C wc_dir
expected_output = svntest.wc.State(wc_dir, {
'A/mu' : Item(status='D '),
'A/Z' : Item(status=' ', treeconflict='C'),
'A/C' : Item(status='D '),
'A/B' : Item(status='D '),
'A/D' : Item(status='D '),
'iota' : Item(status='D '),
})
expected_disk = svntest.main.greek_state.copy()
expected_disk.remove('iota', 'A/B', 'A/B/E', 'A/B/E/beta', 'A/B/E/alpha',
'A/B/F', 'A/B/lambda', 'A/D', 'A/D/G', 'A/D/G/rho', 'A/D/G/pi',
'A/D/G/tau', 'A/D/H', 'A/D/H/psi', 'A/D/H/omega', 'A/D/H/chi',
'A/D/gamma', 'A/mu', 'A/C')
expected_disk.add({
'A/Z' : Item(contents="Look, Mom, a ... tree conflict."),
})
expected_status = actions.get_virginal_state(wc_dir, 2)
expected_status.remove('iota', 'A/B', 'A/B/E', 'A/B/E/beta', 'A/B/E/alpha',
'A/B/F', 'A/B/lambda', 'A/D', 'A/D/G', 'A/D/G/rho', 'A/D/G/pi',
'A/D/G/tau', 'A/D/H', 'A/D/H/psi', 'A/D/H/omega', 'A/D/H/chi',
'A/D/gamma', 'A/mu', 'A/C')
expected_status.add({
# Obstructed node is currently turned into a delete to allow resolving.
'A/Z' : Item(status='D ', treeconflict='C', wc_rev=2),
})
actions.run_and_verify_switch(wc_dir, wc_dir, url_A_C, expected_output,
expected_disk, expected_status,
[], False,
'--ignore-ancestry')
# However, the URL for wc/A should now reflect ^/A/C/A, not something else.
expected_infos = [
{ 'URL' : '.*/A/C/A$' },
]
svntest.actions.run_and_verify_info(expected_infos, A)
# check that we can recover from the tree conflict
# rm A/Z
os.remove(A_Z)
svntest.main.run_svn(None, 'revert', A_Z)
# svn up
expected_output = svntest.wc.State(wc_dir, {
})
expected_disk.tweak('A/Z', contents=None)
expected_status.tweak(status=' ', wc_rev='2')
expected_status.tweak('A/Z', treeconflict=None)
actions.run_and_verify_update(wc_dir, expected_output, expected_disk,
expected_status)
#----------------------------------------------------------------------
# Regression test for issue #1825: failed switch may corrupt
# working copy
@Issue(1825)
def obstructed_switch(sbox):
"obstructed switch"
#svntest.factory.make(sbox, """svn cp -m msgcopy url/A/B/E url/A/B/Esave
# svn rm A/B/E/alpha
# svn commit
# echo "hello" >> A/B/E/alpha
# svn switch url/A/B/Esave A/B/E
# svn status
# svn info A/B/E/alpha""")
sbox.build()
wc_dir = sbox.wc_dir
url = sbox.repo_url
A_B_E = sbox.ospath('A/B/E')
A_B_E_alpha = sbox.ospath('A/B/E/alpha')
url_A_B_E = url + '/A/B/E'
url_A_B_Esave = url + '/A/B/Esave'
# svn cp -m msgcopy url/A/B/E url/A/B/Esave
expected_stdout = [
'Committing transaction...\n',
'Committed revision 2.\n',
]
actions.run_and_verify_svn2(expected_stdout, [], 0, 'cp', '-m',
'msgcopy', url_A_B_E, url_A_B_Esave)
# svn rm A/B/E/alpha
expected_stdout = ['D ' + A_B_E_alpha + '\n']
actions.run_and_verify_svn2(expected_stdout, [], 0, 'rm',
A_B_E_alpha)
# svn commit
expected_output = svntest.wc.State(wc_dir, {
'A/B/E/alpha' : Item(verb='Deleting'),
})
expected_status = actions.get_virginal_state(wc_dir, 1)
expected_status.remove('A/B/E/alpha')
actions.run_and_verify_commit(wc_dir, expected_output, expected_status)
# echo "hello" >> A/B/E/alpha
main.file_append(A_B_E_alpha, 'hello')
# svn switch url/A/B/Esave A/B/E
expected_output = svntest.wc.State(wc_dir, {
'A/B/E/alpha' : Item(status=' ', treeconflict='C'),
})
expected_disk = svntest.main.greek_state.copy()
expected_disk.tweak('A/B/E/alpha', contents='hello')
expected_status.add({
'A/B/E/alpha' : Item(status='D ', treeconflict='C', wc_rev=3),
})
expected_status.tweak('A/B/E', wc_rev='3', switched='S')
expected_status.tweak('A/B/E/beta', wc_rev='3')
actions.run_and_verify_switch(wc_dir, A_B_E, url_A_B_Esave,
expected_output, expected_disk,
expected_status,
[], False, '--ignore-ancestry')
# svn status
expected_status.add({
'A/B/Esave' : Item(status=' '),
'A/B/Esave/beta' : Item(status=' '),
'A/B/Esave/alpha' : Item(status=' '),
})
actions.run_and_verify_unquiet_status(wc_dir, expected_status)
# svn info A/B/E/alpha
expected_stdout = verify.RegexOutput(
".*local file unversioned, incoming file add upon switch",
match_all=False)
actions.run_and_verify_svn2(expected_stdout, [], 0, 'info',
A_B_E_alpha)
#----------------------------------------------------------------------
# Issue 2353.
def commit_mods_below_switch(sbox):
"commit with mods below switch"
sbox.build()
wc_dir = sbox.wc_dir
C_path = sbox.ospath('A/C')
B_url = sbox.repo_url + '/A/B'
expected_output = svntest.wc.State(wc_dir, {
'A/C/E' : Item(status='A '),
'A/C/E/alpha' : Item(status='A '),
'A/C/E/beta' : Item(status='A '),
'A/C/F' : Item(status='A '),
'A/C/lambda' : Item(status='A '),
})
expected_disk = svntest.main.greek_state.copy()
expected_disk.add({
'A/C/E' : Item(),
'A/C/E/alpha' : Item(contents="This is the file 'alpha'.\n"),
'A/C/E/beta' : Item(contents="This is the file 'beta'.\n"),
'A/C/F' : Item(),
'A/C/lambda' : Item(contents="This is the file 'lambda'.\n"),
})
expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
expected_status.tweak('A/C', switched='S')
expected_status.add({
'A/C/E' : Item(status=' ', wc_rev=1),
'A/C/E/alpha' : Item(status=' ', wc_rev=1),
'A/C/E/beta' : Item(status=' ', wc_rev=1),
'A/C/F' : Item(status=' ', wc_rev=1),
'A/C/lambda' : Item(status=' ', wc_rev=1),
})
svntest.actions.run_and_verify_switch(wc_dir, C_path, B_url,
expected_output,
expected_disk,
expected_status,
[],
False, '--ignore-ancestry')
D_path = sbox.ospath('A/D')
svntest.actions.run_and_verify_svn(None, [],
'propset', 'x', 'x', C_path, D_path)
expected_status.tweak('A/C', 'A/D', status=' M')
svntest.actions.run_and_verify_status(wc_dir, expected_status)
expected_output = svntest.wc.State(wc_dir, {
'A/C' : Item(verb='Sending'),
'A/D' : Item(verb='Sending'),
})
expected_status.tweak('A/C', 'A/D', status=' ', wc_rev=2)
# A/C erroneously classified as a wc root caused the commit to fail
# with "'A/C/E' is missing or not locked"
svntest.actions.run_and_verify_commit(wc_dir,
expected_output, expected_status,
[], C_path, D_path)
#----------------------------------------------------------------------
# Issue 2306.
def refresh_read_only_attribute(sbox):
"refresh the WC file system read-only attribute "
# This test will fail when run as root. Since that's normal
# behavior, just skip the test.
if os.name == 'posix':
if os.geteuid() == 0:
raise svntest.Skip('Test doesn\'t work as uid 0')
sbox.build()
wc_dir = sbox.wc_dir
# Create a branch.
url = sbox.repo_url + '/A'
branch_url = sbox.repo_url + '/A-branch'
svntest.actions.run_and_verify_svn(['Committing transaction...\n',
'Committed revision 2.\n'], [],
'cp', '-m', 'svn:needs-lock not set',
url, branch_url)
# Set the svn:needs-lock property on a file from the "trunk".
A_path = sbox.ospath('A')
mu_path = os.path.join(A_path, 'mu')
svntest.actions.run_and_verify_svn(None, [],
'ps', 'svn:needs-lock', '1', mu_path)
# Commit the propset of svn:needs-lock.
expected_output = svntest.wc.State(wc_dir, {
'A/mu' : Item(verb='Sending'),
})
expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
expected_status.tweak('A/mu', wc_rev=3)
svntest.actions.run_and_verify_commit(wc_dir,
expected_output, expected_status,
[], mu_path)
# The file on which svn:needs-lock was set is now expected to be read-only.
if os.access(mu_path, os.W_OK):
raise svntest.Failure("'%s' expected to be read-only after having had "
"its svn:needs-lock property set" % mu_path)
# Switch to the branch with the WC state from before the propset of
# svn:needs-lock.
expected_output = svntest.wc.State(wc_dir, {
'A/mu' : Item(status=' U'),
})
expected_disk = svntest.main.greek_state.copy()
expected_status = svntest.actions.get_virginal_state(wc_dir, 3)
expected_status.tweak('', wc_rev=1)
expected_status.tweak('iota', wc_rev=1)
expected_status.tweak('A', switched='S')
svntest.actions.run_and_verify_switch(wc_dir, A_path, branch_url,
expected_output,
expected_disk,
expected_status,
[],
False, '--ignore-ancestry')
# The file with we set svn:needs-lock on should now be writable, but
# is still read-only!
if not os.access(mu_path, os.W_OK):
raise svntest.Failure("'%s' expected to be writable after being switched "
"to a branch on which its svn:needs-lock property "
"is not set" % mu_path)
# Check that switch can't change the repository root.
def switch_change_repos_root(sbox):
"switch shouldn't allow changing repos root"
sbox.build()
wc_dir = sbox.wc_dir
repo_url = sbox.repo_url
other_repo_url = repo_url
# Strip trailing slashes and add something bogus to that other URL.
while other_repo_url[-1] == '/':
other_repos_url = other_repo_url[:-1]
other_repo_url = other_repo_url + "_bogus"
other_A_url = other_repo_url + "/A"
A_wc_dir = sbox.ospath('A')
# Test 1: A switch that changes to a non-existing repo shouldn't work.
expected_err = ".*Unable to open repository.*|.*Could not open.*|"\
".*Could not find.*|.*No repository found.*"
svntest.actions.run_and_verify_svn(None,
expected_err,
'switch', '--ignore-ancestry',
other_A_url, A_wc_dir)
# Test 2: A switch that changes the repo root part of the URL shouldn't work.
other_repo_dir, other_repo_url = sbox.add_repo_path('other')
other_A_url = other_repo_url + "/A"
svntest.main.create_repos(other_repo_dir)
svntest.actions.run_and_verify_svn(None,
".*UUID.*",
'switch', '--ignore-ancestry',
other_A_url, A_wc_dir)
# Make sure we didn't break the WC.
expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
svntest.actions.run_and_verify_status(wc_dir, expected_status)
#----------------------------------------------------------------------
def forced_switch(sbox):
"forced switch tolerates obstructions to adds"
sbox.build(read_only = True)
# Dir obstruction
G_path = sbox.ospath('A/B/F/G')
os.mkdir(G_path)
# Faux file obstructions
shutil.copyfile(sbox.ospath('A/D/gamma'),
sbox.ospath('A/B/F/gamma'))
shutil.copyfile(sbox.ospath('A/D/G/tau'),
sbox.ospath('A/B/F/G/tau'))
# Real file obstruction
pi_path = sbox.ospath('A/B/F/G/pi')
svntest.main.file_write(pi_path,
"This is the OBSTRUCTING file 'pi'.\n")
# Non-obstructing dir and file
I_path = sbox.ospath('A/B/F/I')
os.mkdir(I_path)
upsilon_path = os.path.join(G_path, 'upsilon')
svntest.main.file_write(upsilon_path,
"This is the unversioned file 'upsilon'.\n")
# Setup expected results of switch.
expected_output = svntest.wc.State(sbox.wc_dir, {
"A/B/F/gamma" : Item(status='E '),
"A/B/F/G" : Item(status='E '),
"A/B/F/G/pi" : Item(status='E '),
"A/B/F/G/rho" : Item(status='A '),
"A/B/F/G/tau" : Item(status='E '),
"A/B/F/H" : Item(status='A '),
"A/B/F/H/chi" : Item(status='A '),
"A/B/F/H/omega" : Item(status='A '),
"A/B/F/H/psi" : Item(status='A '),
})
expected_disk = svntest.main.greek_state.copy()
expected_disk.add({
"A/B/F/gamma" : Item("This is the file 'gamma'.\n"),
"A/B/F/G" : Item(),
"A/B/F/G/pi" : Item("This is the OBSTRUCTING file 'pi'.\n"),
"A/B/F/G/rho" : Item("This is the file 'rho'.\n"),
"A/B/F/G/tau" : Item("This is the file 'tau'.\n"),
"A/B/F/G/upsilon" : Item("This is the unversioned file 'upsilon'.\n"),
"A/B/F/H" : Item(),
"A/B/F/H/chi" : Item("This is the file 'chi'.\n"),
"A/B/F/H/omega" : Item("This is the file 'omega'.\n"),
"A/B/F/H/psi" : Item("This is the file 'psi'.\n"),
"A/B/F/I" : Item(),
})
expected_status = svntest.actions.get_virginal_state(sbox.wc_dir, 1)
expected_status.tweak('A/B/F', switched='S')
expected_status.add({
"A/B/F/gamma" : Item(status=' ', wc_rev=1),
"A/B/F/G" : Item(status=' ', wc_rev=1),
"A/B/F/G/pi" : Item(status='M ', wc_rev=1),
"A/B/F/G/rho" : Item(status=' ', wc_rev=1),
"A/B/F/G/tau" : Item(status=' ', wc_rev=1),
"A/B/F/H" : Item(status=' ', wc_rev=1),
"A/B/F/H/chi" : Item(status=' ', wc_rev=1),
"A/B/F/H/omega" : Item(status=' ', wc_rev=1),
"A/B/F/H/psi" : Item(status=' ', wc_rev=1),
})
# Do the switch and check the results in three ways.
F_path = sbox.ospath('A/B/F')
AD_url = sbox.repo_url + '/A/D'
svntest.actions.run_and_verify_switch(sbox.wc_dir, F_path, AD_url,
expected_output,
expected_disk,
expected_status, [], False,
'--force', '--ignore-ancestry')
#----------------------------------------------------------------------
def forced_switch_failures(sbox):
"forced switch detects tree conflicts"
# svntest.factory.make(sbox,
# """
# # Add a directory to obstruct a file.
# mkdir A/B/F/pi
#
# # Add a file to obstruct a directory.
# echo "The file 'H'" > A/C/H
#
# # Test three cases where forced switch should cause a tree conflict
#
# # 1) A forced switch that tries to add a file when an unversioned
# # directory of the same name already exists. (Currently fails)
# svn switch --force url/A/D A/C
#
# # 2) A forced switch that tries to add a dir when a file of the same
# # name already exists. (Tree conflict)
# svn switch --force url/A/D/G A/B/F
# svn info A/B/F/pi
#
# # 3) A forced update that tries to add a directory when a versioned
# # directory of the same name already exists.
#
# # Make dir A/D/H/I in repos.
# svn mkdir -m "Log message" url/A/D/H/I
#
# # Make A/D/G/I and co A/D/H/I into it.
# mkdir A/D/G/I
# svn co url/A/D/H/I A/D/G/I
#
# # Try the forced switch. A/D/G/I obstructs the dir A/D/G/I coming
# # from the repos, causing an error.
# svn switch --force url/A/D/H A/D/G
#
# # Delete all three obstructions and finish the update.
# rm -rf A/D/G/I
# rm A/B/F/pi
# rm A/C/H
#
# # A/B/F is switched to A/D/G
# # A/C is switched to A/D
# # A/D/G is switched to A/D/H
# svn up
# """)
# exit(0)
sbox.build()
wc_dir = sbox.wc_dir
url = sbox.repo_url
A_B_F = sbox.ospath('A/B/F')
A_B_F_pi = sbox.ospath('A/B/F/pi')
A_C = sbox.ospath('A/C')
A_C_H = sbox.ospath('A/C/H')
A_D_G = sbox.ospath('A/D/G')
A_D_G_I = sbox.ospath('A/D/G/I')
url_A_D = url + '/A/D'
url_A_D_G = url + '/A/D/G'
url_A_D_H = url + '/A/D/H'
url_A_D_H_I = url + '/A/D/H/I'
# Add a directory to obstruct a file.
# mkdir A/B/F/pi
os.makedirs(A_B_F_pi)
# Add a file to obstruct a directory.
# echo "The file 'H'" > A/C/H
main.file_write(A_C_H, "The file 'H'\n")
# Test three cases where forced switch should cause a tree conflict
# 1) A forced switch that tries to add a directory when an unversioned
# file of the same name already exists. (Currently fails)
# svn switch --force url/A/D A/C
expected_output = svntest.wc.State(wc_dir, {
'A/C/G' : Item(status='A '),
'A/C/G/pi' : Item(status='A '),
'A/C/G/rho' : Item(status='A '),
'A/C/G/tau' : Item(status='A '),
'A/C/gamma' : Item(status='A '),
'A/C/H' : Item(status=' ', treeconflict='C'),
'A/C/H/psi' : Item(status=' ', treeconflict='A'),
'A/C/H/omega' : Item(status=' ', treeconflict='A'),
'A/C/H/chi' : Item(status=' ', treeconflict='A'),
})
expected_disk = svntest.main.greek_state.copy()
expected_disk.add({
'A/C/gamma' : Item(contents="This is the file 'gamma'.\n"),
'A/C/G' : Item(),
'A/C/G/pi' : Item(contents="This is the file 'pi'.\n"),
'A/C/G/rho' : Item(contents="This is the file 'rho'.\n"),
'A/C/G/tau' : Item(contents="This is the file 'tau'.\n"),
'A/C/H' : Item(contents="The file 'H'\n"),
'A/B/F/pi' : Item(),
})
expected_status = actions.get_virginal_state(wc_dir, 1)
expected_status.add({
'A/C/G' : Item(status=' ', wc_rev='1'),
'A/C/G/rho' : Item(status=' ', wc_rev='1'),
'A/C/G/tau' : Item(status=' ', wc_rev='1'),
'A/C/G/pi' : Item(status=' ', wc_rev='1'),
'A/C/H' : Item(status='D ', treeconflict='C', wc_rev='1'),
'A/C/H/psi' : Item(status='D ', wc_rev='1'),
'A/C/H/omega' : Item(status='D ', wc_rev='1'),
'A/C/H/chi' : Item(status='D ', wc_rev='1'),
'A/C/gamma' : Item(status=' ', wc_rev='1'),
})
expected_status.tweak('A/C', switched='S')
actions.run_and_verify_switch(wc_dir, A_C, url_A_D, expected_output,
expected_disk, expected_status, [], False,
'--force',
'--ignore-ancestry')
# 2) A forced switch that tries to add a file when a dir of the same
# name already exists. (Tree conflict)
# svn switch --force url/A/D/G A/B/F
expected_output = svntest.wc.State(wc_dir, {
'A/B/F/rho' : Item(status='A '),
'A/B/F/pi' : Item(status=' ', treeconflict='C'),
'A/B/F/tau' : Item(status='A '),
})
expected_disk.add({
'A/B/F/rho' : Item(contents="This is the file 'rho'.\n"),
'A/B/F/tau' : Item(contents="This is the file 'tau'.\n"),
})
expected_status.add({
'A/B/F/tau' : Item(status=' ', wc_rev='1'),
'A/B/F/pi' : Item(status='D ', treeconflict='C', wc_rev='1'),
'A/B/F/rho' : Item(status=' ', wc_rev='1'),
})
expected_status.tweak('A/B/F', switched='S')
actions.run_and_verify_switch(wc_dir, A_B_F, url_A_D_G, expected_output,
expected_disk, expected_status, [], False,
'--force',
'--ignore-ancestry')
# svn info A/B/F/pi
expected_stdout = verify.ExpectedOutput(
'Tree conflict: local dir unversioned, incoming file add upon switch\n',
match_all=False)
actions.run_and_verify_svn2(expected_stdout, [], 0, 'info',
A_B_F_pi)
# 3) A forced update that tries to add a directory when a versioned
# directory of the same name already exists.
# Make dir A/D/H/I in repos.
# svn mkdir -m "Log message" url/A/D/H/I
expected_stdout = verify.UnorderedOutput([
'Committing transaction...\n',
'Committed revision 2.\n',
])
actions.run_and_verify_svn2(expected_stdout, [], 0, 'mkdir',
'-m', 'Log message', url_A_D_H_I)
# Make A/D/G/I and co A/D/H/I into it.
# mkdir A/D/G/I
os.makedirs(A_D_G_I)
# svn co url/A/D/H/I A/D/G/I
expected_output = svntest.wc.State(wc_dir, {})
expected_disk.add({
'A/D/G/I' : Item(),
})
exit_code, so, se = svntest.actions.run_and_verify_svn(
['Checked out revision 2.\n'], [],
"co", url_A_D_H_I, A_D_G_I)
# Try the forced switch. A/D/G/I obstructs the dir A/D/G/I coming
# from the repos, causing an error.
# svn switch --force url/A/D/H A/D/G
expected_output = svntest.wc.State(wc_dir, {
'A/D/G/chi' : Item(status='A '),
'A/D/G/tau' : Item(status='D '),
'A/D/G/omega' : Item(status='A '),
'A/D/G/psi' : Item(status='A '),
'A/D/G/I' : Item(verb='Skipped'),
'A/D/G/rho' : Item(status='D '),
'A/D/G/pi' : Item(status='D '),
})
actions.run_and_verify_switch(wc_dir, A_D_G, url_A_D_H, expected_output,
None, None, [], False,
'--force', '--ignore-ancestry')
# Delete all three obstructions and finish the update.
# rm -rf A/D/G/I
main.safe_rmtree(A_D_G_I)
# rm A/B/F/pi
main.safe_rmtree(A_B_F_pi)
# rm A/C/H
os.remove(A_C_H)
# Resolve the tree conflict on A_C_H and A_B_F_pi
svntest.main.run_svn(None, 'resolved', A_C_H)
svntest.main.run_svn(None, 'revert', A_B_F_pi)
# A/B/F is switched to A/D/G
# A/C is switched to A/D
# A/D/G is switched to A/D/H
# svn up
expected_output = svntest.wc.State(wc_dir, {
'A/C/H/I' : Item(status='A '),
'A/D/G/I' : Item(status='A '),
'A/D/H/I' : Item(status='A '),
})
expected_disk.remove('A/D/G/tau', 'A/D/G/rho', 'A/D/G/pi')
expected_disk.add({
'A/D/H/I' : Item(),
'A/D/G/omega' : Item(contents="This is the file 'omega'.\n"),
'A/D/G/psi' : Item(contents="This is the file 'psi'.\n"),
'A/D/G/chi' : Item(contents="This is the file 'chi'.\n"),
'A/C/H/I' : Item(),
'A/C/H/omega' : Item(contents="This is the file 'omega'.\n"),
'A/C/H/psi' : Item(contents="This is the file 'psi'.\n"),
'A/C/H/chi' : Item(contents="This is the file 'chi'.\n"),
})
expected_disk.tweak('A/C/H', contents=None)
expected_disk.tweak('A/B/F/pi', contents="This is the file 'pi'.\n")
expected_status.remove('A/D/G/tau', 'A/D/G/rho', 'A/D/G/pi')
expected_status.add({
'A/D/G/omega' : Item(status=' ', wc_rev='2'),
'A/D/G/I' : Item(status=' ', wc_rev='2'),
'A/D/G/psi' : Item(status=' ', wc_rev='2'),
'A/D/G/chi' : Item(status=' ', wc_rev='2'),
'A/D/H/I' : Item(status=' ', wc_rev='2'),
'A/C/H/psi' : Item(status=' ', wc_rev='2'),
'A/C/H/omega' : Item(status=' ', wc_rev='2'),
'A/C/H/chi' : Item(status=' ', wc_rev='2'),
'A/C/H/I' : Item(status=' ', wc_rev='2'),
})
expected_status.tweak(wc_rev='2', status=' ')
expected_status.tweak('A/B/F/pi', 'A/C/H', treeconflict=None)
expected_status.tweak('A/D/G', switched='S')
svntest.main.run_svn(None, 'revert', '-R', sbox.ospath('A/C/H'))
actions.run_and_verify_update(wc_dir, expected_output, expected_disk,
expected_status)
def switch_with_obstructing_local_adds(sbox):
"switch tolerates WC adds"
sbox.build(read_only = True)
# Dir obstruction scheduled for addition without history.
G_path = sbox.ospath('A/B/F/G')
os.mkdir(G_path)
# File obstructions scheduled for addition without history.
# Contents identical to additions from switch.
gamma_copy_path = sbox.ospath('A/B/F/gamma')
shutil.copyfile(sbox.ospath('A/D/gamma'),
gamma_copy_path)
shutil.copyfile(sbox.ospath('A/D/G/tau'),
sbox.ospath('A/B/F/G/tau'))
# File obstruction scheduled for addition without history.
# Contents conflict with addition from switch.
pi_path = sbox.ospath('A/B/F/G/pi')
svntest.main.file_write(pi_path,
"This is the OBSTRUCTING file 'pi'.\n")
# Non-obstructing dir and file scheduled for addition without history.
I_path = sbox.ospath('A/B/F/I')
os.mkdir(I_path)
upsilon_path = os.path.join(G_path, 'upsilon')
svntest.main.file_write(upsilon_path,
"This is the unversioned file 'upsilon'.\n")
# Add the above obstructions.
svntest.actions.run_and_verify_svn(None, [],
'add', G_path, I_path,
gamma_copy_path)
# Setup expected results of switch.
expected_output = svntest.wc.State(sbox.wc_dir, {
"A/B/F/gamma" : Item(status=' ', treeconflict='C'),
"A/B/F/G" : Item(status=' ', treeconflict='C'),
'A/B/F/G/tau' : Item(status=' ', treeconflict='A'),
'A/B/F/G/rho' : Item(status=' ', treeconflict='A'),
'A/B/F/G/pi' : Item(status=' ', treeconflict='A'),
"A/B/F/H" : Item(status='A '),
"A/B/F/H/chi" : Item(status='A '),
"A/B/F/H/omega" : Item(status='A '),
"A/B/F/H/psi" : Item(status='A '),
})
expected_disk = svntest.main.greek_state.copy()
expected_disk.add({
"A/B/F/gamma" : Item("This is the file 'gamma'.\n"),
"A/B/F/G" : Item(),
"A/B/F/G/pi" : Item("This is the OBSTRUCTING file 'pi'.\n"),
"A/B/F/G/tau" : Item("This is the file 'tau'.\n"),
"A/B/F/G/upsilon" : Item("This is the unversioned file 'upsilon'.\n"),
"A/B/F/H" : Item(),
"A/B/F/H/chi" : Item("This is the file 'chi'.\n"),
"A/B/F/H/omega" : Item("This is the file 'omega'.\n"),
"A/B/F/H/psi" : Item("This is the file 'psi'.\n"),
"A/B/F/I" : Item(),
})
expected_status = svntest.actions.get_virginal_state(sbox.wc_dir, 1)
expected_status.tweak('A/B/F', switched='S')
expected_status.add({
'A/B/F/gamma' : Item(status='R ', treeconflict='C', wc_rev='1'),
'A/B/F/G' : Item(status='R ', treeconflict='C', wc_rev='1'),
'A/B/F/G/pi' : Item(status='A ', wc_rev='-', entry_status='R ', entry_rev='1'),
'A/B/F/G/tau' : Item(status='A ', wc_rev='-', entry_status='R ', entry_rev='1'),
'A/B/F/G/upsilon' : Item(status='A ', wc_rev='-', entry_rev='0'),
'A/B/F/G/rho' : Item(status='D ', wc_rev='1'),
'A/B/F/H' : Item(status=' ', wc_rev='1'),
'A/B/F/H/chi' : Item(status=' ', wc_rev='1'),
'A/B/F/H/omega' : Item(status=' ', wc_rev='1'),
'A/B/F/H/psi' : Item(status=' ', wc_rev='1'),
'A/B/F/I' : Item(status='A ', wc_rev='-', entry_rev='0'),
})
# Do the switch and check the results in three ways.
F_path = sbox.ospath('A/B/F')
D_url = sbox.repo_url + '/A/D'
svntest.actions.run_and_verify_switch(sbox.wc_dir, F_path, D_url,
expected_output,
expected_disk,
expected_status,
[], False,
'--ignore-ancestry')
#----------------------------------------------------------------------
def switch_scheduled_add(sbox):
"switch a scheduled-add file"
sbox.build(read_only = True)
wc_dir = sbox.wc_dir
file_path = sbox.ospath('stub_file')
switch_url = sbox.repo_url + '/iota'
nodo_path = sbox.ospath('nodo')
svntest.main.file_append(file_path, "")
svntest.actions.run_and_verify_svn(None, [],
'add', file_path)
svntest.actions.run_and_verify_svn(None,
"svn: E200007: Cannot switch '.*file' " +
"because it is not in the repository yet",
'switch', '--ignore-ancestry',
switch_url, file_path)
svntest.actions.run_and_verify_svn(None,
"svn: E155010: The node '.*nodo' was not",
'switch', '--ignore-ancestry',
switch_url, nodo_path)
#----------------------------------------------------------------------
@SkipUnless(server_has_mergeinfo)
def mergeinfo_switch_elision(sbox):
"mergeinfo does not elide post switch"
# When a switch adds mergeinfo on a path which is identical to
# the mergeinfo on one of the path's subtrees, the subtree's mergeinfo
# should *not* elide! If it did this could result in the switch of a
# pristine tree producing local mods.
sbox.build()
wc_dir = sbox.wc_dir
# Some paths we'll care about
lambda_path = sbox.ospath('A/B_COPY_1/lambda')
B_COPY_1_path = sbox.ospath('A/B_COPY_1')
B_COPY_2_path = sbox.ospath('A/B_COPY_2')
E_COPY_2_path = sbox.ospath('A/B_COPY_2/E')
alpha_path = sbox.ospath('A/B/E/alpha')
beta_path = sbox.ospath('A/B/E/beta')
# Make branches A/B_COPY_1 and A/B_COPY_2
expected_stdout = verify.UnorderedOutput([
"A " + B_COPY_1_path + "\n",
"A " + sbox.ospath('A/B_COPY_1/lambda') + "\n",
"A " + sbox.ospath('A/B_COPY_1/E') + "\n",
"A " + sbox.ospath('A/B_COPY_1/E/alpha') + "\n",
"A " + sbox.ospath('A/B_COPY_1/E/beta') + "\n",
"A " + sbox.ospath('A/B_COPY_1/F') + "\n",
])
svntest.actions.run_and_verify_svn(expected_stdout, [], 'copy',
sbox.repo_url + "/A/B", B_COPY_1_path)
expected_stdout = verify.UnorderedOutput([
"A " + B_COPY_2_path + "\n",
"A " + sbox.ospath('A/B_COPY_2/lambda') + "\n",
"A " + sbox.ospath('A/B_COPY_2/E') + "\n",
"A " + sbox.ospath('A/B_COPY_2/E/alpha') + "\n",
"A " + sbox.ospath('A/B_COPY_2/E/beta') + "\n",
"A " + sbox.ospath('A/B_COPY_2/F') + "\n",
])
svntest.actions.run_and_verify_svn(expected_stdout, [], 'copy',
sbox.repo_url + "/A/B", B_COPY_2_path)
expected_output = svntest.wc.State(wc_dir, {
'A/B_COPY_1' : Item(verb='Adding'),
'A/B_COPY_2' : Item(verb='Adding')
})
expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
expected_status.add({
"A/B_COPY_1" : Item(status=' ', wc_rev=2),
"A/B_COPY_1/lambda" : Item(status=' ', wc_rev=2),
"A/B_COPY_1/E" : Item(status=' ', wc_rev=2),
"A/B_COPY_1/E/alpha" : Item(status=' ', wc_rev=2),
"A/B_COPY_1/E/beta" : Item(status=' ', wc_rev=2),
"A/B_COPY_1/F" : Item(status=' ', wc_rev=2),
"A/B_COPY_2" : Item(status=' ', wc_rev=2),
"A/B_COPY_2/lambda" : Item(status=' ', wc_rev=2),
"A/B_COPY_2/E" : Item(status=' ', wc_rev=2),
"A/B_COPY_2/E/alpha" : Item(status=' ', wc_rev=2),
"A/B_COPY_2/E/beta" : Item(status=' ', wc_rev=2),
"A/B_COPY_2/F" : Item(status=' ', wc_rev=2),
})
svntest.actions.run_and_verify_commit(wc_dir,
expected_output,
expected_status)
# Make some changes under A/B
# r3 - modify and commit A/B/E/beta
svntest.main.file_write(beta_path, "New content")
expected_output = svntest.wc.State(wc_dir,
{'A/B/E/beta' : Item(verb='Sending')})
expected_status.tweak('A/B/E/beta', wc_rev=3)
svntest.actions.run_and_verify_commit(wc_dir, expected_output,
expected_status)
# r4 - modify and commit A/B/E/alpha
svntest.main.file_write(alpha_path, "New content")
expected_output = svntest.wc.State(wc_dir,
{'A/B/E/alpha' : Item(verb='Sending')})
expected_status.tweak('A/B/E/alpha', wc_rev=4)
svntest.actions.run_and_verify_commit(wc_dir, expected_output,
expected_status)
# Merge r2:4 into A/B_COPY_1
expected_output = svntest.wc.State(B_COPY_1_path, {
'E/alpha' : Item(status='U '),
'E/beta' : Item(status='U '),
})
expected_mergeinfo_output = svntest.wc.State(B_COPY_1_path, {
'' : Item(status=' U'),
})
expected_elision_output = svntest.wc.State(B_COPY_1_path, {
})
expected_merge_status = svntest.wc.State(B_COPY_1_path, {
'' : Item(status=' M', wc_rev=2),
'lambda' : Item(status=' ', wc_rev=2),
'E' : Item(status=' ', wc_rev=2),
'E/alpha' : Item(status='M ', wc_rev=2),
'E/beta' : Item(status='M ', wc_rev=2),
'F' : Item(status=' ', wc_rev=2),
})
expected_merge_disk = svntest.wc.State('', {
'' : Item(props={SVN_PROP_MERGEINFO : '/A/B:3-4'}),
'lambda' : Item("This is the file 'lambda'.\n"),
'E' : Item(),
'E/alpha' : Item("New content"),
'E/beta' : Item("New content"),
'F' : Item(),
})
expected_skip = svntest.wc.State(B_COPY_1_path, { })
svntest.actions.run_and_verify_merge(B_COPY_1_path, '2', '4',
sbox.repo_url + '/A/B', None,
expected_output,
expected_mergeinfo_output,
expected_elision_output,
expected_merge_disk,
expected_merge_status,
expected_skip,
check_props=True)
# r5 - Commit the merge into A/B_COPY_1/E
expected_output = svntest.wc.State(
wc_dir,
{'A/B_COPY_1' : Item(verb='Sending'),
'A/B_COPY_1/E/alpha' : Item(verb='Sending'),
'A/B_COPY_1/E/beta' : Item(verb='Sending'),
})
expected_status.tweak('A/B_COPY_1', wc_rev=5)
expected_status.tweak('A/B_COPY_1/E/alpha', wc_rev=5)
expected_status.tweak('A/B_COPY_1/E/beta', wc_rev=5)
expected_status.tweak('A/B_COPY_1/lambda', wc_rev=2)
svntest.actions.run_and_verify_commit(wc_dir, expected_output,
expected_status)
# Merge r2:4 into A/B_COPY_2/E
expected_output = svntest.wc.State(E_COPY_2_path, {
'alpha' : Item(status='U '),
'beta' : Item(status='U '),
})
expected_mergeinfo_output = svntest.wc.State(E_COPY_2_path, {
'' : Item(status=' U'),
})
expected_elision_output = svntest.wc.State(E_COPY_2_path, {
})
expected_merge_status = svntest.wc.State(E_COPY_2_path, {
'' : Item(status=' M', wc_rev=2),
'alpha' : Item(status='M ', wc_rev=2),
'beta' : Item(status='M ', wc_rev=2),
})
expected_merge_disk = svntest.wc.State('', {
'' : Item(props={SVN_PROP_MERGEINFO : '/A/B/E:3-4'}),
'alpha' : Item("New content"),
'beta' : Item("New content"),
})
expected_skip = svntest.wc.State(E_COPY_2_path, { })
svntest.actions.run_and_verify_merge(E_COPY_2_path, '2', '4',
sbox.repo_url + '/A/B/E', None,
expected_output,
expected_mergeinfo_output,
expected_elision_output,
expected_merge_disk,
expected_merge_status,
expected_skip,
check_props=True)
# Switch A/B_COPY_2 to URL of A/B_COPY_1. The local mergeinfo for r1,3-4
# on A/B_COPY_2/E is identical to the mergeinfo added to A/B_COPY_2 as a
# result of the switch, but we leave the former in place.
# Setup expected results of switch.
expected_output = svntest.wc.State(sbox.wc_dir, {
"A/B_COPY_2" : Item(status=' U'),
"A/B_COPY_2/E/alpha" : Item(status='G '),
"A/B_COPY_2/E/beta" : Item(status='G '),
})
expected_disk = svntest.main.greek_state.copy()
expected_disk.tweak("A/B/E/alpha", contents="New content")
expected_disk.tweak("A/B/E/beta", contents="New content")
expected_disk.add({
"A/B_COPY_1" : Item(props={SVN_PROP_MERGEINFO : '/A/B:3-4'}),
"A/B_COPY_1/E" : Item(),
"A/B_COPY_1/F" : Item(),
"A/B_COPY_1/lambda" : Item("This is the file 'lambda'.\n"),
"A/B_COPY_1/E/alpha" : Item("New content"),
"A/B_COPY_1/E/beta" : Item("New content"),
"A/B_COPY_2" : Item(props={SVN_PROP_MERGEINFO : '/A/B:3-4'}),
"A/B_COPY_2/E" : Item(props={SVN_PROP_MERGEINFO : '/A/B/E:3-4'}),
"A/B_COPY_2/F" : Item(),
"A/B_COPY_2/lambda" : Item("This is the file 'lambda'.\n"),
"A/B_COPY_2/E/alpha" : Item("New content"),
"A/B_COPY_2/E/beta" : Item("New content"),
})
expected_status = svntest.actions.get_virginal_state(sbox.wc_dir, 1)
expected_status.tweak("A/B/E/beta", wc_rev=3)
expected_status.tweak("A/B/E/alpha", wc_rev=4)
expected_status.add({
"A/B_COPY_1" : Item(status=' ', wc_rev=5),
"A/B_COPY_1/E" : Item(status=' ', wc_rev=2),
"A/B_COPY_1/F" : Item(status=' ', wc_rev=2),
"A/B_COPY_1/lambda" : Item(status=' ', wc_rev=2),
"A/B_COPY_1/E/alpha" : Item(status=' ', wc_rev=5),
"A/B_COPY_1/E/beta" : Item(status=' ', wc_rev=5),
"A/B_COPY_2" : Item(status=' ', wc_rev=5, switched='S'),
"A/B_COPY_2/E" : Item(status=' M', wc_rev=5),
"A/B_COPY_2/F" : Item(status=' ', wc_rev=5),
"A/B_COPY_2/lambda" : Item(status=' ', wc_rev=5),
"A/B_COPY_2/E/alpha" : Item(status=' ', wc_rev=5),
"A/B_COPY_2/E/beta" : Item(status=' ', wc_rev=5),
})
svntest.actions.run_and_verify_switch(sbox.wc_dir,
B_COPY_2_path,
sbox.repo_url + "/A/B_COPY_1",
expected_output,
expected_disk,
expected_status,
[], True,
'--ignore-ancestry')
# Now check a switch which reverses and earlier switch and leaves
# a path in an unswitched state.
#
# Switch A/B_COPY_1/lambda to iota. Use propset to give A/B_COPY/lambda
# the mergeinfo '/A/B/lambda:1,3-4'. Then switch A/B_COPY_1/lambda back
# to A/B_COPY_1/lambda. The local mergeinfo for r1,3-4 should remain on
# A/B_COPY_1/lambda.
expected_output = svntest.wc.State(sbox.wc_dir, {
"A/B_COPY_1/lambda" : Item(status='U '),
})
expected_disk.tweak("A/B_COPY_1/lambda",
contents="This is the file 'iota'.\n")
expected_status.tweak("A/B_COPY_1/lambda", wc_rev=5, switched='S')
svntest.actions.run_and_verify_switch(sbox.wc_dir,
lambda_path,
sbox.repo_url + "/iota",
expected_output,
expected_disk,
expected_status,
[], True,
'--ignore-ancestry')
svntest.actions.run_and_verify_svn(["property '" + SVN_PROP_MERGEINFO +
"' set on '" + lambda_path + "'" +
"\n"], [], 'ps', SVN_PROP_MERGEINFO,
'/A/B/lambda:3-4', lambda_path)
expected_output = svntest.wc.State(sbox.wc_dir, {
"A/B_COPY_1/lambda" : Item(status='U '),
})
expected_disk.tweak("A/B_COPY_1/lambda",
contents="This is the file 'lambda'.\n",
props={SVN_PROP_MERGEINFO : '/A/B/lambda:3-4'})
expected_status.tweak("A/B_COPY_1/lambda", switched=None, status=' M')
svntest.actions.run_and_verify_switch(sbox.wc_dir,
lambda_path,
sbox.repo_url + "/A/B_COPY_1/lambda",
expected_output,
expected_disk,
expected_status,
[], True,
'--ignore-ancestry')
#----------------------------------------------------------------------
def switch_with_depth(sbox):
"basic tests to verify switch along with depth"
sbox.build(read_only = True)
# Form some paths and URLs required
wc_dir = sbox.wc_dir
repo_url = sbox.repo_url
AD_url = repo_url + '/A/D'
AB_url = repo_url + '/A/B'
AB_path = sbox.ospath('A/B')
# Set up expected results of 'switch --depth=empty'
expected_output = svntest.wc.State(wc_dir, {})
expected_disk = svntest.main.greek_state.copy()
expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
expected_status.tweak('A/B', switched='S')
expected_status.tweak('A/B/lambda', switched='S')
expected_status.tweak('A/B/E', switched='S')
expected_status.tweak('A/B/F', switched='S')
# Do 'switch --depth=empty' and check the results in three ways.
svntest.actions.run_and_verify_switch(wc_dir, AB_path, AD_url,
expected_output,
expected_disk,
expected_status,
[], False,
'--depth', 'empty', '--ignore-ancestry')
# Set up expected results for reverting 'switch --depth=empty'
expected_output = svntest.wc.State(wc_dir, {})
expected_disk = svntest.main.greek_state.copy()
expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
svntest.actions.run_and_verify_switch(wc_dir, AB_path, AB_url,
expected_output,
expected_disk,
expected_status,
[], False,
'--depth', 'empty', '--ignore-ancestry')
# Set up expected results of 'switch --depth=files'
expected_output = svntest.wc.State(wc_dir, {
'A/B/lambda' : Item(status='D '),
'A/B/gamma' : Item(status='A '),
})
expected_disk = svntest.main.greek_state.copy()
expected_disk.remove('A/B/lambda')
expected_disk.add({
'A/B/gamma' : Item("This is the file 'gamma'.\n")
})
expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
expected_status.remove('A/B/lambda')
expected_status.add({
'A/B/gamma' : Item(status=' ', wc_rev=1)
})
expected_status.tweak('A/B', switched='S')
expected_status.tweak('A/B/E', switched='S')
expected_status.tweak('A/B/F', switched='S')
# Do 'switch --depth=files' and check the results in three ways.
svntest.actions.run_and_verify_switch(wc_dir, AB_path, AD_url,
expected_output,
expected_disk,
expected_status,
[], False,
'--depth', 'files', '--ignore-ancestry')
# Set up expected results for reverting 'switch --depth=files'
expected_output = svntest.wc.State(wc_dir, {
'A/B/gamma' : Item(status='D '),
'A/B/lambda' : Item(status='A '),
})
expected_disk = svntest.main.greek_state.copy()
expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
svntest.actions.run_and_verify_switch(wc_dir, AB_path, AB_url,
expected_output,
expected_disk,
expected_status,
[], False,
'--depth', 'files', '--ignore-ancestry')
# Putting the depth=immediates stuff in a subroutine, because we're
# going to run it at least twice.
def sw_depth_imm():
# Set up expected results of 'switch --depth=immediates'
expected_output = svntest.wc.State(wc_dir, {
'A/B/lambda' : Item(status='D '),
'A/B/E' : Item(status='D '),
'A/B/F' : Item(status='D '),
'A/B/gamma' : Item(status='A '),
'A/B/G' : Item(status='A '),
'A/B/H' : Item(status='A '),
})
expected_disk = svntest.main.greek_state.copy()
expected_disk.remove('A/B/lambda', 'A/B/E/beta', 'A/B/E/alpha',
'A/B/E', 'A/B/F')
expected_disk.add({
'A/B/gamma' : Item("This is the file 'gamma'.\n"),
'A/B/G' : Item(),
'A/B/H' : Item(),
})
expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
expected_status.remove('A/B/lambda', 'A/B/E/beta', 'A/B/E/alpha',
'A/B/E', 'A/B/F')
expected_status.add({
'A/B/gamma' : Item(status=' ', wc_rev=1),
'A/B/G' : Item(status=' ', wc_rev=1),
'A/B/H' : Item(status=' ', wc_rev=1)
})
expected_status.tweak('A/B', switched='S')
# Do 'switch --depth=immediates' and check the results in three ways.
svntest.actions.run_and_verify_switch(wc_dir, AB_path, AD_url,
expected_output,
expected_disk,
expected_status,
[], False,
'--depth', 'immediates',
'--ignore-ancestry')
sw_depth_imm()
# Set up expected results for reverting 'switch --depth=immediates'.
# (Reverting with default [infinite] depth, so that the result is a
# standard Greek Tree working copy again.)
expected_output = svntest.wc.State(wc_dir, {
'A/B/gamma' : Item(status='D '),
'A/B/G' : Item(status='D '),
'A/B/H' : Item(status='D '),
'A/B/lambda' : Item(status='A '),
'A/B/E' : Item(status='A '),
'A/B/E/alpha' : Item(status='A '),
'A/B/E/beta' : Item(status='A '),
'A/B/F' : Item(status='A '),
})
expected_disk = svntest.main.greek_state.copy()
expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
svntest.actions.run_and_verify_switch(wc_dir, AB_path, AB_url,
expected_output,
expected_disk,
expected_status,
[], False,
'--ignore-ancestry')
# Okay, repeat 'switch --depth=immediates'. (Afterwards we'll
# 'switch --depth=infinity', to test going all the way.)
sw_depth_imm()
# Set up expected results of 'switch --depth=infinity'
expected_output = svntest.wc.State(wc_dir, {
'A/B/gamma' : Item(status='D '),
'A/B/G' : Item(status='D '),
'A/B/H' : Item(status='D '),
'A/B/lambda' : Item(status='A '),
'A/B/E' : Item(status='A '),
'A/B/E/alpha' : Item(status='A '),
'A/B/E/beta' : Item(status='A '),
'A/B/F' : Item(status='A '),
})
expected_disk = svntest.main.greek_state.copy()
expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
# Do the 'switch --depth=infinity' and check the results in three ways.
svntest.actions.run_and_verify_switch(wc_dir, AB_path, AB_url,
expected_output,
expected_disk,
expected_status,
[], False,
'--depth', 'infinity',
'--ignore-ancestry')
#----------------------------------------------------------------------
def switch_to_dir_with_peg_rev(sbox):
"switch to dir@peg where dir doesn't exist in HEAD"
sbox.build()
wc_dir = sbox.wc_dir
repo_url = sbox.repo_url
# prepare two dirs X and Y in rev. 2
X_path = sbox.ospath('X')
Y_path = sbox.ospath('Y')
svntest.main.run_svn(None, 'mkdir', X_path, Y_path)
sbox.simple_commit(message='log message')
# change tau in rev. 3
ADG_path = sbox.ospath('A/D/G')
tau_path = os.path.join(ADG_path, 'tau')
svntest.main.file_append(tau_path, "new line\n")
sbox.simple_commit(message='log message')
# delete A/D/G in rev. 4
svntest.main.run_svn(None, 'up', wc_dir)
svntest.main.run_svn(None, 'rm', ADG_path)
sbox.simple_commit(message='log message')
# Test 1: switch X to A/D/G@2
ADG_url = repo_url + '/A/D/G'
expected_output = svntest.wc.State(wc_dir, {
'X/pi' : Item(status='A '),
'X/rho' : Item(status='A '),
'X/tau' : Item(status='A '),
})
expected_disk = svntest.main.greek_state.copy()
expected_disk.add({
'X' : Item(),
'X/pi' : Item("This is the file 'pi'.\n"),
'X/rho' : Item("This is the file 'rho'.\n"),
'X/tau' : Item("This is the file 'tau'.\n"),
'Y' : Item(),
})
expected_disk.remove('A/D/G', 'A/D/G/pi', 'A/D/G/rho', 'A/D/G/tau')
expected_status = svntest.actions.get_virginal_state(wc_dir, 3)
expected_status.remove('A/D/G', 'A/D/G/pi', 'A/D/G/rho', 'A/D/G/tau')
expected_status.add({
'X' : Item(status=' ', wc_rev=2, switched='S'),
'X/pi' : Item(status=' ', wc_rev=2),
'X/rho' : Item(status=' ', wc_rev=2),
'X/tau' : Item(status=' ', wc_rev=2),
'Y' : Item(status=' ', wc_rev=3)
})
# Do the switch to rev. 2 of /A/D/G@3.
svntest.actions.run_and_verify_switch(wc_dir, X_path, ADG_url + '@3',
expected_output,
expected_disk,
expected_status,
[], False,
'-r', '2', '--ignore-ancestry')
def switch_urls_with_spaces(sbox):
"switch file and dir to url containing spaces"
sbox.build()
wc_dir = sbox.wc_dir
repo_url = sbox.repo_url
# add file and directory with spaces in their names.
XYZ_path = sbox.ospath('X Y Z')
ABC_path = sbox.ospath('A B C')
svntest.main.run_svn(None, 'mkdir', XYZ_path, ABC_path)
tpm_path = sbox.ospath('tau pau mau')
bbb_path = sbox.ospath('bar baz bal')
svntest.main.file_write(tpm_path, "This is the file 'tau pau mau'.\n")
svntest.main.file_write(bbb_path, "This is the file 'bar baz bal'.\n")
svntest.main.run_svn(None, 'add', tpm_path, bbb_path)
sbox.simple_commit(message='log message')
# Test 1: switch directory 'A B C' to url 'X Y Z'
XYZ_url = repo_url + '/X Y Z'
expected_output = svntest.wc.State(wc_dir, {
})
expected_disk = svntest.main.greek_state.copy()
expected_disk.add({
'X Y Z' : Item(),
'A B C' : Item(),
'tau pau mau' : Item("This is the file 'tau pau mau'.\n"),
'bar baz bal' : Item("This is the file 'bar baz bal'.\n"),
})
expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
expected_status.add({
'X Y Z' : Item(status=' ', wc_rev=2),
'A B C' : Item(status=' ', wc_rev=2, switched='S'),
'tau pau mau' : Item(status=' ', wc_rev=2),
'bar baz bal' : Item(status=' ', wc_rev=2),
})
svntest.actions.run_and_verify_switch(wc_dir, ABC_path, XYZ_url,
expected_output,
expected_disk,
expected_status,
[],
False, '--ignore-ancestry')
# Test 2: switch file 'bar baz bal' to 'tau pau mau'
tpm_url = repo_url + '/tau pau mau'
expected_output = svntest.wc.State(wc_dir, {
'bar baz bal' : Item(status='U '),
})
expected_disk = svntest.main.greek_state.copy()
expected_disk.add({
'X Y Z' : Item(),
'A B C' : Item(),
'tau pau mau' : Item("This is the file 'tau pau mau'.\n"),
'bar baz bal' : Item("This is the file 'tau pau mau'.\n"),
})
expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
expected_status.add({
'X Y Z' : Item(status=' ', wc_rev=2),
'A B C' : Item(status=' ', wc_rev=2, switched='S'),
'tau pau mau' : Item(status=' ', wc_rev=2),
'bar baz bal' : Item(status=' ', wc_rev=2, switched='S'),
})
svntest.actions.run_and_verify_switch(wc_dir, bbb_path, tpm_url,
expected_output,
expected_disk,
expected_status,
[],
False, '--ignore-ancestry')
def switch_to_dir_with_peg_rev2(sbox):
"switch to old rev of now renamed branch"
sbox.build()
wc_dir = sbox.wc_dir
repo_url = sbox.repo_url
# prepare dir X in rev. 2
X_path = sbox.ospath('X')
svntest.main.run_svn(None, 'mkdir', X_path)
sbox.simple_commit(message='log message')
# make a change in ADG in rev. 3
tau_path = sbox.ospath('A/D/G/tau')
svntest.main.file_append(tau_path, "extra line\n")
sbox.simple_commit(message='log message')
# Rename ADG to ADY in rev 4
svntest.main.run_svn(None, 'up', wc_dir)
ADG_path = sbox.ospath('A/D/G')
ADY_path = sbox.ospath('A/D/Y')
svntest.main.run_svn(None, 'mv', ADG_path, ADY_path)
sbox.simple_commit(message='log message')
# Test switch X to rev 2 of A/D/Y@HEAD
ADY_url = sbox.repo_url + '/A/D/Y'
expected_output = svntest.wc.State(wc_dir, {
'X/pi' : Item(status='A '),
'X/rho' : Item(status='A '),
'X/tau' : Item(status='A '),
})
expected_disk = svntest.main.greek_state.copy()
expected_disk.add({
'X' : Item(),
'X/pi' : Item("This is the file 'pi'.\n"),
'X/rho' : Item("This is the file 'rho'.\n"),
'X/tau' : Item("This is the file 'tau'.\n"),
'A/D/Y' : Item(),
'A/D/Y/pi' : Item("This is the file 'pi'.\n"),
'A/D/Y/rho' : Item("This is the file 'rho'.\n"),
'A/D/Y/tau' : Item("This is the file 'tau'.\nextra line\n"),
})
expected_disk.remove('A/D/G', 'A/D/G/pi', 'A/D/G/rho', 'A/D/G/tau')
expected_status = svntest.actions.get_virginal_state(wc_dir, 3)
expected_status.remove('A/D/G', 'A/D/G/pi', 'A/D/G/rho', 'A/D/G/tau')
expected_status.add({
'X' : Item(status=' ', wc_rev=2, switched='S'),
'X/pi' : Item(status=' ', wc_rev=2),
'X/rho' : Item(status=' ', wc_rev=2),
'X/tau' : Item(status=' ', wc_rev=2),
'A/D/Y' : Item(status=' ', wc_rev=4),
'A/D/Y/pi' : Item(status=' ', wc_rev=4),
'A/D/Y/rho' : Item(status=' ', wc_rev=4),
'A/D/Y/tau' : Item(status=' ', wc_rev=4),
})
svntest.actions.run_and_verify_switch(wc_dir, X_path, ADY_url + '@HEAD',
expected_output,
expected_disk,
expected_status, [], False,
'-r', '2', '--ignore-ancestry')
def switch_to_root(sbox):
"switch a folder to the root of its repository"
sbox.build(read_only = True)
wc_dir = sbox.wc_dir
repo_url = sbox.repo_url
ADG_path = sbox.ospath('A/D/G')
# Test switch /A/D/G to /
AD_url = sbox.repo_url + '/A/D'
expected_output = svntest.wc.State(wc_dir, {
'A/D/G/pi' : Item(status='D '),
'A/D/G/rho' : Item(status='D '),
'A/D/G/tau' : Item(status='D '),
'A/D/G/A' : Item(status='A '),
'A/D/G/A/B' : Item(status='A '),
'A/D/G/A/B/lambda' : Item(status='A '),
'A/D/G/A/B/E' : Item(status='A '),
'A/D/G/A/B/E/alpha' : Item(status='A '),
'A/D/G/A/B/E/beta' : Item(status='A '),
'A/D/G/A/B/F' : Item(status='A '),
'A/D/G/A/mu' : Item(status='A '),
'A/D/G/A/C' : Item(status='A '),
'A/D/G/A/D' : Item(status='A '),
'A/D/G/A/D/gamma' : Item(status='A '),
'A/D/G/A/D/G' : Item(status='A '),
'A/D/G/A/D/G/pi' : Item(status='A '),
'A/D/G/A/D/G/rho' : Item(status='A '),
'A/D/G/A/D/G/tau' : Item(status='A '),
'A/D/G/A/D/H' : Item(status='A '),
'A/D/G/A/D/H/chi' : Item(status='A '),
'A/D/G/A/D/H/omega' : Item(status='A '),
'A/D/G/A/D/H/psi' : Item(status='A '),
'A/D/G/iota' : Item(status='A '),
})
expected_disk = svntest.main.greek_state.copy()
expected_disk.remove('A/D/G/pi', 'A/D/G/rho', 'A/D/G/tau')
expected_disk.add_state('A/D/G', svntest.main.greek_state.copy())
expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
expected_status.remove('A/D/G/pi', 'A/D/G/rho', 'A/D/G/tau')
expected_status.add_state('A/D/G',
svntest.actions.get_virginal_state(wc_dir + '/A/D/G', 1))
expected_status.tweak('A/D/G', switched = 'S')
svntest.actions.run_and_verify_switch(wc_dir, ADG_path, sbox.repo_url,
expected_output,
expected_disk,
expected_status,
[],
False, '--ignore-ancestry')
#----------------------------------------------------------------------
# Make sure that switch continue after deleting locally modified
# directories, as update and merge do.
@Issue(2505)
def tolerate_local_mods(sbox):
"tolerate deletion of a directory with local mods"
sbox.build()
wc_dir = sbox.wc_dir
A_path = sbox.ospath('A')
L_path = os.path.join(A_path, 'L')
LM_path = os.path.join(L_path, 'local_mod')
A_url = sbox.repo_url + '/A'
A2_url = sbox.repo_url + '/A2'
svntest.actions.run_and_verify_svn(['Committing transaction...\n',
'Committed revision 2.\n'], [],
'cp', '-m', 'make copy', A_url, A2_url)
os.mkdir(L_path)
svntest.main.run_svn(None, 'add', L_path)
sbox.simple_commit(message='Commit added folder')
# locally modified versioned file
svntest.main.file_write(LM_path, 'Locally modified file.\n', 'w+')
sbox.simple_add('A/L/local_mod')
expected_output = svntest.wc.State(wc_dir, {
'A/L' : Item(status=' ', treeconflict='C'),
})
expected_disk = svntest.main.greek_state.copy()
expected_disk.add({
'A/L' : Item(),
'A/L/local_mod' : Item(contents='Locally modified file.\n'),
})
expected_status = svntest.actions.get_virginal_state(wc_dir, 3)
expected_status.tweak('', 'iota', wc_rev=1)
expected_status.tweak('A', switched='S')
expected_status.add({
'A/L' : Item(status='A ', copied='+', treeconflict='C', wc_rev='-'),
'A/L/local_mod' : Item(status='A ', wc_rev='-'),
})
# Used to fail with locally modified or unversioned files
svntest.actions.run_and_verify_switch(wc_dir, A_path, A2_url,
expected_output,
expected_disk,
expected_status,
[],
False, '--ignore-ancestry')
#----------------------------------------------------------------------
# Detect tree conflicts among files and directories,
# edited or deleted in a deep directory structure.
#
# See use cases 1-3 in notes/tree-conflicts/use-cases.txt for background.
# Note that we do not try to track renames. The only difference from
# the behavior of Subversion 1.4 and 1.5 is the conflicted status of the
# parent directory.
# convenience definitions
leaf_edit = svntest.deeptrees.deep_trees_leaf_edit
tree_del = svntest.deeptrees.deep_trees_tree_del
leaf_del = svntest.deeptrees.deep_trees_leaf_del
disk_after_leaf_edit = svntest.deeptrees.deep_trees_after_leaf_edit
disk_after_leaf_del = svntest.deeptrees.deep_trees_after_leaf_del
disk_after_tree_del = svntest.deeptrees.deep_trees_after_tree_del
deep_trees_conflict_output = svntest.deeptrees.deep_trees_conflict_output
deep_trees_conflict_output_skipped = \
svntest.deeptrees.deep_trees_conflict_output_skipped
deep_trees_status_local_tree_del = \
svntest.deeptrees.deep_trees_status_local_tree_del
deep_trees_status_local_leaf_edit = \
svntest.deeptrees.deep_trees_status_local_leaf_edit
DeepTreesTestCase = svntest.deeptrees.DeepTreesTestCase
j = os.path.join
def tree_conflicts_on_switch_1_1(sbox):
"tree conflicts 1.1: tree del, leaf edit on switch"
sbox.build()
# use case 1, as in notes/tree-conflicts/use-cases.txt
# 1.1) local tree delete, incoming leaf edit
expected_output = deep_trees_conflict_output.copy()
expected_output.add({
'DDD/D1/D2' : Item(status=' ', treeconflict='U'),
'DDD/D1/D2/D3' : Item(status=' ', treeconflict='U'),
'DDD/D1/D2/D3/zeta' : Item(status=' ', treeconflict='A'),
'DD/D1/D2' : Item(status=' ', treeconflict='U'),
'DD/D1/D2/epsilon' : Item(status=' ', treeconflict='A'),
'DF/D1/beta' : Item(status=' ', treeconflict='U'),
'D/D1/delta' : Item(status=' ', treeconflict='A'),
'DDF/D1/D2' : Item(status=' ', treeconflict='U'),
'DDF/D1/D2/gamma' : Item(status=' ', treeconflict='U')
})
expected_disk = svntest.wc.State('', {
'F' : Item(),
'D' : Item(),
'DF' : Item(),
'DD' : Item(),
'DDF' : Item(),
'DDD' : Item(),
})
# The files delta, epsilon, and zeta are incoming additions, but since
# they are all within locally deleted trees they should also be schedule
# for deletion.
expected_status = deep_trees_status_local_tree_del.copy()
expected_status.add({
'D/D1/delta' : Item(status='D '),
'DD/D1/D2/epsilon' : Item(status='D '),
'DDD/D1/D2/D3/zeta' : Item(status='D '),
})
expected_status.tweak('', switched='S')
# Update to the target rev.
expected_status.tweak(wc_rev=3)
expected_info = {
'F/alpha' : {
'Tree conflict' :
'^local file delete, incoming file edit upon switch'
+ ' Source left: .file.*/F/alpha@2'
+ ' Source right: .file.*/F/alpha@3$',
},
'DF/D1' : {
'Tree conflict' :
'^local dir delete, incoming dir edit upon switch'
+ ' Source left: .dir.*/DF/D1@2'
+ ' Source right: .dir.*/DF/D1@3$',
},
'DDF/D1' : {
'Tree conflict' :
'^local dir delete, incoming dir edit upon switch'
+ ' Source left: .dir.*/DDF/D1@2'
+ ' Source right: .dir.*/DDF/D1@3$',
},
'D/D1' : {
'Tree conflict' :
'^local dir delete, incoming dir edit upon switch'
+ ' Source left: .dir.*/D/D1@2'
+ ' Source right: .dir.*/D/D1@3$',
},
'DD/D1' : {
'Tree conflict' :
'^local dir delete, incoming dir edit upon switch'
+ ' Source left: .dir.*/DD/D1@2'
+ ' Source right: .dir.*/DD/D1@3$',
},
'DDD/D1' : {
'Tree conflict' :
'^local dir delete, incoming dir edit upon switch'
+ ' Source left: .dir.*/DDD/D1@2'
+ ' Source right: .dir.*/DDD/D1@3$',
},
}
svntest.deeptrees.deep_trees_run_tests_scheme_for_switch(sbox,
[ DeepTreesTestCase("local_tree_del_incoming_leaf_edit",
tree_del,
leaf_edit,
expected_output,
expected_disk,
expected_status,
expected_info = expected_info) ] )
@Issue(3334)
def tree_conflicts_on_switch_1_2(sbox):
"tree conflicts 1.2: tree del, leaf del on switch"
sbox.build()
# 1.2) local tree delete, incoming leaf delete
expected_output = deep_trees_conflict_output.copy()
expected_output.add({
'DD/D1/D2' : Item(status=' ', treeconflict='D'),
'DDF/D1/D2' : Item(status=' ', treeconflict='U'),
'DDF/D1/D2/gamma' : Item(status=' ', treeconflict='D'),
'DDD/D1/D2' : Item(status=' ', treeconflict='U'),
'DDD/D1/D2/D3' : Item(status=' ', treeconflict='D'),
'DF/D1/beta' : Item(status=' ', treeconflict='D'),
})
expected_disk = svntest.wc.State('', {
'F' : Item(),
'D' : Item(),
'DF' : Item(),
'DD' : Item(),
'DDF' : Item(),
'DDD' : Item(),
})
expected_status = deep_trees_status_local_tree_del.copy()
# Expect the incoming leaf deletes to actually occur. Even though they
# are within (or in the case of F/alpha and D/D1 are the same as) the
# trees locally scheduled for deletion we must still delete them and
# update the scheduled for deletion items to the target rev. Otherwise
# once the conflicts are resolved we still have a mixed-rev WC we can't
# commit without updating...which, you guessed it, raises tree conflicts
# again, repeat ad infinitum - see issue #3334.
#
# Update to the target rev.
expected_status.tweak(wc_rev=3)
expected_status.tweak('F/alpha',
'D/D1',
status='! ', wc_rev=None)
expected_status.tweak('', switched='S')
# Remove the incoming deletes from status and disk.
expected_status.remove('DD/D1/D2',
'DDD/D1/D2/D3',
'DDF/D1/D2/gamma',
'DF/D1/beta')
expected_info = {
'F/alpha' : {
'Tree conflict' :
'^local file delete, incoming file delete or move upon switch'
+ ' Source left: .file.*/F/alpha@2'
+ ' Source right: .none.*(/F/alpha@3)?$',
},
'DF/D1' : {
'Tree conflict' :
'^local dir delete, incoming dir edit upon switch'
+ ' Source left: .dir.*/DF/D1@2'
+ ' Source right: .dir.*/DF/D1@3$',
},
'DDF/D1' : {
'Tree conflict' :
'^local dir delete, incoming dir edit upon switch'
+ ' Source left: .dir.*/DDF/D1@2'
+ ' Source right: .dir.*/DDF/D1@3$',
},
'D/D1' : {
'Tree conflict' :
'^local dir delete, incoming dir delete or move upon switch'
+ ' Source left: .dir.*/D/D1@2'
+ ' Source right: .none.*(/D/D1@3)?$',
},
'DD/D1' : {
'Tree conflict' :
'^local dir delete, incoming dir edit upon switch'
+ ' Source left: .dir.*/DD/D1@2'
+ ' Source right: .dir.*/DD/D1@3$',
},
'DDD/D1' : {
'Tree conflict' :
'^local dir delete, incoming dir edit upon switch'
+ ' Source left: .dir.*/DDD/D1@2'
+ ' Source right: .dir.*/DDD/D1@3$',
},
}
svntest.deeptrees.deep_trees_run_tests_scheme_for_switch(sbox,
[ DeepTreesTestCase("local_tree_del_incoming_leaf_del",
tree_del,
leaf_del,
expected_output,
expected_disk,
expected_status,
expected_info = expected_info) ] )
@Issue(3334)
def tree_conflicts_on_switch_2_1(sbox):
"tree conflicts 2.1: leaf edit, tree del on switch"
# use case 2, as in notes/tree-conflicts/use-cases.txt
# 2.1) local leaf edit, incoming tree delete
expected_output = deep_trees_conflict_output
expected_disk = disk_after_leaf_edit.copy()
expected_status = deep_trees_status_local_leaf_edit.copy()
# The expectation on 'alpha' reflects partial progress on issue #3334.
expected_status.tweak('D/D1',
'F/alpha',
'DD/D1',
'DF/D1',
'DDD/D1',
'DDF/D1',
status='A ', copied='+', wc_rev='-')
# See the status of all the paths *under* the above six subtrees. Only the
# roots of the added subtrees show as schedule 'A', these childs paths show
# only that history is scheduled with the commit.
expected_status.tweak(
'DD/D1/D2',
'DDD/D1/D2',
'DDD/D1/D2/D3',
'DF/D1/beta',
'DDF/D1/D2',
'DDF/D1/D2/gamma',
copied='+', wc_rev='-')
expected_status.tweak('', switched='S')
expected_info = {
'F/alpha' : {
'Tree conflict' :
'^local file edit, incoming file delete or move upon switch'
+ ' Source left: .file.*/F/alpha@2'
+ ' Source right: .none.*(/F/alpha@3)?$',
},
'DF/D1' : {
'Tree conflict' :
'^local dir edit, incoming dir delete or move upon switch'
+ ' Source left: .dir.*/DF/D1@2'
+ ' Source right: .none.*(/DF/D1@3)?$',
},
'DDF/D1' : {
'Tree conflict' :
'^local dir edit, incoming dir delete or move upon switch'
+ ' Source left: .dir.*/DDF/D1@2'
+ ' Source right: .none.*(/DDF/D1@3)?$',
},
'D/D1' : {
'Tree conflict' :
'^local dir edit, incoming dir delete or move upon switch'
+ ' Source left: .dir.*/D/D1@2'
+ ' Source right: .none.*(/D/D1@3)?$',
},
'DD/D1' : {
'Tree conflict' :
'^local dir edit, incoming dir delete or move upon switch'
+ ' Source left: .dir.*/DD/D1@2'
+ ' Source right: .none.*(/DD/D1@3)?$',
},
'DDD/D1' : {
'Tree conflict' :
'^local dir edit, incoming dir delete or move upon switch'
+ ' Source left: .dir.*/DDD/D1@2'
+ ' Source right: .none.*(/DDD/D1@3)?$',
},
}
### D/D1/delta is locally-added during leaf_edit. when tree_del executes,
### it will delete D/D1, and the switch reschedules local D/D1 for
### local-copy from its original revision. however, right now, we cannot
### denote that delta is a local-add rather than a child of that D/D1 copy.
### thus, it appears in the status output as a (M)odified child.
svntest.deeptrees.deep_trees_run_tests_scheme_for_switch(sbox,
[ DeepTreesTestCase("local_leaf_edit_incoming_tree_del",
leaf_edit,
tree_del,
expected_output,
expected_disk,
expected_status,
expected_info = expected_info) ] )
def tree_conflicts_on_switch_2_2(sbox):
"tree conflicts 2.2: leaf del, tree del on switch"
# 2.2) local leaf delete, incoming tree delete
### Current behaviour fails to show conflicts when deleting
### a directory tree that has modifications. (Will be solved
### when dirs_same_p() is implemented)
expected_output = deep_trees_conflict_output
expected_disk = svntest.wc.State('', {
'DDF/D1/D2' : Item(),
'F' : Item(),
'D' : Item(),
'DF/D1' : Item(),
'DD/D1' : Item(),
'DDD/D1/D2' : Item(),
})
expected_status = svntest.deeptrees.deep_trees_virginal_state.copy()
expected_status.add({'' : Item(),
'F/alpha' : Item()})
expected_status.tweak(contents=None, status=' ', wc_rev=3)
expected_status.tweak('', switched='S')
# Expect the incoming tree deletes and the local leaf deletes to mean
# that all deleted paths are *really* gone, not simply scheduled for
# deletion.
expected_status.tweak('DD/D1', 'DF/D1', 'DDF/D1', 'DDD/D1',
status='A ', copied='+', treeconflict='C',
wc_rev='-')
expected_status.tweak('DDF/D1/D2', 'DDD/D1/D2',
copied='+', wc_rev='-')
expected_status.tweak('DD/D1/D2', 'DF/D1/beta', 'DDD/D1/D2/D3',
'DDF/D1/D2/gamma',
status='D ', copied='+', wc_rev='-')
expected_status.tweak('F/alpha', 'D/D1',
status='! ', treeconflict='C', wc_rev=None)
expected_info = {
'F/alpha' : {
'Tree conflict' :
'^local file delete, incoming file delete or move upon switch'
+ ' Source left: .file.*/F/alpha@2'
+ ' Source right: .none.*(/F/alpha@3)?$',
},
'DF/D1' : {
'Tree conflict' :
'^local dir edit, incoming dir delete or move upon switch'
+ ' Source left: .dir.*/DF/D1@2'
+ ' Source right: .none.*(/DF/D1@3)?$',
},
'DDF/D1' : {
'Tree conflict' :
'^local dir edit, incoming dir delete or move upon switch'
+ ' Source left: .dir.*/DDF/D1@2'
+ ' Source right: .none.*(/DDF/D1@3)?$',
},
'D/D1' : {
'Tree conflict' :
'^local dir delete, incoming dir delete or move upon switch'
+ ' Source left: .dir.*/D/D1@2'
+ ' Source right: .none.*(/D/D1@3)?$',
},
'DD/D1' : {
'Tree conflict' :
'^local dir edit, incoming dir delete or move upon switch'
+ ' Source left: .dir.*/DD/D1@2'
+ ' Source right: .none.*(/DD/D1@3)?$',
},
'DDD/D1' : {
'Tree conflict' :
'^local dir edit, incoming dir delete or move upon switch'
+ ' Source left: .dir.*/DDD/D1@2'
+ ' Source right: .none.*(/DDD/D1@3)?$',
},
}
svntest.deeptrees.deep_trees_run_tests_scheme_for_switch(sbox,
[ DeepTreesTestCase("local_leaf_del_incoming_tree_del",
leaf_del,
tree_del,
expected_output,
expected_disk,
expected_status,
expected_info = expected_info) ] )
def tree_conflicts_on_switch_3(sbox):
"tree conflicts 3: tree del, tree del on switch"
# use case 3, as in notes/tree-conflicts/use-cases.txt
# local tree delete, incoming tree delete
expected_output = deep_trees_conflict_output
expected_disk = svntest.wc.State('', {
'F' : Item(),
'D' : Item(),
'DF' : Item(),
'DD' : Item(),
'DDF' : Item(),
'DDD' : Item(),
})
expected_status = deep_trees_status_local_tree_del.copy()
expected_status.tweak('', switched='S')
# Expect the incoming tree deletes and the local tree deletes to mean
# that all deleted paths are *really* gone, not simply scheduled for
# deletion.
expected_status.tweak('F/alpha',
'D/D1',
'DD/D1',
'DF/D1',
'DDD/D1',
'DDF/D1',
status='! ', wc_rev=None)
# Remove from expected status and disk everything below the deleted paths.
expected_status.remove('DD/D1/D2',
'DF/D1/beta',
'DDD/D1/D2',
'DDD/D1/D2/D3',
'DDF/D1/D2',
'DDF/D1/D2/gamma',)
expected_info = {
'F/alpha' : {
'Tree conflict' :
'^local file delete, incoming file delete or move upon switch'
+ ' Source left: .file.*/F/alpha@2'
+ ' Source right: .none.*(/F/alpha@3)?$',
},
'DF/D1' : {
'Tree conflict' :
'^local dir delete, incoming dir delete or move upon switch'
+ ' Source left: .dir.*/DF/D1@2'
+ ' Source right: .none.*(/DF/D1@3)?$',
},
'DDF/D1' : {
'Tree conflict' :
'^local dir delete, incoming dir delete or move upon switch'
+ ' Source left: .dir.*/DDF/D1@2'
+ ' Source right: .none.*(/DDF/D1@3)?$',
},
'D/D1' : {
'Tree conflict' :
'^local dir delete, incoming dir delete or move upon switch'
+ ' Source left: .dir.*/D/D1@2'
+ ' Source right: .none.*(/D/D1@3)?$',
},
'DD/D1' : {
'Tree conflict' :
'^local dir delete, incoming dir delete or move upon switch'
+ ' Source left: .dir.*/DD/D1@2'
+ ' Source right: .none.*(/DD/D1@3)?$',
},
'DDD/D1' : {
'Tree conflict' :
'^local dir delete, incoming dir delete or move upon switch'
+ ' Source left: .dir.*/DDD/D1@2'
+ ' Source right: .none.*(/DDD/D1@3)?$',
},
}
svntest.deeptrees.deep_trees_run_tests_scheme_for_switch(sbox,
[ DeepTreesTestCase("local_tree_del_incoming_tree_del",
tree_del,
tree_del,
expected_output,
expected_disk,
expected_status,
expected_info = expected_info) ] )
def copy_with_switched_subdir(sbox):
"copy directory with switched subdir"
sbox.build()
wc_dir = sbox.wc_dir
D = sbox.ospath('A/D')
G = os.path.join(D, 'G')
E_url = sbox.repo_url + '/A/B/E'
R = sbox.ospath('R')
state = svntest.actions.get_virginal_state(wc_dir, 1)
# Verify before switching
svntest.actions.run_and_verify_status(wc_dir, state)
# Switch A/D/G
svntest.actions.run_and_verify_svn(None, [], 'switch',
'--ignore-ancestry', E_url, G)
state.tweak('A/D/G', switched='S')
state.remove('A/D/G/pi', 'A/D/G/rho', 'A/D/G/tau')
state.add({
'A/D/G/alpha' : Item(status=' ', wc_rev=1),
'A/D/G/beta' : Item(status=' ', wc_rev=1),
})
svntest.actions.run_and_verify_status(wc_dir, state)
# And now copy A/D and everything below it to R
svntest.actions.run_and_verify_svn(None, [], 'cp', D, R)
state.add({
'R' : Item(status='A ', copied='+', wc_rev='-'),
'R/gamma' : Item(status=' ', copied='+', wc_rev='-'),
'R/G/alpha' : Item(status=' ', copied='+', wc_rev='-'),
'R/G/beta' : Item(status=' ', copied='+', wc_rev='-'),
'R/H' : Item(status=' ', copied='+', wc_rev='-'),
'R/H/chi' : Item(status=' ', copied='+', wc_rev='-'),
'R/H/omega' : Item(status=' ', copied='+', wc_rev='-'),
'R/H/psi' : Item(status=' ', copied='+', wc_rev='-'),
'R/G' : Item(status='A ', copied='+', wc_rev='-'),
})
svntest.actions.run_and_verify_status(wc_dir, state)
sbox.simple_commit(message='Commit added folder')
# Additional test, it should commit to R/G/alpha.
svntest.main.run_svn(None, 'up', wc_dir)
svntest.main.file_append(sbox.ospath('R/G/alpha'), "apple")
sbox.simple_commit(message='Commit changed file')
# Checkout working copy to verify result
svntest.main.safe_rmtree(wc_dir, 1)
svntest.actions.run_and_verify_svn(None, [],
'checkout',
sbox.repo_url, wc_dir)
# Switch A/D/G again to recreate state
svntest.actions.run_and_verify_svn(None, [], 'switch',
'--ignore-ancestry', E_url, G)
# Clear the statuses
state.tweak(status=' ', copied=None, wc_rev='3', entry_status=None)
# But reset the switched state
state.tweak('A/D/G', switched='S')
svntest.actions.run_and_verify_status(wc_dir, state)
@Issue(3871)
def up_to_old_rev_with_subtree_switched_to_root(sbox):
"up to old rev with subtree switched to root"
sbox.build()
wc_dir = sbox.wc_dir
# Some paths we'll care about.
A_path = sbox.ospath('A')
branch_path = sbox.ospath('branch')
# Starting with a vanilla greek tree, create a branch of A, switch
# that branch to the root of the repository, then update the WC to
# r1.
svntest.actions.run_and_verify_svn(None, [], 'copy', A_path,
branch_path)
svntest.actions.run_and_verify_svn(None, [], 'ci', wc_dir,
'-m', 'Create a branch')
svntest.actions.run_and_verify_svn(None, [], 'sw', sbox.repo_url,
branch_path, '--ignore-ancestry')
# Now update the WC to r1.
svntest.actions.run_and_verify_svn(None, [], 'up', '-r1', wc_dir)
def different_node_kind(sbox):
"switch to a different node kind"
sbox.build(read_only = True)
os.chdir(sbox.wc_dir)
sbox.wc_dir = ''
pristine_disk = svntest.main.greek_state
pristine_status = svntest.actions.get_virginal_state(sbox.wc_dir, 1)
expected_disk = pristine_disk.copy()
expected_status = pristine_status.copy()
def switch_to_dir(sbox, rel_url, rel_path):
full_url = sbox.repo_url + '/' + rel_url
full_path = sbox.ospath(rel_path)
expected_disk.remove(rel_path)
expected_disk.add({ rel_path : pristine_disk.desc[rel_url] })
expected_disk.add_state(rel_path, pristine_disk.subtree(rel_url))
expected_status.tweak(rel_path, switched='S')
expected_status.add_state(rel_path, pristine_status.subtree(rel_url))
svntest.actions.run_and_verify_switch(sbox.wc_dir, full_path, full_url,
None, expected_disk, expected_status,
[], False,
'--ignore-ancestry')
svntest.actions.run_and_verify_svn(None, [], 'info', full_path)
if not os.path.isdir(full_path):
raise svntest.Failure
def switch_to_file(sbox, rel_url, rel_path):
full_url = sbox.repo_url + '/' + rel_url
full_path = sbox.ospath(rel_path)
expected_disk.remove_subtree(rel_path)
expected_disk.add({ rel_path : pristine_disk.desc[rel_url] })
expected_status.remove_subtree(rel_path)
expected_status.add({ rel_path : pristine_status.desc[rel_url] })
expected_status.tweak(rel_path, switched='S')
svntest.actions.run_and_verify_switch(sbox.wc_dir, full_path, full_url,
None, expected_disk, expected_status,
[], False,
'--ignore-ancestry')
svntest.actions.run_and_verify_svn(None, [], 'info', full_path)
if not os.path.isfile(full_path):
raise svntest.Failure
# Switch two files to dirs and two dirs to files.
# 'A/C' is an empty dir; 'A/D/G' is a non-empty dir.
switch_to_dir(sbox, 'A/C', 'iota')
switch_to_dir(sbox, 'A/D/G', 'A/D/gamma')
switch_to_file(sbox, 'iota', 'A/C')
switch_to_file(sbox, 'A/D/gamma', 'A/D/G')
@Issue(3332, 3333)
def switch_to_spaces(sbox):
"switch to a directory with spaces in its name"
sbox.build()
wc_dir = sbox.wc_dir
repo_url = sbox.repo_url
# Paths are normalized in the command processing, so %20 is equivalent to ' '
svntest.actions.run_and_verify_svn(None, [],
'cp', repo_url + '/A',
repo_url + '/A%20with space',
'-m', '')
svntest.actions.run_and_verify_svn(None, [],
'mv', repo_url + '/A%20with space',
repo_url + '/A with%20more spaces',
'-m', '')
expected_status = svntest.actions.get_virginal_state(wc_dir, 3)
expected_status.tweak('A', switched='S')
expected_status.tweak('', 'iota', wc_rev=1)
svntest.actions.run_and_verify_switch(sbox.wc_dir, sbox.ospath('A'),
repo_url + '/A%20with more%20spaces',
None, None, expected_status)
def switch_across_replacement(sbox):
"switch across a node replacement"
sbox.build()
os.chdir(sbox.wc_dir)
sbox.wc_dir = ''
# replacement
sbox.simple_rm('A/mu')
sbox.simple_append('A/mu', "This is the file 'mu'.\n", truncate=True)
sbox.simple_add('A/mu')
sbox.simple_commit() # r2
# When 'switch' of a dir brings in a replacement of a child file with no
# textual difference and ignoring ancestry, the switch doesn't report any
# incoming change at all, (and so won't raise a tree conflict if there is
# a local mod). 'update' on the other hand does report the replacement
# as expected.
# This test FAILs when using a Subversion 1.0-1.7 svnserve.
expected_output = svntest.wc.State(sbox.wc_dir, {
'A/mu' : Item(status='A ', prev_status='D '),
})
svntest.actions.run_and_verify_update(sbox.wc_dir,
expected_output, None, None,
[], False,
'-r1')
svntest.actions.run_and_verify_update(sbox.wc_dir,
expected_output, None, None,
[], False,
'-r2')
svntest.actions.run_and_verify_switch(sbox.wc_dir, sbox.ospath('A'), '^/A',
expected_output, None, None,
[], False,
'-r1')
@Issue(1975)
def switch_keywords(sbox):
"switch and svn:keywords"
sbox.build()
gamma_path = sbox.ospath('A/D/gamma')
psi_path = sbox.ospath('A/D/H/psi')
sbox.simple_propset('svn:keywords', 'URL', 'A/D/gamma')
svntest.main.file_write(gamma_path, "$URL$\n")
sbox.simple_propset('svn:keywords', 'URL', 'A/D/H/psi')
svntest.main.file_write(psi_path, "$URL$\n")
sbox.simple_commit()
expected_disk = svntest.main.greek_state.copy()
expected_disk.tweak('A/D/gamma',
contents="$URL: %s/A/D/gamma $\n" % sbox.repo_url)
expected_disk.tweak('A/D/H/psi',
contents="$URL: %s/A/D/H/psi $\n" % sbox.repo_url)
svntest.actions.run_and_verify_update(sbox.wc_dir,
None, expected_disk, None)
sbox.simple_copy('A', 'A_copy')
sbox.simple_commit()
sbox.simple_update()
# Next, we're going to switch A to A_copy, and expect keywords
# in the switched files gamma and psi to be updated accordingly.
expected_disk.add({
'A_copy/D/H/chi' : Item(contents="This is the file 'chi'.\n"),
'A_copy/D/H/psi' : Item(contents="$URL: %s/A_copy/D/H/psi $\n"
% sbox.repo_url),
'A_copy/D/H/omega' : Item(contents="This is the file 'omega'.\n"),
'A_copy/D/G/pi' : Item(contents="This is the file 'pi'.\n"),
'A_copy/D/G/tau' : Item(contents="This is the file 'tau'.\n"),
'A_copy/D/G/rho' : Item(contents="This is the file 'rho'.\n"),
'A_copy/D/gamma' : Item(contents="$URL: %s/A_copy/D/gamma $\n"
% sbox.repo_url),
'A_copy/B/F' : Item(),
'A_copy/B/E/alpha' : Item(contents="This is the file 'alpha'.\n"),
'A_copy/B/E/beta' : Item(contents="This is the file 'beta'.\n"),
'A_copy/B/lambda' : Item(contents="This is the file 'lambda'.\n"),
'A_copy/mu' : Item(contents="This is the file 'mu'.\n"),
'A_copy/C' : Item(),
})
# update expected URL for switched gamma
expected_disk.tweak('A/D/gamma',
contents="$URL: %s/A_copy/D/gamma $\n" % sbox.repo_url)
# leave gamma unmodified, locally modify psi
svntest.main.file_write(psi_path, "$URL$\nnew line\n")
# update expected URL for switched psi
expected_disk.tweak('A/D/H/psi',
contents="$URL: %s/A_copy/D/H/psi $\nnew line\n"
% sbox.repo_url)
expected_status = svntest.actions.get_virginal_state(sbox.wc_dir, 3)
expected_status.add({
'A_copy' : Item(status=' ', wc_rev='3'),
'A_copy/mu' : Item(status=' ', wc_rev='3'),
'A_copy/D' : Item(status=' ', wc_rev='3'),
'A_copy/D/H' : Item(status=' ', wc_rev='3'),
'A_copy/D/H/psi' : Item(status=' ', wc_rev='3'),
'A_copy/D/H/chi' : Item(status=' ', wc_rev='3'),
'A_copy/D/H/omega' : Item(status=' ', wc_rev='3'),
'A_copy/D/gamma' : Item(status=' ', wc_rev='3'),
'A_copy/D/G' : Item(status=' ', wc_rev='3'),
'A_copy/D/G/rho' : Item(status=' ', wc_rev='3'),
'A_copy/D/G/tau' : Item(status=' ', wc_rev='3'),
'A_copy/D/G/pi' : Item(status=' ', wc_rev='3'),
'A_copy/B' : Item(status=' ', wc_rev='3'),
'A_copy/B/E' : Item(status=' ', wc_rev='3'),
'A_copy/B/E/alpha' : Item(status=' ', wc_rev='3'),
'A_copy/B/E/beta' : Item(status=' ', wc_rev='3'),
'A_copy/B/F' : Item(status=' ', wc_rev='3'),
'A_copy/B/lambda' : Item(status=' ', wc_rev='3'),
'A_copy/C' : Item(status=' ', wc_rev='3'),
})
expected_status.tweak('A', switched='S')
expected_status.tweak('A/D/H/psi', status='M ')
# both gamma and psi should have update URLs after the switch
svntest.actions.run_and_verify_switch(sbox.wc_dir, sbox.ospath('A'), '^/A_copy',
None, expected_disk, expected_status)
@Issue(4524)
def switch_moves(sbox):
"switch moves on wc checkpoint"
sbox.build()
sbox.simple_move('A/B', 'B')
sbox.simple_rm('A')
branch_url = sbox.repo_url + '/branch'
svntest.actions.run_and_verify_svn(None, [],
'cp', sbox.wc_dir, branch_url,
'-m', '')
expected_disk = svntest.wc.State('', {
'B/E/alpha' : Item(contents="This is the file 'alpha'.\n"),
'B/E/beta' : Item(contents="This is the file 'beta'.\n"),
'B/lambda' : Item(contents="This is the file 'lambda'.\n"),
'B/F' : Item(),
'iota' : Item(contents="This is the file 'iota'.\n"),
})
expected_status = svntest.wc.State(sbox.wc_dir, {
'' : Item(status=' ', wc_rev='2'),
'B' : Item(status='R ', copied='+', treeconflict='C', wc_rev='-'),
'B/lambda' : Item(status=' ', copied='+', wc_rev='-'),
'B/F' : Item(status=' ', copied='+', wc_rev='-'),
'B/E' : Item(status=' ', copied='+', wc_rev='-'),
'B/E/beta' : Item(status=' ', copied='+', wc_rev='-'),
'B/E/alpha' : Item(status=' ', copied='+', wc_rev='-'),
'A' : Item(status='! ', treeconflict='C'),
'iota' : Item(status=' ', wc_rev='2'),
})
# In Subversion 1.8 this scenario causes an Sqlite row not found error.
# It would be nice if we could handle the tree conflict more intelligent, as
# the working copy matches the incomming change.
svntest.actions.run_and_verify_switch(sbox.wc_dir, sbox.ospath(''), branch_url,
None, expected_disk, expected_status)
########################################################################
# Run the tests
# list all tests here, starting with None:
test_list = [ None,
routine_switching,
commit_switched_things,
full_update,
full_rev_update,
update_switched_things,
rev_update_switched_things,
log_switched_file,
delete_subdir,
file_dir_file,
nonrecursive_switching,
failed_anchor_is_target,
bad_intermediate_urls,
obstructed_switch,
commit_mods_below_switch,
refresh_read_only_attribute,
switch_change_repos_root,
forced_switch,
forced_switch_failures,
switch_scheduled_add,
mergeinfo_switch_elision,
switch_with_obstructing_local_adds,
switch_with_depth,
switch_to_dir_with_peg_rev,
switch_urls_with_spaces,
switch_to_dir_with_peg_rev2,
switch_to_root,
tolerate_local_mods,
tree_conflicts_on_switch_1_1,
tree_conflicts_on_switch_1_2,
tree_conflicts_on_switch_2_1,
tree_conflicts_on_switch_2_2,
tree_conflicts_on_switch_3,
copy_with_switched_subdir,
up_to_old_rev_with_subtree_switched_to_root,
different_node_kind,
switch_to_spaces,
switch_across_replacement,
switch_keywords,
switch_moves,
]
if __name__ == '__main__':
svntest.main.run_tests(test_list)
# NOTREACHED
### End of file.