Example #1
0
def automatic_conflict_resolution(sbox):
  "resolve -R --accept [base | mf | tf]"

  sbox.build()
  wc_dir = sbox.wc_dir

  # Some paths we'll care about
  A_COPY_path   = os.path.join(wc_dir, "A_COPY")
  psi_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "H", "psi")

  # Branch A to A_COPY in r2, then make some changes under 'A' in r3-6.
  wc_disk, wc_status = set_up_branch(sbox)

  # Make a change on the A_COPY branch such that a subsequent merge
  # conflicts.
  svntest.main.file_write(psi_COPY_path, "Branch content.\n")
  svntest.actions.run_and_verify_svn(None, None, [],
                                     'commit', '-m', 'log msg', wc_dir)
  def do_text_conflicting_merge():
    svntest.actions.run_and_verify_svn(None, None, [],
                                       'revert', '--recursive', A_COPY_path)
    svntest.actions.run_and_verify_svn(
      None,
      expected_merge_output([[3]], [
        "C    %s\n" % psi_COPY_path,
        " U   %s\n" % A_COPY_path],
        target=A_COPY_path, text_conflicts=1),
      [], 'merge', '-c3', '--allow-mixed-revisions',
      sbox.repo_url + '/A',
      A_COPY_path)

  # Test 'svn resolve -R --accept base'
  do_text_conflicting_merge()
  svntest.actions.run_and_verify_resolve([psi_COPY_path],
                                         '-R', '--accept', 'base',
                                         A_COPY_path)
  wc_disk.tweak('A_COPY/D/H/psi', contents="This is the file 'psi'.\n")
  svntest.actions.verify_disk(wc_dir, wc_disk)

  # Test 'svn resolve -R --accept mine-full'
  do_text_conflicting_merge()
  svntest.actions.run_and_verify_resolve([psi_COPY_path],
                                         '-R', '--accept', 'mine-full',
                                         A_COPY_path)
  wc_disk.tweak('A_COPY/D/H/psi', contents="Branch content.\n")
  svntest.actions.verify_disk(wc_dir, wc_disk)

  # Test 'svn resolve -R --accept theirs-full'
  do_text_conflicting_merge()
  svntest.actions.run_and_verify_resolve([psi_COPY_path],
                                         '-R', '--accept', 'tf',
                                         A_COPY_path)
  wc_disk.tweak('A_COPY/D/H/psi', contents="New content")
  svntest.actions.verify_disk(wc_dir, wc_disk)
Example #2
0
def non_inheritable_mergeinfo(sbox):
    "non-inheritable mergeinfo shows as merged"

    sbox.build()
    wc_dir = sbox.wc_dir
    expected_disk, expected_status = set_up_branch(sbox)

    # Some paths we'll care about
    A_COPY_path = os.path.join(wc_dir, "A_COPY")
    D_COPY_path = os.path.join(wc_dir, "A_COPY", "D")
    rho_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "G", "rho")

    # Update the WC, then merge r4 from A to A_COPY and r6 from A to A_COPY
    # at --depth empty and commit the merges as r7.
    svntest.actions.run_and_verify_svn(None, exp_noop_up_out(6), [], 'up',
                                       wc_dir)
    expected_status.tweak(wc_rev=6)
    svntest.actions.run_and_verify_svn(
        None,
        expected_merge_output([[4]], [
            'U    ' + rho_COPY_path + '\n',
            ' U   ' + A_COPY_path + '\n',
        ]), [], 'merge', '-c4', sbox.repo_url + '/A', A_COPY_path)
    svntest.actions.run_and_verify_svn(
        None, expected_merge_output([[6]], ' G   ' + A_COPY_path + '\n'), [],
        'merge', '-c6', sbox.repo_url + '/A', A_COPY_path, '--depth', 'empty')
    expected_output = wc.State(wc_dir, {
        'A_COPY': Item(verb='Sending'),
        'A_COPY/D/G/rho': Item(verb='Sending'),
    })
    expected_status.tweak('A_COPY', 'A_COPY/D/G/rho', wc_rev=7)
    svntest.actions.run_and_verify_commit(wc_dir, expected_output,
                                          expected_status, None, wc_dir)

    # Update the WC a last time to ensure full inheritance.
    svntest.actions.run_and_verify_svn(None, exp_noop_up_out(7), [], 'up',
                                       wc_dir)

    # Despite being non-inheritable, r6 should still show as merged to A_COPY
    # and not eligible for merging.
    svntest.actions.run_and_verify_mergeinfo(
        adjust_error_for_server_version(""), ['4', '6*'], sbox.repo_url + '/A',
        A_COPY_path, '--show-revs', 'merged')
    svntest.actions.run_and_verify_mergeinfo(
        adjust_error_for_server_version(""), ['3', '5', '6*'],
        sbox.repo_url + '/A', A_COPY_path, '--show-revs', 'eligible')
    # But if we drop down to A_COPY/D, r6 should show as eligible because it
    # was only merged into A_COPY, no deeper.
    svntest.actions.run_and_verify_mergeinfo(
        adjust_error_for_server_version(""), ['4'], sbox.repo_url + '/A/D',
        D_COPY_path, '--show-revs', 'merged')
    svntest.actions.run_and_verify_mergeinfo(
        adjust_error_for_server_version(""), ['3', '6'],
        sbox.repo_url + '/A/D', D_COPY_path, '--show-revs', 'eligible')
Example #3
0
def automatic_conflict_resolution(sbox):
  "resolve -R --accept [base | mf | tf]"

  sbox.build()
  wc_dir = sbox.wc_dir

  # Some paths we'll care about
  A_COPY_path   = os.path.join(wc_dir, "A_COPY")
  psi_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "H", "psi")

  # Branch A to A_COPY in r2, then make some changes under 'A' in r3-6.
  wc_disk, wc_status = set_up_branch(sbox)

  # Make a change on the A_COPY branch such that a subsequent merge
  # conflicts.
  svntest.main.file_write(psi_COPY_path, "Branch content.\n")
  svntest.actions.run_and_verify_svn(None, [],
                                     'commit', '-m', 'log msg', wc_dir)
  def do_text_conflicting_merge():
    svntest.actions.run_and_verify_svn(None, [],
                                       'revert', '--recursive', A_COPY_path)
    svntest.actions.run_and_verify_svn(
      expected_merge_output([[3]], [
        "C    %s\n" % psi_COPY_path,
        " U   %s\n" % A_COPY_path],
        target=A_COPY_path, text_conflicts=1),
      [], 'merge', '-c3', '--allow-mixed-revisions',
      sbox.repo_url + '/A',
      A_COPY_path)

  # Test 'svn resolve -R --accept base'
  do_text_conflicting_merge()
  svntest.actions.run_and_verify_resolve([psi_COPY_path],
                                         '-R', '--accept', 'base',
                                         A_COPY_path)
  wc_disk.tweak('A_COPY/D/H/psi', contents="This is the file 'psi'.\n")
  svntest.actions.verify_disk(wc_dir, wc_disk)

  # Test 'svn resolve -R --accept mine-full'
  do_text_conflicting_merge()
  svntest.actions.run_and_verify_resolve([psi_COPY_path],
                                         '-R', '--accept', 'mine-full',
                                         A_COPY_path)
  wc_disk.tweak('A_COPY/D/H/psi', contents="Branch content.\n")
  svntest.actions.verify_disk(wc_dir, wc_disk)

  # Test 'svn resolve -R --accept theirs-full'
  do_text_conflicting_merge()
  svntest.actions.run_and_verify_resolve([psi_COPY_path],
                                         '-R', '--accept', 'tf',
                                         A_COPY_path)
  wc_disk.tweak('A_COPY/D/H/psi', contents="New content")
  svntest.actions.verify_disk(wc_dir, wc_disk)
Example #4
0
def merge_sensitive_blame_and_empty_mergeinfo(sbox):
    "blame -g handles changes from empty mergeinfo"

    sbox.build()
    wc_dir = sbox.wc_dir
    wc_disk, wc_status = set_up_branch(sbox, True)

    A_COPY_path = os.path.join(wc_dir, 'A_COPY')
    psi_path = os.path.join(wc_dir, 'A', 'D', 'H', 'psi')
    psi_COPY_path = os.path.join(wc_dir, 'A_COPY', 'D', 'H', 'psi')

    # Make an edit to A/D/H/psi in r3.
    svntest.main.file_append(psi_path, "trunk edit in revision three.\n")
    sbox.simple_commit(message='trunk edit')

    # Merge r3 from A to A_COPY, reverse merge r3 from A/D/H/psi
    # to A_COPY/D/H/psi, and commit as r4.  This results in empty
    # mergeinfo on A_COPY/D/H/psi.
    svntest.main.run_svn(None, 'up', wc_dir)
    svntest.main.run_svn(None, 'merge', '-c3', sbox.repo_url + '/A',
                         A_COPY_path)
    svntest.main.run_svn(None, 'merge', '-c-3', sbox.repo_url + '/A/D/H/psi',
                         psi_COPY_path)
    sbox.simple_commit(
        message='Sync merge A to A_COPY excepting A_COPY/D/H/psi')

    # Make an edit to A/D/H/psi in r5.
    svntest.main.file_append(psi_path, "trunk edit in revision five.\n")
    sbox.simple_commit(message='trunk edit')

    # Sync merge A/D/H/psi to A_COPY/D/H/psi and commit as r6.  This replaces
    # the empty mergeinfo on A_COPY/D/H/psi with '/A/D/H/psi:2-5'.
    svntest.main.run_svn(None, 'up', wc_dir)
    svntest.main.run_svn(None, 'merge', sbox.repo_url + '/A/D/H/psi',
                         psi_COPY_path)
    sbox.simple_commit(message='Sync merge A/D/H/psi to A_COPY/D/H/psi')

    # Check the blame -g output:
    # Currently this test fails because the trunk edit done in r3 is
    # reported as having been done in r5.
    #
    #   >svn blame -g A_COPY\D\H\psi
    #          1    jrandom This is the file 'psi'.
    #   G      5    jrandom trunk edit in revision three.
    #   G      5    jrandom trunk edit in revision five.
    expected_output = [
        "       1    jrandom This is the file 'psi'.\n",
        "G      3    jrandom trunk edit in revision three.\n",
        "G      5    jrandom trunk edit in revision five.\n"
    ]
    svntest.actions.run_and_verify_svn(expected_output, [], 'blame', '-g',
                                       psi_COPY_path)
Example #5
0
def merge_sensitive_blame_and_empty_mergeinfo(sbox):
  "blame -g handles changes from empty mergeinfo"

  sbox.build()
  wc_dir = sbox.wc_dir
  wc_disk, wc_status = set_up_branch(sbox, True)

  A_COPY_path   = os.path.join(wc_dir, 'A_COPY')
  psi_path      = os.path.join(wc_dir, 'A', 'D', 'H', 'psi')
  psi_COPY_path = os.path.join(wc_dir, 'A_COPY', 'D', 'H', 'psi')

  # Make an edit to A/D/H/psi in r3.
  svntest.main.file_append(psi_path, "trunk edit in revision three.\n")
  sbox.simple_commit(message='trunk edit')

  # Merge r3 from A to A_COPY, reverse merge r3 from A/D/H/psi
  # to A_COPY/D/H/psi, and commit as r4.  This results in empty
  # mergeinfo on A_COPY/D/H/psi.
  svntest.main.run_svn(None, 'up', wc_dir)
  svntest.main.run_svn(None, 'merge', '-c3',
                       sbox.repo_url + '/A', A_COPY_path)
  svntest.main.run_svn(None, 'merge', '-c-3',
                       sbox.repo_url + '/A/D/H/psi', psi_COPY_path)
  sbox.simple_commit(message='Sync merge A to A_COPY excepting A_COPY/D/H/psi')

  # Make an edit to A/D/H/psi in r5.
  svntest.main.file_append(psi_path, "trunk edit in revision five.\n")
  sbox.simple_commit(message='trunk edit')

  # Sync merge A/D/H/psi to A_COPY/D/H/psi and commit as r6.  This replaces
  # the empty mergeinfo on A_COPY/D/H/psi with '/A/D/H/psi:2-5'.
  svntest.main.run_svn(None, 'up', wc_dir)
  svntest.main.run_svn(None, 'merge',  sbox.repo_url + '/A/D/H/psi',
                       psi_COPY_path)
  sbox.simple_commit(message='Sync merge A/D/H/psi to A_COPY/D/H/psi')

  # Check the blame -g output:
  # Currently this test fails because the trunk edit done in r3 is
  # reported as having been done in r5.
  #
  #   >svn blame -g A_COPY\D\H\psi
  #          1    jrandom This is the file 'psi'.
  #   G      5    jrandom trunk edit in revision three.
  #   G      5    jrandom trunk edit in revision five.
  expected_output = [
      "       1    jrandom This is the file 'psi'.\n",
      "G      3    jrandom trunk edit in revision three.\n",
      "G      5    jrandom trunk edit in revision five.\n"]
  svntest.actions.run_and_verify_svn(expected_output, [],
                                    'blame', '-g', psi_COPY_path)
