| #!/usr/bin/env python |
| # |
| # merge_reintegrate_tests.py: testing merge --reintegrate |
| # |
| # Subversion is a tool for revision control. |
| # See http://subversion.apache.org for more information. |
| # |
| # ==================================================================== |
| # Licensed to the Apache Software Foundation (ASF) under one |
| # or more contributor license agreements. See the NOTICE file |
| # distributed with this work for additional information |
| # regarding copyright ownership. The ASF licenses this file |
| # to you under the Apache License, Version 2.0 (the |
| # "License"); you may not use this file except in compliance |
| # with the License. You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, |
| # software distributed under the License is distributed on an |
| # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| # KIND, either express or implied. See the License for the |
| # specific language governing permissions and limitations |
| # under the License. |
| ###################################################################### |
| |
| # General modules |
| import shutil, sys, re, os |
| import time |
| |
| # Our testing module |
| import svntest |
| from svntest import main, wc, verify, actions |
| |
| # (abbreviation) |
| Item = wc.StateItem |
| Skip = svntest.testcase.Skip_deco |
| SkipUnless = svntest.testcase.SkipUnless_deco |
| XFail = svntest.testcase.XFail_deco |
| Issues = svntest.testcase.Issues_deco |
| Issue = svntest.testcase.Issue_deco |
| Wimp = svntest.testcase.Wimp_deco |
| exp_noop_up_out = svntest.actions.expected_noop_update_output |
| |
| from svntest.main import SVN_PROP_MERGEINFO |
| from svntest.main import server_has_mergeinfo |
| from merge_tests import set_up_branch |
| from merge_tests import expected_merge_output |
| |
| #---------------------------------------------------------------------- |
| @SkipUnless(server_has_mergeinfo) |
| @Issue(3640) |
| def basic_reintegrate(sbox): |
| "basic merge --reintegrate support" |
| |
| # Also includes test for issue #3640 'moved target breaks reintegrate merge' |
| |
| # Make A_COPY branch in r2, and do a few more commits to A in r3-6. |
| sbox.build() |
| wc_dir = sbox.wc_dir |
| expected_disk, expected_status = set_up_branch(sbox) |
| |
| # Make a change on the branch, to A/mu. Commit in r7. |
| svntest.main.file_write(os.path.join(wc_dir, "A_COPY", "mu"), |
| "Changed on the branch.") |
| expected_output = wc.State(wc_dir, {'A_COPY/mu' : Item(verb='Sending')}) |
| expected_status.tweak('A_COPY/mu', wc_rev=7) |
| svntest.actions.run_and_verify_commit(wc_dir, expected_output, |
| expected_status, None, wc_dir) |
| expected_disk.tweak('A_COPY/mu', contents='Changed on the branch.') |
| |
| # Update the wcs. |
| expected_output = wc.State(wc_dir, {}) |
| expected_status.tweak(wc_rev='7') |
| svntest.actions.run_and_verify_update(wc_dir, expected_output, |
| expected_disk, expected_status, |
| None, None, None, None, None, True) |
| |
| # Merge from trunk to branch (ie, r3-6), using normal cherry-harvest. |
| A_COPY_path = os.path.join(wc_dir, "A_COPY") |
| expected_output = wc.State(A_COPY_path, { |
| 'D/H/psi' : Item(status='U '), |
| 'D/G/rho' : Item(status='U '), |
| 'B/E/beta' : Item(status='U '), |
| 'D/H/omega' : Item(status='U '), |
| }) |
| expected_mergeinfo_output = wc.State(A_COPY_path, { |
| '' : Item(status=' U'), |
| }) |
| expected_elision_output = wc.State(A_COPY_path, { |
| }) |
| k_expected_status = wc.State(A_COPY_path, { |
| "B" : Item(status=' ', wc_rev=7), |
| "B/lambda" : Item(status=' ', wc_rev=7), |
| "B/E" : Item(status=' ', wc_rev=7), |
| "B/E/alpha" : Item(status=' ', wc_rev=7), |
| "B/E/beta" : Item(status='M ', wc_rev=7), |
| "B/F" : Item(status=' ', wc_rev=7), |
| "mu" : Item(status=' ', wc_rev=7), |
| "C" : Item(status=' ', wc_rev=7), |
| "D" : Item(status=' ', wc_rev=7), |
| "D/gamma" : Item(status=' ', wc_rev=7), |
| "D/G" : Item(status=' ', wc_rev=7), |
| "D/G/pi" : Item(status=' ', wc_rev=7), |
| "D/G/rho" : Item(status='M ', wc_rev=7), |
| "D/G/tau" : Item(status=' ', wc_rev=7), |
| "D/H" : Item(status=' ', wc_rev=7), |
| "D/H/chi" : Item(status=' ', wc_rev=7), |
| "D/H/omega" : Item(status='M ', wc_rev=7), |
| "D/H/psi" : Item(status='M ', wc_rev=7), |
| "" : Item(status=' M', wc_rev=7), |
| }) |
| k_expected_disk = wc.State('', { |
| '' : Item(props={SVN_PROP_MERGEINFO : '/A:2-7'}), |
| 'B' : Item(), |
| 'B/lambda' : Item("This is the file 'lambda'.\n"), |
| 'B/E' : Item(), |
| 'B/E/alpha' : Item("This is the file 'alpha'.\n"), |
| 'B/E/beta' : Item("New content"), |
| 'B/F' : Item(), |
| 'mu' : Item("Changed on the branch."), |
| 'C' : Item(), |
| 'D' : Item(), |
| 'D/gamma' : Item("This is the file 'gamma'.\n"), |
| 'D/G' : Item(), |
| 'D/G/pi' : Item("This is the file 'pi'.\n"), |
| 'D/G/rho' : Item("New content"), |
| 'D/G/tau' : Item("This is the file 'tau'.\n"), |
| 'D/H' : Item(), |
| 'D/H/chi' : Item("This is the file 'chi'.\n"), |
| 'D/H/omega' : Item("New content"), |
| 'D/H/psi' : Item("New content"), |
| }) |
| expected_skip = wc.State(A_COPY_path, {}) |
| svntest.actions.run_and_verify_merge(A_COPY_path, None, None, |
| sbox.repo_url + '/A', None, |
| expected_output, |
| expected_mergeinfo_output, |
| expected_elision_output, |
| k_expected_disk, |
| k_expected_status, |
| expected_skip, |
| None, None, None, None, |
| None, True) |
| expected_disk.tweak('A_COPY', props={SVN_PROP_MERGEINFO: '/A:2-7'}) |
| expected_disk.tweak('A_COPY/B/E/beta', contents="New content") |
| expected_disk.tweak('A_COPY/D/G/rho', contents="New content") |
| expected_disk.tweak('A_COPY/D/H/omega', contents="New content") |
| expected_disk.tweak('A_COPY/D/H/psi', contents="New content") |
| |
| # Commit the merge to branch (r8). |
| expected_output = wc.State(wc_dir, { |
| 'A_COPY/D/H/psi' : Item(verb='Sending'), |
| 'A_COPY/D/G/rho' : Item(verb='Sending'), |
| 'A_COPY/B/E/beta' : Item(verb='Sending'), |
| 'A_COPY/D/H/omega' : Item(verb='Sending'), |
| 'A_COPY' : Item(verb='Sending'), |
| }) |
| expected_status.tweak('A_COPY', 'A_COPY/D/H/psi', 'A_COPY/D/G/rho', |
| 'A_COPY/B/E/beta', 'A_COPY/D/H/omega', wc_rev=8) |
| svntest.actions.run_and_verify_commit(wc_dir, expected_output, |
| expected_status, None, wc_dir) |
| |
| # Update the wcs again. |
| expected_output = wc.State(wc_dir, {}) |
| expected_status.tweak(wc_rev='8') |
| svntest.actions.run_and_verify_update(wc_dir, expected_output, |
| expected_disk, expected_status, |
| None, None, None, None, None, True) |
| |
| |
| # *finally*, actually run merge --reintegrate in trunk with the |
| # branch URL. This should bring in the mu change and the tauprime |
| # change. |
| A_path = os.path.join(wc_dir, "A") |
| expected_output = wc.State(A_path, { |
| 'mu' : Item(status='U '), |
| }) |
| expected_mergeinfo_output = wc.State(A_path, { |
| '' : Item(status=' U'), |
| }) |
| expected_elision_output = wc.State(A_path, { |
| }) |
| k_expected_status = wc.State(A_path, { |
| "B" : Item(status=' ', wc_rev=8), |
| "B/lambda" : Item(status=' ', wc_rev=8), |
| "B/E" : Item(status=' ', wc_rev=8), |
| "B/E/alpha" : Item(status=' ', wc_rev=8), |
| "B/E/beta" : Item(status=' ', wc_rev=8), |
| "B/F" : Item(status=' ', wc_rev=8), |
| "mu" : Item(status='M ', wc_rev=8), |
| "C" : Item(status=' ', wc_rev=8), |
| "D" : Item(status=' ', wc_rev=8), |
| "D/gamma" : Item(status=' ', wc_rev=8), |
| "D/G" : Item(status=' ', wc_rev=8), |
| "D/G/pi" : Item(status=' ', wc_rev=8), |
| "D/G/rho" : Item(status=' ', wc_rev=8), |
| "D/G/tau" : Item(status=' ', wc_rev=8), |
| "D/H" : Item(status=' ', wc_rev=8), |
| "D/H/chi" : Item(status=' ', wc_rev=8), |
| "D/H/omega" : Item(status=' ', wc_rev=8), |
| "D/H/psi" : Item(status=' ', wc_rev=8), |
| "" : Item(status=' M', wc_rev=8), |
| }) |
| k_expected_disk.tweak('', props={SVN_PROP_MERGEINFO : '/A_COPY:2-8'}) |
| expected_skip = wc.State(A_path, {}) |
| svntest.actions.run_and_verify_merge(A_path, None, None, |
| sbox.repo_url + '/A_COPY', None, |
| expected_output, |
| expected_mergeinfo_output, |
| expected_elision_output, |
| k_expected_disk, |
| k_expected_status, |
| expected_skip, |
| None, None, None, None, |
| None, True, True, |
| '--reintegrate', A_path) |
| |
| # Test issue #3640: |
| # |
| # Revert the merge then move A to A_MOVED in r9. Repeat the merge, but |
| # targeting A_MOVED this time. This should work with almost the same |
| # results. The only differences being the inclusion of r9 in the |
| # mergeinfo and the A-->A_MOVED path difference. |
| svntest.actions.run_and_verify_svn(None, None, [], 'revert', '-R', wc_dir) |
| svntest.actions.run_and_verify_svn(None, |
| ['\n', 'Committed revision 9.\n'], |
| [], 'move', |
| sbox.repo_url + '/A', |
| sbox.repo_url + '/A_MOVED', |
| '-m', 'Copy A to A_MOVED') |
| svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir) |
| A_MOVED_path = os.path.join(wc_dir, "A_MOVED") |
| expected_output = wc.State(A_MOVED_path, { |
| 'mu' : Item(status='U '), |
| }) |
| expected_mergeinfo_output = wc.State(A_MOVED_path, { |
| '' : Item(status=' U'), |
| }) |
| expected_elision_output = wc.State(A_MOVED_path, { |
| }) |
| expected_status = wc.State(A_MOVED_path, { |
| "B" : Item(status=' '), |
| "B/lambda" : Item(status=' '), |
| "B/E" : Item(status=' '), |
| "B/E/alpha" : Item(status=' '), |
| "B/E/beta" : Item(status=' '), |
| "B/F" : Item(status=' '), |
| "mu" : Item(status='M '), |
| "C" : Item(status=' '), |
| "D" : Item(status=' '), |
| "D/gamma" : Item(status=' '), |
| "D/G" : Item(status=' '), |
| "D/G/pi" : Item(status=' '), |
| "D/G/rho" : Item(status=' '), |
| "D/G/tau" : Item(status=' '), |
| "D/H" : Item(status=' '), |
| "D/H/chi" : Item(status=' '), |
| "D/H/omega" : Item(status=' '), |
| "D/H/psi" : Item(status=' '), |
| "" : Item(status=' M'), |
| }) |
| expected_status.tweak(wc_rev=9) |
| k_expected_disk.tweak('', props={SVN_PROP_MERGEINFO : '/A_COPY:2-9'}) |
| expected_skip = wc.State(A_MOVED_path, {}) |
| svntest.actions.run_and_verify_merge(A_MOVED_path, None, None, |
| sbox.repo_url + '/A_COPY', None, |
| expected_output, |
| expected_mergeinfo_output, |
| expected_elision_output, |
| k_expected_disk, |
| expected_status, |
| expected_skip, |
| None, None, None, None, |
| None, True, True, |
| '--reintegrate', A_MOVED_path) |
| |
| #---------------------------------------------------------------------- |
| def reintegrate_with_rename(sbox): |
| "merge --reintegrate with renamed file on branch" |
| |
| # Make A_COPY branch in r2, and do a few more commits to A in r3-6. |
| sbox.build() |
| wc_dir = sbox.wc_dir |
| expected_disk, expected_status = set_up_branch(sbox) |
| |
| # Make a change on the branch, to A/mu. Commit in r7. |
| svntest.main.file_write(os.path.join(wc_dir, "A_COPY", "mu"), |
| "Changed on the branch.") |
| expected_output = wc.State(wc_dir, {'A_COPY/mu' : Item(verb='Sending')}) |
| expected_status.tweak('A_COPY/mu', wc_rev=7) |
| svntest.actions.run_and_verify_commit(wc_dir, expected_output, |
| expected_status, None, wc_dir) |
| expected_disk.tweak('A_COPY/mu', contents='Changed on the branch.') |
| |
| # Update the wcs. |
| expected_output = wc.State(wc_dir, {}) |
| expected_status.tweak(wc_rev='7') |
| svntest.actions.run_and_verify_update(wc_dir, expected_output, |
| expected_disk, expected_status, |
| None, None, None, None, None, True) |
| |
| # Merge from trunk to branch (ie, r3-6), using normal cherry-harvest. |
| A_COPY_path = os.path.join(wc_dir, "A_COPY") |
| expected_output = wc.State(A_COPY_path, { |
| 'D/H/psi' : Item(status='U '), |
| 'D/G/rho' : Item(status='U '), |
| 'B/E/beta' : Item(status='U '), |
| 'D/H/omega' : Item(status='U '), |
| }) |
| expected_mergeinfo_output = wc.State(A_COPY_path, { |
| '' : Item(status=' U'), |
| }) |
| expected_elision_output = wc.State(A_COPY_path, { |
| }) |
| k_expected_status = wc.State(A_COPY_path, { |
| "B" : Item(status=' ', wc_rev=7), |
| "B/lambda" : Item(status=' ', wc_rev=7), |
| "B/E" : Item(status=' ', wc_rev=7), |
| "B/E/alpha" : Item(status=' ', wc_rev=7), |
| "B/E/beta" : Item(status='M ', wc_rev=7), |
| "B/F" : Item(status=' ', wc_rev=7), |
| "mu" : Item(status=' ', wc_rev=7), |
| "C" : Item(status=' ', wc_rev=7), |
| "D" : Item(status=' ', wc_rev=7), |
| "D/gamma" : Item(status=' ', wc_rev=7), |
| "D/G" : Item(status=' ', wc_rev=7), |
| "D/G/pi" : Item(status=' ', wc_rev=7), |
| "D/G/rho" : Item(status='M ', wc_rev=7), |
| "D/G/tau" : Item(status=' ', wc_rev=7), |
| "D/H" : Item(status=' ', wc_rev=7), |
| "D/H/chi" : Item(status=' ', wc_rev=7), |
| "D/H/omega" : Item(status='M ', wc_rev=7), |
| "D/H/psi" : Item(status='M ', wc_rev=7), |
| "" : Item(status=' M', wc_rev=7), |
| }) |
| k_expected_disk = wc.State('', { |
| '' : Item(props={SVN_PROP_MERGEINFO : '/A:2-7'}), |
| 'B' : Item(), |
| 'B/lambda' : Item("This is the file 'lambda'.\n"), |
| 'B/E' : Item(), |
| 'B/E/alpha' : Item("This is the file 'alpha'.\n"), |
| 'B/E/beta' : Item("New content"), |
| 'B/F' : Item(), |
| 'mu' : Item("Changed on the branch."), |
| 'C' : Item(), |
| 'D' : Item(), |
| 'D/gamma' : Item("This is the file 'gamma'.\n"), |
| 'D/G' : Item(), |
| 'D/G/pi' : Item("This is the file 'pi'.\n"), |
| 'D/G/rho' : Item("New content"), |
| 'D/G/tau' : Item("This is the file 'tau'.\n"), |
| 'D/H' : Item(), |
| 'D/H/chi' : Item("This is the file 'chi'.\n"), |
| 'D/H/omega' : Item("New content"), |
| 'D/H/psi' : Item("New content"), |
| }) |
| expected_skip = wc.State(A_COPY_path, {}) |
| svntest.actions.run_and_verify_merge(A_COPY_path, None, None, |
| sbox.repo_url + '/A', None, |
| expected_output, |
| expected_mergeinfo_output, |
| expected_elision_output, |
| k_expected_disk, |
| k_expected_status, |
| expected_skip, |
| None, None, None, None, |
| None, True) |
| expected_disk.tweak('A_COPY', props={SVN_PROP_MERGEINFO: '/A:2-7'}) |
| expected_disk.tweak('A_COPY/B/E/beta', contents="New content") |
| expected_disk.tweak('A_COPY/D/G/rho', contents="New content") |
| expected_disk.tweak('A_COPY/D/H/omega', contents="New content") |
| expected_disk.tweak('A_COPY/D/H/psi', contents="New content") |
| |
| # Commit the merge to branch (r8). |
| expected_output = wc.State(wc_dir, { |
| 'A_COPY/D/H/psi' : Item(verb='Sending'), |
| 'A_COPY/D/G/rho' : Item(verb='Sending'), |
| 'A_COPY/B/E/beta' : Item(verb='Sending'), |
| 'A_COPY/D/H/omega' : Item(verb='Sending'), |
| 'A_COPY' : Item(verb='Sending'), |
| }) |
| expected_status.tweak('A_COPY', 'A_COPY/D/H/psi', 'A_COPY/D/G/rho', |
| 'A_COPY/B/E/beta', 'A_COPY/D/H/omega', wc_rev=8) |
| svntest.actions.run_and_verify_commit(wc_dir, expected_output, |
| expected_status, None, wc_dir) |
| |
| |
| # Update the wcs again. |
| # |
| # Note: this update had to be added because of r869016 (which was |
| # merged into the reintegrate branch in r869021). Without this |
| # update, the mergeinfo will not be inherited properly as part of |
| # the 'svn cp tau tauprime' step, and later (during the post-commit |
| # update, with the new expected_disk) we'll get an error like this: |
| # |
| # ============================================================= |
| # Expected 'tauprime' and actual 'tauprime' in disk tree are different! |
| # ============================================================= |
| # EXPECTED NODE TO BE: |
| # ============================================================= |
| # * Node name: tauprime |
| # Path: A_COPY/D/G/tauprime |
| # Contents: This is the file 'tau'. |
| # |
| # Properties: {'svn:mergeinfo': '/A/D/G/tau:2-7'} |
| # Attributes: {} |
| # Children: N/A (node is a file) |
| # ============================================================= |
| # ACTUAL NODE FOUND: |
| # ============================================================= |
| # * Node name: tauprime |
| # Path: G/tauprime |
| # Contents: This is the file 'tau'. |
| # |
| # Properties: {'svn:mergeinfo': ''} |
| # Attributes: {} |
| # Children: N/A (node is a file) |
| # |
| expected_output = wc.State(wc_dir, {}) |
| expected_status.tweak(wc_rev='8') |
| svntest.actions.run_and_verify_update(wc_dir, expected_output, |
| expected_disk, expected_status, |
| None, None, None, None, None, True) |
| |
| # Make another change on the branch: copy tau to tauprime. Commit |
| # in r9. |
| svntest.actions.run_and_verify_svn(None, None, [], 'cp', |
| sbox.repo_url + '/A_COPY/D/G/tau', |
| sbox.repo_url + '/A_COPY/D/G/tauprime', |
| '-m', |
| 'Repos to repos copy of tau to tauprime') |
| |
| # Update the trunk (well, the whole wc) to get the copy above and since |
| # reintegrate really wants a clean wc. |
| expected_output = wc.State(wc_dir, { |
| 'A_COPY/D/G/tauprime' : Item(verb='Adding') |
| }) |
| expected_output = wc.State(A_COPY_path, { |
| 'D/G/tauprime' : Item(status='A '), |
| }) |
| expected_status.add({'A_COPY/D/G/tauprime': Item(status=' ', wc_rev=9)}) |
| expected_disk.add({ |
| 'A_COPY/D/G/tauprime' : Item(props={SVN_PROP_MERGEINFO: '/A/D/G/tau:2-7'}, |
| contents="This is the file 'tau'.\n") |
| }) |
| expected_status.tweak(wc_rev='9') |
| svntest.actions.run_and_verify_update(wc_dir, expected_output, |
| expected_disk, expected_status, |
| None, None, None, None, None, True) |
| |
| # *finally*, actually run merge --reintegrate in trunk with the |
| # branch URL. This should bring in the mu change and the tauprime |
| # change. |
| A_path = os.path.join(wc_dir, "A") |
| expected_output = wc.State(A_path, { |
| 'mu' : Item(status='U '), |
| 'D/G/tauprime' : Item(status='A '), |
| }) |
| expected_mergeinfo_output = wc.State(A_path, { |
| '' : Item(status=' U'), |
| 'D/G/tauprime' : Item(status=' U'), |
| }) |
| expected_elision_output = wc.State(A_path, { |
| }) |
| k_expected_status = wc.State(A_path, { |
| "B" : Item(status=' ', wc_rev=9), |
| "B/lambda" : Item(status=' ', wc_rev=9), |
| "B/E" : Item(status=' ', wc_rev=9), |
| "B/E/alpha" : Item(status=' ', wc_rev=9), |
| "B/E/beta" : Item(status=' ', wc_rev=9), |
| "B/F" : Item(status=' ', wc_rev=9), |
| "mu" : Item(status='M ', wc_rev=9), |
| "C" : Item(status=' ', wc_rev=9), |
| "D" : Item(status=' ', wc_rev=9), |
| "D/gamma" : Item(status=' ', wc_rev=9), |
| "D/G" : Item(status=' ', wc_rev=9), |
| "D/G/pi" : Item(status=' ', wc_rev=9), |
| "D/G/rho" : Item(status=' ', wc_rev=9), |
| "D/G/tau" : Item(status=' ', wc_rev=9), |
| "D/G/tauprime" : Item(status='A ', wc_rev='-', copied='+'), |
| "D/H" : Item(status=' ', wc_rev=9), |
| "D/H/chi" : Item(status=' ', wc_rev=9), |
| "D/H/omega" : Item(status=' ', wc_rev=9), |
| "D/H/psi" : Item(status=' ', wc_rev=9), |
| "" : Item(status=' M', wc_rev=9), |
| }) |
| k_expected_disk.tweak('', props={SVN_PROP_MERGEINFO : '/A_COPY:2-9'}) |
| k_expected_disk.add({ |
| 'D/G/tauprime' : Item(props={SVN_PROP_MERGEINFO : |
| '/A/D/G/tau:2-7\n/A_COPY/D/G/tauprime:9'}, |
| contents="This is the file 'tau'.\n") |
| }) |
| expected_skip = wc.State(A_path, {}) |
| svntest.actions.run_and_verify_merge(A_path, None, None, |
| sbox.repo_url + '/A_COPY', None, |
| expected_output, |
| expected_mergeinfo_output, |
| expected_elision_output, |
| k_expected_disk, |
| k_expected_status, |
| expected_skip, |
| None, None, None, None, |
| None, True, True, |
| '--reintegrate', A_path) |
| |
| # Finally, commit the result of the merge (r10). |
| expected_output = wc.State(wc_dir, { |
| 'A/D/G/tauprime' : Item(verb='Adding'), |
| 'A/mu' : Item(verb='Sending'), |
| 'A' : Item(verb='Sending'), |
| }) |
| expected_status.add({ |
| 'A/D/G/tauprime' : Item(status=' ', wc_rev=10), |
| }) |
| expected_status.tweak('A', 'A/mu', wc_rev=10) |
| svntest.actions.run_and_verify_commit(wc_dir, expected_output, |
| expected_status, None, wc_dir) |
| |
| #---------------------------------------------------------------------- |
| def reintegrate_branch_never_merged_to(sbox): |
| "merge --reintegrate on a never-updated branch" |
| |
| # Make A_COPY branch in r2, and do a few more commits to A in r3-6. |
| sbox.build() |
| wc_dir = sbox.wc_dir |
| expected_disk, expected_status = set_up_branch(sbox) |
| |
| # Make a change on the branch, to A_COPY/mu. Commit in r7. |
| svntest.main.file_write(os.path.join(wc_dir, "A_COPY", "mu"), |
| "Changed on the branch.") |
| expected_output = wc.State(wc_dir, {'A_COPY/mu' : Item(verb='Sending')}) |
| expected_status.tweak('A_COPY/mu', wc_rev=7) |
| svntest.actions.run_and_verify_commit(wc_dir, expected_output, |
| expected_status, None, wc_dir) |
| expected_disk.tweak('A_COPY/mu', contents='Changed on the branch.') |
| |
| # Update the wcs. |
| expected_output = wc.State(wc_dir, {}) |
| expected_status.tweak(wc_rev='7') |
| svntest.actions.run_and_verify_update(wc_dir, expected_output, |
| expected_disk, expected_status, |
| None, None, None, None, None, True) |
| |
| # Make another change on the branch: copy tau to tauprime. Commit |
| # in r8. |
| svntest.actions.run_and_verify_svn(None, None, [], 'cp', |
| os.path.join(wc_dir, 'A_COPY', 'D', 'G', |
| 'tau'), |
| os.path.join(wc_dir, 'A_COPY', 'D', 'G', |
| 'tauprime')) |
| expected_output = wc.State(wc_dir, { |
| 'A_COPY/D/G/tauprime' : Item(verb='Adding') |
| }) |
| expected_status.add({'A_COPY/D/G/tauprime': Item(status=' ', wc_rev=8)}) |
| svntest.actions.run_and_verify_commit(wc_dir, expected_output, |
| expected_status, None, wc_dir) |
| expected_disk.add({ |
| 'A_COPY/D/G/tauprime' : Item(contents="This is the file 'tau'.\n") |
| }) |
| |
| # Update the trunk (well, the whole wc) (since reintegrate really |
| # wants a clean wc). |
| expected_output = wc.State(wc_dir, {}) |
| expected_status.tweak(wc_rev='8') |
| svntest.actions.run_and_verify_update(wc_dir, expected_output, |
| expected_disk, expected_status, |
| None, None, None, None, None, True) |
| |
| # *finally*, actually run merge --reintegrate in trunk with the |
| # branch URL. This should bring in the mu change and the tauprime |
| # change. |
| A_path = os.path.join(wc_dir, "A") |
| expected_output = wc.State(A_path, { |
| 'mu' : Item(status='U '), |
| 'D/G/tauprime' : Item(status='A '), |
| }) |
| expected_mergeinfo_output = wc.State(A_path, { |
| '' : Item(status=' U'), |
| }) |
| expected_elision_output = wc.State(A_path, { |
| }) |
| k_expected_status = wc.State(A_path, { |
| "B" : Item(status=' ', wc_rev=8), |
| "B/lambda" : Item(status=' ', wc_rev=8), |
| "B/E" : Item(status=' ', wc_rev=8), |
| "B/E/alpha" : Item(status=' ', wc_rev=8), |
| "B/E/beta" : Item(status=' ', wc_rev=8), |
| "B/F" : Item(status=' ', wc_rev=8), |
| "mu" : Item(status='M ', wc_rev=8), |
| "C" : Item(status=' ', wc_rev=8), |
| "D" : Item(status=' ', wc_rev=8), |
| "D/gamma" : Item(status=' ', wc_rev=8), |
| "D/G" : Item(status=' ', wc_rev=8), |
| "D/G/pi" : Item(status=' ', wc_rev=8), |
| "D/G/rho" : Item(status=' ', wc_rev=8), |
| "D/G/tau" : Item(status=' ', wc_rev=8), |
| "D/G/tauprime" : Item(status='A ', wc_rev='-', copied='+'), |
| "D/H" : Item(status=' ', wc_rev=8), |
| "D/H/chi" : Item(status=' ', wc_rev=8), |
| "D/H/omega" : Item(status=' ', wc_rev=8), |
| "D/H/psi" : Item(status=' ', wc_rev=8), |
| "" : Item(status=' M', wc_rev=8), |
| }) |
| k_expected_disk = wc.State('', { |
| '' : Item(props={SVN_PROP_MERGEINFO : '/A_COPY:2-8'}), |
| 'B' : Item(), |
| 'B/lambda' : Item("This is the file 'lambda'.\n"), |
| 'B/E' : Item(), |
| 'B/E/alpha' : Item("This is the file 'alpha'.\n"), |
| 'B/E/beta' : Item("New content"), |
| 'B/F' : Item(), |
| 'mu' : Item("Changed on the branch."), |
| 'C' : Item(), |
| 'D' : Item(), |
| 'D/gamma' : Item("This is the file 'gamma'.\n"), |
| 'D/G' : Item(), |
| 'D/G/pi' : Item("This is the file 'pi'.\n"), |
| 'D/G/rho' : Item("New content"), |
| 'D/G/tau' : Item("This is the file 'tau'.\n"), |
| 'D/G/tauprime' : Item("This is the file 'tau'.\n"), |
| 'D/H' : Item(), |
| 'D/H/chi' : Item("This is the file 'chi'.\n"), |
| 'D/H/omega' : Item("New content"), |
| 'D/H/psi' : Item("New content"), |
| }) |
| expected_skip = wc.State(A_path, {}) |
| svntest.actions.run_and_verify_merge(A_path, None, None, |
| sbox.repo_url + '/A_COPY', None, |
| expected_output, |
| expected_mergeinfo_output, |
| expected_elision_output, |
| k_expected_disk, |
| k_expected_status, |
| expected_skip, |
| None, None, None, None, |
| None, True, True, |
| '--reintegrate', A_path) |
| |
| # Finally, commit the result of the merge (r9). |
| expected_output = wc.State(wc_dir, { |
| 'A/D/G/tauprime' : Item(verb='Adding'), |
| 'A/mu' : Item(verb='Sending'), |
| 'A' : Item(verb='Sending'), |
| }) |
| expected_status.add({ |
| 'A/D/G/tauprime' : Item(status=' ', wc_rev=9), |
| }) |
| expected_status.tweak('A', 'A/mu', wc_rev=9) |
| svntest.actions.run_and_verify_commit(wc_dir, expected_output, |
| expected_status, None, wc_dir) |
| |
| #---------------------------------------------------------------------- |
| def reintegrate_fail_on_modified_wc(sbox): |
| "merge --reintegrate should fail in modified wc" |
| sbox.build() |
| wc_dir = sbox.wc_dir |
| A_path = os.path.join(wc_dir, "A") |
| mu_path = os.path.join(A_path, "mu") |
| ignored_expected_disk, ignored_expected_status = set_up_branch(sbox) |
| svntest.main.file_write(mu_path, "Changed on 'trunk' (the merge target).") |
| sbox.simple_update() # avoid mixed-revision error |
| svntest.actions.run_and_verify_merge( |
| A_path, None, None, sbox.repo_url + '/A_COPY', None, None, None, None, |
| None, None, None, |
| ".*Cannot merge into a working copy that has local modifications.*", |
| None, None, None, None, True, False, '--reintegrate', A_path) |
| |
| #---------------------------------------------------------------------- |
| def reintegrate_fail_on_mixed_rev_wc(sbox): |
| "merge --reintegrate should fail in mixed-rev wc" |
| sbox.build() |
| wc_dir = sbox.wc_dir |
| A_path = os.path.join(wc_dir, "A") |
| mu_path = os.path.join(A_path, "mu") |
| ignored_expected_disk, expected_status = set_up_branch(sbox) |
| # Make and commit a change, in order to get a mixed-rev wc. |
| svntest.main.file_write(mu_path, "Changed on 'trunk' (the merge target).") |
| expected_output = wc.State(wc_dir, { |
| 'A/mu' : Item(verb='Sending'), |
| }) |
| expected_status.tweak('A/mu', wc_rev=7) |
| svntest.actions.run_and_verify_commit(wc_dir, expected_output, |
| expected_status, None, wc_dir) |
| # Try merging into that same wc, expecting failure. |
| svntest.actions.run_and_verify_merge( |
| A_path, None, None, sbox.repo_url + '/A_COPY', None, None, None, None, |
| None, None, None, |
| ".*Cannot merge into mixed-revision working copy.*", |
| None, None, None, None, True, False, '--reintegrate', A_path) |
| |
| #---------------------------------------------------------------------- |
| def reintegrate_fail_on_switched_wc(sbox): |
| "merge --reintegrate should fail in switched wc" |
| sbox.build() |
| wc_dir = sbox.wc_dir |
| A_path = os.path.join(wc_dir, "A") |
| G_path = os.path.join(A_path, "D", "G") |
| switch_url = sbox.repo_url + "/A/D/H" |
| expected_disk, expected_status = set_up_branch(sbox) |
| |
| # Switch a subdir of the target. |
| 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/chi' : Item(status='A '), |
| 'A/D/G/psi' : Item(status='A '), |
| 'A/D/G/omega' : Item(status='A '), |
| }) |
| expected_disk.remove('A/D/G/pi', 'A/D/G/rho', 'A/D/G/tau') |
| expected_disk.add({ |
| 'A/D/G/chi' : Item(contents="This is the file 'chi'.\n"), |
| 'A/D/G/psi' : Item(contents="New content"), |
| 'A/D/G/omega' : Item(contents="New content"), |
| }) |
| expected_status.remove('A/D/G/pi', 'A/D/G/rho', 'A/D/G/tau') |
| expected_status.add({ |
| 'A/D/G' : Item(status=' ', wc_rev=6, switched='S'), |
| 'A/D/G/chi' : Item(status=' ', wc_rev=6), |
| 'A/D/G/psi' : Item(status=' ', wc_rev=6), |
| 'A/D/G/omega' : Item(status=' ', wc_rev=6), |
| }) |
| svntest.actions.run_and_verify_switch(wc_dir, |
| G_path, |
| switch_url, |
| expected_output, |
| expected_disk, |
| expected_status, |
| None, None, None, None, None, |
| False, '--ignore-ancestry') |
| sbox.simple_update() # avoid mixed-revision error |
| svntest.actions.run_and_verify_merge( |
| A_path, None, None, sbox.repo_url + '/A_COPY', None, None, None, None, |
| None, None, None, |
| ".*Cannot merge into a working copy with a switched subtree.*", |
| None, None, None, None, True, False, '--reintegrate', A_path) |
| |
| #---------------------------------------------------------------------- |
| # Test for issue #3603 'allow reintegrate merges into WCs with |
| # missing subtrees'. |
| @Issue(3603) |
| def reintegrate_on_shallow_wc(sbox): |
| "merge --reintegrate in shallow wc" |
| |
| # Create a standard greek tree, branch A to A_COPY in r2. |
| sbox.build() |
| wc_dir = sbox.wc_dir |
| expected_disk, expected_status = set_up_branch(sbox, branch_only = True) |
| |
| # Some paths we'll care about |
| A_path = os.path.join(wc_dir, "A") |
| A_D_path = os.path.join(wc_dir, "A", "D") |
| mu_COPY_path = os.path.join(wc_dir, "A_COPY", "mu") |
| psi_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "H", "psi") |
| A_COPY_path = os.path.join(wc_dir, "A_COPY") |
| |
| # r3 - Make a change on the A_COPY branch that will be |
| # reintegrated back to A. |
| svntest.main.file_write(mu_COPY_path, "branch work") |
| svntest.main.run_svn(None, 'commit', '-m', |
| 'Some work on the A_COPY branch', wc_dir) |
| |
| # First try a reintegrate where the target WC has a shallow subtree |
| # that is not affected by the reintegrate. In this case we set the |
| # depth of A/D to empty. Since the only change made on the branch |
| # since the branch point is to A_COPY/mu, the reintegrate should |
| # simply work and update A/mu with the branch's contents. |
| svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir) |
| svntest.actions.run_and_verify_svn(None, None, [], 'up', |
| '--set-depth', 'empty', A_D_path) |
| expected_output = wc.State(A_path, { |
| 'mu' : Item(status='U '), |
| }) |
| expected_mergeinfo_output = wc.State(A_path, { |
| '' : Item(status=' U'), |
| }) |
| expected_elision_output = wc.State(A_path, { |
| }) |
| expected_A_status = wc.State(A_path, { |
| '' : Item(status=' M'), |
| 'B' : Item(status=' '), |
| 'mu' : Item(status='M '), |
| 'B/E' : Item(status=' '), |
| 'B/E/alpha' : Item(status=' '), |
| 'B/E/beta' : Item(status=' '), |
| 'B/lambda' : Item(status=' '), |
| 'B/F' : Item(status=' '), |
| 'C' : Item(status=' '), |
| 'D' : Item(status=' '), # Don't expect anything under D, |
| # its depth is empty! |
| }) |
| expected_A_status.tweak(wc_rev=3) |
| expected_A_disk = wc.State('', { |
| '' : Item(props={SVN_PROP_MERGEINFO : '/A_COPY:2-3'}), |
| 'B' : Item(), |
| 'mu' : Item("branch work"), |
| 'B/E' : Item(), |
| 'B/E/alpha' : Item("This is the file 'alpha'.\n"), |
| 'B/E/beta' : Item("This is the file 'beta'.\n"), |
| 'B/lambda' : Item("This is the file 'lambda'.\n"), |
| 'B/F' : Item(), |
| 'C' : Item(), |
| 'D' : Item(), # Don't expect anything under D, its depth is empty! |
| }) |
| expected_A_skip = wc.State(A_path, {}) |
| svntest.actions.run_and_verify_merge(A_path, None, None, |
| sbox.repo_url + '/A_COPY', None, |
| expected_output, |
| expected_mergeinfo_output, |
| expected_elision_output, |
| expected_A_disk, |
| expected_A_status, |
| expected_A_skip, |
| None, None, None, None, |
| None, 1, 1, "--reintegrate", A_path) |
| |
| # Now revert the reintegrate and make a second change on the |
| # branch in r4, but this time change a subtree that corresponds |
| # to the missing (shallow) portion of the source. The reintegrate |
| # should still succeed, albeit skipping some paths. |
| svntest.actions.run_and_verify_svn(None, None, [], 'revert', '-R', wc_dir) |
| svntest.main.file_write(psi_COPY_path, "more branch work") |
| svntest.main.run_svn(None, 'commit', '-m', |
| 'Some more work on the A_COPY branch', wc_dir) |
| # Reuse the same expectations as the prior merge, except that |
| # non-inheritable mergeinfo is set on the root of the missing subtree... |
| expected_mergeinfo_output.add({ |
| 'D' : Item(status=' U') |
| }) |
| expected_A_status.tweak('D', status=' M') |
| expected_A_disk.tweak('D', props={SVN_PROP_MERGEINFO : '/A_COPY/D:2-4*'}) |
| # ... a depth-restricted item is skipped ... |
| expected_A_skip.add({ |
| 'D/H' : Item() |
| }) |
| # ... and the mergeinfo on the target root includes the latest rev on the branch. |
| expected_A_disk.tweak('', props={SVN_PROP_MERGEINFO : '/A_COPY:2-4'}) |
| svntest.actions.run_and_verify_merge(A_path, None, None, |
| sbox.repo_url + '/A_COPY', None, |
| expected_output, |
| expected_mergeinfo_output, |
| expected_elision_output, |
| expected_A_disk, |
| expected_A_status, |
| expected_A_skip, |
| None, None, None, None, |
| None, 1, 1, "--reintegrate", A_path) |
| |
| #---------------------------------------------------------------------- |
| @SkipUnless(server_has_mergeinfo) |
| def reintegrate_fail_on_stale_source(sbox): |
| "merge --reintegrate should fail on stale source" |
| sbox.build() |
| wc_dir = sbox.wc_dir |
| expected_disk, expected_status = set_up_branch(sbox) |
| A_path = os.path.join(wc_dir, "A") |
| mu_path = os.path.join(A_path, "mu") |
| svntest.main.file_append(mu_path, 'some text appended to mu\n') |
| svntest.actions.run_and_verify_svn(None, None, [], 'commit', |
| '-m', 'a change to mu', mu_path); |
| # Unmix the revisions in the working copy. |
| svntest.actions.run_and_verify_svn(None, None, [], 'update', wc_dir); |
| # The merge --reintegrate succeeds but since there were no changes |
| # on A_COPY after it was branched the only result is updated mergeinfo |
| # on the reintegrate target. |
| expected_output = wc.State(A_path, {}) |
| expected_mergeinfo_output = wc.State(A_path, { |
| '' : Item(status=' U'), |
| }) |
| expected_elision_output = wc.State(A_path, { |
| }) |
| expected_status = wc.State(A_path, { |
| '' : Item(status=' M'), |
| 'B' : Item(status=' '), |
| 'mu' : Item(status=' '), |
| 'B/E' : Item(status=' '), |
| 'B/E/alpha' : Item(status=' '), |
| 'B/E/beta' : Item(status=' '), |
| 'B/lambda' : Item(status=' '), |
| 'B/F' : Item(status=' '), |
| 'C' : Item(status=' '), |
| 'D' : Item(status=' '), |
| 'D/G' : Item(status=' '), |
| 'D/G/pi' : Item(status=' '), |
| 'D/G/rho' : Item(status=' '), |
| 'D/G/tau' : Item(status=' '), |
| 'D/gamma' : Item(status=' '), |
| 'D/H' : Item(status=' '), |
| 'D/H/chi' : Item(status=' '), |
| 'D/H/psi' : Item(status=' '), |
| 'D/H/omega' : Item(status=' '), |
| }) |
| expected_status.tweak(wc_rev=7) |
| expected_disk = wc.State('', { |
| '' : Item(props={SVN_PROP_MERGEINFO : '/A_COPY:2-7'}), |
| 'B' : Item(), |
| 'mu' : Item("This is the file 'mu'.\nsome text appended to mu\n"), |
| 'B/E' : Item(), |
| 'B/E/alpha' : Item("This is the file 'alpha'.\n"), |
| 'B/E/beta' : Item("New content"), |
| 'B/lambda' : Item("This is the file 'lambda'.\n"), |
| 'B/F' : Item(), |
| 'C' : Item(), |
| 'D' : Item(), |
| 'D/G' : Item(), |
| 'D/G/pi' : Item("This is the file 'pi'.\n"), |
| 'D/G/rho' : Item("New content"), |
| 'D/G/tau' : Item("This is the file 'tau'.\n"), |
| 'D/gamma' : Item("This is the file 'gamma'.\n"), |
| 'D/H' : Item(), |
| 'D/H/chi' : Item("This is the file 'chi'.\n"), |
| 'D/H/psi' : Item("New content"), |
| 'D/H/omega' : Item("New content"), |
| }) |
| expected_skip = wc.State(A_path, { }) |
| svntest.actions.run_and_verify_merge(A_path, None, None, |
| sbox.repo_url + '/A_COPY', None, |
| expected_output, |
| expected_mergeinfo_output, |
| expected_elision_output, |
| expected_disk, |
| expected_status, |
| expected_skip, |
| [], None, None, None, None, True, True, |
| '--reintegrate', A_path) |
| |
| #---------------------------------------------------------------------- |
| def merge_file_with_space_in_its_path(sbox): |
| "merge a file with space in its path" |
| |
| sbox.build() |
| wc_dir = sbox.wc_dir |
| some_dir = os.path.join(wc_dir, "some dir") |
| file1 = os.path.join(some_dir, "file1") |
| file2 = os.path.join(some_dir, "file2") |
| |
| # Make r2. |
| os.mkdir(some_dir) |
| svntest.main.file_append(file1, "Initial text in the file.\n") |
| svntest.main.run_svn(None, "add", some_dir) |
| svntest.actions.run_and_verify_svn(None, None, [], |
| "ci", "-m", "r2", wc_dir) |
| |
| # Make r3. |
| svntest.main.run_svn(None, "copy", file1, file2) |
| svntest.actions.run_and_verify_svn(None, None, [], |
| "ci", "-m", "r3", wc_dir) |
| |
| # Make r4. |
| svntest.main.file_append(file2, "Next line of text in the file.\n") |
| svntest.actions.run_and_verify_svn(None, None, [], |
| "ci", "-m", "r4", wc_dir) |
| |
| target_url = sbox.repo_url + '/some%20dir/file2' |
| svntest.actions.run_and_verify_svn(None, None, [], |
| "merge", "--reintegrate", target_url, |
| file1) |
| |
| #---------------------------------------------------------------------- |
| @SkipUnless(server_has_mergeinfo) |
| def reintegrate_with_subtree_mergeinfo(sbox): |
| "merge --reintegrate with subtree mergeinfo" |
| |
| # Create a standard greek tree, branch A to A_COPY in r2, A to A_COPY_2 in |
| # r3, A to A_COPY_3 in r3, and then make some changes under A in r5-8. |
| sbox.build() |
| wc_dir = sbox.wc_dir |
| expected_disk, expected_status = set_up_branch(sbox, False, 3) |
| |
| # Some paths we'll care about |
| gamma_COPY_3_path = os.path.join(wc_dir, "A_COPY_3", "D", "gamma") |
| D_path = os.path.join(wc_dir, "A", "D") |
| gamma_path = os.path.join(wc_dir, "A", "D", "gamma") |
| mu_COPY_2_path = os.path.join(wc_dir, "A_COPY_2", "mu") |
| mu_path = os.path.join(wc_dir, "A", "mu") |
| mu_COPY_path = os.path.join(wc_dir, "A_COPY", "mu") |
| A_COPY_path = os.path.join(wc_dir, "A_COPY") |
| D_COPY_path = os.path.join(wc_dir, "A_COPY") |
| beta_COPY_path = os.path.join(wc_dir, "A_COPY", "B", "E", "beta") |
| gamma_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "gamma") |
| gamma_moved_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "gamma_moved") |
| gamma_moved_path = os.path.join(wc_dir, "A", "D", "gamma_moved") |
| rho_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "G", "rho") |
| omega_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "H", "omega") |
| psi_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "H", "psi") |
| D_COPY_path = os.path.join(wc_dir, "A_COPY", "D") |
| alpha_COPY_path = os.path.join(wc_dir, "A_COPY", "B", "E", "alpha") |
| A_path = os.path.join(wc_dir, "A") |
| |
| # Now set up a situation where we try to reintegrate A_COPY back to A but |
| # both of these paths have subtree mergeinfo. Iff the mergeinfo on A_COPY |
| # reflects that the same revisions have been applied across all of A_COPY, |
| # then the reintegrate merge should succeed. |
| # |
| # r9 - Make a text change to A_COPY_3/D/gamma |
| svntest.main.file_write(gamma_COPY_3_path, "New content") |
| expected_output = wc.State(wc_dir, {'A_COPY_3/D/gamma' : Item(verb='Sending')}) |
| expected_status.tweak('A_COPY_3/D/gamma', wc_rev=9) |
| svntest.actions.run_and_verify_commit(wc_dir, expected_output, |
| expected_status, None, wc_dir) |
| expected_disk.tweak('A_COPY_3/D/gamma', contents="New content") |
| |
| # r10 - Merge r9 from A_COPY_3/D to A/D, creating explicit subtree |
| # mergeinfo under A. For this and every subsequent merge we update the WC |
| # first to allow full inheritance and elision. |
| svntest.actions.run_and_verify_svn(None, exp_noop_up_out(9), [], 'up', |
| wc_dir) |
| expected_status.tweak(wc_rev=9) |
| svntest.actions.run_and_verify_svn( |
| None, |
| expected_merge_output([[9]], |
| ['U ' + gamma_path + '\n', |
| ' U ' + D_path + '\n',]), |
| [], 'merge', '-c9', sbox.repo_url + '/A_COPY_3/D', D_path) |
| expected_output = wc.State(wc_dir, |
| {'A/D' : Item(verb='Sending'), |
| 'A/D/gamma' : Item(verb='Sending')}) |
| expected_status.tweak('A/D', 'A/D/gamma', wc_rev=10) |
| svntest.actions.run_and_verify_commit(wc_dir, expected_output, |
| expected_status, None, wc_dir) |
| expected_disk.tweak('A/D/gamma', contents="New content") |
| expected_disk.tweak('A/D', props={SVN_PROP_MERGEINFO : '/A_COPY_3/D:9'}) |
| |
| # r11 - Make a text change to A_COPY_2/mu |
| svntest.main.file_write(mu_COPY_2_path, "New content") |
| expected_output = wc.State(wc_dir, {'A_COPY_2/mu' : Item(verb='Sending')}) |
| expected_status.tweak('A_COPY_2/mu', wc_rev=11) |
| svntest.actions.run_and_verify_commit(wc_dir, expected_output, |
| expected_status, None, wc_dir) |
| expected_disk.tweak('A_COPY_2/mu', contents="New content") |
| |
| # r12 - Merge r11 from A_COPY_2/mu to A_COPY/mu |
| svntest.actions.run_and_verify_svn(None, exp_noop_up_out(11), [], 'up', |
| wc_dir) |
| expected_status.tweak(wc_rev=11) |
| svntest.actions.run_and_verify_svn( |
| None, |
| expected_merge_output([[11]], |
| ['U ' + mu_COPY_path + '\n', |
| ' U ' + mu_COPY_path + '\n',]), |
| [], 'merge', '-c11', sbox.repo_url + '/A_COPY_2/mu', mu_COPY_path) |
| expected_output = wc.State(wc_dir, |
| {'A_COPY/mu' : Item(verb='Sending')}) |
| expected_status.tweak('A_COPY/mu', wc_rev=12) |
| svntest.actions.run_and_verify_commit(wc_dir, expected_output, |
| expected_status, None, wc_dir) |
| expected_disk.tweak('A_COPY/mu', contents="New content") |
| |
| # r13 - Do a 'synch' cherry harvest merge of all available revisions |
| # from A to A_COPY |
| svntest.actions.run_and_verify_svn(None, exp_noop_up_out(12), [], 'up', |
| wc_dir) |
| expected_status.tweak(wc_rev=12) |
| svntest.actions.run_and_verify_svn( |
| None, |
| expected_merge_output([[2,12]], |
| ['U ' + beta_COPY_path + '\n', |
| 'U ' + gamma_COPY_path + '\n', |
| 'U ' + rho_COPY_path + '\n', |
| 'U ' + omega_COPY_path + '\n', |
| 'U ' + psi_COPY_path + '\n', |
| ' U ' + A_COPY_path + '\n', |
| ' U ' + D_COPY_path + '\n', |
| ' G ' + D_COPY_path + '\n',]), |
| [], 'merge', sbox.repo_url + '/A', A_COPY_path) |
| expected_output = wc.State(wc_dir, |
| {'A_COPY' : Item(verb='Sending'), |
| #'A_COPY/mu' : Item(verb='Sending'), |
| 'A_COPY/B/E/beta' : Item(verb='Sending'), |
| 'A_COPY/D' : Item(verb='Sending'), |
| 'A_COPY/D/G/rho' : Item(verb='Sending'), |
| 'A_COPY/D/H/omega' : Item(verb='Sending'), |
| 'A_COPY/D/H/psi' : Item(verb='Sending'), |
| 'A_COPY/D/gamma' : Item(verb='Sending')}) |
| expected_status.tweak('A_COPY', |
| #'A_COPY/mu', |
| 'A_COPY/B/E/beta', |
| 'A_COPY/D', |
| 'A_COPY/D/G/rho', |
| 'A_COPY/D/H/omega', |
| 'A_COPY/D/H/psi', |
| 'A_COPY/D/gamma', |
| wc_rev=13) |
| svntest.actions.run_and_verify_commit(wc_dir, expected_output, |
| expected_status, None, wc_dir) |
| expected_disk.tweak('A_COPY/B/E/beta', |
| 'A_COPY/D', |
| 'A_COPY/D/G/rho', |
| 'A_COPY/D/H/omega', |
| 'A_COPY/D/H/psi', |
| 'A_COPY/D/gamma', |
| contents="New content") |
| expected_disk.tweak('A_COPY', props={SVN_PROP_MERGEINFO : '/A:2-12'}) |
| expected_disk.tweak('A_COPY/D', |
| props={SVN_PROP_MERGEINFO : '/A/D:2-12\n/A_COPY_3/D:9\n'}) |
| |
| # r14 - Make a text change on A_COPY/B/E/alpha |
| svntest.main.file_write(alpha_COPY_path, "New content") |
| expected_output = wc.State(wc_dir, {'A_COPY/B/E/alpha' : Item(verb='Sending')}) |
| expected_status.tweak('A_COPY/B/E/alpha', wc_rev=14) |
| svntest.actions.run_and_verify_commit(wc_dir, expected_output, |
| expected_status, None, wc_dir) |
| expected_disk.tweak('A_COPY/B/E/alpha', contents="New content") |
| |
| # Now, reintegrate A_COPY to A. This should succeed. |
| svntest.actions.run_and_verify_svn(None, exp_noop_up_out(14), [], 'up', |
| wc_dir) |
| expected_status.tweak(wc_rev=14) |
| expected_output = wc.State(A_path, { |
| 'B/E/alpha' : Item(status='U '), |
| 'mu' : Item(status='UU'), |
| 'D' : Item(status=' U'), |
| }) |
| expected_mergeinfo_output = wc.State(A_path, { |
| '' : Item(status=' U'), |
| 'mu' : Item(status=' G'), |
| 'D' : Item(status=' U'), |
| }) |
| expected_elision_output = wc.State(A_path, { |
| }) |
| expected_A_status = wc.State(A_path, { |
| '' : Item(status=' M'), |
| 'B' : Item(status=' '), |
| 'mu' : Item(status='MM'), |
| 'B/E' : Item(status=' '), |
| 'B/E/alpha' : Item(status='M '), |
| 'B/E/beta' : Item(status=' '), |
| 'B/lambda' : Item(status=' '), |
| 'B/F' : Item(status=' '), |
| 'C' : Item(status=' '), |
| 'D' : Item(status=' M'), |
| 'D/G' : Item(status=' '), |
| 'D/G/pi' : Item(status=' '), |
| 'D/G/rho' : Item(status=' '), |
| 'D/G/tau' : Item(status=' '), |
| 'D/gamma' : Item(status=' '), |
| 'D/H' : Item(status=' '), |
| 'D/H/chi' : Item(status=' '), |
| 'D/H/psi' : Item(status=' '), |
| 'D/H/omega' : Item(status=' '), |
| }) |
| expected_A_status.tweak(wc_rev=14) |
| expected_A_disk = wc.State('', { |
| '' : Item(props={SVN_PROP_MERGEINFO : '/A_COPY:2-14'}), |
| 'B' : Item(), |
| 'mu' : Item("New content", |
| props={SVN_PROP_MERGEINFO : |
| '/A_COPY/mu:2-14\n/A_COPY_2/mu:11'}), |
| 'B/E' : Item(), |
| 'B/E/alpha' : Item("New content"), |
| 'B/E/beta' : Item("New content"), |
| 'B/lambda' : Item("This is the file 'lambda'.\n"), |
| 'B/F' : Item(), |
| 'C' : Item(), |
| 'D' : Item(props= |
| {SVN_PROP_MERGEINFO : '/A_COPY/D:2-14\n/A_COPY_3/D:9'}), |
| 'D/G' : Item(), |
| 'D/G/pi' : Item("This is the file 'pi'.\n"), |
| 'D/G/rho' : Item("New content"), |
| 'D/G/tau' : Item("This is the file 'tau'.\n"), |
| 'D/gamma' : Item("New content"), |
| 'D/H' : Item(), |
| 'D/H/chi' : Item("This is the file 'chi'.\n"), |
| 'D/H/psi' : Item("New content"), |
| 'D/H/omega' : Item("New content"), |
| }) |
| expected_A_skip = wc.State(A_COPY_path, {}) |
| svntest.actions.run_and_verify_merge(A_path, None, None, |
| sbox.repo_url + '/A_COPY', None, |
| expected_output, |
| expected_mergeinfo_output, |
| expected_elision_output, |
| expected_A_disk, |
| expected_A_status, |
| expected_A_skip, |
| None, None, None, None, |
| None, 1, 1, "--reintegrate", A_path) |
| |
| # Make some more changes to A_COPY so that the same revisions have *not* |
| # been uniformly applied from A to A_COPY. In this case the reintegrate |
| # merge should fail, but should provide a helpful message as to where the |
| # problems are. |
| # |
| # First revert the previous reintegrate merge |
| svntest.actions.run_and_verify_svn(None, None, [], |
| 'revert', '-R', wc_dir) |
| |
| # r15 - Reverse Merge r8 from A/D to A_COPY/D. |
| svntest.actions.run_and_verify_svn( |
| None, |
| expected_merge_output([[-8]], |
| ['U ' + omega_COPY_path + '\n', |
| ' U ' + D_COPY_path + '\n',]), |
| [], 'merge', '-c-8', sbox.repo_url + '/A/D', D_COPY_path) |
| expected_output = wc.State(wc_dir, |
| {'A_COPY/D' : Item(verb='Sending'), |
| 'A_COPY/D/H/omega' : Item(verb='Sending')}) |
| expected_status.tweak('A_COPY/D', 'A_COPY/D/H/omega', wc_rev=15) |
| svntest.actions.run_and_verify_commit(wc_dir, expected_output, |
| expected_status, None, wc_dir) |
| |
| # Now reintegrate A_COPY back to A. Since A_COPY/D no longer has r8 merged |
| # to it from A, the merge should fail. Further we expect an error message |
| # that highlights the fact that A_COPY/D is the offending subtree. |
| # |
| # The actions.run_and_verify_* methods are happy if one line of the error |
| # matches the regex, but we want to know that the error actually provides |
| # specific information about the paths that are stopping --reintegrate from |
| # working. So we will pass the stderr to svntest.verify.verify_outputs() |
| # ourselves, but as the 'actual_stdout' argument, that way each line of |
| # error must match the regex. |
| exit_code, out, err = svntest.actions.run_and_verify_svn( |
| None, [], svntest.verify.AnyOutput, |
| 'merge', '--reintegrate', sbox.repo_url + '/A_COPY', A_path) |
| |
| svntest.verify.verify_outputs("Reintegrate failed but not " |
| "in the way expected", |
| err, None, |
| "(svn: E195016: Reintegrate can only be used if " |
| "revisions 2 through 15 were previously " |
| "merged from .*/A to the reintegrate source, " |
| "but this is not the case:\n)" |
| "|( A_COPY/D\n)" |
| "|( Missing ranges: /A/D:8\n)" |
| "|( A_COPY/mu\n)" |
| "|( Missing ranges: /A/mu:2-12\n)" |
| "|(\n)" |
| "|(.*apr_err.*)", # In case of debug build |
| None, |
| True) # Match *all* lines of stdout |
| |
| # Test another common situation that can break reintegrate as a result |
| # of copies and moves: |
| # |
| # A) On our 'trunk' rename a subtree in such a way as the new |
| # subtree has explicit mergeinfo. Commit this rename as rev N. |
| # |
| # B) Synch merge the rename in A) to our 'branch' in rev N+1. The |
| # renamed subtree now has the same explicit mergeinfo on both |
| # the branch and trunk. |
| # |
| # C) Make some more changes on the renamed subtree in 'trunk' and |
| # commit in rev N+2. |
| # |
| # D) Synch merge the changes in C) from 'trunk' to 'branch' and commit in |
| # rev N+3. The renamed subtree on 'branch' now has additional explicit |
| # mergeinfo decribing the synch merge from trunk@N+1 to trunk@N+2. |
| # |
| # E) Reintegrate 'branch' to 'trunk'. This fails as it appears not all |
| # of 'trunk' was previously merged to 'branch' |
| |
| # r16 - A) REPOS-to-REPOS rename of A/D/gamma to A/D/gamma_moved. Since |
| # r874258 WC-to-WC moves won't create mergeinfo on the dest if the source |
| # doesn't have any. So do a repos-to-repos move so explicit mergeinfo |
| # *is* created on the destination. |
| svntest.actions.run_and_verify_svn(None, None,[], 'move', |
| sbox.repo_url + '/A/D/gamma', |
| sbox.repo_url + '/A/D/gamma_moved', |
| '-m', 'REPOS-to-REPOS move' |
| ) |
| svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir) |
| expected_disk.remove('A/D/gamma') |
| expected_disk.add({ |
| 'A/D/gamma_moved' : Item(props={SVN_PROP_MERGEINFO: '/A_COPY_3/D/gamma:9'}, |
| contents="New content") |
| }) |
| expected_status.tweak(wc_rev=16) |
| expected_status.remove('A/D/gamma') |
| expected_status.add({'A/D/gamma_moved' : Item(status=' ', wc_rev=16)}) |
| |
| # r17 - B) Synch merge from A to A_COPY |
| svntest.actions.run_and_verify_svn( |
| None, |
| expected_merge_output([[8], [13,16], [2,16]], |
| ['U ' + omega_COPY_path + '\n', |
| 'A ' + gamma_moved_COPY_path + '\n', |
| 'D ' + gamma_COPY_path + '\n', |
| ' U ' + A_COPY_path + '\n', |
| ' U ' + D_COPY_path + '\n', |
| ' U ' + gamma_moved_COPY_path + '\n']), |
| [], 'merge', sbox.repo_url + '/A', A_COPY_path) |
| expected_output = wc.State( |
| wc_dir, |
| {'A_COPY' : Item(verb='Sending'), # Mergeinfo update |
| 'A_COPY/D' : Item(verb='Sending'), # Mergeinfo update |
| 'A_COPY/D/gamma' : Item(verb='Deleting'), |
| 'A_COPY/D/gamma_moved' : Item(verb='Adding'), |
| 'A_COPY/D/H/omega' : Item(verb='Sending'), # Redoing r15's |
| # reverse merge of r8. |
| }) |
| expected_status.remove('A_COPY/D/gamma') |
| |
| expected_status.tweak('A_COPY', |
| 'A_COPY/D', |
| 'A_COPY/D/H/omega', |
| wc_rev=17) |
| expected_status.add({'A_COPY/D/gamma_moved' : Item(status=' ', wc_rev=17)}) |
| svntest.actions.run_and_verify_commit(wc_dir, expected_output, |
| expected_status, None, wc_dir) |
| expected_disk.remove('A_COPY/D/gamma') |
| expected_disk.add({ |
| 'A/D/gamma_moved' : Item(props={SVN_PROP_MERGEINFO: '/A_COPY_3/D/gamma:9'}, |
| contents="New content") |
| }) |
| |
| # r18 - C) Text mod to A/D/gamma_moved |
| svntest.main.file_write(gamma_moved_path, "Even newer content") |
| expected_output = wc.State(wc_dir, {'A/D/gamma_moved' : Item(verb='Sending')}) |
| expected_status.tweak('A/D/gamma_moved', wc_rev=18) |
| svntest.actions.run_and_verify_commit(wc_dir, expected_output, |
| expected_status, None, wc_dir) |
| |
| # r19 - D) Synch merge from A to A_COPY |
| svntest.actions.run_and_verify_svn( |
| None, |
| expected_merge_output([[17,18], [2,18]], |
| ['U ' + gamma_moved_COPY_path + '\n', |
| ' U ' + A_COPY_path + '\n', |
| ' U ' + D_COPY_path + '\n', |
| ' U ' + gamma_moved_COPY_path + '\n']), |
| [], 'merge', '--allow-mixed-revisions', sbox.repo_url + '/A', A_COPY_path) |
| expected_output = wc.State( |
| wc_dir, |
| {'A_COPY' : Item(verb='Sending'), # Mergeinfo update |
| 'A_COPY/D' : Item(verb='Sending'), # Mergeinfo update |
| 'A_COPY/D/gamma_moved' : Item(verb='Sending'), # Text change |
| }) |
| expected_status.tweak('A_COPY', |
| 'A_COPY/D', |
| 'A_COPY/D/gamma_moved', |
| wc_rev=19) |
| svntest.actions.run_and_verify_commit(wc_dir, expected_output, |
| expected_status, None, wc_dir) |
| |
| # Reintegrate A_COPY to A, this should work since |
| # A_COPY/D/gamma_moved's natural history, |
| # |
| # /A/D/gamma:1-15 |
| # /A/D/gamma_moved:16 |
| # /A_COPY/D/gamma_moved:17-19 |
| # |
| # shows that it is fully synched up with trunk. |
| svntest.actions.run_and_verify_svn(None, exp_noop_up_out(19), [], 'up', |
| wc_dir) |
| expected_output = wc.State(A_path, { |
| 'B/E/alpha' : Item(status='U '), |
| 'mu' : Item(status='UU'), |
| 'D' : Item(status=' U'), |
| 'D/gamma_moved' : Item(status=' U'), |
| }) |
| expected_mergeinfo_output = wc.State(A_path, { |
| '' : Item(status=' U'), |
| 'mu' : Item(status=' G'), |
| 'D' : Item(status=' U'), |
| 'D/gamma_moved' : Item(status=' U'), |
| }) |
| expected_elision_output = wc.State(A_path, { |
| }) |
| expected_A_status = wc.State(A_path, { |
| '' : Item(status=' M'), |
| 'B' : Item(status=' '), |
| 'mu' : Item(status='MM'), |
| 'B/E' : Item(status=' '), |
| 'B/E/alpha' : Item(status='M '), |
| 'B/E/beta' : Item(status=' '), |
| 'B/lambda' : Item(status=' '), |
| 'B/F' : Item(status=' '), |
| 'C' : Item(status=' '), |
| 'D' : Item(status=' M'), |
| 'D/G' : Item(status=' '), |
| 'D/G/pi' : Item(status=' '), |
| 'D/G/rho' : Item(status=' '), |
| 'D/G/tau' : Item(status=' '), |
| 'D/gamma_moved' : Item(status=' M'), |
| 'D/H' : Item(status=' '), |
| 'D/H/chi' : Item(status=' '), |
| 'D/H/psi' : Item(status=' '), |
| 'D/H/omega' : Item(status=' '), |
| }) |
| expected_A_status.tweak(wc_rev=19) |
| expected_A_disk = wc.State('', { |
| '' : Item(props={SVN_PROP_MERGEINFO : '/A_COPY:2-19'}), |
| 'B' : Item(), |
| 'mu' : Item("New content", |
| props={SVN_PROP_MERGEINFO : |
| '/A_COPY/mu:2-19\n/A_COPY_2/mu:11'}), |
| 'B/E' : Item(), |
| 'B/E/alpha' : Item("New content"), |
| 'B/E/beta' : Item("New content"), |
| 'B/lambda' : Item("This is the file 'lambda'.\n"), |
| 'B/F' : Item(), |
| 'C' : Item(), |
| 'D' : Item(props={SVN_PROP_MERGEINFO : |
| '/A_COPY/D:2-19\n/A_COPY_3/D:9'}), |
| 'D/G' : Item(), |
| 'D/G/pi' : Item("This is the file 'pi'.\n"), |
| 'D/G/rho' : Item("New content"), |
| 'D/G/tau' : Item("This is the file 'tau'.\n"), |
| # Why do we expect mergeinfo of '/A_COPY/D/G/tauprime:2-9' on |
| # A/D/G/tauprime? Because this --reintegrate merge is effectively a |
| # two URL merge of %URL%/A@9 %URL%/A_COPY@9 to 'A'. Since %URL%/A@9 and |
| # %URL%/A_COPY@9 have a common ancestor in %URL%/A@1 we expect this 2-URL |
| # merge to record mergeinfo and a component of that mergeinfo describes |
| # the merge of %URL%/A_COPY@2 to %URL%/A_COPY@9. We see that above on |
| # A. But we also get it on A's subtrees with explicit mergeinfo, namely |
| # A/D/G/tauprime. Now I know what you are thinking, "'A_COPY/D/G/tauprime' |
| # doesn't even exist until r9!", and you are quite right. But this |
| # inheritance of bogus mergeinfo is a known problem, see |
| # http://subversion.tigris.org/issues/show_bug.cgi?id=3157#desc8, |
| # and is not what this test is about, so we won't fail because of it. |
| 'D/gamma_moved' : Item( |
| "Even newer content", props={SVN_PROP_MERGEINFO : |
| '/A_COPY/D/gamma_moved:17-19\n' |
| '/A_COPY_3/D/gamma:9'}), |
| 'D/H' : Item(), |
| 'D/H/chi' : Item("This is the file 'chi'.\n"), |
| 'D/H/psi' : Item("New content"), |
| 'D/H/omega' : Item("New content"), |
| }) |
| expected_A_skip = wc.State(A_COPY_path, {}) |
| svntest.actions.run_and_verify_merge(A_path, None, None, |
| sbox.repo_url + '/A_COPY', None, |
| expected_output, |
| expected_mergeinfo_output, |
| expected_elision_output, |
| expected_A_disk, |
| expected_A_status, |
| expected_A_skip, |
| None, None, None, None, |
| None, 1, 1, "--reintegrate", A_path) |
| |
| #---------------------------------------------------------------------- |
| @SkipUnless(server_has_mergeinfo) |
| def multiple_reintegrates_from_the_same_branch(sbox): |
| "multiple reintegrates create self-referential" |
| |
| # Make A_COPY branch in r2, and do a few more commits to A in r3-6. |
| sbox.build() |
| wc_dir = sbox.wc_dir |
| expected_disk, expected_status = set_up_branch(sbox) |
| |
| # Some paths we'll care about |
| A_path = os.path.join(wc_dir, "A") |
| mu_path = os.path.join(wc_dir, "A", "mu") |
| A_COPY_path = os.path.join(wc_dir, "A_COPY") |
| psi_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "H", "psi") |
| Feature_branch_path = os.path.join(wc_dir, "A_FEATURE_BRANCH") |
| Feature_beta_path = os.path.join(wc_dir, "A_FEATURE_BRANCH", "B", "E", |
| "beta") |
| |
| # Create a feature branch and do multiple reintegrates from the branch |
| # without deleting and recreating it. We don't recommend doing this, |
| # but regardless, it shouldn't create self-referential mergeinfo on |
| # the reintegrate target. |
| # |
| # r7 - Create the feature branch. |
| svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir) |
| svntest.actions.run_and_verify_svn(None, None, [], |
| 'copy', A_path, Feature_branch_path) |
| svntest.actions.run_and_verify_svn(None, None, [], |
| 'ci', '-m', 'Make a feature branch', |
| wc_dir) |
| |
| # r8 - Make a change under 'A'. |
| svntest.main.file_write(mu_path, "New trunk content.\n") |
| svntest.actions.run_and_verify_svn(None, None, [], 'ci', '-m', |
| "A text change under 'A'", |
| wc_dir) |
| |
| # r9 - Make a change on the feature branch. |
| svntest.main.file_write(Feature_beta_path, "New branch content.\n") |
| svntest.actions.run_and_verify_svn(None, None, [], 'ci', '-m', |
| "A text change on the feature branch", |
| wc_dir) |
| |
| # r10 - Sync merge all changes from 'A' to the feature branch. |
| svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir) |
| svntest.actions.run_and_verify_svn(None, None, [], 'merge', |
| sbox.repo_url + '/A', |
| Feature_branch_path) |
| svntest.actions.run_and_verify_svn(None, None, [], 'ci', '-m', |
| "Sync merge 'A' to feature branch", |
| wc_dir) |
| |
| # r11 - Reintegrate the feature branch back to 'A'. |
| svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir) |
| svntest.actions.run_and_verify_svn(None, None, [], 'merge', '--reintegrate', |
| sbox.repo_url + '/A_FEATURE_BRANCH', |
| A_path) |
| svntest.actions.run_and_verify_svn(None, None, [], 'ci', '-m', |
| "Reintegrate feature branch back to 'A'", |
| wc_dir) |
| |
| # r12 - Do a --record-only merge from 'A' to the feature branch so we |
| # don't try to merge r11 from trunk during the next sync merge. |
| svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir) |
| svntest.actions.run_and_verify_svn(None, None, [], 'merge', |
| '--record-only', |
| sbox.repo_url + '/A', |
| Feature_branch_path) |
| svntest.actions.run_and_verify_svn(None, None, [], 'ci', '-m', |
| "Sync merge 'A' to feature branch", |
| wc_dir) |
| |
| # r13 - Make another change on the feature branch. |
| svntest.main.file_write(Feature_beta_path, "Even newer branch content.\n") |
| svntest.actions.run_and_verify_svn(None, None, [], 'ci', '-m', |
| "Different text on the feature branch", |
| wc_dir) |
| |
| # r14 - Sync merge all changes from 'A' to the feature branch in |
| # preparation for a second reintegrate from this branch. |
| svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir) |
| svntest.actions.run_and_verify_svn(None, None, [], 'merge', |
| sbox.repo_url + '/A', |
| Feature_branch_path) |
| svntest.actions.run_and_verify_svn(None, None, [], 'ci', '-m', |
| "2nd Sync merge 'A' to feature branch", |
| wc_dir) |
| |
| # r15 - Reintegrate the feature branch back to 'A' a second time. |
| # No self-referential mergeinfo should be applied on 'A'. |
| svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir) |
| expected_output = wc.State(A_path, { |
| #'' : Item(status=' U'), #<-- no self-referential mergeinfo applied! |
| 'B/E/beta' : Item(status='U '), |
| }) |
| expected_mergeinfo_output = wc.State(A_path, { |
| '' : Item(status=' U'), |
| }) |
| expected_elision_output = wc.State(A_path, { |
| }) |
| expected_status = wc.State(A_path, { |
| '' : Item(status=' M'), |
| 'B' : Item(status=' '), |
| 'mu' : Item(status=' '), |
| 'B/E' : Item(status=' '), |
| 'B/E/alpha' : Item(status=' '), |
| 'B/E/beta' : Item(status='M '), |
| 'B/lambda' : Item(status=' '), |
| 'B/F' : Item(status=' '), |
| 'C' : Item(status=' '), |
| 'D' : Item(status=' '), |
| 'D/G' : Item(status=' '), |
| 'D/G/pi' : Item(status=' '), |
| 'D/G/rho' : Item(status=' '), |
| 'D/G/tau' : Item(status=' '), |
| 'D/gamma' : Item(status=' '), |
| 'D/H' : Item(status=' '), |
| 'D/H/chi' : Item(status=' '), |
| 'D/H/psi' : Item(status=' '), |
| 'D/H/omega' : Item(status=' '), |
| }) |
| expected_status.tweak(wc_rev=14) |
| expected_disk = wc.State('', { |
| '' : Item(props={SVN_PROP_MERGEINFO : |
| # Prior to r????? we'd get this |
| # self-referential mergeinfo: |
| #'/A:2-6\n/A_FEATURE_BRANCH:7-14'}), |
| '/A_FEATURE_BRANCH:7-14'}), |
| 'B' : Item(), |
| 'mu' : Item("New trunk content.\n"), |
| 'B/E' : Item(), |
| 'B/E/alpha' : Item("This is the file 'alpha'.\n"), |
| 'B/E/beta' : Item("Even newer branch content.\n"), |
| 'B/lambda' : Item("This is the file 'lambda'.\n"), |
| 'B/F' : Item(), |
| 'C' : Item(), |
| 'D' : Item(), |
| 'D/G' : Item(), |
| 'D/G/pi' : Item("This is the file 'pi'.\n"), |
| 'D/G/rho' : Item("New content"), |
| 'D/G/tau' : Item("This is the file 'tau'.\n"), |
| 'D/gamma' : Item("This is the file 'gamma'.\n"), |
| 'D/H' : Item(), |
| 'D/H/chi' : Item("This is the file 'chi'.\n"), |
| 'D/H/psi' : Item("New content"), |
| 'D/H/omega' : Item("New content"), |
| }) |
| expected_skip = wc.State(A_path, { }) |
| svntest.actions.run_and_verify_merge(A_path, None, None, |
| sbox.repo_url + '/A_FEATURE_BRANCH', |
| None, |
| expected_output, |
| expected_mergeinfo_output, |
| expected_elision_output, |
| expected_disk, |
| expected_status, |
| expected_skip, |
| None, None, None, None, |
| None, 1, 1, '--reintegrate', A_path) |
| svntest.actions.run_and_verify_svn(None, None, [], 'ci', '-m', |
| "2nd Reintegrate feature branch back to 'A'", |
| wc_dir) |
| |
| # Demonstrate the danger of any self-referential mergeinfo on trunk. |
| # |
| # Merge all available revisions except r3 from 'A' to 'A_COPY'. |
| svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir) |
| svntest.actions.run_and_verify_svn(None, None, [], 'merge', '-r3:HEAD', |
| sbox.repo_url + '/A', |
| A_COPY_path) |
| svntest.actions.run_and_verify_svn(None, None, [], 'ci', '-m', |
| "Merge -r3:HEAD from 'A' to 'A_COPY'", |
| wc_dir) |
| # No self-referential mergeinfo should have been carried on 'A_COPY' from |
| # 'A' that would prevent the following merge from being operative. |
| svntest.actions.run_and_verify_svn( |
| None, |
| expected_merge_output([[2,3],[2,16]], |
| ['U ' + psi_COPY_path + '\n', |
| ' U ' + A_COPY_path + '\n',]), |
| [], 'merge', '--allow-mixed-revisions', sbox.repo_url + '/A', A_COPY_path) |
| |
| #---------------------------------------------------------------------- |
| # Test for a reintegrate bug which can occur when the merge source |
| # has mergeinfo that explicitly describes common history with the reintegrate |
| # target, see http://svn.haxx.se/dev/archive-2009-12/0338.shtml |
| # |
| # Also tests Issue #3591 'reintegrate merges update subtree mergeinfo |
| # unconditionally'. |
| @Issue(3591) |
| def reintegrate_with_self_referential_mergeinfo(sbox): |
| "source has target's history as explicit mergeinfo" |
| |
| sbox.build() |
| wc_dir = sbox.wc_dir |
| |
| # Make some changes under 'A' in r2-5. |
| wc_disk, wc_status = set_up_branch(sbox, nbr_of_branches=0) |
| |
| # Some paths we'll care about |
| A_path = os.path.join(wc_dir, "A") |
| A2_path = os.path.join(wc_dir, "A2") |
| A2_B_path = os.path.join(wc_dir, "A2", "B") |
| A2_1_path = os.path.join(wc_dir, "A2.1") |
| A2_1_mu_path = os.path.join(wc_dir, "A2.1", "mu") |
| |
| # r6 Copy A to A2 and then manually set some self-referential mergeinfo on |
| # A2/B and A2. |
| svntest.actions.run_and_verify_svn(None, exp_noop_up_out(5), [], |
| 'up', wc_dir) |
| svntest.actions.run_and_verify_svn(None, None, [], |
| 'copy', A_path, A2_path) |
| # /A:3 describes A2's natural history, a.k.a. it's implicit mergeinfo, so |
| # it is self-referential. Same for /A/B:4 and A2/B. Normally this is |
| # redundant but not harmful. |
| svntest.actions.run_and_verify_svn(None, None, [], |
| 'ps', 'svn:mergeinfo', '/A:3', A2_path) |
| svntest.actions.run_and_verify_svn(None, None, [], |
| 'ps', 'svn:mergeinfo', '/A/B:4', A2_B_path) |
| svntest.actions.run_and_verify_svn( |
| None, None, [], 'ci', '-m', |
| 'copy A to A2 and set some self-referential mergeinfo on the latter.', |
| wc_dir) |
| |
| # r7 Copy A2 to A2.1 |
| svntest.actions.run_and_verify_svn(None, None, [], |
| 'copy', A2_path, A2_1_path) |
| svntest.actions.run_and_verify_svn(None, None, [], 'ci', |
| '-m', 'copy A2to A2.1.', wc_dir) |
| |
| # r8 Make a change on A2.1/mu |
| svntest.main.file_write(A2_1_mu_path, 'New A2.1 stuff') |
| svntest.actions.run_and_verify_svn(None, None, [], 'ci', |
| '-m', 'Work done on the A2.1 branch.', |
| wc_dir) |
| |
| # Update to uniform revision and reintegrate A2.1 back to A2. |
| # Note that the mergeinfo on A2/B is not changed by the reintegration |
| # and so is not expected to by updated to describe the merge. |
| svntest.actions.run_and_verify_svn(None, exp_noop_up_out(8), [], |
| 'up', wc_dir) |
| expected_output = wc.State(A2_path, { |
| 'mu' : Item(status='U '), |
| }) |
| expected_mergeinfo_output = wc.State(A2_path, { |
| '' : Item(status=' U'), |
| }) |
| expected_elision_output = wc.State(A2_path, { |
| }) |
| expected_status = wc.State(A2_path, { |
| '' : Item(status=' M'), |
| 'B' : Item(status=' '), |
| 'mu' : Item(status='M '), |
| 'B/E' : Item(status=' '), |
| 'B/E/alpha' : Item(status=' '), |
| 'B/E/beta' : Item(status=' '), |
| 'B/lambda' : Item(status=' '), |
| 'B/F' : Item(status=' '), |
| 'C' : Item(status=' '), |
| 'D' : Item(status=' '), |
| 'D/G' : Item(status=' '), |
| 'D/G/pi' : Item(status=' '), |
| 'D/G/rho' : Item(status=' '), |
| 'D/G/tau' : Item(status=' '), |
| 'D/gamma' : Item(status=' '), |
| 'D/H' : Item(status=' '), |
| 'D/H/chi' : Item(status=' '), |
| 'D/H/psi' : Item(status=' '), |
| 'D/H/omega' : Item(status=' '), |
| }) |
| expected_status.tweak(wc_rev=8) |
| expected_disk = wc.State('', { |
| '' : Item(props={SVN_PROP_MERGEINFO : '/A:3\n/A2.1:7-8'}), |
| 'B' : Item(props={SVN_PROP_MERGEINFO : '/A/B:4'}), |
| 'mu' : Item("New A2.1 stuff"), |
| 'B/E' : Item(), |
| 'B/E/alpha' : Item("This is the file 'alpha'.\n"), |
| 'B/E/beta' : Item("New content"), |
| 'B/lambda' : Item("This is the file 'lambda'.\n"), |
| 'B/F' : Item(), |
| 'C' : Item(), |
| 'D' : Item(), |
| 'D/G' : Item(), |
| 'D/G/pi' : Item("This is the file 'pi'.\n"), |
| 'D/G/rho' : Item("New content"), |
| 'D/G/tau' : Item("This is the file 'tau'.\n"), |
| 'D/gamma' : Item("This is the file 'gamma'.\n"), |
| 'D/H' : Item(), |
| 'D/H/chi' : Item("This is the file 'chi'.\n"), |
| 'D/H/psi' : Item("New content"), |
| 'D/H/omega' : Item("New content"), |
| }) |
| expected_skip = wc.State(A2_path, { }) |
| # Previously failed with this error: |
| # |
| # svn merge ^/A2.1" A2 --reintegrate |
| # ..\..\..\subversion\svn\merge-cmd.c:349: (apr_err=160013) |
| # ..\..\..\subversion\libsvn_client\merge.c:9219: (apr_err=160013) |
| # ..\..\..\subversion\libsvn_client\ra.c:728: (apr_err=160013) |
| # ..\..\..\subversion\libsvn_client\mergeinfo.c:733: (apr_err=160013) |
| # ..\..\..\subversion\libsvn_client\ra.c:526: (apr_err=160013) |
| # ..\..\..\subversion\libsvn_repos\rev_hunt.c:908: (apr_err=160013) |
| # ..\..\..\subversion\libsvn_repos\rev_hunt.c:607: (apr_err=160013) |
| # ..\..\..\subversion\libsvn_fs_fs\tree.c:2886: (apr_err=160013) |
| # ..\..\..\subversion\libsvn_fs_fs\tree.c:669: (apr_err=160013) |
| # svn: File not found: revision 4, path '/A2' |
| svntest.actions.run_and_verify_merge(A2_path, None, None, |
| sbox.repo_url + '/A2.1', None, |
| expected_output, |
| expected_mergeinfo_output, |
| expected_elision_output, |
| expected_disk, |
| expected_status, |
| expected_skip, |
| None, None, None, None, |
| None, 1, 0, '--reintegrate', A2_path) |
| |
| #---------------------------------------------------------------------- |
| # Test for issue #3577 '1.7 subtree mergeinfo recording breaks reintegrate'. |
| @Issue(3577) |
| def reintegrate_with_subtree_merges(sbox): |
| "reintegrate with prior subtree merges to source" |
| |
| # Create a standard greek tree, branch A to A_COPY in r2, and make |
| # some changes under A in r3-6. |
| sbox.build() |
| wc_dir = sbox.wc_dir |
| expected_disk, expected_status = set_up_branch(sbox) |
| |
| # Some paths we'll care about |
| A_path = os.path.join(wc_dir, "A") |
| mu_COPY_path = os.path.join(wc_dir, "A_COPY", "mu") |
| A_COPY_path = os.path.join(wc_dir, "A_COPY") |
| B_COPY_path = os.path.join(wc_dir, "A_COPY", "B") |
| rho_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "G", "rho") |
| H_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "H") |
| |
| # r7 - Make a change on the A_COPY branch that will be |
| # reintegrated back to A. |
| svntest.main.file_write(mu_COPY_path, "branch work") |
| svntest.main.run_svn(None, 'commit', '-m', |
| 'Some work on the A_COPY branch', wc_dir) |
| |
| # Update the WC to a uniform revision, then merge all of the changes |
| # from A to A_COPY, but do it via subtree merges so the mergeinfo |
| # record of the merges insn't neatly reflected in the root of the |
| # branch. Commit the merge as r8. |
| svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir) |
| svntest.actions.run_and_verify_svn(None, None, [], 'merge', '-c5', |
| sbox.repo_url + '/A/B', |
| B_COPY_path) |
| svntest.actions.run_and_verify_svn(None, None, [], 'merge', '-c4', |
| sbox.repo_url + '/A/D/G/rho', |
| rho_COPY_path) |
| svntest.actions.run_and_verify_svn(None, None, [], 'merge', '-c3', |
| sbox.repo_url + '/A/D/H', |
| H_COPY_path) |
| svntest.actions.run_and_verify_svn(None, None, [], 'merge', '-c6', |
| sbox.repo_url + '/A', |
| A_COPY_path) |
| svntest.actions.run_and_verify_svn(None, None, [], 'commit', '-m', |
| 'Merge everything from A to A_COPY', |
| wc_dir) |
| |
| # Now update the WC and try to reintegrate. Since we really have merged |
| # everything from A to A_COPY, even though it was done via subtree merges, |
| # the reintegrate should succeed. Previously it failed because the naive |
| # interpretation of the mergeinfo on A_COPY didn't reflect that it was |
| # fully synced with A, resulting in this error: |
| # |
| # svn merge ^/A_COPY A --reintegrate |
| # ..\..\..\subversion\svn\merge-cmd.c:358: (apr_err=195016) |
| # ..\..\..\subversion\libsvn_client\merge.c:9318: (apr_err=195016) |
| # svn: Reintegrate can only be used if revisions 2 through 7 were |
| # previously merged from file:///C%3A/SVN/src-trunk-2/Debug/subversion |
| # /tests/cmdline/svn-test-work/repositories/merge_tests-142/A to the |
| # reintegrate source, but this is not the case: |
| # A_COPY |
| # Missing ranges: /A:2-5 |
| # A_COPY/B |
| # Missing ranges: /A/B:2-4,6 |
| # A_COPY/D/G/rho |
| # Missing ranges: /A/D/G/rho:2-3,5-6 |
| # A_COPY/D/H |
| # Missing ranges: /A/D/H:2,4-5 |
| svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir) |
| expected_output = wc.State(A_path, { |
| 'mu' : Item(status='U '), |
| }) |
| expected_mergeinfo_output = wc.State(A_path, { |
| '' : Item(status=' G'), |
| }) |
| expected_elision_output = wc.State(A_path, { |
| }) |
| expected_A_status = wc.State(A_path, { |
| '' : Item(status=' M'), |
| 'B' : Item(status=' '), |
| 'mu' : Item(status='M '), |
| 'B/E' : Item(status=' '), |
| 'B/E/alpha' : Item(status=' '), |
| 'B/E/beta' : Item(status=' '), |
| 'B/lambda' : Item(status=' '), |
| 'B/F' : Item(status=' '), |
| 'C' : Item(status=' '), |
| 'D' : Item(status=' '), |
| 'D/G' : Item(status=' '), |
| 'D/G/pi' : Item(status=' '), |
| 'D/G/rho' : Item(status=' '), |
| 'D/G/tau' : Item(status=' '), |
| 'D/gamma' : Item(status=' '), |
| 'D/H' : Item(status=' '), |
| 'D/H/chi' : Item(status=' '), |
| 'D/H/psi' : Item(status=' '), |
| 'D/H/omega' : Item(status=' '), |
| }) |
| expected_A_status.tweak(wc_rev=8) |
| expected_A_disk = wc.State('', { |
| '' : Item(props={SVN_PROP_MERGEINFO : '/A_COPY:2-8'}), |
| 'B' : Item(), |
| 'mu' : Item("branch work"), |
| 'B/E' : Item(), |
| 'B/E/alpha' : Item("This is the file 'alpha'.\n"), |
| 'B/E/beta' : Item("New content"), |
| 'B/lambda' : Item("This is the file 'lambda'.\n"), |
| 'B/F' : Item(), |
| 'C' : Item(), |
| 'D' : Item(), |
| 'D/G' : Item(), |
| 'D/G/pi' : Item("This is the file 'pi'.\n"), |
| 'D/G/rho' : Item("New content"), |
| 'D/G/tau' : Item("This is the file 'tau'.\n"), |
| 'D/gamma' : Item("This is the file 'gamma'.\n"), |
| 'D/H' : Item(), |
| 'D/H/chi' : Item("This is the file 'chi'.\n"), |
| 'D/H/psi' : Item("New content"), |
| 'D/H/omega' : Item("New content"), |
| }) |
| expected_A_skip = wc.State(A_COPY_path, {}) |
| svntest.actions.run_and_verify_merge(A_path, None, None, |
| sbox.repo_url + '/A_COPY', None, |
| expected_output, |
| expected_mergeinfo_output, |
| expected_elision_output, |
| expected_A_disk, |
| expected_A_status, |
| expected_A_skip, |
| None, None, None, None, |
| None, 1, 1, "--reintegrate", A_path) |
| |
| #---------------------------------------------------------------------- |
| # Test for issue #3654 'added subtrees with mergeinfo break reintegrate'. |
| @Issue(3654) |
| def added_subtrees_with_mergeinfo_break_reintegrate(sbox): |
| "added subtrees with mergeinfo break reintegrate" |
| |
| sbox.build() |
| wc_dir = sbox.wc_dir |
| |
| # Some paths we'll care about |
| A_path = os.path.join(wc_dir, "A") |
| nu_path = os.path.join(wc_dir, "A", "C", "nu") |
| mu_path = os.path.join(wc_dir, "A", "mu") |
| A_COPY_path = os.path.join(wc_dir, "A_COPY") |
| lambda_COPY_path = os.path.join(wc_dir, "A_COPY", "B", "lambda") |
| A_COPY_2_path = os.path.join(wc_dir, "A_COPY_2") |
| nu_COPY_2_path = os.path.join(wc_dir, "A_COPY_2", "C", "nu") |
| |
| # Branch A@1 to A_COPY and A_COPY_2 in r2 and r3 respectively. |
| # Make some changes under 'A' in r4-7. |
| wc_disk, wc_status = set_up_branch(sbox, nbr_of_branches=2) |
| |
| # r8 - Add a new file A_COPY_2/C/nu. |
| svntest.main.file_write(nu_COPY_2_path, "This is the file 'nu'.\n") |
| svntest.actions.run_and_verify_svn(None, None, [], 'add', nu_COPY_2_path) |
| svntest.actions.run_and_verify_svn(None, None, [], 'ci', |
| '-m', 'Add new file in A_COPY_2 branch', |
| wc_dir) |
| |
| |
| # r9 - Cyclic cherry pick merge r8 from A_COPY_2 back to A. |
| svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir) |
| svntest.actions.run_and_verify_svn(None, svntest.verify.AnyOutput, [], |
| 'merge', '-c', '8', |
| sbox.repo_url + '/A_COPY_2', |
| A_path) |
| svntest.actions.run_and_verify_svn(None, None, [], 'ci', |
| '-m', 'Merge r8 from A_COPY_2 to A.', |
| wc_dir) |
| |
| # r10 - Make an edit to A_COPY_2/C/nu. |
| svntest.main.file_write(nu_COPY_2_path, "A_COPY_2 edit to file 'nu'.\n") |
| svntest.actions.run_and_verify_svn(None, None, [], 'ci', |
| '-m', 'Edit new file on A_COPY_2 branch', |
| wc_dir) |
| |
| # r11 - Cyclic subtree cherry pick merge r10 from A_COPY_2/C/nu |
| # back to A/C/nu. |
| svntest.actions.run_and_verify_svn(None, svntest.verify.AnyOutput, [], |
| 'merge', '-c', '10', |
| sbox.repo_url + '/A_COPY_2/C/nu', |
| nu_path) |
| svntest.actions.run_and_verify_svn(None, None, [], 'ci', '-m', |
| 'Merge r8 from A_COPY_2/C/nu to A/C/nu.', |
| wc_dir) |
| |
| # r12 - Edit under A_COPY. |
| svntest.main.file_write(mu_path, "mu edits on A_COPY.\n") |
| svntest.actions.run_and_verify_svn(None, None, [], 'ci', |
| '-m', 'Work on A_COPY branch.', |
| wc_dir) |
| |
| # r13 - Sync merge A to A_COPY in preparation for reintegrate. |
| svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir) |
| svntest.actions.run_and_verify_svn(None, svntest.verify.AnyOutput, [], |
| 'merge', sbox.repo_url + '/A', A_COPY_path) |
| svntest.actions.run_and_verify_svn(None, None, [], 'ci', '-m', |
| 'Prep for reintegrate: Sync A to A_COPY.', |
| wc_dir) |
| |
| # r14 - Reintegrate A_COPY to A. |
| svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir) |
| svntest.actions.run_and_verify_svn(None, svntest.verify.AnyOutput, [], |
| 'merge', '--reintegrate', |
| sbox.repo_url + '/A_COPY', A_path) |
| svntest.actions.run_and_verify_svn(None, None, [], 'ci', '-m', |
| 'Reintegrate A_COPY to A.', |
| wc_dir) |
| |
| # r15 - Delete A_COPY. |
| svntest.actions.run_and_verify_svn(None, svntest.verify.AnyOutput, [], |
| 'delete', A_COPY_path) |
| svntest.actions.run_and_verify_svn(None, None, [], 'ci', '-m', |
| 'Delete A_COPY branch', wc_dir) |
| |
| # r16 - Create new A_COPY from A@HEAD=15. |
| # |
| # Update so we copy HEAD: |
| svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir) |
| svntest.actions.run_and_verify_svn(None, svntest.verify.AnyOutput, [], |
| 'copy', A_path, A_COPY_path) |
| svntest.actions.run_and_verify_svn(None, None, [], 'ci', '-m', |
| 'Create new A_COPY branch from A', wc_dir) |
| |
| # r17 - Unrelated edits under both A and A_COPY. |
| svntest.main.file_write(nu_path, "Trunk work on nu.\n") |
| svntest.main.file_write(lambda_COPY_path, "lambda edit on A_COPY.\n") |
| svntest.actions.run_and_verify_svn(None, None, [], 'ci', '-m', |
| 'Unrelated edits on A and A_COPY branch.', |
| wc_dir) |
| |
| # r18 - Sync A to A_COPY in preparation for another reintegrate. |
| svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir) |
| svntest.actions.run_and_verify_svn(None, svntest.verify.AnyOutput, [], |
| 'merge', sbox.repo_url + '/A', A_COPY_path) |
| svntest.actions.run_and_verify_svn(None, None, [], 'ci', '-m', |
| 'Prep for reintegrate: Sync A to A_COPY.', |
| wc_dir) |
| |
| # Reintegrate A_COPY back to A. We just synced A_COPY with A, so this |
| # should work. The only text change should be the change made to |
| # A_COPY/B/lambda in r17 after the new A_COPY was created. |
| svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir) |
| expected_output = wc.State(A_path, { |
| '' : Item(status=' U'), |
| 'B/lambda' : Item(status='U '), |
| 'C/nu' : Item(status=' U'), |
| }) |
| expected_mergeinfo_output = wc.State(A_path, { |
| '' : Item(status=' U'), |
| 'C/nu' : Item(status=' U'), |
| }) |
| expected_elision_output = wc.State(A_path, { |
| }) |
| expected_status = wc.State(A_path, { |
| '' : Item(status=' M'), |
| 'B' : Item(status=' '), |
| 'mu' : Item(status=' '), |
| 'B/E' : Item(status=' '), |
| 'B/E/alpha' : Item(status=' '), |
| 'B/E/beta' : Item(status=' '), |
| 'B/lambda' : Item(status='M '), |
| 'B/F' : Item(status=' '), |
| 'C' : Item(status=' '), |
| 'C/nu' : Item(status=' M'), |
| 'D' : Item(status=' '), |
| 'D/G' : Item(status=' '), |
| 'D/G/pi' : Item(status=' '), |
| 'D/G/rho' : Item(status=' '), |
| 'D/G/tau' : Item(status=' '), |
| 'D/gamma' : Item(status=' '), |
| 'D/H' : Item(status=' '), |
| 'D/H/chi' : Item(status=' '), |
| 'D/H/psi' : Item(status=' '), |
| 'D/H/omega' : Item(status=' '), |
| }) |
| expected_status.tweak(wc_rev=18) |
| expected_disk = wc.State('', { |
| '' : Item(props={SVN_PROP_MERGEINFO : |
| '/A_COPY:2-13,16-18\n' |
| # ^ ^ |
| # | | |
| # from _| | |
| # 1st | |
| # reintegrate | |
| # | |
| # from this reintegrate |
| # |
| '/A_COPY_2:8'}), # <-- From cyclic merge in r9 |
| 'B' : Item(), |
| 'mu' : Item("mu edits on A_COPY.\n"), # From earlier reintegrate. |
| 'B/E' : Item(), |
| 'B/E/alpha' : Item("This is the file 'alpha'.\n"), |
| 'B/E/beta' : Item("New content"), |
| 'B/lambda' : Item("lambda edit on A_COPY.\n"), # From this reintegrate. |
| 'B/F' : Item(), |
| 'C' : Item(), |
| 'C/nu' : Item("Trunk work on nu.\n", |
| props={SVN_PROP_MERGEINFO : |
| '/A_COPY/C/nu:13,16-18\n' |
| '/A_COPY_2/C/nu:10'}), # <-- From cyclic |
| # merge in r11 |
| 'D' : Item(), |
| 'D/G' : Item(), |
| 'D/G/pi' : Item("This is the file 'pi'.\n"), |
| 'D/G/rho' : Item("New content"), |
| 'D/G/tau' : Item("This is the file 'tau'.\n"), |
| 'D/gamma' : Item("This is the file 'gamma'.\n"), |
| 'D/H' : Item(), |
| 'D/H/chi' : Item("This is the file 'chi'.\n"), |
| 'D/H/psi' : Item("New content"), |
| 'D/H/omega' : Item("New content"), |
| }) |
| expected_skip = wc.State(A_COPY_path, {}) |
| svntest.actions.run_and_verify_merge(A_path, None, None, |
| sbox.repo_url + '/A_COPY', None, |
| expected_output, |
| expected_mergeinfo_output, |
| expected_elision_output, |
| expected_disk, |
| expected_status, |
| expected_skip, |
| None, None, None, None, |
| None, 1, 1, "--reintegrate", A_path) |
| |
| #---------------------------------------------------------------------- |
| # Test for issue #3648 '2-URL merges incorrectly reverse-merge mergeinfo |
| # for merge target'. |
| @Issue(3648) |
| def two_URL_merge_removes_valid_mergeinfo_from_target(sbox): |
| "2-URL merge removes valid mergeinfo from target" |
| |
| sbox.build() |
| wc_dir = sbox.wc_dir |
| |
| # Some paths we'll care about |
| lambda_COPY_path = os.path.join(wc_dir, "A_COPY", "B", "lambda") |
| mu_path = os.path.join(wc_dir, "A", "mu") |
| A_COPY_path = os.path.join(wc_dir, "A_COPY") |
| A_COPY_2_path = os.path.join(wc_dir, "A_COPY_2") |
| |
| # Branch A@1 to A_COPY r2 |
| # Branch A@1 to A_COPY_2 in r3. |
| # Make some changes under 'A' in r4-7. |
| wc_disk, wc_status = set_up_branch(sbox, nbr_of_branches=2) |
| |
| # r8 - A simple text edit on the A_COPY branch. |
| svntest.main.file_write(lambda_COPY_path, "Edit on 'branch 1'.\n") |
| svntest.actions.run_and_verify_svn(None, None, [], 'ci', |
| '-m', "Work on 'branch 1'.", |
| wc_dir) |
| |
| # r9 - Sync the A_COPY branch with A up the HEAD (r8). Now A_COPY |
| # differs from A only by the change made in r8 and by the mergeinfo |
| # '/A:2-8' on A_COPY which was set to describe the merge. |
| svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir) |
| svntest.actions.run_and_verify_svn(None, svntest.verify.AnyOutput, [], |
| 'merge', sbox.repo_url + '/A', A_COPY_path) |
| svntest.actions.run_and_verify_svn(None, None, [], 'ci', |
| '-m', 'Sync A to A_COPY.', |
| wc_dir) |
| |
| # r10 - A simple text edit on our "trunk" A. |
| svntest.main.file_write(mu_path, "Edit on 'trunk'.\n") |
| svntest.actions.run_and_verify_svn(None, None, [], 'ci', |
| '-m', "Work on 'trunk'", |
| wc_dir) |
| |
| # r11 - Sync the A_COPY_2 branch with A up to HEAD (r10). |
| svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir) |
| svntest.actions.run_and_verify_svn(None, svntest.verify.AnyOutput, [], |
| 'merge', sbox.repo_url + '/A', |
| A_COPY_2_path) |
| svntest.actions.run_and_verify_svn(None, None, [], 'ci', |
| '-m', 'Sync A to A_COPY_2.', |
| wc_dir) |
| |
| # Confirm that the mergeinfo on each branch is what we expect. |
| svntest.actions.run_and_verify_svn(None, |
| [A_COPY_path + ' - /A:2-8\n'], |
| [], 'pg', SVN_PROP_MERGEINFO, |
| '-R', A_COPY_path) |
| svntest.actions.run_and_verify_svn(None, |
| [A_COPY_2_path + ' - /A:3-10\n'], |
| [], 'pg', SVN_PROP_MERGEINFO, |
| '-R', A_COPY_2_path) |
| |
| # Now say we want to apply the changes made on the first branch (A_COPY) |
| # to the second branch (A_COPY_2). One way to do this is a 2-URL merge |
| # between A at the revision last synced to A_COPY and A_COPY_2 at HEAD (r11), |
| # i.e.: |
| # |
| # svn merge ^/A@8 ^/A_COPY@11 A_COPY_2_WC |
| # |
| # Recall from the note on r9 that this diff is simply the one text change |
| # made on branch 1 and some mergeinfo: |
| # |
| # >svn diff ^/A@8 ^/A_COPY@11 |
| # Index: B/lambda |
| # =================================================================== |
| # --- B/lambda (.../A) (revision 8) |
| # +++ B/lambda (.../A_COPY) (revision 11) |
| # @@ -1 +1 @@ |
| # -This is the file 'lambda'. |
| # +Edit on 'branch 1'. |
| # |
| # Property changes on: . |
| # ___________________________________________________________________ |
| # Added: svn:mergeinfo |
| # Merged /A:r2-8 |
| # |
| # The mergeinfo diff is already represented in A_COPY_2's mergeinfo, so the |
| # result of the merge should be the text change to lambda and the addition |
| # of mergeinfo showing that the history of A_COPY is now part of A_COPY_2, |
| # i.e. '/A_COPY:2-11' |
| # |
| # Before issue #3648 was fixed this test failed because the valid mergeinfo |
| # '/A:r3-8' on A_COPY_2 was removed by the merge. |
| svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir) |
| expected_output = wc.State(A_COPY_2_path, { |
| '' : Item(status=' G'), |
| 'B/lambda' : Item(status='U '), |
| }) |
| expected_mergeinfo_output = wc.State(A_COPY_2_path, { |
| '' : Item(status=' U'), |
| }) |
| expected_elision_output = wc.State(A_COPY_2_path, { |
| }) |
| expected_status = wc.State(A_COPY_2_path, { |
| '' : Item(status=' M'), |
| 'B' : Item(status=' '), |
| 'mu' : Item(status=' '), |
| 'B/E' : Item(status=' '), |
| 'B/E/alpha' : Item(status=' '), |
| 'B/E/beta' : Item(status=' '), |
| 'B/lambda' : Item(status='M '), |
| 'B/F' : Item(status=' '), |
| 'C' : Item(status=' '), |
| 'D' : Item(status=' '), |
| 'D/G' : Item(status=' '), |
| 'D/G/pi' : Item(status=' '), |
| 'D/G/rho' : Item(status=' '), |
| 'D/G/tau' : Item(status=' '), |
| 'D/gamma' : Item(status=' '), |
| 'D/H' : Item(status=' '), |
| 'D/H/chi' : Item(status=' '), |
| 'D/H/psi' : Item(status=' '), |
| 'D/H/omega' : Item(status=' '), |
| }) |
| expected_status.tweak(wc_rev=11) |
| expected_disk = wc.State('', { |
| '' : Item(props={SVN_PROP_MERGEINFO : |
| '/A:3-10\n/A_COPY:2-11'}), |
| 'B' : Item(), |
| 'mu' : Item("Edit on 'trunk'.\n"), |
| 'B/E' : Item(), |
| 'B/E/alpha' : Item("This is the file 'alpha'.\n"), |
| 'B/E/beta' : Item("New content"), |
| 'B/lambda' : Item("Edit on 'branch 1'.\n"), |
| 'B/F' : Item(), |
| 'C' : Item(), |
| 'D' : Item(), |
| 'D/G' : Item(), |
| 'D/G/pi' : Item("This is the file 'pi'.\n"), |
| 'D/G/rho' : Item("New content"), |
| 'D/G/tau' : Item("This is the file 'tau'.\n"), |
| 'D/gamma' : Item("This is the file 'gamma'.\n"), |
| 'D/H' : Item(), |
| 'D/H/chi' : Item("This is the file 'chi'.\n"), |
| 'D/H/psi' : Item("New content"), |
| 'D/H/omega' : Item("New content"), |
| }) |
| expected_skip = wc.State(A_COPY_path, {}) |
| svntest.actions.run_and_verify_merge(A_COPY_2_path, 8, 11, |
| sbox.repo_url + '/A', |
| sbox.repo_url + '/A_COPY', |
| expected_output, |
| expected_mergeinfo_output, |
| expected_elision_output, |
| expected_disk, |
| expected_status, |
| expected_skip, |
| None, None, None, None, |
| None, 1, 1) |
| |
| #---------------------------------------------------------------------- |
| # Test for issue #3867 'reintegrate merges create mergeinfo for |
| # non-existent paths'. |
| @Issue(3867) |
| @XFail() |
| def reintegrate_creates_bogus_mergeinfo(sbox): |
| "reintegrate creates bogus mergeinfo" |
| |
| sbox.build() |
| wc_dir=sbox.wc_dir |
| |
| mu_path = os.path.join(sbox.wc_dir, "A", "mu") |
| lambda_path = os.path.join(sbox.wc_dir, "A", "B", "lambda") |
| alpha_path = os.path.join(sbox.wc_dir, "A", "B", "E", "alpha") |
| beta_path = os.path.join(sbox.wc_dir, "A", "B", "E", "beta") |
| A_path = os.path.join(sbox.wc_dir, "A") |
| A_path_1 = os.path.join(sbox.wc_dir, "A@1") |
| A_COPY_path = os.path.join(sbox.wc_dir, "A_COPY") |
| A_COPY_psi_path = os.path.join(sbox.wc_dir, "A_COPY", "D", "H", "psi") |
| A_COPY_url = sbox.repo_url + "/A_COPY" |
| |
| # Make 2 commits under /A pushing the repo to rev3 |
| |
| svntest.main.file_write(mu_path, "New content.\n") |
| svntest.main.run_svn(None, "ci", "-m", "simple text edit", wc_dir) |
| svntest.main.file_write(lambda_path, "New content.\n") |
| svntest.main.run_svn(None, "ci", "-m", "simple text edit", wc_dir) |
| |
| # Branch A@1 as A_COPY in revision 4 |
| |
| svntest.main.run_svn(None, "cp", A_path_1, A_COPY_path) |
| svntest.main.run_svn(None, "ci", "-m", "create a branch", wc_dir) |
| |
| # Make a text edit on the branch pushing the repo to rev6 |
| svntest.main.file_write(A_COPY_psi_path, "Branch edit.\n") |
| svntest.main.run_svn(None, "ci", "-m", "branch edit", wc_dir) |
| |
| # Sync the A_COPY with A in preparation for reintegrate |
| svntest.main.run_svn(None, "up", wc_dir) |
| svntest.main.run_svn(None, "merge", sbox.repo_url + "/A", A_COPY_path) |
| svntest.main.run_svn(None, "ci", "-m", "sync A_COPY with A", wc_dir) |
| |
| # Update the working copy to allow the merge |
| svntest.main.run_svn(None, "up", wc_dir) |
| |
| # Reintegrate A_COPY to A. The resulting merginfo on A should be |
| # /A_COPY:4-10 |
| # |
| # Currently this test fails because the resulting mergeinfo is /A_COPY:2-6. |
| # But A_COPY didn't exist unitl r4, so /A_COPY:2-3 describes merge source |
| # path-revs which don't exist. |
| expected_output = wc.State(A_path, { |
| 'D/H/psi' : Item(status='U '), |
| }) |
| expected_mergeinfo_output = wc.State(A_path, { |
| '' : Item(status=' U'), |
| }) |
| expected_elision_output = wc.State(A_path, { |
| }) |
| expected_disk = wc.State('', { |
| '' : Item(props={SVN_PROP_MERGEINFO : |
| '/A_COPY:4-6'}), |
| 'B' : Item(), |
| 'mu' : Item("New content.\n"), |
| 'B/E' : Item(), |
| 'B/E/alpha' : Item("This is the file 'alpha'.\n"), |
| 'B/E/beta' : Item("This is the file 'beta'.\n"), |
| 'B/lambda' : Item("New content.\n"), |
| 'B/F' : Item(), |
| 'C' : Item(), |
| 'D' : Item(), |
| 'D/G' : Item(), |
| 'D/G/pi' : Item("This is the file 'pi'.\n"), |
| 'D/G/rho' : Item("This is the file 'rho'.\n"), |
| 'D/G/tau' : Item("This is the file 'tau'.\n"), |
| 'D/gamma' : Item("This is the file 'gamma'.\n"), |
| 'D/H' : Item(), |
| 'D/H/chi' : Item("This is the file 'chi'.\n"), |
| 'D/H/psi' : Item("Branch edit.\n"), |
| 'D/H/omega' : Item("This is the file 'omega'.\n"), |
| }) |
| expected_skip = wc.State(A_COPY_path, {}) |
| |
| svntest.actions.run_and_verify_merge(A_path, None, None, |
| A_COPY_url, None, |
| expected_output, |
| expected_mergeinfo_output, |
| expected_elision_output, |
| expected_disk, None, expected_skip, |
| None, None, None, None, None, |
| 1, 1, "--reintegrate", A_path) |
| |
| |
| #---------------------------------------------------------------------- |
| # Test for regression on 1.6.x branch, merge fails when source without |
| # subtree mergeinfo is reintegrated into a target with subtree |
| # mergeinfo. Deliberately written in a style that works with the 1.6 |
| # testsuite. |
| @Issue(3957) |
| def no_source_subtree_mergeinfo(sbox): |
| "source without subtree mergeinfo" |
| |
| sbox.build() |
| wc_dir=sbox.wc_dir |
| |
| svntest.main.file_write(os.path.join(wc_dir, 'A', 'B', 'E', 'alpha'), |
| 'AAA\n' + |
| 'BBB\n' + |
| 'CCC\n') |
| svntest.main.run_svn(None, 'commit', '-m', 'log message', wc_dir) |
| svntest.main.run_svn(None, 'update', wc_dir) |
| |
| # Create branch-1 |
| svntest.main.run_svn(None, 'copy', |
| os.path.join(wc_dir, 'A', 'B'), |
| os.path.join(wc_dir, 'A', 'B1')) |
| svntest.main.run_svn(None, 'commit', '-m', 'log message', wc_dir) |
| |
| # Create branch-1 |
| svntest.main.run_svn(None, 'copy', |
| os.path.join(wc_dir, 'A', 'B'), |
| os.path.join(wc_dir, 'A', 'B2')) |
| svntest.main.run_svn(None, 'commit', '-m', 'log message', wc_dir) |
| |
| # Change on trunk |
| svntest.main.file_write(os.path.join(wc_dir, 'A', 'B', 'E', 'alpha'), |
| 'AAAxx\n' + |
| 'BBB\n' + |
| 'CCC\n') |
| svntest.main.run_svn(None, 'commit', '-m', 'log message', wc_dir) |
| |
| # Change on branch-1 |
| svntest.main.file_write(os.path.join(wc_dir, 'A', 'B1', 'E', 'alpha'), |
| 'AAA\n' + |
| 'BBBxx\n' + |
| 'CCC\n') |
| svntest.main.run_svn(None, 'commit', '-m', 'log message', wc_dir) |
| |
| # Change on branch-2 |
| svntest.main.file_write(os.path.join(wc_dir, 'A', 'B2', 'E', 'alpha'), |
| 'AAA\n' + |
| 'BBB\n' + |
| 'CCCxx\n') |
| svntest.main.run_svn(None, 'commit', '-m', 'log message', wc_dir) |
| svntest.main.run_svn(None, 'update', wc_dir) |
| |
| # Merge trunk to branch-1 |
| svntest.main.run_svn(None, 'merge', '^/A/B', os.path.join(wc_dir, 'A', 'B1')) |
| svntest.main.run_svn(None, 'commit', '-m', 'log message', wc_dir) |
| svntest.main.run_svn(None, 'update', wc_dir) |
| |
| # Reintegrate branch-1 subtree to trunk subtree |
| svntest.main.run_svn(None, 'merge', '--reintegrate', |
| '^/A/B1/E', os.path.join(wc_dir, 'A', 'B', 'E')) |
| svntest.main.run_svn(None, 'commit', '-m', 'log message', wc_dir) |
| svntest.main.run_svn(None, 'update', wc_dir) |
| |
| # Merge trunk to branch-2 |
| svntest.main.run_svn(None, 'merge', '^/A/B', os.path.join(wc_dir, 'A', 'B2')) |
| svntest.main.run_svn(None, 'commit', '-m', 'log message', wc_dir) |
| svntest.main.run_svn(None, 'update', wc_dir) |
| |
| # Reverse merge branch-1 subtree to branch-2 subtree, this removes |
| # the subtree mergeinfo from branch 2 |
| svntest.main.run_svn(None, 'merge', '-r8:2', |
| '^/A/B1/E', os.path.join(wc_dir, 'A', 'B2', 'E')) |
| svntest.main.run_svn(None, 'commit', '-m', 'log message', wc_dir) |
| svntest.main.run_svn(None, 'update', wc_dir) |
| |
| # Merge trunk to branch-2 |
| svntest.main.run_svn(None, 'merge', '^/A/B', os.path.join(wc_dir, 'A', 'B2')) |
| svntest.main.run_svn(None, 'commit', '-m', 'log message', wc_dir) |
| svntest.main.run_svn(None, 'update', wc_dir) |
| |
| # Reintegrate branch-2 to trunk, this fails in 1.6.x from 1.6.13. |
| # The error message states revisions /A/B/E:3-11 are missing from |
| # /A/B2/E and yet the mergeinfo on /A/B2 is /A/B:3-11 and /A/B2/E |
| # has no mergeinfo. |
| expected_output = wc.State(os.path.join(wc_dir, 'A', 'B'), { |
| 'E' : Item(status=' U'), |
| 'E/alpha' : Item(status='U '), |
| }) |
| expected_mergeinfo = wc.State(os.path.join(wc_dir, 'A', 'B'), { |
| '' : Item(status=' U'), |
| }) |
| expected_elision = wc.State(os.path.join(wc_dir, 'A', 'B'), { |
| }) |
| expected_disk = wc.State('', { |
| '' : Item(props={SVN_PROP_MERGEINFO : '/A/B2:3-12'}), |
| 'E' : Item(), |
| 'E/alpha' : Item("AAA\n" + |
| "BBB\n" + |
| "CCCxx\n"), |
| 'E/beta' : Item("This is the file 'beta'.\n"), |
| 'F' : Item(), |
| 'lambda' : Item("This is the file 'lambda'.\n"), |
| }) |
| expected_skip = wc.State(os.path.join(wc_dir, 'A', 'B'), { |
| }) |
| svntest.actions.run_and_verify_merge(os.path.join(wc_dir, 'A', 'B'), |
| None, None, '^/A/B2', None, |
| expected_output, expected_mergeinfo, |
| expected_elision, expected_disk, |
| None, expected_skip, |
| None, None, None, None, None, |
| 1, 1, '--reintegrate', |
| os.path.join(wc_dir, 'A', 'B')) |
| # For 1.6 testsuite use: |
| # svntest.actions.run_and_verify_merge(os.path.join(wc_dir, 'A', 'B'), |
| # None, None, '^/A/B2', |
| # expected_output, |
| # expected_disk, |
| # None, expected_skip, |
| # None, None, None, None, None, |
| # 1, 1, '--reintegrate') |
| |
| |
| ######################################################################## |
| # Run the tests |
| |
| |
| # list all tests here, starting with None: |
| test_list = [ None, |
| basic_reintegrate, |
| reintegrate_with_rename, |
| reintegrate_branch_never_merged_to, |
| reintegrate_fail_on_modified_wc, |
| reintegrate_fail_on_mixed_rev_wc, |
| reintegrate_fail_on_switched_wc, |
| reintegrate_on_shallow_wc, |
| reintegrate_fail_on_stale_source, |
| merge_file_with_space_in_its_path, |
| reintegrate_with_subtree_mergeinfo, |
| multiple_reintegrates_from_the_same_branch, |
| reintegrate_with_self_referential_mergeinfo, |
| added_subtrees_with_mergeinfo_break_reintegrate, |
| two_URL_merge_removes_valid_mergeinfo_from_target, |
| reintegrate_creates_bogus_mergeinfo, |
| no_source_subtree_mergeinfo, |
| ] |
| |
| if __name__ == '__main__': |
| svntest.main.run_tests(test_list) |
| # NOTREACHED |
| |
| |
| ### End of file. |