One way to work around this problem is to Ctrl+C the branch operation
and restart it.
-I am unable to access a repository that requires user/password authentication or uses self-signed SSL certificates
-------------------------------------------------------------------------------------------------------------------
-The Python bindings required for password prompting are only present in
-version 1.5 of Subversion so password prompting is only possible if
-you have that version installed.
-
-If you have an older version of Subversion installed, bzr-svn can
-use passwords cached by Subversion or credentials specified in the URL.
-
-Subversion can be forced to cache the password by accessing the repository
-using the Subversion command-line client.
-For example, try running 'svn info <url>'.
-
-You can specify the username and password in the URL in the standard way, e.g.::
-
- $ bzr co svn+http://guest:@tortoisesvn.tigris.org/svn/tortoisesvn/trunk
-
+I am unable to access a repository that uses self-signed SSL certificates
+-------------------------------------------------------------------------
If you are using a Subversion repository over http or https it may be
necessary to prefix the repository URL with "svn+", e.g.
svn+http://svn.python.org/projects/python/trunk/
RST2HTML ?= rst2html
TESTS ?=
-all:: build README.html FAQ.html AUTHORS.html
+all:: build build-inplace README.html FAQ.html AUTHORS.html
build::
$(SETUP) build
clean::
$(SETUP) clean
+ rm -f *.so
TMP_PLUGINS_DIR = $(shell pwd)/.plugins
ln -sf .. $@
check:: build-inplace $(TMP_PLUGINS_DIR)/svn
- BZR_PLUGIN_PATH=$(TMP_PLUGINS_DIR) $(DEBUGGER) $(PYTHON) $(BZR) selftest $(TEST_OPTIONS) --starting-with=bzrlib.plugins.svn $(TESTS)
+ BZR_PLUGIN_PATH=$(TMP_PLUGINS_DIR) $(DEBUGGER) $(PYTHON) $(PYTHON_OPTIONS) $(BZR) selftest $(TEST_OPTIONS) --starting-with=bzrlib.plugins.svn $(TESTS)
check-verbose::
$(MAKE) check TEST_OPTIONS=-v
check-one::
$(MAKE) check TEST_OPTIONS=--one
+check-random::
+ $(MAKE) check TEST_OPTIONS="--random=now --verbose --one"
+
+valgrind-check::
+ $(MAKE) check DEBUGGER="valgrind --suppressions=/usr/lib/valgrind/python.supp $(VALGRIND_OPTIONS)"
+
+gdb-check::
+ $(MAKE) check DEBUGGER="gdb --args $(GDB_OPTIONS)"
+
show-plugins::
BZR_PLUGIN_PATH=$(TMP_PLUGINS_DIR) $(BZR) plugins
CHANGES
+ * bzr-svn now comes with its own Python bindings for the Subversion
+ libraries, removing the need for a unreleased version of Subversion and
+ improving performance.
+
+ It does however mean the extensions have to be built. This requires
+ the Subversion development libraries and should be possible by
+ simply running ``make`` from the plugin directory.
+
* Now uses absolute imports and no longer adds plugin directory to the system path.
BUG FIXES
bzr-svn 0.4.9 2008-03-23
+ CHANGES
+
+ * The Python-Subversion bindings are no longer necessary. Instead,
+ bzr-svn now comes with its own Python bindings for the Subversion
+ libraries.
+
FEATURES
* Set revision properties when possible.
* Be a bit quieter with messages about experimental mappings. (#162496)
+ * Properly warn when trying to open a working copy with a newer version.
+
* More correct implementation of Repository.get_ancestry().
* Properly use current branching scheme when following branches. (#183361)
If you are using Python 2.4, you will need to have the pysqlite module installed. Python 2.5 and higher have sqlite support built in.
-Python-Subversion >= 1.5
-~~~~~~~~~~~~~~~~~~~~~~~~
-
-You also need a fairly recent version of the official Python bindings to the
-Subversion libraries. At the moment, the svn plugin only works with
-Subversion 1.5. The python-subversion (not python-svn!) package
-in Ubuntu since Feisty and Debian since Etch also contain the
-required changes.
-
-The plugin requires a couple of fixes to the Python bindings for Subversion that are only available in Subversion 1.5 and higher. Subversion 1.5 has not been released yet, but packages with the appropriate patches applied to older versions are available for some platforms.
+Subversion development files
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+You need the Subversion libraries, including the development files. This should
+be readily packaged for most platforms. The package name for Debian
+and Debian-based distributions is ``libsvn-dev``.
+
bzr-rebase
~~~~~~~~~~
Simply place this directory in ~/.bazaar/plugins and you should be able
to check out branches from Subversion using bzr. Make sure the directory
-name is 'svn'.
+name is 'svn'. The plugin has to be built as well by running ``make``.
Development
-----------
# versions ending in 'exp' mean experimental mappings
# versions ending in 'dev' mean development version
# versions ending in 'final' mean release (well tested, etc)
-version_info = (0, 4, 11, 'dev', 0)
+version_info = (0, 4, 11, 'exp', 0)
if version_info[3] == 'final':
version_string = '%d.%d.%d' % version_info[:3]
if not (bzrlib_version[0], bzrlib_version[1]-1) in desired:
raise BzrError('Version mismatch')
-def check_subversion_version():
- """Check that Subversion is compatible.
-
- """
- try:
- import svn.delta
- except ImportError:
- warning('No Python bindings for Subversion installed. See the '
- 'bzr-svn README for details.')
- raise bzrlib.errors.BzrError("missing python subversion bindings")
- if (not hasattr(svn.delta, 'svn_delta_invoke_txdelta_window_handler') and
- not hasattr(svn.delta, 'tx_invoke_window_handler')):
- warning('Installed Subversion version does not have updated Python '
- 'bindings. See the bzr-svn README for details.')
- raise bzrlib.errors.BzrError("incompatible python subversion bindings")
- import svn.core
- if (svn.core.SVN_VER_MINOR >= 5 and
- 27729 <= svn.core.SVN_VER_REVISION < 31470):
- warning('Installed Subversion has buggy svn.ra.get_log() implementation, please install newer.')
-
- mutter("bzr-svn: using Subversion %d.%d.%d (%s)", svn.core.SVN_VER_MAJOR, svn.core.SVN_VER_MINOR, svn.core.SVN_VER_MICRO, svn.core.__file__)
-
def check_rebase_version(min_version):
"""Check what version of bzr-rebase is installed.
raise RebaseNotPresent(e)
-check_subversion_version()
+def check_subversion_version():
+ try:
+ import core
+ except:
+ warning("Unable to load bzr-svn extensions - did you build it?")
register_transport_proto('svn+ssh://',
help="Access using the Subversion smart server tunneled over SSH.")
return
versions_checked = True
check_bzrlib_version(COMPATIBLE_BZR_VERSIONS)
+ check_subversion_version()
optimizers_registered = False
def lazy_register_optimizers():
from bzrlib.config import AuthenticationConfig
from bzrlib.ui import ui_factory
-import svn.core
-from svn.core import (svn_auth_cred_username_t,
- svn_auth_cred_simple_t,
- svn_auth_cred_ssl_client_cert_t,
- svn_auth_cred_ssl_client_cert_pw_t,
- svn_auth_cred_ssl_server_trust_t,
- svn_auth_get_username_prompt_provider,
- svn_auth_get_simple_prompt_provider,
- svn_auth_get_ssl_server_trust_prompt_provider,
- svn_auth_get_ssl_client_cert_pw_prompt_provider,
- svn_auth_open)
+from bzrlib.plugins.svn.ra import (get_username_prompt_provider,
+ get_simple_prompt_provider,
+ get_ssl_server_trust_prompt_provider,
+ get_ssl_client_cert_pw_prompt_provider,
+ get_simple_provider, get_username_provider,
+ get_ssl_client_cert_file_provider,
+ get_ssl_client_cert_pw_file_provider,
+ get_ssl_server_trust_file_provider,
+ Auth
+ )
+from bzrlib.plugins.svn import ra
import urlparse
import urllib
+AUTH_PARAM_DEFAULT_USERNAME = 'svn:auth:username'
+AUTH_PARAM_DEFAULT_PASSWORD = 'svn:auth:password'
class SubversionAuthenticationConfig(AuthenticationConfig):
"""Simple extended version of AuthenticationConfig that can provide
self.port = port
self.path = path
- def get_svn_username(self, realm, may_save, pool=None):
+ def get_svn_username(self, realm, may_save):
"""Look up a Subversion user name in the Bazaar authentication cache.
:param realm: Authentication realm (optional)
:param may_save: Whether or not the username should be saved.
- :param pool: Allocation pool, is ignored.
- :param default: Assumed username
"""
- username_cred = svn_auth_cred_username_t()
- username_cred.username = self.get_user(self.scheme,
- host=self.host, path=self.path,
- realm=realm)
- username_cred.may_save = False
- return username_cred
+ username = self.get_user(self.scheme, host=self.host, path=self.path, realm=realm)
+ return (username, False)
def get_svn_simple(self, realm, username, may_save, pool):
"""Look up a Subversion user name+password combination in the Bazaar
:param may_save: Whether or not the username should be saved.
:param pool: Allocation pool, is ignored.
"""
- simple_cred = svn_auth_cred_simple_t()
- simple_cred.username = self.get_user(self.scheme,
+ username = self.get_user(self.scheme,
host=self.host, path=self.path, realm=realm) or username
- simple_cred.password = self.get_password(self.scheme, host=self.host,
+ password = self.get_password(self.scheme, host=self.host,
path=self.path, user=simple_cred.username,
realm=realm, prompt="%s %s password" % (realm, simple_cred.username))
- simple_cred.may_save = False
- return simple_cred
+ return (username, password, False)
def get_svn_ssl_server_trust(self, realm, failures, cert_info, may_save,
pool):
:param cert_info: Certificate information
:param may_save: Whether this information may be stored.
"""
- ssl_server_trust = svn_auth_cred_ssl_server_trust_t()
credentials = self.get_credentials(self.scheme, host=self.host)
if (credentials is not None and
credentials.has_key("verify_certificates") and
credentials["verify_certificates"] == False):
- ssl_server_trust.accepted_failures = (
- svn.core.SVN_AUTH_SSL_NOTYETVALID +
- svn.core.SVN_AUTH_SSL_EXPIRED +
- svn.core.SVN_AUTH_SSL_CNMISMATCH +
- svn.core.SVN_AUTH_SSL_UNKNOWNCA +
- svn.core.SVN_AUTH_SSL_OTHER)
+ accepted_failures = (
+ AUTH_SSL_NOTYETVALID +
+ AUTH_SSL_EXPIRED +
+ AUTH_SSL_CNMISMATCH +
+ AUTH_SSL_UNKNOWNCA +
+ AUTH_SSL_OTHER)
else:
- ssl_server_trust.accepted_failures = 0
- ssl_server_trust.may_save = False
- return ssl_server_trust
+ accepted_failures = 0
+ return (accepted_failures, False)
def get_svn_username_prompt_provider(self, retries):
"""Return a Subversion auth provider for retrieving the username, as
:param retries: Number of allowed retries.
"""
- return svn_auth_get_username_prompt_provider(self.get_svn_username,
+ return get_username_prompt_provider(self.get_svn_username,
retries)
def get_svn_simple_prompt_provider(self, retries):
:param retries: Number of allowed retries.
"""
- return svn_auth_get_simple_prompt_provider(self.get_svn_simple, retries)
+ return get_simple_prompt_provider(self.get_svn_simple, retries)
def get_svn_ssl_server_trust_prompt_provider(self):
"""Return a Subversion auth provider for checking
whether a SSL server is trusted."""
- return svn_auth_get_ssl_server_trust_prompt_provider(self.get_svn_ssl_server_trust)
+ return get_ssl_server_trust_prompt_provider(
+ self.get_svn_ssl_server_trust)
def get_svn_auth_providers(self):
"""Return a list of auth providers for this authentication file.
:param realm: Realm, optional.
:param may_save: Whether the password can be cached.
"""
- ssl_cred_pw = svn_auth_cred_ssl_client_cert_pw_t()
- ssl_cred_pw.password = ui_factory.get_password(
+ password = ui_factory.get_password(
"Please enter password for client certificate[realm=%s]" % realm)
- ssl_cred_pw.may_save = False
- return ssl_cred_pw
+ return (password, False)
def get_ssl_client_cert_pw_provider(tries):
- return svn_auth_get_ssl_client_cert_pw_prompt_provider(
+ return get_ssl_client_cert_pw_prompt_provider(
get_ssl_client_cert_pw, tries)
-
def get_stock_svn_providers():
- providers = [svn.client.get_simple_provider(),
- svn.client.get_username_provider(),
- svn.client.get_ssl_client_cert_file_provider(),
- svn.client.get_ssl_client_cert_pw_file_provider(),
- svn.client.get_ssl_server_trust_file_provider(),
+ providers = [get_simple_provider(),
+ get_username_provider(),
+ get_ssl_client_cert_file_provider(),
+ get_ssl_client_cert_pw_file_provider(),
+ get_ssl_server_trust_file_provider(),
]
- if hasattr(svn.client, 'get_windows_simple_provider'):
- providers.append(svn.client.get_windows_simple_provider())
+ if hasattr(ra, 'get_windows_simple_provider'):
+ providers.append(ra.get_windows_simple_provider())
- if hasattr(svn.client, 'get_keychain_simple_provider'):
- providers.append(svn.client.get_keychain_simple_provider())
+ if hasattr(ra, 'get_keychain_simple_provider'):
+ providers.append(ra.get_keychain_simple_provider())
- if hasattr(svn.client, 'get_windows_ssl_server_trust_provider'):
- providers.append(svn.client.get_windows_ssl_server_trust_provider())
+ if hasattr(ra, 'get_windows_ssl_server_trust_provider'):
+ providers.append(ra.get_windows_ssl_server_trust_provider())
return providers
# rather than prompting the user.
providers = get_stock_svn_providers()
- if svn.core.SVN_VER_MAJOR == 1 and svn.core.SVN_VER_MINOR >= 5:
+ (major, minor, patch, tag) = ra.version()
+ if major == 1 and minor >= 5:
providers += auth_config.get_svn_auth_providers()
providers += [get_ssl_client_cert_pw_provider(1)]
- auth_baton = svn.core.svn_auth_open(providers)
+ auth_baton = Auth(providers)
if creds is not None:
- (auth_baton.user, auth_baton.password) = urllib.splitpasswd(creds)
- if auth_baton.user is not None:
- svn.core.svn_auth_set_parameter(auth_baton,
- svn.core.SVN_AUTH_PARAM_DEFAULT_USERNAME, auth_baton.user)
- if auth_baton.password is not None:
- svn.core.svn_auth_set_parameter(auth_baton,
- svn.core.SVN_AUTH_PARAM_DEFAULT_PASSWORD, auth_baton.password)
+ (user, password) = urllib.splitpasswd(creds)
+ if user is not None:
+ auth_baton.set_parameter(AUTH_PARAM_DEFAULT_USERNAME, user)
+ if password is not None:
+ auth_baton.set_parameter(AUTH_PARAM_DEFAULT_PASSWORD, password)
return auth_baton
from bzrlib.revision import is_null, ensure_null, NULL_REVISION
from bzrlib.workingtree import WorkingTree
-import svn.client, svn.core
-from svn.core import SubversionException
-
+from bzrlib.plugins.svn import core
from bzrlib.plugins.svn.commit import push
from bzrlib.plugins.svn.config import BranchConfig
+from bzrlib.plugins.svn.core import SubversionException
from bzrlib.plugins.svn.errors import NotSvnBranchPath, ERR_FS_NO_SUCH_REVISION
from bzrlib.plugins.svn.format import get_rich_root_format
from bzrlib.plugins.svn.repository import SvnRepository
-from bzrlib.plugins.svn.transport import bzr_to_svn_url, create_svn_client
+from bzrlib.plugins.svn.transport import bzr_to_svn_url
class FakeControlFiles(object):
if revnum is None:
raise NotBranchError(self.base)
if self.repository.transport.check_path(branch_path.strip("/"),
- revnum) != svn.core.svn_node_dir:
+ revnum) != core.NODE_DIR:
raise NotBranchError(self.base)
except SubversionException, (_, num):
if num == ERR_FS_NO_SUCH_REVISION:
:param revision_id: Tip of the checkout.
:return: WorkingTree object of the checkout.
"""
- peg_rev = svn.core.svn_opt_revision_t()
- peg_rev.kind = svn.core.svn_opt_revision_head
-
- rev = svn.core.svn_opt_revision_t()
- if revision_id is None:
- rev.kind = svn.core.svn_opt_revision_head
- else:
+ import os, wc
+ if revision_id is not None:
revnum = self.lookup_revision_id(revision_id)
- rev.kind = svn.core.svn_opt_revision_number
- rev.value.number = revnum
+ else:
+ revnum = self.get_revnum()
+ os.mkdir(to_location)
svn_url = bzr_to_svn_url(self.base)
- client_ctx = create_svn_client(svn_url)
- svn.client.checkout(svn_url, to_location, rev,
- True, client_ctx)
-
- return WorkingTree.open(to_location)
+ wc.ensure_adm(to_location, self.repository.uuid, bzr_to_svn_url(self.base),
+ svn_url, revnum)
+ wt = WorkingTree.open(to_location)
+ wt.update(["."], revnum=revnum)
+ return wt
def create_checkout(self, to_location, revision_id=None, lightweight=False,
accelerator_tree=None, hardlink=False):
from bzrlib.errors import NoSuchRevision
from bzrlib.trace import mutter
+from bzrlib.plugins.svn.core import SubversionException
from bzrlib.plugins.svn.errors import ERR_FS_NO_SUCH_REVISION
-from svn.core import SubversionException
-import svn.core
-
class PathPropertyProvider(object):
def __init__(self, log):
--- /dev/null
+/*
+ * Copyright © 2008 Jelmer Vernooij <jelmer@samba.org>
+ * -*- coding: utf-8 -*-
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <stdbool.h>
+#include <Python.h>
+#include <apr_general.h>
+#include <svn_opt.h>
+#include <svn_client.h>
+
+#include "util.h"
+
+PyAPI_DATA(PyTypeObject) Client_Type;
+
+static bool to_opt_revision(PyObject *arg, svn_opt_revision_t *ret)
+{
+ if (PyInt_Check(arg)) {
+ ret->kind = svn_opt_revision_number;
+ ret->value.number = PyLong_AsLong(arg);
+ return true;
+ } else if (arg == Py_None) {
+ ret->kind = svn_opt_revision_unspecified;
+ return true;
+ } else if (PyString_Check(arg)) {
+ char *text = PyString_AsString(arg);
+ if (!strcmp(text, "HEAD")) {
+ ret->kind = svn_opt_revision_head;
+ return true;
+ } else if (!strcmp(text, "WORKING")) {
+ ret->kind = svn_opt_revision_working;
+ return true;
+ } else if (!strcmp(text, "BASE")) {
+ ret->kind = svn_opt_revision_base;
+ return true;
+ }
+ }
+
+ PyErr_SetString(PyExc_ValueError, "Unable to parse revision");
+ return false;
+}
+
+static PyObject *wrap_py_commit_items(const apr_array_header_t *commit_items)
+{
+ PyObject *ret;
+ int i;
+
+ ret = PyList_New(commit_items->nelts);
+ if (ret == NULL)
+ return NULL;
+
+ assert(commit_items->elt_size == sizeof(svn_client_commit_item_2_t *));
+
+ for (i = 0; i < commit_items->nelts; i++) {
+ svn_client_commit_item2_t *commit_item =
+ APR_ARRAY_IDX(commit_items, i, svn_client_commit_item2_t *);
+ PyObject *item, *copyfrom;
+
+ if (commit_item->copyfrom_url != NULL)
+ copyfrom = Py_BuildValue("(si)", commit_item->copyfrom_url,
+ commit_item->copyfrom_rev);
+ else
+ copyfrom = Py_None;
+
+ item = Py_BuildValue("(szlOi)",
+ /* commit_item->path */ "foo",
+ commit_item->url, commit_item->revision,
+ copyfrom,
+ commit_item->state_flags);
+
+ if (PyList_SetItem(ret, i, item) != 0)
+ return NULL;
+ }
+
+ return ret;
+}
+
+static svn_error_t *py_log_msg_func2(const char **log_msg, const char **tmp_file, const apr_array_header_t *commit_items, void *baton, apr_pool_t *pool)
+{
+ PyObject *py_commit_items, *ret, *py_log_msg, *py_tmp_file;
+ if (baton == Py_None)
+ return NULL;
+
+ py_commit_items = wrap_py_commit_items(commit_items);
+ if (py_commit_items == NULL)
+ return py_svn_error();
+
+ ret = PyObject_CallFunction(baton, "O", py_commit_items);
+ Py_DECREF(py_commit_items);
+ if (ret == NULL)
+ return py_svn_error();
+ if (PyTuple_Check(ret)) {
+ py_log_msg = PyTuple_GetItem(ret, 0);
+ py_tmp_file = PyTuple_GetItem(ret, 1);
+ } else {
+ py_tmp_file = Py_None;
+ py_log_msg = ret;
+ }
+ if (py_log_msg != Py_None) {
+ *log_msg = PyString_AsString(py_log_msg);
+ }
+ if (py_tmp_file != Py_None) {
+ *tmp_file = PyString_AsString(py_tmp_file);
+ }
+ Py_DECREF(ret);
+ return NULL;
+}
+
+static PyObject *py_commit_info_tuple(svn_commit_info_t *ci)
+{
+ if (ci == NULL)
+ Py_RETURN_NONE;
+ if (ci->revision == SVN_INVALID_REVNUM)
+ Py_RETURN_NONE;
+ return Py_BuildValue("(izz)", ci->revision, ci->date, ci->author);
+}
+
+typedef struct {
+ PyObject_HEAD
+ svn_client_ctx_t *client;
+ apr_pool_t *pool;
+ PyObject *callbacks;
+} ClientObject;
+
+static PyObject *client_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
+{
+ ClientObject *ret;
+ PyObject *config = Py_None;
+ char *kwnames[] = { "config", NULL };
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|O", kwnames, &config))
+ return NULL;
+
+ ret = PyObject_New(ClientObject, &Client_Type);
+ if (ret == NULL)
+ return NULL;
+
+ ret->pool = Pool();
+ if (ret->pool == NULL) {
+ PyObject_Del(ret);
+ return NULL;
+ }
+
+ if (!check_error(svn_client_create_context(&ret->client, ret->pool))) {
+ apr_pool_destroy(ret->pool);
+ PyObject_Del(ret);
+ return NULL;
+ }
+
+ if (config != Py_None) {
+ PyErr_SetString(PyExc_NotImplementedError, "custom config not supported yet");
+ }
+
+ return (PyObject *)ret;
+}
+
+static void client_dealloc(PyObject *self)
+{
+ ClientObject *client = (ClientObject *)self;
+ if (client->client->log_msg_func2 != NULL) {
+ Py_DECREF((PyObject *)client->client->log_msg_baton2);
+ }
+ apr_pool_destroy(client->pool);
+ PyObject_Del(self);
+}
+
+static PyObject *client_get_log_msg_func(PyObject *self, void *closure)
+{
+ ClientObject *client = (ClientObject *)self;
+ if (client->client->log_msg_func2 == NULL)
+ Py_RETURN_NONE;
+ return client->client->log_msg_baton2;
+}
+
+static int client_set_log_msg_func(PyObject *self, PyObject *func, void *closure)
+{
+ ClientObject *client = (ClientObject *)self;
+
+ if (client->client->log_msg_baton2 != NULL) {
+ Py_DECREF((PyObject *)client->client->log_msg_baton2);
+ }
+ if (func == Py_None) {
+ client->client->log_msg_func2 = NULL;
+ client->client->log_msg_baton2 = Py_None;
+ } else {
+ client->client->log_msg_func2 = py_log_msg_func2;
+ client->client->log_msg_baton2 = (void *)func;
+ }
+ Py_INCREF(func);
+ return 0;
+}
+
+static PyObject *client_add(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ char *path;
+ ClientObject *client = (ClientObject *)self;
+ bool recursive=true, force=false, no_ignore=false;
+ apr_pool_t *temp_pool;
+ char *kwnames[] = { "path", "recursive", "force", "no_ignore", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|bbb", kwnames,
+ &path, &recursive, &force, &no_ignore))
+ return NULL;
+
+ temp_pool = Pool();
+ if (temp_pool == NULL)
+ return NULL;
+
+ RUN_SVN_WITH_POOL(temp_pool,
+ svn_client_add3(path, recursive, force, no_ignore,
+ client->client, temp_pool));
+ apr_pool_destroy(temp_pool);
+ Py_RETURN_NONE;
+}
+
+static PyObject *client_checkout(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ ClientObject *client = (ClientObject *)self;
+ char *kwnames[] = { "url", "path", "peg_rev", "rev", "recurse", "ignore_externals", NULL };
+ svn_revnum_t result_rev;
+ svn_opt_revision_t c_peg_rev, c_rev;
+ char *url, *path;
+ apr_pool_t *temp_pool;
+ PyObject *peg_rev=Py_None, *rev=Py_None;
+ bool recurse=true, ignore_externals=false;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ss|OObb", kwnames, &url, &path, &peg_rev, &rev, &recurse, &ignore_externals))
+ return NULL;
+
+ if (!to_opt_revision(peg_rev, &c_peg_rev))
+ return NULL;
+ if (!to_opt_revision(rev, &c_rev))
+ return NULL;
+
+ temp_pool = Pool();
+ if (temp_pool == NULL)
+ return NULL;
+ RUN_SVN_WITH_POOL(temp_pool, svn_client_checkout2(&result_rev, url, path,
+ &c_peg_rev, &c_rev, recurse,
+ ignore_externals, client->client, temp_pool));
+ apr_pool_destroy(temp_pool);
+ return PyLong_FromLong(result_rev);
+}
+
+static PyObject *client_commit(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ PyObject *targets;
+ ClientObject *client = (ClientObject *)self;
+ bool recurse=true, keep_locks=true;
+ apr_pool_t *temp_pool;
+ svn_commit_info_t *commit_info = NULL;
+ PyObject *ret;
+ apr_array_header_t *apr_targets;
+ char *kwnames[] = { "targets", "recurse", "keep_locks", NULL };
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|bb", kwnames, &targets, &recurse, &keep_locks))
+ return NULL;
+ temp_pool = Pool();
+ if (temp_pool == NULL)
+ return NULL;
+ if (!string_list_to_apr_array(temp_pool, targets, &apr_targets)) {
+ apr_pool_destroy(temp_pool);
+ return NULL;
+ }
+ RUN_SVN_WITH_POOL(temp_pool, svn_client_commit3(&commit_info,
+ apr_targets,
+ recurse, keep_locks, client->client, temp_pool));
+ ret = py_commit_info_tuple(commit_info);
+ apr_pool_destroy(temp_pool);
+
+ return ret;
+}
+
+static PyObject *client_mkdir(PyObject *self, PyObject *args)
+{
+ PyObject *paths;
+ svn_commit_info_t *commit_info = NULL;
+ apr_pool_t *temp_pool;
+ apr_array_header_t *apr_paths;
+ ClientObject *client = (ClientObject *)self;
+ PyObject *ret;
+ if (!PyArg_ParseTuple(args, "O", &paths))
+ return NULL;
+ temp_pool = Pool();
+ if (temp_pool == NULL)
+ return NULL;
+ if (!string_list_to_apr_array(temp_pool, paths, &apr_paths)) {
+ apr_pool_destroy(temp_pool);
+ return NULL;
+ }
+
+ if (!check_error(svn_client_mkdir2(&commit_info, apr_paths,
+ client->client, temp_pool)))
+ return NULL;
+ ret = py_commit_info_tuple(commit_info);
+ apr_pool_destroy(temp_pool);
+
+ return ret;
+}
+
+static PyObject *client_delete(PyObject *self, PyObject *args)
+{
+ PyObject *paths;
+ bool force=false;
+ apr_pool_t *temp_pool;
+ svn_commit_info_t *commit_info = NULL;
+ PyObject *ret;
+ apr_array_header_t *apr_paths;
+ ClientObject *client = (ClientObject *)self;
+
+ if (!PyArg_ParseTuple(args, "O|b", &paths, &force))
+ return NULL;
+
+ temp_pool = Pool();
+ if (temp_pool == NULL)
+ return NULL;
+ if (!string_list_to_apr_array(temp_pool, paths, &apr_paths)) {
+ apr_pool_destroy(temp_pool);
+ return NULL;
+ }
+
+ RUN_SVN_WITH_POOL(temp_pool, svn_client_delete2(&commit_info,
+ apr_paths,
+ force, client->client, temp_pool));
+
+ ret = py_commit_info_tuple(commit_info);
+
+ apr_pool_destroy(temp_pool);
+
+ return ret;
+}
+
+static PyObject *client_copy(PyObject *self, PyObject *args)
+{
+ char *src_path, *dst_path;
+ PyObject *src_rev=Py_None;
+ svn_commit_info_t *commit_info = NULL;
+ apr_pool_t *temp_pool;
+ svn_opt_revision_t c_src_rev;
+ PyObject *ret;
+ ClientObject *client = (ClientObject *)self;
+ if (!PyArg_ParseTuple(args, "ss|O", &src_path, &dst_path, &src_rev))
+ return NULL;
+ if (!to_opt_revision(src_rev, &c_src_rev))
+ return NULL;
+ temp_pool = Pool();
+ if (temp_pool == NULL)
+ return NULL;
+ RUN_SVN_WITH_POOL(temp_pool, svn_client_copy3(&commit_info, src_path,
+ &c_src_rev, dst_path, client->client, temp_pool));
+ ret = py_commit_info_tuple(commit_info);
+ apr_pool_destroy(temp_pool);
+ return ret;
+}
+
+static PyObject *client_propset(PyObject *self, PyObject *args)
+{
+ char *propname;
+ svn_string_t c_propval;
+ int recurse = true;
+ int skip_checks = false;
+ ClientObject *client = (ClientObject *)self;
+ apr_pool_t *temp_pool;
+ char *target;
+
+ if (!PyArg_ParseTuple(args, "sz#s|bb", &propname, &c_propval.data, &c_propval.len, &target, &recurse, &skip_checks))
+ return NULL;
+ temp_pool = Pool();
+ if (temp_pool == NULL)
+ return NULL;
+ RUN_SVN_WITH_POOL(temp_pool, svn_client_propset2(propname, &c_propval,
+ target, recurse, skip_checks, client->client, temp_pool));
+ apr_pool_destroy(temp_pool);
+ Py_RETURN_NONE;
+}
+
+static PyObject *client_propget(PyObject *self, PyObject *args)
+{
+ svn_opt_revision_t c_peg_rev;
+ svn_opt_revision_t c_rev;
+ apr_hash_t *hash_props;
+ bool recurse = false;
+ char *propname;
+ apr_pool_t *temp_pool;
+ char *target;
+ PyObject *peg_revision;
+ PyObject *revision;
+ ClientObject *client = (ClientObject *)self;
+ PyObject *ret;
+
+ if (!PyArg_ParseTuple(args, "ssOO|b", &propname, &target, &peg_revision,
+ &revision, &recurse))
+ return NULL;
+ if (!to_opt_revision(peg_revision, &c_peg_rev))
+ return NULL;
+ if (!to_opt_revision(revision, &c_rev))
+ return NULL;
+ temp_pool = Pool();
+ if (temp_pool == NULL)
+ return NULL;
+ RUN_SVN_WITH_POOL(temp_pool,
+ svn_client_propget2(&hash_props, propname, target,
+ &c_peg_rev, &c_rev, recurse, client->client, temp_pool));
+ ret = prop_hash_to_dict(hash_props);
+ apr_pool_destroy(temp_pool);
+ return ret;
+}
+
+static PyObject *client_update(PyObject *self, PyObject *args)
+{
+ bool recurse = true;
+ bool ignore_externals = false;
+ apr_pool_t *temp_pool;
+ PyObject *rev = Py_None, *paths;
+ apr_array_header_t *result_revs, *apr_paths;
+ svn_opt_revision_t c_rev;
+ svn_revnum_t ret_rev;
+ PyObject *ret;
+ int i = 0;
+ ClientObject *client = (ClientObject *)self;
+
+ if (!PyArg_ParseTuple(args, "O|Obb", &paths, &rev, &recurse, &ignore_externals))
+ return NULL;
+
+ if (!to_opt_revision(rev, &c_rev))
+ return NULL;
+ temp_pool = Pool();
+ if (temp_pool == NULL)
+ return NULL;
+ if (!string_list_to_apr_array(temp_pool, paths, &apr_paths)) {
+ apr_pool_destroy(temp_pool);
+ return NULL;
+ }
+ RUN_SVN_WITH_POOL(temp_pool, svn_client_update2(&result_revs,
+ apr_paths, &c_rev,
+ recurse, ignore_externals, client->client, temp_pool));
+ ret = PyList_New(result_revs->nelts);
+ if (ret == NULL)
+ return NULL;
+ for (i = 0; i < result_revs->nelts; i++) {
+ ret_rev = APR_ARRAY_IDX(result_revs, i, svn_revnum_t);
+ if (PyList_SetItem(ret, i, PyLong_FromLong(ret_rev)) != 0)
+ return NULL;
+ }
+ apr_pool_destroy(temp_pool);
+ return ret;
+}
+
+static PyObject *client_revprop_get(PyObject *self, PyObject *args)
+{
+ PyObject *rev = Py_None;
+ char *propname, *propval, *url;
+ svn_revnum_t set_rev;
+ svn_opt_revision_t c_rev;
+ svn_string_t *c_val;
+ ClientObject *client = (ClientObject *)self;
+ apr_pool_t *temp_pool;
+ PyObject *ret;
+ if (!PyArg_ParseTuple(args, "sssO", &propname, &propval, &url, &rev))
+ return NULL;
+ if (!to_opt_revision(rev, &c_rev))
+ return NULL;
+ temp_pool = Pool();
+ if (temp_pool == NULL)
+ return NULL;
+ RUN_SVN_WITH_POOL(temp_pool, svn_client_revprop_get(propname, &c_val, url,
+ &c_rev, &set_rev, client->client, temp_pool));
+ ret = Py_BuildValue("(z#i)", c_val->data, c_val->len, set_rev);
+ apr_pool_destroy(temp_pool);
+ return ret;
+}
+
+static PyObject *client_revprop_set(PyObject *self, PyObject *args)
+{
+ PyObject *rev = Py_None;
+ bool force = false;
+ ClientObject *client = (ClientObject *)self;
+ char *propname, *url;
+ svn_revnum_t set_rev;
+ svn_opt_revision_t c_rev;
+ apr_pool_t *temp_pool;
+ svn_string_t c_val;
+ if (!PyArg_ParseTuple(args, "sz#s|Ob", &propname, &c_val.data, &c_val.len, &url, &rev, &force))
+ return NULL;
+ if (!to_opt_revision(rev, &c_rev))
+ return NULL;
+ temp_pool = Pool();
+ if (temp_pool == NULL)
+ return NULL;
+ RUN_SVN_WITH_POOL(temp_pool, svn_client_revprop_set(propname, &c_val, url,
+ &c_rev, &set_rev, force, client->client, temp_pool));
+ apr_pool_destroy(temp_pool);
+ return PyLong_FromLong(set_rev);
+}
+
+static PyObject *client_log(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ char *kwnames[] = { "targets", "callback", "peg_revision", "start", "end", "limit", "discover_changed_paths", "strict_node_history", NULL };
+ PyObject *targets, *callback, *peg_revision=Py_None, *start=Py_None,
+ *end=Py_None;
+ ClientObject *client = (ClientObject *)self;
+ apr_pool_t *temp_pool;
+ int limit=0;
+ bool discover_changed_paths=true, strict_node_history=true;
+ svn_opt_revision_t c_peg_rev, c_start_rev, c_end_rev;
+ apr_array_header_t *apr_paths;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OO|OOOlbb",
+ kwnames, &targets, &callback,
+ &peg_revision, &start, &end,
+ &limit, &discover_changed_paths,
+ &strict_node_history))
+ return NULL;
+
+ if (!to_opt_revision(peg_revision, &c_peg_rev))
+ return NULL;
+ if (!to_opt_revision(start, &c_start_rev))
+ return NULL;
+ if (!to_opt_revision(end, &c_end_rev))
+ return NULL;
+ temp_pool = Pool();
+ if (temp_pool == NULL)
+ return NULL;
+ if (!string_list_to_apr_array(temp_pool, targets, &apr_paths)) {
+ apr_pool_destroy(temp_pool);
+ return NULL;
+ }
+ RUN_SVN_WITH_POOL(temp_pool, svn_client_log3(apr_paths,
+ &c_peg_rev, &c_start_rev, &c_end_rev, limit, discover_changed_paths, strict_node_history, py_svn_log_wrapper, callback, client->client, temp_pool));
+ apr_pool_destroy(temp_pool);
+ Py_RETURN_NONE;
+}
+
+static PyMethodDef client_methods[] = {
+ { "add", (PyCFunction)client_add, METH_VARARGS|METH_KEYWORDS, NULL },
+ { "checkout", (PyCFunction)client_checkout, METH_VARARGS|METH_KEYWORDS, NULL },
+ { "commit", (PyCFunction)client_commit, METH_VARARGS|METH_KEYWORDS, NULL },
+ { "mkdir", client_mkdir, METH_VARARGS, NULL },
+ { "delete", client_delete, METH_VARARGS, NULL },
+ { "copy", client_copy, METH_VARARGS, NULL },
+ { "propset", client_propset, METH_VARARGS, NULL },
+ { "propget", client_propget, METH_VARARGS, NULL },
+ { "update", client_update, METH_VARARGS, NULL },
+ { "revprop_get", client_revprop_get, METH_VARARGS, NULL },
+ { "revprop_set", client_revprop_set, METH_VARARGS, NULL },
+ { "log", (PyCFunction)client_log, METH_KEYWORDS|METH_VARARGS, NULL },
+ { NULL, }
+};
+
+static PyGetSetDef client_getset[] = {
+ { "log_msg_func", client_get_log_msg_func, client_set_log_msg_func, NULL },
+ { NULL, }
+};
+
+PyTypeObject Client_Type = {
+ PyObject_HEAD_INIT(&PyType_Type) 0,
+ .tp_name = "client.Client",
+ .tp_basicsize = sizeof(ClientObject),
+ .tp_methods = client_methods,
+ .tp_dealloc = client_dealloc,
+ .tp_new = client_new,
+ .tp_getset = client_getset
+};
+
+void initclient(void)
+{
+ PyObject *mod;
+
+ if (PyType_Ready(&Client_Type) < 0)
+ return;
+
+ /* Make sure APR is initialized */
+ apr_initialize();
+
+ mod = Py_InitModule3("client", NULL, "Client methods");
+ if (mod == NULL)
+ return;
+
+ Py_INCREF(&Client_Type);
+ PyModule_AddObject(mod, "Client", (PyObject *)&Client_Type);
+}
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
"""Committing and pushing to Subversion repositories."""
-import svn.delta
-from svn.core import Pool, SubversionException, svn_time_to_cstring
+from core import SubversionException, time_to_cstring
+import core
from bzrlib import debug, osutils, urlutils
from bzrlib.branch import Branch
from bzrlib.plugins.svn import properties
from cStringIO import StringIO
+
from bzrlib.plugins.svn.errors import ChangesRootLHSHistory, MissingPrefix, RevpropChangeFailed, ERR_FS_TXN_OUT_OF_DATE, ERR_REPOS_DISABLED_FEATURE
from bzrlib.plugins.svn.svk import (generate_svk_feature, serialize_svk_features,
parse_svk_features, SVN_PROP_SVK_MERGE)
from bzrlib.plugins.svn.logwalker import lazy_dict
from bzrlib.plugins.svn.mapping import parse_revision_id
+from bzrlib.plugins.svn.ra import txdelta_send_stream
from bzrlib.plugins.svn.repository import SvnRepositoryFormat, SvnRepository
import urllib
for i in range(len(bp_parts), 0, -1):
current = bp_parts[:i]
path = "/".join(current).strip("/")
- if transport.check_path(path, base_rev) == svn.core.svn_node_dir:
+ if transport.check_path(path, base_rev) == core.NODE_DIR:
return current
return []
super(SvnCommitBuilder, self).__init__(repository, parents,
config, timestamp, timezone, committer, revprops, revision_id)
self.branch = branch
- self.pool = Pool()
# Gather information about revision on top of which the commit is
# happening
"""See CommitBuilder.modified_directory()."""
self.modified_dirs.add(file_id)
- def _file_process(self, file_id, contents, baton):
+ def _file_process(self, file_id, contents, file_editor):
"""Pass the changes to a file to the Subversion commit editor.
:param file_id: Id of the file to modify.
:param contents: Contents of the file.
- :param baton: Baton under which the file is known to the editor.
+ :param file_editor: Editor for this file
"""
- assert baton is not None
- (txdelta, txbaton) = self.editor.apply_textdelta(baton, None, self.pool)
- digest = svn.delta.svn_txdelta_send_stream(StringIO(contents), txdelta, txbaton, self.pool)
+ assert file_editor is not None
+ txdelta = file_editor.apply_textdelta(None)
+ digest = txdelta_send_stream(StringIO(contents), txdelta)
if 'validate' in debug.debug_flags:
from fetch import md5_strings
assert digest == md5_strings(contents)
- def _dir_process(self, path, file_id, baton):
+ def _dir_process(self, path, file_id, dir_editor):
"""Pass the changes to a directory to the commit editor.
:param path: Path (from repository root) to the directory.
:param file_id: File id of the directory
- :param baton: Baton of the directory for the editor.
+ :param dir_editor: Editor for the directory.
"""
- assert baton is not None
+ assert dir_editor is not None
# Loop over entries of file_id in self.old_inv
# remove if they no longer exist with the same name
# or parents
child_ie.parent_id != self.new_inventory[child_ie.file_id].parent_id or
# ... name changed
self.new_inventory[child_ie.file_id].name != child_name):
- self.mutter('removing %r(%r)', (child_name, child_ie.file_id))
- self.editor.delete_entry(
+ self.mutter('removing %r(%r)', child_name, child_ie.file_id)
+ dir_editor.delete_entry(
urlutils.join(self.branch.get_branch_path(), path, child_name),
- self.base_revnum, baton, self.pool)
+ self.base_revnum)
# Loop over file children of file_id in self.new_inventory
for child_name in self.new_inventory[file_id].children:
# add them if they didn't exist in old_inv
if not child_ie.file_id in self.old_inv:
self.mutter('adding %s %r', child_ie.kind, new_child_path)
- child_baton = self.editor.add_file(
- full_new_child_path, baton, None, -1, self.pool)
-
+ child_editor = dir_editor.add_file(full_new_child_path)
# copy if they existed at different location
elif (self.old_inv.id2path(child_ie.file_id) != new_child_path or
self.mutter('copy %s %r -> %r', child_ie.kind,
self.old_inv.id2path(child_ie.file_id),
new_child_path)
- child_baton = self.editor.add_file(
- full_new_child_path, baton,
+ child_editor = dir_editor.add_file(
+ full_new_child_path,
urlutils.join(self.repository.transport.svn_url, self.base_path, self.old_inv.id2path(child_ie.file_id)),
- self.base_revnum, self.pool)
+ self.base_revnum)
# open if they existed at the same location
elif child_ie.revision is None:
self.mutter('open %s %r', child_ie.kind, new_child_path)
- child_baton = self.editor.open_file(
- full_new_child_path, baton, self.base_revnum, self.pool)
+ child_editor = dir_editor.open_file(full_new_child_path, self.base_revnum)
else:
# Old copy of the file was retained. No need to send changes
assert child_ie.file_id not in self.modified_files
- child_baton = None
+ child_editor = None
if child_ie.file_id in self.old_inv:
old_executable = self.old_inv[child_ie.file_id].executable
old_special = False
old_executable = False
- if child_baton is not None:
+ if child_editor is not None:
if old_executable != child_ie.executable:
if child_ie.executable:
value = properties.PROP_EXECUTABLE_VALUE
else:
value = None
- self.editor.change_file_prop(child_baton,
- properties.PROP_EXECUTABLE, value, self.pool)
+ child_editor.change_prop(properties.PROP_EXECUTABLE, value)
if old_special != (child_ie.kind == 'symlink'):
if child_ie.kind == 'symlink':
else:
value = None
- self.editor.change_file_prop(child_baton,
- properties.PROP_SPECIAL, value, self.pool)
+ child_editor.change_prop(properties.PROP_SPECIAL, value)
# handle the file
if child_ie.file_id in self.modified_files:
self._file_process(child_ie.file_id,
- self.modified_files[child_ie.file_id], child_baton)
+ self.modified_files[child_ie.file_id], child_editor)
- if child_baton is not None:
- self.editor.close_file(child_baton, None, self.pool)
+ if child_editor is not None:
+ child_editor.close()
# Loop over subdirectories of file_id in self.new_inventory
for child_name in self.new_inventory[file_id].children:
# add them if they didn't exist in old_inv
if not child_ie.file_id in self.old_inv:
self.mutter('adding dir %r', child_ie.name)
- child_baton = self.editor.add_directory(
+ child_editor = dir_editor.add_directory(
urlutils.join(self.branch.get_branch_path(),
- new_child_path), baton, None, -1, self.pool)
+ new_child_path))
# copy if they existed at different location
elif self.old_inv.id2path(child_ie.file_id) != new_child_path:
old_child_path = self.old_inv.id2path(child_ie.file_id)
- self.mutter('copy dir %r -> %r', old_child_path, new_child_path)
- child_baton = self.editor.add_directory(
+ self.mutter('copy dir %r -> %r', old_child_path, new_child_path)
+ child_editor = dir_editor.add_directory(
urlutils.join(self.branch.get_branch_path(), new_child_path),
- baton,
- urlutils.join(self.repository.transport.svn_url, self.base_path, old_child_path), self.base_revnum, self.pool)
+ urlutils.join(self.repository.transport.svn_url, self.base_path, old_child_path), self.base_revnum)
# open if they existed at the same location and
# the directory was touched
elif self.new_inventory[child_ie.file_id].revision is None:
self.mutter('open dir %r', new_child_path)
- child_baton = self.editor.open_directory(
+ child_editor = dir_editor.open_directory(
urlutils.join(self.branch.get_branch_path(), new_child_path),
- baton, self.base_revnum, self.pool)
+ self.base_revnum)
else:
assert child_ie.file_id not in self.modified_dirs
continue
# Handle this directory
if child_ie.file_id in self.modified_dirs:
- self._dir_process(new_child_path, child_ie.file_id, child_baton)
+ self._dir_process(new_child_path, child_ie.file_id, child_editor)
- self.editor.close_directory(child_baton, self.pool)
+ child_editor.close()
- def open_branch_batons(self, root, elements, existing_elements,
+ def open_branch_editors(self, root, elements, existing_elements,
base_path, base_rev, replace_existing):
- """Open a specified directory given a baton for the repository root.
+ """Open a specified directory given an editor for the repository root.
- :param root: Baton for the repository root
+ :param root: Editor for the repository root
:param elements: List of directory names to open
:param existing_elements: List of directory names that exist
:param base_path: Path to base top-level branch on
# Open paths leading up to branch
for i in range(0, len(elements)-1):
# Does directory already exist?
- ret.append(self.editor.open_directory(
- "/".join(existing_elements[0:i+1]), ret[-1], -1, self.pool))
+ ret.append(ret[-1].open_directory("/".join(existing_elements[0:i+1]), -1))
if (len(existing_elements) != len(elements) and
len(existing_elements)+1 != len(elements)):
# branch_path.
if (len(existing_elements) == len(elements) and
not replace_existing):
- ret.append(self.editor.open_directory(
- "/".join(elements), ret[-1], base_rev, self.pool))
+ ret.append(ret[-1].open_directory(
+ "/".join(elements), base_rev))
else: # Branch has to be created
# Already exists, old copy needs to be removed
name = "/".join(elements)
if name == "":
raise ChangesRootLHSHistory()
self.mutter("removing branch dir %r", name)
- self.editor.delete_entry(name, -1, ret[-1])
+ ret[-1].delete_entry(name, -1)
if base_path is not None:
base_url = urlutils.join(self.repository.transport.svn_url, base_path)
else:
base_url = None
self.mutter("adding branch dir %r", name)
- ret.append(self.editor.add_directory(
- name, ret[-1], base_url, base_rev, self.pool))
+ ret.append(ret[-1].add_directory(
+ name, base_url, base_rev))
return ret
"""Finish the commit.
"""
- def done(revision_data, pool):
+ def done(revision, author, date):
"""Callback that is called by the Subversion commit editor
once the commit finishes.
-
- :param revision_data: Revision metadata
"""
- self.revision_metadata = revision_data
-
+ class CommitResult:
+ def __init__(self, revision, author, date):
+ self.revision = revision
+ self.author = author
+ self.date = date
+ self.revision_metadata = CommitResult(revision, author, date)
+
bp_parts = self.branch.get_branch_path().split("/")
repository_latest_revnum = self.repository.get_latest_revnum()
lock = self.repository.transport.lock_write(".")
try:
existing_bp_parts = _check_dirs_exist(self.repository.transport,
bp_parts, -1)
- self.revision_metadata = None
for prop in self._svn_revprops:
if not properties.is_valid_property_name(prop):
warning("Setting property %r with invalid characters in name", prop)
+ conn = self.repository.transport.get_connection()
try:
- self.editor = self.repository.transport.get_commit_editor(
- self._svn_revprops, done, None, False)
- self._svn_revprops = {}
- except NotImplementedError:
- if set_revprops:
- raise
- # Try without bzr: revprops
- self.editor = self.repository.transport.get_commit_editor({
- properties.PROP_REVISION_LOG: self._svn_revprops[properties.PROP_REVISION_LOG]},
- done, None, False)
- del self._svn_revprops[properties.PROP_REVISION_LOG]
-
- root = self.editor.open_root(self.base_revnum)
-
- replace_existing = False
- # See whether the base of the commit matches the lhs parent
- # if not, we need to replace the existing directory
- if len(bp_parts) == len(existing_bp_parts):
- if self.base_path.strip("/") != "/".join(bp_parts).strip("/"):
- replace_existing = True
- elif self.base_revnum < self.repository._log.find_latest_change(self.branch.get_branch_path(), repository_latest_revnum):
- replace_existing = True
-
- if replace_existing and self.branch._get_append_revisions_only():
- raise AppendRevisionsOnlyViolation(self.branch.base)
-
- # TODO: Accept create_prefix argument (#118787)
- branch_batons = self.open_branch_batons(root, bp_parts,
- existing_bp_parts, self.base_path, self.base_revnum,
- replace_existing)
-
- self._dir_process("", self.new_inventory.root.file_id,
- branch_batons[-1])
-
- # Set all the revprops
- for prop, value in self._svnprops.items():
- if not properties.is_valid_property_name(prop):
- warning("Setting property %r with invalid characters in name", prop)
- if value is not None:
- value = value.encode('utf-8')
- self.editor.change_dir_prop(branch_batons[-1], prop, value,
- self.pool)
- self.mutter("Setting root file property %r -> %r", prop, value)
-
- for baton in reversed(branch_batons):
- self.editor.close_directory(baton, self.pool)
-
- self.editor.close()
+ try:
+ self.editor = conn.get_commit_editor(
+ self._svn_revprops, done, None, False)
+ self._svn_revprops = {}
+ self.editor_active = True
+ except NotImplementedError:
+ if set_revprops:
+ raise
+ # Try without bzr: revprops
+ self.editor = conn.get_commit_editor({
+ properties.PROP_REVISION_LOG: self._svn_revprops[properties.PROP_REVISION_LOG]},
+ done, None, False)
+ del self._svn_revprops[properties.PROP_REVISION_LOG]
+
+ root = self.editor.open_root(self.base_revnum)
+
+ replace_existing = False
+ # See whether the base of the commit matches the lhs parent
+ # if not, we need to replace the existing directory
+ if len(bp_parts) == len(existing_bp_parts):
+ if self.base_path.strip("/") != "/".join(bp_parts).strip("/"):
+ replace_existing = True
+ elif self.base_revnum < self.repository._log.find_latest_change(self.branch.get_branch_path(), repository_latest_revnum):
+ replace_existing = True
+
+ if replace_existing and self.branch._get_append_revisions_only():
+ raise AppendRevisionsOnlyViolation(self.branch.base)
+
+ # TODO: Accept create_prefix argument (#118787)
+ branch_editors = self.open_branch_editors(root, bp_parts,
+ existing_bp_parts, self.base_path, self.base_revnum,
+ replace_existing)
+
+ self._dir_process("", self.new_inventory.root.file_id,
+ branch_editors[-1])
+
+ # Set all the revprops
+ for prop, value in self._svnprops.items():
+ if not properties.is_valid_property_name(prop):
+ warning("Setting property %r with invalid characters in name", prop)
+ if value is not None:
+ value = value.encode('utf-8')
+ branch_editors[-1].change_prop(prop, value)
+ self.mutter("Setting root file property %r -> %r", prop, value)
+
+ for dir_editor in reversed(branch_editors):
+ dir_editor.close()
+
+ self.editor.close()
+ self.editor_active = False
+ finally:
+ if self.editor_active:
+ self.editor.abort()
+ self.repository.transport.add_connection(conn)
finally:
lock.unlock()
if properties.PROP_REVISION_AUTHOR in override_svn_revprops:
new_revprops[properties.PROP_REVISION_AUTHOR] = self._committer.encode("utf-8")
if properties.PROP_REVISION_DATE in override_svn_revprops:
- new_revprops[properties.PROP_REVISION_DATE] = svn_time_to_cstring(1000000*self._timestamp)
+ new_revprops[properties.PROP_REVISION_DATE] = time_to_cstring(1000000*self._timestamp)
set_svn_revprops(self.repository.transport, self.revision_metadata.revision, new_revprops)
try:
it is a candidate to commit.
"""
self.new_inventory.add(ie)
+ return self._get_delta(ie, parent_invs[0], path), True
def replay_delta(builder, old_tree, new_tree):
from bzrlib import osutils, urlutils, trace
from bzrlib.config import IniBasedConfig, config_dir, ensure_config_dir_exists, GlobalConfig, LocationConfig, Config, STORE_BRANCH, STORE_GLOBAL, STORE_LOCATION
+from bzrlib.plugins.svn.core import SubversionException
+
import os
from bzrlib.plugins.svn import properties
-import svn.core
-from svn.core import SubversionException
-
# Settings are stored by UUID.
# Data stored includes default branching scheme and locations the repository
# was seen at.
from bzrlib.plugins.svn.errors import ERR_STREAM_MALFORMED_DATA
from bzrlib.plugins.svn.format import get_rich_root_format
-
-import svn.core, svn.repos
-from svn.core import SubversionException
+from bzrlib.plugins.svn import core, repos
+from bzrlib.plugins.svn.core import SubversionException
def transport_makedirs(transport, location_url):
"""Create missing directories.
created.
"""
from cStringIO import StringIO
- repos = svn.repos.svn_repos_create(outputdir, '', '', None, None)
+ r = repos.create(outputdir)
if dumpfile.endswith(".gz"):
import gzip
file = gzip.GzipFile(dumpfile)
else:
file = open(dumpfile)
try:
- svn.repos.load_fs2(repos, file, StringIO(),
- svn.repos.load_uuid_default, '', 0, 0, None)
+ r.load_fs(file, StringIO(), repos.LOAD_UUID_DEFAULT)
except SubversionException, (_, num):
if num == ERR_STREAM_MALFORMED_DATA:
raise NotDumpFile(dumpfile)
raise
- return repos
+ return r
def convert_repository(source_repos, output_url, scheme=None, layout=None,
--- /dev/null
+/*
+ * Copyright © 2008 Jelmer Vernooij <jelmer@samba.org>
+ * -*- coding: utf-8 -*-
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <stdbool.h>
+#include <Python.h>
+#include <apr_general.h>
+#include <string.h>
+#include <svn_time.h>
+#include <svn_config.h>
+#include <svn_io.h>
+#include <svn_utf.h>
+
+#include "util.h"
+
+/** Convert a UNIX timestamp to a Subversion CString. */
+static PyObject *time_to_cstring(PyObject *self, PyObject *args)
+{
+ PyObject *ret;
+ apr_pool_t *pool;
+ apr_time_t when;
+ if (!PyArg_ParseTuple(args, "L", &when))
+ return NULL;
+ pool = Pool();
+ if (pool == NULL)
+ return NULL;
+ ret = PyString_FromString(svn_time_to_cstring(when, pool));
+ apr_pool_destroy(pool);
+ return ret;
+}
+
+/** Parse a Subversion time string and return a UNIX timestamp. */
+static PyObject *time_from_cstring(PyObject *self, PyObject *args)
+{
+ apr_time_t when;
+ apr_pool_t *pool;
+ char *data;
+
+ if (!PyArg_ParseTuple(args, "s", &data))
+ return NULL;
+
+ pool = Pool();
+ if (pool == NULL)
+ return NULL;
+ RUN_SVN_WITH_POOL(pool, svn_time_from_cstring(&when, data, pool));
+ apr_pool_destroy(pool);
+ return PyLong_FromLongLong(when);
+}
+
+typedef struct {
+ PyObject_HEAD
+ svn_config_t *item;
+} ConfigObject;
+
+PyTypeObject Config_Type = {
+ PyObject_HEAD_INIT(NULL) 0,
+ .tp_name = "core.Config",
+ .tp_basicsize = sizeof(ConfigObject),
+ .tp_dealloc = (destructor)PyObject_Del,
+};
+
+static PyObject *get_config(PyObject *self, PyObject *args)
+{
+ apr_pool_t *pool;
+ apr_hash_t *cfg_hash = NULL;
+ apr_hash_index_t *idx;
+ const char *key;
+ svn_config_t *val;
+ apr_ssize_t klen;
+ char *config_dir = NULL;
+ PyObject *ret;
+
+ if (!PyArg_ParseTuple(args, "|z", &config_dir))
+ return NULL;
+
+ pool = Pool();
+ if (pool == NULL)
+ return NULL;
+
+ RUN_SVN_WITH_POOL(pool,
+ svn_config_get_config(&cfg_hash, config_dir, pool));
+ ret = PyDict_New();
+ for (idx = apr_hash_first(pool, cfg_hash); idx != NULL;
+ idx = apr_hash_next(idx)) {
+ ConfigObject *data;
+ apr_hash_this(idx, (const void **)&key, &klen, (void **)&val);
+ data = PyObject_New(ConfigObject, &Config_Type);
+ data->item = val;
+ PyDict_SetItemString(ret, key, (PyObject *)data);
+ }
+ apr_pool_destroy(pool);
+ return ret;
+}
+
+
+static PyMethodDef core_methods[] = {
+ { "get_config", get_config, METH_VARARGS, NULL },
+ { "time_from_cstring", time_from_cstring, METH_VARARGS, NULL },
+ { "time_to_cstring", time_to_cstring, METH_VARARGS, NULL },
+ { NULL, }
+};
+
+void initcore(void)
+{
+ static apr_pool_t *pool;
+ PyObject *mod;
+
+ if (PyType_Ready(&Config_Type) < 0)
+ return;
+
+ apr_initialize();
+ pool = Pool();
+ if (pool == NULL)
+ return;
+ svn_utf_initialize(pool);
+
+ mod = Py_InitModule3("core", core_methods, "Core functions");
+ if (mod == NULL)
+ return;
+
+ PyModule_AddIntConstant(mod, "NODE_DIR", svn_node_dir);
+ PyModule_AddIntConstant(mod, "NODE_FILE", svn_node_file);
+ PyModule_AddIntConstant(mod, "NODE_UNKNOWN", svn_node_unknown);
+ PyModule_AddIntConstant(mod, "NODE_NONE", svn_node_none);
+
+ PyModule_AddObject(mod, "SubversionException",
+ PyErr_NewException("core.SubversionException", NULL, NULL));
+}
--- /dev/null
+# Copyright (C) 2005-2006 Jelmer Vernooij <jelmer@samba.org>
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+"""Subversion delta operations."""
+
+def apply_txdelta_handler(sbuf, target_stream):
+ def apply_window(window):
+ (sview_offset, sview_len, tview_len, src_ops, ops, new_data) = window
+ sview = sbuf[sview_offset:sview_offset+sview_len]
+ tview = txdelta_apply_ops(src_ops, ops, new_data, sview)
+ assert len(tview) == tview_len
+ target_stream.write(tview)
+ return apply_window
+
+def txdelta_apply_ops(src_ops, ops, new_data, sview):
+ tview = ""
+ for (action, offset, length) in ops:
+ if action == 0:
+ # Copy from source area.
+ tview += sview[offset:offset+length]
+ elif action == 1:
+ for i in xrange(length):
+ tview += tview[offset+i]
+ elif action == 2:
+ tview += new_data[offset:offset+length]
+ else:
+ raise Exception("Invalid delta instruction code")
+
+ return tview
--- /dev/null
+/* Copyright © 2008 Jelmer Vernooij <jelmer@samba.org>
+ * -*- coding: utf-8 -*-
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <stdbool.h>
+#include <Python.h>
+#include <apr_general.h>
+#include <svn_types.h>
+#include <svn_delta.h>
+
+#include "editor.h"
+#include "util.h"
+
+typedef struct {
+ PyObject_HEAD
+ const svn_delta_editor_t *editor;
+ void *baton;
+ apr_pool_t *pool;
+ void (*done_cb) (void *baton);
+ void *done_baton;
+} EditorObject;
+
+PyObject *new_editor_object(const svn_delta_editor_t *editor, void *baton, apr_pool_t *pool, PyTypeObject *type, void (*done_cb) (void *), void *done_baton)
+{
+ EditorObject *obj = PyObject_New(EditorObject, type);
+ if (obj == NULL)
+ return NULL;
+ obj->editor = editor;
+ obj->baton = baton;
+ obj->pool = pool;
+ obj->done_cb = done_cb;
+ obj->done_baton = done_baton;
+ return (PyObject *)obj;
+}
+
+static void py_editor_dealloc(PyObject *self)
+{
+ EditorObject *editor = (EditorObject *)self;
+ apr_pool_destroy(editor->pool);
+ PyObject_Del(self);
+}
+
+
+PyTypeObject TxDeltaWindowHandler_Type = {
+ PyObject_HEAD_INIT(&PyType_Type) 0,
+ .tp_basicsize = sizeof(TxDeltaWindowHandlerObject),
+ .tp_name = "ra.TxDeltaWindowHandler",
+ .tp_call = NULL, /* FIXME */
+ .tp_dealloc = (destructor)PyObject_Del
+};
+
+static PyObject *py_file_editor_apply_textdelta(PyObject *self, PyObject *args)
+{
+ EditorObject *editor = (EditorObject *)self;
+ char *c_base_checksum = NULL;
+ svn_txdelta_window_handler_t txdelta_handler;
+ void *txdelta_baton;
+ TxDeltaWindowHandlerObject *py_txdelta;
+
+ if (!FileEditor_Check(self)) {
+ PyErr_BadArgument();
+ return NULL;
+ }
+
+ if (!PyArg_ParseTuple(args, "|z", &c_base_checksum))
+ return NULL;
+ if (!check_error(editor->editor->apply_textdelta(editor->baton,
+ c_base_checksum, editor->pool,
+ &txdelta_handler, &txdelta_baton)))
+ return NULL;
+ py_txdelta = PyObject_New(TxDeltaWindowHandlerObject, &TxDeltaWindowHandler_Type);
+ py_txdelta->txdelta_handler = txdelta_handler;
+ py_txdelta->txdelta_baton = txdelta_baton;
+ return (PyObject *)py_txdelta;
+}
+
+static PyObject *py_file_editor_change_prop(PyObject *self, PyObject *args)
+{
+ EditorObject *editor = (EditorObject *)self;
+ char *name;
+ svn_string_t c_value;
+
+ if (!FileEditor_Check(self)) {
+ PyErr_BadArgument();
+ return NULL;
+ }
+
+ if (!PyArg_ParseTuple(args, "sz#", &name, &c_value.data, &c_value.len))
+ return NULL;
+ if (!check_error(editor->editor->change_file_prop(editor->baton, name,
+ &c_value, editor->pool)))
+ return NULL;
+ Py_RETURN_NONE;
+}
+
+static PyObject *py_file_editor_close(PyObject *self, PyObject *args)
+{
+ EditorObject *editor = (EditorObject *)self;
+ char *c_checksum = NULL;
+
+ if (!FileEditor_Check(self)) {
+ PyErr_BadArgument();
+ return NULL;
+ }
+
+ if (!PyArg_ParseTuple(args, "|z", &c_checksum))
+ return NULL;
+ if (!check_error(editor->editor->close_file(editor->baton, c_checksum,
+ editor->pool)))
+ return NULL;
+ Py_RETURN_NONE;
+}
+
+static PyMethodDef py_file_editor_methods[] = {
+ { "change_prop", py_file_editor_change_prop, METH_VARARGS, NULL },
+ { "close", py_file_editor_close, METH_VARARGS, NULL },
+ { "apply_textdelta", py_file_editor_apply_textdelta, METH_VARARGS, NULL },
+ { NULL }
+};
+
+PyTypeObject FileEditor_Type = {
+ PyObject_HEAD_INIT(&PyType_Type) 0,
+ .tp_name = "ra.FileEditor",
+ .tp_basicsize = sizeof(EditorObject),
+ .tp_methods = py_file_editor_methods,
+ .tp_dealloc = (destructor)PyObject_Del,
+};
+
+static PyObject *py_dir_editor_delete_entry(PyObject *self, PyObject *args)
+{
+ EditorObject *editor = (EditorObject *)self;
+ char *path;
+ svn_revnum_t revision = -1;
+
+ if (!DirectoryEditor_Check(self)) {
+ PyErr_BadArgument();
+ return NULL;
+ }
+
+ if (!PyArg_ParseTuple(args, "s|l", &path, &revision))
+ return NULL;
+
+ if (!check_error(editor->editor->delete_entry(path, revision, editor->baton,
+ editor->pool)))
+ return NULL;
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *py_dir_editor_add_directory(PyObject *self, PyObject *args)
+{
+ char *path;
+ char *copyfrom_path=NULL;
+ int copyfrom_rev=-1;
+ void *child_baton;
+ EditorObject *editor = (EditorObject *)self;
+
+ if (!DirectoryEditor_Check(self)) {
+ PyErr_BadArgument();
+ return NULL;
+ }
+
+ if (!PyArg_ParseTuple(args, "s|zl", &path, ©from_path, ©from_rev))
+ return NULL;
+
+ if (!check_error(editor->editor->add_directory(path, editor->baton,
+ copyfrom_path, copyfrom_rev, editor->pool, &child_baton)))
+ return NULL;
+
+ return new_editor_object(editor->editor, child_baton, editor->pool,
+ &DirectoryEditor_Type, NULL, NULL);
+}
+
+static PyObject *py_dir_editor_open_directory(PyObject *self, PyObject *args)
+{
+ char *path;
+ EditorObject *editor = (EditorObject *)self;
+ int base_revision=-1;
+ void *child_baton;
+
+ if (!DirectoryEditor_Check(self)) {
+ PyErr_BadArgument();
+ return NULL;
+ }
+
+ if (!PyArg_ParseTuple(args, "s|l", &path, &base_revision))
+ return NULL;
+
+ if (!check_error(editor->editor->open_directory(path, editor->baton,
+ base_revision, editor->pool, &child_baton)))
+ return NULL;
+
+ return new_editor_object(editor->editor, child_baton, editor->pool,
+ &DirectoryEditor_Type, NULL, NULL);
+}
+
+static PyObject *py_dir_editor_change_prop(PyObject *self, PyObject *args)
+{
+ char *name;
+ svn_string_t c_value, *p_c_value;
+ EditorObject *editor = (EditorObject *)self;
+
+ if (!DirectoryEditor_Check(self)) {
+ PyErr_BadArgument();
+ return NULL;
+ }
+
+ if (!PyArg_ParseTuple(args, "sz#", &name, &c_value.data, &c_value.len))
+ return NULL;
+
+ p_c_value = &c_value;
+
+ if (!check_error(editor->editor->change_dir_prop(editor->baton, name,
+ p_c_value, editor->pool)))
+ return NULL;
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *py_dir_editor_close(PyObject *self)
+{
+ EditorObject *editor = (EditorObject *)self;
+
+ if (!DirectoryEditor_Check(self)) {
+ PyErr_BadArgument();
+ return NULL;
+ }
+
+ if (!check_error(editor->editor->close_directory(editor->baton,
+ editor->pool)))
+ return NULL;
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *py_dir_editor_absent_directory(PyObject *self, PyObject *args)
+{
+ char *path;
+ EditorObject *editor = (EditorObject *)self;
+
+ if (!Editor_Check(self)) {
+ PyErr_BadArgument();
+ return NULL;
+ }
+
+
+ if (!PyArg_ParseTuple(args, "s", &path))
+ return NULL;
+
+ if (!check_error(editor->editor->absent_directory(path, editor->baton,
+ editor->pool)))
+ return NULL;
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *py_dir_editor_add_file(PyObject *self, PyObject *args)
+{
+ char *path, *copy_path=NULL;
+ int copy_rev=-1;
+ void *file_baton;
+ EditorObject *editor = (EditorObject *)self;
+
+ if (!DirectoryEditor_Check(self)) {
+ PyErr_BadArgument();
+ return NULL;
+ }
+
+ if (!PyArg_ParseTuple(args, "s|zl", &path, ©_path, ©_rev))
+ return NULL;
+
+ if (!check_error(editor->editor->add_file(path, editor->baton, copy_path,
+ copy_rev, editor->pool, &file_baton)))
+ return NULL;
+
+ return new_editor_object(editor->editor, file_baton, editor->pool,
+ &FileEditor_Type, NULL, NULL);
+}
+
+static PyObject *py_dir_editor_open_file(PyObject *self, PyObject *args)
+{
+ char *path;
+ int base_revision=-1;
+ void *file_baton;
+ EditorObject *editor = (EditorObject *)self;
+
+ if (!DirectoryEditor_Check(self)) {
+ PyErr_BadArgument();
+ return NULL;
+ }
+
+ if (!PyArg_ParseTuple(args, "s|l", &path, &base_revision))
+ return NULL;
+
+ if (!check_error(editor->editor->open_file(path, editor->baton,
+ base_revision, editor->pool, &file_baton)))
+ return NULL;
+
+ return new_editor_object(editor->editor, file_baton, editor->pool,
+ &FileEditor_Type, NULL, NULL);
+}
+
+static PyObject *py_dir_editor_absent_file(PyObject *self, PyObject *args)
+{
+ char *path;
+ EditorObject *editor = (EditorObject *)self;
+
+ if (!DirectoryEditor_Check(self)) {
+ PyErr_BadArgument();
+ return NULL;
+ }
+
+ if (!PyArg_ParseTuple(args, "s", &path))
+ return NULL;
+
+ if (!check_error(editor->editor->absent_file(path, editor->baton, editor->pool)))
+ return NULL;
+
+ Py_RETURN_NONE;
+}
+
+static PyMethodDef py_dir_editor_methods[] = {
+ { "absent_file", py_dir_editor_absent_file, METH_VARARGS, NULL },
+ { "absent_directory", py_dir_editor_absent_directory, METH_VARARGS, NULL },
+ { "delete_entry", py_dir_editor_delete_entry, METH_VARARGS, NULL },
+ { "add_file", py_dir_editor_add_file, METH_VARARGS, NULL },
+ { "open_file", py_dir_editor_open_file, METH_VARARGS, NULL },
+ { "add_directory", py_dir_editor_add_directory, METH_VARARGS, NULL },
+ { "open_directory", py_dir_editor_open_directory, METH_VARARGS, NULL },
+ { "close", (PyCFunction)py_dir_editor_close, METH_NOARGS, NULL },
+ { "change_prop", py_dir_editor_change_prop, METH_VARARGS, NULL },
+
+ { NULL, }
+};
+
+PyTypeObject DirectoryEditor_Type = {
+ PyObject_HEAD_INIT(&PyType_Type) 0,
+ .tp_name = "ra.DirEditor",
+ .tp_basicsize = sizeof(EditorObject),
+ .tp_methods = py_dir_editor_methods,
+ .tp_dealloc = (destructor)PyObject_Del,
+};
+
+static PyObject *py_editor_set_target_revision(PyObject *self, PyObject *args)
+{
+ int target_revision;
+ EditorObject *editor = (EditorObject *)self;
+
+ if (!Editor_Check(self)) {
+ PyErr_BadArgument();
+ return NULL;
+ }
+
+ if (!PyArg_ParseTuple(args, "i", &target_revision))
+ return NULL;
+
+ if (!check_error(editor->editor->set_target_revision(editor->baton,
+ target_revision, editor->pool)))
+ return NULL;
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *py_editor_open_root(PyObject *self, PyObject *args)
+{
+ svn_revnum_t base_revision=-1;
+ void *root_baton;
+ EditorObject *editor = (EditorObject *)self;
+
+ if (!Editor_Check(self)) {
+ PyErr_BadArgument();
+ return NULL;
+ }
+
+ if (!PyArg_ParseTuple(args, "|l:open_root", &base_revision))
+ return NULL;
+
+ if (!check_error(editor->editor->open_root(editor->baton, base_revision,
+ editor->pool, &root_baton)))
+ return NULL;
+
+ return new_editor_object(editor->editor, root_baton, editor->pool,
+ &DirectoryEditor_Type, NULL, NULL);
+}
+
+static PyObject *py_editor_close(PyObject *self)
+{
+ EditorObject *editor = (EditorObject *)self;
+
+ if (!Editor_Check(self)) {
+ PyErr_BadArgument();
+ return NULL;
+ }
+
+ if (editor->done_cb != NULL)
+ editor->done_cb(editor->done_baton);
+
+ if (!check_error(editor->editor->close_edit(editor->baton, editor->pool)))
+ return NULL;
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *py_editor_abort(PyObject *self)
+{
+ EditorObject *editor = (EditorObject *)self;
+
+ if (!Editor_Check(self)) {
+ PyErr_BadArgument();
+ return NULL;
+ }
+
+ if (editor->done_cb != NULL)
+ editor->done_cb(editor->done_baton);
+
+ if (!check_error(editor->editor->abort_edit(editor->baton, editor->pool)))
+ return NULL;
+
+ Py_RETURN_NONE;
+}
+
+static PyMethodDef py_editor_methods[] = {
+ { "abort", (PyCFunction)py_editor_abort, METH_NOARGS, NULL },
+ { "close", (PyCFunction)py_editor_close, METH_NOARGS, NULL },
+ { "open_root", py_editor_open_root, METH_VARARGS, NULL },
+ { "set_target_revision", py_editor_set_target_revision, METH_VARARGS, NULL },
+ { NULL, }
+};
+
+PyTypeObject Editor_Type = {
+ PyObject_HEAD_INIT(&PyType_Type) 0,
+ .tp_name = "ra.Editor",
+ .tp_basicsize = sizeof(EditorObject),
+ .tp_methods = py_editor_methods,
+ .tp_dealloc = py_editor_dealloc,
+};
+
+
--- /dev/null
+/*
+ * Copyright © 2008 Jelmer Vernooij <jelmer@samba.org>
+ * -*- coding: utf-8 -*-
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _BZR_SVN_EDITOR_H_
+#define _BZR_SVN_EDITOR_H_
+
+#pragma GCC visibility push(hidden)
+
+PyAPI_DATA(PyTypeObject) DirectoryEditor_Type;
+PyAPI_DATA(PyTypeObject) FileEditor_Type;
+PyAPI_DATA(PyTypeObject) Editor_Type;
+PyAPI_DATA(PyTypeObject) TxDeltaWindowHandler_Type;
+PyObject *new_editor_object(const svn_delta_editor_t *editor, void *baton, apr_pool_t *pool, PyTypeObject *type, void (*done_cb) (void *baton), void *done_baton);
+
+#define DirectoryEditor_Check(op) PyObject_TypeCheck(op, &DirectoryEditor_Type)
+#define FileEditor_Check(op) PyObject_TypeCheck(op, &FileEditor_Type)
+#define Editor_Check(op) PyObject_TypeCheck(op, &Editor_Type)
+#define TxDeltaWindowHandler_Check(op) PyObject_TypeCheck(op, &TxDeltaWindowHandler_Type)
+
+typedef struct {
+ PyObject_HEAD
+ svn_txdelta_window_handler_t txdelta_handler;
+ void *txdelta_baton;
+} TxDeltaWindowHandlerObject;
+
+#pragma GCC visibility pop
+
+#endif /* _BZR_SVN_EDITOR_H_ */
TransportError, UnexpectedEndOfContainerError)
import urllib
-import svn.core
+from bzrlib.plugins.svn import core
class InvalidExternalsDescription(BzrError):
ERR_WC_UNSUPPORTED_FORMAT = 155021
-
class NotSvnBranchPath(NotBranchError):
"""Error raised when a path was specified that did not exist."""
_fmt = """%(path)s is not a valid Subversion branch path.
def convert(*args, **kwargs):
try:
return unbound(*args, **kwargs)
- except svn.core.SubversionException, e:
+ except core.SubversionException, e:
raise convert_error(e)
convert.__doc__ = unbound.__doc__
return convert
-class NoCheckoutSupport(BzrError):
-
- _fmt = 'Subversion version too old for working tree support.'
-
-
-class LocalCommitsUnsupported(BzrError):
-
- _fmt = 'Local commits are not supported for lightweight Subversion checkouts.'
-
-
class InvalidPropertyValue(BzrError):
_fmt = 'Invalid property value for Subversion property %(property)s: %(msg)s'
from cStringIO import StringIO
import md5
-from svn.core import Pool
-import svn.core
-
+from bzrlib.plugins.svn.delta import apply_txdelta_handler
from bzrlib.plugins.svn import properties
from bzrlib.plugins.svn.errors import InvalidFileName
from bzrlib.plugins.svn.logwalker import lazy_dict
parse_revision_metadata)
from bzrlib.plugins.svn.repository import SvnRepository, SvnRepositoryFormat
from bzrlib.plugins.svn.svk import SVN_PROP_SVK_MERGE
-from bzrlib.plugins.svn.tree import (apply_txdelta_handler, parse_externals_description,
+from bzrlib.plugins.svn.tree import (parse_externals_description,
inventory_add_external)
raise InvalidFileName(path)
-class RevisionBuildEditor(svn.delta.Editor):
+class RevisionBuildEditor:
"""Implementation of the Subversion commit editor interface that builds a
Bazaar revision.
"""
self.source = source
self.transact = target.get_transaction()
+ def set_target_revision(self, target_revision):
+ pass
+
def start_revision(self, revid, prev_inventory, revmeta):
self.revid = revid
(self.branch_path, self.revnum, self.mapping) = self.source.lookup_revision_id(revid)
self.dir_baserev = {}
self._revinfo = None
self._premature_deletes = set()
- self.pool = Pool()
self.old_inventory = prev_inventory
self.inventory = prev_inventory.copy()
self._start_revision()
return (rev, signature)
- def open_root(self, base_revnum, baton):
+ def open_root(self, base_revnum):
if self.old_inventory.root is None:
# First time the root is set
old_file_id = None
file_id = self.mapping.generate_file_id(self.source.uuid, self.revnum, self.branch_path, u"")
- self.dir_baserev[file_id] = []
+ file_parents = []
else:
assert self.old_inventory.root.revision is not None
old_file_id = self.old_inventory.root.file_id
file_id = self._get_id_map().get("", old_file_id)
- self.dir_baserev[file_id] = [self.old_inventory.root.revision]
+ file_parents = [self.old_inventory.root.revision]
if self.inventory.root is not None and \
file_id == self.inventory.root.file_id:
else:
ie = self.inventory.add_path("", 'directory', file_id)
ie.revision = self.revid
- return (old_file_id, file_id)
+ return DirectoryBuildEditor(self, old_file_id, file_id, file_parents)
+
+ def close(self):
+ pass
+
+ def _store_directory(self, file_id, parents):
+ raise NotImplementedError(self._store_directory)
+
+ def _get_file_data(self, file_id, revid):
+ raise NotImplementedError(self._get_file_data)
+
+ def _finish_commit(self):
+ raise NotImplementedError(self._finish_commit)
+
+ def abort(self):
+ pass
+
+ def _start_revision(self):
+ pass
+
+ def _store_file(self, file_id, lines, parents):
+ raise NotImplementedError(self._store_file)
def _get_existing_id(self, old_parent_id, new_parent_id, path):
assert isinstance(path, unicode)
return
self.inventory.rename(file_id, parent_id, urlutils.basename(path))
- def delete_entry(self, path, revnum, (old_parent_id, new_parent_id), pool):
- assert isinstance(path, str)
- path = path.decode("utf-8")
- if path in self._premature_deletes:
- # Delete recursively
- self._premature_deletes.remove(path)
- for p in self._premature_deletes.copy():
- if p.startswith("%s/" % path):
- self._premature_deletes.remove(p)
- else:
- self.inventory.remove_recursive_id(self._get_old_id(old_parent_id, path))
+class DirectoryBuildEditor:
+ def __init__(self, editor, old_id, new_id, parent_revids=[]):
+ self.editor = editor
+ self.old_id = old_id
+ self.new_id = new_id
+ self.parent_revids = parent_revids
- def close_directory(self, (old_id, new_id)):
- self.inventory[new_id].revision = self.revid
+ def close(self):
+ self.editor.inventory[self.new_id].revision = self.editor.revid
+ self.editor._store_directory(self.new_id, self.parent_revids)
- # Only record root if the target repository supports it
- self._store_directory(new_id, self.dir_baserev[new_id])
+ if self.new_id == self.editor.inventory.root.file_id:
+ assert len(self.editor._premature_deletes) == 0
+ self.editor._finish_commit()
- def add_directory(self, path, (old_parent_id, new_parent_id), copyfrom_path, copyfrom_revnum,
- pool):
+ def add_directory(self, path, copyfrom_path=None, copyfrom_revnum=-1):
assert isinstance(path, str)
path = path.decode("utf-8")
check_filename(path)
- file_id = self._get_new_id(new_parent_id, path)
+ file_id = self.editor._get_new_id(self.new_id, path)
- self.dir_baserev[file_id] = []
- if file_id in self.inventory:
+ if file_id in self.editor.inventory:
# This directory was moved here from somewhere else, but the
# other location hasn't been removed yet.
if copyfrom_path is None:
# This should ideally never happen!
- copyfrom_path = self.old_inventory.id2path(file_id)
+ copyfrom_path = self.editor.old_inventory.id2path(file_id)
mutter('no copyfrom path set, assuming %r', copyfrom_path)
- assert copyfrom_path == self.old_inventory.id2path(file_id)
- assert copyfrom_path not in self._premature_deletes
- self._premature_deletes.add(copyfrom_path)
- self._rename(file_id, new_parent_id, path)
- ie = self.inventory[file_id]
+ assert copyfrom_path == self.editor.old_inventory.id2path(file_id)
+ assert copyfrom_path not in self.editor._premature_deletes
+ self.editor._premature_deletes.add(copyfrom_path)
+ self.editor._rename(file_id, self.new_id, path)
+ ie = self.editor.inventory[file_id]
old_file_id = file_id
else:
old_file_id = None
- ie = self.inventory.add_path(path, 'directory', file_id)
- ie.revision = self.revid
+ ie = self.editor.inventory.add_path(path, 'directory', file_id)
+ ie.revision = self.editor.revid
- return (old_file_id, file_id)
+ return DirectoryBuildEditor(self.editor, old_file_id, file_id)
- def open_directory(self, path, (old_parent_id, new_parent_id), base_revnum, pool):
+ def open_directory(self, path, base_revnum):
assert isinstance(path, str)
path = path.decode("utf-8")
- assert base_revnum >= 0
- base_file_id = self._get_old_id(old_parent_id, path)
- base_revid = self.old_inventory[base_file_id].revision
- file_id = self._get_existing_id(old_parent_id, new_parent_id, path)
+ assert isinstance(base_revnum, int)
+ base_file_id = self.editor._get_old_id(self.old_id, path)
+ base_revid = self.editor.old_inventory[base_file_id].revision
+ file_id = self.editor._get_existing_id(self.old_id, self.new_id, path)
if file_id == base_file_id:
- self.dir_baserev[file_id] = [base_revid]
- ie = self.inventory[file_id]
+ file_parents = [base_revid]
+ ie = self.editor.inventory[file_id]
else:
# Replace if original was inside this branch
# change id of base_file_id to file_id
- ie = self.inventory[base_file_id]
+ ie = self.editor.inventory[base_file_id]
for name in ie.children:
ie.children[name].parent_id = file_id
# FIXME: Don't touch inventory internals
- del self.inventory._byid[base_file_id]
- self.inventory._byid[file_id] = ie
+ del self.editor.inventory._byid[base_file_id]
+ self.editor.inventory._byid[file_id] = ie
ie.file_id = file_id
- self.dir_baserev[file_id] = []
- ie.revision = self.revid
- return (base_file_id, file_id)
+ file_parents = []
+ ie.revision = self.editor.revid
+ return DirectoryBuildEditor(self.editor, base_file_id, file_id,
+ file_parents)
- def change_dir_prop(self, (old_id, new_id), name, value, pool):
- if new_id == self.inventory.root.file_id:
+ def change_prop(self, name, value):
+ if self.new_id == self.editor.inventory.root.file_id:
# Replay lazy_dict, since it may be more expensive
- if type(self.revmeta.fileprops) != dict:
- self.revmeta.fileprops = {}
- self.revmeta.fileprops[name] = value
+ if type(self.editor.revmeta.fileprops) != dict:
+ self.editor.revmeta.fileprops = {}
+ self.editor.revmeta.fileprops[name] = value
if name in (properties.PROP_ENTRY_COMMITTED_DATE,
properties.PROP_ENTRY_COMMITTED_REV,
elif name.startswith(properties.PROP_PREFIX):
mutter('unsupported dir property %r', name)
- def change_file_prop(self, id, name, value, pool):
+ def add_file(self, path, copyfrom_path=None, copyfrom_revnum=-1):
+ assert isinstance(path, str)
+ path = path.decode("utf-8")
+ check_filename(path)
+ file_id = self.editor._get_new_id(self.new_id, path)
+ if file_id in self.editor.inventory:
+ # This file was moved here from somewhere else, but the
+ # other location hasn't been removed yet.
+ if copyfrom_path is None:
+ # This should ideally never happen
+ copyfrom_path = self.editor.old_inventory.id2path(file_id)
+ mutter('no copyfrom path set, assuming %r' % copyfrom_path)
+ assert copyfrom_path == self.editor.old_inventory.id2path(file_id)
+ assert copyfrom_path not in self.editor._premature_deletes
+ self.editor._premature_deletes.add(copyfrom_path)
+ # No need to rename if it's already in the right spot
+ self.editor._rename(file_id, self.new_id, path)
+ return FileBuildEditor(self.editor, path, file_id)
+
+ def open_file(self, path, base_revnum):
+ assert isinstance(path, str)
+ path = path.decode("utf-8")
+ base_file_id = self.editor._get_old_id(self.old_id, path)
+ base_revid = self.editor.old_inventory[base_file_id].revision
+ file_id = self.editor._get_existing_id(self.old_id, self.new_id, path)
+ is_symlink = (self.editor.inventory[base_file_id].kind == 'symlink')
+ file_data = self.editor._get_file_data(base_file_id, base_revid)
+ if file_id == base_file_id:
+ file_parents = [base_revid]
+ else:
+ # Replace
+ del self.editor.inventory[base_file_id]
+ file_parents = []
+ return FileBuildEditor(self.editor, path, file_id,
+ file_parents, file_data, is_symlink=is_symlink)
+
+ def delete_entry(self, path, revnum):
+ assert isinstance(path, str)
+ path = path.decode("utf-8")
+ if path in self.editor._premature_deletes:
+ # Delete recursively
+ self.editor._premature_deletes.remove(path)
+ for p in self.editor._premature_deletes.copy():
+ if p.startswith("%s/" % path):
+ self.editor._premature_deletes.remove(p)
+ else:
+ self.editor.inventory.remove_recursive_id(self.editor._get_old_id(self.old_id, path))
+
+class FileBuildEditor:
+ def __init__(self, editor, path, file_id, file_parents=[], data="",
+ is_symlink=False):
+ self.path = path
+ self.editor = editor
+ self.file_id = file_id
+ self.file_data = data
+ self.is_symlink = is_symlink
+ self.file_parents = file_parents
+ self.is_executable = None
+ self.file_stream = None
+
+ def apply_textdelta(self, base_checksum=None):
+ actual_checksum = md5.new(self.file_data).hexdigest()
+ assert (base_checksum is None or base_checksum == actual_checksum,
+ "base checksum mismatch: %r != %r" % (base_checksum,
+ actual_checksum))
+ self.file_stream = StringIO()
+ return apply_txdelta_handler(self.file_data, self.file_stream)
+
+ def change_prop(self, name, value):
if name == properties.PROP_EXECUTABLE:
# You'd expect executable to match
- # properties.PROP_EXECUTABLE_VALUE, but that's not
+ # constants.PROP_EXECUTABLE_VALUE, but that's not
# how SVN behaves. It appears to consider the presence
# of the property sufficient to mark it executable.
self.is_executable = (value != None)
self.is_symlink = (value != None)
elif name == properties.PROP_ENTRY_COMMITTED_REV:
self.last_file_rev = int(value)
- elif name == properties.PROP_EXTERNALS:
- mutter('svn:externals property on file!')
elif name in (properties.PROP_ENTRY_COMMITTED_DATE,
properties.PROP_ENTRY_LAST_AUTHOR,
properties.PROP_ENTRY_LOCK_TOKEN,
pass
elif name.startswith(properties.PROP_WC_PREFIX):
pass
+ elif name == properties.PROP_EXTERNALS:
+ mutter('svn:externals property on file!')
elif (name.startswith(properties.PROP_PREFIX) or
name.startswith(SVN_PROP_BZR_PREFIX)):
mutter('unsupported file property %r', name)
- def add_file(self, path, (old_parent_id, new_parent_id), copyfrom_path, copyfrom_revnum, baton):
- assert isinstance(path, str)
- path = path.decode("utf-8")
- check_filename(path)
- self.is_symlink = False
- self.is_executable = None
- self.file_data = ""
- self.file_parents = []
- self.file_stream = None
- self.file_id = self._get_new_id(new_parent_id, path)
- if self.file_id in self.inventory:
- # This file was moved here from somewhere else, but the
- # other location hasn't been removed yet.
- if copyfrom_path is None:
- # This should ideally never happen
- copyfrom_path = self.old_inventory.id2path(self.file_id)
- mutter('no copyfrom path set, assuming %r', copyfrom_path)
- assert copyfrom_path == self.old_inventory.id2path(self.file_id)
- assert copyfrom_path not in self._premature_deletes
- self._premature_deletes.add(copyfrom_path)
- # No need to rename if it's already in the right spot
- self._rename(self.file_id, new_parent_id, path)
- return path
-
- def open_file(self, path, (old_parent_id, new_parent_id), base_revnum, pool):
- assert isinstance(path, str)
- path = path.decode("utf-8")
- base_file_id = self._get_old_id(old_parent_id, path)
- base_revid = self.old_inventory[base_file_id].revision
- self.file_id = self._get_existing_id(old_parent_id, new_parent_id, path)
- self.is_executable = None
- self.is_symlink = (self.inventory[base_file_id].kind == 'symlink')
- self.file_data = self._get_file_data(base_file_id, base_revid)
- self.file_stream = None
- if self.file_id == base_file_id:
- self.file_parents = [base_revid]
- else:
- # Replace
- del self.inventory[base_file_id]
- self.file_parents = []
- return path
-
- def close_file(self, path, checksum):
- assert isinstance(path, unicode)
+ def close(self, checksum=None):
+ assert isinstance(self.path, unicode)
if self.file_stream is not None:
self.file_stream.seek(0)
lines = osutils.split_lines(self.file_stream.read())
actual_checksum = md5_strings(lines)
assert checksum is None or checksum == actual_checksum
- self._store_file(self.file_id, lines, self.file_parents)
+ self.editor._store_file(self.file_id, lines, self.file_parents)
assert self.is_symlink in (True, False)
- if self.file_id in self.inventory:
- del self.inventory[self.file_id]
+ if self.file_id in self.editor.inventory:
+ del self.editor.inventory[self.file_id]
if self.is_symlink:
- ie = self.inventory.add_path(path, 'symlink', self.file_id)
+ ie = self.editor.inventory.add_path(self.path, 'symlink', self.file_id)
ie.symlink_target = lines[0][len("link "):]
ie.text_sha1 = None
ie.text_size = None
ie.executable = False
- ie.revision = self.revid
+ ie.revision = self.editor.revid
else:
- ie = self.inventory.add_path(path, 'file', self.file_id)
- ie.revision = self.revid
+ ie = self.editor.inventory.add_path(self.path, 'file', self.file_id)
+ ie.revision = self.editor.revid
ie.kind = 'file'
ie.symlink_target = None
ie.text_sha1 = osutils.sha_strings(lines)
if self.is_executable is not None:
ie.executable = self.is_executable
-
self.file_stream = None
- def close_edit(self):
- assert len(self._premature_deletes) == 0
- self._finish_commit()
- self.pool.destroy()
-
- def apply_textdelta(self, file_id, base_checksum):
- actual_checksum = md5.new(self.file_data).hexdigest(),
- assert (base_checksum is None or base_checksum == actual_checksum,
- "base checksum mismatch: %r != %r" % (base_checksum,
- actual_checksum))
- self.file_stream = StringIO()
- return apply_txdelta_handler(StringIO(self.file_data),
- self.file_stream, self.pool)
-
- def _store_file(self, file_id, lines, parents):
- raise NotImplementedError(self._store_file)
-
- def _store_directory(self, file_id, parents):
- raise NotImplementedError(self._store_directory)
-
- def _get_file_data(self, file_id, revid):
- raise NotImplementedError(self._get_file_data)
-
- def _finish_commit(self):
- raise NotImplementedError(self._finish_commit)
-
- def abort_edit(self):
- pass
-
- def _start_revision(self):
- pass
-
class WeaveRevisionBuildEditor(RevisionBuildEditor):
"""Subversion commit editor that can write to a weave-based repository.
self.target.commit_write_group()
self._write_group_active = False
- def abort_edit(self):
+ def abort(self):
if self._write_group_active:
self.target.abort_write_group()
self._write_group_active = False
"""See InterRepository.copy_content."""
self.fetch(revision_id, pb, find_ghosts=False)
- def _fetch_replay(self, revids, pb=None):
- """Copy a set of related revisions using svn.ra.replay.
-
- :param revids: Revision ids to copy.
- :param pb: Optional progress bar
- """
- raise NotImplementedError(self._copy_revisions_replay)
-
def _fetch_switch(self, repos_root, revids, pb=None):
"""Copy a set of related revisions using svn.ra.switch.
editor.branch_path)
conn = self.source.transport.connections.get(branch_url)
- reporter = conn.do_update(editor.revnum, True,
+ reporter = conn.do_update(editor.revnum, "", True,
editor)
try:
# Report status of existing paths
reporter.set_path("", editor.revnum, True, None)
except:
- reporter.abort_report()
+ reporter.abort()
raise
else:
(parent_branch, parent_revnum, mapping) = \
conn = self.source.transport.connections.get(urlutils.join(repos_root, parent_branch))
if parent_branch != editor.branch_path:
- reporter = conn.do_switch(editor.revnum, True,
+ reporter = conn.do_switch(editor.revnum, "", True,
urlutils.join(repos_root, editor.branch_path),
editor)
else:
- reporter = conn.do_update(editor.revnum, True, editor)
+ reporter = conn.do_update(editor.revnum, "", True, editor)
try:
# Report status of existing paths
reporter.set_path("", parent_revnum, False, None)
except:
- reporter.abort_report()
+ reporter.abort()
raise
- reporter.finish_report()
+ reporter.finish()
finally:
if conn is not None:
self.source.transport.add_connection(conn)
except:
- editor.abort_edit()
+ editor.abort()
raise
prev_inv = editor.inventory
"""Fetch revisions. """
if revision_id == NULL_REVISION:
return
+
+ self._supports_replay = True # assume replay supported by default
# Dictionary with paths as keys, revnums as values
if pb:
import os
lazy_import(globals(), """
-from bzrlib.plugins.svn import errors
-from bzrlib.plugins.svn import remote
+from bzrlib.plugins.svn import errors, remote
from bzrlib import errors as bzr_errors
""")
@classmethod
def probe_transport(klass, transport):
from transport import get_svn_ra_transport
- import svn.core
+ from bzrlib.plugins.svn.core import SubversionException
format = klass()
try:
transport = get_svn_ra_transport(transport)
- except svn.core.SubversionException, (_, num):
+ except SubversionException, (_, num):
if num in (errors.ERR_RA_ILLEGAL_URL, \
errors.ERR_RA_LOCAL_REPOS_OPEN_FAILED, \
errors.ERR_BAD_URL):
return format
def _open(self, transport):
- import svn.core
+ from bzrlib.plugins.svn.core import SubversionException
try:
return remote.SvnRemoteAccess(transport, self)
- except svn.core.SubversionException, (_, num):
+ except SubversionException, (_, num):
if num == errors.ERR_RA_DAV_REQUEST_FAILED:
raise bzr_errors.NotBranchError(transport.base)
raise
"""See BzrDir.initialize_on_transport()."""
from transport import get_svn_ra_transport
from bzrlib.transport.local import LocalTransport
- import svn.repos
+ import repos
if not isinstance(transport, LocalTransport):
raise NotImplementedError(self.initialize,
"non-local transports")
local_path = transport._local_base.rstrip("/")
- svn.repos.create(local_path, '', '', None, None)
+ repos.create(local_path)
# All revision property changes
revprop_hook = os.path.join(local_path, "hooks", "pre-revprop-change")
open(revprop_hook, 'w').write("#!/bin/sh")
@classmethod
def probe_transport(klass, transport):
- import svn
from bzrlib.transport.local import LocalTransport
+ import wc
format = klass()
if isinstance(transport, LocalTransport) and \
- transport.has(svn.wc.get_adm_dir()):
+ transport.has(wc.get_adm_dir()):
return format
raise bzr_errors.NotBranchError(path=transport.base)
def _open(self, transport):
- import svn.core
+ from bzrlib.plugins.svn.core import SubversionException
from workingtree import SvnCheckout
- subr_version = svn.core.svn_subr_version()
- if subr_version.major == 1 and subr_version.minor < 4:
- raise errors.NoCheckoutSupport()
try:
return SvnCheckout(transport, self)
- except svn.core.SubversionException, (_, num):
+ except SubversionException, (_, num):
if num in (errors.ERR_RA_LOCAL_REPOS_OPEN_FAILED,):
raise errors.NoSvnRepositoryPresent(transport.base)
raise
from bzrlib.trace import mutter
import bzrlib.ui as ui
-from svn.core import SubversionException, Pool
-from transport import SvnRaTransport
-import svn.core
+from bzrlib.plugins.svn.core import SubversionException
+from bzrlib.plugins.svn.transport import SvnRaTransport
+from bzrlib.plugins.svn import core
from bzrlib.plugins.svn.cache import CacheTable
-from bzrlib.plugins.svn import changes
from bzrlib.plugins.svn.errors import ERR_FS_NO_SUCH_REVISION, ERR_FS_NOT_FOUND
-
-LOG_CHUNK_LIMIT = 0
+from bzrlib.plugins.svn import changes
+from bzrlib.plugins.svn.ra import DIRENT_KIND
class lazy_dict(object):
def __init__(self, initial, create_fn, *args):
revprops = lazy_dict({}, self._transport.revprop_list, revnum)
if changes.changes_path(revpaths, path, True):
+ assert isinstance(revnum, int)
yield (revpaths, revnum, revprops)
i += 1
if limit != 0 and i == limit:
if next is None:
break
+ assert (ascending and next[1] > revnum) or \
+ (not ascending and next[1] < revnum)
(path, revnum) = next
+ assert isinstance(path, str)
+ assert isinstance(revnum, int)
def get_previous(self, path, revnum):
"""Return path,revnum pair specified pair was derived from.
:param to_revnum: End of range to fetch information for
"""
+ assert isinstance(self.saved_revnum, int)
if to_revnum <= self.saved_revnum:
return
latest_revnum = self.actual._transport.get_latest_revnum()
+ assert isinstance(latest_revnum, int)
to_revnum = max(latest_revnum, to_revnum)
pb = ui.ui_factory.nested_progress_bar()
try:
+ def update_db(orig_paths, revision, revprops):
+ assert isinstance(orig_paths, dict) or orig_paths is None
+ assert isinstance(revision, int)
+ assert isinstance(revprops, dict)
+ pb.update('fetching svn revision info', revision, to_revnum)
+ if orig_paths is None:
+ orig_paths = {}
+ for p in orig_paths:
+ (action, copyfrom_path, copyfrom_rev) = orig_paths[p]
+
+ if copyfrom_path:
+ copyfrom_path = copyfrom_path.strip("/")
+ self.cachedb.execute(
+ "replace into changed_path (rev, path, action, copyfrom_path, copyfrom_rev) values (?, ?, ?, ?, ?)",
+ (revision, p.strip("/"), action, copyfrom_path, copyfrom_rev))
+
+ self.saved_revnum = revision
+ if self.saved_revnum % 1000 == 0:
+ self.cachedb.commit()
+
try:
- while self.saved_revnum < to_revnum:
- for (orig_paths, revision, revprops) in self.actual._transport.iter_log(None, self.saved_revnum,
- to_revnum, self.actual._limit, True,
- True, []):
- pb.update('fetching svn revision info', revision, to_revnum)
- if orig_paths is None:
- orig_paths = {}
- for p in orig_paths:
- copyfrom_path = orig_paths[p].copyfrom_path
- if copyfrom_path is not None:
- copyfrom_path = copyfrom_path.strip("/")
-
- self.cachedb.execute(
- "replace into changed_path (rev, path, action, copyfrom_path, copyfrom_rev) values (?, ?, ?, ?, ?)",
- (revision, p.strip("/"), orig_paths[p].action, copyfrom_path, orig_paths[p].copyfrom_rev))
- # Work around nasty memory leak in Subversion
- orig_paths[p]._parent_pool.destroy()
-
- self.saved_revnum = revision
- if self.saved_revnum % 1000 == 0:
- self.cachedb.commit()
+ assert isinstance(self.saved_revnum, int)
+ assert isinstance(to_revnum, int)
+ self.actual._transport.get_log(update_db, None, self.saved_revnum, to_revnum, 0, True, True, [])
finally:
pb.finished()
except SubversionException, (_, num):
def struct_revpaths_to_tuples(changed_paths):
assert isinstance(changed_paths, dict)
revpaths = {}
- for k,v in changed_paths.items():
- if v.copyfrom_path is None:
+ for k,(action, copyfrom_path, copyfrom_rev) in changed_paths.items():
+ if copyfrom_path is None:
copyfrom_path = None
else:
- copyfrom_path = v.copyfrom_path.strip("/")
- revpaths[k.strip("/")] = (v.action, copyfrom_path, v.copyfrom_rev)
+ copyfrom_path = copyfrom_path.strip("/")
+ revpaths[k.strip("/")] = (action, copyfrom_path, copyfrom_rev)
return revpaths
self._transport = transport
- if limit is not None:
- self._limit = limit
- else:
- self._limit = LOG_CHUNK_LIMIT
-
def find_latest_change(self, path, revnum):
"""Find latest revision that touched path.
assert isinstance(path, str), "invalid path"
path = path.strip("/")
conn = self._transport.connections.get(self._transport.get_svn_repos_root())
+ results = []
+ unchecked_dirs = set([path])
try:
- ft = conn.check_path(path, revnum)
- if ft == svn.core.svn_node_file:
- return []
- assert ft == svn.core.svn_node_dir
- finally:
- self._transport.connections.add(conn)
-
- class TreeLister(svn.delta.Editor):
- def __init__(self, base):
- self.files = []
- self.base = base
-
- def set_target_revision(self, revnum):
- """See Editor.set_target_revision()."""
- pass
-
- def open_root(self, revnum, baton):
- """See Editor.open_root()."""
- return path
-
- def add_directory(self, path, parent_baton, copyfrom_path, copyfrom_revnum, pool):
- """See Editor.add_directory()."""
- self.files.append(urlutils.join(self.base, path))
- return path
-
- def change_dir_prop(self, id, name, value, pool):
- pass
-
- def change_file_prop(self, id, name, value, pool):
- pass
-
- def add_file(self, path, parent_id, copyfrom_path, copyfrom_revnum, baton):
- self.files.append(urlutils.join(self.base, path))
- return path
-
- def close_dir(self, id):
- pass
-
- def close_file(self, path, checksum):
- pass
-
- def close_edit(self):
- pass
-
- def abort_edit(self):
- pass
-
- def apply_textdelta(self, file_id, base_checksum):
- pass
-
- pool = Pool()
- editor = TreeLister(path)
- try:
- conn = self._transport.connections.get(urlutils.join(self._transport.get_svn_repos_root(), path))
- reporter = conn.do_update(revnum, True, editor, pool)
- reporter.set_path("", revnum, True, None, pool)
- reporter.finish_report(pool)
+ while len(unchecked_dirs) > 0:
+ nextp = unchecked_dirs.pop()
+ dirents = conn.get_dir(nextp, revnum, DIRENT_KIND)[0]
+ for k,v in dirents.items():
+ childp = urlutils.join(nextp, k)
+ if v['kind'] == core.NODE_DIR:
+ unchecked_dirs.add(childp)
+ results.append(childp)
finally:
self._transport.connections.add(conn)
- return editor.files
+ return results
def get_previous(self, path, revnum):
"""Return path,revnum pair specified pair was derived from.
from bzrlib.errors import InvalidRevisionId
from bzrlib.trace import mutter
-from bzrlib.plugins.svn import version_info, errors, properties
+from bzrlib.plugins.svn import core, constants, version_info, errors, properties
import calendar
-import svn
+import sha
import time
import urllib
pass
if svn_revprops.has_key(properties.PROP_REVISION_DATE):
- rev.timestamp = 1.0 * svn.core.secs_from_timestr(svn_revprops[properties.PROP_REVISION_DATE], None)
+ rev.timestamp = core.time_from_cstring(svn_revprops[properties.PROP_REVISION_DATE]) / 1000000.0
else:
rev.timestamp = 0.0 # FIXME: Obtain repository creation time
rev.timezone = None
from bzrlib import osutils, ui
from bzrlib.errors import InvalidRevisionId
from bzrlib.trace import mutter
-from bzrlib.plugins.svn import mapping, properties
+
+from bzrlib.plugins.svn import core, mapping, properties
+from bzrlib.plugins.svn.core import SubversionException
from bzrlib.plugins.svn.errors import ERR_FS_NOT_DIRECTORY, ERR_FS_NOT_FOUND, ERR_RA_DAV_PATH_NOT_FOUND
from bzrlib.plugins.svn.layout import RepositoryLayout
from bzrlib.plugins.svn.mapping3.scheme import (BranchingScheme, guess_scheme_from_branch_path,
guess_scheme_from_history, ListBranchingScheme,
parse_list_scheme_text, NoBranchingScheme,
TrunkBranchingScheme, ListBranchingScheme)
-import sha, svn
-from svn.core import SubversionException
+import sha
SVN_PROP_BZR_BRANCHING_SCHEME = 'bzr:branching-scheme'
def get_branches(self, revnum, project=""):
def check_path(path):
- return self.repository.transport.check_path(path, revnum) == svn.core.svn_node_dir
+ return self.repository.transport.check_path(path, revnum) == core.NODE_DIR
def find_children(path):
try:
+ assert not path.startswith("/")
dirents = self.repository.transport.get_dir(path, revnum)[0]
except SubversionException, (msg, num):
if num in (ERR_FS_NOT_DIRECTORY, ERR_FS_NOT_FOUND, ERR_RA_DAV_PATH_NOT_FOUND):
def set_property_scheme(repository, scheme):
- def done(revmetadata, pool):
- pass
editor = repository.transport.get_commit_editor(
- {properties.PROP_REVISION_LOG: "Updating branching scheme for Bazaar."},
- done, None, False)
- root = editor.open_root(-1)
- editor.change_dir_prop(root, SVN_PROP_BZR_BRANCHING_SCHEME,
+ {properties.PROP_REVISION_LOG: "Updating branching scheme for Bazaar."})
+ root = editor.open_root()
+ root.change_prop(SVN_PROP_BZR_BRANCHING_SCHEME,
"".join(map(lambda x: x+"\n", scheme.branch_list)).encode("utf-8"))
- editor.close_directory(root)
+ root.close()
editor.close()
--- /dev/null
+/* Copyright © 2008 Jelmer Vernooij <jelmer@samba.org>
+ * -*- coding: utf-8 -*-
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <stdbool.h>
+#include <Python.h>
+#include <apr_general.h>
+#include <svn_types.h>
+#include <svn_ra.h>
+#include <apr_file_io.h>
+
+#include <structmember.h>
+
+#include "editor.h"
+#include "util.h"
+
+static PyObject *busy_exc;
+
+PyAPI_DATA(PyTypeObject) Reporter_Type;
+PyAPI_DATA(PyTypeObject) RemoteAccess_Type;
+PyAPI_DATA(PyTypeObject) Auth_Type;
+PyAPI_DATA(PyTypeObject) AuthProvider_Type;
+PyAPI_DATA(PyTypeObject) TxDeltaWindowHandler_Type;
+
+typedef struct {
+ PyObject_HEAD
+ svn_auth_baton_t *auth_baton;
+ apr_pool_t *pool;
+ PyObject *providers;
+} AuthObject;
+
+static svn_error_t *py_commit_callback(const svn_commit_info_t *commit_info, void *baton, apr_pool_t *pool)
+{
+ PyObject *fn = (PyObject *)baton, *ret;
+
+ if (fn == Py_None)
+ return NULL;
+
+ ret = PyObject_CallFunction(fn, "izz",
+ commit_info->revision, commit_info->date,
+ commit_info->author);
+ if (ret == NULL)
+ return py_svn_error();
+ Py_DECREF(ret);
+ return NULL;
+}
+
+static PyObject *pyify_lock(const svn_lock_t *lock)
+{
+ Py_RETURN_NONE; /* FIXME */
+}
+
+static svn_error_t *py_lock_func (void *baton, const char *path, int do_lock,
+ const svn_lock_t *lock, svn_error_t *ra_err,
+ apr_pool_t *pool)
+{
+ PyObject *py_ra_err = Py_None, *ret, *py_lock;
+ if (ra_err != NULL) {
+ py_ra_err = PyErr_NewSubversionException(ra_err);
+ }
+ py_lock = pyify_lock(lock);
+ ret = PyObject_CallFunction((PyObject *)baton, "zbOO", path, do_lock,
+ py_lock, py_ra_err);
+ Py_DECREF(py_lock);
+ if (ret == NULL)
+ return py_svn_error();
+ Py_DECREF(ret);
+ return NULL;
+}
+
+static void py_progress_func(apr_off_t progress, apr_off_t total, void *baton, apr_pool_t *pool)
+{
+ PyObject *fn = (PyObject *)baton, *ret;
+ if (fn == Py_None) {
+ return;
+ }
+ ret = PyObject_CallFunction(fn, "ll", progress, total);
+ /* TODO: What to do with exceptions raised here ? */
+ if (ret == NULL)
+ return;
+ Py_DECREF(ret);
+}
+
+
+typedef struct {
+ PyObject_HEAD
+ const svn_ra_reporter2_t *reporter;
+ void *report_baton;
+ apr_pool_t *pool;
+ void (*done_cb)(void *baton);
+ void *done_baton;
+} ReporterObject;
+
+static PyObject *reporter_set_path(PyObject *self, PyObject *args)
+{
+ char *path;
+ svn_revnum_t revision;
+ bool start_empty;
+ char *lock_token = NULL;
+ ReporterObject *reporter = (ReporterObject *)self;
+
+ if (!PyArg_ParseTuple(args, "slb|z", &path, &revision, &start_empty,
+ &lock_token))
+ return NULL;
+
+ if (!check_error(reporter->reporter->set_path(reporter->report_baton,
+ path, revision, start_empty,
+ lock_token, reporter->pool)))
+ return NULL;
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *reporter_delete_path(PyObject *self, PyObject *args)
+{
+ ReporterObject *reporter = (ReporterObject *)self;
+ char *path;
+ if (!PyArg_ParseTuple(args, "s", &path))
+ return NULL;
+
+ if (!check_error(reporter->reporter->delete_path(reporter->report_baton,
+ path, reporter->pool)))
+ return NULL;
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *reporter_link_path(PyObject *self, PyObject *args)
+{
+ char *path, *url;
+ svn_revnum_t revision;
+ bool start_empty;
+ char *lock_token = NULL;
+ ReporterObject *reporter = (ReporterObject *)self;
+
+ if (!PyArg_ParseTuple(args, "sslb|z", &path, &url, &revision, &start_empty, &lock_token))
+ return NULL;
+
+ if (!check_error(reporter->reporter->link_path(reporter->report_baton, path, url,
+ revision, start_empty, lock_token, reporter->pool)))
+ return NULL;
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *reporter_finish(PyObject *self)
+{
+ ReporterObject *reporter = (ReporterObject *)self;
+
+ if (reporter->done_cb != NULL)
+ reporter->done_cb(reporter->done_baton);
+
+ if (!check_error(reporter->reporter->finish_report(reporter->report_baton,
+ reporter->pool)))
+ return NULL;
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *reporter_abort(PyObject *self)
+{
+ ReporterObject *reporter = (ReporterObject *)self;
+
+ if (reporter->done_cb != NULL)
+ reporter->done_cb(reporter->done_baton);
+
+ if (!check_error(reporter->reporter->abort_report(reporter->report_baton,
+ reporter->pool)))
+ return NULL;
+
+ Py_RETURN_NONE;
+}
+
+static PyMethodDef reporter_methods[] = {
+ { "abort", (PyCFunction)reporter_abort, METH_NOARGS, NULL },
+ { "finish", (PyCFunction)reporter_finish, METH_NOARGS, NULL },
+ { "link_path", (PyCFunction)reporter_link_path, METH_VARARGS, NULL },
+ { "set_path", (PyCFunction)reporter_set_path, METH_VARARGS, NULL },
+ { "delete_path", (PyCFunction)reporter_delete_path, METH_VARARGS, NULL },
+ { NULL, }
+};
+
+static void reporter_dealloc(PyObject *self)
+{
+ ReporterObject *reporter = (ReporterObject *)self;
+ /* FIXME: Warn the user if abort_report/finish_report wasn't called? */
+ apr_pool_destroy(reporter->pool);
+ PyObject_Del(self);
+}
+
+PyTypeObject Reporter_Type = {
+ PyObject_HEAD_INIT(NULL) 0,
+ .tp_name = "ra.Reporter",
+ .tp_basicsize = sizeof(ReporterObject),
+ .tp_methods = reporter_methods,
+ .tp_dealloc = reporter_dealloc,
+};
+
+/**
+ * Get libsvn_ra version information.
+ *
+ * :return: tuple with major, minor, patch version number and tag.
+ */
+static PyObject *version(PyObject *self)
+{
+ const svn_version_t *ver = svn_ra_version();
+ return Py_BuildValue("(iiis)", ver->major, ver->minor,
+ ver->patch, ver->tag);
+}
+
+static svn_error_t *py_cb_editor_set_target_revision(void *edit_baton, svn_revnum_t target_revision, apr_pool_t *pool)
+{
+ PyObject *self = (PyObject *)edit_baton, *ret;
+
+ ret = PyObject_CallMethod(self, "set_target_revision", "l", target_revision);
+ if (ret == NULL)
+ return py_svn_error();
+ Py_DECREF(ret);
+ return NULL;
+}
+
+static svn_error_t *py_cb_editor_open_root(void *edit_baton, svn_revnum_t base_revision, apr_pool_t *pool, void **root_baton)
+{
+ PyObject *self = (PyObject *)edit_baton, *ret;
+ *root_baton = NULL;
+ ret = PyObject_CallMethod(self, "open_root", "l", base_revision);
+ if (ret == NULL)
+ return py_svn_error();
+ *root_baton = (void *)ret;
+ return NULL;
+}
+
+static svn_error_t *py_cb_editor_delete_entry(const char *path, long revision, void *parent_baton, apr_pool_t *pool)
+{
+ PyObject *self = (PyObject *)parent_baton, *ret;
+ ret = PyObject_CallMethod(self, "delete_entry", "sl", path, revision);
+ if (ret == NULL)
+ return py_svn_error();
+ Py_DECREF(ret);
+ return NULL;
+}
+
+static svn_error_t *py_cb_editor_add_directory(const char *path, void *parent_baton, const char *copyfrom_path, long copyfrom_revision, apr_pool_t *pool, void **child_baton)
+{
+ PyObject *self = (PyObject *)parent_baton, *ret;
+ *child_baton = NULL;
+ if (copyfrom_path == NULL) {
+ ret = PyObject_CallMethod(self, "add_directory", "s", path);
+ } else {
+ ret = PyObject_CallMethod(self, "add_directory", "ssl", path, copyfrom_path, copyfrom_revision);
+ }
+ if (ret == NULL)
+ return py_svn_error();
+ *child_baton = (void *)ret;
+ return NULL;
+}
+
+static svn_error_t *py_cb_editor_open_directory(const char *path, void *parent_baton, long base_revision, apr_pool_t *pool, void **child_baton)
+{
+ PyObject *self = (PyObject *)parent_baton, *ret;
+ *child_baton = NULL;
+ ret = PyObject_CallMethod(self, "open_directory", "sl", path, base_revision);
+ if (ret == NULL)
+ return py_svn_error();
+ *child_baton = (void *)ret;
+ return NULL;
+}
+
+static svn_error_t *py_cb_editor_change_prop(void *dir_baton, const char *name, const svn_string_t *value, apr_pool_t *pool)
+{
+ PyObject *self = (PyObject *)dir_baton, *ret;
+ if (value != NULL) {
+ ret = PyObject_CallMethod(self, "change_prop", "sz#", name, value->data, value->len);
+ } else {
+ ret = PyObject_CallMethod(self, "change_prop", "sO", name, Py_None);
+ }
+ if (ret == NULL)
+ return py_svn_error();
+ Py_DECREF(ret);
+ return NULL;
+}
+
+static svn_error_t *py_cb_editor_close_directory(void *dir_baton, apr_pool_t *pool)
+{
+ PyObject *self = (PyObject *)dir_baton, *ret;
+ ret = PyObject_CallMethod(self, "close", "");
+ Py_DECREF(self);
+ if (ret == NULL)
+ return py_svn_error();
+ Py_DECREF(ret);
+ return NULL;
+}
+
+static svn_error_t *py_cb_editor_absent_directory(const char *path, void *parent_baton, apr_pool_t *pool)
+{
+ PyObject *self = (PyObject *)parent_baton, *ret;
+ ret = PyObject_CallMethod(self, "absent_directory", "s", path);
+ if (ret == NULL)
+ return py_svn_error();
+ Py_DECREF(ret);
+ return NULL;
+}
+
+static svn_error_t *py_cb_editor_add_file(const char *path, void *parent_baton, const char *copy_path, long copy_revision, apr_pool_t *file_pool, void **file_baton)
+{
+ PyObject *self = (PyObject *)parent_baton, *ret;
+ if (copy_path == NULL) {
+ ret = PyObject_CallMethod(self, "add_file", "s", path);
+ } else {
+ ret = PyObject_CallMethod(self, "add_file", "ssl", path, copy_path,
+ copy_revision);
+ }
+ if (ret == NULL)
+ return py_svn_error();
+ *file_baton = (void *)ret;
+ return NULL;
+}
+
+static svn_error_t *py_cb_editor_open_file(const char *path, void *parent_baton, long base_revision, apr_pool_t *file_pool, void **file_baton)
+{
+ PyObject *self = (PyObject *)parent_baton, *ret;
+ ret = PyObject_CallMethod(self, "open_file", "sl", path, base_revision);
+ if (ret == NULL)
+ return py_svn_error();
+ *file_baton = (void *)ret;
+ return NULL;
+}
+
+static svn_error_t *py_txdelta_window_handler(svn_txdelta_window_t *window, void *baton)
+{
+ int i;
+ PyObject *ops, *ret;
+ PyObject *fn = (PyObject *)baton, *py_new_data, *py_window;
+ if (window == NULL) {
+ /* Signals all delta windows have been received */
+ Py_DECREF(fn);
+ return NULL;
+ }
+ if (fn == Py_None) {
+ /* User doesn't care about deltas */
+ return NULL;
+ }
+ ops = PyList_New(window->num_ops);
+ for (i = 0; i < window->num_ops; i++) {
+ PyList_SetItem(ops, i, Py_BuildValue("(iII)", window->ops[i].action_code,
+ window->ops[i].offset,
+ window->ops[i].length));
+ }
+ if (window->new_data != NULL && window->new_data->data != NULL) {
+ py_new_data = PyString_FromStringAndSize(window->new_data->data, window->new_data->len);
+ } else {
+ py_new_data = Py_None;
+ }
+ py_window = Py_BuildValue("((LIIiOO))", window->sview_offset, window->sview_len, window->tview_len,
+ window->src_ops, ops, py_new_data);
+ ret = PyObject_CallFunction(fn, "O", py_window);
+ Py_DECREF(py_window);
+ if (ret == NULL)
+ return py_svn_error();
+ Py_DECREF(ret);
+ return NULL;
+}
+
+static svn_error_t *py_cb_editor_apply_textdelta(void *file_baton, const char *base_checksum, apr_pool_t *pool, svn_txdelta_window_handler_t *handler, void **handler_baton)
+{
+ PyObject *self = (PyObject *)file_baton, *ret;
+ *handler_baton = NULL;
+ ret = PyObject_CallMethod(self, "apply_textdelta", "z", base_checksum);
+ if (ret == NULL)
+ return py_svn_error();
+ *handler_baton = (void *)ret;
+ *handler = py_txdelta_window_handler;
+ return NULL;
+}
+
+static svn_error_t *py_cb_editor_close_file(void *file_baton,
+ const char *text_checksum, apr_pool_t *pool)
+{
+ PyObject *self = (PyObject *)file_baton, *ret;
+ if (text_checksum != NULL) {
+ ret = PyObject_CallMethod(self, "close", "");
+ } else {
+ ret = PyObject_CallMethod(self, "close", "s", text_checksum);
+ }
+ Py_DECREF(self);
+ if (ret == NULL)
+ return py_svn_error();
+ Py_DECREF(ret);
+ return NULL;
+}
+
+static svn_error_t *py_cb_editor_absent_file(const char *path, void *parent_baton, apr_pool_t *pool)
+{
+ PyObject *self = (PyObject *)parent_baton, *ret;
+ ret = PyObject_CallMethod(self, "absent_file", "s", path);
+ if (ret == NULL)
+ return py_svn_error();
+ Py_DECREF(ret);
+ return NULL;
+}
+
+static svn_error_t *py_cb_editor_close_edit(void *edit_baton, apr_pool_t *pool)
+{
+ PyObject *self = (PyObject *)edit_baton, *ret;
+ ret = PyObject_CallMethod(self, "close", "");
+ Py_DECREF(self);
+ if (ret == NULL)
+ return py_svn_error();
+ Py_DECREF(ret);
+ return NULL;
+}
+
+static svn_error_t *py_cb_editor_abort_edit(void *edit_baton, apr_pool_t *pool)
+{
+ PyObject *self = (PyObject *)edit_baton, *ret;
+ ret = PyObject_CallMethod(self, "abort", "");
+ Py_DECREF(self);
+ if (ret == NULL)
+ return py_svn_error();
+ Py_DECREF(ret);
+ return NULL;
+}
+
+static const svn_delta_editor_t py_editor = {
+ .set_target_revision = py_cb_editor_set_target_revision,
+ .open_root = py_cb_editor_open_root,
+ .delete_entry = py_cb_editor_delete_entry,
+ .add_directory = py_cb_editor_add_directory,
+ .open_directory = py_cb_editor_open_directory,
+ .change_dir_prop = py_cb_editor_change_prop,
+ .close_directory = py_cb_editor_close_directory,
+ .absent_directory = py_cb_editor_absent_directory,
+ .add_file = py_cb_editor_add_file,
+ .open_file = py_cb_editor_open_file,
+ .apply_textdelta = py_cb_editor_apply_textdelta,
+ .change_file_prop = py_cb_editor_change_prop,
+ .close_file = py_cb_editor_close_file,
+ .absent_file = py_cb_editor_absent_file,
+ .close_edit = py_cb_editor_close_edit,
+ .abort_edit = py_cb_editor_abort_edit
+};
+
+static svn_error_t *py_file_rev_handler(void *baton, const char *path, svn_revnum_t rev, apr_hash_t *rev_props, svn_txdelta_window_handler_t *delta_handler, void **delta_baton, apr_array_header_t *prop_diffs, apr_pool_t *pool)
+{
+ PyObject *fn = (PyObject *)baton, *ret, *py_rev_props;
+
+ py_rev_props = prop_hash_to_dict(rev_props);
+ if (py_rev_props == NULL)
+ return py_svn_error();
+
+ /* FIXME: delta handler */
+ ret = PyObject_CallFunction(fn, "slO", path, rev, py_rev_props);
+ Py_DECREF(py_rev_props);
+ if (ret == NULL)
+ return py_svn_error();
+ Py_DECREF(ret);
+ return NULL;
+}
+
+/** Connection to a remote Subversion repository. */
+typedef struct {
+ PyObject_HEAD
+ svn_ra_session_t *ra;
+ apr_pool_t *pool;
+ char *url;
+ PyObject *progress_func;
+ AuthObject *auth;
+ bool busy;
+} RemoteAccessObject;
+
+static void ra_done_handler(void *_ra)
+{
+ RemoteAccessObject *ra = (RemoteAccessObject *)_ra;
+
+ ra->busy = false;
+}
+
+#define RUN_RA_WITH_POOL(pool, ra, cmd) \
+ if (!check_error((cmd))) { \
+ apr_pool_destroy(pool); \
+ ra->busy = false; \
+ return NULL; \
+ } \
+ ra->busy = false;
+
+static bool ra_check_busy(RemoteAccessObject *raobj)
+{
+ if (raobj->busy) {
+ PyErr_SetString(busy_exc, "Remote access object already in use");
+ return true;
+ }
+ raobj->busy = true;
+ return false;
+}
+
+static svn_error_t *py_open_tmp_file(apr_file_t **fp, void *callback,
+ apr_pool_t *pool)
+{
+ RemoteAccessObject *self = (RemoteAccessObject *)callback;
+
+ PyErr_SetString(PyExc_NotImplementedError, "open_tmp_file not wrapped yet");
+
+ return py_svn_error(); /* FIXME */
+}
+
+static PyObject *ra_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
+{
+ char *kwnames[] = { "url", "progress_cb", "auth", "config", NULL };
+ char *url;
+ PyObject *progress_cb = Py_None;
+ AuthObject *auth = (AuthObject *)Py_None;
+ PyObject *config = Py_None;
+ RemoteAccessObject *ret;
+ apr_hash_t *config_hash;
+ svn_ra_callbacks2_t *callbacks2;
+ Py_ssize_t idx = 0;
+ PyObject *key, *value;
+ svn_auth_baton_t *auth_baton;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|OOO", kwnames, &url, &progress_cb, (PyObject **)&auth, &config))
+ return NULL;
+
+ ret = PyObject_New(RemoteAccessObject, &RemoteAccess_Type);
+ if (ret == NULL)
+ return NULL;
+
+ if ((PyObject *)auth == Py_None) {
+ auth_baton = NULL;
+ ret->auth = NULL;
+ } else {
+ /* FIXME: check auth is an instance of Auth_Type */
+ Py_INCREF(auth);
+ ret->auth = auth;
+ auth_baton = ret->auth->auth_baton;
+ }
+
+ ret->pool = Pool();
+ if (ret->pool == NULL)
+ return NULL;
+ ret->url = apr_pstrdup(ret->pool, url);
+ if (!check_error(svn_ra_create_callbacks(&callbacks2, ret->pool))) {
+ apr_pool_destroy(ret->pool);
+ PyObject_Del(ret);
+ return NULL;
+ }
+
+ callbacks2->progress_func = py_progress_func;
+ callbacks2->auth_baton = auth_baton;
+ callbacks2->open_tmp_file = py_open_tmp_file;
+ ret->progress_func = progress_cb;
+ callbacks2->progress_baton = (void *)ret->progress_func;
+ config_hash = apr_hash_make(ret->pool);
+ if (PyDict_Check(config)) {
+ while (PyDict_Next(config, &idx, &key, &value)) {
+ apr_hash_set(config_hash, apr_pstrdup(ret->pool, PyString_AsString(key)),
+ PyString_Size(key), apr_pstrdup(ret->pool, PyString_AsString(value)));
+ }
+ } else if (config != Py_None) {
+ PyErr_SetString(PyExc_TypeError, "Expected dictionary for config");
+ apr_pool_destroy(ret->pool);
+ PyObject_Del(ret);
+ return NULL;
+ }
+ if (!check_error(svn_ra_open2(&ret->ra, apr_pstrdup(ret->pool, url),
+ callbacks2, ret, config_hash, ret->pool))) {
+ apr_pool_destroy(ret->pool);
+ PyObject_Del(ret);
+ return NULL;
+ }
+ ret->busy = false;
+ return (PyObject *)ret;
+}
+
+ /**
+ * Obtain the globally unique identifier for this repository.
+ */
+static PyObject *ra_get_uuid(PyObject *self)
+{
+ const char *uuid;
+ RemoteAccessObject *ra = (RemoteAccessObject *)self;
+ PyObject *ret;
+ apr_pool_t *temp_pool;
+
+ if (ra_check_busy(ra))
+ return NULL;
+
+ temp_pool = Pool();
+ if (temp_pool == NULL)
+ return NULL;
+ RUN_RA_WITH_POOL(temp_pool, ra, svn_ra_get_uuid(ra->ra, &uuid, temp_pool));
+ ret = PyString_FromString(uuid);
+ apr_pool_destroy(temp_pool);
+ return ret;
+}
+
+/** Switch to a different url. */
+static PyObject *ra_reparent(PyObject *self, PyObject *args)
+{
+ char *url;
+ apr_pool_t *temp_pool;
+ RemoteAccessObject *ra = (RemoteAccessObject *)self;
+
+ if (!PyArg_ParseTuple(args, "s", &url))
+ return NULL;
+
+ if (ra_check_busy(ra))
+ return NULL;
+
+ temp_pool = Pool();
+ if (temp_pool == NULL)
+ return NULL;
+ RUN_RA_WITH_POOL(temp_pool, ra, svn_ra_reparent(ra->ra, url, temp_pool));
+ apr_pool_destroy(temp_pool);
+ Py_RETURN_NONE;
+}
+
+/**
+ * Obtain the number of the latest committed revision in the
+ * connected repository.
+ */
+static PyObject *ra_get_latest_revnum(PyObject *self)
+{
+ RemoteAccessObject *ra = (RemoteAccessObject *)self;
+ svn_revnum_t latest_revnum;
+ apr_pool_t *temp_pool;
+ if (ra_check_busy(ra))
+ return NULL;
+
+ temp_pool = Pool();
+ if (temp_pool == NULL)
+ return NULL;
+ RUN_RA_WITH_POOL(temp_pool, ra,
+ svn_ra_get_latest_revnum(ra->ra, &latest_revnum, temp_pool));
+ apr_pool_destroy(temp_pool);
+ return PyInt_FromLong(latest_revnum);
+}
+
+static PyObject *ra_get_log(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ char *kwnames[] = { "callback", "paths", "start", "end", "limit",
+ "discover_changed_paths", "strict_node_history", "revprops", NULL };
+ PyObject *callback, *paths;
+ svn_revnum_t start = 0, end = 0;
+ int limit=0;
+ bool discover_changed_paths=false, strict_node_history=true;
+ RemoteAccessObject *ra = (RemoteAccessObject *)self;
+ PyObject *revprops = Py_None;
+ apr_pool_t *temp_pool;
+ apr_array_header_t *apr_paths;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OOll|ibbO:get_log", kwnames,
+ &callback, &paths, &start, &end, &limit,
+ &discover_changed_paths, &strict_node_history,
+ &revprops))
+ return NULL;
+
+ if (ra_check_busy(ra))
+ return NULL;
+
+ temp_pool = Pool();
+ if (temp_pool == NULL)
+ return NULL;
+ if (paths == Py_None) {
+ /* FIXME: The subversion libraries don't behave as expected,
+ * so tweak our own parameters a bit. */
+ apr_paths = apr_array_make(temp_pool, 1, sizeof(char *));
+ APR_ARRAY_PUSH(apr_paths, char *) = apr_pstrdup(temp_pool, "");
+ } else if (!string_list_to_apr_array(temp_pool, paths, &apr_paths)) {
+ apr_pool_destroy(temp_pool);
+ return NULL;
+ }
+ RUN_RA_WITH_POOL(temp_pool, ra, svn_ra_get_log(ra->ra,
+ apr_paths, start, end, limit,
+ discover_changed_paths, strict_node_history, py_svn_log_wrapper,
+ callback, temp_pool));
+ apr_pool_destroy(temp_pool);
+ Py_RETURN_NONE;
+}
+
+/**
+ * Obtain the URL of the root of this repository.
+ */
+static PyObject *ra_get_repos_root(PyObject *self)
+{
+ RemoteAccessObject *ra = (RemoteAccessObject *)self;
+ const char *root;
+ apr_pool_t *temp_pool;
+
+ if (ra_check_busy(ra))
+ return NULL;
+
+ temp_pool = Pool();
+ if (temp_pool == NULL)
+ return NULL;
+ RUN_RA_WITH_POOL(temp_pool, ra,
+ svn_ra_get_repos_root(ra->ra, &root, temp_pool));
+ apr_pool_destroy(temp_pool);
+ return PyString_FromString(root);
+}
+
+static PyObject *ra_do_update(PyObject *self, PyObject *args)
+{
+ svn_revnum_t revision_to_update_to;
+ char *update_target;
+ bool recurse;
+ PyObject *update_editor;
+ const svn_ra_reporter2_t *reporter;
+ void *report_baton;
+ apr_pool_t *temp_pool;
+ ReporterObject *ret;
+ RemoteAccessObject *ra = (RemoteAccessObject *)self;
+
+ if (!PyArg_ParseTuple(args, "lsbO:do_update", &revision_to_update_to, &update_target, &recurse, &update_editor))
+ return NULL;
+
+ if (ra_check_busy(ra))
+ return NULL;
+
+ temp_pool = Pool();
+ if (temp_pool == NULL)
+ return NULL;
+
+ Py_INCREF(update_editor);
+ if (!check_error(svn_ra_do_update(ra->ra, &reporter,
+ &report_baton,
+ revision_to_update_to,
+ update_target, recurse,
+ &py_editor, update_editor,
+ temp_pool))) {
+ apr_pool_destroy(temp_pool);
+ ra->busy = false;
+ return NULL;
+ }
+ ret = PyObject_New(ReporterObject, &Reporter_Type);
+ if (ret == NULL)
+ return NULL;
+ ret->reporter = reporter;
+ ret->report_baton = report_baton;
+ ret->pool = temp_pool;
+ ret->done_cb = ra_done_handler;
+ ret->done_baton = ra;
+ return (PyObject *)ret;
+}
+
+static PyObject *ra_do_switch(PyObject *self, PyObject *args)
+{
+ RemoteAccessObject *ra = (RemoteAccessObject *)self;
+ svn_revnum_t revision_to_update_to;
+ char *update_target;
+ bool recurse;
+ char *switch_url;
+ PyObject *update_editor;
+ const svn_ra_reporter2_t *reporter;
+ void *report_baton;
+ apr_pool_t *temp_pool;
+ ReporterObject *ret;
+
+ if (!PyArg_ParseTuple(args, "lsbsO:do_switch", &revision_to_update_to, &update_target,
+ &recurse, &switch_url, &update_editor))
+ return NULL;
+ if (ra_check_busy(ra))
+ return NULL;
+
+ temp_pool = Pool();
+ if (temp_pool == NULL)
+ return NULL;
+ Py_INCREF(update_editor);
+ if (!check_error(svn_ra_do_switch(
+ ra->ra, &reporter, &report_baton,
+ revision_to_update_to, update_target,
+ recurse, switch_url, &py_editor,
+ update_editor, temp_pool))) {
+ apr_pool_destroy(temp_pool);
+ ra->busy = false;
+ return NULL;
+ }
+ ret = PyObject_New(ReporterObject, &Reporter_Type);
+ if (ret == NULL)
+ return NULL;
+ ret->reporter = reporter;
+ ret->report_baton = report_baton;
+ ret->pool = temp_pool;
+ ret->done_cb = ra_done_handler;
+ ret->done_baton = ra;
+ return (PyObject *)ret;
+}
+
+static PyObject *ra_replay(PyObject *self, PyObject *args)
+{
+ RemoteAccessObject *ra = (RemoteAccessObject *)self;
+ apr_pool_t *temp_pool;
+ svn_revnum_t revision, low_water_mark;
+ PyObject *update_editor;
+ bool send_deltas = true;
+
+ if (!PyArg_ParseTuple(args, "llO|b", &revision, &low_water_mark, &update_editor, &send_deltas))
+ return NULL;
+
+ if (ra_check_busy(ra))
+ return NULL;
+
+ temp_pool = Pool();
+ if (temp_pool == NULL)
+ return NULL;
+ Py_INCREF(update_editor);
+ RUN_RA_WITH_POOL(temp_pool, ra,
+ svn_ra_replay(ra->ra, revision, low_water_mark,
+ send_deltas, &py_editor, update_editor,
+ temp_pool));
+ apr_pool_destroy(temp_pool);
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *ra_rev_proplist(PyObject *self, PyObject *args)
+{
+ apr_pool_t *temp_pool;
+ apr_hash_t *props;
+ RemoteAccessObject *ra = (RemoteAccessObject *)self;
+ svn_revnum_t rev;
+ PyObject *py_props;
+ if (!PyArg_ParseTuple(args, "l", &rev))
+ return NULL;
+
+ if (ra_check_busy(ra))
+ return NULL;
+
+ temp_pool = Pool();
+ if (temp_pool == NULL)
+ return NULL;
+ RUN_RA_WITH_POOL(temp_pool, ra,
+ svn_ra_rev_proplist(ra->ra, rev, &props, temp_pool));
+ py_props = prop_hash_to_dict(props);
+ apr_pool_destroy(temp_pool);
+ return py_props;
+}
+
+static PyObject *get_commit_editor(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ char *kwnames[] = { "revprops", "callback", "lock_tokens", "keep_locks",
+ NULL };
+ PyObject *revprops, *commit_callback = Py_None, *lock_tokens = Py_None;
+ bool keep_locks = false;
+ apr_pool_t *temp_pool, *pool;
+ const svn_delta_editor_t *editor;
+ void *edit_baton;
+ RemoteAccessObject *ra = (RemoteAccessObject *)self;
+ apr_hash_t *hash_lock_tokens;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|OOb", kwnames, &revprops, &commit_callback, &lock_tokens, &keep_locks))
+ return NULL;
+
+ temp_pool = Pool();
+ if (temp_pool == NULL)
+ return NULL;
+ if (lock_tokens == Py_None) {
+ hash_lock_tokens = NULL;
+ } else {
+ Py_ssize_t idx = 0;
+ PyObject *k, *v;
+ hash_lock_tokens = apr_hash_make(temp_pool);
+ while (PyDict_Next(lock_tokens, &idx, &k, &v)) {
+ apr_hash_set(hash_lock_tokens, PyString_AsString(k),
+ PyString_Size(k), PyString_AsString(v));
+ }
+ }
+
+ if (!PyDict_Check(revprops)) {
+ apr_pool_destroy(temp_pool);
+ PyErr_SetString(PyExc_TypeError, "Expected dictionary with revision properties");
+ return NULL;
+ }
+
+ pool = Pool();
+ if (pool == NULL)
+ return NULL;
+
+ if (ra_check_busy(ra))
+ return NULL;
+
+ if (!check_error(svn_ra_get_commit_editor2(ra->ra, &editor,
+ &edit_baton,
+ PyString_AsString(PyDict_GetItemString(revprops, SVN_PROP_REVISION_LOG)), py_commit_callback,
+ commit_callback, hash_lock_tokens, keep_locks, pool))) {
+ apr_pool_destroy(pool);
+ apr_pool_destroy(temp_pool);
+ ra->busy = false;
+ return NULL;
+ }
+ apr_pool_destroy(temp_pool);
+ return new_editor_object(editor, edit_baton, pool,
+ &Editor_Type, ra_done_handler, ra);
+}
+
+static PyObject *ra_change_rev_prop(PyObject *self, PyObject *args)
+{
+ svn_revnum_t rev;
+ char *name;
+ RemoteAccessObject *ra = (RemoteAccessObject *)self;
+ char *value;
+ int vallen;
+ apr_pool_t *temp_pool;
+ svn_string_t *val_string;
+
+ if (!PyArg_ParseTuple(args, "lss#", &rev, &name, &value, &vallen))
+ return NULL;
+ if (ra_check_busy(ra))
+ return NULL;
+
+ temp_pool = Pool();
+ if (temp_pool == NULL)
+ return NULL;
+ val_string = svn_string_ncreate(value, vallen, temp_pool);
+ RUN_RA_WITH_POOL(temp_pool, ra,
+ svn_ra_change_rev_prop(ra->ra, rev, name, val_string,
+ temp_pool));
+ apr_pool_destroy(temp_pool);
+ Py_RETURN_NONE;
+}
+
+static PyObject *ra_get_dir(PyObject *self, PyObject *args)
+{
+ apr_pool_t *temp_pool;
+ apr_hash_t *dirents;
+ apr_hash_index_t *idx;
+ apr_hash_t *props;
+ svn_revnum_t fetch_rev;
+ const char *key;
+ RemoteAccessObject *ra = (RemoteAccessObject *)self;
+ svn_dirent_t *dirent;
+ apr_ssize_t klen;
+ char *path;
+ svn_revnum_t revision = -1;
+ int dirent_fields = 0;
+ PyObject *py_dirents, *py_props;
+
+ if (!PyArg_ParseTuple(args, "s|li", &path, &revision, &dirent_fields))
+ return NULL;
+
+ if (ra_check_busy(ra))
+ return NULL;
+
+ temp_pool = Pool();
+ if (temp_pool == NULL)
+ return NULL;
+
+ if (revision != SVN_INVALID_REVNUM)
+ fetch_rev = revision;
+
+ RUN_RA_WITH_POOL(temp_pool, ra, svn_ra_get_dir2(ra->ra, &dirents, &fetch_rev, &props,
+ path, revision, dirent_fields, temp_pool));
+
+ if (dirents == NULL) {
+ py_dirents = Py_None;
+ } else {
+ py_dirents = PyDict_New();
+ idx = apr_hash_first(temp_pool, dirents);
+ while (idx != NULL) {
+ PyObject *py_dirent;
+ apr_hash_this(idx, (const void **)&key, &klen, (void **)&dirent);
+ py_dirent = PyDict_New();
+ if (dirent_fields & 0x1)
+ PyDict_SetItemString(py_dirent, "kind",
+ PyInt_FromLong(dirent->kind));
+ if (dirent_fields & 0x2)
+ PyDict_SetItemString(py_dirent, "size",
+ PyLong_FromLong(dirent->size));
+ if (dirent_fields & 0x4)
+ PyDict_SetItemString(py_dirent, "has_props",
+ PyBool_FromLong(dirent->has_props));
+ if (dirent_fields & 0x8)
+ PyDict_SetItemString(py_dirent, "created_rev",
+ PyLong_FromLong(dirent->created_rev));
+ if (dirent_fields & 0x10)
+ PyDict_SetItemString(py_dirent, "time",
+ PyLong_FromLong(dirent->time));
+ if (dirent_fields & 0x20)
+ PyDict_SetItemString(py_dirent, "last_author",
+ PyString_FromString(dirent->last_author));
+ PyDict_SetItemString(py_dirents, key, py_dirent);
+ idx = apr_hash_next(idx);
+ }
+ }
+
+ py_props = prop_hash_to_dict(props);
+ apr_pool_destroy(temp_pool);
+ return Py_BuildValue("(OlO)", py_dirents, fetch_rev, py_props);
+}
+
+static PyObject *ra_get_lock(PyObject *self, PyObject *args)
+{
+ char *path;
+ RemoteAccessObject *ra = (RemoteAccessObject *)self;
+ svn_lock_t *lock;
+ apr_pool_t *temp_pool;
+
+ if (!PyArg_ParseTuple(args, "s", &path))
+ return NULL;
+
+ if (ra_check_busy(ra))
+ return NULL;
+
+ temp_pool = Pool();
+ if (temp_pool == NULL)
+ return NULL;
+ RUN_RA_WITH_POOL(temp_pool, ra,
+ svn_ra_get_lock(ra->ra, &lock, path, temp_pool));
+ apr_pool_destroy(temp_pool);
+ return wrap_lock(lock);
+}
+
+static PyObject *ra_check_path(PyObject *self, PyObject *args)
+{
+ char *path;
+ RemoteAccessObject *ra = (RemoteAccessObject *)self;
+ svn_revnum_t revision;
+ svn_node_kind_t kind;
+ apr_pool_t *temp_pool;
+
+ if (!PyArg_ParseTuple(args, "sl", &path, &revision))
+ return NULL;
+ if (ra_check_busy(ra))
+ return NULL;
+
+ temp_pool = Pool();
+ if (temp_pool == NULL)
+ return NULL;
+ RUN_RA_WITH_POOL(temp_pool, ra,
+ svn_ra_check_path(ra->ra, path, revision, &kind,
+ temp_pool));
+ apr_pool_destroy(temp_pool);
+ return PyInt_FromLong(kind);
+}
+
+static PyObject *ra_has_capability(PyObject *self, PyObject *args)
+{
+#if SVN_VER_MAJOR >= 1 && SVN_VER_MINOR >= 5
+ char *capability;
+ apr_pool_t *temp_pool;
+ RemoteAccessObject *ra = (RemoteAccessObject *)self;
+ int has = 0;
+
+ if (!PyArg_ParseTuple(args, "s", &capability))
+ return NULL;
+
+ if (ra_check_busy(ra))
+ return NULL;
+
+ temp_pool = Pool();
+ if (temp_pool == NULL)
+ return NULL;
+ RUN_RA_WITH_POOL(temp_pool, ra,
+ svn_ra_has_capability(ra->ra, &has, capability, temp_pool));
+ apr_pool_destroy(temp_pool);
+ return PyBool_FromLong(has);
+#else
+ PyErr_SetString(PyExc_NotImplementedError, "has_capability is only supported in Subversion >= 1.5");
+ return NULL;
+#endif
+}
+
+static PyObject *ra_unlock(PyObject *self, PyObject *args)
+{
+ RemoteAccessObject *ra = (RemoteAccessObject *)self;
+ PyObject *path_tokens, *lock_func, *k, *v;
+ bool break_lock;
+ apr_ssize_t idx;
+ apr_pool_t *temp_pool;
+ apr_hash_t *hash_path_tokens;
+
+ if (!PyArg_ParseTuple(args, "ObO", &path_tokens, &break_lock, &lock_func))
+ return NULL;
+
+ if (ra_check_busy(ra))
+ return NULL;
+
+ temp_pool = Pool();
+ if (temp_pool == NULL)
+ return NULL;
+ hash_path_tokens = apr_hash_make(temp_pool);
+ while (PyDict_Next(path_tokens, &idx, &k, &v)) {
+ apr_hash_set(hash_path_tokens, PyString_AsString(k), PyString_Size(k), (char *)PyString_AsString(v));
+ }
+ RUN_RA_WITH_POOL(temp_pool, ra, svn_ra_unlock(ra->ra, hash_path_tokens, break_lock,
+ py_lock_func, lock_func, temp_pool));
+
+ apr_pool_destroy(temp_pool);
+ Py_RETURN_NONE;
+}
+
+static PyObject *ra_lock(PyObject *self, PyObject *args)
+{
+ RemoteAccessObject *ra = (RemoteAccessObject *)self;
+ PyObject *path_revs;
+ char *comment;
+ int steal_lock;
+ PyObject *lock_func, *k, *v;
+ apr_pool_t *temp_pool;
+ apr_hash_t *hash_path_revs;
+ svn_revnum_t *rev;
+ Py_ssize_t idx = 0;
+
+ if (!PyArg_ParseTuple(args, "OsbO", &path_revs, &comment, &steal_lock,
+ &lock_func))
+ return NULL;
+
+ if (ra_check_busy(ra))
+ return NULL;
+
+ temp_pool = Pool();
+ if (temp_pool == NULL)
+ return NULL;
+ if (path_revs == Py_None) {
+ hash_path_revs = NULL;
+ } else {
+ hash_path_revs = apr_hash_make(temp_pool);
+ }
+
+ while (PyDict_Next(path_revs, &idx, &k, &v)) {
+ rev = (svn_revnum_t *)apr_palloc(temp_pool, sizeof(svn_revnum_t));
+ *rev = PyLong_AsLong(v);
+ apr_hash_set(hash_path_revs, PyString_AsString(k), PyString_Size(k),
+ PyString_AsString(v));
+ }
+ RUN_RA_WITH_POOL(temp_pool, ra, svn_ra_lock(ra->ra, hash_path_revs, comment, steal_lock,
+ py_lock_func, lock_func, temp_pool));
+ apr_pool_destroy(temp_pool);
+ return NULL;
+}
+
+static PyObject *ra_get_locks(PyObject *self, PyObject *args)
+{
+ char *path;
+ apr_pool_t *temp_pool;
+ apr_hash_t *hash_locks;
+ apr_hash_index_t *idx;
+ RemoteAccessObject *ra = (RemoteAccessObject *)self;
+ char *key;
+ apr_ssize_t klen;
+ svn_lock_t *lock;
+ PyObject *ret;
+
+ if (!PyArg_ParseTuple(args, "s", &path))
+ return NULL;
+
+ if (ra_check_busy(ra))
+ return NULL;
+
+ temp_pool = Pool();
+ if (temp_pool == NULL)
+ return NULL;
+ RUN_RA_WITH_POOL(temp_pool, ra, svn_ra_get_locks(ra->ra, &hash_locks, path, temp_pool));
+
+ ret = PyDict_New();
+ for (idx = apr_hash_first(temp_pool, hash_locks); idx != NULL;
+ idx = apr_hash_next(idx)) {
+ apr_hash_this(idx, (const void **)&key, &klen, (void **)&lock);
+ PyDict_SetItemString(ret, key, pyify_lock(lock));
+ }
+
+ apr_pool_destroy(temp_pool);
+ return ret;
+}
+
+static PyObject *ra_get_locations(PyObject *self, PyObject *args)
+{
+ char *path;
+ RemoteAccessObject *ra = (RemoteAccessObject *)self;
+ svn_revnum_t peg_revision;
+ PyObject *location_revisions;
+ apr_pool_t *temp_pool;
+ apr_hash_t *hash_locations;
+ apr_hash_index_t *idx;
+ svn_revnum_t *key;
+ PyObject *ret;
+ apr_ssize_t klen;
+ char *val;
+
+ if (!PyArg_ParseTuple(args, "slO", &path, &peg_revision, &location_revisions))
+ return NULL;
+
+ if (ra_check_busy(ra))
+ return NULL;
+
+ temp_pool = Pool();
+ if (temp_pool == NULL)
+ return NULL;
+ RUN_RA_WITH_POOL(temp_pool, ra, svn_ra_get_locations(ra->ra, &hash_locations,
+ path, peg_revision,
+ revnum_list_to_apr_array(temp_pool, location_revisions),
+ temp_pool));
+ ret = PyDict_New();
+
+ for (idx = apr_hash_first(temp_pool, hash_locations); idx != NULL;
+ idx = apr_hash_next(idx)) {
+ apr_hash_this(idx, (const void **)&key, &klen, (void **)&val);
+ PyDict_SetItem(ret, PyInt_FromLong(*key), PyString_FromString(val));
+ }
+ apr_pool_destroy(temp_pool);
+ return ret;
+}
+
+static PyObject *ra_get_file_revs(PyObject *self, PyObject *args)
+{
+ char *path;
+ svn_revnum_t start, end;
+ PyObject *file_rev_handler;
+ apr_pool_t *temp_pool;
+ RemoteAccessObject *ra = (RemoteAccessObject *)self;
+
+ if (!PyArg_ParseTuple(args, "sllO", &path, &start, &end, &file_rev_handler))
+ return NULL;
+
+ if (ra_check_busy(ra))
+ return NULL;
+
+ temp_pool = Pool();
+ if (temp_pool == NULL)
+ return NULL;
+
+ RUN_RA_WITH_POOL(temp_pool, ra, svn_ra_get_file_revs(ra->ra, path, start, end,
+ py_file_rev_handler, (void *)file_rev_handler,
+ temp_pool));
+
+ apr_pool_destroy(temp_pool);
+
+ Py_RETURN_NONE;
+}
+
+static void ra_dealloc(PyObject *self)
+{
+ RemoteAccessObject *ra = (RemoteAccessObject *)self;
+ apr_pool_destroy(ra->pool);
+ Py_XDECREF(ra->auth);
+ PyObject_Del(self);
+}
+
+static PyObject *ra_repr(PyObject *self)
+{
+ RemoteAccessObject *ra = (RemoteAccessObject *)self;
+ return PyString_FromFormat("RemoteAccess(%s)", ra->url);
+}
+
+static PyMethodDef ra_methods[] = {
+ { "get_file_revs", ra_get_file_revs, METH_VARARGS, NULL },
+ { "get_locations", ra_get_locations, METH_VARARGS, NULL },
+ { "get_locks", ra_get_locks, METH_VARARGS, NULL },
+ { "lock", ra_lock, METH_VARARGS, NULL },
+ { "unlock", ra_unlock, METH_VARARGS, NULL },
+ { "has_capability", ra_has_capability, METH_VARARGS, NULL },
+ { "check_path", ra_check_path, METH_VARARGS, NULL },
+ { "get_lock", ra_get_lock, METH_VARARGS, NULL },
+ { "get_dir", ra_get_dir, METH_VARARGS, NULL },
+ { "change_rev_prop", ra_change_rev_prop, METH_VARARGS, NULL },
+ { "get_commit_editor", (PyCFunction)get_commit_editor, METH_VARARGS|METH_KEYWORDS, NULL },
+ { "rev_proplist", ra_rev_proplist, METH_VARARGS, NULL },
+ { "replay", ra_replay, METH_VARARGS, NULL },
+ { "do_switch", ra_do_switch, METH_VARARGS, NULL },
+ { "do_update", ra_do_update, METH_VARARGS, NULL },
+ { "get_repos_root", (PyCFunction)ra_get_repos_root, METH_VARARGS|METH_NOARGS, NULL },
+ { "get_log", (PyCFunction)ra_get_log, METH_VARARGS|METH_KEYWORDS, NULL },
+ { "get_latest_revnum", (PyCFunction)ra_get_latest_revnum, METH_NOARGS, NULL },
+ { "reparent", ra_reparent, METH_VARARGS, NULL },
+ { "get_uuid", (PyCFunction)ra_get_uuid, METH_NOARGS, NULL },
+ { NULL, }
+};
+
+static PyMemberDef ra_members[] = {
+ { "busy", T_BYTE, offsetof(RemoteAccessObject, busy), READONLY, NULL },
+ { "url", T_STRING, offsetof(RemoteAccessObject, url), READONLY, NULL },
+ { NULL, }
+};
+
+PyTypeObject RemoteAccess_Type = {
+ PyObject_HEAD_INIT(&PyType_Type) 0,
+ .tp_name = "ra.RemoteAccess",
+ .tp_basicsize = sizeof(RemoteAccessObject),
+ .tp_new = ra_new,
+ .tp_dealloc = ra_dealloc,
+ .tp_repr = ra_repr,
+ .tp_methods = ra_methods,
+ .tp_members = ra_members,
+};
+
+typedef struct {
+ PyObject_HEAD
+ apr_pool_t *pool;
+ svn_auth_provider_object_t *provider;
+} AuthProviderObject;
+
+static void auth_provider_dealloc(PyObject *self)
+{
+ AuthProviderObject *auth_provider = (AuthProviderObject *)self;
+ apr_pool_destroy(auth_provider->pool);
+ PyObject_Del(self);
+}
+
+PyTypeObject AuthProvider_Type = {
+ PyObject_HEAD_INIT(&PyType_Type) 0,
+ .tp_name = "ra.AuthProvider",
+ .tp_basicsize = sizeof(AuthProviderObject),
+ .tp_dealloc = auth_provider_dealloc,
+};
+
+static PyObject *auth_init(PyTypeObject *type, PyObject *args, PyObject *kwargs)
+{
+ char *kwnames[] = { "providers", NULL };
+ apr_array_header_t *c_providers;
+ svn_auth_provider_object_t **el;
+ PyObject *providers = Py_None;
+ AuthObject *ret;
+ int i;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|O", kwnames, &providers))
+ return NULL;
+
+ ret = PyObject_New(AuthObject, &Auth_Type);
+ if (ret == NULL)
+ return NULL;
+
+ ret->pool = Pool();
+ if (ret->pool == NULL)
+ return NULL;
+ ret->providers = providers;
+ Py_INCREF(providers);
+
+ c_providers = apr_array_make(ret->pool, PyList_Size(providers), 4);
+ for (i = 0; i < PyList_Size(providers); i++) {
+ AuthProviderObject *provider;
+ el = (svn_auth_provider_object_t **)apr_array_push(c_providers);
+ /* FIXME: Check that provider is indeed a AuthProviderObject object */
+ provider = (AuthProviderObject *)PyList_GetItem(providers, i);
+ *el = provider->provider;
+ }
+ svn_auth_open(&ret->auth_baton, c_providers, ret->pool);
+ return (PyObject *)ret;
+}
+
+static PyObject *auth_set_parameter(PyObject *self, PyObject *args)
+{
+ AuthObject *auth = (AuthObject *)self;
+ char *name, *value;
+ if (!PyArg_ParseTuple(args, "ss", &name, &value)) {
+ svn_auth_set_parameter(auth->auth_baton, name, (char *)value);
+ }
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *auth_get_parameter(PyObject *self, PyObject *args)
+{
+ char *name;
+ AuthObject *auth = (AuthObject *)self;
+
+ if (!PyArg_ParseTuple(args, "s", &name))
+ return NULL;
+
+ return PyString_FromString(svn_auth_get_parameter(auth->auth_baton, name));
+}
+
+static PyMethodDef auth_methods[] = {
+ { "set_parameter", auth_set_parameter, METH_VARARGS, NULL },
+ { "get_parameter", auth_get_parameter, METH_VARARGS, NULL },
+ { NULL, }
+};
+
+static void auth_dealloc(PyObject *self)
+{
+ AuthObject *auth = (AuthObject *)self;
+ apr_pool_destroy(auth->pool);
+ Py_DECREF(auth->providers);
+}
+
+PyTypeObject Auth_Type = {
+ PyObject_HEAD_INIT(&PyType_Type) 0,
+ .tp_new = auth_init,
+ .tp_basicsize = sizeof(AuthObject),
+ .tp_dealloc = auth_dealloc,
+ .tp_name = "ra.Auth",
+ .tp_methods = auth_methods,
+};
+
+static svn_error_t *py_username_prompt(svn_auth_cred_username_t **cred, void *baton, const char *realm, int may_save, apr_pool_t *pool)
+{
+ PyObject *fn = (PyObject *)baton, *ret;
+ ret = PyObject_CallFunction(fn, "sb", realm, may_save);
+ if (ret == NULL)
+ return py_svn_error();
+ (*cred)->username = apr_pstrdup(pool, PyString_AsString(PyTuple_GetItem(ret, 0)));
+ (*cred)->may_save = (PyTuple_GetItem(ret, 1) == Py_True);
+ return NULL;
+}
+
+static PyObject *get_username_prompt_provider(PyObject *self, PyObject *args)
+{
+ AuthProviderObject *auth;
+ PyObject *prompt_func;
+ int retry_limit;
+ if (!PyArg_ParseTuple(args, "Oi", &prompt_func, &retry_limit))
+ return NULL;
+ auth = PyObject_New(AuthProviderObject, &AuthProvider_Type);
+ auth->pool = Pool();
+ if (auth->pool == NULL)
+ return NULL;
+ svn_auth_get_username_prompt_provider(&auth->provider, py_username_prompt, (void *)prompt_func, retry_limit, auth->pool);
+ return (PyObject *)auth;
+}
+
+static svn_error_t *py_simple_prompt(svn_auth_cred_simple_t **cred, void *baton, const char *realm, const char *username, int may_save, apr_pool_t *pool)
+{
+ PyObject *fn = (PyObject *)baton, *ret;
+ ret = PyObject_CallFunction(fn, "ssb", realm, username, may_save);
+ if (ret == NULL)
+ return py_svn_error();
+ /* FIXME: Check type of ret */
+ (*cred)->username = apr_pstrdup(pool, PyString_AsString(PyTuple_GetItem(ret, 0)));
+ (*cred)->password = apr_pstrdup(pool, PyString_AsString(PyTuple_GetItem(ret, 1)));
+ (*cred)->may_save = (PyTuple_GetItem(ret, 2) == Py_True);
+ return NULL;
+}
+
+static PyObject *get_simple_prompt_provider(PyObject *self, PyObject *args)
+{
+ PyObject *prompt_func;
+ int retry_limit;
+ AuthProviderObject *auth;
+
+ if (!PyArg_ParseTuple(args, "Oi", &prompt_func, &retry_limit))
+ return NULL;
+
+ auth = PyObject_New(AuthProviderObject, &AuthProvider_Type);
+ auth->pool = Pool();
+ if (auth->pool == NULL)
+ return NULL;
+ svn_auth_get_simple_prompt_provider (&auth->provider, py_simple_prompt, (void *)prompt_func, retry_limit, auth->pool);
+ return (PyObject *)auth;
+}
+
+static svn_error_t *py_ssl_server_trust_prompt(svn_auth_cred_ssl_server_trust_t **cred, void *baton, const char *realm, apr_uint32_t failures, const svn_auth_ssl_server_cert_info_t *cert_info, svn_boolean_t may_save, apr_pool_t *pool)
+{
+ PyObject *fn = (PyObject *)baton;
+ PyObject *ret;
+
+ ret = PyObject_CallFunction(fn, "sl(ssssss)b", realm, failures,
+ cert_info->hostname, cert_info->fingerprint,
+ cert_info->valid_from, cert_info->valid_until,
+ cert_info->issuer_dname, cert_info->ascii_cert,
+ may_save);
+ if (ret == NULL)
+ return py_svn_error();
+
+ /* FIXME: Check that ret is a tuple of size 2 */
+
+ (*cred)->may_save = (PyTuple_GetItem(ret, 0) == Py_True);
+ (*cred)->accepted_failures = PyLong_AsLong(PyTuple_GetItem(ret, 1));
+
+ return NULL;
+}
+
+static PyObject *get_ssl_server_trust_prompt_provider(PyObject *self, PyObject *args)
+{
+ AuthProviderObject *auth;
+ PyObject *prompt_func;
+
+ if (!PyArg_ParseTuple(args, "O", &prompt_func))
+ return NULL;
+
+ auth = PyObject_New(AuthProviderObject, &AuthProvider_Type);
+ if (auth == NULL)
+ return NULL;
+ auth->pool = Pool();
+ if (auth->pool == NULL)
+ return NULL;
+ svn_auth_get_ssl_server_trust_prompt_provider (&auth->provider, py_ssl_server_trust_prompt, (void *)prompt_func, auth->pool);
+ return (PyObject *)auth;
+}
+
+static svn_error_t *py_ssl_client_cert_pw_prompt(svn_auth_cred_ssl_client_cert_pw_t **cred, void *baton, const char *realm, svn_boolean_t may_save, apr_pool_t *pool)
+{
+ PyObject *fn = (PyObject *)baton, *ret;
+ ret = PyObject_CallFunction(fn, "sb", realm, may_save);
+ if (ret == NULL)
+ return py_svn_error();
+ /* FIXME: Check ret is a tuple of size 2 */
+ (*cred)->password = apr_pstrdup(pool, PyString_AsString(PyTuple_GetItem(ret, 0)));
+ (*cred)->may_save = (PyTuple_GetItem(ret, 1) == Py_True);
+ return NULL;
+}
+
+static PyObject *get_ssl_client_cert_pw_prompt_provider(PyObject *self, PyObject *args)
+{
+ PyObject *prompt_func;
+ int retry_limit;
+ AuthProviderObject *auth;
+
+ if (!PyArg_ParseTuple(args, "Oi", &prompt_func, &retry_limit))
+ return NULL;
+
+ auth = PyObject_New(AuthProviderObject, &AuthProvider_Type);
+ if (auth == NULL)
+ return NULL;
+ auth->pool = Pool();
+ if (auth->pool == NULL)
+ return NULL;
+ svn_auth_get_ssl_client_cert_pw_prompt_provider (&auth->provider, py_ssl_client_cert_pw_prompt, (void *)prompt_func, retry_limit, auth->pool);
+ return (PyObject *)auth;
+}
+
+static PyObject *get_username_provider(PyObject *self)
+{
+ AuthProviderObject *auth;
+ auth = PyObject_New(AuthProviderObject, &AuthProvider_Type);
+ if (auth == NULL)
+ return NULL;
+ auth->pool = Pool();
+ if (auth->pool == NULL)
+ return NULL;
+ svn_auth_get_username_provider(&auth->provider, auth->pool);
+ return (PyObject *)auth;
+}
+
+static PyObject *get_simple_provider(PyObject *self)
+{
+ AuthProviderObject *auth = PyObject_New(AuthProviderObject,
+ &AuthProvider_Type);
+ auth->pool = Pool();
+ if (auth->pool == NULL)
+ return NULL;
+ svn_auth_get_simple_provider(&auth->provider, auth->pool);
+ return (PyObject *)auth;
+}
+
+static PyObject *get_ssl_server_trust_file_provider(PyObject *self)
+{
+ AuthProviderObject *auth = PyObject_New(AuthProviderObject, &AuthProvider_Type);
+ auth->pool = Pool();
+ if (auth->pool == NULL)
+ return NULL;
+ svn_auth_get_ssl_server_trust_file_provider(&auth->provider, auth->pool);
+ return (PyObject *)auth;
+}
+
+static PyObject *get_ssl_client_cert_file_provider(PyObject *self)
+{
+ AuthProviderObject *auth = PyObject_New(AuthProviderObject, &AuthProvider_Type);
+ auth->pool = Pool();
+ if (auth->pool == NULL)
+ return NULL;
+ svn_auth_get_ssl_client_cert_file_provider(&auth->provider, auth->pool);
+ return (PyObject *)auth;
+}
+
+static PyObject *get_ssl_client_cert_pw_file_provider(PyObject *self)
+{
+ AuthProviderObject *auth = PyObject_New(AuthProviderObject, &AuthProvider_Type);
+ auth->pool = Pool();
+ if (auth->pool == NULL)
+ return NULL;
+ svn_auth_get_ssl_client_cert_pw_file_provider(&auth->provider, auth->pool);
+ return (PyObject *)auth;
+}
+
+static PyObject *txdelta_send_stream(PyObject *self, PyObject *args)
+{
+ unsigned char digest[16];
+ apr_pool_t *pool;
+ PyObject *stream;
+ TxDeltaWindowHandlerObject *py_txdelta;
+
+ if (!PyArg_ParseTuple(args, "OO", &stream, &py_txdelta))
+ return NULL;
+
+ pool = Pool();
+
+ if (pool == NULL)
+ return NULL;
+
+ if (!check_error(svn_txdelta_send_stream(new_py_stream(pool, stream), py_txdelta->txdelta_handler, py_txdelta->txdelta_baton, (unsigned char *)digest, pool))) {
+ apr_pool_destroy(pool);
+ return NULL;
+ }
+ apr_pool_destroy(pool);
+ return PyString_FromStringAndSize((char *)digest, 16);
+}
+
+static PyMethodDef ra_module_methods[] = {
+ { "version", (PyCFunction)version, METH_NOARGS, NULL },
+ { "txdelta_send_stream", (PyCFunction)txdelta_send_stream, METH_VARARGS, NULL },
+ { "get_ssl_client_cert_pw_file_provider", (PyCFunction)get_ssl_client_cert_pw_file_provider, METH_NOARGS, NULL },
+ { "get_ssl_client_cert_file_provider", (PyCFunction)get_ssl_client_cert_file_provider, METH_NOARGS, NULL },
+ { "get_ssl_server_trust_file_provider", (PyCFunction)get_ssl_server_trust_file_provider, METH_NOARGS, NULL },
+ { "get_simple_provider", (PyCFunction)get_simple_provider, METH_NOARGS, NULL },
+ { "get_username_prompt_provider", (PyCFunction)get_username_prompt_provider, METH_VARARGS, NULL },
+ { "get_simple_prompt_provider", (PyCFunction)get_simple_prompt_provider, METH_VARARGS, NULL },
+ { "get_ssl_server_trust_prompt_provider", (PyCFunction)get_ssl_server_trust_prompt_provider, METH_VARARGS, NULL },
+ { "get_ssl_client_cert_pw_prompt_provider", (PyCFunction)get_ssl_client_cert_pw_prompt_provider, METH_VARARGS, NULL },
+ { "get_username_provider", (PyCFunction)get_username_provider, METH_NOARGS, NULL },
+ { NULL, }
+};
+
+void initra(void)
+{
+ static apr_pool_t *pool;
+ PyObject *mod;
+
+ if (PyType_Ready(&RemoteAccess_Type) < 0)
+ return;
+
+ if (PyType_Ready(&Editor_Type) < 0)
+ return;
+
+ if (PyType_Ready(&FileEditor_Type) < 0)
+ return;
+
+ if (PyType_Ready(&DirectoryEditor_Type) < 0)
+ return;
+
+ if (PyType_Ready(&Reporter_Type) < 0)
+ return;
+
+ if (PyType_Ready(&TxDeltaWindowHandler_Type) < 0)
+ return;
+
+ if (PyType_Ready(&Auth_Type) < 0)
+ return;
+
+ if (PyType_Ready(&AuthProvider_Type) < 0)
+ return;
+
+ apr_initialize();
+ pool = Pool();
+ if (pool == NULL)
+ return;
+ svn_ra_initialize(pool);
+
+ mod = Py_InitModule3("ra", ra_module_methods, "Remote Access");
+ if (mod == NULL)
+ return;
+
+ PyModule_AddObject(mod, "RemoteAccess", (PyObject *)&RemoteAccess_Type);
+ Py_INCREF(&RemoteAccess_Type);
+
+ PyModule_AddObject(mod, "Auth", (PyObject *)&Auth_Type);
+ Py_INCREF(&Auth_Type);
+
+ busy_exc = PyErr_NewException("ra.BusyException", NULL, NULL);
+ PyModule_AddObject(mod, "BusyException", busy_exc);
+
+ PyModule_AddIntConstant(mod, "DIRENT_KIND", SVN_DIRENT_KIND);
+ PyModule_AddIntConstant(mod, "DIRENT_SIZE", SVN_DIRENT_SIZE);
+ PyModule_AddIntConstant(mod, "DIRENT_HAS_PROPS", SVN_DIRENT_HAS_PROPS);
+ PyModule_AddIntConstant(mod, "DIRENT_CREATED_REV", SVN_DIRENT_CREATED_REV);
+ PyModule_AddIntConstant(mod, "DIRENT_TIME", SVN_DIRENT_TIME);
+ PyModule_AddIntConstant(mod, "DIRENT_LAST_AUTHOR", SVN_DIRENT_LAST_AUTHOR);
+ PyModule_AddIntConstant(mod, "DIRENT_ALL", SVN_DIRENT_ALL);
+}
NoWorkingTree, AlreadyBranchError)
from bzrlib.transport.local import LocalTransport
-from svn.core import SubversionException
-import svn.core, svn.repos
+from core import SubversionException
+import core
from bzrlib.plugins.svn.errors import NoSvnRepositoryPresent
from bzrlib.plugins.svn.format import get_rich_root_format, SvnRemoteFormat
full_branch_url = urlutils.join(repos.transport.base,
target_branch_path)
if repos.transport.check_path(target_branch_path,
- repos.get_latest_revnum()) != svn.core.svn_node_none:
+ repos.get_latest_revnum()) != core.NODE_NONE:
raise AlreadyBranchError(full_branch_url)
push_new(repos, target_branch_path, source, stop_revision)
finally:
--- /dev/null
+/*
+ * Copyright © 2008 Jelmer Vernooij <jelmer@samba.org>
+ * -*- coding: utf-8 -*-
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <stdbool.h>
+#include <Python.h>
+#include <apr_general.h>
+#include <svn_fs.h>
+#include <svn_repos.h>
+
+#include "util.h"
+
+PyAPI_DATA(PyTypeObject) Repository_Type;
+PyAPI_DATA(PyTypeObject) FileSystem_Type;
+
+typedef struct {
+ PyObject_HEAD
+ apr_pool_t *pool;
+ svn_repos_t *repos;
+} RepositoryObject;
+
+static PyObject *repos_create(PyObject *self, PyObject *args)
+{
+ char *path;
+ PyObject *config=Py_None, *fs_config=Py_None;
+ svn_repos_t *repos;
+ apr_pool_t *pool;
+ apr_hash_t *hash_config, *hash_fs_config;
+ RepositoryObject *ret;
+
+ if (!PyArg_ParseTuple(args, "s|OO", &path, &config, &fs_config))
+ return NULL;
+
+ pool = Pool();
+ if (pool == NULL)
+ return NULL;
+ hash_config = NULL; /* FIXME */
+ hash_fs_config = NULL; /* FIXME */
+ RUN_SVN_WITH_POOL(pool, svn_repos_create(&repos, path, "", "",
+ hash_config, hash_fs_config, pool));
+
+ ret = PyObject_New(RepositoryObject, &Repository_Type);
+ if (ret == NULL)
+ return NULL;
+
+ ret->pool = pool;
+ ret->repos = repos;
+
+ return (PyObject *)ret;
+}
+
+static void repos_dealloc(PyObject *self)
+{
+ RepositoryObject *repos = (RepositoryObject *)self;
+
+ apr_pool_destroy(repos->pool);
+}
+
+static PyObject *repos_init(PyTypeObject *type, PyObject *args, PyObject *kwargs)
+{
+ char *path;
+ char *kwnames[] = { "path", NULL };
+ RepositoryObject *ret;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s", kwnames, &path))
+ return NULL;
+
+ ret = PyObject_New(RepositoryObject, &Repository_Type);
+ if (ret == NULL)
+ return NULL;
+
+ ret->pool = Pool();
+ if (ret->pool == NULL)
+ return NULL;
+ if (!check_error(svn_repos_open(&ret->repos, path, ret->pool))) {
+ apr_pool_destroy(ret->pool);
+ PyObject_Del(ret);
+ return NULL;
+ }
+
+ return (PyObject *)ret;
+}
+
+typedef struct {
+ PyObject_HEAD
+ RepositoryObject *repos;
+ apr_pool_t *pool;
+ svn_fs_t *fs;
+} FileSystemObject;
+
+static PyObject *repos_fs(PyObject *self)
+{
+ RepositoryObject *reposobj = (RepositoryObject *)self;
+ FileSystemObject *ret;
+ svn_fs_t *fs;
+
+ fs = svn_repos_fs(reposobj->repos);
+
+ ret = PyObject_New(FileSystemObject, &FileSystem_Type);
+ if (ret == NULL)
+ return NULL;
+
+ ret->fs = fs;
+ ret->repos = reposobj;
+ ret->pool = reposobj->pool;
+ Py_INCREF(reposobj);
+
+ return (PyObject *)ret;
+}
+
+static PyObject *fs_get_uuid(PyObject *self)
+{
+ FileSystemObject *fsobj = (FileSystemObject *)self;
+ const char *uuid;
+ PyObject *ret;
+ apr_pool_t *temp_pool;
+
+ temp_pool = Pool();
+ if (temp_pool == NULL)
+ return NULL;
+ RUN_SVN_WITH_POOL(temp_pool, svn_fs_get_uuid(fsobj->fs, &uuid, temp_pool));
+ ret = PyString_FromString(uuid);
+ apr_pool_destroy(temp_pool);
+
+ return ret;
+}
+
+static PyMethodDef fs_methods[] = {
+ { "get_uuid", (PyCFunction)fs_get_uuid, METH_NOARGS, NULL },
+ { NULL, }
+};
+
+static void fs_dealloc(PyObject *self)
+{
+ FileSystemObject *fsobj = (FileSystemObject *)self;
+
+ Py_DECREF(fsobj->repos);
+ apr_pool_destroy(fsobj->pool);
+}
+
+PyTypeObject FileSystem_Type = {
+ PyObject_HEAD_INIT(NULL) 0,
+ .tp_name = "repos.FileSystem",
+ .tp_basicsize = sizeof(FileSystemObject),
+ .tp_dealloc = fs_dealloc,
+ .tp_methods = fs_methods,
+};
+
+static PyObject *repos_load_fs(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ const char *parent_dir = "";
+ PyObject *dumpstream, *feedback_stream, *cancel_func = Py_None;
+ bool use_pre_commit_hook = false, use_post_commit_hook = false;
+ char *kwnames[] = { "dumpstream", "feedback_stream", "uuid_action",
+ "parent_dir", "use_pre_commit_hook",
+ "use_post_commit_hook", "cancel_func", NULL };
+ int uuid_action;
+ apr_pool_t *temp_pool;
+ RepositoryObject *reposobj = (RepositoryObject *)self;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OOi|sbbO", kwnames,
+ &dumpstream, &feedback_stream, &uuid_action,
+ &parent_dir, &use_pre_commit_hook,
+ &use_post_commit_hook,
+ &cancel_func))
+ return NULL;
+
+ temp_pool = Pool();
+ if (temp_pool == NULL)
+ return NULL;
+ RUN_SVN_WITH_POOL(temp_pool, svn_repos_load_fs2(reposobj->repos,
+ new_py_stream(temp_pool, dumpstream),
+ new_py_stream(temp_pool, feedback_stream),
+ uuid_action, parent_dir, use_pre_commit_hook,
+ use_post_commit_hook, py_cancel_func, (void *)cancel_func,
+ reposobj->pool));
+ apr_pool_destroy(temp_pool);
+ Py_RETURN_NONE;
+}
+
+static PyMethodDef repos_module_methods[] = {
+ { "create", repos_create, METH_VARARGS, NULL },
+ { NULL, }
+};
+
+static PyMethodDef repos_methods[] = {
+ { "load_fs", (PyCFunction)repos_load_fs, METH_VARARGS|METH_KEYWORDS, NULL },
+ { "fs", (PyCFunction)repos_fs, METH_NOARGS, NULL },
+ { NULL, }
+};
+
+PyTypeObject Repository_Type = {
+ PyObject_HEAD_INIT(&PyType_Type) 0,
+ .tp_name = "repos.Repository",
+ .tp_basicsize = sizeof(RepositoryObject),
+ .tp_dealloc = repos_dealloc,
+ .tp_methods = repos_methods,
+ .tp_new = repos_init,
+};
+
+void initrepos(void)
+{
+ static apr_pool_t *pool;
+ PyObject *mod;
+
+ if (PyType_Ready(&Repository_Type) < 0)
+ return;
+
+ if (PyType_Ready(&FileSystem_Type) < 0)
+ return;
+
+ apr_initialize();
+ pool = Pool();
+ if (pool == NULL)
+ return;
+ svn_fs_initialize(pool);
+
+ mod = Py_InitModule3("repos", repos_module_methods, "Local repository management");
+ if (mod == NULL)
+ return;
+
+ PyModule_AddObject(mod, "LOAD_UUID_DEFAULT", PyLong_FromLong(0));
+ PyModule_AddObject(mod, "LOAD_UUID_IGNORE", PyLong_FromLong(1));
+ PyModule_AddObject(mod, "LOAD_UUID_FORCE", PyLong_FromLong(2));
+
+ PyModule_AddObject(mod, "Repository", (PyObject *)&Repository_Type);
+ Py_INCREF(&Repository_Type);
+}
from bzrlib import osutils, ui, urlutils, xml5
from bzrlib.branch import Branch, BranchCheckResult
from bzrlib.errors import (InvalidRevisionId, NoSuchRevision, NotBranchError,
- UninitializableFormat, UnrelatedBranches)
+ UninitializableFormat, UnrelatedBranches,
+ NotWriteLocked)
from bzrlib.graph import CachingParentsProvider
from bzrlib.inventory import Inventory
from bzrlib.lockable_files import LockableFiles, TransportLock
from bzrlib.transport import Transport, get_transport
from bzrlib.trace import info, mutter
-from svn.core import SubversionException
-import svn.core
import os
+from bzrlib.plugins.svn import changes, core, errors, logwalker
from bzrlib.plugins.svn.branchprops import PathPropertyProvider
from bzrlib.plugins.svn.cache import create_cache_dir, sqlite3
-from bzrlib.plugins.svn import changes
+from bzrlib.plugins.svn.core import SubversionException
from bzrlib.plugins.svn.changes import changes_path, find_prev_location
from bzrlib.plugins.svn.config import SvnRepositoryConfig
from bzrlib.plugins.svn.parents import SqliteCachingParentsProvider
-from bzrlib.plugins.svn import errors
-from bzrlib.plugins.svn import logwalker
from bzrlib.plugins.svn.mapping import (SVN_PROP_BZR_REVISION_ID, SVN_REVPROP_BZR_SIGNATURE,
parse_revision_metadata, parse_revid_property,
parse_merge_property, BzrSvnMapping,
return False
try:
- return (svn.core.svn_node_dir == self.transport.check_path(path, revnum))
+ return (core.NODE_DIR == self.transport.check_path(path, revnum))
except SubversionException, (_, num):
if num == errors.ERR_FS_NO_SUCH_REVISION:
return False
parent_ids = (mainline_parent,)
if mainline_parent != NULL_REVISION:
-
svn_fileprops = logwalker.lazy_dict({}, self.branchprop_list.get_changed_properties, branch, revnum)
svn_revprops = logwalker.lazy_dict({}, self.transport.revprop_list, revnum)
revmeta = RevisionMetadata(self, branch, None, revnum, svn_revprops, svn_fileprops)
else:
assert False
+ def abort_write_group(self):
+ self._write_group = None
+
+ def commit_write_group(self):
+ self._write_group = None
+
+ def start_write_group(self):
+ if not self.is_write_locked():
+ raise NotWriteLocked(self)
+ self._write_group = None
from bzrlib.errors import (InvalidRevisionId, NoSuchRevision)
from bzrlib.trace import mutter
-import svn.core
-from svn.core import SubversionException
-
+from bzrlib.plugins.svn import core
+from bzrlib.plugins.svn.core import SubversionException
from bzrlib.plugins.svn.cache import CacheTable
from bzrlib.plugins.svn.errors import InvalidPropertyValue, ERR_FS_NO_SUCH_REVISION, ERR_FS_NOT_DIRECTORY
from bzrlib.plugins.svn.mapping import (parse_revision_id, BzrSvnMapping,
#!/usr/bin/env python
+# Setup file for bzr-svn
+# Copyright (C) 2005-2008 Jelmer Vernooij <jelmer@samba.org>
from distutils.core import setup
+from distutils.extension import Extension
+import os
+
+def apr_include_dir():
+ """Determine the APR header file location."""
+ f = os.popen("apr-config --includedir")
+ dir = f.read().rstrip("\n")
+ if not os.path.isdir(dir):
+ raise Exception("APR development headers not found")
+ return dir
+
+def svn_include_dir():
+ """Determine the Subversion header file location."""
+ dirs = ["/usr/local/include/subversion-1", "/usr/include/subversion-1"]
+ for dir in dirs:
+ if os.path.isdir(dir):
+ return dir
+ raise Exception("Subversion development headers not found")
setup(name='bzr-svn',
description='Support for Subversion branches in Bazaar',
'bzrlib.plugins.svn.tests':'tests'},
packages=['bzrlib.plugins.svn',
'bzrlib.plugins.svn.mapping3',
- 'bzrlib.plugins.svn.tests']
+ 'bzrlib.plugins.svn.tests'],
+ ext_modules=[
+ Extension("core", ["core.c", "util.c"], libraries=["svn_subr-1"],
+ include_dirs=[apr_include_dir(), svn_include_dir()]),
+ Extension("client", ["client.c", "util.c"], libraries=["svn_client-1"],
+ include_dirs=[apr_include_dir(), svn_include_dir()]),
+ Extension("ra", ["ra.c", "util.c", "editor.c"], libraries=["svn_ra-1"],
+ include_dirs=[apr_include_dir(), svn_include_dir()]),
+ Extension("repos", ["repos.c", "util.c"], libraries=["svn_repos-1"],
+ include_dirs=[apr_include_dir(), svn_include_dir()]),
+ Extension("wc", ["wc.c", "util.c", "editor.c"], libraries=["svn_wc-1"],
+ include_dirs=[apr_include_dir(), svn_include_dir()])],
)
from bzrlib import osutils, urlutils
from bzrlib.bzrdir import BzrDir
+from bzrlib.plugins.svn.ra import RemoteAccess, txdelta_send_stream
from bzrlib.tests import TestCaseInTempDir, TestSkipped
from bzrlib.trace import mutter
-from bzrlib.urlutils import local_path_to_url
from bzrlib.workingtree import WorkingTree
-import svn.repos, svn.wc
-from bzrlib.plugins.svn.errors import NoCheckoutSupport
+from bzrlib.plugins.svn import repos, wc, client, ra, properties
class TestCaseWithSubversionRepository(TestCaseInTempDir):
"""A test case that provides the ability to build Subversion
def setUp(self):
super(TestCaseWithSubversionRepository, self).setUp()
- self.client_ctx = svn.client.create_context()
- self.client_ctx.log_msg_func2 = svn.client.svn_swig_py_get_commit_log_func
- self.client_ctx.log_msg_baton2 = self.log_message_func
+ self.client_ctx = client.Client()
+ self.client_ctx.log_msg_func = self.log_message_func
- def log_message_func(self, items, pool):
- return self.next_message
+ def log_message_func(self, items):
+ return (self.next_message, None)
def make_repository(self, relpath, allow_revprop_changes=True):
"""Create a repository.
"""
abspath = os.path.join(self.test_dir, relpath)
- svn.repos.create(abspath, '', '', None, None)
+ repos.create(abspath)
if allow_revprop_changes:
if sys.platform == 'win32':
open(revprop_hook, 'w').write("#!/bin/sh\n")
os.chmod(revprop_hook, os.stat(revprop_hook).st_mode | 0111)
- return local_path_to_url(abspath)
+ return urlutils.local_path_to_url(abspath)
def make_remote_bzrdir(self, relpath):
"""Create a repository."""
repos_url = self.make_repository(repos_path)
- try:
- return self.open_local_bzrdir(repos_url, relpath)
- except NoCheckoutSupport:
- raise TestSkipped('No Checkout Support')
+ return self.open_local_bzrdir(repos_url, relpath)
def make_checkout(self, repos_url, relpath):
- rev = svn.core.svn_opt_revision_t()
- rev.kind = svn.core.svn_opt_revision_head
-
- svn.client.checkout2(repos_url, relpath,
- rev, rev, True, False, self.client_ctx)
+ self.client_ctx.checkout(repos_url, relpath, "HEAD", "HEAD",
+ True, False)
@staticmethod
def create_checkout(branch, path, revision_id=None, lightweight=False):
- try:
- return branch.create_checkout(path, revision_id=revision_id,
+ return branch.create_checkout(path, revision_id=revision_id,
lightweight=lightweight)
- except NoCheckoutSupport:
- raise TestSkipped('No Checkout Support')
@staticmethod
def open_checkout(url):
- try:
- return WorkingTree.open(url)
- except NoCheckoutSupport:
- raise TestSkipped('No Checkout Support')
+ return WorkingTree.open(url)
@staticmethod
def open_checkout_bzrdir(url):
- try:
- return BzrDir.open(url)
- except NoCheckoutSupport:
- raise TestSkipped('No Checkout Support')
+ return BzrDir.open(url)
@staticmethod
def create_branch_convenience(url):
- try:
- return BzrDir.create_branch_convenience(url)
- except NoCheckoutSupport:
- raise TestSkipped('No Checkout Support')
+ return BzrDir.create_branch_convenience(url)
def client_set_prop(self, path, name, value):
if value is None:
value = ""
- svn.client.propset2(name, value, path, False, True, self.client_ctx)
+ self.client_ctx.propset(name, value, path, False, True)
def client_get_prop(self, path, name, revnum=None, recursive=False):
- rev = svn.core.svn_opt_revision_t()
-
if revnum is None:
- rev.kind = svn.core.svn_opt_revision_working
- else:
- rev.kind = svn.core.svn_opt_revision_number
- rev.value.number = revnum
- ret = svn.client.propget2(name, path, rev, rev, recursive,
- self.client_ctx)
+ revnum = "WORKING"
+ ret = self.client_ctx.propget(name, path, revnum, revnum, recursive)
if recursive:
return ret
else:
return ret.values()[0]
def client_get_revprop(self, url, revnum, name):
- rev = svn.core.svn_opt_revision_t()
- rev.kind = svn.core.svn_opt_revision_number
- rev.value.number = revnum
- return svn.client.revprop_get(name, url, rev, self.client_ctx)[0]
+ return self.client_ctx.revprop_get(name, url, revnum)[0]
def client_set_revprop(self, url, revnum, name, value):
- rev = svn.core.svn_opt_revision_t()
- rev.kind = svn.core.svn_opt_revision_number
- rev.value.number = revnum
- svn.client.revprop_set(name, value, url, rev, True, self.client_ctx)
+ return self.client_ctx.revprop_set(name, value, url, revnum)
def client_commit(self, dir, message=None, recursive=True):
"""Commit current changes in specified working copy.
"""
olddir = os.path.abspath('.')
self.next_message = message
- os.chdir(dir)
- info = svn.client.commit2(["."], recursive, False, self.client_ctx)
- os.chdir(olddir)
+ info = self.client_ctx.commit([dir], recursive, False)
assert info is not None
- return (info.revision, info.date, info.author)
+ return info
def client_add(self, relpath, recursive=True):
"""Add specified files to working copy.
:param relpath: Path to the files to add.
"""
- svn.client.add3(relpath, recursive, False, False, self.client_ctx)
+ self.client_ctx.add(relpath, recursive, False, False)
- def revnum_to_opt_rev(self, revnum):
- rev = svn.core.svn_opt_revision_t()
- if revnum is None:
- rev.kind = svn.core.svn_opt_revision_head
- else:
- assert isinstance(revnum, int)
- rev.kind = svn.core.svn_opt_revision_number
- rev.value.number = revnum
- return rev
-
- def client_log(self, path, start_revnum=None, stop_revnum=None):
- assert isinstance(path, str)
+ def client_log(self, url, start_revnum=0, stop_revnum=-1):
+ ra = RemoteAccess(url.encode("utf-8"))
ret = {}
- def rcvr(orig_paths, rev, author, date, message, pool):
- ret[rev] = (orig_paths, author, date, message)
- svn.client.log([path], self.revnum_to_opt_rev(start_revnum),
- self.revnum_to_opt_rev(stop_revnum),
- True,
- True,
- rcvr,
- self.client_ctx)
+ def rcvr(orig_paths, rev, revprops):
+ ret[rev] = (orig_paths,
+ revprops.get(properties.PROP_REVISION_AUTHOR),
+ revprops.get(properties.PROP_REVISION_DATE),
+ revprops.get(properties.PROP_REVISION_LOG))
+ if stop_revnum == -1:
+ stop_revnum = ra.get_latest_revnum()
+ ra.get_log(rcvr, None, start_revnum, stop_revnum, 0, True, True)
return ret
def client_delete(self, relpath):
:param relpath: Path to the files to remove.
"""
- svn.client.delete2([relpath], True, self.client_ctx)
+ self.client_ctx.delete([relpath], True)
def client_copy(self, oldpath, newpath, revnum=None):
"""Copy file in working copy.
:param oldpath: Relative path to original file.
:param newpath: Relative path to new file.
"""
- rev = svn.core.svn_opt_revision_t()
- if revnum is None:
- rev.kind = svn.core.svn_opt_revision_head
- else:
- rev.kind = svn.core.svn_opt_revision_number
- rev.value.number = revnum
- svn.client.copy2(oldpath, rev, newpath, self.client_ctx)
+ self.client_ctx.copy(oldpath, newpath, revnum)
def client_update(self, path):
- rev = svn.core.svn_opt_revision_t()
- rev.kind = svn.core.svn_opt_revision_head
- svn.client.update(path, rev, True, self.client_ctx)
+ self.client_ctx.update([path], None, True)
def build_tree(self, files):
"""Create a directory tree.
:return: FS.
"""
- repos = svn.repos.open(relpath)
-
- return svn.repos.fs(repos)
+ return repos.Repository(relpath).fs()
def commit_editor(self, url, message="Test commit"):
- ra = svn.client.open_ra_session(url.encode('utf8'),
- self.client_ctx)
+ ra = RemoteAccess(url.encode('utf8'))
class CommitEditor:
- def __init__(self, ra, editor, edit_baton, base_revnum, base_url):
+ def __init__(self, ra, edit_baton, base_revnum, base_url):
self._used = False
self.ra = ra
self.base_revnum = base_revnum
- self.editor = editor
self.edit_baton = edit_baton
self.data = {}
self.create = set()
for name, contents in dir_dict.items():
subpath = urlutils.join(path, name).strip("/")
if contents is None:
- svn.delta.editor_invoke_delete_entry(self.editor, subpath, -1, dir_baton)
+ dir_baton.delete_entry(subpath, -1)
elif isinstance(contents, dict):
if subpath in self.create:
- child_baton = svn.delta.editor_invoke_add_directory(self.editor, subpath, dir_baton, self.copyfrom[subpath][0], self.copyfrom[subpath][1])
+ child_baton = dir_baton.add_directory(subpath, self.copyfrom[subpath][0], self.copyfrom[subpath][1])
else:
- child_baton = svn.delta.editor_invoke_open_directory(self.editor, subpath, dir_baton, -1)
+ child_baton = dir_baton.open_directory(subpath, -1)
if subpath in self.props:
for k, v in self.props[subpath].items():
- svn.delta.editor_invoke_change_dir_prop(self.editor, child_baton, k, v)
+ child_baton.change_prop(k, v)
self._process_dir(child_baton, dir_dict[name], subpath)
- svn.delta.editor_invoke_close_directory(self.editor, child_baton)
+ child_baton.close()
else:
if subpath in self.create:
- child_baton = svn.delta.editor_invoke_add_file(self.editor, subpath, dir_baton, None, -1)
+ child_baton = dir_baton.add_file(subpath, None, -1)
else:
- child_baton = svn.delta.editor_invoke_open_file(self.editor, subpath, dir_baton, -1)
+ child_baton = dir_baton.open_file(subpath, -1)
if isinstance(contents, str):
- (txdelta, txbaton) = svn.delta.editor_invoke_apply_textdelta(self.editor, child_baton, None)
- svn.delta.svn_txdelta_send_stream(StringIO(contents), txdelta, txbaton)
+ txdelta = child_baton.apply_textdelta(None)
+ txdelta_send_stream(StringIO(contents), txdelta)
if subpath in self.props:
for k, v in self.props[subpath].items():
- svn.delta.editor_invoke_change_file_prop(self.editor, child_baton, k, v)
- svn.delta.editor_invoke_close_file(self.editor, child_baton, None)
+ child_baton.change_prop(k, v)
+ child_baton.close(None)
def done(self):
assert self._used == False
self._used = True
- root_baton = svn.delta.editor_invoke_open_root(self.editor, self.edit_baton,
- self.base_revnum)
+ root_baton = self.edit_baton.open_root(self.base_revnum)
self._process_dir(root_baton, self.data, "")
- svn.delta.editor_invoke_close_directory(self.editor, root_baton)
- svn.delta.editor_invoke_close_edit(self.editor, self.edit_baton)
+ root_baton.close()
+ self.edit_baton.close()
- my_revnum = svn.ra.get_latest_revnum(ra)
+ my_revnum = self.ra.get_latest_revnum()
assert my_revnum > self.base_revnum
return my_revnum
- base_revnum = svn.ra.get_latest_revnum(ra)
- editor, edit_baton = svn.ra.get_commit_editor(ra, message, None, None, True)
- return CommitEditor(ra, editor, edit_baton, base_revnum, url)
+ base_revnum = ra.get_latest_revnum()
+ edit_baton = ra.get_commit_editor({"svn:log": message})
+ return CommitEditor(ra, edit_baton, base_revnum, url)
def test_suite():
'test_branchprops',
'test_changes',
'test_checkout',
+ 'test_client',
+ 'test_core',
'test_commit',
'test_config',
'test_convert',
'test_logwalker',
'test_mapping',
'test_push',
+ 'test_ra',
'test_radir',
'test_repos',
+ 'test_repository',
'test_revids',
'test_revspec',
'test_scheme',
'test_transport',
'test_tree',
'test_upgrade',
+ 'test_wc',
'test_workingtree',
'test_blackbox']
suite.addTest(loader.loadTestsFromModuleNames(["%s.%s" % (__name__, i) for i in testmod_names]))
repos_url = self.make_repository('d')
dc = self.commit_editor(repos_url)
-
dc.add_dir("trunk")
dc.add_file("trunk/hosts")
dc.done()
class TestWorkingTreeFormat(TestCase):
def setUp(self):
super(TestWorkingTreeFormat, self).setUp()
- self.format = SvnWorkingTreeFormat()
+ self.format = SvnWorkingTreeFormat(4)
def test_get_format_desc(self):
- self.assertEqual("Subversion Working Copy",
+ self.assertEqual("Subversion Working Copy Version 4",
self.format.get_format_description())
def test_initialize(self):
--- /dev/null
+# Copyright (C) 2005-2007 Jelmer Vernooij <jelmer@samba.org>
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+"""Subversion client library tests."""
+
+from bzrlib.tests import TestCase
+from bzrlib.plugins.svn import client
+from bzrlib.plugins.svn.tests import TestCaseWithSubversionRepository
+
+class TestClient(TestCaseWithSubversionRepository):
+ def setUp(self):
+ super(TestClient, self).setUp()
+ self.repos_url = self.make_client("d", "dc")
+ self.client = client.Client()
+
+ def test_add(self):
+ self.build_tree({"dc/foo": None})
+ self.client.add("dc/foo")
from bzrlib.plugins.svn.transport import SvnRaTransport
from bzrlib.plugins.svn.tests import TestCaseWithSubversionRepository
-from svn.core import svn_time_to_cstring
+from bzrlib.plugins.svn.core import time_to_cstring
class TestNativeCommit(TestCaseWithSubversionRepository):
def test_simple_commit(self):
wt.rename_one("foo", "bar")
wt.commit(message="doe")
paths = self.client_log("dc", 2, 0)[2][0]
- self.assertEquals('D', paths["/foo"].action)
- self.assertEquals('A', paths["/bar"].action)
- self.assertEquals('/foo', paths["/bar"].copyfrom_path)
- self.assertEquals(1, paths["/bar"].copyfrom_rev)
+ self.assertEquals('D', paths["/foo"][0])
+ self.assertEquals('A', paths["/bar"][0])
+ self.assertEquals('/foo', paths["/bar"][1])
+ self.assertEquals(1, paths["/bar"][2])
self.assertEquals("bar\t%s\n" % oldid,
self.client_get_prop(repos_url, "bzr:file-ids", 2))
self.assertTrue(wt.has_filename("bar"))
wt.commit(message="doe")
paths = self.client_log("dc", 2, 0)[2][0]
- self.assertEquals('D', paths["/adir/foo"].action)
- self.assertEquals('A', paths["/bar"].action)
- self.assertEquals('/adir/foo', paths["/bar"].copyfrom_path)
- self.assertEquals(1, paths["/bar"].copyfrom_rev)
+ self.assertEquals('D', paths["/adir/foo"][0])
+ self.assertEquals('A', paths["/bar"][0])
+ self.assertEquals('/adir/foo', paths["/bar"][1])
+ self.assertEquals(1, paths["/bar"][2])
def test_commit_revision_id(self):
repos_url = self.make_client('d', 'dc')
wt.commit(message="doe")
self.olddir.open_branch().pull(self.newdir.open_branch())
paths = self.client_log(self.repos_url, 3, 0)[3][0]
- self.assertEquals('D', paths["/vla"].action)
- self.assertEquals('A', paths["/bar"].action)
- self.assertEquals('/vla', paths["/bar"].copyfrom_path)
- self.assertEquals(2, paths["/bar"].copyfrom_rev)
+ self.assertEquals('D', paths["/vla"][0])
+ self.assertEquals('A', paths["/bar"][0])
+ self.assertEquals('/vla', paths["/bar"][1])
+ self.assertEquals(2, paths["/bar"][2])
def test_commit_rename_file_from_directory(self):
wt = self.newdir.open_workingtree()
self.olddir.open_branch().pull(self.newdir.open_branch())
paths = self.client_log(self.repos_url, 3, 0)[3][0]
mutter('paths %r' % paths)
- self.assertEquals('D', paths["/adir/foo"].action)
- self.assertEquals('A', paths["/bar"].action)
- self.assertEquals('/adir/foo', paths["/bar"].copyfrom_path)
- self.assertEquals(2, paths["/bar"].copyfrom_rev)
+ self.assertEquals('D', paths["/adir/foo"][0])
+ self.assertEquals('A', paths["/bar"][0])
+ self.assertEquals('/adir/foo', paths["/bar"][1])
+ self.assertEquals(2, paths["/bar"][2])
def test_commit_remove(self):
wt = self.newdir.open_workingtree()
self.olddir.open_branch().pull(self.newdir.open_branch())
paths = self.client_log(self.repos_url, 3, 0)[3][0]
mutter('paths %r' % paths)
- self.assertEquals('D', paths["/foob"].action)
+ self.assertEquals('D', paths["/foob"][0])
def test_commit_rename_remove_parent(self):
wt = self.newdir.open_workingtree()
self.olddir.open_branch().pull(self.newdir.open_branch())
paths = self.client_log(self.repos_url, 3, 0)[3][0]
mutter('paths %r' % paths)
- self.assertEquals('D', paths["/adir"].action)
- self.assertEquals('A', paths["/bar"].action)
- self.assertEquals('/adir/foob', paths["/bar"].copyfrom_path)
- self.assertEquals(2, paths["/bar"].copyfrom_rev)
+ self.assertEquals('D', paths["/adir"][0])
+ self.assertEquals('A', paths["/bar"][0])
+ self.assertEquals('/adir/foob', paths["/bar"][1])
+ self.assertEquals(2, paths["/bar"][2])
def test_commit_remove_nested(self):
wt = self.newdir.open_workingtree()
self.olddir.open_branch().pull(self.newdir.open_branch())
paths = self.client_log(self.repos_url, 3, 0)[3][0]
mutter('paths %r' % paths)
- self.assertEquals('D', paths["/adir/foob"].action)
+ self.assertEquals('D', paths["/adir/foob"][0])
class TestPushNested(TestCaseWithSubversionRepository):
class RevpropTests(TestCaseWithSubversionRepository):
def test_change_revprops(self):
- repos_url = self.make_repository("d")
+ repos_url = self.make_repository("d", allow_revprop_changes=True)
dc = self.commit_editor(repos_url, message="My commit")
dc.add_file("foo.txt")
transport = SvnRaTransport(repos_url)
set_svn_revprops(transport, 1, {"svn:author": "Somebody",
- "svn:date": svn_time_to_cstring(1000000*473385600)})
+ "svn:date": time_to_cstring(1000000*473385600)})
+
+ self.assertEquals(1, transport.get_latest_revnum())
self.assertEquals(("Somebody", "1985-01-01T00:00:00.000000Z", "My commit"),
- self.client_log(repos_url)[1][1:])
+ self.client_log(repos_url)[1][1:])
def test_change_revprops_disallowed(self):
repos_url = self.make_repository("d", allow_revprop_changes=False)
transport = SvnRaTransport(repos_url)
self.assertRaises(RevpropChangeFailed,
- lambda: set_svn_revprops(transport, 1, {"svn:author": "Somebody", "svn:date": svn_time_to_cstring(1000000*473385600)}))
+ lambda: set_svn_revprops(transport, 1, {"svn:author": "Somebody", "svn:date": time_to_cstring(1000000*473385600)}))
class SvkTestCase(TestCase):
from bzrlib.plugins.svn.mapping3.scheme import TrunkBranchingScheme, NoBranchingScheme
from bzrlib.plugins.svn.tests import TestCaseWithSubversionRepository
-import svn.repos
+from bzrlib.plugins.svn import repos
class TestLoadDumpfile(TestCaseInTempDir):
def test_loaddumpfile(self):
PROPS-END
""")
load_dumpfile(dumpfile, "d")
- repos = svn.repos.open("d")
- fs = svn.repos.fs(repos)
+ r = repos.Repository("d")
self.assertEqual("6987ef2d-cd6b-461f-9991-6f1abef3bd59",
- svn.fs.get_uuid(fs))
+ r.fs().get_uuid())
def test_loaddumpfile_invalid(self):
dumpfile = os.path.join(self.test_dir, "dumpfile")
--- /dev/null
+# Copyright (C) 2005-2007 Jelmer Vernooij <jelmer@samba.org>
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+"""Subversion core library tests."""
+
+from bzrlib.tests import TestCase
+from bzrlib.plugins.svn import core
+
+class TestCore(TestCase):
+ def setUp(self):
+ super(TestCore, self).setUp()
+
+ def test_exc(self):
+ self.assertIsInstance(core.SubversionException("foo", 1), Exception)
+
+ def test_get_config(self):
+ self.assertIsInstance(core.get_config(), dict)
+
+ def test_time_from_cstring(self):
+ self.assertEquals(1225704780716938L, core.time_from_cstring("2008-11-03T09:33:00.716938Z"))
+
+ def test_time_to_cstring(self):
+ self.assertEquals("2008-11-03T09:33:00.716938Z", core.time_to_cstring(1225704780716938L))
+
UnexpectedEndOfContainerError)
from bzrlib.tests import TestCase
+from bzrlib.plugins.svn.core import SubversionException
from bzrlib.plugins.svn.errors import *
-import svn.core
-from svn.core import SubversionException
class TestConvertError(TestCase):
def test_decorator_unknown(self):
self.client_add("dc/old-trunk")
self.client_commit("dc", "trunk data")
- self.build_tree({'dc/trunk': None})
- self.client_add("dc/trunk")
- self.client_copy("dc/old-trunk/lib", "dc/trunk")
+ self.client_copy("dc/old-trunk", "dc/trunk")
self.client_commit("dc", "revive old trunk")
oldrepos = Repository.open(repos_url)
"""Log walker tests."""
+from bzrlib import debug
from bzrlib.errors import NoSuchRevision
import os
from bzrlib.plugins.svn import logwalker
-from bzrlib import debug
from bzrlib.plugins.svn.tests import TestCaseWithSubversionRepository
from bzrlib.plugins.svn.transport import SvnRaTransport
from bzrlib.workingtree import WorkingTree
import os
+from time import sleep
+
from bzrlib.plugins.svn import format
-import svn.core
from bzrlib.plugins.svn.errors import ChangesRootLHSHistory, MissingPrefix
-from time import sleep
from bzrlib.plugins.svn.commit import push
from bzrlib.plugins.svn.mapping import SVN_PROP_BZR_REVISION_ID
from bzrlib.plugins.svn.tests import TestCaseWithSubversionRepository
--- /dev/null
+# Copyright (C) 2005-2007 Jelmer Vernooij <jelmer@samba.org>
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+"""Subversion ra library tests."""
+
+from bzrlib.tests import TestCase
+from bzrlib.plugins.svn import ra
+from bzrlib.plugins.svn.tests import TestCaseWithSubversionRepository
+
+class VersionTest(TestCase):
+ def test_version_length(self):
+ self.assertEquals(4, len(ra.version()))
+
+
+class TestRemoteAccess(TestCaseWithSubversionRepository):
+ def setUp(self):
+ super(TestRemoteAccess, self).setUp()
+ self.repos_url = self.make_client("d", "dc")
+ self.ra = ra.RemoteAccess(self.repos_url)
+
+ def do_commit(self):
+ self.build_tree({'dc/foo': None})
+ self.client_add("dc/foo")
+ self.client_commit("dc", "msg")
+
+ def test_repr(self):
+ self.assertEquals("RemoteAccess(%s)" % self.repos_url,
+ repr(self.ra))
+
+ def test_latest_revnum(self):
+ self.assertEquals(0, self.ra.get_latest_revnum())
+
+ def test_latest_revnum_one(self):
+ self.do_commit()
+ self.assertEquals(1, self.ra.get_latest_revnum())
+
+ def test_get_uuid(self):
+ self.assertIsInstance(self.ra.get_uuid(), str)
+
+ def test_get_repos_root(self):
+ self.assertEqual(self.repos_url, self.ra.get_repos_root())
+
+ def test_reparent(self):
+ self.ra.reparent(self.repos_url)
+
+ def test_has_capability(self):
+ self.assertRaises(NotImplementedError, self.ra.has_capability, "FOO")
+
+ def test_get_dir(self):
+ ret = self.ra.get_dir("", 0)
+ self.assertIsInstance(ret, tuple)
+
+ def test_change_rev_prop(self):
+ self.do_commit()
+ self.ra.change_rev_prop(1, "foo", "bar")
+
+ def test_rev_proplist(self):
+ self.assertIsInstance(self.ra.rev_proplist(0), dict)
+
+ def test_get_log(self):
+ returned = []
+ def cb(*args):
+ returned.append(args)
+ def check_results(returned):
+ self.assertEquals(2, len(returned))
+ (paths, revnum, props) = returned[0]
+ self.assertEquals(None, paths)
+ self.assertEquals(revnum, 0)
+ self.assertEquals(["svn:date"], props.keys())
+ (paths, revnum, props) = returned[1]
+ self.assertEquals({'/foo': ('A', None, -1)}, paths)
+ self.assertEquals(revnum, 1)
+ self.assertEquals(set(["svn:date", "svn:author", "svn:log"]),
+ set(props.keys()))
+ self.ra.get_log(cb, [""], 0, 0)
+ self.assertEquals(1, len(returned))
+ self.do_commit()
+ returned = []
+ self.ra.get_log(cb, ["/"], 0, 1, discover_changed_paths=True,
+ strict_node_history=False)
+ check_results(returned)
+ returned = []
+ self.ra.get_log(cb, None, 0, 1, discover_changed_paths=True,
+ strict_node_history=False)
+ check_results(returned)
+
+ def test_get_commit_editor_busy(self):
+ def mycb(rev):
+ pass
+ editor = self.ra.get_commit_editor({"svn:log": "foo"}, mycb)
+ self.assertRaises(ra.BusyException, self.ra.get_commit_editor, {"svn:log": "foo"}, mycb)
+ editor.abort()
+
+ def test_get_commit_editor(self):
+ def mycb(paths, rev, revprops):
+ pass
+ editor = self.ra.get_commit_editor({"svn:log": "foo"}, mycb)
+ dir = editor.open_root(0)
+ subdir = dir.add_directory("foo")
+ subdir.close()
+ dir.close()
+ editor.close()
+
from bzrlib.errors import (NoRepositoryPresent, NotBranchError, NotLocalUrl,
NoWorkingTree, AlreadyBranchError)
-import svn
-
+from bzrlib.plugins.svn import core
from bzrlib.plugins.svn.format import SvnRemoteFormat
from bzrlib.plugins.svn.tests import TestCaseWithSubversionRepository
from bzrlib.plugins.svn.transport import SvnRaTransport
b = x.create_branch()
self.assertEquals(repos_url+"/trunk", b.base)
transport = SvnRaTransport(repos_url)
- self.assertEquals(svn.core.svn_node_dir,
+ self.assertEquals(core.NODE_DIR,
transport.check_path("trunk", 1))
def test_bad_dir(self):
-# -*- coding: utf-8 -*-
-
-# Copyright (C) 2006-2007 Jelmer Vernooij <jelmer@samba.org>
-
+# Copyright (C) 2005-2007 Jelmer Vernooij <jelmer@samba.org>
+
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-
-"""Subversion repository tests."""
-
-from bzrlib import urlutils
-from bzrlib.branch import Branch
-from bzrlib.bzrdir import BzrDir, format_registry
-from bzrlib.config import GlobalConfig
-from bzrlib.errors import NoSuchRevision, UninitializableFormat, BzrError
-from bzrlib.inventory import Inventory
-from bzrlib.osutils import has_symlinks
-from bzrlib.repository import Repository
-from bzrlib.revision import NULL_REVISION, Revision
-from bzrlib.tests import TestCase
-
-import os, sys
-
-import svn.fs, svn
-
-from bzrlib.plugins.svn import format
-from bzrlib.plugins.svn.mapping import (escape_svn_path, unescape_svn_path,
- SVN_PROP_BZR_REVISION_ID)
-from bzrlib.plugins.svn.mapping3 import (SVN_PROP_BZR_BRANCHING_SCHEME, set_branching_scheme,
- set_property_scheme, BzrSvnMappingv3)
-from bzrlib.plugins.svn.mapping3.scheme import (TrunkBranchingScheme, NoBranchingScheme,
- ListBranchingScheme, SingleBranchingScheme)
-from bzrlib.plugins.svn.transport import SvnRaTransport
-from bzrlib.plugins.svn.tests import TestCaseWithSubversionRepository
-from bzrlib.plugins.svn.repository import SvnRepositoryFormat
-
-
-class TestSubversionRepositoryWorks(TestCaseWithSubversionRepository):
- def test_format(self):
- """ Test repository format is correct """
- bzrdir = self.make_local_bzrdir('a', 'ac')
- self.assertEqual(bzrdir._format.get_format_string(), \
- "Subversion Local Checkout")
-
- self.assertEqual(bzrdir._format.get_format_description(), \
- "Subversion Local Checkout")
-
- def test_get_branch_log(self):
- repos_url = self.make_repository("a")
- cb = self.commit_editor(repos_url)
- cb.add_file("foo")
- cb.done()
-
- repos = Repository.open(repos_url)
-
- self.assertEqual([
- ('', {'foo': ('A', None, -1)}, 1),
- ('', {'': ('A', None, -1)}, 0)],
- [(l.branch_path, l.paths, l.revnum) for l in repos.iter_reverse_branch_changes("", 1, NoBranchingScheme())])
-
- def test_make_working_trees(self):
- repos_url = self.make_repository("a")
- repos = Repository.open(repos_url)
- self.assertFalse(repos.make_working_trees())
-
- def test_get_config_global_set(self):
- repos_url = self.make_repository("a")
- cfg = GlobalConfig()
- cfg.set_user_option("foo", "Still Life")
-
- repos = Repository.open(repos_url)
- self.assertEquals("Still Life",
- repos.get_config().get_user_option("foo"))
-
- def test_get_config(self):
- repos_url = self.make_repository("a")
- repos = Repository.open(repos_url)
- repos.get_config().set_user_option("foo", "Van Der Graaf Generator")
-
- repos = Repository.open(repos_url)
- self.assertEquals("Van Der Graaf Generator",
- repos.get_config().get_user_option("foo"))
-
-
- def test_get_physical_lock_status(self):
- repos_url = self.make_repository("a")
- repos = Repository.open(repos_url)
- self.assertFalse(repos.get_physical_lock_status())
-
- def test_iter_changes_parent_rename(self):
- repos_url = self.make_client("a", "dc")
- self.build_tree({'dc/foo/bar': None})
- self.client_add('dc/foo')
- self.client_commit('dc', 'a')
- self.client_update('dc')
- self.client_copy('dc/foo', 'dc/bla')
- self.client_commit('dc', 'b')
- repos = Repository.open(repos_url)
- ret = list(repos.iter_changes('bla/bar', 2, BzrSvnMappingv3(SingleBranchingScheme('bla/bar'))))
- self.assertEquals(1, len(ret))
- self.assertEquals("bla/bar", ret[0][0])
-
- def test_set_make_working_trees(self):
- repos_url = self.make_repository("a")
- repos = Repository.open(repos_url)
- repos.set_make_working_trees(True)
- self.assertFalse(repos.make_working_trees())
-
- def test_get_fileid_map(self):
- repos_url = self.make_repository("a")
- repos = Repository.open(repos_url)
- mapping = repos.get_mapping()
- self.assertEqual({u"": (mapping.generate_file_id(repos.uuid, 0, "", u""), mapping.generate_revision_id(repos.uuid, 0, ""))}, repos.get_fileid_map(0, "", mapping))
-
- def test_generate_revision_id_forced_revid(self):
- repos_url = self.make_client("a", "dc")
- self.client_set_prop("dc", SVN_PROP_BZR_REVISION_ID+"none",
- "2 someid\n")
- self.client_commit("dc", "set id")
- repos = Repository.open(repos_url)
- revid = repos.generate_revision_id(1, "", repos.get_mapping())
- self.assertEquals("someid", revid)
-
- def test_generate_revision_id_forced_revid_invalid(self):
- repos_url = self.make_client("a", "dc")
- self.client_set_prop("dc", SVN_PROP_BZR_REVISION_ID+"none",
- "corrupt-id\n")
- self.client_commit("dc", "set id")
- repos = Repository.open(repos_url)
- revid = repos.generate_revision_id(1, "", repos.get_mapping())
- self.assertEquals(
- repos.get_mapping().generate_revision_id(repos.uuid, 1, ""),
- revid)
-
- def test_add_revision(self):
- repos_url = self.make_repository("a")
- repos = Repository.open(repos_url)
- self.assertRaises(NotImplementedError, repos.add_revision, "revid",
- None)
-
- def test_has_signature_for_revision_id_no(self):
- repos_url = self.make_repository("a")
- repos = Repository.open(repos_url)
- self.assertFalse(repos.has_signature_for_revision_id("foo"))
-
- def test_set_signature(self):
- repos_url = self.make_client("a", "dc")
- repos = Repository.open(repos_url)
- self.build_tree({"dc/foo": "bar"})
- self.client_add("dc/foo")
- self.client_commit("dc", "msg")
- revid = repos.get_mapping().generate_revision_id(repos.uuid, 1, "")
- repos.add_signature_text(revid, "TEXT")
- self.assertTrue(repos.has_signature_for_revision_id(revid))
- self.assertEquals(repos.get_signature_text(revid), "TEXT")
-
- def test_repr(self):
- repos_url = self.make_client("a", "dc")
- self.build_tree({'dc/foo': "data"})
- self.client_add("dc/foo")
- self.client_commit("dc", "My Message")
-
- repos = Repository.open(repos_url)
-
- self.assertEqual("SvnRepository('%s/')" % urlutils.local_path_to_url(urlutils.join(self.test_dir, "a")), repos.__repr__())
-
- def test_get_branch_invalid_revision(self):
- repos_url = self.make_repository("a")
- repos = Repository.open(repos_url)
- self.assertRaises(NoSuchRevision, list,
- repos.iter_reverse_branch_changes("/", 20, NoBranchingScheme()))
-
- def test_follow_branch_switched_parents(self):
- repos_url = self.make_client('a', 'dc')
- self.build_tree({'dc/pykleur/trunk/pykleur': None})
- self.client_add("dc/pykleur")
- self.client_commit("dc", "initial")
- self.build_tree({'dc/pykleur/trunk/pykleur/afile': 'contents'})
- self.client_add("dc/pykleur/trunk/pykleur/afile")
- self.client_commit("dc", "add file")
- self.client_copy("dc/pykleur", "dc/pygments", 1)
- self.client_delete('dc/pykleur')
- self.client_update("dc")
- self.client_commit("dc", "commit")
- repos = Repository.open(repos_url)
- self.assertEquals([
- ('pygments/trunk', {'pygments': (u'A', 'pykleur', 1),
- 'pygments/trunk': (u'R', 'pykleur/trunk', 2),
- 'pykleur': (u'D', None, -1)}, 3),
- ('pykleur/trunk', {'pykleur/trunk/pykleur/afile': (u'A', None, -1)}, 2),
- ('pykleur/trunk',
- {'pykleur': (u'A', None, -1),
- 'pykleur/trunk': (u'A', None, -1),
- 'pykleur/trunk/pykleur': (u'A', None, -1)},
- 1)],
- [(l.branch_path, l.paths, l.revnum) for l in repos.iter_reverse_branch_changes("pygments/trunk", 3, TrunkBranchingScheme(1))])
-
- def test_follow_branch_move_single(self):
- repos_url = self.make_client('a', 'dc')
- self.build_tree({'dc/pykleur/bla': None})
- self.client_add("dc/pykleur")
- self.client_commit("dc", "initial")
- self.client_copy("dc/pykleur", "dc/pygments", 1)
- self.client_update("dc")
- self.client_commit("dc", "commit")
- repos = Repository.open(repos_url)
- changes = repos.iter_reverse_branch_changes("pygments", 2, SingleBranchingScheme("pygments"))
- self.assertEquals([('pygments',
- {'pygments/bla': ('A', None, -1), 'pygments': ('A', None, -1)},
- 2)],
- [(l.branch_path, l.paths, l.revnum) for l in changes])
-
- def test_history_all(self):
- repos_url = self.make_client("a", "dc")
- self.build_tree({'dc/trunk/file': "data", "dc/foo/file":"data"})
- self.client_add("dc/trunk")
- self.client_add("dc/foo")
- self.client_commit("dc", "My Message")
-
- repos = Repository.open(repos_url)
-
- self.assertEqual(2,
- len(list(repos.all_revision_ids(repos.get_layout()))))
-
- def test_all_revs_empty(self):
- repos_url = self.make_repository("a")
- repos = Repository.open(repos_url)
- set_branching_scheme(repos, TrunkBranchingScheme())
- self.assertEqual([], list(repos.all_revision_ids()))
-
- def test_all_revs(self):
- repos_url = self.make_client("a", "dc")
- self.build_tree({'dc/trunk/file': "data", "dc/foo/file":"data"})
- self.client_add("dc/trunk")
- self.client_commit("dc", "add trunk")
- self.build_tree({'dc/branches/somebranch/somefile': 'data'})
- self.client_add("dc/branches")
- self.client_commit("dc", "add a branch")
- self.client_delete("dc/branches/somebranch")
- self.client_commit("dc", "remove branch")
-
- repos = Repository.open(repos_url)
- set_branching_scheme(repos, TrunkBranchingScheme())
- mapping = repos.get_mapping()
- self.assertEqual([
- repos.generate_revision_id(1, "trunk", mapping),
- repos.generate_revision_id(2, "branches/somebranch", mapping)],
- list(repos.all_revision_ids()))
-
- def test_follow_history_empty(self):
- repos_url = self.make_repository("a")
- repos = Repository.open(repos_url)
- self.assertEqual([repos.generate_revision_id(0, '', repos.get_mapping())],
- list(repos.all_revision_ids(repos.get_layout())))
-
- def test_follow_history_empty_branch(self):
- repos_url = self.make_client("a", "dc")
- self.build_tree({'dc/trunk/afile': "data", "dc/branches": None})
- self.client_add("dc/trunk")
- self.client_add("dc/branches")
- self.client_commit("dc", "My Message")
- repos = Repository.open(repos_url)
- set_branching_scheme(repos, TrunkBranchingScheme())
- self.assertEqual([repos.generate_revision_id(1, 'trunk', repos.get_mapping())],
- list(repos.all_revision_ids(repos.get_layout())))
-
- def test_follow_history_follow(self):
- repos_url = self.make_client("a", "dc")
- self.build_tree({'dc/trunk/afile': "data", "dc/branches": None})
- self.client_add("dc/trunk")
- self.client_add("dc/branches")
- self.client_commit("dc", "My Message")
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
- self.client_copy("dc/trunk", "dc/branches/abranch")
- self.client_commit("dc", "Create branch")
+"""Subversion rpeository library tests."""
- repos = Repository.open(repos_url)
- set_branching_scheme(repos, TrunkBranchingScheme())
+import os
+from bzrlib.plugins.svn import repos
+from bzrlib.tests import TestCaseInTempDir
- items = list(repos.all_revision_ids(repos.get_layout()))
- self.assertEqual([repos.generate_revision_id(1, 'trunk', repos.get_mapping()),
- repos.generate_revision_id(2, 'branches/abranch', repos.get_mapping())
- ], items)
-
- def test_branch_log_specific(self):
- repos_url = self.make_client("a", "dc")
- self.build_tree({
- 'dc/branches': None,
- 'dc/branches/brancha': None,
- 'dc/branches/branchab': None,
- 'dc/branches/brancha/data': "data",
- "dc/branches/branchab/data":"data"})
- self.client_add("dc/branches")
- self.client_commit("dc", "My Message")
-
- repos = Repository.open(repos_url)
- set_branching_scheme(repos, TrunkBranchingScheme())
-
- self.assertEqual(1, len(list(repos.iter_reverse_branch_changes("branches/brancha",
- 1, TrunkBranchingScheme()))))
-
- def test_branch_log_specific_ignore(self):
- repos_url = self.make_client("a", "dc")
- self.build_tree({'dc/branches': None})
- self.client_add("dc/branches")
- self.build_tree({
- 'dc/branches/brancha': None,
- 'dc/branches/branchab': None,
- 'dc/branches/brancha/data': "data",
- "dc/branches/branchab/data":"data"})
- self.client_add("dc/branches/brancha")
- self.client_commit("dc", "My Message")
-
- self.client_add("dc/branches/branchab")
- self.client_commit("dc", "My Message2")
-
- repos = Repository.open(repos_url)
- set_branching_scheme(repos, TrunkBranchingScheme())
-
- self.assertEqual(1, len(list(repos.iter_reverse_branch_changes("branches/brancha",
- 2, TrunkBranchingScheme()))))
-
- def test_find_branches(self):
- repos_url = self.make_client("a", "dc")
- self.build_tree({
- 'dc/branches/brancha': None,
- 'dc/branches/branchab': None,
- 'dc/branches/brancha/data': "data",
- "dc/branches/branchab/data":"data"})
- self.client_add("dc/branches")
- self.client_commit("dc", "My Message")
- repos = Repository.open(repos_url)
- set_branching_scheme(repos, TrunkBranchingScheme())
- branches = repos.find_branches()
- self.assertEquals(2, len(branches))
- self.assertEquals(urlutils.join(repos.base, "branches/brancha"),
- branches[1].base)
- self.assertEquals(urlutils.join(repos.base, "branches/branchab"),
- branches[0].base)
-
- def test_find_branchpaths_moved(self):
- repos_url = self.make_client("a", "dc")
- self.build_tree({
- 'dc/tmp/branches/brancha': None,
- 'dc/tmp/branches/branchab': None,
- 'dc/tmp/branches/brancha/data': "data",
- "dc/tmp/branches/branchab/data":"data"})
- self.client_add("dc/tmp")
- self.client_commit("dc", "My Message")
- self.client_copy("dc/tmp/branches", "dc/tags")
- self.client_commit("dc", "My Message 2")
-
- repos = Repository.open(repos_url)
- set_branching_scheme(repos, TrunkBranchingScheme())
-
- self.assertEqual([("tags/branchab", 2, True),
- ("tags/brancha", 2, True)],
- list(repos.find_branchpaths(TrunkBranchingScheme(), to_revnum=2)))
-
- def test_find_branchpaths_start_revno(self):
- repos_url = self.make_client("a", "dc")
- self.build_tree({'dc/branches/brancha': None})
- self.client_add("dc/branches")
- self.client_commit("dc", "My Message")
- self.build_tree({'dc/branches/branchb': None})
- self.client_add("dc/branches/branchb")
- self.client_commit("dc", "My Message 2")
-
- repos = Repository.open(repos_url)
- set_branching_scheme(repos, TrunkBranchingScheme())
-
- self.assertEqual([("branches/branchb", 2, True)],
- list(repos.find_branchpaths(TrunkBranchingScheme(), from_revnum=2,
- to_revnum=2)))
-
- def test_find_branchpaths_file_moved_from_nobranch(self):
- repos_url = self.make_client("a", "dc")
- self.build_tree({
- 'dc/tmp/trunk': None,
- 'dc/bla/somefile': "contents"})
- self.client_add("dc/tmp")
- self.client_add("dc/bla")
- self.client_commit("dc", "My Message")
- self.client_copy("dc/bla", "dc/tmp/branches")
- self.client_delete("dc/tmp/branches/somefile")
- self.client_commit("dc", "My Message 2")
-
- Repository.open(repos_url).find_branchpaths(TrunkBranchingScheme(2))
-
- def test_find_branchpaths_deleted_from_nobranch(self):
- repos_url = self.make_client("a", "dc")
- self.build_tree({
- 'dc/tmp/trunk': None,
- 'dc/bla/somefile': "contents"})
- self.client_add("dc/tmp")
- self.client_add("dc/bla")
- self.client_commit("dc", "My Message")
- self.client_copy("dc/bla", "dc/tmp/branches")
- self.client_delete("dc/tmp/branches/somefile")
- self.client_commit("dc", "My Message 2")
-
- Repository.open(repos_url).find_branchpaths(TrunkBranchingScheme(1))
-
- def test_find_branchpaths_moved_nobranch(self):
- repos_url = self.make_client("a", "dc")
- self.build_tree({
- 'dc/tmp/nested/foobar': None,
- 'dc/tmp/nested/branches/brancha': None,
- 'dc/tmp/nested/branches/branchab': None,
- 'dc/tmp/nested/branches/brancha/data': "data",
- "dc/tmp/nested/branches/branchab/data":"data"})
- self.client_add("dc/tmp")
- self.client_commit("dc", "My Message")
- self.client_copy("dc/tmp/nested", "dc/t2")
- self.client_commit("dc", "My Message 2")
-
- repos = Repository.open(repos_url)
- set_branching_scheme(repos, TrunkBranchingScheme(1))
-
- self.assertEqual([("t2/branches/brancha", 2, True),
- ("t2/branches/branchab", 2, True)],
- list(repos.find_branchpaths(TrunkBranchingScheme(1), to_revnum=2)))
-
- def test_find_branchpaths_no(self):
- repos_url = self.make_repository("a")
-
- repos = Repository.open(repos_url)
- set_branching_scheme(repos, NoBranchingScheme())
-
- self.assertEqual([("", 0, True)],
- list(repos.find_branchpaths(NoBranchingScheme(), to_revnum=0)))
-
- def test_find_branchpaths_no_later(self):
- repos_url = self.make_repository("a")
-
- repos = Repository.open(repos_url)
- set_branching_scheme(repos, NoBranchingScheme())
-
- self.assertEqual([("", 0, True)],
- list(repos.find_branchpaths(NoBranchingScheme(), to_revnum=0)))
-
- def test_find_branchpaths_trunk_empty(self):
- repos_url = self.make_repository("a")
-
- repos = Repository.open(repos_url)
- set_branching_scheme(repos, TrunkBranchingScheme())
-
- self.assertEqual([],
- list(repos.find_branchpaths(TrunkBranchingScheme(), to_revnum=0)))
-
- def test_find_branchpaths_trunk_one(self):
- repos_url = self.make_client("a", "dc")
-
- repos = Repository.open(repos_url)
- set_branching_scheme(repos, TrunkBranchingScheme())
-
- self.build_tree({'dc/trunk/foo': "data"})
- self.client_add("dc/trunk")
- self.client_commit("dc", "My Message")
-
- self.assertEqual([("trunk", 1, True)],
- list(repos.find_branchpaths(TrunkBranchingScheme(), to_revnum=1)))
-
- def test_find_branchpaths_removed(self):
- repos_url = self.make_client("a", "dc")
-
- repos = Repository.open(repos_url)
- set_branching_scheme(repos, TrunkBranchingScheme())
-
- self.build_tree({'dc/trunk/foo': "data"})
- self.client_add("dc/trunk")
- self.client_commit("dc", "My Message")
-
- self.client_delete("dc/trunk")
- self.client_commit("dc", "remove")
-
- self.assertEqual([("trunk", 1, True)],
- list(repos.find_branchpaths(TrunkBranchingScheme(), to_revnum=1)))
- self.assertEqual([("trunk", 1, False)],
- list(repos.find_branchpaths(TrunkBranchingScheme(), to_revnum=2)))
-
- def test_url(self):
- """ Test repository URL is kept """
- bzrdir = self.make_local_bzrdir('b', 'bc')
- self.assertTrue(isinstance(bzrdir, BzrDir))
-
- def test_uuid(self):
- """ Test UUID is retrieved correctly """
- bzrdir = self.make_local_bzrdir('c', 'cc')
- self.assertTrue(isinstance(bzrdir, BzrDir))
- repository = bzrdir._find_repository()
- fs = self.open_fs('c')
- self.assertEqual(svn.fs.get_uuid(fs), repository.uuid)
-
- def test_get_inventory_weave(self):
- bzrdir = self.make_client_and_bzrdir('d', 'dc')
- repository = bzrdir.find_repository()
- self.assertRaises(NotImplementedError, repository.get_inventory_weave)
-
- def test_has_revision(self):
- bzrdir = self.make_client_and_bzrdir('d', 'dc')
- repository = bzrdir.find_repository()
- self.build_tree({'dc/foo': "data"})
- self.client_add("dc/foo")
- self.client_commit("dc", "My Message")
- self.assertTrue(repository.has_revision(
- repository.generate_revision_id(1, "", repository.get_mapping())))
- self.assertFalse(repository.has_revision("some-other-revision"))
-
- def test_has_revision_none(self):
- bzrdir = self.make_client_and_bzrdir('d', 'dc')
- repository = bzrdir.find_repository()
- self.assertTrue(repository.has_revision(None))
-
- def test_has_revision_future(self):
- bzrdir = self.make_client_and_bzrdir('d', 'dc')
- repository = bzrdir.find_repository()
- self.assertFalse(repository.has_revision(
- repository.get_mapping().generate_revision_id(repository.uuid, 5, "")))
-
- def test_get_parent_map(self):
- repos_url = self.make_client('d', 'dc')
- self.build_tree({'dc/foo': "data"})
- self.client_add("dc/foo")
- self.client_commit("dc", "My Message")
- self.build_tree({'dc/foo': "data2"})
- self.client_commit("dc", "Second Message")
- repository = Repository.open("svn+%s" % repos_url)
- mapping = repository.get_mapping()
- revid = repository.generate_revision_id(0, "", mapping)
- self.assertEqual({revid: (NULL_REVISION,)}, repository.get_parent_map([revid]))
- revid = repository.generate_revision_id(1, "", mapping)
- self.assertEqual({revid: (repository.generate_revision_id(0, "", mapping),)}, repository.get_parent_map([revid]))
- revid = repository.generate_revision_id(2, "", mapping)
- self.assertEqual({revid: (repository.generate_revision_id(1, "", mapping),)},
- repository.get_parent_map([revid]))
- self.assertEqual({}, repository.get_parent_map(["notexisting"]))
-
- def test_revision_fileidmap(self):
- repos_url = self.make_client('d', 'dc')
- self.build_tree({'dc/foo': "data"})
- self.client_add("dc/foo")
- self.client_set_prop("dc", "bzr:revision-info", "")
- self.client_set_prop("dc", "bzr:file-ids", "foo\tsomeid\n")
- self.client_commit("dc", "My Message")
- repository = Repository.open("svn+%s" % repos_url)
- tree = repository.revision_tree(Branch.open(repos_url).last_revision())
- self.assertEqual("someid", tree.inventory.path2id("foo"))
- self.assertFalse("1@%s::foo" % repository.uuid in tree.inventory)
-
- def test_revision_ghost_parents(self):
- repos_url = self.make_client('d', 'dc')
- self.build_tree({'dc/foo': "data"})
- self.client_add("dc/foo")
- self.client_commit("dc", "My Message")
- self.client_update("dc")
- self.build_tree({'dc/foo': "data2"})
- self.client_set_prop("dc", "bzr:ancestry:v3-none", "ghostparent\n")
- self.client_commit("dc", "Second Message")
- repository = Repository.open("svn+%s" % repos_url)
- mapping = repository.get_mapping()
- self.assertEqual((),
- repository.get_revision(
- repository.generate_revision_id(0, "", mapping)).parent_ids)
- self.assertEqual((repository.generate_revision_id(0, "", mapping),),
- repository.get_revision(
- repository.generate_revision_id(1, "", mapping)).parent_ids)
- self.assertEqual((repository.generate_revision_id(1, "", mapping),
- "ghostparent"),
- repository.get_revision(
- repository.generate_revision_id(2, "", mapping)).parent_ids)
-
- def test_revision_svk_parent(self):
- repos_url = self.make_client('d', 'dc')
- self.build_tree({'dc/trunk/foo': "data", 'dc/branches/foo': None})
- self.client_add("dc/trunk")
- self.client_add("dc/branches")
- self.client_commit("dc", "My Message")
- self.client_update("dc")
- self.build_tree({'dc/trunk/foo': "data2"})
- repository = Repository.open("svn+%s" % repos_url)
- set_branching_scheme(repository, TrunkBranchingScheme())
- self.client_set_prop("dc/trunk", "svk:merge",
- "%s:/branches/foo:1\n" % repository.uuid)
- self.client_commit("dc", "Second Message")
- mapping = repository.get_mapping()
- self.assertEqual((repository.generate_revision_id(1, "trunk", mapping),
- repository.generate_revision_id(1, "branches/foo", mapping)),
- repository.get_revision(
- repository.generate_revision_id(2, "trunk", mapping)).parent_ids)
-
- def test_get_revision(self):
- repos_url = self.make_client('d', 'dc')
- repository = Repository.open("svn+%s" % repos_url)
- self.assertRaises(NoSuchRevision, repository.get_revision,
- "nonexisting")
- self.build_tree({'dc/foo': "data"})
- self.client_add("dc/foo")
- self.client_commit("dc", "My Message")
- self.client_update("dc")
- self.build_tree({'dc/foo': "data2"})
- (num, date, author) = self.client_commit("dc", "Second Message")
- repository = Repository.open("svn+%s" % repos_url)
- mapping = repository.get_mapping()
- rev = repository.get_revision(
- repository.generate_revision_id(2, "", mapping))
- self.assertEqual((repository.generate_revision_id(1, "", mapping),),
- rev.parent_ids)
- self.assertEqual(rev.revision_id,
- repository.generate_revision_id(2, "", mapping))
- self.assertEqual(author, rev.committer)
- self.assertIsInstance(rev.properties, dict)
-
- def test_get_revision_id_overriden(self):
- repos_url = self.make_client('d', 'dc')
- repository = Repository.open("svn+%s" % repos_url)
- self.assertRaises(NoSuchRevision, repository.get_revision, "nonexisting")
- self.build_tree({'dc/foo': "data"})
- self.client_add("dc/foo")
- self.client_commit("dc", "My Message")
- self.build_tree({'dc/foo': "data2"})
- self.client_set_prop("dc", "bzr:revision-id:v3-none",
- "3 myrevid\n")
- self.client_update("dc")
- (num, date, author) = self.client_commit("dc", "Second Message")
- repository = Repository.open("svn+%s" % repos_url)
- mapping = repository.get_mapping()
- revid = mapping.generate_revision_id(repository.uuid, 2, "")
- rev = repository.get_revision("myrevid")
- self.assertEqual((repository.generate_revision_id(1, "", mapping),),
- rev.parent_ids)
- self.assertEqual(rev.revision_id,
- repository.generate_revision_id(2, "", mapping))
- self.assertEqual(author, rev.committer)
- self.assertIsInstance(rev.properties, dict)
-
- def test_get_revision_zero(self):
- repos_url = self.make_client('d', 'dc')
- repository = Repository.open("svn+%s" % repos_url)
- mapping = repository.get_mapping()
- rev = repository.get_revision(
- repository.generate_revision_id(0, "", mapping))
- self.assertEqual(repository.generate_revision_id(0, "", mapping),
- rev.revision_id)
- self.assertEqual("", rev.committer)
- self.assertEqual({}, rev.properties)
- self.assertEqual(None, rev.timezone)
-
- def test_store_branching_scheme(self):
- repos_url = self.make_client('d', 'dc')
- repository = Repository.open(repos_url)
- set_branching_scheme(repository, TrunkBranchingScheme(42))
- repository = Repository.open(repos_url)
- self.assertEquals("trunk42", str(repository.get_mapping().scheme))
-
- def test_get_ancestry(self):
- repos_url = self.make_client('d', 'dc')
- repository = Repository.open("svn+%s" % repos_url)
- self.assertRaises(NoSuchRevision, repository.get_revision, "nonexisting")
- self.build_tree({'dc/foo': "data"})
- self.client_add("dc/foo")
- self.client_commit("dc", "My Message")
- self.client_update("dc")
- self.build_tree({'dc/foo': "data2"})
- self.client_commit("dc", "Second Message")
- self.client_update("dc")
- self.build_tree({'dc/foo': "data3"})
- self.client_commit("dc", "Third Message")
- self.client_update("dc")
- repository = Repository.open("svn+%s" % repos_url)
- mapping = repository.get_mapping()
- self.assertEqual([None,
- repository.generate_revision_id(0, "", mapping),
- repository.generate_revision_id(1, "", mapping),
- repository.generate_revision_id(2, "", mapping),
- repository.generate_revision_id(3, "", mapping)],
- repository.get_ancestry(
- repository.generate_revision_id(3, "", mapping)))
- self.assertEqual([None,
- repository.generate_revision_id(0, "", mapping),
- repository.generate_revision_id(1, "", mapping),
- repository.generate_revision_id(2, "", mapping)],
- repository.get_ancestry(
- repository.generate_revision_id(2, "", mapping)))
- self.assertEqual([None,
- repository.generate_revision_id(0, "", mapping),
- repository.generate_revision_id(1, "", mapping)],
- repository.get_ancestry(
- repository.generate_revision_id(1, "", mapping)))
- self.assertEqual([None, repository.generate_revision_id(0, "", mapping)],
- repository.get_ancestry(
- repository.generate_revision_id(0, "", mapping)))
- self.assertEqual([None], repository.get_ancestry(NULL_REVISION))
-
- def test_get_ancestry2(self):
- repos_url = self.make_client('d', 'dc')
- self.build_tree({'dc/foo': "data"})
- self.client_add("dc/foo")
- self.client_commit("dc", "My Message")
- self.build_tree({'dc/foo': "data2"})
- self.client_commit("dc", "Second Message")
- repository = Repository.open("svn+%s" % repos_url)
- mapping = repository.get_mapping()
- self.assertEqual([None, repository.generate_revision_id(0, "", mapping)],
- repository.get_ancestry(
- repository.generate_revision_id(0, "", mapping)))
- self.assertEqual([None, repository.generate_revision_id(0, "", mapping),
- repository.generate_revision_id(1, "", mapping)],
- repository.get_ancestry(
- repository.generate_revision_id(1, "", mapping)))
- self.assertEqual([None,
- repository.generate_revision_id(0, "", mapping),
- repository.generate_revision_id(1, "", mapping),
- repository.generate_revision_id(2, "", mapping)],
- repository.get_ancestry(
- repository.generate_revision_id(2, "", mapping)))
-
- def test_get_ancestry_merged(self):
- repos_url = self.make_client('d', 'dc')
- self.build_tree({'dc/foo': "data"})
- self.client_add("dc/foo")
- self.client_commit("dc", "My Message")
- self.client_update("dc")
- self.client_set_prop("dc", "bzr:ancestry:v3-none", "a-parent\n")
- self.build_tree({'dc/foo': "data2"})
- self.client_commit("dc", "Second Message")
- repository = Repository.open("svn+%s" % repos_url)
- mapping = repository.get_mapping()
- self.assertEqual([None, repository.generate_revision_id(0, "", mapping)],
- repository.get_ancestry(
- repository.generate_revision_id(0, "", mapping)))
- self.assertEqual([None, repository.generate_revision_id(0, "", mapping),
- repository.generate_revision_id(1, "", mapping)],
- repository.get_ancestry(
- repository.generate_revision_id(1, "", mapping)))
- self.assertEqual([None,
- repository.generate_revision_id(0, "", mapping), "a-parent",
- repository.generate_revision_id(1, "", mapping),
- repository.generate_revision_id(2, "", mapping)],
- repository.get_ancestry(
- repository.generate_revision_id(2, "", mapping)))
-
- def test_get_inventory(self):
- repos_url = self.make_client('d', 'dc')
- repository = Repository.open("svn+%s" % repos_url)
- self.assertRaises(NoSuchRevision, repository.get_inventory,
- "nonexisting")
- self.build_tree({'dc/foo': "data", 'dc/blah': "other data"})
- self.client_add("dc/foo")
- self.client_add("dc/blah")
- self.client_commit("dc", "My Message") #1
- self.client_update("dc")
- self.build_tree({'dc/foo': "data2", "dc/bar/foo": "data3"})
- self.client_add("dc/bar")
- self.client_commit("dc", "Second Message") #2
- self.client_update("dc")
- self.build_tree({'dc/foo': "data3"})
- self.client_commit("dc", "Third Message") #3
- self.client_update("dc")
- repository = Repository.open("svn+%s" % repos_url)
- mapping = repository.get_mapping()
- inv = repository.get_inventory(
- repository.generate_revision_id(1, "", mapping))
- self.assertIsInstance(inv, Inventory)
- self.assertIsInstance(inv.path2id("foo"), basestring)
- inv = repository.get_inventory(
- repository.generate_revision_id(2, "", mapping))
- self.assertEqual(repository.generate_revision_id(2, "", mapping),
- inv[inv.path2id("foo")].revision)
- self.assertEqual(repository.generate_revision_id(1, "", mapping),
- inv[inv.path2id("blah")].revision)
- self.assertIsInstance(inv, Inventory)
- self.assertIsInstance(inv.path2id("foo"), basestring)
- self.assertIsInstance(inv.path2id("bar"), basestring)
- self.assertIsInstance(inv.path2id("bar/foo"), basestring)
-
- def test_generate_revision_id(self):
- repos_url = self.make_client('d', 'dc')
- self.build_tree({'dc/bla/bloe': None})
- self.client_add("dc/bla")
- self.client_commit("dc", "bla")
- repository = Repository.open("svn+%s" % repos_url)
- mapping = repository.get_mapping()
- self.assertEqual(
- mapping.generate_revision_id(repository.uuid, 1, "bla/bloe"),
- repository.generate_revision_id(1, "bla/bloe", mapping))
-
- def test_generate_revision_id_zero(self):
- repos_url = self.make_client('d', 'dc')
- repository = Repository.open("svn+%s" % repos_url)
- mapping = repository.get_mapping()
- self.assertEqual(mapping.generate_revision_id(repository.uuid, 0, ""),
- repository.generate_revision_id(0, "", mapping))
-
- def test_lookup_revision_id(self):
- repos_url = self.make_client('d', 'dc')
- self.build_tree({'dc/bloe': None})
- self.client_add("dc/bloe")
- self.client_commit("dc", "foobar")
- repository = Repository.open("svn+%s" % repos_url)
- self.assertRaises(NoSuchRevision, repository.lookup_revision_id,
- "nonexisting")
- mapping = repository.get_mapping()
- self.assertEqual(("bloe", 1),
- repository.lookup_revision_id(
- repository.generate_revision_id(1, "bloe", mapping))[:2])
-
- def test_lookup_revision_id_overridden(self):
- repos_url = self.make_client('d', 'dc')
- self.build_tree({'dc/bloe': None})
- self.client_add("dc/bloe")
- self.client_set_prop("dc", SVN_PROP_BZR_REVISION_ID+"none", "2 myid\n")
- self.client_commit("dc", "foobar")
- repository = Repository.open("svn+%s" % repos_url)
- mapping = repository.get_mapping()
- self.assertEqual(("", 1), repository.lookup_revision_id(
- mapping.generate_revision_id(repository.uuid, 1, ""))[:2])
- self.assertEqual(("", 1),
- repository.lookup_revision_id("myid")[:2])
-
- def test_lookup_revision_id_overridden_invalid(self):
- repos_url = self.make_client('d', 'dc')
- self.build_tree({'dc/bloe': None})
- self.client_add("dc/bloe")
- self.client_set_prop("dc", SVN_PROP_BZR_REVISION_ID+"none",
- "corrupt-entry\n")
- self.client_commit("dc", "foobar")
- repository = Repository.open("svn+%s" % repos_url)
- mapping = repository.get_mapping()
- self.assertEqual(("", 1), repository.lookup_revision_id(
- mapping.generate_revision_id(repository.uuid, 1, ""))[:2])
- self.assertRaises(NoSuchRevision, repository.lookup_revision_id,
- "corrupt-entry")
-
- def test_lookup_revision_id_overridden_invalid_dup(self):
- repos_url = self.make_client('d', 'dc')
- self.build_tree({'dc/bloe': None})
- self.client_add("dc/bloe")
- self.client_set_prop("dc", SVN_PROP_BZR_REVISION_ID+"none",
- "corrupt-entry\n")
- self.client_commit("dc", "foobar")
- self.build_tree({'dc/bla': None})
- self.client_add("dc/bla")
- self.client_set_prop("dc", SVN_PROP_BZR_REVISION_ID+"none",
- "corrupt-entry\n2 corrupt-entry\n")
- self.client_commit("dc", "foobar")
- repository = Repository.open("svn+%s" % repos_url)
- mapping = repository.get_mapping()
- self.assertEqual(("", 2), repository.lookup_revision_id(
- mapping.generate_revision_id(repository.uuid, 2, ""))[:2])
- self.assertEqual(("", 1), repository.lookup_revision_id(
- mapping.generate_revision_id(repository.uuid, 1, ""))[:2])
- self.assertEqual(("", 2), repository.lookup_revision_id(
- "corrupt-entry")[:2])
-
- def test_lookup_revision_id_overridden_not_found(self):
- """Make sure a revision id that is looked up but doesn't exist
- doesn't accidently end up in the revid cache."""
- repos_url = self.make_client('d', 'dc')
- self.build_tree({'dc/bloe': None})
- self.client_add("dc/bloe")
- self.client_set_prop("dc", SVN_PROP_BZR_REVISION_ID+"none", "2 myid\n")
- self.client_commit("dc", "foobar")
- repository = Repository.open("svn+%s" % repos_url)
- self.assertRaises(NoSuchRevision,
- repository.lookup_revision_id, "foobar")
-
- def test_set_branching_scheme_property(self):
- repos_url = self.make_client('d', 'dc')
- self.client_set_prop("dc", SVN_PROP_BZR_BRANCHING_SCHEME,
- "trunk\nbranches/*\nbranches/tmp/*")
- self.client_commit("dc", "set scheme")
- repository = Repository.open("svn+%s" % repos_url)
- self.assertEquals(ListBranchingScheme(["trunk", "branches/*", "branches/tmp/*"]).branch_list,
- repository.get_mapping().scheme.branch_list)
-
- def test_set_property_scheme(self):
- repos_url = self.make_client('d', 'dc')
- repos = Repository.open(repos_url)
- set_property_scheme(repos, ListBranchingScheme(["bla/*"]))
- self.client_update("dc")
- self.assertEquals("bla/*\n",
- self.client_get_prop("dc", SVN_PROP_BZR_BRANCHING_SCHEME))
- self.assertEquals("Updating branching scheme for Bazaar.",
- self.client_log("dc", 1, 1)[1][3])
-
- def test_lookup_revision_id_invalid_uuid(self):
- repos_url = self.make_client('d', 'dc')
- repository = Repository.open("svn+%s" % repos_url)
- mapping = repository.get_mapping()
- self.assertRaises(NoSuchRevision,
- repository.lookup_revision_id,
- mapping.generate_revision_id("invaliduuid", 0, ""))
-
- def test_check(self):
- repos_url = self.make_client('d', 'dc')
- self.build_tree({'dc/foo': "data"})
- self.client_add("dc/foo")
- self.client_commit("dc", "My Message")
- repository = Repository.open("svn+%s" % repos_url)
- mapping = repository.get_mapping()
- repository.check([
- repository.generate_revision_id(0, "", mapping),
- repository.generate_revision_id(1, "", mapping)])
-
- def test_copy_contents_into(self):
- repos_url = self.make_client('d', 'dc')
- self.build_tree({'dc/foo/bla': "data"})
- self.client_add("dc/foo")
- self.client_commit("dc", "My Message")
- self.build_tree({'dc/foo/blo': "data2", "dc/bar/foo": "data3", 'dc/foo/bla': "data"})
- self.client_add("dc/foo/blo")
- self.client_add("dc/bar")
- self.client_commit("dc", "Second Message")
- repository = Repository.open("svn+%s" % repos_url)
- mapping = repository.get_mapping()
-
- to_bzrdir = BzrDir.create("e", format.get_rich_root_format())
- to_repos = to_bzrdir.create_repository()
-
- repository.copy_content_into(to_repos,
- repository.generate_revision_id(2, "", mapping))
-
- self.assertTrue(repository.has_revision(
- repository.generate_revision_id(2, "", mapping)))
- self.assertTrue(repository.has_revision(
- repository.generate_revision_id(1, "", mapping)))
-
- def test_is_shared(self):
- repos_url = self.make_client('d', 'dc')
- self.build_tree({'dc/foo/bla': "data"})
- self.client_add("dc/foo")
- self.client_commit("dc", "My Message")
- repository = Repository.open("svn+%s" % repos_url)
- self.assertTrue(repository.is_shared())
-
- def test_fetch_property_change_only_trunk(self):
- repos_url = self.make_client('d', 'dc')
- self.build_tree({'dc/trunk/bla': "data"})
- self.client_add("dc/trunk")
- self.client_commit("dc", "My Message")
- self.client_set_prop("dc/trunk", "some:property", "some data\n")
- self.client_commit("dc", "My 3")
- self.client_set_prop("dc/trunk", "some2:property", "some data\n")
- self.client_commit("dc", "My 2")
- self.client_set_prop("dc/trunk", "some:property", "some other data\n")
- self.client_commit("dc", "My 4")
- oldrepos = Repository.open("svn+"+repos_url)
- self.assertEquals([('trunk', {'trunk': (u'M', None, -1)}, 3),
- ('trunk', {'trunk': (u'M', None, -1)}, 2),
- ('trunk', {'trunk/bla': (u'A', None, -1), 'trunk': (u'A', None, -1)}, 1)],
- [(l.branch_path, l.paths, l.revnum) for l in oldrepos.iter_reverse_branch_changes("trunk", 3, TrunkBranchingScheme())])
-
- def test_control_code_msg(self):
- repos_url = self.make_client('d', 'dc')
-
- self.build_tree({'dc/trunk': None})
- self.client_add("dc/trunk")
- self.client_commit("dc", "\x24")
-
- self.build_tree({'dc/trunk/hosts': 'hej2'})
- self.client_add("dc/trunk/hosts")
- self.client_commit("dc", "bla\xfcbla") #2
-
- self.build_tree({'dc/trunk/hosts': 'hej3'})
- self.client_commit("dc", "a\x0cb") #3
-
- self.build_tree({'dc/branches/foobranch/file': 'foohosts'})
- self.client_add("dc/branches")
- self.client_commit("dc", "foohosts") #4
-
- oldrepos = Repository.open("svn+"+repos_url)
- set_branching_scheme(oldrepos, TrunkBranchingScheme())
- dir = BzrDir.create("f",format=format.get_rich_root_format())
- newrepos = dir.create_repository()
- oldrepos.copy_content_into(newrepos)
-
- mapping = oldrepos.get_mapping()
-
- self.assertTrue(newrepos.has_revision(
- oldrepos.generate_revision_id(1, "trunk", mapping)))
- self.assertTrue(newrepos.has_revision(
- oldrepos.generate_revision_id(2, "trunk", mapping)))
- self.assertTrue(newrepos.has_revision(
- oldrepos.generate_revision_id(3, "trunk", mapping)))
- self.assertTrue(newrepos.has_revision(
- oldrepos.generate_revision_id(4, "branches/foobranch", mapping)))
- self.assertFalse(newrepos.has_revision(
- oldrepos.generate_revision_id(4, "trunk", mapping)))
- self.assertFalse(newrepos.has_revision(
- oldrepos.generate_revision_id(2, "", mapping)))
-
- rev = newrepos.get_revision(oldrepos.generate_revision_id(1, "trunk", mapping))
- self.assertEqual("$", rev.message)
-
- rev = newrepos.get_revision(
- oldrepos.generate_revision_id(2, "trunk", mapping))
- self.assertEqual('bla\xc3\xbcbla', rev.message.encode("utf-8"))
-
- rev = newrepos.get_revision(oldrepos.generate_revision_id(3, "trunk", mapping))
- self.assertEqual(u"a\\x0cb", rev.message)
-
- def test_set_branching_scheme(self):
- repos_url = self.make_client('d', 'dc')
- repos = Repository.open(repos_url)
- set_branching_scheme(repos, NoBranchingScheme())
-
- def testlhs_revision_parent_none(self):
- repos_url = self.make_client('d', 'dc')
- repos = Repository.open(repos_url)
- set_branching_scheme(repos, NoBranchingScheme())
- self.assertEquals(NULL_REVISION, repos.lhs_revision_parent("", 0, NoBranchingScheme()))
-
- def testlhs_revision_parent_first(self):
- repos_url = self.make_client('d', 'dc')
- repos = Repository.open(repos_url)
- set_branching_scheme(repos, NoBranchingScheme())
- self.build_tree({'dc/adir/afile': "data"})
- self.client_add("dc/adir")
- self.client_commit("dc", "Initial commit")
- mapping = repos.get_mapping()
- self.assertEquals(repos.generate_revision_id(0, "", mapping), \
- repos.lhs_revision_parent("", 1, mapping))
-
- def testlhs_revision_parent_simple(self):
- repos_url = self.make_client('d', 'dc')
- self.build_tree({'dc/trunk/adir/afile': "data",
- 'dc/trunk/adir/stationary': None,
- 'dc/branches/abranch': None})
- self.client_add("dc/trunk")
- self.client_add("dc/branches")
- self.client_commit("dc", "Initial commit")
- self.build_tree({'dc/trunk/adir/afile': "bla"})
- self.client_commit("dc", "Incremental commit")
- repos = Repository.open(repos_url)
- set_branching_scheme(repos, TrunkBranchingScheme())
- mapping = repos.get_mapping()
- self.assertEquals(repos.generate_revision_id(1, "trunk", mapping), \
- repos.lhs_revision_parent("trunk", 2, mapping))
-
- def testlhs_revision_parent_copied(self):
- repos_url = self.make_client('d', 'dc')
- self.build_tree({'dc/py/trunk/adir/afile': "data",
- 'dc/py/trunk/adir/stationary': None})
- self.client_add("dc/py")
- self.client_commit("dc", "Initial commit")
- self.client_copy("dc/py", "dc/de")
- self.client_commit("dc", "Incremental commit")
- self.build_tree({'dc/de/trunk/adir/afile': "bla"})
- self.client_commit("dc", "Change de")
- repos = Repository.open(repos_url)
- set_branching_scheme(repos, TrunkBranchingScheme(1))
- mapping = repos.get_mapping()
- self.assertEquals(repos.generate_revision_id(1, "py/trunk", mapping), \
- repos.lhs_revision_parent("de/trunk", 3, mapping))
-
- def test_mainline_revision_copied(self):
- repos_url = self.make_client('d', 'dc')
- self.build_tree({'dc/py/trunk/adir/afile': "data",
- 'dc/py/trunk/adir/stationary': None})
- self.client_add("dc/py")
- self.client_commit("dc", "Initial commit")
- self.build_tree({'dc/de':None})
- self.client_add("dc/de")
- self.client_copy("dc/py/trunk", "dc/de/trunk")
- self.client_commit("dc", "Copy trunk")
- repos = Repository.open(repos_url)
- set_branching_scheme(repos, TrunkBranchingScheme(1))
- mapping = repos.get_mapping()
- self.assertEquals(repos.generate_revision_id(1, "py/trunk", mapping), \
- repos.lhs_revision_parent("de/trunk", 2, mapping))
-
- def test_mainline_revision_nested_deleted(self):
- repos_url = self.make_client('d', 'dc')
- self.build_tree({'dc/py/trunk/adir/afile': "data",
- 'dc/py/trunk/adir/stationary': None})
- self.client_add("dc/py")
- self.client_commit("dc", "Initial commit")
- self.client_copy("dc/py", "dc/de")
- self.client_commit("dc", "Incremental commit")
- self.client_delete("dc/de/trunk/adir")
- self.client_commit("dc", "Another incremental commit")
- repos = Repository.open(repos_url)
- set_branching_scheme(repos, TrunkBranchingScheme(1))
- mapping = repos.get_mapping()
- self.assertEquals(repos.generate_revision_id(1, "py/trunk", mapping), \
- repos.lhs_revision_parent("de/trunk", 3, mapping))
-
- def test_mainline_revision_missing(self):
- repos_url = self.make_client('d', 'dc')
- repos = Repository.open(repos_url)
- self.build_tree({'dc/py/trunk/adir/afile': "data",
- 'dc/py/trunk/adir/stationary': None})
- self.client_add("dc/py")
- self.client_commit("dc", "Initial commit")
- self.assertRaises(NoSuchRevision,
- lambda: repos.lhs_revision_parent("trunk", 2, repos.get_mapping()))
-
-
-class TestSvnRevisionTree(TestCaseWithSubversionRepository):
- def setUp(self):
- super(TestSvnRevisionTree, self).setUp()
- repos_url = self.make_client('d', 'dc')
- self.build_tree({'dc/foo/bla': "data"})
- self.client_add("dc/foo")
- self.client_commit("dc", "My Message")
- self.repos = Repository.open(repos_url)
- mapping = self.repos.get_mapping()
- self.inventory = self.repos.get_inventory(
- self.repos.generate_revision_id(1, "", mapping))
- self.tree = self.repos.revision_tree(
- self.repos.generate_revision_id(1, "", mapping))
-
- def test_inventory(self):
- self.assertIsInstance(self.tree.inventory, Inventory)
- self.assertEqual(self.inventory, self.tree.inventory)
-
- def test_get_parent_ids(self):
- mapping = self.repos.get_mapping()
- self.assertEqual((self.repos.generate_revision_id(0, "", mapping),), self.tree.get_parent_ids())
-
- def test_get_parent_ids_zero(self):
- mapping = self.repos.get_mapping()
- tree = self.repos.revision_tree(
- self.repos.generate_revision_id(0, "", mapping))
- self.assertEqual((), tree.get_parent_ids())
-
- def test_get_revision_id(self):
- mapping = self.repos.get_mapping()
- self.assertEqual(self.repos.generate_revision_id(1, "", mapping),
- self.tree.get_revision_id())
-
- def test_get_file_lines(self):
- self.assertEqual(["data"],
- self.tree.get_file_lines(self.inventory.path2id("foo/bla")))
-
- def test_executable(self):
- self.client_set_prop("dc/foo/bla", "svn:executable", "*")
- self.client_commit("dc", "My Message")
-
- mapping = self.repos.get_mapping()
-
- inventory = self.repos.get_inventory(
- self.repos.generate_revision_id(2, "", mapping))
-
- self.assertTrue(inventory[inventory.path2id("foo/bla")].executable)
-
- def test_symlink(self):
- if not has_symlinks():
- return
- os.symlink('foo/bla', 'dc/bar')
- self.client_add('dc/bar')
- self.client_commit("dc", "My Message")
-
- mapping = self.repos.get_mapping()
-
- inventory = self.repos.get_inventory(
- self.repos.generate_revision_id(2, "", mapping))
-
- self.assertEqual('symlink', inventory[inventory.path2id("bar")].kind)
- self.assertEqual('foo/bla',
- inventory[inventory.path2id("bar")].symlink_target)
-
- def test_not_executable(self):
- self.assertFalse(self.inventory[
- self.inventory.path2id("foo/bla")].executable)
-
-
-class EscapeTest(TestCase):
- def test_escape_svn_path_none(self):
- self.assertEqual("", escape_svn_path(""))
-
- def test_escape_svn_path_simple(self):
- self.assertEqual("ab", escape_svn_path("ab"))
-
- def test_escape_svn_path_percent(self):
- self.assertEqual("a%25b", escape_svn_path("a%b"))
-
- def test_escape_svn_path_whitespace(self):
- self.assertEqual("foobar%20", escape_svn_path("foobar "))
-
- def test_escape_svn_path_slash(self):
- self.assertEqual("foobar%2F", escape_svn_path("foobar/"))
-
- def test_escape_svn_path_special_char(self):
- self.assertEqual("foobar%8A", escape_svn_path("foobar\x8a"))
-
- def test_unescape_svn_path_slash(self):
- self.assertEqual("foobar/", unescape_svn_path("foobar%2F"))
-
- def test_unescape_svn_path_none(self):
- self.assertEqual("foobar", unescape_svn_path("foobar"))
-
- def test_unescape_svn_path_percent(self):
- self.assertEqual("foobar%b", unescape_svn_path("foobar%25b"))
-
- def test_escape_svn_path_nordic(self):
- self.assertEqual("foobar%C3%A6", escape_svn_path(u"foobar\xe6".encode("utf-8")))
-
-
-class SvnRepositoryFormatTests(TestCase):
+class TestClient(TestCaseInTempDir):
def setUp(self):
- self.format = SvnRepositoryFormat()
-
- def test_initialize(self):
- self.assertRaises(UninitializableFormat, self.format.initialize, None)
-
- def test_get_format_description(self):
- self.assertEqual("Subversion Repository",
- self.format.get_format_description())
-
- def test_conversion_target_self(self):
- self.assertTrue(self.format.check_conversion_target(self.format))
-
- def test_conversion_target_incompatible(self):
- self.assertFalse(self.format.check_conversion_target(
- format_registry.make_bzrdir('weave').repository_format))
-
- def test_conversion_target_compatible(self):
- self.assertTrue(self.format.check_conversion_target(
- format_registry.make_bzrdir('rich-root').repository_format))
+ super(TestClient, self).setUp()
+ def test_create(self):
+ repos.create(os.path.join(self.test_dir, "foo"))
+ def test_open(self):
+ repos.create(os.path.join(self.test_dir, "foo"))
+ repos.Repository("foo")
+ def test_uuid(self):
+ repos.create(os.path.join(self.test_dir, "foo"))
+ self.assertIsInstance(repos.Repository("foo").fs().get_uuid(), str)
--- /dev/null
+# -*- coding: utf-8 -*-
+
+# Copyright (C) 2006-2007 Jelmer Vernooij <jelmer@samba.org>
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+"""Subversion repository tests."""
+
+from bzrlib import urlutils
+from bzrlib.branch import Branch
+from bzrlib.bzrdir import BzrDir, format_registry
+from bzrlib.config import GlobalConfig
+from bzrlib.errors import NoSuchRevision, UninitializableFormat, BzrError
+from bzrlib.inventory import Inventory
+from bzrlib.osutils import has_symlinks
+from bzrlib.repository import Repository
+from bzrlib.revision import NULL_REVISION, Revision
+from bzrlib.tests import TestCase
+
+import os, sys
+
+from bzrlib.plugins.svn import format
+from bzrlib.plugins.svn.mapping import (escape_svn_path, unescape_svn_path,
+ SVN_PROP_BZR_REVISION_ID)
+from bzrlib.plugins.svn.mapping3 import (SVN_PROP_BZR_BRANCHING_SCHEME, set_branching_scheme,
+ set_property_scheme, BzrSvnMappingv3)
+from bzrlib.plugins.svn.mapping3.scheme import (TrunkBranchingScheme, NoBranchingScheme,
+ ListBranchingScheme, SingleBranchingScheme)
+from bzrlib.plugins.svn.transport import SvnRaTransport
+from bzrlib.plugins.svn.tests import TestCaseWithSubversionRepository
+from bzrlib.plugins.svn.repository import SvnRepositoryFormat
+
+
+class TestSubversionRepositoryWorks(TestCaseWithSubversionRepository):
+ def test_format(self):
+ """ Test repository format is correct """
+ bzrdir = self.make_local_bzrdir('a', 'ac')
+ self.assertEqual(bzrdir._format.get_format_string(), \
+ "Subversion Local Checkout")
+
+ self.assertEqual(bzrdir._format.get_format_description(), \
+ "Subversion Local Checkout")
+
+ def test_get_branch_log(self):
+ repos_url = self.make_repository("a")
+ cb = self.commit_editor(repos_url)
+ cb.add_file("foo")
+ cb.done()
+
+ repos = Repository.open(repos_url)
+
+ self.assertEqual([
+ ('', {'foo': ('A', None, -1)}, 1),
+ ('', {'': ('A', None, -1)}, 0)],
+ [(l.branch_path, l.paths, l.revnum) for l in repos.iter_reverse_branch_changes("", 1, NoBranchingScheme())])
+
+ def test_make_working_trees(self):
+ repos_url = self.make_repository("a")
+ repos = Repository.open(repos_url)
+ self.assertFalse(repos.make_working_trees())
+
+ def test_get_config_global_set(self):
+ repos_url = self.make_repository("a")
+ cfg = GlobalConfig()
+ cfg.set_user_option("foo", "Still Life")
+
+ repos = Repository.open(repos_url)
+ self.assertEquals("Still Life",
+ repos.get_config().get_user_option("foo"))
+
+ def test_get_config(self):
+ repos_url = self.make_repository("a")
+ repos = Repository.open(repos_url)
+ repos.get_config().set_user_option("foo", "Van Der Graaf Generator")
+
+ repos = Repository.open(repos_url)
+ self.assertEquals("Van Der Graaf Generator",
+ repos.get_config().get_user_option("foo"))
+
+
+ def test_get_physical_lock_status(self):
+ repos_url = self.make_repository("a")
+ repos = Repository.open(repos_url)
+ self.assertFalse(repos.get_physical_lock_status())
+
+ def test_iter_changes_parent_rename(self):
+ repos_url = self.make_client("a", "dc")
+ self.build_tree({'dc/foo/bar': None})
+ self.client_add('dc/foo')
+ self.client_commit('dc', 'a')
+ self.client_update('dc')
+ self.client_copy('dc/foo', 'dc/bla')
+ self.client_commit('dc', 'b')
+ repos = Repository.open(repos_url)
+ ret = list(repos.iter_changes('bla/bar', 2, BzrSvnMappingv3(SingleBranchingScheme('bla/bar'))))
+ self.assertEquals(1, len(ret))
+ self.assertEquals("bla/bar", ret[0][0])
+
+ def test_set_make_working_trees(self):
+ repos_url = self.make_repository("a")
+ repos = Repository.open(repos_url)
+ repos.set_make_working_trees(True)
+ self.assertFalse(repos.make_working_trees())
+
+ def test_get_fileid_map(self):
+ repos_url = self.make_repository("a")
+ repos = Repository.open(repos_url)
+ mapping = repos.get_mapping()
+ self.assertEqual({u"": (mapping.generate_file_id(repos.uuid, 0, "", u""), mapping.generate_revision_id(repos.uuid, 0, ""))}, repos.get_fileid_map(0, "", mapping))
+
+ def test_generate_revision_id_forced_revid(self):
+ repos_url = self.make_client("a", "dc")
+ self.client_set_prop("dc", SVN_PROP_BZR_REVISION_ID+"none",
+ "2 someid\n")
+ self.client_commit("dc", "set id")
+ repos = Repository.open(repos_url)
+ revid = repos.generate_revision_id(1, "", repos.get_mapping())
+ self.assertEquals("someid", revid)
+
+ def test_generate_revision_id_forced_revid_invalid(self):
+ repos_url = self.make_client("a", "dc")
+ self.client_set_prop("dc", SVN_PROP_BZR_REVISION_ID+"none",
+ "corrupt-id\n")
+ self.client_commit("dc", "set id")
+ repos = Repository.open(repos_url)
+ revid = repos.generate_revision_id(1, "", repos.get_mapping())
+ self.assertEquals(
+ repos.get_mapping().generate_revision_id(repos.uuid, 1, ""),
+ revid)
+
+ def test_add_revision(self):
+ repos_url = self.make_repository("a")
+ repos = Repository.open(repos_url)
+ self.assertRaises(NotImplementedError, repos.add_revision, "revid",
+ None)
+
+ def test_has_signature_for_revision_id_no(self):
+ repos_url = self.make_repository("a")
+ repos = Repository.open(repos_url)
+ self.assertFalse(repos.has_signature_for_revision_id("foo"))
+
+ def test_set_signature(self):
+ repos_url = self.make_client("a", "dc")
+ repos = Repository.open(repos_url)
+ self.build_tree({"dc/foo": "bar"})
+ self.client_add("dc/foo")
+ self.client_commit("dc", "msg")
+ revid = repos.get_mapping().generate_revision_id(repos.uuid, 1, "")
+ repos.add_signature_text(revid, "TEXT")
+ self.assertTrue(repos.has_signature_for_revision_id(revid))
+ self.assertEquals(repos.get_signature_text(revid), "TEXT")
+
+ def test_repr(self):
+ repos_url = self.make_client("a", "dc")
+ self.build_tree({'dc/foo': "data"})
+ self.client_add("dc/foo")
+ self.client_commit("dc", "My Message")
+
+ repos = Repository.open(repos_url)
+
+ self.assertEqual("SvnRepository('%s/')" % urlutils.local_path_to_url(urlutils.join(self.test_dir, "a")), repos.__repr__())
+
+ def test_get_branch_invalid_revision(self):
+ repos_url = self.make_repository("a")
+ repos = Repository.open(repos_url)
+ self.assertRaises(NoSuchRevision, list,
+ repos.iter_reverse_branch_changes("/", 20, NoBranchingScheme()))
+
+ def test_follow_branch_switched_parents(self):
+ repos_url = self.make_client('a', 'dc')
+ self.build_tree({'dc/pykleur/trunk/pykleur': None})
+ self.client_add("dc/pykleur")
+ self.client_commit("dc", "initial")
+ self.build_tree({'dc/pykleur/trunk/pykleur/afile': 'contents'})
+ self.client_add("dc/pykleur/trunk/pykleur/afile")
+ self.client_commit("dc", "add file")
+ self.client_copy("dc/pykleur", "dc/pygments", 1)
+ self.client_delete('dc/pykleur')
+ self.client_update("dc")
+ self.client_commit("dc", "commit")
+ repos = Repository.open(repos_url)
+ self.assertEquals([
+ ('pygments/trunk', {'pygments': (u'A', 'pykleur', 1),
+ 'pygments/trunk': (u'R', 'pykleur/trunk', 2),
+ 'pykleur': (u'D', None, -1)}, 3),
+ ('pykleur/trunk', {'pykleur/trunk/pykleur/afile': (u'A', None, -1)}, 2),
+ ('pykleur/trunk',
+ {'pykleur': (u'A', None, -1),
+ 'pykleur/trunk': (u'A', None, -1),
+ 'pykleur/trunk/pykleur': (u'A', None, -1)},
+ 1)],
+ [(l.branch_path, l.paths, l.revnum) for l in repos.iter_reverse_branch_changes("pygments/trunk", 3, TrunkBranchingScheme(1))])
+
+ def test_follow_branch_move_single(self):
+ repos_url = self.make_client('a', 'dc')
+ self.build_tree({'dc/pykleur/bla': None})
+ self.client_add("dc/pykleur")
+ self.client_commit("dc", "initial")
+ self.client_copy("dc/pykleur", "dc/pygments", 1)
+ self.client_update("dc")
+ self.client_commit("dc", "commit")
+ repos = Repository.open(repos_url)
+ changes = repos.iter_reverse_branch_changes("pygments", 2, SingleBranchingScheme("pygments"))
+ self.assertEquals([('pygments',
+ {'pygments/bla': ('A', None, -1), 'pygments': ('A', None, -1)},
+ 2)],
+ [(l.branch_path, l.paths, l.revnum) for l in changes])
+
+ def test_history_all(self):
+ repos_url = self.make_client("a", "dc")
+ self.build_tree({'dc/trunk/file': "data", "dc/foo/file":"data"})
+ self.client_add("dc/trunk")
+ self.client_add("dc/foo")
+ self.client_commit("dc", "My Message")
+
+ repos = Repository.open(repos_url)
+
+ self.assertEqual(2,
+ len(list(repos.all_revision_ids(repos.get_layout()))))
+
+ def test_all_revs_empty(self):
+ repos_url = self.make_repository("a")
+ repos = Repository.open(repos_url)
+ set_branching_scheme(repos, TrunkBranchingScheme())
+ self.assertEqual([], list(repos.all_revision_ids()))
+
+ def test_all_revs(self):
+ repos_url = self.make_client("a", "dc")
+ self.build_tree({'dc/trunk/file': "data", "dc/foo/file":"data"})
+ self.client_add("dc/trunk")
+ self.client_commit("dc", "add trunk")
+ self.build_tree({'dc/branches/somebranch/somefile': 'data'})
+ self.client_add("dc/branches")
+ self.client_commit("dc", "add a branch")
+ self.client_delete("dc/branches/somebranch")
+ self.client_commit("dc", "remove branch")
+
+ repos = Repository.open(repos_url)
+ set_branching_scheme(repos, TrunkBranchingScheme())
+ mapping = repos.get_mapping()
+ self.assertEqual([
+ repos.generate_revision_id(1, "trunk", mapping),
+ repos.generate_revision_id(2, "branches/somebranch", mapping)],
+ list(repos.all_revision_ids()))
+
+ def test_follow_history_empty(self):
+ repos_url = self.make_repository("a")
+ repos = Repository.open(repos_url)
+ self.assertEqual([repos.generate_revision_id(0, '', repos.get_mapping())],
+ list(repos.all_revision_ids(repos.get_layout())))
+
+ def test_follow_history_empty_branch(self):
+ repos_url = self.make_client("a", "dc")
+ self.build_tree({'dc/trunk/afile': "data", "dc/branches": None})
+ self.client_add("dc/trunk")
+ self.client_add("dc/branches")
+ self.client_commit("dc", "My Message")
+ repos = Repository.open(repos_url)
+ set_branching_scheme(repos, TrunkBranchingScheme())
+ self.assertEqual([repos.generate_revision_id(1, 'trunk', repos.get_mapping())],
+ list(repos.all_revision_ids(repos.get_layout())))
+
+ def test_follow_history_follow(self):
+ repos_url = self.make_client("a", "dc")
+ self.build_tree({'dc/trunk/afile': "data", "dc/branches": None})
+ self.client_add("dc/trunk")
+ self.client_add("dc/branches")
+ self.client_commit("dc", "My Message")
+
+ self.client_copy("dc/trunk", "dc/branches/abranch")
+ self.client_commit("dc", "Create branch")
+
+ repos = Repository.open(repos_url)
+ set_branching_scheme(repos, TrunkBranchingScheme())
+
+ items = list(repos.all_revision_ids(repos.get_layout()))
+ self.assertEqual([repos.generate_revision_id(1, 'trunk', repos.get_mapping()),
+ repos.generate_revision_id(2, 'branches/abranch', repos.get_mapping())
+ ], items)
+
+ def test_branch_log_specific(self):
+ repos_url = self.make_client("a", "dc")
+ self.build_tree({
+ 'dc/branches': None,
+ 'dc/branches/brancha': None,
+ 'dc/branches/branchab': None,
+ 'dc/branches/brancha/data': "data",
+ "dc/branches/branchab/data":"data"})
+ self.client_add("dc/branches")
+ self.client_commit("dc", "My Message")
+
+ repos = Repository.open(repos_url)
+ set_branching_scheme(repos, TrunkBranchingScheme())
+
+ self.assertEqual(1, len(list(repos.iter_reverse_branch_changes("branches/brancha",
+ 1, TrunkBranchingScheme()))))
+
+ def test_branch_log_specific_ignore(self):
+ repos_url = self.make_client("a", "dc")
+ self.build_tree({'dc/branches': None})
+ self.client_add("dc/branches")
+ self.build_tree({
+ 'dc/branches/brancha': None,
+ 'dc/branches/branchab': None,
+ 'dc/branches/brancha/data': "data",
+ "dc/branches/branchab/data":"data"})
+ self.client_add("dc/branches/brancha")
+ self.client_commit("dc", "My Message")
+
+ self.client_add("dc/branches/branchab")
+ self.client_commit("dc", "My Message2")
+
+ repos = Repository.open(repos_url)
+ set_branching_scheme(repos, TrunkBranchingScheme())
+
+ self.assertEqual(1, len(list(repos.iter_reverse_branch_changes("branches/brancha",
+ 2, TrunkBranchingScheme()))))
+
+ def test_find_branches(self):
+ repos_url = self.make_client("a", "dc")
+ self.build_tree({
+ 'dc/branches/brancha': None,
+ 'dc/branches/branchab': None,
+ 'dc/branches/brancha/data': "data",
+ "dc/branches/branchab/data":"data"})
+ self.client_add("dc/branches")
+ self.client_commit("dc", "My Message")
+ repos = Repository.open(repos_url)
+ set_branching_scheme(repos, TrunkBranchingScheme())
+ branches = repos.find_branches()
+ self.assertEquals(2, len(branches))
+ self.assertEquals(urlutils.join(repos.base, "branches/brancha"),
+ branches[1].base)
+ self.assertEquals(urlutils.join(repos.base, "branches/branchab"),
+ branches[0].base)
+
+ def test_find_branchpaths_moved(self):
+ repos_url = self.make_client("a", "dc")
+ self.build_tree({
+ 'dc/tmp/branches/brancha': None,
+ 'dc/tmp/branches/branchab': None,
+ 'dc/tmp/branches/brancha/data': "data",
+ "dc/tmp/branches/branchab/data":"data"})
+ self.client_add("dc/tmp")
+ self.client_commit("dc", "My Message")
+ self.client_copy("dc/tmp/branches", "dc/tags")
+ self.client_commit("dc", "My Message 2")
+
+ repos = Repository.open(repos_url)
+ set_branching_scheme(repos, TrunkBranchingScheme())
+
+ self.assertEqual([("tags/branchab", 2, True),
+ ("tags/brancha", 2, True)],
+ list(repos.find_branchpaths(TrunkBranchingScheme(), to_revnum=2)))
+
+ def test_find_branchpaths_start_revno(self):
+ repos_url = self.make_client("a", "dc")
+ self.build_tree({'dc/branches/brancha': None})
+ self.client_add("dc/branches")
+ self.client_commit("dc", "My Message")
+ self.build_tree({'dc/branches/branchb': None})
+ self.client_add("dc/branches/branchb")
+ self.client_commit("dc", "My Message 2")
+
+ repos = Repository.open(repos_url)
+ set_branching_scheme(repos, TrunkBranchingScheme())
+
+ self.assertEqual([("branches/branchb", 2, True)],
+ list(repos.find_branchpaths(TrunkBranchingScheme(), from_revnum=2,
+ to_revnum=2)))
+
+ def test_find_branchpaths_file_moved_from_nobranch(self):
+ repos_url = self.make_client("a", "dc")
+ self.build_tree({
+ 'dc/tmp/trunk': None,
+ 'dc/bla/somefile': "contents"})
+ self.client_add("dc/tmp")
+ self.client_add("dc/bla")
+ self.client_commit("dc", "My Message")
+ self.client_copy("dc/bla", "dc/tmp/branches")
+ self.client_delete("dc/tmp/branches/somefile")
+ self.client_commit("dc", "My Message 2")
+
+ Repository.open(repos_url).find_branchpaths(TrunkBranchingScheme(2))
+
+ def test_find_branchpaths_deleted_from_nobranch(self):
+ repos_url = self.make_client("a", "dc")
+ self.build_tree({
+ 'dc/tmp/trunk': None,
+ 'dc/bla/somefile': "contents"})
+ self.client_add("dc/tmp")
+ self.client_add("dc/bla")
+ self.client_commit("dc", "My Message")
+ self.client_copy("dc/bla", "dc/tmp/branches")
+ self.client_delete("dc/tmp/branches/somefile")
+ self.client_commit("dc", "My Message 2")
+
+ Repository.open(repos_url).find_branchpaths(TrunkBranchingScheme(1))
+
+ def test_find_branchpaths_moved_nobranch(self):
+ repos_url = self.make_client("a", "dc")
+ self.build_tree({
+ 'dc/tmp/nested/foobar': None,
+ 'dc/tmp/nested/branches/brancha': None,
+ 'dc/tmp/nested/branches/branchab': None,
+ 'dc/tmp/nested/branches/brancha/data': "data",
+ "dc/tmp/nested/branches/branchab/data":"data"})
+ self.client_add("dc/tmp")
+ self.client_commit("dc", "My Message")
+ self.client_copy("dc/tmp/nested", "dc/t2")
+ self.client_commit("dc", "My Message 2")
+
+ repos = Repository.open(repos_url)
+ set_branching_scheme(repos, TrunkBranchingScheme(1))
+
+ self.assertEqual([("t2/branches/brancha", 2, True),
+ ("t2/branches/branchab", 2, True)],
+ list(repos.find_branchpaths(TrunkBranchingScheme(1), to_revnum=2)))
+
+ def test_find_branchpaths_no(self):
+ repos_url = self.make_repository("a")
+
+ repos = Repository.open(repos_url)
+ set_branching_scheme(repos, NoBranchingScheme())
+
+ self.assertEqual([("", 0, True)],
+ list(repos.find_branchpaths(NoBranchingScheme(), to_revnum=0)))
+
+ def test_find_branchpaths_no_later(self):
+ repos_url = self.make_repository("a")
+
+ repos = Repository.open(repos_url)
+ set_branching_scheme(repos, NoBranchingScheme())
+
+ self.assertEqual([("", 0, True)],
+ list(repos.find_branchpaths(NoBranchingScheme(), to_revnum=0)))
+
+ def test_find_branchpaths_trunk_empty(self):
+ repos_url = self.make_repository("a")
+
+ repos = Repository.open(repos_url)
+ set_branching_scheme(repos, TrunkBranchingScheme())
+
+ self.assertEqual([],
+ list(repos.find_branchpaths(TrunkBranchingScheme(), to_revnum=0)))
+
+ def test_find_branchpaths_trunk_one(self):
+ repos_url = self.make_client("a", "dc")
+
+ repos = Repository.open(repos_url)
+ set_branching_scheme(repos, TrunkBranchingScheme())
+
+ self.build_tree({'dc/trunk/foo': "data"})
+ self.client_add("dc/trunk")
+ self.client_commit("dc", "My Message")
+
+ self.assertEqual([("trunk", 1, True)],
+ list(repos.find_branchpaths(TrunkBranchingScheme(), to_revnum=1)))
+
+ def test_find_branchpaths_removed(self):
+ repos_url = self.make_client("a", "dc")
+
+ repos = Repository.open(repos_url)
+ set_branching_scheme(repos, TrunkBranchingScheme())
+
+ self.build_tree({'dc/trunk/foo': "data"})
+ self.client_add("dc/trunk")
+ self.client_commit("dc", "My Message")
+
+ self.client_delete("dc/trunk")
+ self.client_commit("dc", "remove")
+
+ self.assertEqual([("trunk", 1, True)],
+ list(repos.find_branchpaths(TrunkBranchingScheme(), to_revnum=1)))
+ self.assertEqual([("trunk", 1, False)],
+ list(repos.find_branchpaths(TrunkBranchingScheme(), to_revnum=2)))
+
+ def test_url(self):
+ """ Test repository URL is kept """
+ bzrdir = self.make_local_bzrdir('b', 'bc')
+ self.assertTrue(isinstance(bzrdir, BzrDir))
+
+ def test_uuid(self):
+ """ Test UUID is retrieved correctly """
+ bzrdir = self.make_local_bzrdir('c', 'cc')
+ self.assertTrue(isinstance(bzrdir, BzrDir))
+ repository = bzrdir._find_repository()
+ fs = self.open_fs('c')
+ self.assertEqual(fs.get_uuid(), repository.uuid)
+
+ def test_get_inventory_weave(self):
+ bzrdir = self.make_client_and_bzrdir('d', 'dc')
+ repository = bzrdir.find_repository()
+ self.assertRaises(NotImplementedError, repository.get_inventory_weave)
+
+ def test_has_revision(self):
+ bzrdir = self.make_client_and_bzrdir('d', 'dc')
+ repository = bzrdir.find_repository()
+ self.build_tree({'dc/foo': "data"})
+ self.client_add("dc/foo")
+ self.client_commit("dc", "My Message")
+ self.assertTrue(repository.has_revision(
+ repository.generate_revision_id(1, "", repository.get_mapping())))
+ self.assertFalse(repository.has_revision("some-other-revision"))
+
+ def test_has_revision_none(self):
+ bzrdir = self.make_client_and_bzrdir('d', 'dc')
+ repository = bzrdir.find_repository()
+ self.assertTrue(repository.has_revision(None))
+
+ def test_has_revision_future(self):
+ bzrdir = self.make_client_and_bzrdir('d', 'dc')
+ repository = bzrdir.find_repository()
+ self.assertFalse(repository.has_revision(
+ repository.get_mapping().generate_revision_id(repository.uuid, 5, "")))
+
+ def test_get_parent_map(self):
+ repos_url = self.make_client('d', 'dc')
+ self.build_tree({'dc/foo': "data"})
+ self.client_add("dc/foo")
+ self.client_commit("dc", "My Message")
+ self.build_tree({'dc/foo': "data2"})
+ self.client_commit("dc", "Second Message")
+ repository = Repository.open("svn+%s" % repos_url)
+ mapping = repository.get_mapping()
+ revid = repository.generate_revision_id(0, "", mapping)
+ self.assertEqual({revid: (NULL_REVISION,)}, repository.get_parent_map([revid]))
+ revid = repository.generate_revision_id(1, "", mapping)
+ self.assertEqual({revid: (repository.generate_revision_id(0, "", mapping),)}, repository.get_parent_map([revid]))
+ revid = repository.generate_revision_id(2, "", mapping)
+ self.assertEqual({revid: (repository.generate_revision_id(1, "", mapping),)},
+ repository.get_parent_map([revid]))
+ self.assertEqual({}, repository.get_parent_map(["notexisting"]))
+
+ def test_revision_fileidmap(self):
+ repos_url = self.make_client('d', 'dc')
+ self.build_tree({'dc/foo': "data"})
+ self.client_add("dc/foo")
+ self.client_set_prop("dc", "bzr:revision-info", "")
+ self.client_set_prop("dc", "bzr:file-ids", "foo\tsomeid\n")
+ self.client_commit("dc", "My Message")
+ repository = Repository.open("svn+%s" % repos_url)
+ tree = repository.revision_tree(Branch.open(repos_url).last_revision())
+ self.assertEqual("someid", tree.inventory.path2id("foo"))
+ self.assertFalse("1@%s::foo" % repository.uuid in tree.inventory)
+
+ def test_revision_ghost_parents(self):
+ repos_url = self.make_client('d', 'dc')
+ self.build_tree({'dc/foo': "data"})
+ self.client_add("dc/foo")
+ self.client_commit("dc", "My Message")
+ self.client_update("dc")
+ self.build_tree({'dc/foo': "data2"})
+ self.client_set_prop("dc", "bzr:ancestry:v3-none", "ghostparent\n")
+ self.client_commit("dc", "Second Message")
+ repository = Repository.open("svn+%s" % repos_url)
+ mapping = repository.get_mapping()
+ self.assertEqual((),
+ repository.get_revision(
+ repository.generate_revision_id(0, "", mapping)).parent_ids)
+ self.assertEqual((repository.generate_revision_id(0, "", mapping),),
+ repository.get_revision(
+ repository.generate_revision_id(1, "", mapping)).parent_ids)
+ self.assertEqual((repository.generate_revision_id(1, "", mapping),
+ "ghostparent"),
+ repository.get_revision(
+ repository.generate_revision_id(2, "", mapping)).parent_ids)
+
+ def test_revision_svk_parent(self):
+ repos_url = self.make_client('d', 'dc')
+ self.build_tree({'dc/trunk/foo': "data", 'dc/branches/foo': None})
+ self.client_add("dc/trunk")
+ self.client_add("dc/branches")
+ self.client_commit("dc", "My Message")
+ self.client_update("dc")
+ self.build_tree({'dc/trunk/foo': "data2"})
+ repository = Repository.open("svn+%s" % repos_url)
+ set_branching_scheme(repository, TrunkBranchingScheme())
+ self.client_set_prop("dc/trunk", "svk:merge",
+ "%s:/branches/foo:1\n" % repository.uuid)
+ self.client_commit("dc", "Second Message")
+ mapping = repository.get_mapping()
+ self.assertEqual((repository.generate_revision_id(1, "trunk", mapping),
+ repository.generate_revision_id(1, "branches/foo", mapping)),
+ repository.get_revision(
+ repository.generate_revision_id(2, "trunk", mapping)).parent_ids)
+
+ def test_get_revision(self):
+ repos_url = self.make_client('d', 'dc')
+ repository = Repository.open("svn+%s" % repos_url)
+ self.assertRaises(NoSuchRevision, repository.get_revision,
+ "nonexisting")
+ self.build_tree({'dc/foo': "data"})
+ self.client_add("dc/foo")
+ self.client_commit("dc", "My Message")
+ self.client_update("dc")
+ self.build_tree({'dc/foo': "data2"})
+ (num, date, author) = self.client_commit("dc", "Second Message")
+ repository = Repository.open("svn+%s" % repos_url)
+ mapping = repository.get_mapping()
+ rev = repository.get_revision(
+ repository.generate_revision_id(2, "", mapping))
+ self.assertEqual((repository.generate_revision_id(1, "", mapping),),
+ rev.parent_ids)
+ self.assertEqual(rev.revision_id,
+ repository.generate_revision_id(2, "", mapping))
+ self.assertEqual(author, rev.committer)
+ self.assertIsInstance(rev.properties, dict)
+
+ def test_get_revision_id_overriden(self):
+ repos_url = self.make_client('d', 'dc')
+ repository = Repository.open("svn+%s" % repos_url)
+ self.assertRaises(NoSuchRevision, repository.get_revision, "nonexisting")
+ self.build_tree({'dc/foo': "data"})
+ self.client_add("dc/foo")
+ self.client_commit("dc", "My Message")
+ self.build_tree({'dc/foo': "data2"})
+ self.client_set_prop("dc", "bzr:revision-id:v3-none",
+ "3 myrevid\n")
+ self.client_update("dc")
+ (num, date, author) = self.client_commit("dc", "Second Message")
+ repository = Repository.open("svn+%s" % repos_url)
+ mapping = repository.get_mapping()
+ revid = mapping.generate_revision_id(repository.uuid, 2, "")
+ rev = repository.get_revision("myrevid")
+ self.assertEqual((repository.generate_revision_id(1, "", mapping),),
+ rev.parent_ids)
+ self.assertEqual(rev.revision_id,
+ repository.generate_revision_id(2, "", mapping))
+ self.assertEqual(author, rev.committer)
+ self.assertIsInstance(rev.properties, dict)
+
+ def test_get_revision_zero(self):
+ repos_url = self.make_client('d', 'dc')
+ repository = Repository.open("svn+%s" % repos_url)
+ mapping = repository.get_mapping()
+ rev = repository.get_revision(
+ repository.generate_revision_id(0, "", mapping))
+ self.assertEqual(repository.generate_revision_id(0, "", mapping),
+ rev.revision_id)
+ self.assertEqual("", rev.committer)
+ self.assertEqual({}, rev.properties)
+ self.assertEqual(None, rev.timezone)
+
+ def test_store_branching_scheme(self):
+ repos_url = self.make_client('d', 'dc')
+ repository = Repository.open(repos_url)
+ set_branching_scheme(repository, TrunkBranchingScheme(42))
+ repository = Repository.open(repos_url)
+ self.assertEquals("trunk42", str(repository.get_mapping().scheme))
+
+ def test_get_ancestry(self):
+ repos_url = self.make_client('d', 'dc')
+ repository = Repository.open("svn+%s" % repos_url)
+ self.assertRaises(NoSuchRevision, repository.get_revision, "nonexisting")
+ self.build_tree({'dc/foo': "data"})
+ self.client_add("dc/foo")
+ self.client_commit("dc", "My Message")
+ self.client_update("dc")
+ self.build_tree({'dc/foo': "data2"})
+ self.client_commit("dc", "Second Message")
+ self.client_update("dc")
+ self.build_tree({'dc/foo': "data3"})
+ self.client_commit("dc", "Third Message")
+ self.client_update("dc")
+ repository = Repository.open("svn+%s" % repos_url)
+ mapping = repository.get_mapping()
+ self.assertEqual([None,
+ repository.generate_revision_id(0, "", mapping),
+ repository.generate_revision_id(1, "", mapping),
+ repository.generate_revision_id(2, "", mapping),
+ repository.generate_revision_id(3, "", mapping)],
+ repository.get_ancestry(
+ repository.generate_revision_id(3, "", mapping)))
+ self.assertEqual([None,
+ repository.generate_revision_id(0, "", mapping),
+ repository.generate_revision_id(1, "", mapping),
+ repository.generate_revision_id(2, "", mapping)],
+ repository.get_ancestry(
+ repository.generate_revision_id(2, "", mapping)))
+ self.assertEqual([None,
+ repository.generate_revision_id(0, "", mapping),
+ repository.generate_revision_id(1, "", mapping)],
+ repository.get_ancestry(
+ repository.generate_revision_id(1, "", mapping)))
+ self.assertEqual([None, repository.generate_revision_id(0, "", mapping)],
+ repository.get_ancestry(
+ repository.generate_revision_id(0, "", mapping)))
+ self.assertEqual([None], repository.get_ancestry(NULL_REVISION))
+
+ def test_get_ancestry2(self):
+ repos_url = self.make_client('d', 'dc')
+ self.build_tree({'dc/foo': "data"})
+ self.client_add("dc/foo")
+ self.client_commit("dc", "My Message")
+ self.build_tree({'dc/foo': "data2"})
+ self.client_commit("dc", "Second Message")
+ repository = Repository.open("svn+%s" % repos_url)
+ mapping = repository.get_mapping()
+ self.assertEqual([None, repository.generate_revision_id(0, "", mapping)],
+ repository.get_ancestry(
+ repository.generate_revision_id(0, "", mapping)))
+ self.assertEqual([None, repository.generate_revision_id(0, "", mapping),
+ repository.generate_revision_id(1, "", mapping)],
+ repository.get_ancestry(
+ repository.generate_revision_id(1, "", mapping)))
+ self.assertEqual([None,
+ repository.generate_revision_id(0, "", mapping),
+ repository.generate_revision_id(1, "", mapping),
+ repository.generate_revision_id(2, "", mapping)],
+ repository.get_ancestry(
+ repository.generate_revision_id(2, "", mapping)))
+
+ def test_get_ancestry_merged(self):
+ repos_url = self.make_client('d', 'dc')
+ self.build_tree({'dc/foo': "data"})
+ self.client_add("dc/foo")
+ self.client_commit("dc", "My Message")
+ self.client_update("dc")
+ self.client_set_prop("dc", "bzr:ancestry:v3-none", "a-parent\n")
+ self.build_tree({'dc/foo': "data2"})
+ self.client_commit("dc", "Second Message")
+ repository = Repository.open("svn+%s" % repos_url)
+ mapping = repository.get_mapping()
+ self.assertEqual([None, repository.generate_revision_id(0, "", mapping)],
+ repository.get_ancestry(
+ repository.generate_revision_id(0, "", mapping)))
+ self.assertEqual([None, repository.generate_revision_id(0, "", mapping),
+ repository.generate_revision_id(1, "", mapping)],
+ repository.get_ancestry(
+ repository.generate_revision_id(1, "", mapping)))
+ self.assertEqual([None,
+ repository.generate_revision_id(0, "", mapping), "a-parent",
+ repository.generate_revision_id(1, "", mapping),
+ repository.generate_revision_id(2, "", mapping)],
+ repository.get_ancestry(
+ repository.generate_revision_id(2, "", mapping)))
+
+ def test_get_inventory(self):
+ repos_url = self.make_client('d', 'dc')
+ repository = Repository.open("svn+%s" % repos_url)
+ self.assertRaises(NoSuchRevision, repository.get_inventory,
+ "nonexisting")
+ self.build_tree({'dc/foo': "data", 'dc/blah': "other data"})
+ self.client_add("dc/foo")
+ self.client_add("dc/blah")
+ self.client_commit("dc", "My Message") #1
+ self.client_update("dc")
+ self.build_tree({'dc/foo': "data2", "dc/bar/foo": "data3"})
+ self.client_add("dc/bar")
+ self.client_commit("dc", "Second Message") #2
+ self.client_update("dc")
+ self.build_tree({'dc/foo': "data3"})
+ self.client_commit("dc", "Third Message") #3
+ self.client_update("dc")
+ repository = Repository.open("svn+%s" % repos_url)
+ mapping = repository.get_mapping()
+ inv = repository.get_inventory(
+ repository.generate_revision_id(1, "", mapping))
+ self.assertIsInstance(inv, Inventory)
+ self.assertIsInstance(inv.path2id("foo"), basestring)
+ inv = repository.get_inventory(
+ repository.generate_revision_id(2, "", mapping))
+ self.assertEqual(repository.generate_revision_id(2, "", mapping),
+ inv[inv.path2id("foo")].revision)
+ self.assertEqual(repository.generate_revision_id(1, "", mapping),
+ inv[inv.path2id("blah")].revision)
+ self.assertIsInstance(inv, Inventory)
+ self.assertIsInstance(inv.path2id("foo"), basestring)
+ self.assertIsInstance(inv.path2id("bar"), basestring)
+ self.assertIsInstance(inv.path2id("bar/foo"), basestring)
+
+ def test_generate_revision_id(self):
+ repos_url = self.make_client('d', 'dc')
+ self.build_tree({'dc/bla/bloe': None})
+ self.client_add("dc/bla")
+ self.client_commit("dc", "bla")
+ repository = Repository.open("svn+%s" % repos_url)
+ mapping = repository.get_mapping()
+ self.assertEqual(
+ mapping.generate_revision_id(repository.uuid, 1, "bla/bloe"),
+ repository.generate_revision_id(1, "bla/bloe", mapping))
+
+ def test_generate_revision_id_zero(self):
+ repos_url = self.make_client('d', 'dc')
+ repository = Repository.open("svn+%s" % repos_url)
+ mapping = repository.get_mapping()
+ self.assertEqual(mapping.generate_revision_id(repository.uuid, 0, ""),
+ repository.generate_revision_id(0, "", mapping))
+
+ def test_lookup_revision_id(self):
+ repos_url = self.make_client('d', 'dc')
+ self.build_tree({'dc/bloe': None})
+ self.client_add("dc/bloe")
+ self.client_commit("dc", "foobar")
+ repository = Repository.open("svn+%s" % repos_url)
+ self.assertRaises(NoSuchRevision, repository.lookup_revision_id,
+ "nonexisting")
+ mapping = repository.get_mapping()
+ self.assertEqual(("bloe", 1),
+ repository.lookup_revision_id(
+ repository.generate_revision_id(1, "bloe", mapping))[:2])
+
+ def test_lookup_revision_id_overridden(self):
+ repos_url = self.make_client('d', 'dc')
+ self.build_tree({'dc/bloe': None})
+ self.client_add("dc/bloe")
+ self.client_set_prop("dc", SVN_PROP_BZR_REVISION_ID+"none", "2 myid\n")
+ self.client_commit("dc", "foobar")
+ repository = Repository.open("svn+%s" % repos_url)
+ mapping = repository.get_mapping()
+ self.assertEqual(("", 1), repository.lookup_revision_id(
+ mapping.generate_revision_id(repository.uuid, 1, ""))[:2])
+ self.assertEqual(("", 1),
+ repository.lookup_revision_id("myid")[:2])
+
+ def test_lookup_revision_id_overridden_invalid(self):
+ repos_url = self.make_client('d', 'dc')
+ self.build_tree({'dc/bloe': None})
+ self.client_add("dc/bloe")
+ self.client_set_prop("dc", SVN_PROP_BZR_REVISION_ID+"none",
+ "corrupt-entry\n")
+ self.client_commit("dc", "foobar")
+ repository = Repository.open("svn+%s" % repos_url)
+ mapping = repository.get_mapping()
+ self.assertEqual(("", 1), repository.lookup_revision_id(
+ mapping.generate_revision_id(repository.uuid, 1, ""))[:2])
+ self.assertRaises(NoSuchRevision, repository.lookup_revision_id,
+ "corrupt-entry")
+
+ def test_lookup_revision_id_overridden_invalid_dup(self):
+ repos_url = self.make_client('d', 'dc')
+ self.build_tree({'dc/bloe': None})
+ self.client_add("dc/bloe")
+ self.client_set_prop("dc", SVN_PROP_BZR_REVISION_ID+"none",
+ "corrupt-entry\n")
+ self.client_commit("dc", "foobar")
+ self.build_tree({'dc/bla': None})
+ self.client_add("dc/bla")
+ self.client_set_prop("dc", SVN_PROP_BZR_REVISION_ID+"none",
+ "corrupt-entry\n2 corrupt-entry\n")
+ self.client_commit("dc", "foobar")
+ repository = Repository.open("svn+%s" % repos_url)
+ mapping = repository.get_mapping()
+ self.assertEqual(("", 2), repository.lookup_revision_id(
+ mapping.generate_revision_id(repository.uuid, 2, ""))[:2])
+ self.assertEqual(("", 1), repository.lookup_revision_id(
+ mapping.generate_revision_id(repository.uuid, 1, ""))[:2])
+ self.assertEqual(("", 2), repository.lookup_revision_id(
+ "corrupt-entry")[:2])
+
+ def test_lookup_revision_id_overridden_not_found(self):
+ """Make sure a revision id that is looked up but doesn't exist
+ doesn't accidently end up in the revid cache."""
+ repos_url = self.make_client('d', 'dc')
+ self.build_tree({'dc/bloe': None})
+ self.client_add("dc/bloe")
+ self.client_set_prop("dc", SVN_PROP_BZR_REVISION_ID+"none", "2 myid\n")
+ self.client_commit("dc", "foobar")
+ repository = Repository.open("svn+%s" % repos_url)
+ self.assertRaises(NoSuchRevision,
+ repository.lookup_revision_id, "foobar")
+
+ def test_set_branching_scheme_property(self):
+ repos_url = self.make_client('d', 'dc')
+ self.client_set_prop("dc", SVN_PROP_BZR_BRANCHING_SCHEME,
+ "trunk\nbranches/*\nbranches/tmp/*")
+ self.client_commit("dc", "set scheme")
+ repository = Repository.open("svn+%s" % repos_url)
+ self.assertEquals(ListBranchingScheme(["trunk", "branches/*", "branches/tmp/*"]).branch_list,
+ repository.get_mapping().scheme.branch_list)
+
+ def test_set_property_scheme(self):
+ repos_url = self.make_client('d', 'dc')
+ repos = Repository.open(repos_url)
+ set_property_scheme(repos, ListBranchingScheme(["bla/*"]))
+ self.client_update("dc")
+ self.assertEquals("bla/*\n",
+ self.client_get_prop("dc", SVN_PROP_BZR_BRANCHING_SCHEME))
+ self.assertEquals("Updating branching scheme for Bazaar.",
+ self.client_log("dc", 1, 1)[1][3])
+
+ def test_lookup_revision_id_invalid_uuid(self):
+ repos_url = self.make_client('d', 'dc')
+ repository = Repository.open("svn+%s" % repos_url)
+ mapping = repository.get_mapping()
+ self.assertRaises(NoSuchRevision,
+ repository.lookup_revision_id,
+ mapping.generate_revision_id("invaliduuid", 0, ""))
+
+ def test_check(self):
+ repos_url = self.make_client('d', 'dc')
+ self.build_tree({'dc/foo': "data"})
+ self.client_add("dc/foo")
+ self.client_commit("dc", "My Message")
+ repository = Repository.open("svn+%s" % repos_url)
+ mapping = repository.get_mapping()
+ repository.check([
+ repository.generate_revision_id(0, "", mapping),
+ repository.generate_revision_id(1, "", mapping)])
+
+ def test_copy_contents_into(self):
+ repos_url = self.make_client('d', 'dc')
+ self.build_tree({'dc/foo/bla': "data"})
+ self.client_add("dc/foo")
+ self.client_commit("dc", "My Message")
+ self.build_tree({'dc/foo/blo': "data2", "dc/bar/foo": "data3", 'dc/foo/bla': "data"})
+ self.client_add("dc/foo/blo")
+ self.client_add("dc/bar")
+ self.client_commit("dc", "Second Message")
+ repository = Repository.open("svn+%s" % repos_url)
+ mapping = repository.get_mapping()
+
+ to_bzrdir = BzrDir.create("e", format.get_rich_root_format())
+ to_repos = to_bzrdir.create_repository()
+
+ repository.copy_content_into(to_repos,
+ repository.generate_revision_id(2, "", mapping))
+
+ self.assertTrue(repository.has_revision(
+ repository.generate_revision_id(2, "", mapping)))
+ self.assertTrue(repository.has_revision(
+ repository.generate_revision_id(1, "", mapping)))
+
+ def test_is_shared(self):
+ repos_url = self.make_client('d', 'dc')
+ self.build_tree({'dc/foo/bla': "data"})
+ self.client_add("dc/foo")
+ self.client_commit("dc", "My Message")
+ repository = Repository.open("svn+%s" % repos_url)
+ self.assertTrue(repository.is_shared())
+
+ def test_fetch_property_change_only_trunk(self):
+ repos_url = self.make_client('d', 'dc')
+ self.build_tree({'dc/trunk/bla': "data"})
+ self.client_add("dc/trunk")
+ self.client_commit("dc", "My Message")
+ self.client_set_prop("dc/trunk", "some:property", "some data\n")
+ self.client_commit("dc", "My 3")
+ self.client_set_prop("dc/trunk", "some2:property", "some data\n")
+ self.client_commit("dc", "My 2")
+ self.client_set_prop("dc/trunk", "some:property", "some other data\n")
+ self.client_commit("dc", "My 4")
+ oldrepos = Repository.open("svn+"+repos_url)
+ self.assertEquals([('trunk', {'trunk': (u'M', None, -1)}, 3),
+ ('trunk', {'trunk': (u'M', None, -1)}, 2),
+ ('trunk', {'trunk/bla': (u'A', None, -1), 'trunk': (u'A', None, -1)}, 1)],
+ [(l.branch_path, l.paths, l.revnum) for l in oldrepos.iter_reverse_branch_changes("trunk", 3, TrunkBranchingScheme())])
+
+ def test_control_code_msg(self):
+ repos_url = self.make_client('d', 'dc')
+
+ self.build_tree({'dc/trunk': None})
+ self.client_add("dc/trunk")
+ self.client_commit("dc", "\x24")
+
+ self.build_tree({'dc/trunk/hosts': 'hej2'})
+ self.client_add("dc/trunk/hosts")
+ self.client_commit("dc", "bla\xfcbla") #2
+
+ self.build_tree({'dc/trunk/hosts': 'hej3'})
+ self.client_commit("dc", "a\x0cb") #3
+
+ self.build_tree({'dc/branches/foobranch/file': 'foohosts'})
+ self.client_add("dc/branches")
+ self.client_commit("dc", "foohosts") #4
+
+ oldrepos = Repository.open("svn+"+repos_url)
+ set_branching_scheme(oldrepos, TrunkBranchingScheme())
+ dir = BzrDir.create("f",format=format.get_rich_root_format())
+ newrepos = dir.create_repository()
+ oldrepos.copy_content_into(newrepos)
+
+ mapping = oldrepos.get_mapping()
+
+ self.assertTrue(newrepos.has_revision(
+ oldrepos.generate_revision_id(1, "trunk", mapping)))
+ self.assertTrue(newrepos.has_revision(
+ oldrepos.generate_revision_id(2, "trunk", mapping)))
+ self.assertTrue(newrepos.has_revision(
+ oldrepos.generate_revision_id(3, "trunk", mapping)))
+ self.assertTrue(newrepos.has_revision(
+ oldrepos.generate_revision_id(4, "branches/foobranch", mapping)))
+ self.assertFalse(newrepos.has_revision(
+ oldrepos.generate_revision_id(4, "trunk", mapping)))
+ self.assertFalse(newrepos.has_revision(
+ oldrepos.generate_revision_id(2, "", mapping)))
+
+ rev = newrepos.get_revision(oldrepos.generate_revision_id(1, "trunk", mapping))
+ self.assertEqual("$", rev.message)
+
+ rev = newrepos.get_revision(
+ oldrepos.generate_revision_id(2, "trunk", mapping))
+ self.assertEqual('bla\xc3\xbcbla', rev.message.encode("utf-8"))
+
+ rev = newrepos.get_revision(oldrepos.generate_revision_id(3, "trunk", mapping))
+ self.assertEqual(u"a\\x0cb", rev.message)
+
+ def test_set_branching_scheme(self):
+ repos_url = self.make_client('d', 'dc')
+ repos = Repository.open(repos_url)
+ set_branching_scheme(repos, NoBranchingScheme())
+
+ def testlhs_revision_parent_none(self):
+ repos_url = self.make_client('d', 'dc')
+ repos = Repository.open(repos_url)
+ set_branching_scheme(repos, NoBranchingScheme())
+ self.assertEquals(NULL_REVISION, repos.lhs_revision_parent("", 0, NoBranchingScheme()))
+
+ def testlhs_revision_parent_first(self):
+ repos_url = self.make_client('d', 'dc')
+ repos = Repository.open(repos_url)
+ set_branching_scheme(repos, NoBranchingScheme())
+ self.build_tree({'dc/adir/afile': "data"})
+ self.client_add("dc/adir")
+ self.client_commit("dc", "Initial commit")
+ mapping = repos.get_mapping()
+ self.assertEquals(repos.generate_revision_id(0, "", mapping), \
+ repos.lhs_revision_parent("", 1, mapping))
+
+ def testlhs_revision_parent_simple(self):
+ repos_url = self.make_client('d', 'dc')
+ self.build_tree({'dc/trunk/adir/afile': "data",
+ 'dc/trunk/adir/stationary': None,
+ 'dc/branches/abranch': None})
+ self.client_add("dc/trunk")
+ self.client_add("dc/branches")
+ self.client_commit("dc", "Initial commit")
+ self.build_tree({'dc/trunk/adir/afile': "bla"})
+ self.client_commit("dc", "Incremental commit")
+ repos = Repository.open(repos_url)
+ set_branching_scheme(repos, TrunkBranchingScheme())
+ mapping = repos.get_mapping()
+ self.assertEquals(repos.generate_revision_id(1, "trunk", mapping), \
+ repos.lhs_revision_parent("trunk", 2, mapping))
+
+ def testlhs_revision_parent_copied(self):
+ repos_url = self.make_client('d', 'dc')
+ self.build_tree({'dc/py/trunk/adir/afile': "data",
+ 'dc/py/trunk/adir/stationary': None})
+ self.client_add("dc/py")
+ self.client_commit("dc", "Initial commit")
+ self.client_copy("dc/py", "dc/de")
+ self.client_commit("dc", "Incremental commit")
+ self.build_tree({'dc/de/trunk/adir/afile': "bla"})
+ self.client_commit("dc", "Change de")
+ repos = Repository.open(repos_url)
+ set_branching_scheme(repos, TrunkBranchingScheme(1))
+ mapping = repos.get_mapping()
+ self.assertEquals(repos.generate_revision_id(1, "py/trunk", mapping), \
+ repos.lhs_revision_parent("de/trunk", 3, mapping))
+
+ def test_mainline_revision_copied(self):
+ repos_url = self.make_client('d', 'dc')
+ self.build_tree({'dc/py/trunk/adir/afile': "data",
+ 'dc/py/trunk/adir/stationary': None})
+ self.client_add("dc/py")
+ self.client_commit("dc", "Initial commit")
+ self.build_tree({'dc/de':None})
+ self.client_add("dc/de")
+ self.client_copy("dc/py/trunk", "dc/de/trunk")
+ self.client_commit("dc", "Copy trunk")
+ repos = Repository.open(repos_url)
+ set_branching_scheme(repos, TrunkBranchingScheme(1))
+ mapping = repos.get_mapping()
+ self.assertEquals(repos.generate_revision_id(1, "py/trunk", mapping), \
+ repos.lhs_revision_parent("de/trunk", 2, mapping))
+
+ def test_mainline_revision_nested_deleted(self):
+ repos_url = self.make_client('d', 'dc')
+ self.build_tree({'dc/py/trunk/adir/afile': "data",
+ 'dc/py/trunk/adir/stationary': None})
+ self.client_add("dc/py")
+ self.client_commit("dc", "Initial commit")
+ self.client_copy("dc/py", "dc/de")
+ self.client_commit("dc", "Incremental commit")
+ self.client_delete("dc/de/trunk/adir")
+ self.client_commit("dc", "Another incremental commit")
+ repos = Repository.open(repos_url)
+ set_branching_scheme(repos, TrunkBranchingScheme(1))
+ mapping = repos.get_mapping()
+ self.assertEquals(repos.generate_revision_id(1, "py/trunk", mapping), \
+ repos.lhs_revision_parent("de/trunk", 3, mapping))
+
+ def test_mainline_revision_missing(self):
+ repos_url = self.make_client('d', 'dc')
+ repos = Repository.open(repos_url)
+ self.build_tree({'dc/py/trunk/adir/afile': "data",
+ 'dc/py/trunk/adir/stationary': None})
+ self.client_add("dc/py")
+ self.client_commit("dc", "Initial commit")
+ self.assertRaises(NoSuchRevision,
+ lambda: repos.lhs_revision_parent("trunk", 2, repos.get_mapping()))
+
+
+class TestSvnRevisionTree(TestCaseWithSubversionRepository):
+ def setUp(self):
+ super(TestSvnRevisionTree, self).setUp()
+ repos_url = self.make_client('d', 'dc')
+ self.build_tree({'dc/foo/bla': "data"})
+ self.client_add("dc/foo")
+ self.client_commit("dc", "My Message")
+ self.repos = Repository.open(repos_url)
+ mapping = self.repos.get_mapping()
+ self.inventory = self.repos.get_inventory(
+ self.repos.generate_revision_id(1, "", mapping))
+ self.tree = self.repos.revision_tree(
+ self.repos.generate_revision_id(1, "", mapping))
+
+ def test_inventory(self):
+ self.assertIsInstance(self.tree.inventory, Inventory)
+ self.assertEqual(self.inventory, self.tree.inventory)
+
+ def test_get_parent_ids(self):
+ mapping = self.repos.get_mapping()
+ self.assertEqual((self.repos.generate_revision_id(0, "", mapping),), self.tree.get_parent_ids())
+
+ def test_get_parent_ids_zero(self):
+ mapping = self.repos.get_mapping()
+ tree = self.repos.revision_tree(
+ self.repos.generate_revision_id(0, "", mapping))
+ self.assertEqual((), tree.get_parent_ids())
+
+ def test_get_revision_id(self):
+ mapping = self.repos.get_mapping()
+ self.assertEqual(self.repos.generate_revision_id(1, "", mapping),
+ self.tree.get_revision_id())
+
+ def test_get_file_lines(self):
+ self.assertEqual(["data"],
+ self.tree.get_file_lines(self.inventory.path2id("foo/bla")))
+
+ def test_executable(self):
+ self.client_set_prop("dc/foo/bla", "svn:executable", "*")
+ self.client_commit("dc", "My Message")
+
+ mapping = self.repos.get_mapping()
+
+ inventory = self.repos.get_inventory(
+ self.repos.generate_revision_id(2, "", mapping))
+
+ self.assertTrue(inventory[inventory.path2id("foo/bla")].executable)
+
+ def test_symlink(self):
+ if not has_symlinks():
+ return
+ os.symlink('foo/bla', 'dc/bar')
+ self.client_add('dc/bar')
+ self.client_commit("dc", "My Message")
+
+ mapping = self.repos.get_mapping()
+
+ inventory = self.repos.get_inventory(
+ self.repos.generate_revision_id(2, "", mapping))
+
+ self.assertEqual('symlink', inventory[inventory.path2id("bar")].kind)
+ self.assertEqual('foo/bla',
+ inventory[inventory.path2id("bar")].symlink_target)
+
+ def test_not_executable(self):
+ self.assertFalse(self.inventory[
+ self.inventory.path2id("foo/bla")].executable)
+
+
+class EscapeTest(TestCase):
+ def test_escape_svn_path_none(self):
+ self.assertEqual("", escape_svn_path(""))
+
+ def test_escape_svn_path_simple(self):
+ self.assertEqual("ab", escape_svn_path("ab"))
+
+ def test_escape_svn_path_percent(self):
+ self.assertEqual("a%25b", escape_svn_path("a%b"))
+
+ def test_escape_svn_path_whitespace(self):
+ self.assertEqual("foobar%20", escape_svn_path("foobar "))
+
+ def test_escape_svn_path_slash(self):
+ self.assertEqual("foobar%2F", escape_svn_path("foobar/"))
+
+ def test_escape_svn_path_special_char(self):
+ self.assertEqual("foobar%8A", escape_svn_path("foobar\x8a"))
+
+ def test_unescape_svn_path_slash(self):
+ self.assertEqual("foobar/", unescape_svn_path("foobar%2F"))
+
+ def test_unescape_svn_path_none(self):
+ self.assertEqual("foobar", unescape_svn_path("foobar"))
+
+ def test_unescape_svn_path_percent(self):
+ self.assertEqual("foobar%b", unescape_svn_path("foobar%25b"))
+
+ def test_escape_svn_path_nordic(self):
+ self.assertEqual("foobar%C3%A6", escape_svn_path(u"foobar\xe6".encode("utf-8")))
+
+
+class SvnRepositoryFormatTests(TestCase):
+ def setUp(self):
+ self.format = SvnRepositoryFormat()
+
+ def test_initialize(self):
+ self.assertRaises(UninitializableFormat, self.format.initialize, None)
+
+ def test_get_format_description(self):
+ self.assertEqual("Subversion Repository",
+ self.format.get_format_description())
+
+ def test_conversion_target_self(self):
+ self.assertTrue(self.format.check_conversion_target(self.format))
+
+ def test_conversion_target_incompatible(self):
+ self.assertFalse(self.format.check_conversion_target(
+ format_registry.make_bzrdir('weave').repository_format))
+
+ def test_conversion_target_compatible(self):
+ self.assertTrue(self.format.check_conversion_target(
+ format_registry.make_bzrdir('rich-root').repository_format))
+
+
+
--- /dev/null
+# Copyright (C) 2005-2007 Jelmer Vernooij <jelmer@samba.org>
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+"""Subversion ra library tests."""
+
+from bzrlib.tests import TestCase
+from bzrlib.plugins.svn import wc
+
+class VersionTest(TestCase):
+ def test_version_length(self):
+ self.assertEquals(4, len(wc.version()))
+
+class WorkingCopyTests(TestCase):
+ def test_get_adm_dir(self):
+ self.assertEquals(".svn", wc.get_adm_dir())
+
+ def test_is_normal_prop(self):
+ self.assertTrue(wc.is_normal_prop("svn:ignore"))
+
+ def test_is_entry_prop(self):
+ self.assertTrue(wc.is_entry_prop("svn:entry:foo"))
+
+ def test_is_wc_prop(self):
+ self.assertTrue(wc.is_wc_prop("svn:wc:foo"))
+
+ def test_get_default_ignores(self):
+ self.assertIsInstance(wc.get_default_ignores({}), list)
from bzrlib.trace import mutter
from bzrlib.workingtree import WorkingTree
-import svn.core
-import svn.wc
+from bzrlib.plugins.svn import core, wc
import os, sys
def test_get_ignore_list_empty(self):
self.make_client('a', 'dc')
tree = self.open_checkout("dc")
- self.assertEqual(set([".svn"] + svn.wc.get_default_ignores(svn_config)), tree.get_ignore_list())
+ self.assertEqual(set([".svn"] + wc.get_default_ignores(svn_config)), tree.get_ignore_list())
def test_get_ignore_list_onelevel(self):
self.make_client('a', 'dc')
self.client_set_prop("dc", "svn:ignore", "*.d\n*.c\n")
tree = self.open_checkout("dc")
- self.assertEqual(set([".svn"] + svn.wc.get_default_ignores(svn_config) + ["./*.d", "./*.c"]), tree.get_ignore_list())
+ self.assertEqual(set([".svn"] + wc.get_default_ignores(svn_config) + ["./*.d", "./*.c"]), tree.get_ignore_list())
def test_get_ignore_list_morelevel(self):
self.make_client('a', 'dc')
self.client_add("dc/x")
self.client_set_prop("dc/x", "svn:ignore", "*.e\n")
tree = self.open_checkout("dc")
- self.assertEqual(set([".svn"] + svn.wc.get_default_ignores(svn_config) + ["./*.d", "./*.c", "./x/*.e"]), tree.get_ignore_list())
+ self.assertEqual(set([".svn"] + wc.get_default_ignores(svn_config) + ["./*.d", "./*.c", "./x/*.e"]), tree.get_ignore_list())
def test_add_reopen(self):
self.make_client('a', 'dc')
self.build_tree({"dc/bl": "data"})
tree = self.open_checkout("dc")
self.assertEqual([], tree.pending_merges())
-
+
+ def test_get_parent_ids(self):
+ self.make_client('a', 'dc')
+ self.build_tree({"dc/bl": "data"})
+ tree = self.open_checkout("dc")
+ self.assertEqual([Branch.open('a').last_revision()], tree.get_parent_ids())
+
def test_delta(self):
self.make_client('a', 'dc')
self.build_tree({"dc/bl": "data"})
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, ERR_FS_NOT_DIRECTORY
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, *args):
- if 'transport' in debug.debug_flags:
- mutter(text, *args)
-
- @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
+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
- @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)
+ from bzrlib.plugins.svn import lazy_check_versions
+ lazy_check_versions()
- @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
-
- class SvnLock(object):
- def __init__(self, connection, tokens):
- self._tokens = tokens
- self._connection = connection
-
- 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:
self.add_connection(conn)
- def mkdir(self, relpath, mode=None):
+ def mkdir(self, relpath, message="Creating directory"):
conn = self.get_connection()
+ self.mutter('svn mkdir %s' % (relpath,))
try:
- return conn.mkdir(relpath, mode)
+ ce = conn.get_commit_editor({"svn:log": message})
+ node = ce.open_root(-1)
+ batons = relpath.split("/")
+ toclose = [node]
+ for i in range(len(batons)):
+ node = node.open_directory("/".join(batons[:i]), -1)
+ toclose.append(node)
+ toclose.append(node.add_directory(relpath, None, -1))
+ for c in reversed(toclose):
+ c.close()
+ ce.close()
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):
+ def get_commit_editor(self, revprops, done_cb=None,
+ lock_token=None, keep_locks=False):
conn = self._open_real_transport()
- conn.set_unbusy_handler(lambda: self.add_connection(conn))
- return conn.get_commit_editor(revprops, done_cb,
- lock_token, keep_locks)
+ self.mutter('svn get-commit-editor %r' % (revprops,))
+ return conn.get_commit_editor(revprops, done_cb, lock_token, keep_locks)
def listable(self):
"""See Transport.listable().
-# Copyright (C) 2005-2006 Jelmer Vernooij <jelmer@samba.org>
+# Copyright (C) 2005-2008 Jelmer Vernooij <jelmer@samba.org>
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
from cStringIO import StringIO
import urllib
-import svn.core, svn.wc, svn.delta
-from svn.core import Pool
-
-from bzrlib.plugins.svn import errors, properties
+from bzrlib.plugins.svn.delta import apply_txdelta_handler
+from bzrlib.plugins.svn import core, errors, wc, properties
def parse_externals_description(base_url, val):
"""Parse an svn:externals property value.
inv.add(ie)
-# Deal with Subversion 1.5 and the patched Subversion 1.4 (which are
-# slightly different).
-
-if hasattr(svn.delta, 'tx_invoke_window_handler'):
- def apply_txdelta_handler(src_stream, target_stream, pool):
- assert hasattr(src_stream, 'read')
- assert hasattr(target_stream, 'write')
- window_handler, baton = svn.delta.tx_apply(src_stream, target_stream,
- None, pool)
-
- def wrapper(window):
- window_handler(window, baton)
-
- return wrapper
-else:
- def apply_txdelta_handler(src_stream, target_stream, pool):
- assert hasattr(src_stream, 'read')
- assert hasattr(target_stream, 'write')
- ret = svn.delta.svn_txdelta_apply(src_stream, target_stream, None, pool)
-
- def wrapper(window):
- svn.delta.invoke_txdelta_window_handler(
- ret[1], window, ret[2])
-
- return wrapper
-
-
class SvnRevisionTree(RevisionTree):
"""A tree that existed in a historical Subversion revision."""
def __init__(self, repository, revision_id):
self._repository = repository
self._revision_id = revision_id
- pool = Pool()
(self.branch_path, self.revnum, mapping) = repository.lookup_revision_id(revision_id)
self._inventory = Inventory()
self.id_map = repository.get_fileid_map(self.revnum, self.branch_path,
mapping)
- editor = TreeBuildEditor(self, pool)
+ editor = TreeBuildEditor(self)
self.file_data = {}
root_repos = repository.transport.get_svn_repos_root()
- reporter = repository.transport.do_switch(
- self.revnum, True,
- urlutils.join(root_repos, self.branch_path), editor, pool)
- reporter.set_path("", 0, True, None, pool)
- reporter.finish_report(pool)
- pool.destroy()
+ conn = repository.transport.get_connection()
+ reporter = conn.do_switch(
+ self.revnum, "", True,
+ urlutils.join(root_repos, self.branch_path), editor)
+ try:
+ reporter.set_path("", 0, True)
+ reporter.finish()
+ finally:
+ repository.transport.add_connection(conn)
def get_file_lines(self, file_id):
return osutils.split_lines(self.file_data[file_id])
-class TreeBuildEditor(svn.delta.Editor):
+class TreeBuildEditor:
"""Builds a tree given Subversion tree transform calls."""
- def __init__(self, tree, pool):
+ def __init__(self, tree):
self.tree = tree
self.repository = tree._repository
self.last_revnum = {}
- self.dir_revnum = {}
- self.dir_ignores = {}
- self.pool = pool
def set_target_revision(self, revnum):
self.revnum = revnum
- def open_root(self, revnum, baton):
+ def open_root(self, revnum):
file_id, revision_id = self.tree.id_map[""]
ie = self.tree._inventory.add_path("", 'directory', file_id)
ie.revision = revision_id
self.tree._inventory.revision_id = revision_id
- return file_id
+ return DirectoryTreeEditor(self.tree, file_id)
- def add_directory(self, path, parent_baton, copyfrom_path, copyfrom_revnum, pool):
+ def close(self):
+ pass
+
+ def abort(self):
+ pass
+
+class DirectoryTreeEditor:
+ def __init__(self, tree, file_id):
+ self.tree = tree
+ self.file_id = file_id
+
+ def add_directory(self, path, copyfrom_path=None, copyfrom_revnum=-1):
path = path.decode("utf-8")
file_id, revision_id = self.tree.id_map[path]
ie = self.tree._inventory.add_path(path, 'directory', file_id)
ie.revision = revision_id
- return file_id
+ return DirectoryTreeEditor(self.tree, file_id)
- def change_dir_prop(self, id, name, value, pool):
- if name == properties.PROP_ENTRY_COMMITTED_REV:
- self.dir_revnum[id] = int(value)
- elif name == properties.PROP_IGNORE:
- self.dir_ignores[id] = value
- elif name in (properties.PROP_ENTRY_COMMITTED_DATE,
+ def change_prop(self, name, value):
+ if name in (properties.PROP_ENTRY_COMMITTED_DATE,
+ properties.PROP_ENTRY_COMMITTED_REV,
properties.PROP_ENTRY_LAST_AUTHOR,
properties.PROP_ENTRY_LOCK_TOKEN,
properties.PROP_ENTRY_UUID,
- properties.PROP_EXECUTABLE):
+ properties.PROP_EXECUTABLE,
+ properties.PROP_IGNORE):
pass
elif name.startswith(properties.PROP_WC_PREFIX):
pass
elif name.startswith(properties.PROP_PREFIX):
mutter('unsupported dir property %r', name)
- def change_file_prop(self, id, name, value, pool):
+ def add_file(self, path, copyfrom_path=None, copyfrom_revnum=-1):
+ path = path.decode("utf-8")
+ return FileTreeEditor(self.tree, path)
+
+ def close(self):
+ pass
+
+
+class FileTreeEditor:
+ def __init__(self, tree, path):
+ self.tree = tree
+ self.path = path
+ self.is_executable = False
+ self.is_symlink = False
+ self.last_file_rev = None
+
+ def change_prop(self, name, value):
+ from mapping import SVN_PROP_BZR_PREFIX
+
if name == properties.PROP_EXECUTABLE:
self.is_executable = (value != None)
elif name == properties.PROP_SPECIAL:
elif name.startswith(properties.PROP_PREFIX):
mutter('unsupported file property %r', name)
- def add_file(self, path, parent_id, copyfrom_path, copyfrom_revnum, baton):
- path = path.decode("utf-8")
- self.is_symlink = False
- self.is_executable = False
- return path
-
- def close_dir(self, id):
- if id in self.tree._inventory and self.dir_ignores.has_key(id):
- self.tree._inventory[id].ignores = self.dir_ignores[id]
-
- def close_file(self, path, checksum):
- file_id, revision_id = self.tree.id_map[path]
+ def close(self, checksum=None):
+ file_id, revision_id = self.tree.id_map[self.path]
if self.is_symlink:
- ie = self.tree._inventory.add_path(path, 'symlink', file_id)
+ ie = self.tree._inventory.add_path(self.path, 'symlink', file_id)
else:
- ie = self.tree._inventory.add_path(path, 'file', file_id)
+ ie = self.tree._inventory.add_path(self.path, 'file', file_id)
ie.revision = revision_id
if self.file_stream:
self.file_stream = None
- def close_edit(self):
- pass
-
- def abort_edit(self):
- pass
-
- def apply_textdelta(self, file_id, base_checksum):
+ def apply_textdelta(self, base_checksum=None):
self.file_stream = StringIO()
- return apply_txdelta_handler(StringIO(""), self.file_stream, self.pool)
+ return apply_txdelta_handler("", self.file_stream)
class SvnBasisTree(RevisionTree):
self._inventory = Inventory(root_id=None)
self._repository = workingtree.branch.repository
- def add_file_to_inv(relpath, id, revid, wc):
- props = svn.wc.get_prop_diffs(self.workingtree.abspath(relpath).encode("utf-8"), wc)
- if isinstance(props, list): # Subversion 1.5
- props = props[1]
+ def add_file_to_inv(relpath, id, revid, adm):
+ (delta_props, props) = adm.get_prop_diffs(self.workingtree.abspath(relpath))
if props.has_key(properties.PROP_SPECIAL):
ie = self._inventory.add_path(relpath, 'symlink', id)
ie.symlink_target = open(self._abspath(relpath)).read()[len("link "):]
def find_ids(entry):
relpath = urllib.unquote(entry.url[len(entry.repos):].strip("/"))
- if entry.schedule in (svn.wc.schedule_normal,
- svn.wc.schedule_delete,
- svn.wc.schedule_replace):
+ if entry.schedule in (wc.SCHEDULE_NORMAL,
+ wc.SCHEDULE_DELETE,
+ wc.SCHEDULE_REPLACE):
return self.id_map[workingtree.branch.unprefix(relpath.decode("utf-8"))]
return (None, None)
- def add_dir_to_inv(relpath, wc, parent_id):
- entries = svn.wc.entries_read(wc, False)
+ def add_dir_to_inv(relpath, adm, parent_id):
+ entries = adm.entries_read(False)
entry = entries[""]
(id, revid) = find_ids(entry)
if id == None:
assert entry
- if entry.kind == svn.core.svn_node_dir:
- subwc = svn.wc.adm_open3(wc,
+ if entry.kind == core.NODE_DIR:
+ subwc = wc.WorkingCopy(adm,
self.workingtree.abspath(subrelpath),
False, 0, None)
try:
add_dir_to_inv(subrelpath, subwc, id)
finally:
- svn.wc.adm_close(subwc)
+ subwc.close()
else:
(subid, subrevid) = find_ids(entry)
if subid is not None:
- add_file_to_inv(subrelpath, subid, subrevid, wc)
+ add_file_to_inv(subrelpath, subid, subrevid, adm)
- wc = workingtree._get_wc()
+ adm = workingtree._get_wc()
try:
- add_dir_to_inv(u"", wc, None)
+ add_dir_to_inv(u"", adm, None)
finally:
- svn.wc.adm_close(wc)
+ adm.close()
def _abspath(self, relpath):
- return svn.wc.get_pristine_copy_path(self.workingtree.abspath(relpath).encode("utf-8"))
+ return wc.get_pristine_copy_path(self.workingtree.abspath(relpath).encode("utf-8"))
def get_file_lines(self, file_id):
base_copy = self._abspath(self.id2path(file_id))
--- /dev/null
+/*
+ * Copyright © 2008 Jelmer Vernooij <jelmer@samba.org>
+ * -*- coding: utf-8 -*-
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <stdbool.h>
+#include <Python.h>
+#include <apr_general.h>
+#include <svn_error.h>
+#include <svn_io.h>
+#include <apr_errno.h>
+#include <svn_error_codes.h>
+
+#include "util.h"
+
+#define BZR_SVN_APR_ERROR_OFFSET (APR_OS_START_USERERR + \
+ (50 * SVN_ERR_CATEGORY_SIZE))
+
+
+apr_pool_t *Pool()
+{
+ apr_status_t status;
+ apr_pool_t *ret;
+ char errmsg[1024];
+ ret = NULL;
+ status = apr_pool_create(&ret, NULL);
+ if (status != 0) {
+ PyErr_SetString(PyExc_Exception,
+ apr_strerror(status, errmsg, sizeof(errmsg)));
+ return NULL;
+ }
+ return ret;
+}
+
+PyObject *PyErr_NewSubversionException(svn_error_t *error)
+{
+ return Py_BuildValue("(si)", error->message, error->apr_err);
+}
+
+void PyErr_SetSubversionException(svn_error_t *error)
+{
+ PyObject *coremod = PyImport_ImportModule("bzrlib.plugins.svn.core"), *excobj;
+
+ if (coremod == NULL) {
+ return;
+ }
+
+ excobj = PyObject_GetAttrString(coremod, "SubversionException");
+
+ if (excobj == NULL) {
+ PyErr_BadInternalCall();
+ return;
+ }
+
+ PyErr_SetObject(excobj, Py_BuildValue("(si)", error->message, error->apr_err));
+}
+
+bool check_error(svn_error_t *error)
+{
+ if (error == NULL)
+ return true;
+
+ if (error->apr_err == BZR_SVN_APR_ERROR_OFFSET)
+ return false; /* Just let Python deal with it */
+
+ PyErr_SetSubversionException(error);
+ return false;
+}
+
+bool string_list_to_apr_array(apr_pool_t *pool, PyObject *l, apr_array_header_t **ret)
+{
+ int i;
+ if (l == Py_None) {
+ *ret = NULL;
+ return true;
+ }
+ if (!PyList_Check(l)) {
+ PyErr_Format(PyExc_TypeError, "Expected list of strings, got: %s",
+ l->ob_type->tp_name);
+ return false;
+ }
+ *ret = apr_array_make(pool, PyList_Size(l), sizeof(char *));
+ for (i = 0; i < PyList_GET_SIZE(l); i++) {
+ PyObject *item = PyList_GET_ITEM(l, i);
+ if (!PyString_Check(item)) {
+ PyErr_SetString(PyExc_TypeError, "Expected list of strings");
+ return false;
+ }
+ APR_ARRAY_PUSH(*ret, char *) = apr_pstrdup(pool, PyString_AsString(item));
+ }
+ return true;
+}
+
+PyObject *prop_hash_to_dict(apr_hash_t *props)
+{
+ const char *key;
+ apr_hash_index_t *idx;
+ apr_ssize_t klen;
+ svn_string_t *val;
+ apr_pool_t *pool;
+ PyObject *py_props;
+ if (props == NULL) {
+ Py_RETURN_NONE;
+ }
+ pool = Pool();
+ if (pool == NULL)
+ return NULL;
+ py_props = PyDict_New();
+ for (idx = apr_hash_first(pool, props); idx != NULL;
+ idx = apr_hash_next(idx)) {
+ PyObject *py_val;
+ apr_hash_this(idx, (const void **)&key, &klen, (void **)&val);
+ if (val == NULL || val->data == NULL)
+ py_val = Py_None;
+ else
+ py_val = PyString_FromStringAndSize(val->data, val->len);
+ PyDict_SetItemString(py_props, key, py_val);
+ }
+ apr_pool_destroy(pool);
+ return py_props;
+}
+
+svn_error_t *py_svn_log_wrapper(void *baton, apr_hash_t *changed_paths, long revision, const char *author, const char *date, const char *message, apr_pool_t *pool)
+{
+ apr_hash_index_t *idx;
+ const char *key;
+ apr_ssize_t klen;
+ svn_log_changed_path_t *val;
+ PyObject *revprops, *py_changed_paths, *ret;
+
+ if (changed_paths == NULL) {
+ py_changed_paths = Py_None;
+ } else {
+ py_changed_paths = PyDict_New();
+ for (idx = apr_hash_first(pool, changed_paths); idx != NULL;
+ idx = apr_hash_next(idx)) {
+ apr_hash_this(idx, (const void **)&key, &klen, (void **)&val);
+ PyDict_SetItemString(py_changed_paths, key,
+ Py_BuildValue("(czi)", val->action, val->copyfrom_path,
+ val->copyfrom_rev));
+ }
+ }
+ revprops = PyDict_New();
+ if (message != NULL) {
+ PyDict_SetItemString(revprops, SVN_PROP_REVISION_LOG,
+ PyString_FromString(message));
+ }
+ if (author != NULL) {
+ PyDict_SetItemString(revprops, SVN_PROP_REVISION_AUTHOR,
+ PyString_FromString(author));
+ }
+ if (date != NULL) {
+ PyDict_SetItemString(revprops, SVN_PROP_REVISION_DATE,
+ PyString_FromString(date));
+ }
+ ret = PyObject_CallFunction((PyObject *)baton, "OiO", py_changed_paths,
+ revision, revprops);
+ if (ret == NULL)
+ return py_svn_error();
+ return NULL;
+}
+
+svn_error_t *py_svn_error()
+{
+ return svn_error_create(BZR_SVN_APR_ERROR_OFFSET, NULL, "Error occured in python bindings");
+}
+
+PyObject *wrap_lock(svn_lock_t *lock)
+{
+ return Py_BuildValue("(zzzbzz)", lock->path, lock->token, lock->owner,
+ lock->comment, lock->is_dav_comment,
+ lock->creation_date, lock->expiration_date);
+}
+
+apr_array_header_t *revnum_list_to_apr_array(apr_pool_t *pool, PyObject *l)
+{
+ int i;
+ apr_array_header_t *ret;
+ if (l == Py_None) {
+ return NULL;
+ }
+ ret = apr_array_make(pool, PyList_Size(l), sizeof(svn_revnum_t));
+ if (ret == NULL) {
+ PyErr_NoMemory();
+ return NULL;
+ }
+ for (i = 0; i < PyList_Size(l); i++) {
+ APR_ARRAY_PUSH(ret, svn_revnum_t) = PyLong_AsLong(PyList_GetItem(l, i));
+ }
+ return ret;
+}
+
+
+static svn_error_t *py_stream_read(void *baton, char *buffer, apr_size_t *length)
+{
+ PyObject *self = (PyObject *)baton, *ret;
+ ret = PyObject_CallMethod(self, "read", "i", *length);
+ if (ret == NULL)
+ return py_svn_error();
+ /* FIXME: Handle !PyString_Check(ret) */
+ *length = PyString_Size(ret);
+ memcpy(buffer, PyString_AS_STRING(ret), *length);
+ return NULL;
+}
+
+static svn_error_t *py_stream_write(void *baton, const char *data, apr_size_t *len)
+{
+ PyObject *self = (PyObject *)baton, *ret;
+ ret = PyObject_CallMethod(self, "write", "s#", data, len[0]);
+ if (ret == NULL)
+ return py_svn_error();
+ return NULL;
+}
+
+static svn_error_t *py_stream_close(void *baton)
+{
+ PyObject *self = (PyObject *)baton, *ret;
+ ret = PyObject_CallMethod(self, "close", NULL);
+ if (ret == NULL)
+ return py_svn_error();
+ Py_DECREF(self);
+ return NULL;
+}
+
+svn_stream_t *new_py_stream(apr_pool_t *pool, PyObject *py)
+{
+ svn_stream_t *stream;
+ Py_INCREF(py);
+ stream = svn_stream_create((void *)py, pool);
+ svn_stream_set_read(stream, py_stream_read);
+ svn_stream_set_write(stream, py_stream_write);
+ svn_stream_set_close(stream, py_stream_close);
+ return stream;
+}
+
+svn_error_t *py_cancel_func(void *cancel_baton)
+{
+ PyObject *py_fn = (PyObject *)cancel_baton;
+ if (py_fn != Py_None) {
+ PyObject *ret = PyObject_CallFunction(py_fn, NULL);
+ if (PyBool_Check(ret) && ret == Py_True) {
+ return svn_error_create(SVN_ERR_CANCELLED, NULL, NULL);
+ }
+ }
+ return NULL;
+}
+
+
--- /dev/null
+/*
+ * Copyright © 2008 Jelmer Vernooij <jelmer@samba.org>
+ * -*- coding: utf-8 -*-
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _BZR_SVN_UTIL_H_
+#define _BZR_SVN_UTIL_H_
+
+#pragma GCC visibility push(hidden)
+
+__attribute__((warn_unused_result)) apr_pool_t *Pool(void);
+__attribute__((warn_unused_result)) bool check_error(svn_error_t *error);
+bool string_list_to_apr_array(apr_pool_t *pool, PyObject *l, apr_array_header_t **);
+PyObject *prop_hash_to_dict(apr_hash_t *props);
+svn_error_t *py_svn_log_wrapper(void *baton, apr_hash_t *changed_paths,
+ long revision, const char *author,
+ const char *date, const char *message,
+ apr_pool_t *pool);
+svn_error_t *py_svn_error(void);
+void PyErr_SetSubversionException(svn_error_t *error);
+
+#define RUN_SVN_WITH_POOL(pool, cmd) \
+ if (!check_error((cmd))) { \
+ apr_pool_destroy(pool); \
+ return NULL; \
+ }
+
+PyObject *wrap_lock(svn_lock_t *lock);
+apr_array_header_t *revnum_list_to_apr_array(apr_pool_t *pool, PyObject *l);
+svn_stream_t *new_py_stream(apr_pool_t *pool, PyObject *py);
+PyObject *PyErr_NewSubversionException(svn_error_t *error);
+svn_error_t *py_cancel_func(void *cancel_baton);
+
+#pragma GCC visibility pop
+
+#endif /* _BZR_SVN_UTIL_H_ */
--- /dev/null
+/*
+ * Copyright © 2008 Jelmer Vernooij <jelmer@samba.org>
+ * -*- coding: utf-8 -*-
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <Python.h>
+#include <apr_general.h>
+#include <svn_wc.h>
+#include <structmember.h>
+#include <stdbool.h>
+
+#include "util.h"
+#include "editor.h"
+
+PyAPI_DATA(PyTypeObject) Entry_Type;
+PyAPI_DATA(PyTypeObject) Adm_Type;
+
+static PyObject *py_entry(const svn_wc_entry_t *entry);
+
+static svn_error_t *py_ra_report_set_path(void *baton, const char *path, long revision, int start_empty, const char *lock_token, apr_pool_t *pool)
+{
+ PyObject *self = (PyObject *)baton, *py_lock_token, *ret;
+ if (lock_token == NULL) {
+ py_lock_token = Py_None;
+ } else {
+ py_lock_token = PyString_FromString(lock_token);
+ }
+ ret = PyObject_CallFunction(self, "set_path", "slbO", path, revision, start_empty, py_lock_token);
+ if (ret == NULL)
+ return py_svn_error();
+ return NULL;
+}
+
+static svn_error_t *py_ra_report_delete_path(void *baton, const char *path, apr_pool_t *pool)
+{
+ PyObject *self = (PyObject *)baton, *ret;
+ ret = PyObject_CallFunction(self, "delete_path", "s", path);
+ if (ret == NULL)
+ return py_svn_error();
+ return NULL;
+}
+
+static svn_error_t *py_ra_report_link_path(void *report_baton, const char *path, const char *url, long revision, int start_empty, const char *lock_token, apr_pool_t *pool)
+{
+ PyObject *self = (PyObject *)report_baton, *ret, *py_lock_token;
+ if (lock_token == NULL) {
+ py_lock_token = Py_None;
+ } else {
+ py_lock_token = PyString_FromString(lock_token);
+ }
+ ret = PyObject_CallFunction(self, "link_path", "sslbO", path, url, revision, start_empty, py_lock_token);
+ if (ret == NULL)
+ return py_svn_error();
+ return NULL;
+}
+
+static svn_error_t *py_ra_report_finish(void *baton, apr_pool_t *pool)
+{
+ PyObject *self = (PyObject *)baton, *ret;
+ ret = PyObject_CallFunction(self, "finish", NULL);
+ if (ret == NULL)
+ return py_svn_error();
+ return NULL;
+}
+
+static svn_error_t *py_ra_report_abort(void *baton, apr_pool_t *pool)
+{
+ PyObject *self = (PyObject *)baton, *ret;
+ ret = PyObject_CallFunction(self, "abort", NULL);
+ if (ret == NULL)
+ return py_svn_error();
+ return NULL;
+}
+
+static const svn_ra_reporter2_t py_ra_reporter = {
+ .finish_report = py_ra_report_finish,
+ .abort_report = py_ra_report_abort,
+ .link_path = py_ra_report_link_path,
+ .delete_path = py_ra_report_delete_path,
+ .set_path = py_ra_report_set_path,
+};
+
+
+
+/**
+ * Get libsvn_wc version information.
+ *
+ * :return: tuple with major, minor, patch version number and tag.
+ */
+static PyObject *version(PyObject *self)
+{
+ const svn_version_t *ver = svn_wc_version();
+ return Py_BuildValue("(iiis)", ver->major, ver->minor,
+ ver->patch, ver->tag);
+}
+
+static svn_error_t *py_wc_found_entry(const char *path, const svn_wc_entry_t *entry, void *walk_baton, apr_pool_t *pool)
+{
+ PyObject *fn = (PyObject *)walk_baton, *ret;
+ ret = PyObject_CallFunction(fn, "sO", path, py_entry(entry));
+ if (ret == NULL)
+ return py_svn_error();
+ return NULL;
+}
+
+static svn_wc_entry_callbacks_t py_wc_entry_callbacks = {
+ .found_entry = py_wc_found_entry
+};
+
+static void py_wc_notify_func(void *baton, const svn_wc_notify_t *notify, apr_pool_t *pool)
+{
+ /* FIXME */
+}
+
+typedef struct {
+ PyObject_HEAD
+ apr_pool_t *pool;
+ svn_wc_entry_t entry;
+} EntryObject;
+
+static void entry_dealloc(PyObject *self)
+{
+ apr_pool_destroy(((EntryObject *)self)->pool);
+ PyObject_Del(self);
+}
+
+static PyMemberDef entry_members[] = {
+ { "copyfrom_url", T_STRING, offsetof(EntryObject, entry.copyfrom_url), READONLY, NULL },
+ { "url", T_STRING, offsetof(EntryObject, entry.url), READONLY, NULL },
+ { "repos", T_STRING, offsetof(EntryObject, entry.repos), READONLY, NULL },
+ { "schedule", T_INT, offsetof(EntryObject, entry.schedule), READONLY, NULL },
+ { "kind", T_INT, offsetof(EntryObject, entry.kind), READONLY, NULL },
+ { "revision", T_LONG, offsetof(EntryObject, entry.revision), READONLY, NULL },
+ { "cmt_rev", T_LONG, offsetof(EntryObject, entry.cmt_rev), READONLY, NULL },
+ { NULL, }
+};
+
+PyTypeObject Entry_Type = {
+ PyObject_HEAD_INIT(&PyType_Type) 0,
+ .tp_name = "wc.Entry",
+ .tp_basicsize = sizeof(EntryObject),
+ .tp_dealloc = entry_dealloc,
+ .tp_members = entry_members,
+};
+
+static PyObject *py_entry(const svn_wc_entry_t *entry)
+{
+ EntryObject *ret = PyObject_New(EntryObject, &Entry_Type);
+ if (ret == NULL)
+ return NULL;
+
+ ret->pool = Pool();
+ if (ret->pool == NULL)
+ return NULL;
+ ret->entry = *svn_wc_entry_dup(entry, ret->pool);
+ return (PyObject *)ret;
+}
+
+typedef struct {
+ PyObject_HEAD
+ svn_wc_adm_access_t *adm;
+ apr_pool_t *pool;
+} AdmObject;
+
+static PyObject *adm_init(PyTypeObject *self, PyObject *args, PyObject *kwargs)
+{
+ PyObject *associated;
+ char *path;
+ bool write_lock=false;
+ int depth=0;
+ PyObject *cancel_func=Py_None;
+ svn_wc_adm_access_t *parent_wc;
+ AdmObject *ret;
+ char *kwnames[] = { "associated", "path", "write_lock", "depth", "cancel_func", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "Os|biO", kwnames, &associated, &path, &write_lock, &depth, &cancel_func))
+ return NULL;
+
+ ret = PyObject_New(AdmObject, &Adm_Type);
+ if (ret == NULL)
+ return NULL;
+
+ ret->pool = Pool();
+ if (ret->pool == NULL)
+ return NULL;
+ if (associated == Py_None) {
+ parent_wc = NULL;
+ } else {
+ parent_wc = ((AdmObject *)associated)->adm;
+ }
+ if (!check_error(svn_wc_adm_open3(&ret->adm, parent_wc, path,
+ write_lock, depth, py_cancel_func, cancel_func,
+ ret->pool)))
+ return NULL;
+
+ return (PyObject *)ret;
+}
+
+static PyObject *adm_access_path(PyObject *self)
+{
+ AdmObject *admobj = (AdmObject *)self;
+ return PyString_FromString(svn_wc_adm_access_path(admobj->adm));
+}
+
+static PyObject *adm_locked(PyObject *self)
+{
+ AdmObject *admobj = (AdmObject *)self;
+ return PyBool_FromLong(svn_wc_adm_locked(admobj->adm));
+}
+
+static PyObject *adm_prop_get(PyObject *self, PyObject *args)
+{
+ char *name, *path;
+ AdmObject *admobj = (AdmObject *)self;
+ const svn_string_t *value;
+ apr_pool_t *temp_pool;
+ PyObject *ret;
+
+ if (!PyArg_ParseTuple(args, "ss", &name, &path))
+ return NULL;
+
+ temp_pool = Pool();
+ if (temp_pool == NULL)
+ return NULL;
+ RUN_SVN_WITH_POOL(temp_pool, svn_wc_prop_get(&value, name, path, admobj->adm, temp_pool));
+ if (value == NULL || value->data == NULL) {
+ ret = Py_None;
+ } else {
+ ret = PyString_FromStringAndSize(value->data, value->len);
+ }
+ apr_pool_destroy(temp_pool);
+ return ret;
+}
+
+static PyObject *adm_prop_set(PyObject *self, PyObject *args)
+{
+ char *name, *value, *path;
+ AdmObject *admobj = (AdmObject *)self;
+ bool skip_checks=false;
+ apr_pool_t *temp_pool;
+ int vallen;
+ svn_string_t *cvalue;
+
+ if (!PyArg_ParseTuple(args, "ss#s|b", &name, &value, &vallen, &path, &skip_checks))
+ return NULL;
+
+ temp_pool = Pool();
+ if (temp_pool == NULL)
+ return NULL;
+ cvalue = svn_string_ncreate(value, vallen, temp_pool);
+ RUN_SVN_WITH_POOL(temp_pool, svn_wc_prop_set2(name, cvalue, path, admobj->adm,
+ skip_checks, temp_pool));
+ apr_pool_destroy(temp_pool);
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *adm_entries_read(PyObject *self, PyObject *args)
+{
+ apr_hash_t *entries;
+ AdmObject *admobj = (AdmObject *)self;
+ apr_pool_t *temp_pool;
+ bool show_hidden=false;
+ apr_hash_index_t *idx;
+ const char *key;
+ apr_ssize_t klen;
+ svn_wc_entry_t *entry;
+ PyObject *py_entries;
+
+ if (!PyArg_ParseTuple(args, "|b", &show_hidden))
+ return NULL;
+
+ temp_pool = Pool();
+ if (temp_pool == NULL)
+ return NULL;
+ RUN_SVN_WITH_POOL(temp_pool, svn_wc_entries_read(&entries, admobj->adm,
+ show_hidden, temp_pool));
+ py_entries = PyDict_New();
+ idx = apr_hash_first(temp_pool, entries);
+ while (idx != NULL) {
+ apr_hash_this(idx, (const void **)&key, &klen, (void **)&entry);
+ PyDict_SetItemString(py_entries, key, py_entry(entry));
+ idx = apr_hash_next(idx);
+ }
+ apr_pool_destroy(temp_pool);
+ return py_entries;
+}
+
+static PyObject *adm_walk_entries(PyObject *self, PyObject *args)
+{
+ char *path;
+ PyObject *callbacks;
+ bool show_hidden=false;
+ PyObject *cancel_func=Py_None;
+ apr_pool_t *temp_pool;
+ AdmObject *admobj = (AdmObject *)self;
+
+ if (!PyArg_ParseTuple(args, "sO|bO", &path, &callbacks, &show_hidden, &cancel_func))
+ return NULL;
+
+ temp_pool = Pool();
+ if (temp_pool == NULL)
+ return NULL;
+ RUN_SVN_WITH_POOL(temp_pool, svn_wc_walk_entries2(path, admobj->adm,
+ &py_wc_entry_callbacks, (void *)callbacks,
+ show_hidden, py_cancel_func, (void *)cancel_func,
+ temp_pool));
+ apr_pool_destroy(temp_pool);
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *adm_entry(PyObject *self, PyObject *args)
+{
+ char *path;
+ bool show_hidden=false;
+ apr_pool_t *temp_pool;
+ AdmObject *admobj = (AdmObject *)self;
+ const svn_wc_entry_t *entry;
+
+ if (!PyArg_ParseTuple(args, "s|b", &path, &show_hidden))
+ return NULL;
+
+ temp_pool = Pool();
+ if (temp_pool == NULL)
+ return NULL;
+ RUN_SVN_WITH_POOL(temp_pool, svn_wc_entry(&entry, path, admobj->adm, show_hidden, temp_pool));
+ apr_pool_destroy(temp_pool);
+
+ return py_entry(entry);
+}
+
+static PyObject *adm_get_prop_diffs(PyObject *self, PyObject *args)
+{
+ char *path;
+ apr_pool_t *temp_pool;
+ apr_array_header_t *propchanges;
+ apr_hash_t *original_props;
+ AdmObject *admobj = (AdmObject *)self;
+ svn_prop_t *el;
+ int i;
+ PyObject *py_propchanges, *py_orig_props;
+
+ if (!PyArg_ParseTuple(args, "s", &path))
+ return NULL;
+
+ temp_pool = Pool();
+ if (temp_pool == NULL)
+ return NULL;
+ RUN_SVN_WITH_POOL(temp_pool, svn_wc_get_prop_diffs(&propchanges, &original_props,
+ path, admobj->adm, temp_pool));
+ py_propchanges = PyList_New(propchanges->nelts);
+ for (i = 0; i < propchanges->nelts; i++) {
+ el = APR_ARRAY_IDX(propchanges, i, svn_prop_t *);
+ PyList_SetItem(py_propchanges, i,
+ Py_BuildValue("(ss#)", el->name, el->value->data, el->value->len));
+ }
+ py_orig_props = prop_hash_to_dict(original_props);
+ apr_pool_destroy(temp_pool);
+ return Py_BuildValue("(OO)", py_propchanges, py_orig_props);
+}
+
+static PyObject *adm_add(PyObject *self, PyObject *args)
+{
+ char *path, *copyfrom_url=NULL;
+ svn_revnum_t copyfrom_rev=-1;
+ PyObject *cancel_func=Py_None, *notify_func=Py_None;
+ AdmObject *admobj = (AdmObject *)self;
+ apr_pool_t *temp_pool;
+
+ temp_pool = Pool();
+ if (temp_pool == NULL)
+ return NULL;
+
+ if (!PyArg_ParseTuple(args, "s|zlOO", &path, ©from_url, ©from_rev, &cancel_func, ¬ify_func))
+ return NULL;
+
+ RUN_SVN_WITH_POOL(temp_pool, svn_wc_add2(path, admobj->adm, copyfrom_url,
+ copyfrom_rev, py_cancel_func,
+ (void *)cancel_func,
+ py_wc_notify_func,
+ (void *)notify_func,
+ temp_pool));
+ apr_pool_destroy(temp_pool);
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *adm_copy(PyObject *self, PyObject *args)
+{
+ AdmObject *admobj = (AdmObject *)self;
+ char *src, *dst;
+ PyObject *cancel_func=Py_None, *notify_func=Py_None;
+ apr_pool_t *temp_pool;
+
+ if (!PyArg_ParseTuple(args, "ss|OO", &src, &dst, &cancel_func, ¬ify_func))
+ return NULL;
+
+ temp_pool = Pool();
+ if (temp_pool == NULL)
+ return NULL;
+ RUN_SVN_WITH_POOL(temp_pool, svn_wc_copy2(src, admobj->adm, dst,
+ py_cancel_func, (void *)cancel_func,
+ py_wc_notify_func, (void *)notify_func,
+ temp_pool));
+ apr_pool_destroy(temp_pool);
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *adm_delete(PyObject *self, PyObject *args)
+{
+ AdmObject *admobj = (AdmObject *)self;
+ apr_pool_t *temp_pool;
+ char *path;
+ PyObject *cancel_func=Py_None, *notify_func=Py_None;
+
+ if (!PyArg_ParseTuple(args, "s|OO", &path, &cancel_func, ¬ify_func))
+ return NULL;
+
+ temp_pool = Pool();
+ if (temp_pool == NULL)
+ return NULL;
+ RUN_SVN_WITH_POOL(temp_pool, svn_wc_delete2(path, admobj->adm,
+ py_cancel_func, (void *)cancel_func,
+ py_wc_notify_func, (void *)notify_func,
+ temp_pool));
+ apr_pool_destroy(temp_pool);
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *adm_crawl_revisions(PyObject *self, PyObject *args)
+{
+ char *path;
+ PyObject *reporter;
+ bool restore_files=true, recurse=true, use_commit_times=true;
+ PyObject *notify_func=Py_None;
+ apr_pool_t *temp_pool;
+ AdmObject *admobj = (AdmObject *)self;
+ svn_wc_traversal_info_t *traversal_info;
+
+ if (!PyArg_ParseTuple(args, "sO|bbbO", &path, &reporter, &restore_files, &recurse, &use_commit_times,
+ ¬ify_func))
+ return NULL;
+
+ temp_pool = Pool();
+ if (temp_pool == NULL)
+ return NULL;
+ traversal_info = svn_wc_init_traversal_info(temp_pool);
+ RUN_SVN_WITH_POOL(temp_pool, svn_wc_crawl_revisions2(path, admobj->adm,
+ &py_ra_reporter, (void *)reporter,
+ restore_files, recurse, use_commit_times,
+ py_wc_notify_func, (void *)notify_func,
+ traversal_info, temp_pool));
+ apr_pool_destroy(temp_pool);
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *adm_get_update_editor(PyObject *self, PyObject *args)
+{
+ char *target;
+ bool use_commit_times=true, recurse=true;
+ PyObject * notify_func=Py_None, *cancel_func=Py_None;
+ char *diff3_cmd=NULL;
+ const svn_delta_editor_t *editor;
+ AdmObject *admobj = (AdmObject *)self;
+ void *edit_baton;
+ apr_pool_t *pool;
+ svn_revnum_t *latest_revnum;
+
+ if (!PyArg_ParseTuple(args, "s|bbOOz", &target, &use_commit_times, &recurse, ¬ify_func, &cancel_func, &diff3_cmd))
+ return NULL;
+
+ pool = Pool();
+ if (pool == NULL)
+ return NULL;
+ latest_revnum = (svn_revnum_t *)apr_palloc(pool, sizeof(svn_revnum_t));
+ if (!check_error(svn_wc_get_update_editor2(latest_revnum, admobj->adm, target,
+ use_commit_times, recurse, py_wc_notify_func, (void *)notify_func,
+ py_cancel_func, (void *)cancel_func, diff3_cmd, &editor, &edit_baton,
+ NULL, pool))) {
+ apr_pool_destroy(pool);
+ return NULL;
+ }
+ return new_editor_object(editor, edit_baton, pool, &Editor_Type, NULL, NULL);
+}
+
+static bool py_dict_to_wcprop_changes(PyObject *dict, apr_pool_t *pool, apr_array_header_t **ret)
+{
+ PyObject *key, *val;
+ Py_ssize_t idx;
+
+ if (dict == Py_None) {
+ *ret = NULL;
+ return true;
+ }
+
+ if (!PyDict_Check(dict)) {
+ PyErr_SetString(PyExc_TypeError, "Expected dictionary with property changes");
+ return false;
+ }
+
+ *ret = apr_array_make(pool, PyDict_Size(dict), sizeof(char *));
+
+ while (PyDict_Next(dict, &idx, &key, &val)) {
+ svn_prop_t *prop = apr_palloc(pool, sizeof(svn_prop_t));
+ prop->name = PyString_AsString(key);
+ if (val == Py_None) {
+ prop->value = NULL;
+ } else {
+ prop->value = svn_string_ncreate(PyString_AsString(val), PyString_Size(val), pool);
+ }
+ APR_ARRAY_PUSH(*ret, svn_prop_t *) = prop;
+ }
+
+ return true;
+}
+
+static PyObject *adm_process_committed(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ char *path, *rev_date, *rev_author;
+ bool recurse, remove_lock = false;
+ unsigned char *digest = NULL;
+ svn_revnum_t new_revnum;
+ PyObject *py_wcprop_changes = Py_None;
+ apr_array_header_t *wcprop_changes;
+ AdmObject *admobj = (AdmObject *)self;
+ apr_pool_t *temp_pool;
+ char *kwnames[] = { "path", "recurse", "new_revnum", "rev_date", "rev_author",
+ "wcprop_changes", "remove_lock", "digest", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "sblss|Obs", kwnames,
+ &path, &recurse, &new_revnum, &rev_date,
+ &rev_author, &py_wcprop_changes,
+ &remove_lock, &digest))
+ return NULL;
+
+ temp_pool = Pool();
+ if (temp_pool == NULL)
+ return NULL;
+
+ if (!py_dict_to_wcprop_changes(py_wcprop_changes, temp_pool, &wcprop_changes)) {
+ apr_pool_destroy(temp_pool);
+ return NULL;
+ }
+
+ RUN_SVN_WITH_POOL(temp_pool, svn_wc_process_committed3(path, admobj->adm, recurse, new_revnum,
+ rev_date, rev_author, wcprop_changes,
+ remove_lock, digest, temp_pool));
+
+ apr_pool_destroy(temp_pool);
+
+ return Py_None;
+}
+
+static PyObject *adm_close(PyObject *self)
+{
+ AdmObject *admobj = (AdmObject *)self;
+ if (admobj->adm != NULL) {
+ svn_wc_adm_close(admobj->adm);
+ admobj->adm = NULL;
+ }
+
+ Py_RETURN_NONE;
+}
+
+static void adm_dealloc(PyObject *self)
+{
+ apr_pool_destroy(((AdmObject *)self)->pool);
+ PyObject_Del(self);
+}
+
+static PyMethodDef adm_methods[] = {
+ { "prop_set", adm_prop_set, METH_VARARGS, NULL },
+ { "access_path", (PyCFunction)adm_access_path, METH_NOARGS, NULL },
+ { "prop_get", adm_prop_get, METH_VARARGS, NULL },
+ { "entries_read", adm_entries_read, METH_VARARGS, NULL },
+ { "walk_entries", adm_walk_entries, METH_VARARGS, NULL },
+ { "locked", (PyCFunction)adm_locked, METH_NOARGS, NULL },
+ { "get_prop_diffs", adm_get_prop_diffs, METH_VARARGS, NULL },
+ { "add", adm_add, METH_VARARGS, NULL },
+ { "copy", adm_copy, METH_VARARGS, NULL },
+ { "delete", adm_delete, METH_VARARGS, NULL },
+ { "crawl_revisions", adm_crawl_revisions, METH_VARARGS, NULL },
+ { "get_update_editor", adm_get_update_editor, METH_VARARGS, NULL },
+ { "close", (PyCFunction)adm_close, METH_NOARGS, NULL },
+ { "entry", (PyCFunction)adm_entry, METH_VARARGS, NULL },
+ { "process_committed", (PyCFunction)adm_process_committed, METH_VARARGS|METH_KEYWORDS, NULL },
+ { NULL, }
+};
+
+PyTypeObject Adm_Type = {
+ PyObject_HEAD_INIT(&PyType_Type) 0,
+ .tp_name = "wc.WorkingCopy",
+ .tp_basicsize = sizeof(AdmObject),
+ .tp_new = adm_init,
+ .tp_dealloc = adm_dealloc,
+ .tp_methods = adm_methods,
+};
+
+/**
+ * Determine the revision status of a specified working copy.
+ *
+ * :return: Tuple with minimum and maximum revnums found, whether the
+ * working copy was switched and whether it was modified.
+ */
+static PyObject *revision_status(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ char *kwnames[] = { "wc_path", "trail_url", "committed", "cancel_func", NULL };
+ char *wc_path, *trail_url=NULL;
+ bool committed=false;
+ PyObject *cancel_func=Py_None, *ret;
+ svn_wc_revision_status_t *revstatus;
+ apr_pool_t *temp_pool;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|zbO", kwnames, &wc_path, &trail_url, &committed,
+ &cancel_func))
+ return NULL;
+
+ temp_pool = Pool();
+ if (temp_pool == NULL)
+ return NULL;
+ RUN_SVN_WITH_POOL(temp_pool, svn_wc_revision_status(&revstatus, wc_path, trail_url,
+ committed, py_cancel_func, cancel_func, temp_pool));
+ ret = Py_BuildValue("(llbb)", revstatus->min_rev, revstatus->max_rev,
+ revstatus->switched, revstatus->modified);
+ apr_pool_destroy(temp_pool);
+ return ret;
+}
+
+static PyObject *is_normal_prop(PyObject *self, PyObject *args)
+{
+ char *name;
+
+ if (!PyArg_ParseTuple(args, "s", &name))
+ return NULL;
+
+ return PyBool_FromLong(svn_wc_is_normal_prop(name));
+}
+
+static PyObject *is_wc_prop(PyObject *self, PyObject *args)
+{
+ char *name;
+
+ if (!PyArg_ParseTuple(args, "s", &name))
+ return NULL;
+
+ return PyBool_FromLong(svn_wc_is_wc_prop(name));
+}
+
+static PyObject *is_entry_prop(PyObject *self, PyObject *args)
+{
+ char *name;
+
+ if (!PyArg_ParseTuple(args, "s", &name))
+ return NULL;
+
+ return PyBool_FromLong(svn_wc_is_entry_prop(name));
+}
+
+static PyObject *get_adm_dir(PyObject *self)
+{
+ apr_pool_t *pool;
+ PyObject *ret;
+ const char *dir;
+ pool = Pool();
+ if (pool == NULL)
+ return NULL;
+ dir = svn_wc_get_adm_dir(pool);
+ ret = PyString_FromString(dir);
+ apr_pool_destroy(pool);
+ return ret;
+}
+
+static PyObject *get_pristine_copy_path(PyObject *self, PyObject *args)
+{
+ apr_pool_t *pool;
+ const char *pristine_path;
+ char *path;
+ PyObject *ret;
+
+ if (!PyArg_ParseTuple(args, "s", &path))
+ return NULL;
+
+ pool = Pool();
+ if (pool == NULL)
+ return NULL;
+ RUN_SVN_WITH_POOL(pool, svn_wc_get_pristine_copy_path(path, &pristine_path, pool));
+ ret = PyString_FromString(pristine_path);
+ apr_pool_destroy(pool);
+ return ret;
+}
+
+static PyObject *get_default_ignores(PyObject *self, PyObject *args)
+{
+ apr_array_header_t *patterns;
+ apr_pool_t *pool;
+ apr_hash_t *hash_config;
+ apr_ssize_t idx = 0;
+ int i = 0;
+ PyObject *pyk, *pyv, *config;
+ PyObject *ret;
+
+ if (!PyArg_ParseTuple(args, "O", &config))
+ return NULL;
+
+ pool = Pool();
+ if (pool == NULL)
+ return NULL;
+ hash_config = apr_hash_make(pool);
+ while (PyDict_Next(config, &idx, &pyk, &pyv))
+ apr_hash_set(hash_config, (char *)PyString_AsString(pyk), PyString_Size(pyk), (char *)PyString_AsString(pyv));
+ RUN_SVN_WITH_POOL(pool, svn_wc_get_default_ignores(&patterns, hash_config, pool));
+ ret = PyList_New(patterns->nelts);
+ for (i = 0; i < patterns->nelts; i++) {
+ PyList_SetItem(ret, i, PyString_FromString(APR_ARRAY_IDX(patterns, i, char *)));
+ }
+ apr_pool_destroy(pool);
+ return ret;
+}
+
+static PyObject *ensure_adm(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ char *path, *uuid, *url;
+ char *repos=NULL;
+ svn_revnum_t rev=-1;
+ apr_pool_t *pool;
+ char *kwnames[] = { "path", "uuid", "url", "repos", "rev", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "sss|sl", kwnames,
+ &path, &uuid, &url, &repos, &rev))
+ return NULL;
+
+ pool = Pool();
+ if (pool == NULL)
+ return NULL;
+ RUN_SVN_WITH_POOL(pool,
+ svn_wc_ensure_adm2(path, uuid, url, repos, rev, pool));
+ apr_pool_destroy(pool);
+ Py_RETURN_NONE;
+}
+
+static PyObject *check_wc(PyObject *self, PyObject *args)
+{
+ char *path;
+ apr_pool_t *pool;
+ int wc_format;
+
+ if (!PyArg_ParseTuple(args, "s", &path))
+ return NULL;
+
+ pool = Pool();
+ if (pool == NULL)
+ return NULL;
+ RUN_SVN_WITH_POOL(pool, svn_wc_check_wc(path, &wc_format, pool));
+ apr_pool_destroy(pool);
+ return PyLong_FromLong(wc_format);
+}
+
+static PyMethodDef wc_methods[] = {
+ { "check_wc", check_wc, METH_VARARGS, NULL },
+ { "ensure_adm", (PyCFunction)ensure_adm, METH_KEYWORDS|METH_VARARGS, NULL },
+ { "get_default_ignores", get_default_ignores, METH_VARARGS, NULL },
+ { "get_adm_dir", (PyCFunction)get_adm_dir, METH_NOARGS, NULL },
+ { "get_pristine_copy_path", get_pristine_copy_path, METH_VARARGS, NULL },
+ { "is_normal_prop", is_normal_prop, METH_VARARGS, NULL },
+ { "is_entry_prop", is_entry_prop, METH_VARARGS, NULL },
+ { "is_wc_prop", is_wc_prop, METH_VARARGS, NULL },
+ { "revision_status", (PyCFunction)revision_status, METH_KEYWORDS|METH_VARARGS, NULL },
+ { "version", (PyCFunction)version, METH_NOARGS, NULL },
+ { NULL, }
+};
+
+void initwc(void)
+{
+ PyObject *mod;
+
+ if (PyType_Ready(&Entry_Type) < 0)
+ return;
+
+ if (PyType_Ready(&Adm_Type) < 0)
+ return;
+
+ if (PyType_Ready(&Editor_Type) < 0)
+ return;
+
+ if (PyType_Ready(&FileEditor_Type) < 0)
+ return;
+
+ if (PyType_Ready(&DirectoryEditor_Type) < 0)
+ return;
+
+ if (PyType_Ready(&TxDeltaWindowHandler_Type) < 0)
+ return;
+
+ apr_initialize();
+
+ mod = Py_InitModule3("wc", wc_methods, "Working Copies");
+ if (mod == NULL)
+ return;
+
+ PyModule_AddIntConstant(mod, "SCHEDULE_NORMAL", 0);
+ PyModule_AddIntConstant(mod, "SCHEDULE_ADD", 1);
+ PyModule_AddIntConstant(mod, "SCHEDULE_DELETE", 2);
+ PyModule_AddIntConstant(mod, "SCHEDULE_REPLACE", 3);
+
+ PyModule_AddObject(mod, "WorkingCopy", (PyObject *)&Adm_Type);
+ Py_INCREF(&Adm_Type);
+}
"""Checkouts and working trees (working copies)."""
import bzrlib, bzrlib.add
-from bzrlib import urlutils
+from bzrlib import osutils, urlutils, ignores
from bzrlib.branch import PullResult
from bzrlib.bzrdir import BzrDirFormat, BzrDir
from bzrlib.errors import (InvalidRevisionId, NotBranchError, NoSuchFile,
NoRepositoryPresent, BzrError, UninitializableFormat,
- OutOfDateTree)
+ OutOfDateTree, UnsupportedFormatError)
from bzrlib.inventory import Inventory, InventoryFile, InventoryLink
from bzrlib.lockable_files import TransportLock, LockableFiles
from bzrlib.lockdir import LockDir
-from bzrlib.osutils import file_kind, fingerprint_file, supports_executable
from bzrlib.revision import NULL_REVISION
from bzrlib.trace import mutter
from bzrlib.revisiontree import RevisionTree
from bzrlib.plugins.svn.branch import SvnBranch
from bzrlib.plugins.svn.commit import _revision_id_to_svk_feature
from bzrlib.plugins.svn.convert import SvnConverter
-from bzrlib.plugins.svn.errors import LocalCommitsUnsupported, NoSvnRepositoryPresent, ERR_FS_TXN_OUT_OF_DATE, ERR_ENTRY_EXISTS, ERR_WC_PATH_NOT_FOUND, ERR_WC_NOT_DIRECTORY
+from bzrlib.plugins.svn.errors import NoSvnRepositoryPresent, ERR_FS_TXN_OUT_OF_DATE, ERR_ENTRY_EXISTS, ERR_WC_PATH_NOT_FOUND, ERR_WC_NOT_DIRECTORY, ERR_WC_UNSUPPORTED_FORMAT
from bzrlib.plugins.svn.mapping import (SVN_PROP_BZR_ANCESTRY, SVN_PROP_BZR_FILEIDS,
SVN_PROP_BZR_REVISION_ID, SVN_PROP_BZR_REVISION_INFO,
generate_revision_metadata)
from bzrlib.plugins.svn.repository import SvnRepository
from bzrlib.plugins.svn.svk import SVN_PROP_SVK_MERGE, parse_svk_features, serialize_svk_features
from bzrlib.plugins.svn.mapping import escape_svn_path
-from bzrlib.plugins.svn.transport import (SvnRaTransport, bzr_to_svn_url, create_svn_client,
- svn_config)
+from bzrlib.plugins.svn.transport import (SvnRaTransport, bzr_to_svn_url, svn_config)
from bzrlib.plugins.svn.tree import SvnBasisTree
import os
import urllib
-import svn.core, svn.wc
-from svn.core import SubversionException
+import core, wc
+from core import SubversionException, time_to_cstring
-from bzrlib.plugins.svn.errors import NoCheckoutSupport
from bzrlib.plugins.svn.format import get_rich_root_format
def generate_ignore_list(ignore_map):
class SvnWorkingTree(WorkingTree):
"""WorkingTree implementation that uses a Subversion Working Copy for storage."""
def __init__(self, bzrdir, local_path, branch):
- self._format = SvnWorkingTreeFormat()
+ version = wc.check_wc(local_path)
+ self._format = SvnWorkingTreeFormat(version)
self.basedir = local_path
assert isinstance(self.basedir, unicode)
self.bzrdir = bzrdir
self._branch = branch
- self.base_revnum = 0
- self.client_ctx = create_svn_client(bzrdir.svn_url)
- self.client_ctx.log_msg_func2 = \
- svn.client.svn_swig_py_get_commit_log_func
-
self._get_wc()
- status = svn.wc.revision_status(self.basedir, None, True, None, None)
- self.base_revnum = status.max_rev
+ (min_rev, max_rev, switch, modified) = wc.revision_status(self.basedir, None, True, None)
+ self.base_revnum = max_rev
self.base_tree = SvnBasisTree(self)
self.base_revid = branch.generate_revision_id(self.base_revnum)
self.read_working_inventory()
- self.controldir = os.path.join(self.basedir, svn.wc.get_adm_dir(),
- 'bzr')
+ self.controldir = os.path.join(self.basedir, wc.get_adm_dir(), 'bzr')
try:
os.makedirs(self.controldir)
os.makedirs(os.path.join(self.controldir, 'lock'))
except OSError:
pass
control_transport = bzrdir.transport.clone(urlutils.join(
- svn.wc.get_adm_dir(), 'bzr'))
+ wc.get_adm_dir(), 'bzr'))
self._control_files = LockableFiles(control_transport, 'lock', LockDir)
def get_ignore_list(self):
- ignores = set([svn.wc.get_adm_dir()])
- ignores.update(svn.wc.get_default_ignores(svn_config))
+ ignore_globs = set([wc.get_adm_dir()])
+ ignore_globs.update(ignores.get_runtime_ignores())
+ ignore_globs.update(ignores.get_user_ignores())
- def dir_add(wc, prefix, patprefix):
- ignorestr = svn.wc.prop_get(properties.PROP_IGNORE,
- self.abspath(prefix).rstrip("/"), wc)
+ def dir_add(adm, prefix, patprefix):
+ ignorestr = adm.prop_get(properties.PROP_IGNORE,
+ self.abspath(prefix).rstrip("/"))
if ignorestr is not None:
for pat in ignorestr.splitlines():
- ignores.add(urlutils.joinpath(patprefix, pat))
+ ignore_globs.add(urlutils.joinpath(patprefix, pat))
- entries = svn.wc.entries_read(wc, False)
+ entries = adm.entries_read(False)
for entry in entries:
if entry == "":
continue
# Ignore ignores on things that aren't directories
- if entries[entry].kind != svn.core.svn_node_dir:
+ if entries[entry].kind != core.NODE_DIR:
continue
subprefix = os.path.join(prefix, entry)
- subwc = svn.wc.adm_open3(wc, self.abspath(subprefix), False,
- 0, None)
+ subwc = wc.WorkingCopy(adm, self.abspath(subprefix))
try:
dir_add(subwc, subprefix, urlutils.joinpath(patprefix, entry))
finally:
- svn.wc.adm_close(subwc)
+ subwc.close()
- wc = self._get_wc()
+ adm = self._get_wc()
try:
- dir_add(wc, "", ".")
+ dir_add(adm, "", ".")
finally:
- svn.wc.adm_close(wc)
+ adm.close()
- return ignores
+ return ignore_globs
def is_control_filename(self, path):
- return svn.wc.is_adm_dir(path)
+ return wc.is_adm_dir(path)
def apply_inventory_delta(self, changes):
raise NotImplementedError(self.apply_inventory_delta)
- def update(self, change_reporter=None):
- rev = svn.core.svn_opt_revision_t()
- rev.kind = svn.core.svn_opt_revision_head
- svn.client.update(self.basedir, rev, True, self.client_ctx)
+ def _update(self, revnum=None):
+ if revnum is None:
+ # FIXME: should be able to use -1 here
+ revnum = self.branch.get_revnum()
+ adm = self._get_wc()
+ # FIXME: honor SVN_CONFIG_SECTION_HELPERS:SVN_CONFIG_OPTION_DIFF3_CMD
+ # FIXME: honor SVN_CONFIG_SECTION_MISCELLANY:SVN_CONFIG_OPTION_USE_COMMIT_TIMES
+ # FIXME: honor SVN_CONFIG_SECTION_MISCELLANY:SVN_CONFIG_OPTION_PRESERVED_CF_EXTS
+ try:
+ editor = adm.get_update_editor(self.basedir, use_commit_times=False, recurse=True)
+ assert editor is not None
+ conn = self.branch.repository.transport.get_connection()
+ try:
+ reporter = conn.do_update(revnum, True, editor)
+ adm.crawl_revisions(self.basedir, reporter, restore_files=False, recurse=True,
+ use_commit_times=False)
+ finally:
+ self.branch.repository.transport.add_connection(conn)
+ # FIXME: handle externals
+ finally:
+ adm.close()
+ return revnum
+
+ def update(self, change_reporter=None, possible_transports=None, revnum=None):
+ orig_revnum = self.base_revnum
+ self.base_revnum = self._update(revnum)
+ self.base_revid = self.branch.generate_revision_id(self.base_revnum)
+ self.base_tree = None
+ self.read_working_inventory()
+ return self.base_revnum - orig_revnum
def remove(self, files, verbose=False, to_file=None):
# FIXME: Use to_file argument
# FIXME: Use verbose argument
assert isinstance(files, list)
- wc = self._get_wc(write_lock=True)
+ adm = self._get_wc(write_lock=True)
try:
for file in files:
- svn.wc.delete2(self.abspath(file), wc, None, None, None)
+ adm.delete(self.abspath(file))
finally:
- svn.wc.adm_close(wc)
+ adm.close()
for file in files:
self._change_fileid_mapping(None, file)
self.read_working_inventory()
def _get_wc(self, relpath="", write_lock=False):
- return svn.wc.adm_open3(None, self.abspath(relpath).rstrip("/"),
- write_lock, 0, None)
+ return wc.WorkingCopy(None, self.abspath(relpath).rstrip("/"), write_lock)
def _get_rel_wc(self, relpath, write_lock=False):
dir = os.path.dirname(relpath)
def move(self, from_paths, to_dir=None, after=False, **kwargs):
# FIXME: Use after argument
assert after != True
- revt = svn.core.svn_opt_revision_t()
- revt.kind = svn.core.svn_opt_revision_working
for entry in from_paths:
try:
to_wc = self._get_wc(to_dir, write_lock=True)
- svn.wc.copy(self.abspath(entry), to_wc,
+ to_wc.copy(self.abspath(entry),
os.path.basename(entry), None, None)
finally:
- svn.wc.adm_close(to_wc)
+ to_wc.close()
try:
from_wc = self._get_wc(write_lock=True)
- svn.wc.delete2(self.abspath(entry), from_wc, None, None, None)
+ from_wc.delete(self.abspath(entry))
finally:
- svn.wc.adm_close(from_wc)
+ from_wc.close()
new_name = urlutils.join(to_dir, os.path.basename(entry))
self._change_fileid_mapping(self.inventory.path2id(entry), new_name)
self._change_fileid_mapping(None, entry)
def rename_one(self, from_rel, to_rel, after=False):
# FIXME: Use after
assert after != True
- revt = svn.core.svn_opt_revision_t()
- revt.kind = svn.core.svn_opt_revision_unspecified
(to_wc, to_file) = self._get_rel_wc(to_rel, write_lock=True)
if os.path.dirname(from_rel) == os.path.dirname(to_rel):
# Prevent lock contention
(from_wc, _) = self._get_rel_wc(from_rel, write_lock=True)
from_id = self.inventory.path2id(from_rel)
try:
- svn.wc.copy(self.abspath(from_rel), to_wc, to_file, None, None)
- svn.wc.delete2(self.abspath(from_rel), from_wc, None, None, None)
+ to_wc.copy(self.abspath(from_rel), to_file)
+ from_wc.delete(self.abspath(from_rel))
finally:
- svn.wc.adm_close(to_wc)
+ to_wc.close()
self._change_fileid_mapping(None, from_rel)
self._change_fileid_mapping(from_id, to_rel)
self.read_working_inventory()
assert isinstance(path, str)
rp = self.branch.unprefix(path)
- entry = self.base_tree.id_map[rp]
+ entry = self.basis_tree().id_map[rp]
assert entry[0] is not None
assert isinstance(entry[0], str), "fileid %r for %r is not a string" % (entry[0], path)
return entry
file = InventoryFile(id, os.path.basename(relpath), parent_id)
file.revision = revid
try:
- data = fingerprint_file(open(self.abspath(relpath)))
+ data = osutils.fingerprint_file(open(self.abspath(relpath)))
file.text_sha1 = data['sha1']
file.text_size = data['size']
file.executable = self.is_executable(id, relpath)
pass
def find_copies(url, relpath=""):
- wc = self._get_wc(relpath)
- entries = svn.wc.entries_read(wc, False)
+ adm = self._get_wc(relpath)
+ entries = adm.entries_read(False)
for entry in entries.values():
subrelpath = os.path.join(relpath, entry.name)
if entry.name == "" or entry.kind != 'directory':
if ((entry.copyfrom_url == url or entry.url == url) and
- not (entry.schedule in (svn.wc.schedule_delete,
- svn.wc.schedule_replace))):
+ not (entry.schedule in (wc.SCHEDULE_DELETE,
+ wc.SCHEDULE_REPLACE))):
yield os.path.join(
self.branch.get_branch_path().strip("/"),
subrelpath)
else:
find_copies(subrelpath)
- svn.wc.adm_close(wc)
+ adm.close()
def find_ids(entry, rootwc):
relpath = urllib.unquote(entry.url[len(entry.repos):].strip("/"))
- assert entry.schedule in (svn.wc.schedule_normal,
- svn.wc.schedule_delete,
- svn.wc.schedule_add,
- svn.wc.schedule_replace)
- if entry.schedule == svn.wc.schedule_normal:
+ assert entry.schedule in (wc.SCHEDULE_NORMAL,
+ wc.SCHEDULE_DELETE,
+ wc.SCHEDULE_ADD,
+ wc.SCHEDULE_REPLACE)
+ if entry.schedule == wc.SCHEDULE_NORMAL:
assert entry.revision >= 0
# Keep old id
return self.path_to_file_id(entry.cmt_rev, entry.revision,
relpath)
- elif entry.schedule == svn.wc.schedule_delete:
+ elif entry.schedule == wc.SCHEDULE_DELETE:
return (None, None)
- elif (entry.schedule == svn.wc.schedule_add or
- entry.schedule == svn.wc.schedule_replace):
+ elif (entry.schedule == wc.SCHEDULE_ADD or
+ entry.schedule == wc.SCHEDULE_REPLACE):
# See if the file this file was copied from disappeared
# and has no other copies -> in that case, take id of other file
if (entry.copyfrom_url and
# FIXME: Generate more random file ids
return ("NEW-" + escape_svn_path(entry.url[len(entry.repos):].strip("/")), None)
- def add_dir_to_inv(relpath, wc, parent_id):
+ def add_dir_to_inv(relpath, adm, parent_id):
assert isinstance(relpath, unicode)
- entries = svn.wc.entries_read(wc, False)
+ entries = adm.entries_read(False)
entry = entries[""]
assert parent_id is None or isinstance(parent_id, str), \
"%r is not a string" % parent_id
entry = entries[name]
assert entry
- if entry.kind == svn.core.svn_node_dir:
- subwc = svn.wc.adm_open3(wc, self.abspath(subrelpath),
- False, 0, None)
+ if entry.kind == core.NODE_DIR:
+ subwc = wc.WorkingCopy(adm, self.abspath(subrelpath))
try:
add_dir_to_inv(subrelpath, subwc, id)
finally:
- svn.wc.adm_close(subwc)
+ subwc.close()
else:
(subid, subrevid) = find_ids(entry, rootwc)
if subid:
try:
add_dir_to_inv(u"", rootwc, None)
finally:
- svn.wc.adm_close(rootwc)
+ rootwc.close()
self._set_inventory(inv, dirty=False)
return inv
if revid is None or revid == NULL_REVISION:
self.base_revid = revid
self.base_revnum = 0
- self.base_tree = RevisionTree(self, Inventory(), revid)
+ self.base_tree = None
return
rev = self.branch.lookup_revision_id(revid)
self.base_revnum = rev
self.base_revid = revid
- self.base_tree = SvnBasisTree(self)
+ self.base_tree = None
# TODO: Implement more efficient version
newrev = self.branch.repository.get_revision(revid)
newrevtree = self.branch.repository.revision_tree(revid)
- def update_settings(wc, path):
+ def update_settings(adm, path):
id = newrevtree.inventory.path2id(path)
mutter("Updating settings for %r", id)
revnum = self.branch.lookup_revision_id(
newrevtree.inventory[id].revision)
- svn.wc.process_committed2(self.abspath(path).rstrip("/"), wc,
+ adm.process_committed(self.abspath(path).rstrip("/"),
False, revnum,
- svn.core.svn_time_to_cstring(newrev.timestamp),
- newrev.committer, None, False)
+ time_to_cstring(newrev.timestamp),
+ newrev.committer)
if newrevtree.inventory[id].kind != 'directory':
return
- entries = svn.wc.entries_read(wc, True)
+ entries = adm.entries_read(True)
for entry in entries:
if entry == "":
continue
- subwc = svn.wc.adm_open3(wc, os.path.join(self.basedir, path, entry), False, 0, None)
+ subwc = wc.WorkingCopy(adm, os.path.join(self.basedir, path, entry), write_lock=True)
try:
update_settings(subwc, os.path.join(path, entry))
finally:
- svn.wc.adm_close(subwc)
+ subwc.close()
# Set proper version for all files in the wc
- wc = self._get_wc(write_lock=True)
+ adm = self._get_wc(write_lock=True)
try:
- update_settings(wc, "")
+ update_settings(adm, "")
finally:
- svn.wc.adm_close(wc)
+ adm.close()
self.base_revid = revid
- def commit(self, message=None, message_callback=None, revprops=None,
- timestamp=None, timezone=None, committer=None, rev_id=None,
- allow_pointless=True, strict=False, verbose=False, local=False,
- reporter=None, config=None, specific_files=None, author=None):
- if author is not None:
- revprops['author'] = author
- # FIXME: Use allow_pointless
- # FIXME: Use verbose
- # FIXME: Use reporter
- # FIXME: Use strict
- if local:
- raise LocalCommitsUnsupported()
-
- if specific_files:
- specific_files = [self.abspath(x).encode('utf8') for x in specific_files]
- else:
- specific_files = [self.basedir.encode('utf8')]
-
- if message_callback is not None:
- def log_message_func(items, pool):
- """ Simple log message provider for unit tests. """
- return message_callback(self).encode("utf-8")
- else:
- assert isinstance(message, basestring)
- def log_message_func(items, pool):
- """ Simple log message provider for unit tests. """
- return message.encode("utf-8")
-
- self.client_ctx.log_msg_baton2 = log_message_func
- if rev_id is not None:
- extra = "%d %s\n" % (self.branch.revno()+1, rev_id)
- else:
- extra = ""
- wc = self._get_wc(write_lock=True)
- try:
- svn.wc.prop_set(SVN_PROP_BZR_REVISION_ID+str(self.branch.mapping.scheme),
- self._get_bzr_revids(self._get_base_branch_props()) + extra,
- self.basedir, wc)
- svn.wc.prop_set(SVN_PROP_BZR_REVISION_INFO,
- generate_revision_metadata(timestamp,
- timezone,
- committer,
- revprops),
- self.basedir, wc)
- finally:
- svn.wc.adm_close(wc)
-
- try:
- try:
- commit_info = svn.client.commit3(specific_files, True, False,
- self.client_ctx)
- except SubversionException, (_, num):
- if num == ERR_FS_TXN_OUT_OF_DATE:
- raise OutOfDateTree(self)
- raise
- except:
- # Reset properties so the next subversion commit won't
- # accidently set these properties.
- wc = self._get_wc(write_lock=True)
- base_branch_props = self._get_base_branch_props()
- svn.wc.prop_set(SVN_PROP_BZR_REVISION_ID+str(self.branch.mapping.scheme),
- self._get_bzr_revids(base_branch_props), self.basedir, wc)
- svn.wc.prop_set(SVN_PROP_BZR_REVISION_INFO,
- base_branch_props.get(SVN_PROP_BZR_REVISION_INFO, ""),
- self.basedir, wc)
- svn.wc.adm_close(wc)
- raise
-
- self.client_ctx.log_msg_baton2 = None
-
- revid = self.branch.generate_revision_id(commit_info.revision)
-
- self.base_revid = revid
- self.base_revnum = commit_info.revision
- self.base_tree = SvnBasisTree(self)
-
- return revid
-
def smart_add(self, file_list, recurse=True, action=None, save=True):
assert isinstance(recurse, bool)
if action is None:
todo = []
file_path = os.path.abspath(file_path)
f = self.relpath(file_path)
- wc = self._get_wc(os.path.dirname(f), write_lock=True)
+ adm = self._get_wc(os.path.dirname(f), write_lock=True)
try:
if not self.inventory.has_filename(f):
if save:
mutter('adding %r', file_path)
- svn.wc.add2(file_path, wc, None, 0, None, None, None)
+ adm.add(file_path, None, 0, None, None, None)
added.append(file_path)
- if recurse and file_kind(file_path) == 'directory':
+ if recurse and osutils.file_kind(file_path) == 'directory':
# Filter out ignored files and update ignored
for c in os.listdir(file_path):
if self.is_control_filename(c):
ignored.setdefault(ignore_glob, []).append(c_path)
todo.append(c_path)
finally:
- svn.wc.adm_close(wc)
+ adm.close()
if todo != []:
cadded, cignored = self.smart_add(todo, recurse, action, save)
added.extend(cadded)
ids = iter(ids)
assert isinstance(files, list)
for f in files:
- wc = self._get_wc(os.path.dirname(f), write_lock=True)
+ adm = self._get_wc(os.path.dirname(f), write_lock=True)
try:
try:
- svn.wc.add2(os.path.join(self.basedir, f), wc, None, 0,
- None, None, None)
+ adm.add(os.path.join(self.basedir, f))
if ids is not None:
- self._change_fileid_mapping(ids.next(), f, wc)
+ self._change_fileid_mapping(ids.next(), f, adm)
except SubversionException, (_, num):
if num == ERR_ENTRY_EXISTS:
continue
raise NoSuchFile(path=f)
raise
finally:
- svn.wc.adm_close(wc)
+ adm.close()
self.read_working_inventory()
def basis_tree(self):
if self.base_revid is None or self.base_revid == NULL_REVISION:
return self.branch.repository.revision_tree(self.base_revid)
+ if self.base_tree is None:
+ self.base_tree = SvnBasisTree(self)
+
return self.base_tree
def pull(self, source, overwrite=False, stop_revision=None,
delta_reporter=None, possible_transports=None):
# FIXME: Use delta_reporter
+ # FIXME: Use source
# FIXME: Use overwrite
result = PullResult()
result.source_branch = source
(result.old_revno, result.old_revid) = self.branch.last_revision_info()
if stop_revision is None:
stop_revision = self.branch.last_revision()
- rev = svn.core.svn_opt_revision_t()
- rev.kind = svn.core.svn_opt_revision_number
- rev.value.number = self.branch.lookup_revision_id(stop_revision)
- fetched = svn.client.update(self.basedir, rev, True, self.client_ctx)
+ revnumber = self.branch.lookup_revision_id(stop_revision)
+ fetched = self._update(revnum)
+ self.base_revnum = fetched
self.base_revid = self.branch.generate_revision_id(fetched)
+ self.base_tree = None
+ self.read_working_inventory()
result.new_revid = self.base_revid
result.new_revno = self.branch.revision_id_to_revno(result.new_revid)
return result
def get_file_sha1(self, file_id, path=None, stat_value=None):
if not path:
path = self._inventory.id2path(file_id)
- return fingerprint_file(open(self.abspath(path)))['sha1']
+ return osutils.fingerprint_file(open(self.abspath(path)))['sha1']
- def _change_fileid_mapping(self, id, path, wc=None):
- if wc is None:
+ def _change_fileid_mapping(self, id, path, adm=None):
+ if adm is None:
subwc = self._get_wc(write_lock=True)
else:
- subwc = wc
+ subwc = adm
new_entries = self._get_new_file_ids(subwc)
if id is None:
if new_entries.has_key(path):
new_entries[path] = id
existing = "".join(map(lambda (path, id): "%s\t%s\n" % (path, id), new_entries.items()))
if existing != "":
- svn.wc.prop_set(SVN_PROP_BZR_FILEIDS, existing.encode("utf-8"), self.basedir, subwc)
- if wc is None:
- svn.wc.adm_close(subwc)
+ subwc.prop_set(SVN_PROP_BZR_FILEIDS, existing.encode("utf-8"), self.basedir)
+ if adm is None:
+ subwc.close()
def _get_base_branch_props(self):
return self.branch.repository.branchprop_list.get_properties(
self.branch.get_branch_path(self.base_revnum), self.base_revnum)
- def _get_new_file_ids(self, wc):
+ def _get_new_file_ids(self, adm):
committed = self._get_base_branch_props().get(SVN_PROP_BZR_FILEIDS, "")
- existing = svn.wc.prop_get(SVN_PROP_BZR_FILEIDS, self.basedir, wc)
+ existing = adm.prop_get(SVN_PROP_BZR_FILEIDS, self.basedir)
if existing is None or committed == existing:
return {}
return dict(map(lambda x: str(x).split("\t"),
def set_pending_merges(self, merges):
"""See MutableTree.set_pending_merges()."""
- wc = self._get_wc(write_lock=True)
+ adm = self._get_wc(write_lock=True)
try:
# Set bzr:merge
if len(merges) > 0:
else:
bzr_merge = ""
- svn.wc.prop_set(SVN_PROP_BZR_ANCESTRY+str(self.branch.mapping.scheme),
+ adm.prop_set(SVN_PROP_BZR_ANCESTRY+str(self.branch.mapping.scheme),
self._get_bzr_merges(self._get_base_branch_props()) + bzr_merge,
- self.basedir, wc)
+ self.basedir)
svk_merges = parse_svk_features(self._get_svk_merges(self._get_base_branch_props()))
except InvalidRevisionId:
pass
- svn.wc.prop_set2(SVN_PROP_SVK_MERGE,
+ adm.prop_set(SVN_PROP_SVK_MERGE,
serialize_svk_features(svk_merges), self.basedir,
- wc, False)
+ False)
finally:
- svn.wc.adm_close(wc)
+ adm.close()
def add_pending_merge(self, revid):
merges = self.pending_merges()
merges.append(revid)
self.set_pending_merges(merges)
+ def get_parent_ids(self):
+ return [self.base_revid] + self.pending_merges()
+
def pending_merges(self):
merged = self._get_bzr_merges(self._get_base_branch_props()).splitlines()
- wc = self._get_wc()
+ adm = self._get_wc()
try:
- merged_data = svn.wc.prop_get(
- SVN_PROP_BZR_ANCESTRY+str(self.branch.mapping.scheme), self.basedir, wc)
+ merged_data = adm.prop_get(
+ SVN_PROP_BZR_ANCESTRY+str(self.branch.mapping.scheme), self.basedir)
if merged_data is None:
set_merged = []
else:
set_merged = merged_data.splitlines()
finally:
- svn.wc.adm_close(wc)
+ adm.close()
assert (len(merged) == len(set_merged) or
len(merged)+1 == len(set_merged))
return []
+ def path_content_summary(self, path, _lstat=os.lstat,
+ _mapper=osutils.file_kind_from_stat_mode):
+ """See Tree.path_content_summary."""
+ abspath = self.abspath(path)
+ try:
+ stat_result = _lstat(abspath)
+ except OSError, e:
+ if getattr(e, 'errno', None) == errno.ENOENT:
+ # no file.
+ return ('missing', None, None, None)
+ # propagate other errors
+ raise
+ kind = _mapper(stat_result.st_mode)
+ if kind == 'file':
+ size = stat_result.st_size
+ # try for a stat cache lookup
+ executable = self._is_executable_from_path_and_stat(path, stat_result)
+ return (kind, size, executable, self._sha_from_stat(
+ path, stat_result))
+ elif kind == 'directory':
+ return kind, None, None, None
+ elif kind == 'symlink':
+ return ('symlink', None, None, os.readlink(abspath))
+ else:
+ return (kind, None, None, None)
+
def _reset_data(self):
pass
finally:
self.branch.unlock()
- if not supports_executable():
+ if not osutils.supports_executable():
def is_executable(self, file_id, path=None):
inv = self.basis_tree()._inventory
if file_id in inv:
class SvnWorkingTreeFormat(WorkingTreeFormat):
"""Subversion working copy format."""
+ def __init__(self, version):
+ self.version = version
+
def __get_matchingbzrdir(self):
return SvnWorkingTreeDirFormat()
_matchingbzrdir = property(__get_matchingbzrdir)
def get_format_description(self):
- return "Subversion Working Copy"
+ return "Subversion Working Copy Version %d" % self.version
def get_format_string(self):
- return "Subversion Working Copy Format"
+ raise NotImplementedError
def initialize(self, a_bzrdir, revision_id=None):
raise NotImplementedError(self.initialize)
self.local_path = transport.local_abspath(".")
# Open related remote repository + branch
- wc = svn.wc.adm_open3(None, self.local_path, False, 0, None)
try:
- self.svn_url = svn.wc.entry(self.local_path, wc, True).url
+ adm = wc.WorkingCopy(None, self.local_path)
+ except SubversionException, (msg, ERR_WC_UNSUPPORTED_FORMAT):
+ raise UnsupportedFormatError(msg, kind='workingtree')
+ try:
+ self.svn_url = adm.entry(self.local_path, True).url
finally:
- svn.wc.adm_close(wc)
+ adm.close()
self.remote_transport = SvnRaTransport(self.svn_url)
self.remote_bzrdir = SvnRemoteAccess(self.remote_transport)