def natural_history_is_not_eligible_nor_merged(sbox):
  "natural history is not eligible nor merged"

  sbox.build()
  wc_dir = sbox.wc_dir
  wc_disk, wc_status = set_up_branch(sbox)

  nu_path      = os.path.join(wc_dir, 'A', 'C', 'nu')
  A_COPY_path  = os.path.join(wc_dir, 'A_COPY')
  nu_COPY_path = os.path.join(wc_dir, 'A_COPY', 'C', 'nu')

  # r7 - Add a new file A/C/nu
  svntest.main.file_write(nu_path, "This is the file 'nu'.\n")
  svntest.actions.run_and_verify_svn(None, [], 'add', nu_path)
  svntest.actions.run_and_verify_svn(None, [], 'ci',
                                     '-m', 'Add a file', wc_dir)

  # r8 - Sync merge ^/A to A_COPY
  svntest.actions.run_and_verify_svn(None, [], 'merge',
                                     sbox.repo_url + '/A', A_COPY_path)
  svntest.actions.run_and_verify_svn(None, [], 'ci',
                                     '-m', 'Add a file', wc_dir)

  # r9 - Modify the file added in r7
  svntest.main.file_write(nu_path, "Modification to file 'nu'.\n")
  svntest.actions.run_and_verify_svn(None, [], 'ci',
                                     '-m', 'Modify added file', wc_dir)

  # r10 - Merge ^/A/C/nu to A_COPY/C/nu, creating subtree mergeinfo.
  svntest.actions.run_and_verify_svn(None, [], 'merge',
                                     sbox.repo_url + '/A/C/nu', nu_COPY_path)
  svntest.actions.run_and_verify_svn(None, [], 'ci',
                                     '-m', 'Add a file', wc_dir)
  svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir)

  # We've effectively merged everything from ^/A to A_COPY, check
  # that svn mergeinfo -R agrees.
  #
  # First check if there are eligible revisions, there should be none.
  svntest.actions.run_and_verify_mergeinfo(
    adjust_error_for_server_version(''),
    [], sbox.repo_url + '/A',
    A_COPY_path, '--show-revs', 'eligible', '-R')

  # Now check that all operative revisions show as merged.
  svntest.actions.run_and_verify_mergeinfo(
    adjust_error_for_server_version(''),
    ['3','4','5','6','7','9'], sbox.repo_url + '/A',
    A_COPY_path, '--show-revs', 'merged', '-R')
Example #7
0
def natural_history_is_not_eligible_nor_merged(sbox):
  "natural history is not eligible nor merged"

  sbox.build()
  wc_dir = sbox.wc_dir
  wc_disk, wc_status = set_up_branch(sbox)

  nu_path      = os.path.join(wc_dir, 'A', 'C', 'nu')
  A_COPY_path  = os.path.join(wc_dir, 'A_COPY')
  nu_COPY_path = os.path.join(wc_dir, 'A_COPY', 'C', 'nu')

  # r7 - Add a new file A/C/nu
  svntest.main.file_write(nu_path, "This is the file 'nu'.\n")
  svntest.actions.run_and_verify_svn(None, [], 'add', nu_path)
  svntest.actions.run_and_verify_svn(None, [], 'ci',
                                     '-m', 'Add a file', wc_dir)

  # r8 - Sync merge ^/A to A_COPY
  svntest.actions.run_and_verify_svn(None, [], 'merge',
                                     sbox.repo_url + '/A', A_COPY_path)
  svntest.actions.run_and_verify_svn(None, [], 'ci',
                                     '-m', 'Add a file', wc_dir)

  # r9 - Modify the file added in r7
  svntest.main.file_write(nu_path, "Modification to file 'nu'.\n")
  svntest.actions.run_and_verify_svn(None, [], 'ci',
                                     '-m', 'Modify added file', wc_dir)

  # r10 - Merge ^/A/C/nu to A_COPY/C/nu, creating subtree mergeinfo.
  svntest.actions.run_and_verify_svn(None, [], 'merge',
                                     sbox.repo_url + '/A/C/nu', nu_COPY_path)
  svntest.actions.run_and_verify_svn(None, [], 'ci',
                                     '-m', 'Add a file', wc_dir)
  svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir)

  # We've effectively merged everything from ^/A to A_COPY, check
  # that svn mergeinfo -R agrees.
  #
  # First check if there are eligible revisions, there should be none.
  svntest.actions.run_and_verify_mergeinfo(
    adjust_error_for_server_version(''),
    [], sbox.repo_url + '/A',
    A_COPY_path, '--show-revs', 'eligible', '-R')

  # Now check that all operative revisions show as merged.
  svntest.actions.run_and_verify_mergeinfo(
    adjust_error_for_server_version(''),
    ['3','4','5','6','7','9'], sbox.repo_url + '/A',
    A_COPY_path, '--show-revs', 'merged', '-R')
Example #8
0
def wc_target_inherits_mergeinfo_from_repos(sbox):
  "wc target inherits mergeinfo from repos"

  sbox.build()
  wc_dir = sbox.wc_dir
  wc_disk, wc_status = set_up_branch(sbox, nbr_of_branches=2)

  A_COPY_path   = os.path.join(wc_dir, 'A_COPY')
  rho_COPY_path = os.path.join(wc_dir, 'A_COPY', 'D', 'G', 'rho')
  gamma_2_path  = os.path.join(wc_dir, 'A_COPY_2', 'D', 'gamma')
  tau_path      = os.path.join(wc_dir, 'A', 'D', 'G', 'tau')
  D_COPY_path   = os.path.join(wc_dir, 'A_COPY', 'D')

  # Merge -c5 ^/A/D/G/rho A_COPY\D\G\rho
  # Merge -c7 ^/A A_COPY
  # Commit as r8
  #
  # This gives us some explicit mergeinfo on the "branch" root and
  # one of its subtrees:
  #
  #   Properties on 'A_COPY\D\G\rho':
  #     svn:mergeinfo
  #       /A/D/G/rho:5
  #   Properties on 'A_COPY':
  #     svn:mergeinfo
  #       /A:7
  svntest.actions.run_and_verify_svn(None, [], 'merge',
                                     sbox.repo_url + '/A/D/G/rho',
                                     rho_COPY_path, '-c5')
  svntest.actions.run_and_verify_svn(None, [], 'merge',
                                     sbox.repo_url + '/A',
                                     A_COPY_path, '-c7')
  svntest.actions.run_and_verify_svn(None, [], 'ci', '-m',
                                     'Cherrypicks to branch subtree and root',
                                     wc_dir)

  # Checkout a new wc rooted at ^/A_COPY/D.
  subtree_wc = sbox.add_wc_path('D_COPY')
  svntest.actions.run_and_verify_svn(None, [], 'co',
                                     sbox.repo_url + '/A_COPY/D',
                                     subtree_wc)

  # Check the merged and eligible revisions both recursively and
  # non-recursively.

  # Eligible : Non-recursive
  svntest.actions.run_and_verify_mergeinfo(
    adjust_error_for_server_version(''),
    ['4','5'], sbox.repo_url + '/A/D', subtree_wc,
    '--show-revs', 'eligible')

  # Eligible : Recursive
  svntest.actions.run_and_verify_mergeinfo(
    adjust_error_for_server_version(''),
    ['4'], sbox.repo_url + '/A/D', subtree_wc,
    '--show-revs', 'eligible', '-R')

  # Merged : Non-recursive
  svntest.actions.run_and_verify_mergeinfo(
    adjust_error_for_server_version(''),
    ['7'], sbox.repo_url + '/A/D', subtree_wc,
    '--show-revs', 'merged')

  # Merged : Recursive
  svntest.actions.run_and_verify_mergeinfo(
    adjust_error_for_server_version(''),
    ['5','7'], sbox.repo_url + '/A/D', subtree_wc,
    '--show-revs', 'merged', '-R')

  # Test that intersecting revisions in the 'svn mergeinfo' target
  # from one source don't show up as merged when asking about a different
  # source.
  #
  # In r9 make a change that effects two branches:
  svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir)
  svntest.main.file_write(gamma_2_path, "New content.\n")
  svntest.main.file_write(tau_path, "New content.\n")
  svntest.actions.run_and_verify_svn(None, [], 'ci', '-m',
                                     'Make changes under both A and A_COPY_2',
                                     wc_dir)

  # In r10 merge r9 from A_COPY_2 to A_COPY.
  #
  # This gives us this mergeinfo:
  #
  #   Properties on 'A_COPY':
  #     svn:mergeinfo
  #       /A:7
  #       /A_COPY_2:9
  #   Properties on 'A_COPY\D\G\rho':
  #     svn:mergeinfo
  #       /A/D/G/rho:5
  svntest.actions.run_and_verify_svn(None, [], 'merge',
                                     sbox.repo_url + '/A_COPY_2',
                                     A_COPY_path, '-c9')
  svntest.actions.run_and_verify_svn(None, [], 'ci', '-m',
                                     'Merge r8 from A_COPY_2 to A_COPY',
                                     wc_dir)

  def test_svn_mergeinfo_4_way(wc_target):
    # Eligible : Non-recursive
    svntest.actions.run_and_verify_mergeinfo(
      adjust_error_for_server_version(''),
      ['4','5','9'], sbox.repo_url + '/A/D', wc_target,
      '--show-revs', 'eligible')

    # Eligible : Recursive
    svntest.actions.run_and_verify_mergeinfo(
      adjust_error_for_server_version(''),
      ['4','9'], sbox.repo_url + '/A/D', wc_target,
      '--show-revs', 'eligible', '-R')

    # Merged : Non-recursive
    svntest.actions.run_and_verify_mergeinfo(
      adjust_error_for_server_version(''),
      ['7'], sbox.repo_url + '/A/D', wc_target,
      '--show-revs', 'merged')

    # Merged : Recursive
    svntest.actions.run_and_verify_mergeinfo(
      adjust_error_for_server_version(''),
      ['5','7'], sbox.repo_url + '/A/D', wc_target,
      '--show-revs', 'merged', '-R')

  # Test while the target is the full WC and then with the subtree WC:
  svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir)
  svntest.actions.run_and_verify_svn(None, [], 'up', subtree_wc)

  test_svn_mergeinfo_4_way(D_COPY_path)
  test_svn_mergeinfo_4_way(subtree_wc)
Example #9
0
def mergeinfo_on_pegged_wc_path(sbox):
  "svn mergeinfo on pegged working copy target"

  sbox.build()
  wc_dir = sbox.wc_dir
  expected_disk, expected_status = set_up_branch(sbox)

  # Some paths we'll care about
  A_path          = os.path.join(wc_dir, "A")
  A_COPY_path     = os.path.join(wc_dir, "A_COPY")
  psi_COPY_path   = os.path.join(wc_dir, "A_COPY", "D", "H", "psi")
  omega_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "H", "omega")
  beta_COPY_path  = os.path.join(wc_dir, "A_COPY", "B", "E", "beta")

  # Do a couple merges
  #
  # r7 - Merge -c3,6 from A to A_COPY.
  svntest.actions.run_and_verify_svn(
    expected_merge_output([[3],[6]],
                          ['U    ' + psi_COPY_path + '\n',
                           'U    ' + omega_COPY_path + '\n',
                           ' U   ' + A_COPY_path + '\n',
                           ' G   ' + A_COPY_path + '\n',]),
    [], 'merge', '-c3,6', sbox.repo_url + '/A', A_COPY_path)
  svntest.actions.run_and_verify_svn(None, [],
                                     'ci', wc_dir,
                                     '-m', 'Merge r3 and r6')

  # r8 - Merge -c5 from A to A_COPY.
  svntest.actions.run_and_verify_svn(
    expected_merge_output([[5]],
                          ['U    ' + beta_COPY_path + '\n',
                           ' U   ' + A_COPY_path + '\n']),
    [], 'merge', '-c5', '--allow-mixed-revisions',
    sbox.repo_url + '/A', A_COPY_path)
  svntest.actions.run_and_verify_svn(None, [],
                                     'ci', wc_dir,
                                     '-m', 'Merge r5')

  # Ask for merged and eligible revisions to A_COPY pegged at various values.
  # Prior to issue #3180 fix the peg revision was ignored.
  #
  # A_COPY pegged to non-existent revision
  svntest.actions.run_and_verify_mergeinfo(
    adjust_error_for_server_version('.*No such revision 99'),
    [], A_path, A_COPY_path + '@99', '--show-revs', 'merged')

  # A_COPY@BASE
  svntest.actions.run_and_verify_mergeinfo(
    adjust_error_for_server_version(''),
    ['3','5','6'], A_path, A_COPY_path + '@BASE', '--show-revs', 'merged')

  # A_COPY@HEAD
  svntest.actions.run_and_verify_mergeinfo(
    adjust_error_for_server_version(''),
    ['3','5','6'], A_path, A_COPY_path + '@HEAD', '--show-revs', 'merged')

  # A_COPY@4 (Prior to any merges)
  svntest.actions.run_and_verify_mergeinfo(
    adjust_error_for_server_version(''),
    [], A_path, A_COPY_path + '@4', '--show-revs', 'merged')

  # A_COPY@COMMITTED (r8)
  svntest.actions.run_and_verify_mergeinfo(
    adjust_error_for_server_version(''),
    ['3','5','6'], A_path, A_COPY_path + '@COMMITTED', '--show-revs',
    'merged')

  # A_COPY@PREV (r7)
  svntest.actions.run_and_verify_mergeinfo(
    adjust_error_for_server_version(''),
    ['3', '6'], A_path, A_COPY_path + '@PREV', '--show-revs', 'merged')

  # A_COPY@BASE
  svntest.actions.run_and_verify_mergeinfo(
    adjust_error_for_server_version(''),
    ['4'], A_path, A_COPY_path + '@BASE', '--show-revs', 'eligible')

  # A_COPY@HEAD
  svntest.actions.run_and_verify_mergeinfo(
    adjust_error_for_server_version(''),
    ['4'], A_path, A_COPY_path + '@HEAD', '--show-revs', 'eligible')

  # A_COPY@4 (Prior to any merges)
  svntest.actions.run_and_verify_mergeinfo(
    adjust_error_for_server_version(''),
    ['3', '4', '5', '6'], A_path, A_COPY_path + '@4', '--show-revs', 'eligible')

  # A_COPY@COMMITTED (r8)
  svntest.actions.run_and_verify_mergeinfo(
    adjust_error_for_server_version(''),
    ['4'], A_path, A_COPY_path + '@COMMITTED', '--show-revs',
    'eligible')

  # A_COPY@PREV (r7)
  svntest.actions.run_and_verify_mergeinfo(
    adjust_error_for_server_version(''),
    ['4', '5'], A_path, A_COPY_path + '@PREV', '--show-revs', 'eligible')
