[svn.haxx.se] · SVN Dev · SVN Users · SVN Org · TSVN Dev · TSVN Users · Subclipse Dev · Subclipse Users · this month's index

BUG with complex branching, only over neon and serf

From: Eric Gillespie <epg_at_pretzelnet.org>
Date: 2007-09-12 21:14:38 CEST

I might be able to get help from Sussman on this, but maybe not.
So any other neon or serf experts, please chime in :).

I found a bug in ra -neon and -serf that can't be shown with the
client library, but only when using the ra commit editor to build
up a relatively complex transaction. Presumably you can
demonstrate this problem with mucc, but I have not tried.

The problem is basically:

  1. Delete /trunk/dir1/dir2/
  => commit r13
  2. In one transaction:
     a. Copy /trunk@13 to /branch
     b. Create /branch/dir1/dir2
  => commit r14
  3. In one transaction:
     a. Delete /branch
     b. Copy /trunk@13 to /branch
     c. Create /branch/dir1/dir2
  => ERROR: .../branch/dir1/dir2 already exists

Of course, /branch/dir1/dir2 does *not* exist in the transaction
I'm building in step 3! Some or all of Neon, serf, and
mod-dav-svn are looking at branch/dir1/dir2 in the revision root
rather than the transaction root.

There's a bit more to it, and serf is slightly different than
neon, but that's basically it. See the test script for details.
Plop it into subversion/bindings/swig/python/tests and run it.
Comment out both REOPS_URL settings to see it run over local.

0 trunk2% python subversion/bindings/swig/python/tests/bug.py
*** testing over file
D trunk/dir1/dir2/
*** Committed r13
branch 0
A branch/ (from trunk@13)
A branch/dir1/dir2/
*** Committed r14
branch 1
R branch/ (from trunk@13)
A branch/dir1/dir2/
*** Committed r15
branch 2
R branch/ (from trunk@13)
A branch/dir1/dir2
*** Committed r16
branch 3
R branch/ (from trunk@13)
A branch/dir1/dir2
*** Committed r17
.
----------------------------------------------------------------------
Ran 1 test in 0.760s

OK
0 trunk2% python subversion/bindings/swig/python/tests/bug.py
*** testing over svn+ssh
D trunk/dir1/dir2/
*** Committed r13
branch 0
A branch/ (from trunk@13)
A branch/dir1/dir2/
*** Committed r14
branch 1
R branch/ (from trunk@13)
A branch/dir1/dir2/
*** Committed r15
branch 2
R branch/ (from trunk@13)
A branch/dir1/dir2
*** Committed r16
branch 3
R branch/ (from trunk@13)
A branch/dir1/dir2
*** Committed r17
.
----------------------------------------------------------------------
Ran 1 test in 0.902s

