blob: 825d18d76a17292cc4a5c46970b22e6dd9fabd9b [file] [log] [blame]
#!/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.