Example #10
0
def recursive_mergeinfo(sbox):
  "test svn mergeinfo -R"

  sbox.build()
  wc_dir = sbox.wc_dir
  expected_disk, expected_status = set_up_branch(sbox)

  # Some paths we'll care about
  A_path          = os.path.join(wc_dir, "A")
  A_COPY_path     = os.path.join(wc_dir, "A_COPY")
  B_COPY_path     = os.path.join(wc_dir, "A_COPY", "B")
  C_COPY_path     = os.path.join(wc_dir, "A_COPY", "C")
  rho_COPY_path   = os.path.join(wc_dir, "A_COPY", "D", "G", "rho")
  H_COPY_path     = os.path.join(wc_dir, "A_COPY", "D", "H")
  F_COPY_path     = os.path.join(wc_dir, "A_COPY", "B", "F")
  omega_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "H", "omega")
  beta_COPY_path  = os.path.join(wc_dir, "A_COPY", "B", "E", "beta")
  A2_path         = os.path.join(wc_dir, "A2")
  nu_path         = os.path.join(wc_dir, "A2", "B", "F", "nu")
  nu_COPY_path    = os.path.join(wc_dir, "A_COPY", "B", "F", "nu")
  nu2_path        = os.path.join(wc_dir, "A2", "C", "nu2")

  # Rename A to A2 in r7.
  svntest.actions.run_and_verify_svn(exp_noop_up_out(6), [], 'up', wc_dir)
  svntest.actions.run_and_verify_svn(None, [],
                                     'ren', A_path, A2_path)
  svntest.actions.run_and_verify_svn(None, [],
                                     'ci', wc_dir, '-m', 'rename A to A2')

  # Add the files A/B/F/nu and A/C/nu2 and commit them as r8.
  svntest.main.file_write(nu_path, "A new file.\n")
  svntest.main.file_write(nu2_path, "Another new file.\n")
  svntest.main.run_svn(None, "add", nu_path, nu2_path)
  svntest.actions.run_and_verify_svn(None, [],
                                     'ci', wc_dir, '-m', 'Add 2 new files')

  # Do several merges to create varied subtree mergeinfo

  # Merge r4 from A2 to A_COPY at depth empty
  svntest.actions.run_and_verify_svn(exp_noop_up_out(8), [], 'up',
                                     wc_dir)
  svntest.actions.run_and_verify_svn(
    expected_merge_output([[4]], ' U   ' + A_COPY_path + '\n'),
    [], 'merge', '-c4', '--depth', 'empty',
    sbox.repo_url + '/A2',
    A_COPY_path)

  # Merge r6 from A2/D/H to A_COPY/D/H
  svntest.actions.run_and_verify_svn(
    expected_merge_output([[6]],
                          ['U    ' + omega_COPY_path + '\n',
                           ' G   ' + H_COPY_path + '\n']),
    [], 'merge', '-c6',
    sbox.repo_url + '/A2/D/H',
    H_COPY_path)

  # Merge r5 from A2 to A_COPY
  svntest.actions.run_and_verify_svn(
    expected_merge_output([[5]],
                          ['U    ' + beta_COPY_path + '\n',
                           ' G   ' + A_COPY_path + '\n',
                           ' G   ' + B_COPY_path + '\n',
                           ' U   ' + B_COPY_path + '\n',], # Elision
                          elides=True),
    [], 'merge', '-c5',
    sbox.repo_url + '/A2',
    A_COPY_path)

  # Reverse merge -r5 from A2/C to A_COPY/C leaving empty mergeinfo on
  # A_COPY/C.
  svntest.actions.run_and_verify_svn(
    expected_merge_output([[-5]],
                          ' G   ' + C_COPY_path + '\n'),
    [], 'merge', '-c-5',
    sbox.repo_url + '/A2/C', C_COPY_path)

  # Merge r8 from A2/B/F to A_COPY/B/F
  svntest.actions.run_and_verify_svn(
    expected_merge_output([[8]],
                          ['A    ' + nu_COPY_path + '\n',
                           ' G   ' + F_COPY_path + '\n']),
    [], 'merge', '-c8',
    sbox.repo_url + '/A2/B/F',
    F_COPY_path)

  # Commit everything this far as r9
  svntest.actions.run_and_verify_svn(None, [],
                                     'ci', wc_dir, '-m', 'Many merges')
  svntest.actions.run_and_verify_svn(exp_noop_up_out(9), [], 'up',
                                     wc_dir)

  # Test svn mergeinfo -R / --depth infinity.

  # Asking for eligible revisions from A2 to A_COPY should show:
  #
  #  r3  - Was never merged.
  #
  #  r4 - Was merged at depth empty, so while there is mergeinfo for the
  #       revision, the actual text change to A_COPY/D/G/rho hasn't yet
  #       happened.
  #
  #  r8* - Was only partially merged to the subtree at A_COPY/B/F.  The
  #        addition of A_COPY/C/nu2 is still outstanding.
  svntest.actions.run_and_verify_mergeinfo(adjust_error_for_server_version(""),
                                           ['3', '4*', '8*'],
                                           sbox.repo_url + '/A2',
                                           sbox.repo_url + '/A_COPY',
                                           '--show-revs', 'eligible', '-R')
  # Do the same as above, but test that we can request the revisions
  # in reverse order.
  svntest.actions.run_and_verify_mergeinfo(adjust_error_for_server_version(""),
                                           ['8*', '4*', '3'],
                                           sbox.repo_url + '/A2',
                                           sbox.repo_url + '/A_COPY',
                                           '--show-revs', 'eligible', '-R',
                                           '-r', '9:0')

  # Asking for merged revisions from A2 to A_COPY should show:
  #
  #  r4* - Was merged at depth empty, so while there is mergeinfo for the
  #        revision, the actual text change to A_COPY/D/G/rho hasn't yet
  #        happened.
  #
  #  r5  - Was merged at depth infinity to the root of the 'branch', so it
  #        should show as fully merged.
  #
  #  r6  - This was a subtree merge, but since the subtree A_COPY/D/H was
  #        the ancestor of the only change made in r6 it is considered
  #        fully merged.
  #
  #  r8* - Was only partially merged to the subtree at A_COPY/B/F.  The
  #        addition of A_COPY/C/nu2 is still outstanding.
  svntest.actions.run_and_verify_mergeinfo(adjust_error_for_server_version(""),
                                           ['4*', '5', '6', '8*'],
                                           A2_path,
                                           A_COPY_path,
                                           '--show-revs', 'merged',
                                           '--depth', 'infinity')
  # Do the same as above, but test that we can request the revisions
  # in reverse order.
  svntest.actions.run_and_verify_mergeinfo(adjust_error_for_server_version(""),
                                           ['8*', '6', '5', '4*'],
                                           A2_path,
                                           A_COPY_path,
                                           '--show-revs', 'merged',
                                           '--depth', 'infinity',
                                           '-r', '9:0')

  # A couple tests of problems found with initial issue #3242 fixes.
  # We should be able to check for the merged revs from a URL to a URL
  # when the latter has explicit mergeinfo...
  svntest.actions.run_and_verify_mergeinfo(
    adjust_error_for_server_version(''), ['6'],
    sbox.repo_url + '/A2/D/H',
    sbox.repo_url + '/A_COPY/D/H',
    '--show-revs', 'merged')
  # ...and when the latter has inherited mergeinfo.
  svntest.actions.run_and_verify_mergeinfo(
    adjust_error_for_server_version(''), ['6'],
    sbox.repo_url + '/A2/D/H/omega',
    sbox.repo_url + '/A_COPY/D/H/omega',
    '--show-revs', 'merged')
Example #11
0
def non_inheritable_mergeinfo(sbox):
  "non-inheritable mergeinfo shows as merged"

  sbox.build()
  wc_dir = sbox.wc_dir
  expected_disk, expected_status = set_up_branch(sbox)

  # Some paths we'll care about
  A_COPY_path   = os.path.join(wc_dir, "A_COPY")
  D_COPY_path   = os.path.join(wc_dir, "A_COPY", "D")
  rho_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "G", "rho")

  # Update the WC, then merge r4 from A to A_COPY and r6 from A to A_COPY
  # at --depth empty and commit the merges as r7.
  svntest.actions.run_and_verify_svn(exp_noop_up_out(6), [], 'up',
                                     wc_dir)
  expected_status.tweak(wc_rev=6)
  svntest.actions.run_and_verify_svn(
    expected_merge_output([[4]],
                          ['U    ' + rho_COPY_path + '\n',
                           ' U   ' + A_COPY_path + '\n',]),
    [], 'merge', '-c4',
    sbox.repo_url + '/A',
    A_COPY_path)
  svntest.actions.run_and_verify_svn(
    expected_merge_output([[6]], ' G   ' + A_COPY_path + '\n'),
    [], 'merge', '-c6',
    sbox.repo_url + '/A',
    A_COPY_path, '--depth', 'empty')
  expected_output = wc.State(wc_dir, {
    'A_COPY'         : Item(verb='Sending'),
    'A_COPY/D/G/rho' : Item(verb='Sending'),
    })
  expected_status.tweak('A_COPY', 'A_COPY/D/G/rho', wc_rev=7)
  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
                                        expected_status)

  # Update the WC a last time to ensure full inheritance.
  svntest.actions.run_and_verify_svn(exp_noop_up_out(7), [], 'up',
                                     wc_dir)

  # Despite being non-inheritable, r6 should still show as merged to A_COPY
  # and not eligible for merging.
  svntest.actions.run_and_verify_mergeinfo(adjust_error_for_server_version(""),
                                           ['4','6*'],
                                           sbox.repo_url + '/A',
                                           A_COPY_path,
                                           '--show-revs', 'merged')
  svntest.actions.run_and_verify_mergeinfo(adjust_error_for_server_version(""),
                                           ['3','5','6*'],
                                           sbox.repo_url + '/A',
                                           A_COPY_path,
                                           '--show-revs', 'eligible')
  # But if we drop down to A_COPY/D, r6 should show as eligible because it
  # was only merged into A_COPY, no deeper.
  svntest.actions.run_and_verify_mergeinfo(adjust_error_for_server_version(""),
                                           ['4'],
                                           sbox.repo_url + '/A/D',
                                           D_COPY_path,
                                           '--show-revs', 'merged')
  svntest.actions.run_and_verify_mergeinfo(adjust_error_for_server_version(""),
                                           ['3','6'],
                                           sbox.repo_url + '/A/D',
                                           D_COPY_path,
                                           '--show-revs', 'eligible')
