from bzrlib.trace import mutter
from bzrlib.transport import Transport
-from svn.core import SubversionException, Pool
-import svn.ra
-import svn.core
-import svn.client
+from bzrlib.plugins.svn.core import SubversionException
+from bzrlib.plugins.svn.auth import create_auth_baton
-from bzrlib.plugins.svn import properties
+from bzrlib.plugins.svn import core, properties, ra
from bzrlib.plugins.svn.errors import convert_svn_error, NoSvnRepositoryPresent, ERR_BAD_URL, ERR_RA_SVN_REPOS_NOT_FOUND, ERR_FS_ALREADY_EXISTS, ERR_FS_NOT_FOUND
import urlparse
import urllib
-svn_config = svn.core.svn_config_get_config(None)
+svn_config = core.get_config()
def get_client_string():
"""Return a string that can be send as part of the User Agent string."""
return "bzr%s+bzr-svn%s" % (bzrlib.__version__, bzrlib.plugins.svn.__version__)
-
-def create_svn_client(url):
- from auth import create_auth_baton
- client = svn.client.create_context()
- client.auth_baton = create_auth_baton(url)
- client.config = svn_config
- return client
-
-
# Don't run any tests on SvnTransport as it is not intended to be
# a full implementation of Transport
def get_test_permutations():
return convert
-class Editor(object):
- """Simple object wrapper around the Subversion delta editor interface."""
- def __init__(self, connection, (editor, editor_baton)):
- self.editor = editor
- self.editor_baton = editor_baton
- self.recent_baton = []
- self._connection = connection
-
- @convert_svn_error
- def open_root(self, base_revnum):
- assert self.recent_baton == [], "root already opened"
- baton = svn.delta.editor_invoke_open_root(self.editor,
- self.editor_baton, base_revnum)
- self.recent_baton.append(baton)
- return baton
-
- @convert_svn_error
- def close_directory(self, baton, *args, **kwargs):
- assert self.recent_baton.pop() == baton, \
- "only most recently opened baton can be closed"
- svn.delta.editor_invoke_close_directory(self.editor, baton, *args, **kwargs)
-
- @convert_svn_error
- def close(self):
- assert self.recent_baton == []
- svn.delta.editor_invoke_close_edit(self.editor, self.editor_baton)
- self._connection._unmark_busy()
-
- @convert_svn_error
- def apply_textdelta(self, baton, *args, **kwargs):
- assert self.recent_baton[-1] == baton
- return svn.delta.editor_invoke_apply_textdelta(self.editor, baton,
- *args, **kwargs)
-
- @convert_svn_error
- def change_dir_prop(self, baton, name, value, pool=None):
- assert self.recent_baton[-1] == baton
- return svn.delta.editor_invoke_change_dir_prop(self.editor, baton,
- name, value, pool)
-
- @convert_svn_error
- def delete_entry(self, *args, **kwargs):
- return svn.delta.editor_invoke_delete_entry(self.editor, *args, **kwargs)
-
- @convert_svn_error
- def add_file(self, path, parent_baton, *args, **kwargs):
- assert self.recent_baton[-1] == parent_baton
- baton = svn.delta.editor_invoke_add_file(self.editor, path,
- parent_baton, *args, **kwargs)
- self.recent_baton.append(baton)
- return baton
-
- @convert_svn_error
- def open_file(self, path, parent_baton, *args, **kwargs):
- assert self.recent_baton[-1] == parent_baton
- baton = svn.delta.editor_invoke_open_file(self.editor, path,
- parent_baton, *args, **kwargs)
- self.recent_baton.append(baton)
- return baton
-
- @convert_svn_error
- def change_file_prop(self, baton, name, value, pool=None):
- assert self.recent_baton[-1] == baton
- svn.delta.editor_invoke_change_file_prop(self.editor, baton, name,
- value, pool)
-
- @convert_svn_error
- def close_file(self, baton, *args, **kwargs):
- assert self.recent_baton.pop() == baton
- svn.delta.editor_invoke_close_file(self.editor, baton, *args, **kwargs)
-
- @convert_svn_error
- def add_directory(self, path, parent_baton, *args, **kwargs):
- assert self.recent_baton[-1] == parent_baton
- baton = svn.delta.editor_invoke_add_directory(self.editor, path,
- parent_baton, *args, **kwargs)
- self.recent_baton.append(baton)
- return baton
-
- @convert_svn_error
- def open_directory(self, path, parent_baton, *args, **kwargs):
- assert self.recent_baton[-1] == parent_baton
- baton = svn.delta.editor_invoke_open_directory(self.editor, path,
- parent_baton, *args, **kwargs)
- self.recent_baton.append(baton)
- return baton
-
-
-class Connection(object):
- """An single connection to a Subversion repository. This usually can
- only do one operation at a time."""
- def __init__(self, url):
- self._busy = False
- self._root = None
- self._client = create_svn_client(url)
- self._unbusy_handler = None
- try:
- self.mutter('opening SVN RA connection to %r' % url)
- self._ra = svn.client.open_ra_session(url.encode('utf8'),
- self._client)
- except SubversionException, (_, num):
- if num == ERR_RA_SVN_REPOS_NOT_FOUND:
- raise NoSvnRepositoryPresent(url=url)
- if num == ERR_BAD_URL:
- raise InvalidURL(url)
- raise
- self.url = url
-
- class Reporter(object):
- def __init__(self, connection, (reporter, report_baton)):
- self._reporter = reporter
- self._baton = report_baton
- self._connection = connection
-
- @convert_svn_error
- def set_path(self, path, revnum, start_empty, lock_token, pool=None):
- svn.ra.reporter2_invoke_set_path(self._reporter, self._baton,
- path, revnum, start_empty, lock_token, pool)
-
- @convert_svn_error
- def delete_path(self, path, pool=None):
- svn.ra.reporter2_invoke_delete_path(self._reporter, self._baton,
- path, pool)
-
- @convert_svn_error
- def link_path(self, path, url, revision, start_empty, lock_token,
- pool=None):
- svn.ra.reporter2_invoke_link_path(self._reporter, self._baton,
- path, url, revision, start_empty, lock_token,
- pool)
-
- @convert_svn_error
- def finish_report(self, pool=None):
- try:
- svn.ra.reporter2_invoke_finish_report(self._reporter,
- self._baton, pool)
- finally:
- self._connection._unmark_busy()
-
- @convert_svn_error
- def abort_report(self, pool=None):
- try:
- svn.ra.reporter2_invoke_abort_report(self._reporter,
- self._baton, pool)
- finally:
- self._connection._unmark_busy()
-
- def is_busy(self):
- return self._busy
-
- def _mark_busy(self):
- assert not self._busy, "already busy"
- self._busy = True
-
- def set_unbusy_handler(self, handler):
- self._unbusy_handler = handler
-
- def _unmark_busy(self):
- assert self._busy, "not busy"
- self._busy = False
- if self._unbusy_handler is not None:
- self._unbusy_handler()
- self._unbusy_handler = None
-
- def mutter(self, text):
- if 'transport' in debug.debug_flags:
- mutter(text)
-
- @convert_svn_error
- @needs_busy
- def get_uuid(self):
- self.mutter('svn get-uuid')
- return svn.ra.get_uuid(self._ra)
-
- @convert_svn_error
- @needs_busy
- def get_repos_root(self):
- if self._root is None:
- self.mutter("svn get-repos-root")
- self._root = svn.ra.get_repos_root(self._ra)
- return self._root
-
- @convert_svn_error
- @needs_busy
- def get_latest_revnum(self):
- self.mutter("svn get-latest-revnum")
- return svn.ra.get_latest_revnum(self._ra)
-
- def _make_editor(self, editor, pool=None):
- edit, edit_baton = svn.delta.make_editor(editor, pool)
- self._edit = edit
- self._edit_baton = edit_baton
- return self._edit, self._edit_baton
-
- @convert_svn_error
- def do_switch(self, switch_rev, recurse, switch_url, editor, pool=None):
- self.mutter('svn switch -r %d -> %r' % (switch_rev, switch_url))
- self._mark_busy()
- edit, edit_baton = self._make_editor(editor, pool)
- return self.Reporter(self, svn.ra.do_switch(self._ra, switch_rev, "",
- recurse, switch_url, edit, edit_baton, pool))
-
- @convert_svn_error
- def change_rev_prop(self, revnum, name, value, pool=None):
- self.mutter('svn revprop -r%d --set %s=%s' % (revnum, name, value))
- svn.ra.change_rev_prop(self._ra, revnum, name, value)
-
- @convert_svn_error
- @needs_busy
- def get_lock(self, path):
- return svn.ra.get_lock(self._ra, path)
-
- @convert_svn_error
- @needs_busy
- def unlock(self, locks, break_lock=False):
- def lock_cb(baton, path, do_lock, lock, ra_err, pool):
- pass
- return svn.ra.unlock(self._ra, locks, break_lock, lock_cb)
-
- @convert_svn_error
- @needs_busy
- def get_dir(self, path, revnum, pool=None, kind=False):
- self.mutter("svn ls -r %d '%r'" % (revnum, path))
- assert len(path) == 0 or path[0] != "/"
- # ra_dav backends fail with strange errors if the path starts with a
- # slash while other backends don't.
- if hasattr(svn.ra, 'get_dir2'):
- fields = 0
- if kind:
- fields += svn.core.SVN_DIRENT_KIND
- return svn.ra.get_dir2(self._ra, path, revnum, fields)
- else:
- return svn.ra.get_dir(self._ra, path, revnum)
-
- @convert_svn_error
- @needs_busy
- def check_path(self, path, revnum):
- assert len(path) == 0 or path[0] != "/"
- self.mutter("svn check_path -r%d %s" % (revnum, path))
- return svn.ra.check_path(self._ra, path.encode('utf-8'), revnum)
-
- @convert_svn_error
- @needs_busy
- def mkdir(self, relpath, mode=None):
- assert len(relpath) == 0 or relpath[0] != "/"
- path = urlutils.join(self.url, relpath)
- try:
- svn.client.mkdir([path.encode("utf-8")], self._client)
- except SubversionException, (msg, num):
- if num == ERR_FS_NOT_FOUND:
- raise NoSuchFile(path)
- if num == ERR_FS_ALREADY_EXISTS:
- raise FileExists(path)
- raise
-
- @convert_svn_error
- def replay(self, revision, low_water_mark, send_deltas, editor, pool=None):
- self.mutter('svn replay -r%r:%r' % (low_water_mark, revision))
- self._mark_busy()
- edit, edit_baton = self._make_editor(editor, pool)
- svn.ra.replay(self._ra, revision, low_water_mark, send_deltas,
- edit, edit_baton, pool)
-
- @convert_svn_error
- def do_update(self, revnum, recurse, editor, pool=None):
- self.mutter('svn update -r %r' % revnum)
- self._mark_busy()
- edit, edit_baton = self._make_editor(editor, pool)
- return self.Reporter(self, svn.ra.do_update(self._ra, revnum, "",
- recurse, edit, edit_baton, pool))
-
- @convert_svn_error
- def has_capability(self, cap):
- return svn.ra.has_capability(self._ra, cap)
-
- @convert_svn_error
- def revprop_list(self, revnum, pool=None):
- self.mutter('svn revprop-list -r %r' % revnum)
- return svn.ra.rev_proplist(self._ra, revnum, pool)
-
- @convert_svn_error
- def get_commit_editor(self, revprops, done_cb, lock_token, keep_locks):
- self._mark_busy()
- try:
- if hasattr(svn.ra, 'get_commit_editor3'):
- editor = svn.ra.get_commit_editor3(self._ra, revprops, done_cb,
- lock_token, keep_locks)
- elif revprops.keys() != [properties.PROP_REVISION_LOG]:
- raise NotImplementedError()
- else:
- editor = svn.ra.get_commit_editor2(self._ra,
- revprops[properties.PROP_REVISION_LOG],
- done_cb, lock_token, keep_locks)
-
- return Editor(self, editor)
- except:
- self._unmark_busy()
- raise
+def Connection(url):
+ try:
+ mutter('opening SVN RA connection to %r' % url)
+ ret = ra.RemoteAccess(url.encode('utf8'),
+ auth=create_auth_baton(url))
+ # FIXME: Callbacks
+ except SubversionException, (_, num):
+ if num in (ERR_RA_SVN_REPOS_NOT_FOUND,):
+ raise NoSvnRepositoryPresent(url=url)
+ if num == ERR_BAD_URL:
+ raise InvalidURL(url)
+ raise
- class SvnLock(object):
- def __init__(self, connection, tokens):
- self._tokens = tokens
- self._connection = connection
+ from bzrlib.plugins.svn import lazy_check_versions
+ lazy_check_versions()
- def unlock(self):
- self._connection.unlock(self.locks)
-
- @convert_svn_error
- @needs_busy
- def lock_write(self, path_revs, comment=None, steal_lock=False):
- tokens = {}
- def lock_cb(baton, path, do_lock, lock, ra_err, pool):
- tokens[path] = lock
- svn.ra.lock(self._ra, path_revs, comment, steal_lock, lock_cb)
- return SvnLock(self, tokens)
-
- @convert_svn_error
- @needs_busy
- def get_log(self, paths, from_revnum, to_revnum, limit,
- discover_changed_paths, strict_node_history, revprops, rcvr,
- pool=None):
- # No paths starting with slash, please
- assert paths is None or all([not p.startswith("/") for p in paths])
- if (paths is None and
- (svn.core.SVN_VER_MINOR < 6 or (
- svn.core.SVN_VER_REVISION < 31470 and svn.core.SVN_VER_REVISION != 0))):
- paths = ["/"]
- self.mutter('svn log %r:%r %r (limit: %r)' % (from_revnum, to_revnum, paths, limit))
- if hasattr(svn.ra, 'get_log2'):
- return svn.ra.get_log2(self._ra, paths,
- from_revnum, to_revnum, limit,
- discover_changed_paths, strict_node_history, False,
- revprops, rcvr, pool)
-
- class LogEntry(object):
- def __init__(self, changed_paths, rev, author, date, message):
- self.changed_paths = changed_paths
- self.revprops = {}
- if properties.PROP_REVISION_AUTHOR in revprops:
- self.revprops[properties.PROP_REVISION_AUTHOR] = author
- if properties.PROP_REVISION_LOG in revprops:
- self.revprops[properties.PROP_REVISION_LOG] = message
- if properties.PROP_REVISION_DATE in revprops:
- self.revprops[properties.PROP_REVISION_DATE] = date
- # FIXME: Check other revprops
- # FIXME: Handle revprops is None
- self.revision = rev
- self.has_children = None
-
- def rcvr_convert(orig_paths, rev, author, date, message, pool):
- rcvr(LogEntry(orig_paths, rev, author, date, message), pool)
-
- return svn.ra.get_log(self._ra, paths,
- from_revnum, to_revnum, limit, discover_changed_paths,
- strict_node_history, rcvr_convert, pool)
-
- @convert_svn_error
- @needs_busy
- def reparent(self, url):
- if self.url == url:
- return
- if hasattr(svn.ra, 'reparent'):
- self.mutter('svn reparent %r' % url)
- svn.ra.reparent(self._ra, url)
- self.url = url
- else:
- raise NotImplementedError(self.reparent)
+ return ret
class ConnectionPool(object):
def get(self, url):
# Check if there is an existing connection we can use
for c in self.connections:
- assert not c.is_busy(), "busy connection in pool"
+ assert not c.busy, "busy connection in pool"
if c.url == url:
self.connections.remove(c)
return c
raise
def add(self, connection):
- assert not connection.is_busy(), "adding busy connection in pool"
+ assert not connection.busy, "adding busy connection in pool"
self.connections.add(connection)
to fool Bazaar. """
@convert_svn_error
def __init__(self, url="", _backing_url=None, pool=None):
- self.pool = Pool()
bzr_url = url
self.svn_url = bzr_to_svn_url(url)
# _backing_url is an evil hack so the root directory of a repository
def get_uuid(self):
conn = self.get_connection()
+ self.mutter('svn get-uuid')
try:
return conn.get_uuid()
finally:
def get_svn_repos_root(self):
conn = self.get_connection()
+ self.mutter('svn get-repos-root')
try:
return conn.get_repos_root()
finally:
def get_latest_revnum(self):
conn = self.get_connection()
+ self.mutter('svn get-latest-revnum')
try:
return conn.get_latest_revnum()
finally:
self.add_connection(conn)
- def do_switch(self, switch_rev, recurse, switch_url, editor, pool=None):
+ def do_switch(self, switch_rev, recurse, switch_url, editor):
conn = self._open_real_transport()
- conn.set_unbusy_handler(lambda: self.add_connection(conn))
- return conn.do_switch(switch_rev, recurse, switch_url, editor, pool)
+ self.mutter('svn do-switch -r%d %s' % (switch_rev, switch_url))
+ return conn.do_switch(switch_rev, "", recurse, switch_url, editor)
def iter_log(self, paths, from_revnum, to_revnum, limit, discover_changed_paths,
strict_node_history, revprops):
-
assert paths is None or isinstance(paths, list)
assert paths is None or all([isinstance(x, str) for x in paths])
assert isinstance(from_revnum, int) and isinstance(to_revnum, int)
from threading import Thread, Semaphore
class logfetcher(Thread):
- def __init__(self, transport, **kwargs):
+ def __init__(self, transport, *args, **kwargs):
Thread.__init__(self)
self.setDaemon(True)
self.transport = transport
+ self.args = args
self.kwargs = kwargs
self.pending = []
self.conn = None
def run(self):
assert self.conn is None, "already running"
- def rcvr(log_entry, pool):
- self.pending.append((log_entry.changed_paths, log_entry.revision, log_entry.revprops))
+ def rcvr(*args):
+ self.pending.append(args)
self.semaphore.release()
self.conn = self.transport.get_connection()
try:
- self.conn.get_log(rcvr=rcvr, **self.kwargs)
+ self.conn.get_log(callback=rcvr, *self.args, **self.kwargs)
self.pending.append(None)
except Exception, e:
self.pending.append(e)
else:
newpaths = [self._request_path(path) for path in paths]
- fetcher = logfetcher(self, paths=newpaths, from_revnum=from_revnum, to_revnum=to_revnum, limit=limit, discover_changed_paths=discover_changed_paths, strict_node_history=strict_node_history, revprops=revprops)
+ fetcher = logfetcher(self, paths=newpaths, start=from_revnum, end=to_revnum, limit=limit, discover_changed_paths=discover_changed_paths, strict_node_history=strict_node_history, revprops=revprops)
fetcher.start()
return iter(fetcher.next, None)
- def get_log(self, paths, from_revnum, to_revnum, limit, discover_changed_paths,
- strict_node_history, revprops, rcvr, pool=None):
+ def get_log(self, rcvr, paths, from_revnum, to_revnum, limit, discover_changed_paths,
+ strict_node_history, revprops):
assert paths is None or isinstance(paths, list), "Invalid paths"
assert paths is None or all([isinstance(x, str) for x in paths])
+ self.mutter('svn log -r%d:%d %r' % (from_revnum, to_revnum, paths))
+
if paths is None:
newpaths = None
else:
conn = self.get_connection()
try:
- return conn.get_log(newpaths,
+ return conn.get_log(rcvr, newpaths,
from_revnum, to_revnum,
limit, discover_changed_paths, strict_node_history,
- revprops, rcvr, pool)
+ revprops)
finally:
self.add_connection(conn)
return self.connections.get(self.svn_url)
return self.get_connection()
- def change_rev_prop(self, revnum, name, value, pool=None):
+ def change_rev_prop(self, revnum, name, value):
conn = self.get_connection()
+ self.mutter('svn change-revprop -r%d %s=%s' % (revnum, name, value))
try:
- return conn.change_rev_prop(revnum, name, value, pool)
+ return conn.change_rev_prop(revnum, name, value)
finally:
self.add_connection(conn)
- def get_dir(self, path, revnum, pool=None, kind=False):
+ def get_dir(self, path, revnum, kind=False):
path = self._request_path(path)
conn = self.get_connection()
+ self.mutter('svn get-dir -r%d %s' % (revnum, path))
try:
- return conn.get_dir(path, revnum, pool, kind)
+ return conn.get_dir(path, revnum, kind)
finally:
self.add_connection(conn)
def check_path(self, path, revnum):
path = self._request_path(path)
conn = self.get_connection()
+ self.mutter('svn check-path -r%d %s' % (revnum, path))
try:
return conn.check_path(path, revnum)
finally:
def mkdir(self, relpath, mode=None):
conn = self.get_connection()
+ self.mutter('svn mkdir %s' % (relpath,))
try:
return conn.mkdir(relpath, mode)
finally:
self.add_connection(conn)
- def replay(self, revision, low_water_mark, send_deltas, editor, pool=None):
+ def replay(self, revision, low_water_mark, send_deltas, editor):
conn = self._open_real_transport()
+ self.mutter('svn replay -r%d:%d' % (low_water_mark,revision))
try:
return conn.replay(revision, low_water_mark,
- send_deltas, editor, pool)
+ send_deltas, editor)
finally:
self.add_connection(conn)
- def do_update(self, revnum, recurse, editor, pool=None):
+ def do_update(self, revnum, recurse, editor):
conn = self._open_real_transport()
- conn.set_unbusy_handler(lambda: self.add_connection(conn))
- return conn.do_update(revnum, recurse, editor, pool)
+ self.mutter('svn do-update -r%d' % (revnum,))
+ return conn.do_update(revnum, "", recurse, editor)
def has_capability(self, cap):
conn = self.get_connection()
+ self.mutter('svn has-capability %s' % (cap,))
try:
return conn.has_capability(cap)
finally:
self.add_connection(conn)
- def revprop_list(self, revnum, pool=None):
+ def revprop_list(self, revnum):
conn = self.get_connection()
+ self.mutter('svn revprop-list -r%d' % (revnum,))
try:
- return conn.revprop_list(revnum, pool)
+ return conn.rev_proplist(revnum)
finally:
self.add_connection(conn)
def get_commit_editor(self, revprops, done_cb, lock_token, keep_locks):
conn = self._open_real_transport()
- conn.set_unbusy_handler(lambda: self.add_connection(conn))
+ self.mutter('svn get-commit-editor %r' % (revprops,))
return conn.get_commit_editor(revprops, done_cb,
lock_token, keep_locks)