OK
0 trunk2% python subversion/bindings/swig/python/tests/bug.py
*** testing over http
D trunk/dir1/dir2/
*** Committed r13
branch 0
A branch/ (from trunk@13)
A branch/dir1/dir2/
*** Committed r14
branch 1
R branch/ (from trunk@13)
A branch/dir1/dir2/
*** Committed r15
branch 2
R branch/ (from trunk@13)
A branch/dir1/dir2
E
======================================================================
ERROR: test_bug (__main__.SubversionRepositoryAccessTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "subversion/bindings/swig/python/tests/bug.py", line 152, in test_bug
    driver_cb)
  File "/usr/local/google/home/epg/work/svn/trunk2/subversion/bindings/swig/python/libsvn/delta.py", line 514, in svn_delta_path_driver
    return apply(_delta.svn_delta_path_driver, args)
  File "subversion/bindings/swig/python/tests/bug.py", line 149, in driver_cb
    editor.close_file(editor.add_file(path, parent, None, -1),
  File "/usr/local/google/home/epg/work/svn/trunk2/subversion/bindings/swig/python/libsvn/delta.py", line 441, in add_file
    return svn_delta_editor_invoke_add_file(self, *args)
  File "/usr/local/google/home/epg/work/svn/trunk2/subversion/bindings/swig/python/libsvn/delta.py", line 651, in svn_delta_editor_invoke_add_file
    return apply(_delta.svn_delta_editor_invoke_add_file, args)
SubversionException: ("File '/svn-test-work/repositories/basic_tests-1/branch/dir1/dir2' already exists", 175005)

import unittest, os, setup_path

from svn import core, repos, fs, delta, client, ra
from StringIO import StringIO

# Use repository path where davautochec.sh-started httpd looks.
import trac.versioncontrol.tests.svn_fs
trac.versioncontrol.tests.svn_fs.REPOS_PATH = os.path.abspath(os.path.dirname(__file__) + '/../../../../tests/cmdline/svn-test-work/repositories/basic_tests-1')

# works over ra -local and -svn
trac.versioncontrol.tests.svn_fs.REPOS_URL = 'svn+ssh://localhost + trac.versioncontrol.tests.svn_fs.REPOS_PATH
# not over ra-neon
trac.versioncontrol.tests.svn_fs.REPOS_URL = 'http://localhost:7818/svn-test-work/repositories/basic_tests-1'

from trac.versioncontrol.tests.svn_fs import SubversionRepositoryTestSetup, \
  REPOS_PATH, REPOS_URL

class Callbacks:
  open_tmp_file = None
  progress_func = None
  cancel_func = None
  get_wc_prop = None
  push_wc_prop = None
  def __init__(self):
    self.auth_baton = core.svn_auth_open([
        client.get_simple_provider(),
        client.get_username_provider(),
        ])
    core.svn_auth_set_parameter(self.auth_baton,
                                core.SVN_AUTH_PARAM_DEFAULT_USERNAME,
                                'jrandom')
    core.svn_auth_set_parameter(self.auth_baton,
                                core.SVN_AUTH_PARAM_DEFAULT_PASSWORD,
                                'rayjandom')

class SubversionRepositoryAccessTestCase(unittest.TestCase):
  def setUp(self):
    SubversionRepositoryTestSetup().setUp()

    ra.initialize()

    # Open repository directly for cross-checking
    self.repos = repos.open(REPOS_PATH)
    self.fs = repos.fs(self.repos)

    self.callbacks = Callbacks()

    svn_config_dir = os.path.expanduser('~/.subversion')
    self.svn_config = core.svn_config_get_config(svn_config_dir)

    print '*** testing over', REPOS_URL[:REPOS_URL.find(':')]
    self.ra_ctx = ra.open2(REPOS_URL, self.callbacks, self.svn_config, None)

  def test_bug(self):
    head = fs.youngest_rev(self.fs)

    info = [None]
    def commit_cb(commit_info, pool):
      info[0] = commit_info

    revprops = {"svn:log": "foobar"}

    print 'D trunk/dir1/dir2/'
    (editor, edit_baton) = ra.get_commit_editor3(self.ra_ctx, revprops,
                                                 commit_cb, None, False)
    def driver_cb(parent, path, pool):
      editor.delete_entry(path, head, parent, pool)
    delta.path_driver(editor, edit_baton, head, ['trunk/dir1/dir2'], driver_cb)
    editor.close_edit(edit_baton)
    self.assertEqual(info[0].revision, head + 1)
    head = info[0].revision
    print '*** Committed r' + str(head)

    # Prepare for branch operations.
    branch_code = 'A'
    copyfrom_rev = head

    # Four operations: branch 0 and 1 add a directory as
    # branch/dir1/dir2, followed by branch 2 and 3 adding a file as
    # branch/dir1/dir2. All work over svn, but branch 2 fails over
    # neon.

    # Comment out branch 0 and 1 (leaving only the file operations),
    # and then it's branch 3 that fails. Basically, if
    # branch/dir1/dir2 exists at all (file or dir), then adding
    # branch/dir1/dir2 as a file fails; adding dir2 as a directory is
    # no problem at all.

    # serf has the problem with directories as well as files, as it
    # fails even branch 1.

    print 'branch 0'
    print '%c branch/ (from trunk@%d)' % (branch_code, copyfrom_rev)
    print 'A branch/dir1/dir2/'
    editor, edit_baton = ra.get_commit_editor3(self.ra_ctx, revprops,
                                               commit_cb, None, False)
    def driver_cb(parent, path, pool):
      if path == 'branch':
        if branch_code == 'R':
          editor.delete_entry(path, head, parent, pool)
        return editor.add_directory(path, parent,
                                    '/'.join([REPOS_URL, 'trunk']),
                                    copyfrom_rev)
      elif path == 'branch/dir1/dir2':
        return editor.add_directory(path, parent, None, -1)
    delta.path_driver(editor, edit_baton, head, ['branch', 'branch/dir1/dir2'],
                      driver_cb)
    editor.close_edit(edit_baton)
    self.assertEqual(info[0].revision, head + 1)
    head = info[0].revision
    print '*** Committed r' + str(head)
    branch_code = 'R'

    print 'branch 1'
    print '%c branch/ (from trunk@%d)' % (branch_code, copyfrom_rev)
    print 'A branch/dir1/dir2/'
    editor, edit_baton = ra.get_commit_editor3(self.ra_ctx, revprops,
                                               commit_cb, None, False)
    def driver_cb(parent, path, pool):
      if path == 'branch':
        if branch_code == 'R':
          editor.delete_entry(path, head, parent, pool)
        return editor.add_directory(path, parent,
                                    '/'.join([REPOS_URL, 'trunk']),
                                    copyfrom_rev)
      elif path == 'branch/dir1/dir2':
        return editor.add_directory(path, parent, None, -1)
    delta.path_driver(editor, edit_baton, head, ['branch', 'branch/dir1/dir2'],
                      driver_cb)
    editor.close_edit(edit_baton)
    self.assertEqual(info[0].revision, head + 1)
    head = info[0].revision
    print '*** Committed r' + str(head)
    branch_code = 'R'

    print 'branch 2'
    print '%c branch/ (from trunk@%d)' % (branch_code, copyfrom_rev)
    print 'A branch/dir1/dir2'
    editor, edit_baton = ra.get_commit_editor3(self.ra_ctx, revprops,
                                               commit_cb, None, False)
    def driver_cb(parent, path, pool):
      if path == 'branch':
        if branch_code == 'R':
          editor.delete_entry(path, head, parent, pool)
        return editor.add_directory(path, parent,
                                    '/'.join([REPOS_URL, 'trunk']),
                                    copyfrom_rev)
      elif path == 'branch/dir1/dir2':
        editor.close_file(editor.add_file(path, parent, None, -1),
                          None, pool)
    delta.path_driver(editor, edit_baton, head, ['branch', 'branch/dir1/dir2'],
                      driver_cb)
    editor.close_edit(edit_baton)
    self.assertEqual(info[0].revision, head + 1)
    head = info[0].revision
    print '*** Committed r' + str(head)
    branch_code = 'R'

    print 'branch 3'
    print '%c branch/ (from trunk@%d)' % (branch_code, copyfrom_rev)
    print 'A branch/dir1/dir2'
    editor, edit_baton = ra.get_commit_editor3(self.ra_ctx, revprops,
                                               commit_cb, None, False)
    def driver_cb(parent, path, pool):
      if path == 'branch':
        if branch_code == 'R':
          editor.delete_entry(path, head, parent, pool)
        return editor.add_directory(path, parent,
                                    '/'.join([REPOS_URL, 'trunk']),
                                    copyfrom_rev)
      elif path == 'branch/dir1/dir2':
        editor.close_file(editor.add_file(path, parent, None, -1),
                          None, pool)
    delta.path_driver(editor, edit_baton, head, ['branch', 'branch/dir1/dir2'],
                      driver_cb)
    editor.close_edit(edit_baton)
    self.assertEqual(info[0].revision, head + 1)
    head = info[0].revision
    print '*** Committed r' + str(head)
    branch_code = 'R'

def suite():
    return unittest.makeSuite(SubversionRepositoryAccessTestCase, 'test')

if __name__ == '__main__':
    runner = unittest.TextTestRunner()
    runner.run(suite())

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@subversion.tigris.org
For additional commands, e-mail: dev-help@subversion.tigris.org
Received on Wed Sep 12 23:54:42 2007

This is an archived mail posted to the Subversion Dev mailing list.