Example #12
0
def mergeinfo_and_skipped_paths(sbox):
  "skipped paths get overriding mergeinfo"

  # Test that we override the mergeinfo for child paths which weren't
  # actually merged because they were skipped.
  #
  # This test covers paths skipped because:
  #
  #   1) The source of a merge is inaccessible due to authz restrictions.
  #   2) Destination of merge is inaccessible due to authz restrictions.
  #   3) Source *and* destination of merge is inaccessible due to authz
  #      restrictions.

  sbox.build()
  wc_dir = sbox.wc_dir
  wc_disk, wc_status = set_up_branch(sbox, False, 3)

  # Create a restrictive authz where part of the merge source and part
  # of the target are inaccesible.
  write_restrictive_svnserve_conf(sbox.repo_dir)
  write_authz_file(sbox, {"/"               : svntest.main.wc_author +"=rw",
                          # Make a directory in the merge source inaccessible.
                          "/A/B/E"            : svntest.main.wc_author + "=",
                          # Make a file and dir in the merge destination
                          # inaccessible.
                          "/A_COPY_2/D/H/psi" : svntest.main.wc_author + "=",
                          "/A_COPY_2/D/G" : svntest.main.wc_author + "=",
                          # Make the source and destination inaccessible.
                          "/A_COPY_3/B/E"     : svntest.main.wc_author + "=",
                          })

  # Checkout just the branch under the newly restricted authz.
  wc_restricted = sbox.add_wc_path('restricted')
  svntest.actions.run_and_verify_svn(None, [], 'checkout',
                                     sbox.repo_url,
                                     wc_restricted)

  # Some paths we'll use in the second WC.
  A_COPY_path = os.path.join(wc_restricted, "A_COPY")
  A_COPY_2_path = os.path.join(wc_restricted, "A_COPY_2")
  A_COPY_2_H_path = os.path.join(wc_restricted, "A_COPY_2", "D", "H")
  A_COPY_3_path = os.path.join(wc_restricted, "A_COPY_3")
  omega_path = os.path.join(wc_restricted, "A_COPY", "D", "H", "omega")
  zeta_path = sbox.ospath("A/D/H/zeta")

  # Merge r4:8 into the restricted WC's A_COPY.
  #
  # We expect A_COPY/B/E to be skipped because we can't access the source
  # and A_COPY/D/H/omega because it is missing.  Since we have A_COPY/B/E
  # we should override it's inherited mergeinfo, giving it just what it
  # inherited from A_COPY before the merge.
  expected_output = wc.State(A_COPY_path, {
    'D/G/rho'   : Item(status='U '),
    'D/H/psi'   : Item(status='U '),
    'D/H/omega' : Item(status='U '),
    })
  expected_mergeinfo_output = wc.State(A_COPY_path, {
    ''          : Item(status=' U'),
    'B/E'       : Item(status=' U'),
    })
  expected_elision_output = wc.State(A_COPY_path, {
    })
  expected_status = wc.State(A_COPY_path, {
    ''          : Item(status=' M', wc_rev=8),
    'D/H/chi'   : Item(status='  ', wc_rev=8),
    'D/H/psi'   : Item(status='M ', wc_rev=8),
    'D/H/omega' : Item(status='M ', wc_rev=8),
    'D/H'       : Item(status='  ', wc_rev=8),
    'D/G/pi'    : Item(status='  ', wc_rev=8),
    'D/G/rho'   : Item(status='M ', wc_rev=8),
    'D/G/tau'   : Item(status='  ', wc_rev=8),
    'D/G'       : Item(status='  ', wc_rev=8),
    'D/gamma'   : Item(status='  ', wc_rev=8),
    'D'         : Item(status='  ', wc_rev=8),
    'B/lambda'  : Item(status='  ', wc_rev=8),
    'B/E'       : Item(status=' M', 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),
    'B'         : Item(status='  ', wc_rev=8),
    'mu'        : Item(status='  ', wc_rev=8),
    'C'         : Item(status='  ', wc_rev=8),
    })
  expected_disk = wc.State('', {
    ''          : Item(props={SVN_PROP_MERGEINFO : '/A:5-8'}),
    'D/H/psi'   : Item("New content"),
    'D/H/chi'   : Item("This is the file 'chi'.\n"),
    'D/H/omega' : Item("New content"),
    'D/H'       : 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'       : Item(),
    'D/gamma'   : Item("This is the file 'gamma'.\n"),
    'D'         : Item(),
    'B/lambda'  : Item("This is the file 'lambda'.\n"),
    'B/E'       : Item(props={SVN_PROP_MERGEINFO : ''}),
    'B/E/alpha' : Item("This is the file 'alpha'.\n"),
    'B/E/beta'  : Item("This is the file 'beta'.\n"),
    'B/F'       : Item(),
    'B'         : Item(),
    'mu'        : Item("This is the file 'mu'.\n"),
    'C'         : Item(),
    })
  expected_skip = wc.State(A_COPY_path, {
    'B/E'       : Item(verb='Skipped missing target'),
    })
  svntest.actions.run_and_verify_merge(A_COPY_path, '4', '8',
                                       sbox.repo_url + '/A', None,
                                       expected_output,
                                       expected_mergeinfo_output,
                                       expected_elision_output,
                                       expected_disk,
                                       expected_status,
                                       expected_skip,
                                       check_props=True)

  # Merge r4:8 into the restricted WC's A_COPY_2.
  #
  # As before we expect A_COPY_2/B/E to be skipped because we can't access the
  # source but now the destination paths A_COPY_2/D/G, A_COPY_2/D/G/rho, and
  # A_COPY_2/D/H/psi should also be skipped because our test user doesn't have
  # access.
  #
  # After the merge the parents of the missing dest paths, A_COPY_2/D and
  # A_COPY_2/D/H get non-inheritable mergeinfo.  Those parents' children that
  # *are* present and are affected by the merge, only A_COPY_2/D/H/omega in
  # this case, get their own mergeinfo.  Note that A_COPY_2/D/H is both the
  # parent of a missing child and the sibling of missing child, but the former
  # always takes precedence in terms of getting *non*-inheritable mergeinfo.
  expected_output = wc.State(A_COPY_2_path, {
    'D/H/omega' : Item(status='U '),
    # Below the skip
    'D/G/rho'   : Item(status='  ', treeconflict='U'),
    })
  expected_mergeinfo_output = wc.State(A_COPY_2_path, {
    ''          : Item(status=' U'),
    'D'         : Item(status=' U'),
    'D/H'       : Item(status=' U'),
    'D/H/omega' : Item(status=' U'),
    'B/E'       : Item(status=' U'),
    })
  expected_elision_output = wc.State(A_COPY_2_path, {
    })
  expected_status = wc.State(A_COPY_2_path, {
    ''          : Item(status=' M', wc_rev=8),
    'D/H/chi'   : Item(status='  ', wc_rev=8),
    'D/H/omega' : Item(status='MM', wc_rev=8),
    'D/H'       : Item(status=' M', wc_rev=8),
    'D/gamma'   : Item(status='  ', wc_rev=8),
    'D'         : Item(status=' M', wc_rev=8),
    'B/lambda'  : Item(status='  ', wc_rev=8),
    'B/E'       : Item(status=' M', 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),
    'B'         : Item(status='  ', wc_rev=8),
    'mu'        : Item(status='  ', wc_rev=8),
    'C'         : Item(status='  ', wc_rev=8),
    })
  expected_disk = wc.State('', {
    ''          : Item(props={SVN_PROP_MERGEINFO : '/A:5-8'}),
    'D/H/omega' : Item("New content",
                       props={SVN_PROP_MERGEINFO : '/A/D/H/omega:5-8'}),
    'D/H/chi'   : Item("This is the file 'chi'.\n"),
    'D/H'       : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:5-8*'}),
    'D/gamma'   : Item("This is the file 'gamma'.\n"),
    'D'         : Item(props={SVN_PROP_MERGEINFO : '/A/D:5-8*'}),
    'B/lambda'  : Item("This is the file 'lambda'.\n"),
    'B/E'       : Item(props={SVN_PROP_MERGEINFO : ''}),
    'B/E/alpha' : Item("This is the file 'alpha'.\n"),
    'B/E/beta'  : Item("This is the file 'beta'.\n"),
    'B/F'       : Item(),
    'B'         : Item(),
    'mu'        : Item("This is the file 'mu'.\n"),
    'C'         : Item(),
    })
  expected_skip = wc.State(A_COPY_2_path, {
    'B/E'     : Item(verb='Skipped missing target'),
    'D/G'     : Item(verb='Skipped missing target'),
    'D/H/psi' : Item(verb='Skipped missing target'),
    })
  svntest.actions.run_and_verify_merge(A_COPY_2_path, '4', '8',
                                       sbox.repo_url + '/A', None,
                                       expected_output,
                                       expected_mergeinfo_output,
                                       expected_elision_output,
                                       expected_disk,
                                       expected_status,
                                       expected_skip,
                                       check_props=True)

  # Merge r5:7 into the restricted WC's A_COPY_3.
  #
  # Again A_COPY_3/B/E should be skipped, but because we can't access the
  # source *or* the destination we expect its parent A_COPY_3/B to get
  # non-inheritable mergeinfo.  A_COPY_3B's two existing siblings,
  # A_COPY_3/B/F and A_COPY_3/B/lambda are untouched by the merge so
  # neither gets any mergeinfo recorded.
  expected_output = wc.State(A_COPY_3_path, {
    'D/G/rho' : Item(status='U '),
    })
  expected_mergeinfo_output = wc.State(A_COPY_3_path, {
    ''  : Item(status=' U'),
    'B' : Item(status=' U'),
    })
  expected_elision_output = wc.State(A_COPY_3_path, {
    })
  expected_status = wc.State(A_COPY_3_path, {
    ''          : Item(status=' M', 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),
    'D/H'       : Item(status='  ', wc_rev=8),
    'D/gamma'   : Item(status='  ', wc_rev=8),
    'D'         : 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='M ', wc_rev=8),
    'D/G/tau'   : Item(status='  ', wc_rev=8),
    'B/lambda'  : Item(status='  ', wc_rev=8),
    'B/F'       : Item(status='  ', wc_rev=8),
    'B'         : Item(status=' M', wc_rev=8),
    'mu'        : Item(status='  ', wc_rev=8),
    'C'         : Item(status='  ', wc_rev=8),
    })
  expected_disk = wc.State('', {
    ''          : Item(props={SVN_PROP_MERGEINFO : '/A:6-7'}),
    'D/H/omega' : Item("This is the file 'omega'.\n"),
    'D/H/chi'   : Item("This is the file 'chi'.\n"),
    'D/H/psi'   : Item("This is the file 'psi'.\n"),
    'D/H'       : Item(),
    'D/gamma'   : Item("This is the file 'gamma'.\n"),
    '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"),
    'B/lambda'  : Item("This is the file 'lambda'.\n"),
    'B/F'       : Item(),
    'B'         : Item(props={SVN_PROP_MERGEINFO : '/A/B:6-7*'}),
    'mu'        : Item("This is the file 'mu'.\n"),
    'C'         : Item(),
    })
  expected_skip = wc.State(A_COPY_3_path,
                           {'B/E' : Item(verb='Skipped missing target')})
  svntest.actions.run_and_verify_merge(A_COPY_3_path, '5', '7',
                                       sbox.repo_url + '/A', None,
                                       expected_output,
                                       expected_mergeinfo_output,
                                       expected_elision_output,
                                       expected_disk,
                                       expected_status,
                                       expected_skip,
                                       check_props=True)
  svntest.actions.run_and_verify_svn(None, [], 'revert', '--recursive',
                                     wc_restricted)

  # Test issue #2997.  If a merge requires two separate editor drives and the
  # first is non-operative we should still update the mergeinfo to reflect
  # this.
  #
  # Merge -c5 -c8 to the restricted WC's A_COPY_2/D/H.  r5 gets merged first
  # but is a no-op, r8 get's merged next and is operative so the mergeinfo
  # should be updated on the merge target to reflect both merges.
  expected_output = wc.State(A_COPY_2_H_path, {
    'omega' : Item(status='U '),
    })
  expected_elision_output = wc.State(A_COPY_2_H_path, {
    })
  expected_status = wc.State(A_COPY_2_H_path, {
    ''      : Item(status=' M', wc_rev=8),
    'chi'   : Item(status='  ', wc_rev=8),
    'omega' : Item(status='MM', wc_rev=8),
    })
  expected_disk = wc.State('', {
    ''      : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:5*,8*'}),
    'omega' : Item("New content",
                   props={SVN_PROP_MERGEINFO : '/A/D/H/omega:8'}),
    'chi'   : Item("This is the file 'chi'.\n"),
    })
  expected_skip = wc.State(A_COPY_2_H_path, {
    'psi'   : Item(verb='Skipped missing target'),
    })
  # Note we don't bother checking expected mergeinfo output because the
  # multiple merges being performed here, -c5 and -c8, will result in
  # first ' U' and then ' G' mergeinfo notifications.  Our expected
  # tree structures can't handle checking for multiple values for the
  # same key.
  svntest.actions.run_and_verify_merge(A_COPY_2_H_path, '4', '5',
                                       sbox.repo_url + '/A/D/H', None,
                                       expected_output,
                                       None, # expected_mergeinfo_output,
                                       expected_elision_output,
                                       expected_disk,
                                       expected_status,
                                       expected_skip,
                                       [], True, False,
                                       '-c5', '-c8',
                                       A_COPY_2_H_path)

  # Test issue #2829 'Improve handling for skipped paths encountered
  # during a merge'

  # Revert previous changes to restricted WC
  svntest.actions.run_and_verify_svn(None, [], 'revert', '--recursive',
                                     wc_restricted)
  # Add new path 'A/D/H/zeta'
  svntest.main.file_write(zeta_path, "This is the file 'zeta'.\n")
  svntest.actions.run_and_verify_svn(None, [], 'add', zeta_path)
  expected_output = wc.State(wc_dir, {'A/D/H/zeta' : Item(verb='Adding')})
  wc_status.add({'A/D/H/zeta' : Item(status='  ', wc_rev=9)})
  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
                                        wc_status)

  # Merge -r7:9 to the restricted WC's A_COPY_2/D/H.
  #
  # r9 adds a path, 'A_COPY_2/D/H/zeta', which has a missing sibling 'psi',
  # but since 'psi' is untouched by the merge it isn't skipped, and since it
  # isn't skipped, its parent 'A_COPY_2/D/H' won't get non-inheritable
  # mergeinfo set on it to describe the merge, so none of the parent's
  # children will get explicit mergeinfo -- see issue #4056.
  expected_output = wc.State(A_COPY_2_H_path, {
    'omega' : Item(status='U '),
    'zeta'  : Item(status='A '),
    })
  expected_mergeinfo_output = wc.State(A_COPY_2_H_path, {
    ''      : Item(status=' U'),
    'omega' : Item(status=' U'),
    })
  expected_elision_output = wc.State(A_COPY_2_H_path, {
    'omega' : Item(status=' U'),
    })
  expected_status = wc.State(A_COPY_2_H_path, {
    ''      : Item(status=' M', wc_rev=8),
    'chi'   : Item(status='  ', wc_rev=8),
    'omega' : Item(status='M ', wc_rev=8),
    'zeta'  : Item(status='A ', copied='+', wc_rev='-'),
    })
  expected_disk = wc.State('', {
    ''      : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:8-9'}),
    'omega' : Item("New content"),
    'chi'   : Item("This is the file 'chi'.\n"),
    'zeta'  : Item("This is the file 'zeta'.\n"),
    })
  expected_skip = wc.State(A_COPY_2_H_path, {})
  svntest.actions.run_and_verify_merge(A_COPY_2_H_path, '7', '9',
                                       sbox.repo_url + '/A/D/H', None,
                                       expected_output,
                                       expected_mergeinfo_output,
                                       expected_elision_output,
                                       expected_disk,
                                       expected_status,
                                       expected_skip,
                                       check_props=True)

  # Merge -r4:9 to the restricted WC's A_COPY_2/D/H.
  #
  # r9 adds a path, 'A_COPY_2/D/H/zeta', which has a parent with
  # non-inheritable mergeinfo (due to the fact 'A_COPY_2/D/H/psi' is missing
  # and skipped). 'A_COPY_2/D/H/zeta' must therefore get its own explicit
  # mergeinfo from this merge.
  svntest.actions.run_and_verify_svn(None, [], 'revert', '--recursive',
                                     wc_restricted)
  expected_output = wc.State(A_COPY_2_H_path, {
    'omega' : Item(status='U '),
    'zeta'  : Item(status='A '),
    })
  expected_mergeinfo_output = wc.State(A_COPY_2_H_path, {
    ''      : Item(status=' U'),
    'omega' : Item(status=' U'),
    'zeta'  : Item(status=' U'),
    })
  expected_elision_output = wc.State(A_COPY_2_H_path, {
    })
  expected_status = wc.State(A_COPY_2_H_path, {
    ''      : Item(status=' M', wc_rev=8),
    'chi'   : Item(status='  ', wc_rev=8),
    'omega' : Item(status='MM', wc_rev=8),
    'zeta'  : Item(status='A ', copied='+', wc_rev='-'),
    })
  expected_disk = wc.State('', {
    ''      : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:5-9*'}),
    'omega' : Item("New content",
                   props={SVN_PROP_MERGEINFO : '/A/D/H/omega:5-9'}),
    'chi'   : Item("This is the file 'chi'.\n"),
    'zeta'  : Item("This is the file 'zeta'.\n",
                   props={SVN_PROP_MERGEINFO : '/A/D/H/zeta:9'}),
    })
  expected_skip = wc.State(A_COPY_2_H_path, {
    'psi' : Item(verb='Skipped missing target'),
    })
  svntest.actions.run_and_verify_merge(A_COPY_2_H_path, '4', '9',
                                       sbox.repo_url + '/A/D/H', None,
                                       expected_output,
                                       expected_mergeinfo_output,
                                       expected_elision_output,
                                       expected_disk,
                                       expected_status,
                                       expected_skip,
                                       check_props=True)
