| #!/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. |