Example #13
0
def reintegrate_fails_if_no_root_access(sbox):
  "reintegrate fails if no root access"

  # If a user is authorized to a reintegrate source and target, they
  # should be able to reintegrate, regardless of what authorization
  # they have to parents of the source and target.
  #
  # See https://issues.apache.org/jira/browse/SVN-3242#desc78

  # Some paths we'll care about
  wc_dir = sbox.wc_dir
  A_path          = sbox.ospath('A')
  A_COPY_path     = sbox.ospath('A_COPY')
  beta_COPY_path  = sbox.ospath('A_COPY/B/E/beta')
  rho_COPY_path   = sbox.ospath('A_COPY/D/G/rho')
  omega_COPY_path = sbox.ospath('A_COPY/D/H/omega')
  psi_COPY_path   = sbox.ospath('A_COPY/D/H/psi')

  # Copy A@1 to A_COPY in r2, and then make some changes 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(sbox.ospath("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)
  expected_disk.tweak('A_COPY/mu', contents='Changed on the branch.')

  # Update the WC.
  svntest.main.run_svn(None, 'up', wc_dir)


  # Sync A_COPY with A.
  expected_output = expected_merge_output([[2,7]],
                                          ['U    ' + beta_COPY_path  + '\n',
                                           'U    ' + rho_COPY_path   + '\n',
                                           'U    ' + omega_COPY_path + '\n',
                                           'U    ' + psi_COPY_path   + '\n',
                                           # Mergeinfo notification
                                           ' U   ' + A_COPY_path     + '\n'])
  svntest.actions.run_and_verify_svn(expected_output, [], 'merge',
                                     sbox.repo_url + '/A', A_COPY_path)
  sbox.simple_commit(message='synch A_COPY with A')

  # Update so we are ready for reintegrate.
  svntest.main.run_svn(None, 'up', wc_dir)

  # Change authz file so everybody has access to everything but the root.
  if is_ra_type_svn() or is_ra_type_dav():
    write_restrictive_svnserve_conf(sbox.repo_dir)
    write_authz_file(sbox, {"/"       : "* =",
                            "/A"      : "* = rw",
                            "/A_COPY" : "* = rw",
                            "/iota"   : "* = rw"})

  # Now reintegrate A_COPY back to A.  The lack of access to the root of the
  # repository shouldn't be a problem.
  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_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/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_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),
  })
  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,
                                       [], True, True,
                                       '--reintegrate', A_path)
def subtree_to_and_fro(sbox):
    "reintegrate considers source subtree mergeinfo"

    #   A      (-----o-o-o-o------------x
    #          ( \            \        /
    #          (  \            \      /
    #   A_COPY (   o---------o--s--o--
    #              2 3 4 5 6 7  8  9

    # Some paths we'll care about.
    A_COPY_gamma_path = sbox.ospath('A_COPY/D/gamma')
    psi_path = sbox.ospath('A/D/H/psi')
    A_COPY_D_path = sbox.ospath('A_COPY/D')
    A_path = sbox.ospath('A')

    sbox.build()
    wc_dir = sbox.wc_dir

    # Setup a simple 'trunk & branch': Copy ^/A to ^/A_COPY in r2 and then
    # make a few edits under A in r3-6 (edits r3, r4, r6 are under subtree 'D'):
    wc_disk, wc_status = set_up_branch(sbox)

    # r7 - Edit a file on the branch.
    svntest.main.file_write(A_COPY_gamma_path, "Branch edit to 'gamma'.\n")
    svntest.actions.run_and_verify_svn(None, [], 'ci', wc_dir, '-m',
                                       'Edit a file on our branch')

    # r8 - Do a subtree sync merge from ^/A/D to A_COPY/D.
    # Note that among other things this changes A_COPY/D/H/psi.
    svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir)
    svntest.actions.run_and_verify_svn(None, [], 'merge',
                                       sbox.repo_url + '/A/D', A_COPY_D_path)
    svntest.actions.run_and_verify_svn(None, [], 'ci', wc_dir, '-m',
                                       'Automatic subtree merge')

    # r9 - Make an edit to A/D/H/psi.
    svntest.main.file_write(psi_path, "Trunk Edit to 'psi'.\n")
    svntest.actions.run_and_verify_svn(None, [], 'ci', wc_dir, '-m',
                                       'Edit a file on our trunk')

    # Now reintegrate ^/A_COPY back to A.  Prior to issue #4258's fix, the
    # the subtree merge to A_COPY/D just looks like any other branch edit and
    # was not considered a merge.  So the changes which exist on A/D and were
    # merged to A_COPY/D, were merged *back* to A, resulting in a conflict:
    #
    #   C:\...\working_copies\merge_automatic_tests-18>svn merge ^^/A_COPY A
    #   DBG: merge.c:11461: base on source: ^/A@1
    #   DBG: merge.c:11462: base on target: ^/A@1
    #   DBG: merge.c:11567: yca   ^/A@1
    #   DBG: merge.c:11568: base  ^/A@1
    #   DBG: merge.c:11571: right ^/A_COPY@8
    #   Conflict discovered in file 'A\D\H\psi'.
    #   Select: (p) postpone, (df) diff-full, (e) edit,
    #           (mc) mine-conflict, (tc) theirs-conflict,
    #           (s) show all options: p
    #   --- Merging r2 through r9 into 'A':
    #   C    A\D\H\psi
    #   U    A\D\gamma
    #   --- Recording mergeinfo for merge of r2 through r9 into 'A':
    #    U   A
    #   Summary of conflicts:
    #     Text conflicts: 1
    svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir)
    exit_code, out, err = svntest.actions.run_and_verify_svn(
        [], svntest.verify.AnyOutput, 'merge', sbox.repo_url + '/A_COPY',
        A_path)

    # Better to produce the same warning that explicitly using the
    # --reintegrate option would produce:
    svntest.verify.verify_outputs(
        "Automatic Reintegrate failed, but not "
        "in the way expected", err, None,
        "(svn: E195016: Reintegrate can only be used if "
        "revisions 2 through 9 were previously "
        "merged from .*/A to the reintegrate source, "
        "but this is not the case:\n)"
        "|(  A_COPY\n)"
        "|(    Missing ranges: /A:5\n)"
        "|(\n)"
        "|" + svntest.main.stack_trace_regexp, None,
        True)  # Match *all* lines of stdout
Example #15
0
def mergeinfo_on_pegged_wc_path(sbox):
    "svn mergeinfo on pegged working copy target"

    sbox.build()
    wc_dir = sbox.wc_dir
    expected_disk, expected_status = set_up_branch(sbox)

    # Some paths we'll care about
    A_path = os.path.join(wc_dir, "A")
    A_COPY_path = os.path.join(wc_dir, "A_COPY")
    psi_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "H", "psi")
    omega_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "H", "omega")
    beta_COPY_path = os.path.join(wc_dir, "A_COPY", "B", "E", "beta")

    # Do a couple merges
    #
    # r7 - Merge -c3,6 from A to A_COPY.
    svntest.actions.run_and_verify_svn(
        expected_merge_output([[3], [6]], [
            'U    ' + psi_COPY_path + '\n',
            'U    ' + omega_COPY_path + '\n',
            ' U   ' + A_COPY_path + '\n',
            ' G   ' + A_COPY_path + '\n',
        ]), [], 'merge', '-c3,6', sbox.repo_url + '/A', A_COPY_path)
    svntest.actions.run_and_verify_svn(None, [], 'ci', wc_dir, '-m',
                                       'Merge r3 and r6')

    # r8 - Merge -c5 from A to A_COPY.
    svntest.actions.run_and_verify_svn(
        expected_merge_output(
            [[5]],
            ['U    ' + beta_COPY_path + '\n', ' U   ' + A_COPY_path + '\n']),
        [], 'merge', '-c5', '--allow-mixed-revisions', sbox.repo_url + '/A',
        A_COPY_path)
    svntest.actions.run_and_verify_svn(None, [], 'ci', wc_dir, '-m',
                                       'Merge r5')

    # Ask for merged and eligible revisions to A_COPY pegged at various values.
    # Prior to issue #3180 fix the peg revision was ignored.
    #
    # A_COPY pegged to non-existent revision
    svntest.actions.run_and_verify_mergeinfo(
        adjust_error_for_server_version('.*No such revision 99'), [], A_path,
        A_COPY_path + '@99', '--show-revs', 'merged')

    # A_COPY@BASE
    svntest.actions.run_and_verify_mergeinfo(
        adjust_error_for_server_version(''), ['3', '5', '6'], A_path,
        A_COPY_path + '@BASE', '--show-revs', 'merged')

    # A_COPY@HEAD
    svntest.actions.run_and_verify_mergeinfo(
        adjust_error_for_server_version(''), ['3', '5', '6'], A_path,
        A_COPY_path + '@HEAD', '--show-revs', 'merged')

    # A_COPY@4 (Prior to any merges)
    svntest.actions.run_and_verify_mergeinfo(
        adjust_error_for_server_version(''), [], A_path, A_COPY_path + '@4',
        '--show-revs', 'merged')

    # A_COPY@COMMITTED (r8)
    svntest.actions.run_and_verify_mergeinfo(
        adjust_error_for_server_version(''), ['3', '5', '6'], A_path,
        A_COPY_path + '@COMMITTED', '--show-revs', 'merged')

    # A_COPY@PREV (r7)
    svntest.actions.run_and_verify_mergeinfo(
        adjust_error_for_server_version(''), ['3', '6'], A_path,
        A_COPY_path + '@PREV', '--show-revs', 'merged')

    # A_COPY@BASE
    svntest.actions.run_and_verify_mergeinfo(
        adjust_error_for_server_version(''), ['4'], A_path,
        A_COPY_path + '@BASE', '--show-revs', 'eligible')

    # A_COPY@HEAD
    svntest.actions.run_and_verify_mergeinfo(
        adjust_error_for_server_version(''), ['4'], A_path,
        A_COPY_path + '@HEAD', '--show-revs', 'eligible')

    # A_COPY@4 (Prior to any merges)
    svntest.actions.run_and_verify_mergeinfo(
        adjust_error_for_server_version(''), ['3', '4', '5', '6'], A_path,
        A_COPY_path + '@4', '--show-revs', 'eligible')

    # A_COPY@COMMITTED (r8)
    svntest.actions.run_and_verify_mergeinfo(
        adjust_error_for_server_version(''), ['4'], A_path,
        A_COPY_path + '@COMMITTED', '--show-revs', 'eligible')

    # A_COPY@PREV (r7)
    svntest.actions.run_and_verify_mergeinfo(
        adjust_error_for_server_version(''), ['4', '5'], A_path,
        A_COPY_path + '@PREV', '--show-revs', 'eligible')
def merge_to_reverse_cherry_subtree_to_merge_to(sbox):
  "sync merge considers target subtree mergeinfo"

  #   A (--o-o-o-o------------------
  #     ( \         \        \     \
  #     (  \         \        \     \
  #   B (   o--o------x-------rc-----x

  # Some paths we'll care about.
  A_COPY_path = sbox.ospath('A_COPY')
  A_COPY_B_path = sbox.ospath('A_COPY/B')
  A_COPY_beta_path = sbox.ospath('A_COPY/B/E/beta')

  sbox.build()
  wc_dir = sbox.wc_dir

  # Setup a simple 'trunk & branch': Copy ^/A to ^/A_COPY in r2 and then
  # make a few edits under A in r3-6:
  wc_disk, wc_status = set_up_branch(sbox)

  # Sync merge ^/A to A_COPY, then reverse merge r5 from ^/A/B to A_COPY/B.
  # This results in mergeinfo on the target which makes it appear that the
  # branch is synced up to r6, but the subtree mergeinfo on A_COPY/B reveals
  # that r5 has not been merged to that subtree:
  #
  #   Properties on 'A_COPY':
  #     svn:mergeinfo
  #       /A:2-6
  #   Properties on 'A_COPY\B':
  #     svn:mergeinfo
  #       /A/B:2-4,6
  svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
  svntest.actions.run_and_verify_svn(None, None, [], 'merge',
                                     sbox.repo_url + '/A', A_COPY_path)
  svntest.actions.run_and_verify_svn(None, None, [], 'merge', '-c-5',
                                     sbox.repo_url + '/A/B',
                                     A_COPY_B_path)
  svntest.actions.run_and_verify_svn(None, None, [], 'ci', wc_dir, '-m',
                                     'sync merge and reverse subtree merge')

  # Try an automatic sync merge from ^/A to A_COPY.  Revision 5 should be
  # merged to A_COPY/B as its subtree mergeinfo reveals that rev is missing,
  # like so:
  #
  #   >svn merge ^/A A_COPY
  #   --- Merging r5 into 'A_COPY\B':
  #   U    A_COPY\B\E\beta
  #   --- Recording mergeinfo for merge of r5 through r7 into 'A_COPY':
  #    U   A_COPY
  #   --- Recording mergeinfo for merge of r5 through r7 into 'A_COPY\B':
  #    U   A_COPY\B
  #   --- Eliding mergeinfo from 'A_COPY\B':
  #    U   A_COPY\B
  #
  # But the merge ignores the subtree mergeinfo and considers
  # only the mergeinfo on the target itself (and thus is a no-op but for
  # the mergeinfo change on the root of the merge target):
  #
  #   >svn merge ^/A A_COPY
  #   --- Recording mergeinfo for merge of r7 into 'A_COPY':
  #    U   A_COPY
  #
  #   >svn diff
  #   Index: A_COPY
  #   ===================================================================
  #   --- A_COPY      (revision 7)
  #   +++ A_COPY      (working copy)
  #
  #   Property changes on: A_COPY
  #   ___________________________________________________________________
  #   Modified: svn:mergeinfo
  #      Merged /A:r7
  svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
  expected_output = wc.State(A_COPY_path, {
    'B/E/beta'   : Item(status='U '),
    })
  expected_mergeinfo_output = wc.State(A_COPY_path, {
    ''  : Item(status=' U'),
    'B' : Item(status=' U'),
    })
  expected_elision_output = wc.State(A_COPY_path, {
    'B' : Item(status=' U'),
    })
  expected_status = wc.State(A_COPY_path, {
    ''           : Item(status=' M'),
    'B'          : Item(status=' M'),
    '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='7')
  expected_disk = wc.State('', {
    ''           : Item(props={SVN_PROP_MERGEINFO : '/A:2-7'}),
    'B'          : Item(),
    'mu'         : Item("This is the file '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_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,
                                       expected_disk,
                                       expected_status,
                                       expected_skip,
                                       None, None, None, None,
                                       None, 1, 0, A_COPY_path)
def subtree_to_and_fro(sbox):
  "reintegrate considers source subtree mergeinfo"

  #   A      (-----o-o-o-o------------x
  #          ( \            \        /
  #          (  \            \      /
  #   A_COPY (   o---------o--s--o--
  #              2 3 4 5 6 7  8  9

  # Some paths we'll care about.
  A_COPY_gamma_path = sbox.ospath('A_COPY/D/gamma')
  psi_path = sbox.ospath('A/D/H/psi')
  A_COPY_D_path = sbox.ospath('A_COPY/D')
  A_path = sbox.ospath('A')

  sbox.build()
  wc_dir = sbox.wc_dir

  # Setup a simple 'trunk & branch': Copy ^/A to ^/A_COPY in r2 and then
  # make a few edits under A in r3-6 (edits r3, r4, r6 are under subtree 'D'):
  wc_disk, wc_status = set_up_branch(sbox)

  # r7 - Edit a file on the branch.
  svntest.main.file_write(A_COPY_gamma_path, "Branch edit to 'gamma'.\n")
  svntest.actions.run_and_verify_svn(None, None, [], 'ci', wc_dir,
                                     '-m', 'Edit a file on our branch')

  # r8 - Do a subtree sync merge from ^/A/D to A_COPY/D.
  # Note that among other things this changes A_COPY/D/H/psi.
  svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
  svntest.actions.run_and_verify_svn(None, None, [], 'merge',
                                     sbox.repo_url + '/A/D', A_COPY_D_path)
  svntest.actions.run_and_verify_svn(None, None, [], 'ci', wc_dir,
                                     '-m', 'Automatic subtree merge')

  # r9 - Make an edit to A/D/H/psi.
  svntest.main.file_write(psi_path, "Trunk Edit to 'psi'.\n")
  svntest.actions.run_and_verify_svn(None, None, [], 'ci', wc_dir,
                                     '-m', 'Edit a file on our trunk')

  # Now reintegrate ^/A_COPY back to A.  Prior to issue #4258's fix, the
  # the subtree merge to A_COPY/D just looks like any other branch edit and
  # was not considered a merge.  So the changes which exist on A/D and were
  # merged to A_COPY/D, were merged *back* to A, resulting in a conflict:
  #
  #   C:\...\working_copies\merge_automatic_tests-18>svn merge ^^/A_COPY A
  #   DBG: merge.c:11461: base on source: ^/A@1
  #   DBG: merge.c:11462: base on target: ^/A@1
  #   DBG: merge.c:11567: yca   ^/A@1
  #   DBG: merge.c:11568: base  ^/A@1
  #   DBG: merge.c:11571: right ^/A_COPY@8
  #   Conflict discovered in file 'A\D\H\psi'.
  #   Select: (p) postpone, (df) diff-full, (e) edit,
  #           (mc) mine-conflict, (tc) theirs-conflict,
  #           (s) show all options: p
  #   --- Merging r2 through r9 into 'A':
  #   C    A\D\H\psi
  #   U    A\D\gamma
  #   --- Recording mergeinfo for merge of r2 through r9 into 'A':
  #    U   A
  #   Summary of conflicts:
  #     Text conflicts: 1
  svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
  exit_code, out, err = svntest.actions.run_and_verify_svn(
    None, [], svntest.verify.AnyOutput,
    'merge', sbox.repo_url + '/A_COPY', A_path)

  # Better to produce the same warning that explicitly using the
  # --reintegrate option would produce:
  svntest.verify.verify_outputs("Automatic Reintegrate failed, but not "
                                "in the way expected",
                                err, None,
                                "(svn: E195016: Reintegrate can only be used if "
                                "revisions 2 through 9 were previously "
                                "merged from .*/A to the reintegrate source, "
                                "but this is not the case:\n)"
                                "|(  A_COPY\n)"
                                "|(    Missing ranges: /A:5\n)"
                                "|(\n)"
                                "|" + svntest.main.stack_trace_regexp,
                                None,
                                True) # Match *all* lines of stdout
def merge_to_reverse_cherry_subtree_to_merge_to(sbox):
    "sync merge considers target subtree mergeinfo"

    #   A (--o-o-o-o------------------
    #     ( \         \        \     \
    #     (  \         \        \     \
    #   B (   o--o------x-------rc-----x

    # Some paths we'll care about.
    A_COPY_path = sbox.ospath('A_COPY')
    A_COPY_B_path = sbox.ospath('A_COPY/B')
    A_COPY_beta_path = sbox.ospath('A_COPY/B/E/beta')

    sbox.build()
    wc_dir = sbox.wc_dir

    # Setup a simple 'trunk & branch': Copy ^/A to ^/A_COPY in r2 and then
    # make a few edits under A in r3-6:
    wc_disk, wc_status = set_up_branch(sbox)

    # Sync merge ^/A to A_COPY, then reverse merge r5 from ^/A/B to A_COPY/B.
    # This results in mergeinfo on the target which makes it appear that the
    # branch is synced up to r6, but the subtree mergeinfo on A_COPY/B reveals
    # that r5 has not been merged to that subtree:
    #
    #   Properties on 'A_COPY':
    #     svn:mergeinfo
    #       /A:2-6
    #   Properties on 'A_COPY\B':
    #     svn:mergeinfo
    #       /A/B:2-4,6
    svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir)
    svntest.actions.run_and_verify_svn(None, [], 'merge', sbox.repo_url + '/A',
                                       A_COPY_path)
    svntest.actions.run_and_verify_svn(None, [], 'merge', '-c-5',
                                       sbox.repo_url + '/A/B', A_COPY_B_path)
    svntest.actions.run_and_verify_svn(None, [], 'ci', wc_dir, '-m',
                                       'sync merge and reverse subtree merge')

    # Try an automatic sync merge from ^/A to A_COPY.  Revision 5 should be
    # merged to A_COPY/B as its subtree mergeinfo reveals that rev is missing,
    # like so:
    #
    #   >svn merge ^/A A_COPY
    #   --- Merging r5 into 'A_COPY\B':
    #   U    A_COPY\B\E\beta
    #   --- Recording mergeinfo for merge of r5 through r7 into 'A_COPY':
    #    U   A_COPY
    #   --- Recording mergeinfo for merge of r5 through r7 into 'A_COPY\B':
    #    U   A_COPY\B
    #   --- Eliding mergeinfo from 'A_COPY\B':
    #    U   A_COPY\B
    #
    # But the merge ignores the subtree mergeinfo and considers
    # only the mergeinfo on the target itself (and thus is a no-op but for
    # the mergeinfo change on the root of the merge target):
    #
    #   >svn merge ^/A A_COPY
    #   --- Recording mergeinfo for merge of r7 into 'A_COPY':
    #    U   A_COPY
    #
    #   >svn diff
    #   Index: A_COPY
    #   ===================================================================
    #   --- A_COPY      (revision 7)
    #   +++ A_COPY      (working copy)
    #
    #   Property changes on: A_COPY
    #   ___________________________________________________________________
    #   Modified: svn:mergeinfo
    #      Merged /A:r7
    svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir)
    expected_output = wc.State(A_COPY_path, {
        'B/E/beta': Item(status='U '),
    })
    expected_mergeinfo_output = wc.State(A_COPY_path, {
        '': Item(status=' U'),
        'B': Item(status=' U'),
    })
    expected_elision_output = wc.State(A_COPY_path, {
        'B': Item(status=' U'),
    })
    expected_status = wc.State(
        A_COPY_path, {
            '': Item(status=' M'),
            'B': Item(status=' M'),
            '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='7')
    expected_disk = wc.State(
        '', {
            '': Item(props={SVN_PROP_MERGEINFO: '/A:2-7'}),
            'B': Item(),
            'mu': Item("This is the file '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_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, expected_disk,
        expected_status, expected_skip, [], True, False, A_COPY_path)
Example #19
0
def recursive_mergeinfo(sbox):
    "test svn mergeinfo -R"

    sbox.build()
    wc_dir = sbox.wc_dir
    expected_disk, expected_status = set_up_branch(sbox)

    # Some paths we'll care about
    A_path = os.path.join(wc_dir, "A")
    A_COPY_path = os.path.join(wc_dir, "A_COPY")
    B_COPY_path = os.path.join(wc_dir, "A_COPY", "B")
    C_COPY_path = os.path.join(wc_dir, "A_COPY", "C")
    rho_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "G", "rho")
    H_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "H")
    F_COPY_path = os.path.join(wc_dir, "A_COPY", "B", "F")
    omega_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "H", "omega")
    beta_COPY_path = os.path.join(wc_dir, "A_COPY", "B", "E", "beta")
    A2_path = os.path.join(wc_dir, "A2")
    nu_path = os.path.join(wc_dir, "A2", "B", "F", "nu")
    nu_COPY_path = os.path.join(wc_dir, "A_COPY", "B", "F", "nu")
    nu2_path = os.path.join(wc_dir, "A2", "C", "nu2")

    # Rename A to A2 in r7.
    svntest.actions.run_and_verify_svn(exp_noop_up_out(6), [], 'up', wc_dir)
    svntest.actions.run_and_verify_svn(None, [], 'ren', A_path, A2_path)
    svntest.actions.run_and_verify_svn(None, [], 'ci', wc_dir, '-m',
                                       'rename A to A2')

    # Add the files A/B/F/nu and A/C/nu2 and commit them as r8.
    svntest.main.file_write(nu_path, "A new file.\n")
    svntest.main.file_write(nu2_path, "Another new file.\n")
    svntest.main.run_svn(None, "add", nu_path, nu2_path)
    svntest.actions.run_and_verify_svn(None, [], 'ci', wc_dir, '-m',
                                       'Add 2 new files')

    # Do several merges to create varied subtree mergeinfo

    # Merge r4 from A2 to A_COPY at depth empty
    svntest.actions.run_and_verify_svn(exp_noop_up_out(8), [], 'up', wc_dir)
    svntest.actions.run_and_verify_svn(
        expected_merge_output([[4]], ' U   ' + A_COPY_path + '\n'), [],
        'merge', '-c4', '--depth', 'empty', sbox.repo_url + '/A2', A_COPY_path)

    # Merge r6 from A2/D/H to A_COPY/D/H
    svntest.actions.run_and_verify_svn(
        expected_merge_output(
            [[6]],
            ['U    ' + omega_COPY_path + '\n', ' G   ' + H_COPY_path + '\n']),
        [], 'merge', '-c6', sbox.repo_url + '/A2/D/H', H_COPY_path)

    # Merge r5 from A2 to A_COPY
    svntest.actions.run_and_verify_svn(
        expected_merge_output(
            [[5]],
            [
                'U    ' + beta_COPY_path + '\n',
                ' G   ' + A_COPY_path + '\n',
                ' G   ' + B_COPY_path + '\n',
                ' U   ' + B_COPY_path + '\n',
            ],  # Elision
            elides=True),
        [],
        'merge',
        '-c5',
        sbox.repo_url + '/A2',
        A_COPY_path)

    # Reverse merge -r5 from A2/C to A_COPY/C leaving empty mergeinfo on
    # A_COPY/C.
    svntest.actions.run_and_verify_svn(
        expected_merge_output([[-5]], ' G   ' + C_COPY_path + '\n'), [],
        'merge', '-c-5', sbox.repo_url + '/A2/C', C_COPY_path)

    # Merge r8 from A2/B/F to A_COPY/B/F
    svntest.actions.run_and_verify_svn(
        expected_merge_output(
            [[8]],
            ['A    ' + nu_COPY_path + '\n', ' G   ' + F_COPY_path + '\n']), [],
        'merge', '-c8', sbox.repo_url + '/A2/B/F', F_COPY_path)

    # Commit everything this far as r9
    svntest.actions.run_and_verify_svn(None, [], 'ci', wc_dir, '-m',
                                       'Many merges')
    svntest.actions.run_and_verify_svn(exp_noop_up_out(9), [], 'up', wc_dir)

    # Test svn mergeinfo -R / --depth infinity.

    # Asking for eligible revisions from A2 to A_COPY should show:
    #
    #  r3  - Was never merged.
    #
    #  r4 - Was merged at depth empty, so while there is mergeinfo for the
    #       revision, the actual text change to A_COPY/D/G/rho hasn't yet
    #       happened.
    #
    #  r8* - Was only partially merged to the subtree at A_COPY/B/F.  The
    #        addition of A_COPY/C/nu2 is still outstanding.
    svntest.actions.run_and_verify_mergeinfo(
        adjust_error_for_server_version(""), ['3', '4*', '8*'],
        sbox.repo_url + '/A2', sbox.repo_url + '/A_COPY', '--show-revs',
        'eligible', '-R')
    # Do the same as above, but test that we can request the revisions
    # in reverse order.
    svntest.actions.run_and_verify_mergeinfo(
        adjust_error_for_server_version(""), ['8*', '4*', '3'],
        sbox.repo_url + '/A2', sbox.repo_url + '/A_COPY', '--show-revs',
        'eligible', '-R', '-r', '9:0')

    # Asking for merged revisions from A2 to A_COPY should show:
    #
    #  r4* - Was merged at depth empty, so while there is mergeinfo for the
    #        revision, the actual text change to A_COPY/D/G/rho hasn't yet
    #        happened.
    #
    #  r5  - Was merged at depth infinity to the root of the 'branch', so it
    #        should show as fully merged.
    #
    #  r6  - This was a subtree merge, but since the subtree A_COPY/D/H was
    #        the ancestor of the only change made in r6 it is considered
    #        fully merged.
    #
    #  r8* - Was only partially merged to the subtree at A_COPY/B/F.  The
    #        addition of A_COPY/C/nu2 is still outstanding.
    svntest.actions.run_and_verify_mergeinfo(
        adjust_error_for_server_version(""), ['4*', '5', '6', '8*'], A2_path,
        A_COPY_path, '--show-revs', 'merged', '--depth', 'infinity')
    # Do the same as above, but test that we can request the revisions
    # in reverse order.
    svntest.actions.run_and_verify_mergeinfo(
        adjust_error_for_server_version(""), ['8*', '6', '5', '4*'], A2_path,
        A_COPY_path, '--show-revs', 'merged', '--depth', 'infinity', '-r',
        '9:0')

    # A couple tests of problems found with initial issue #3242 fixes.
    # We should be able to check for the merged revs from a URL to a URL
    # when the latter has explicit mergeinfo...
    svntest.actions.run_and_verify_mergeinfo(
        adjust_error_for_server_version(''), ['6'], sbox.repo_url + '/A2/D/H',
        sbox.repo_url + '/A_COPY/D/H', '--show-revs', 'merged')
    # ...and when the latter has inherited mergeinfo.
    svntest.actions.run_and_verify_mergeinfo(
        adjust_error_for_server_version(''), ['6'],
        sbox.repo_url + '/A2/D/H/omega', sbox.repo_url + '/A_COPY/D/H/omega',
        '--show-revs', 'merged')
def reintegrate_fails_if_no_root_access(sbox):
  "reintegrate fails if no root access"

  # If a user is authorized to a reintegrate source and target, they
  # should be able to reintegrate, regardless of what authorization
  # they have to parents of the source and target.
  #
  # See https://issues.apache.org/jira/browse/SVN-3242#desc78

  # Some paths we'll care about
  wc_dir = sbox.wc_dir
  A_path          = sbox.ospath('A')
  A_COPY_path     = sbox.ospath('A_COPY')
  beta_COPY_path  = sbox.ospath('A_COPY/B/E/beta')
  rho_COPY_path   = sbox.ospath('A_COPY/D/G/rho')
  omega_COPY_path = sbox.ospath('A_COPY/D/H/omega')
  psi_COPY_path   = sbox.ospath('A_COPY/D/H/psi')

  # Copy A@1 to A_COPY in r2, and then make some changes 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(sbox.ospath("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)
  expected_disk.tweak('A_COPY/mu', contents='Changed on the branch.')

  # Update the WC.
  svntest.main.run_svn(None, 'up', wc_dir)


  # Sync A_COPY with A.
  expected_output = expected_merge_output([[2,7]],
                                          ['U    ' + beta_COPY_path  + '\n',
                                           'U    ' + rho_COPY_path   + '\n',
                                           'U    ' + omega_COPY_path + '\n',
                                           'U    ' + psi_COPY_path   + '\n',
                                           # Mergeinfo notification
                                           ' U   ' + A_COPY_path     + '\n'])
  svntest.actions.run_and_verify_svn(expected_output, [], 'merge',
                                     sbox.repo_url + '/A', A_COPY_path)
  sbox.simple_commit(message='synch A_COPY with A')

  # Update so we are ready for reintegrate.
  svntest.main.run_svn(None, 'up', wc_dir)

  # Change authz file so everybody has access to everything but the root.
  if is_ra_type_svn() or is_ra_type_dav():
    write_restrictive_svnserve_conf(sbox.repo_dir)
    write_authz_file(sbox, {"/"       : "* =",
                            "/A"      : "* = rw",
                            "/A_COPY" : "* = rw",
                            "/iota"   : "* = rw"})

  # Now reintegrate A_COPY back to A.  The lack of access to the root of the
  # repository shouldn't be a problem.
  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_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/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_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),
  })
  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,
                                       [], True, True,
                                       '--reintegrate', A_path)
Example #21
0
def wc_target_inherits_mergeinfo_from_repos(sbox):
    "wc target inherits mergeinfo from repos"

    sbox.build()
    wc_dir = sbox.wc_dir
    wc_disk, wc_status = set_up_branch(sbox, nbr_of_branches=2)

    A_COPY_path = os.path.join(wc_dir, 'A_COPY')
    rho_COPY_path = os.path.join(wc_dir, 'A_COPY', 'D', 'G', 'rho')
    gamma_2_path = os.path.join(wc_dir, 'A_COPY_2', 'D', 'gamma')
    tau_path = os.path.join(wc_dir, 'A', 'D', 'G', 'tau')
    D_COPY_path = os.path.join(wc_dir, 'A_COPY', 'D')

    # Merge -c5 ^/A/D/G/rho A_COPY\D\G\rho
    # Merge -c7 ^/A A_COPY
    # Commit as r8
    #
    # This gives us some explicit mergeinfo on the "branch" root and
    # one of its subtrees:
    #
    #   Properties on 'A_COPY\D\G\rho':
    #     svn:mergeinfo
    #       /A/D/G/rho:5
    #   Properties on 'A_COPY':
    #     svn:mergeinfo
    #       /A:7
    svntest.actions.run_and_verify_svn(None, [], 'merge',
                                       sbox.repo_url + '/A/D/G/rho',
                                       rho_COPY_path, '-c5')
    svntest.actions.run_and_verify_svn(None, [], 'merge', sbox.repo_url + '/A',
                                       A_COPY_path, '-c7')
    svntest.actions.run_and_verify_svn(
        None, [], 'ci', '-m', 'Cherrypicks to branch subtree and root', wc_dir)

    # Checkout a new wc rooted at ^/A_COPY/D.
    subtree_wc = sbox.add_wc_path('D_COPY')
    svntest.actions.run_and_verify_svn(None, [], 'co',
                                       sbox.repo_url + '/A_COPY/D', subtree_wc)

    # Check the merged and eligible revisions both recursively and
    # non-recursively.

    # Eligible : Non-recursive
    svntest.actions.run_and_verify_mergeinfo(
        adjust_error_for_server_version(''), ['4', '5'],
        sbox.repo_url + '/A/D', subtree_wc, '--show-revs', 'eligible')

    # Eligible : Recursive
    svntest.actions.run_and_verify_mergeinfo(
        adjust_error_for_server_version(''), ['4'], sbox.repo_url + '/A/D',
        subtree_wc, '--show-revs', 'eligible', '-R')

    # Merged : Non-recursive
    svntest.actions.run_and_verify_mergeinfo(
        adjust_error_for_server_version(''), ['7'], sbox.repo_url + '/A/D',
        subtree_wc, '--show-revs', 'merged')

    # Merged : Recursive
    svntest.actions.run_and_verify_mergeinfo(
        adjust_error_for_server_version(''), ['5', '7'],
        sbox.repo_url + '/A/D', subtree_wc, '--show-revs', 'merged', '-R')

    # Test that intersecting revisions in the 'svn mergeinfo' target
    # from one source don't show up as merged when asking about a different
    # source.
    #
    # In r9 make a change that effects two branches:
    svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir)
    svntest.main.file_write(gamma_2_path, "New content.\n")
    svntest.main.file_write(tau_path, "New content.\n")
    svntest.actions.run_and_verify_svn(
        None, [], 'ci', '-m', 'Make changes under both A and A_COPY_2', wc_dir)

    # In r10 merge r9 from A_COPY_2 to A_COPY.
    #
    # This gives us this mergeinfo:
    #
    #   Properties on 'A_COPY':
    #     svn:mergeinfo
    #       /A:7
    #       /A_COPY_2:9
    #   Properties on 'A_COPY\D\G\rho':
    #     svn:mergeinfo
    #       /A/D/G/rho:5
    svntest.actions.run_and_verify_svn(None, [], 'merge',
                                       sbox.repo_url + '/A_COPY_2',
                                       A_COPY_path, '-c9')
    svntest.actions.run_and_verify_svn(None, [], 'ci', '-m',
                                       'Merge r8 from A_COPY_2 to A_COPY',
                                       wc_dir)

    def test_svn_mergeinfo_4_way(wc_target):
        # Eligible : Non-recursive
        svntest.actions.run_and_verify_mergeinfo(
            adjust_error_for_server_version(''), ['4', '5', '9'],
            sbox.repo_url + '/A/D', wc_target, '--show-revs', 'eligible')

        # Eligible : Recursive
        svntest.actions.run_and_verify_mergeinfo(
            adjust_error_for_server_version(''), ['4', '9'],
            sbox.repo_url + '/A/D', wc_target, '--show-revs', 'eligible', '-R')

        # Merged : Non-recursive
        svntest.actions.run_and_verify_mergeinfo(
            adjust_error_for_server_version(''), ['7'], sbox.repo_url + '/A/D',
            wc_target, '--show-revs', 'merged')

        # Merged : Recursive
        svntest.actions.run_and_verify_mergeinfo(
            adjust_error_for_server_version(''), ['5', '7'],
            sbox.repo_url + '/A/D', wc_target, '--show-revs', 'merged', '-R')

    # Test while the target is the full WC and then with the subtree WC:
    svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir)
    svntest.actions.run_and_verify_svn(None, [], 'up', subtree_wc)

    test_svn_mergeinfo_4_way(D_COPY_path)
    test_svn_mergeinfo_4_way(subtree_wc)
def mergeinfo_and_skipped_paths(sbox):
  "skipped paths get overriding mergeinfo"

  # Test that we override the mergeinfo for child paths which weren't
  # actually merged because they were skipped.
  #
  # This test covers paths skipped because:
  #
  #   1) The source of a merge is inaccessible due to authz restrictions.
  #   2) Destination of merge is inaccessible due to authz restrictions.
  #   3) Source *and* destination of merge is inaccessible due to authz
  #      restrictions.

  sbox.build()
  wc_dir = sbox.wc_dir
  wc_disk, wc_status = set_up_branch(sbox, False, 3)

  # Create a restrictive authz where part of the merge source and part
  # of the target are inaccesible.
  write_restrictive_svnserve_conf(sbox.repo_dir)
  write_authz_file(sbox, {"/"               : svntest.main.wc_author +"=rw",
                          # Make a directory in the merge source inaccessible.
                          "/A/B/E"            : svntest.main.wc_author + "=",
                          # Make a file and dir in the merge destination
                          # inaccessible.
                          "/A_COPY_2/D/H/psi" : svntest.main.wc_author + "=",
                          "/A_COPY_2/D/G" : svntest.main.wc_author + "=",
                          # Make the source and destination inaccessible.
                          "/A_COPY_3/B/E"     : svntest.main.wc_author + "=",
                          })

  # Checkout just the branch under the newly restricted authz.
  wc_restricted = sbox.add_wc_path('restricted')
  svntest.actions.run_and_verify_svn(None, [], 'checkout',
                                     sbox.repo_url,
                                     wc_restricted)

  # Some paths we'll use in the second WC.
  A_COPY_path = os.path.join(wc_restricted, "A_COPY")
  A_COPY_2_path = os.path.join(wc_restricted, "A_COPY_2")
  A_COPY_2_H_path = os.path.join(wc_restricted, "A_COPY_2", "D", "H")
  A_COPY_3_path = os.path.join(wc_restricted, "A_COPY_3")
  omega_path = os.path.join(wc_restricted, "A_COPY", "D", "H", "omega")
  zeta_path = sbox.ospath("A/D/H/zeta")

  # Merge r4:8 into the restricted WC's A_COPY.
  #
  # We expect A_COPY/B/E to be skipped because we can't access the source
  # and A_COPY/D/H/omega because it is missing.  Since we have A_COPY/B/E
  # we should override it's inherited mergeinfo, giving it just what it
  # inherited from A_COPY before the merge.
  expected_output = wc.State(A_COPY_path, {
    'D/G/rho'   : Item(status='U '),
    'D/H/psi'   : Item(status='U '),
    'D/H/omega' : Item(status='U '),
    })
  expected_mergeinfo_output = wc.State(A_COPY_path, {
    ''          : Item(status=' U'),
    'B/E'       : Item(status=' U'),
    })
  expected_elision_output = wc.State(A_COPY_path, {
    })
  expected_status = wc.State(A_COPY_path, {
    ''          : Item(status=' M', wc_rev=8),
    'D/H/chi'   : Item(status='  ', wc_rev=8),
    'D/H/psi'   : Item(status='M ', wc_rev=8),
    'D/H/omega' : Item(status='M ', wc_rev=8),
    'D/H'       : Item(status='  ', wc_rev=8),
    'D/G/pi'    : Item(status='  ', wc_rev=8),
    'D/G/rho'   : Item(status='M ', wc_rev=8),
    'D/G/tau'   : Item(status='  ', wc_rev=8),
    'D/G'       : Item(status='  ', wc_rev=8),
    'D/gamma'   : Item(status='  ', wc_rev=8),
    'D'         : Item(status='  ', wc_rev=8),
    'B/lambda'  : Item(status='  ', wc_rev=8),
    'B/E'       : Item(status=' M', 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),
    'B'         : Item(status='  ', wc_rev=8),
    'mu'        : Item(status='  ', wc_rev=8),
    'C'         : Item(status='  ', wc_rev=8),
    })
  expected_disk = wc.State('', {
    ''          : Item(props={SVN_PROP_MERGEINFO : '/A:5-8'}),
    'D/H/psi'   : Item("New content"),
    'D/H/chi'   : Item("This is the file 'chi'.\n"),
    'D/H/omega' : Item("New content"),
    'D/H'       : 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'       : Item(),
    'D/gamma'   : Item("This is the file 'gamma'.\n"),
    'D'         : Item(),
    'B/lambda'  : Item("This is the file 'lambda'.\n"),
    'B/E'       : Item(props={SVN_PROP_MERGEINFO : ''}),
    'B/E/alpha' : Item("This is the file 'alpha'.\n"),
    'B/E/beta'  : Item("This is the file 'beta'.\n"),
    'B/F'       : Item(),
    'B'         : Item(),
    'mu'        : Item("This is the file 'mu'.\n"),
    'C'         : Item(),
    })
  expected_skip = wc.State(A_COPY_path, {
    'B/E'       : Item(verb='Skipped missing target'),
    })
  svntest.actions.run_and_verify_merge(A_COPY_path, '4', '8',
                                       sbox.repo_url + '/A', None,
                                       expected_output,
                                       expected_mergeinfo_output,
                                       expected_elision_output,
                                       expected_disk,
                                       expected_status,
                                       expected_skip,
                                       check_props=True)

  # Merge r4:8 into the restricted WC's A_COPY_2.
  #
  # As before we expect A_COPY_2/B/E to be skipped because we can't access the
  # source but now the destination paths A_COPY_2/D/G, A_COPY_2/D/G/rho, and
  # A_COPY_2/D/H/psi should also be skipped because our test user doesn't have
  # access.
  #
  # After the merge the parents of the missing dest paths, A_COPY_2/D and
  # A_COPY_2/D/H get non-inheritable mergeinfo.  Those parents' children that
  # *are* present and are affected by the merge, only A_COPY_2/D/H/omega in
  # this case, get their own mergeinfo.  Note that A_COPY_2/D/H is both the
  # parent of a missing child and the sibling of missing child, but the former
  # always takes precedence in terms of getting *non*-inheritable mergeinfo.
  expected_output = wc.State(A_COPY_2_path, {
    'D/H/omega' : Item(status='U '),
    # Below the skip
    'D/G/rho'   : Item(status='  ', treeconflict='U'),
    })
  expected_mergeinfo_output = wc.State(A_COPY_2_path, {
    ''          : Item(status=' U'),
    'D'         : Item(status=' U'),
    'D/H'       : Item(status=' U'),
    'D/H/omega' : Item(status=' U'),
    'B/E'       : Item(status=' U'),
    })
  expected_elision_output = wc.State(A_COPY_2_path, {
    })
  expected_status = wc.State(A_COPY_2_path, {
    ''          : Item(status=' M', wc_rev=8),
    'D/H/chi'   : Item(status='  ', wc_rev=8),
    'D/H/omega' : Item(status='MM', wc_rev=8),
    'D/H'       : Item(status=' M', wc_rev=8),
    'D/gamma'   : Item(status='  ', wc_rev=8),
    'D'         : Item(status=' M', wc_rev=8),
    'B/lambda'  : Item(status='  ', wc_rev=8),
    'B/E'       : Item(status=' M', 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),
    'B'         : Item(status='  ', wc_rev=8),
    'mu'        : Item(status='  ', wc_rev=8),
    'C'         : Item(status='  ', wc_rev=8),
    })
  expected_disk = wc.State('', {
    ''          : Item(props={SVN_PROP_MERGEINFO : '/A:5-8'}),
    'D/H/omega' : Item("New content",
                       props={SVN_PROP_MERGEINFO : '/A/D/H/omega:5-8'}),
    'D/H/chi'   : Item("This is the file 'chi'.\n"),
    'D/H'       : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:5-8*'}),
    'D/gamma'   : Item("This is the file 'gamma'.\n"),
    'D'         : Item(props={SVN_PROP_MERGEINFO : '/A/D:5-8*'}),
    'B/lambda'  : Item("This is the file 'lambda'.\n"),
    'B/E'       : Item(props={SVN_PROP_MERGEINFO : ''}),
    'B/E/alpha' : Item("This is the file 'alpha'.\n"),
    'B/E/beta'  : Item("This is the file 'beta'.\n"),
    'B/F'       : Item(),
    'B'         : Item(),
    'mu'        : Item("This is the file 'mu'.\n"),
    'C'         : Item(),
    })
  expected_skip = wc.State(A_COPY_2_path, {
    'B/E'     : Item(verb='Skipped missing target'),
    'D/G'     : Item(verb='Skipped missing target'),
    'D/H/psi' : Item(verb='Skipped missing target'),
    })
  svntest.actions.run_and_verify_merge(A_COPY_2_path, '4', '8',
                                       sbox.repo_url + '/A', None,
                                       expected_output,
                                       expected_mergeinfo_output,
                                       expected_elision_output,
                                       expected_disk,
                                       expected_status,
                                       expected_skip,
                                       check_props=True)

  # Merge r5:7 into the restricted WC's A_COPY_3.
  #
  # Again A_COPY_3/B/E should be skipped, but because we can't access the
  # source *or* the destination we expect its parent A_COPY_3/B to get
  # non-inheritable mergeinfo.  A_COPY_3B's two existing siblings,
  # A_COPY_3/B/F and A_COPY_3/B/lambda are untouched by the merge so
  # neither gets any mergeinfo recorded.
  expected_output = wc.State(A_COPY_3_path, {
    'D/G/rho' : Item(status='U '),
    })
  expected_mergeinfo_output = wc.State(A_COPY_3_path, {
    ''  : Item(status=' U'),
    'B' : Item(status=' U'),
    })
  expected_elision_output = wc.State(A_COPY_3_path, {
    })
  expected_status = wc.State(A_COPY_3_path, {
    ''          : Item(status=' M', 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),
    'D/H'       : Item(status='  ', wc_rev=8),
    'D/gamma'   : Item(status='  ', wc_rev=8),
    'D'         : 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='M ', wc_rev=8),
    'D/G/tau'   : Item(status='  ', wc_rev=8),
    'B/lambda'  : Item(status='  ', wc_rev=8),
    'B/F'       : Item(status='  ', wc_rev=8),
    'B'         : Item(status=' M', wc_rev=8),
    'mu'        : Item(status='  ', wc_rev=8),
    'C'         : Item(status='  ', wc_rev=8),
    })
  expected_disk = wc.State('', {
    ''          : Item(props={SVN_PROP_MERGEINFO : '/A:6-7'}),
    'D/H/omega' : Item("This is the file 'omega'.\n"),
    'D/H/chi'   : Item("This is the file 'chi'.\n"),
    'D/H/psi'   : Item("This is the file 'psi'.\n"),
    'D/H'       : Item(),
    'D/gamma'   : Item("This is the file 'gamma'.\n"),
    '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"),
    'B/lambda'  : Item("This is the file 'lambda'.\n"),
    'B/F'       : Item(),
    'B'         : Item(props={SVN_PROP_MERGEINFO : '/A/B:6-7*'}),
    'mu'        : Item("This is the file 'mu'.\n"),
    'C'         : Item(),
    })
  expected_skip = wc.State(A_COPY_3_path,
                           {'B/E' : Item(verb='Skipped missing target')})
  svntest.actions.run_and_verify_merge(A_COPY_3_path, '5', '7',
                                       sbox.repo_url + '/A', None,
                                       expected_output,
                                       expected_mergeinfo_output,
                                       expected_elision_output,
                                       expected_disk,
                                       expected_status,
                                       expected_skip,
                                       check_props=True)
  svntest.actions.run_and_verify_svn(None, [], 'revert', '--recursive',
                                     wc_restricted)

  # Test issue #2997.  If a merge requires two separate editor drives and the
  # first is non-operative we should still update the mergeinfo to reflect
  # this.
  #
  # Merge -c5 -c8 to the restricted WC's A_COPY_2/D/H.  r5 gets merged first
  # but is a no-op, r8 get's merged next and is operative so the mergeinfo
  # should be updated on the merge target to reflect both merges.
  expected_output = wc.State(A_COPY_2_H_path, {
    'omega' : Item(status='U '),
    })
  expected_elision_output = wc.State(A_COPY_2_H_path, {
    })
  expected_status = wc.State(A_COPY_2_H_path, {
    ''      : Item(status=' M', wc_rev=8),
    'chi'   : Item(status='  ', wc_rev=8),
    'omega' : Item(status='MM', wc_rev=8),
    })
  expected_disk = wc.State('', {
    ''      : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:5*,8*'}),
    'omega' : Item("New content",
                   props={SVN_PROP_MERGEINFO : '/A/D/H/omega:8'}),
    'chi'   : Item("This is the file 'chi'.\n"),
    })
  expected_skip = wc.State(A_COPY_2_H_path, {
    'psi'   : Item(verb='Skipped missing target'),
    })
  # Note we don't bother checking expected mergeinfo output because the
  # multiple merges being performed here, -c5 and -c8, will result in
  # first ' U' and then ' G' mergeinfo notifications.  Our expected
  # tree structures can't handle checking for multiple values for the
  # same key.
  svntest.actions.run_and_verify_merge(A_COPY_2_H_path, '4', '5',
                                       sbox.repo_url + '/A/D/H', None,
                                       expected_output,
                                       None, # expected_mergeinfo_output,
                                       expected_elision_output,
                                       expected_disk,
                                       expected_status,
                                       expected_skip,
                                       [], True, False,
                                       '-c5', '-c8',
                                       A_COPY_2_H_path)

  # Test issue #2829 'Improve handling for skipped paths encountered
  # during a merge'

  # Revert previous changes to restricted WC
  svntest.actions.run_and_verify_svn(None, [], 'revert', '--recursive',
                                     wc_restricted)
  # Add new path 'A/D/H/zeta'
  svntest.main.file_write(zeta_path, "This is the file 'zeta'.\n")
  svntest.actions.run_and_verify_svn(None, [], 'add', zeta_path)
  expected_output = wc.State(wc_dir, {'A/D/H/zeta' : Item(verb='Adding')})
  wc_status.add({'A/D/H/zeta' : Item(status='  ', wc_rev=9)})
  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
                                        wc_status)

  # Merge -r7:9 to the restricted WC's A_COPY_2/D/H.
  #
  # r9 adds a path, 'A_COPY_2/D/H/zeta', which has a missing sibling 'psi',
  # but since 'psi' is untouched by the merge it isn't skipped, and since it
  # isn't skipped, its parent 'A_COPY_2/D/H' won't get non-inheritable
  # mergeinfo set on it to describe the merge, so none of the parent's
  # children will get explicit mergeinfo -- see issue #4056.
  expected_output = wc.State(A_COPY_2_H_path, {
    'omega' : Item(status='U '),
    'zeta'  : Item(status='A '),
    })
  expected_mergeinfo_output = wc.State(A_COPY_2_H_path, {
    ''      : Item(status=' U'),
    'omega' : Item(status=' U'),
    })
  expected_elision_output = wc.State(A_COPY_2_H_path, {
    'omega' : Item(status=' U'),
    })
  expected_status = wc.State(A_COPY_2_H_path, {
    ''      : Item(status=' M', wc_rev=8),
    'chi'   : Item(status='  ', wc_rev=8),
    'omega' : Item(status='M ', wc_rev=8),
    'zeta'  : Item(status='A ', copied='+', wc_rev='-'),
    })
  expected_disk = wc.State('', {
    ''      : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:8-9'}),
    'omega' : Item("New content"),
    'chi'   : Item("This is the file 'chi'.\n"),
    'zeta'  : Item("This is the file 'zeta'.\n"),
    })
  expected_skip = wc.State(A_COPY_2_H_path, {})
  svntest.actions.run_and_verify_merge(A_COPY_2_H_path, '7', '9',
                                       sbox.repo_url + '/A/D/H', None,
                                       expected_output,
                                       expected_mergeinfo_output,
                                       expected_elision_output,
                                       expected_disk,
                                       expected_status,
                                       expected_skip,
                                       check_props=True)

  # Merge -r4:9 to the restricted WC's A_COPY_2/D/H.
  #
  # r9 adds a path, 'A_COPY_2/D/H/zeta', which has a parent with
  # non-inheritable mergeinfo (due to the fact 'A_COPY_2/D/H/psi' is missing
  # and skipped). 'A_COPY_2/D/H/zeta' must therefore get its own explicit
  # mergeinfo from this merge.
  svntest.actions.run_and_verify_svn(None, [], 'revert', '--recursive',
                                     wc_restricted)
  expected_output = wc.State(A_COPY_2_H_path, {
    'omega' : Item(status='U '),
    'zeta'  : Item(status='A '),
    })
  expected_mergeinfo_output = wc.State(A_COPY_2_H_path, {
    ''      : Item(status=' U'),
    'omega' : Item(status=' U'),
    'zeta'  : Item(status=' U'),
    })
  expected_elision_output = wc.State(A_COPY_2_H_path, {
    })
  expected_status = wc.State(A_COPY_2_H_path, {
    ''      : Item(status=' M', wc_rev=8),
    'chi'   : Item(status='  ', wc_rev=8),
    'omega' : Item(status='MM', wc_rev=8),
    'zeta'  : Item(status='A ', copied='+', wc_rev='-'),
    })
  expected_disk = wc.State('', {
    ''      : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:5-9*'}),
    'omega' : Item("New content",
                   props={SVN_PROP_MERGEINFO : '/A/D/H/omega:5-9'}),
    'chi'   : Item("This is the file 'chi'.\n"),
    'zeta'  : Item("This is the file 'zeta'.\n",
                   props={SVN_PROP_MERGEINFO : '/A/D/H/zeta:9'}),
    })
  expected_skip = wc.State(A_COPY_2_H_path, {
    'psi' : Item(verb='Skipped missing target'),
    })
  svntest.actions.run_and_verify_merge(A_COPY_2_H_path, '4', '9',
                                       sbox.repo_url + '/A/D/H', None,
                                       expected_output,
                                       expected_mergeinfo_output,
                                       expected_elision_output,
                                       expected_disk,
                                       expected_status,
                                       expected_skip,
                                       check_props=True)