Page MenuHomeSoftware Heritage

No OneTemporary

diff --git a/subvertpy/client.c b/subvertpy/client.c
index 7206e019..dac45009 100644
--- a/subvertpy/client.c
+++ b/subvertpy/client.c
@@ -1,2070 +1,2070 @@
/*
* 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 Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser 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 <structmember.h>
#include <apr_general.h>
#include <svn_opt.h>
#include <svn_client.h>
#include <svn_config.h>
#include <svn_path.h>
#include "util.h"
#include "ra.h"
#include "wc.h"
#if ONLY_SINCE_SVN(1, 6)
#define INFO_SIZE size64
#define WORKING_SIZE working_size64
#else
#define INFO_SIZE size
#define WORKING_SIZE working_size
#endif
extern PyTypeObject Client_Type;
extern PyTypeObject Config_Type;
extern PyTypeObject ConfigItem_Type;
extern PyTypeObject Info_Type;
extern PyTypeObject WCInfo_Type;
typedef struct {
PyObject_HEAD
svn_config_t *item;
PyObject *parent;
} ConfigItemObject;
typedef struct {
PyObject_HEAD
#if ONLY_SINCE_SVN(1, 7)
svn_wc_info_t info;
#else
svn_info_t info;
#endif
apr_pool_t *pool;
} WCInfoObject;
typedef struct {
PyObject_HEAD
svn_info_t info;
WCInfoObject *wc_info;
apr_pool_t *pool;
} InfoObject;
static int client_set_auth(PyObject *self, PyObject *auth, void *closure);
static int client_set_config(PyObject *self, PyObject *auth, void *closure);
static bool to_opt_revision(PyObject *arg, svn_opt_revision_t *ret)
{
if (PyInt_Check(arg) || PyLong_Check(arg)) {
ret->kind = svn_opt_revision_number;
ret->value.number = PyInt_AsLong(arg);
if (ret->value.number == -1 && PyErr_Occurred())
return false;
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_item2_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("(sl)", commit_item->copyfrom_url,
commit_item->copyfrom_rev);
if (copyfrom == NULL) {
Py_DECREF(ret);
return NULL;
}
} else {
copyfrom = Py_None;
Py_INCREF(copyfrom);
}
item = Py_BuildValue("(szlNi)",
/* commit_item->path */ "foo",
commit_item->url, commit_item->revision,
copyfrom,
commit_item->state_flags);
if (item == NULL) {
Py_DECREF(ret);
return NULL;
}
if (PyList_SetItem(ret, i, item) != 0) {
Py_DECREF(ret);
return NULL;
}
}
return ret;
}
#if ONLY_SINCE_SVN(1, 5)
static svn_error_t *proplist_receiver(void *prop_list, const char *path,
apr_hash_t *prop_hash, apr_pool_t *pool)
{
PyGILState_STATE state = PyGILState_Ensure();
PyObject *prop_dict;
PyObject *value;
prop_dict = prop_hash_to_dict(prop_hash);
if (prop_dict == NULL) {
PyGILState_Release(state);
return py_svn_error();
}
value = Py_BuildValue("(sO)", path, prop_dict);
if (value == NULL) {
PyGILState_Release(state);
return py_svn_error();
}
if (PyList_Append(prop_list, value) != 0) {
PyGILState_Release(state);
return py_svn_error();
}
PyGILState_Release(state);
return NULL;
}
#endif
static svn_error_t *list_receiver(void *dict, const char *path,
const svn_dirent_t *dirent,
const svn_lock_t *lock, const char *abs_path,
apr_pool_t *pool)
{
PyGILState_STATE state = PyGILState_Ensure();
PyObject *value;
value = py_dirent(dirent, SVN_DIRENT_ALL);
if (value == NULL) {
PyGILState_Release(state);
return py_svn_error();
}
if (PyDict_SetItemString(dict, path, value) != 0) {
Py_DECREF(value);
PyGILState_Release(state);
return py_svn_error();
}
Py_DECREF(value);
PyGILState_Release(state);
return NULL;
}
static PyObject *py_info(const svn_info_t *info)
{
InfoObject *ret;
ret = PyObject_New(InfoObject, &Info_Type);
if (ret == NULL)
return NULL;
ret->wc_info = PyObject_New(WCInfoObject, &WCInfo_Type);
if (ret->wc_info == NULL)
return NULL;
ret->pool = ret->wc_info->pool = Pool(NULL);
if (ret->pool == NULL)
return NULL;
#if ONLY_SINCE_SVN(1, 7)
ret->info = *svn_client_info2_dup(info, ret->pool);
ret->wc_info->info = *svn_wc_info_dup(info.wc_info, ret->pool);
#else
ret->info = *svn_info_dup(info, ret->pool);
if (info->has_wc_info) {
ret->wc_info->info = *svn_info_dup(info, ret->pool);
}
#endif
return (PyObject *)ret;
}
static svn_error_t *info_receiver(void *dict, const char *path,
const svn_info_t *info,
apr_pool_t *pool)
{
PyGILState_STATE state = PyGILState_Ensure();
PyObject *value;
value = py_info(info);
if (value == NULL) {
PyGILState_Release(state);
return py_svn_error();
}
if (PyDict_SetItemString(dict, path, value) != 0) {
Py_DECREF(value);
PyGILState_Release(state);
return py_svn_error();
}
Py_DECREF(value);
PyGILState_Release(state);
return NULL;
}
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;
PyGILState_STATE state;
if (baton == Py_None)
return NULL;
state = PyGILState_Ensure();
py_commit_items = wrap_py_commit_items(commit_items);
CB_CHECK_PYRETVAL(py_commit_items);
ret = PyObject_CallFunction(baton, "O", py_commit_items);
Py_DECREF(py_commit_items);
CB_CHECK_PYRETVAL(ret);
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);
PyGILState_Release(state);
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("(lzz)", ci->revision, ci->date, ci->author);
}
typedef struct {
PyObject_HEAD
svn_client_ctx_t *client;
apr_pool_t *pool;
PyObject *callbacks;
PyObject *py_auth;
PyObject *py_config;
} ClientObject;
static PyObject *client_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
{
ClientObject *ret;
PyObject *config = Py_None, *auth = Py_None, *log_msg_func = Py_None;
char *kwnames[] = { "config", "auth", "log_msg_func", NULL };
svn_error_t *err;
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|OOO", kwnames,
&config, &auth, &log_msg_func))
return NULL;
ret = PyObject_New(ClientObject, &Client_Type);
if (ret == NULL)
return NULL;
ret->pool = Pool(NULL);
if (ret->pool == NULL) {
Py_DECREF(ret);
return NULL;
}
err = svn_client_create_context(&ret->client, ret->pool);
if (err != NULL) {
handle_svn_error(err);
svn_error_clear(err);
apr_pool_destroy(ret->pool);
PyObject_Del(ret);
return NULL;
}
ret->py_auth = NULL;
ret->py_config = NULL;
ret->client->notify_func2 = NULL;
ret->client->notify_baton2 = NULL;
ret->client->cancel_func = py_cancel_check;
ret->client->cancel_baton = NULL;
if (log_msg_func != Py_None) {
ret->client->log_msg_func2 = py_log_msg_func2;
} else {
ret->client->log_msg_func2 = NULL;
}
Py_INCREF(log_msg_func);
ret->client->log_msg_baton2 = (void *)log_msg_func;
client_set_config((PyObject *)ret, config, NULL);
client_set_auth((PyObject *)ret, auth, NULL);
return (PyObject *)ret;
}
static void client_dealloc(PyObject *self)
{
ClientObject *client = (ClientObject *)self;
Py_XDECREF((PyObject *)client->client->notify_baton2);
Py_XDECREF((PyObject *)client->client->log_msg_baton2);
Py_XDECREF(client->py_auth);
Py_XDECREF(client->py_config);
if (client->pool != NULL)
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_get_notify_func(PyObject *self, void *closure)
{
ClientObject *client = (ClientObject *)self;
if (client->client->notify_func2 == NULL)
Py_RETURN_NONE;
Py_INCREF((PyObject *)client->client->notify_baton2);
return client->client->notify_baton2;
}
static int client_set_notify_func(PyObject *self, PyObject *func, void *closure)
{
ClientObject *client = (ClientObject *)self;
if (client->client->notify_baton2 != NULL) {
Py_DECREF((PyObject *)client->client->notify_baton2);
}
if (func == Py_None) {
client->client->notify_func2 = NULL;
client->client->notify_baton2 = Py_None;
} else {
client->client->notify_func2 = py_wc_notify_func;
client->client->notify_baton2 = (void *)func;
}
Py_INCREF(func);
return 0;
}
static int client_set_auth(PyObject *self, PyObject *auth, void *closure)
{
ClientObject *client = (ClientObject *)self;
apr_array_header_t *auth_providers;
Py_XDECREF(client->py_auth);
if (auth == Py_None) {
auth_providers = apr_array_make(client->pool, 0, sizeof(svn_auth_provider_object_t *));
if (auth_providers == NULL) {
PyErr_NoMemory();
return 1;
}
Py_BEGIN_ALLOW_THREADS
svn_auth_open(&client->client->auth_baton, auth_providers, client->pool);
Py_END_ALLOW_THREADS
} else {
client->client->auth_baton = ((AuthObject *)auth)->auth_baton;
}
client->py_auth = auth;
Py_INCREF(auth);
return 0;
}
static int client_set_config(PyObject *self, PyObject *config, void *closure)
{
ClientObject *client = (ClientObject *)self;
Py_XDECREF(client->py_config);
client->client->config = config_hash_from_object(config, client->pool);
if (client->client->config == NULL) {
client->py_config = NULL;
return -1;
}
client->py_config = config;
Py_INCREF(config);
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;
bool add_parents = false;
apr_pool_t *temp_pool;
char *kwnames[] = { "path", "recursive", "force", "no_ignore",
"add_parents", NULL };
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|bbbb", kwnames,
&path, &recursive, &force, &no_ignore, &add_parents))
return NULL;
#if ONLY_BEFORE_SVN(1, 4)
if (add_parents == false) {
PyErr_SetString(PyExc_NotImplementedError,
"Subversion < 1.4 does not support add_parents=false");
return NULL;
}
#endif
temp_pool = Pool(NULL);
if (temp_pool == NULL)
return NULL;
#if ONLY_SINCE_SVN(1, 5)
RUN_SVN_WITH_POOL(temp_pool,
svn_client_add4(path, recursive?svn_depth_infinity:svn_depth_empty,
force, no_ignore, add_parents,
client->client, temp_pool)
);
#else
RUN_SVN_WITH_POOL(temp_pool,
svn_client_add3(path, recursive, force, no_ignore, client->client,
temp_pool)
);
#endif
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", "rev", "peg_rev", "recurse", "ignore_externals", "allow_unver_obstructions", 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, allow_unver_obstructions=false;
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ss|OObbb", kwnames, &url, &path, &rev, &peg_rev, &recurse, &ignore_externals, &allow_unver_obstructions))
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(NULL);
if (temp_pool == NULL)
return NULL;
#if ONLY_SINCE_SVN(1, 5)
RUN_SVN_WITH_POOL(temp_pool, svn_client_checkout3(&result_rev, url,
svn_path_canonicalize(path, temp_pool),
&c_peg_rev, &c_rev, recurse?svn_depth_infinity:svn_depth_files,
ignore_externals, allow_unver_obstructions, client->client, temp_pool));
#else
if (allow_unver_obstructions) {
PyErr_SetString(PyExc_NotImplementedError,
"allow_unver_obstructions not supported when built against svn<1.5");
apr_pool_destroy(temp_pool);
return NULL;
}
RUN_SVN_WITH_POOL(temp_pool, svn_client_checkout2(&result_rev, url,
svn_path_canonicalize(path, temp_pool),
&c_peg_rev, &c_rev, recurse,
ignore_externals, client->client, temp_pool));
#endif
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;
PyObject *revprops = Py_None;
char *kwnames[] = { "targets", "recurse", "keep_locks", "revprops", NULL };
#if ONLY_SINCE_SVN(1, 5)
apr_hash_t *hash_revprops;
#endif
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|bbO", kwnames, &targets, &recurse, &keep_locks, &revprops))
return NULL;
temp_pool = Pool(NULL);
if (temp_pool == NULL)
return NULL;
if (!path_list_to_apr_array(temp_pool, targets, &apr_targets)) {
apr_pool_destroy(temp_pool);
return NULL;
}
if (revprops != Py_None && !PyDict_Check(revprops)) {
apr_pool_destroy(temp_pool);
PyErr_SetString(PyExc_TypeError, "Expected dictionary with revision properties");
return NULL;
}
#if ONLY_SINCE_SVN(1, 5)
if (revprops != Py_None) {
hash_revprops = prop_dict_to_hash(temp_pool, revprops);
if (hash_revprops == NULL) {
apr_pool_destroy(temp_pool);
return NULL;
}
} else {
hash_revprops = NULL;
}
/* FIXME: Support keep_changelist and changelists */
RUN_SVN_WITH_POOL(temp_pool, svn_client_commit4(&commit_info,
apr_targets, recurse?svn_depth_infinity:svn_depth_files,
keep_locks, false, NULL, hash_revprops,
client->client, temp_pool));
#else
if (revprops != Py_None && PyDict_Size(revprops) > 0) {
PyErr_SetString(PyExc_NotImplementedError,
"Setting revision properties only supported on svn > 1.5");
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));
#endif
ret = py_commit_info_tuple(commit_info);
apr_pool_destroy(temp_pool);
return ret;
}
static PyObject *client_export(PyObject *self, PyObject *args, PyObject *kwargs)
{
ClientObject *client = (ClientObject *)self;
char *kwnames[] = { "from", "to", "rev", "peg_rev", "recurse", "ignore_externals", "overwrite", "native_eol", NULL };
svn_revnum_t result_rev;
svn_opt_revision_t c_peg_rev, c_rev;
char *from, *to;
apr_pool_t *temp_pool;
char *native_eol = NULL;
PyObject *peg_rev=Py_None, *rev=Py_None;
bool recurse=true, ignore_externals=false, overwrite=false;
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ss|OObbbb", kwnames, &from, &to, &rev, &peg_rev, &recurse, &ignore_externals, &overwrite, &native_eol))
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(NULL);
if (temp_pool == NULL)
return NULL;
#if ONLY_SINCE_SVN(1, 5)
RUN_SVN_WITH_POOL(temp_pool, svn_client_export4(&result_rev, from,
svn_path_canonicalize(to, temp_pool),
&c_peg_rev, &c_rev, overwrite, ignore_externals,
recurse?svn_depth_infinity:svn_depth_files,
native_eol, client->client, temp_pool));
#else
RUN_SVN_WITH_POOL(temp_pool, svn_client_export3(&result_rev, from,
svn_path_canonicalize(to, temp_pool),
&c_peg_rev, &c_rev, overwrite, ignore_externals, recurse,
native_eol, client->client, temp_pool));
#endif
apr_pool_destroy(temp_pool);
return PyLong_FromLong(result_rev);
}
static PyObject *client_cat(PyObject *self, PyObject *args, PyObject *kwargs)
{
ClientObject *client = (ClientObject *)self;
char *kwnames[] = { "path", "output_stream", "revision", "peg_revision", NULL };
char *path;
PyObject *peg_rev=Py_None, *rev=Py_None;
svn_opt_revision_t c_peg_rev, c_rev;
apr_pool_t *temp_pool;
svn_stream_t *stream;
PyObject *py_stream;
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "sO|OO", kwnames, &path, &py_stream, &rev, &peg_rev))
return NULL;
if (!to_opt_revision(rev, &c_rev))
return NULL;
if (!to_opt_revision(peg_rev, &c_peg_rev))
return NULL;
temp_pool = Pool(NULL);
if (temp_pool == NULL)
return NULL;
stream = new_py_stream(temp_pool, py_stream);
RUN_SVN_WITH_POOL(temp_pool, svn_client_cat2(stream, path,
&c_peg_rev, &c_rev, client->client, temp_pool));
apr_pool_destroy(temp_pool);
Py_RETURN_NONE;
}
static PyObject *client_delete(PyObject *self, PyObject *args)
{
PyObject *paths;
bool force=false, keep_local=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|bb", &paths, &force, &keep_local))
return NULL;
temp_pool = Pool(NULL);
if (temp_pool == NULL)
return NULL;
if (!path_list_to_apr_array(temp_pool, paths, &apr_paths)) {
apr_pool_destroy(temp_pool);
return NULL;
}
#if ONLY_SINCE_SVN(1, 5)
RUN_SVN_WITH_POOL(temp_pool, svn_client_delete3(&commit_info,
apr_paths,
force, keep_local, NULL, client->client, temp_pool));
#else
if (keep_local) {
PyErr_SetString(PyExc_ValueError,
"keep_local not supported against svn 1.4");
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));
#endif
ret = py_commit_info_tuple(commit_info);
apr_pool_destroy(temp_pool);
return ret;
}
static PyObject *client_mkdir(PyObject *self, PyObject *args)
{
PyObject *paths, *revprops = NULL;
svn_boolean_t make_parents=FALSE;
apr_pool_t *temp_pool;
svn_commit_info_t *commit_info = NULL;
PyObject *ret;
apr_array_header_t *apr_paths;
apr_hash_t *hash_revprops;
ClientObject *client = (ClientObject *)self;
if (!PyArg_ParseTuple(args, "O|bO", &paths, &make_parents, &revprops))
return NULL;
temp_pool = Pool(NULL);
if (temp_pool == NULL)
return NULL;
if (!path_list_to_apr_array(temp_pool, paths, &apr_paths)) {
apr_pool_destroy(temp_pool);
return NULL;
}
if (revprops != NULL && !PyDict_Check(revprops)) {
apr_pool_destroy(temp_pool);
PyErr_SetString(PyExc_TypeError, "Expected dictionary with revision properties");
return NULL;
}
#if ONLY_SINCE_SVN(1, 5)
if (revprops != NULL && revprops != Py_None) {
hash_revprops = prop_dict_to_hash(temp_pool, revprops);
if (hash_revprops == NULL) {
apr_pool_destroy(temp_pool);
return NULL;
}
} else {
hash_revprops = NULL;
}
RUN_SVN_WITH_POOL(temp_pool, svn_client_mkdir3(&commit_info,
apr_paths,
make_parents, hash_revprops, client->client, temp_pool));
#else
if (make_parents) {
PyErr_SetString(PyExc_ValueError,
"make_parents not supported against svn 1.4");
apr_pool_destroy(temp_pool);
return NULL;
}
if (revprops != Py_None) {
PyErr_SetString(PyExc_ValueError,
"revprops not supported against svn 1.4");
apr_pool_destroy(temp_pool);
return NULL;
}
RUN_SVN_WITH_POOL(temp_pool, svn_client_mkdir2(&commit_info,
apr_paths,
client->client, temp_pool));
#endif
ret = py_commit_info_tuple(commit_info);
apr_pool_destroy(temp_pool);
return ret;
}
static PyObject *client_copy(PyObject *self, PyObject *args, PyObject *kwargs)
{
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;
bool copy_as_child = true, make_parents = false;
PyObject *ret;
apr_hash_t *revprops;
bool ignore_externals = false;
ClientObject *client = (ClientObject *)self;
char *kwnames[] = { "src_path", "dst_path", "src_rev", "copy_as_child",
"make_parents", "ignore_externals", "revprpos", NULL };
#if ONLY_SINCE_SVN(1, 4)
PyObject *py_revprops = Py_None;
#endif
#if ONLY_SINCE_SVN(1, 5)
apr_array_header_t *src_paths;
svn_client_copy_source_t src;
#endif
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ss|ObbbO", kwnames,
&src_path, &dst_path, &src_rev, &copy_as_child, &make_parents,
&ignore_externals, &py_revprops))
return NULL;
if (!to_opt_revision(src_rev, &c_src_rev))
return NULL;
temp_pool = Pool(NULL);
if (temp_pool == NULL)
return NULL;
if (py_revprops != Py_None) {
revprops = prop_dict_to_hash(temp_pool, py_revprops);
if (revprops == NULL) {
apr_pool_destroy(temp_pool);
return NULL;
}
} else {
revprops = NULL;
}
#if ONLY_BEFORE_SVN(1, 4)
if (copy_as_child) {
PyErr_SetString(PyExc_NotImplementedError,
"copy_as_child not supported in svn < 1.4");
apr_pool_destroy(temp_pool);
return NULL;
}
if (make_parents) {
PyErr_SetString(PyExc_NotImplementedError,
"make_parents not supported in svn < 1.4");
apr_pool_destroy(temp_pool);
return NULL;
}
if (revprops) {
PyErr_SetString(PyExc_NotImplementedError,
"revprops not supported in svn < 1.4");
apr_pool_destroy(temp_pool);
return NULL;
}
#endif
#if ONLY_BEFORE_SVN(1, 5)
if (ignore_externals) {
PyErr_SetString(PyExc_NotImplementedError,
"ignore_externals not supported in svn < 1.5");
apr_pool_destroy(temp_pool);
return NULL;
}
#endif
#if ONLY_SINCE_SVN(1, 5)
src.path = src_path;
src.revision = src.peg_revision = &c_src_rev;
src_paths = apr_array_make(temp_pool, 1, sizeof(svn_client_copy_source_t *));
if (src_paths == NULL) {
PyErr_NoMemory();
apr_pool_destroy(temp_pool);
return NULL;
}
APR_ARRAY_IDX(src_paths, 0, svn_client_copy_source_t *) = &src;
#endif
#if ONLY_SINCE_SVN(1, 6)
RUN_SVN_WITH_POOL(temp_pool, svn_client_copy5(&commit_info, src_paths,
dst_path, copy_as_child, make_parents,
ignore_externals, revprops, client->client, temp_pool));
#elif ONLY_SINCE_SVN(1, 5)
RUN_SVN_WITH_POOL(temp_pool, svn_client_copy4(&commit_info, src_paths,
dst_path, copy_as_child, make_parents,
revprops, client->client, temp_pool));
#else
RUN_SVN_WITH_POOL(temp_pool, svn_client_copy2(&commit_info, src_path,
&c_src_rev, dst_path, client->client, temp_pool));
#endif
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 vallen;
int recurse = true;
int skip_checks = false;
ClientObject *client = (ClientObject *)self;
apr_pool_t *temp_pool;
char *target;
#if ONLY_SINCE_SVN(1, 5)
svn_commit_info_t *commit_info = NULL;
#endif
PyObject *ret, *py_revprops = Py_None;
svn_revnum_t base_revision_for_url = SVN_INVALID_REVNUM;
apr_hash_t *revprops;
if (!PyArg_ParseTuple(args, "sz#s|bblO", &propname, &c_propval.data,
&vallen, &target, &recurse, &skip_checks,
&base_revision_for_url, &py_revprops))
return NULL;
c_propval.len = vallen;
temp_pool = Pool(NULL);
if (temp_pool == NULL)
return NULL;
if (py_revprops != Py_None) {
revprops = prop_dict_to_hash(temp_pool, py_revprops);
if (revprops == NULL) {
apr_pool_destroy(temp_pool);
return NULL;
}
} else {
revprops = NULL;
}
#if ONLY_SINCE_SVN(1, 5)
/* FIXME: Support changelists */
/* FIXME: Support depth */
RUN_SVN_WITH_POOL(temp_pool, svn_client_propset3(&commit_info, propname,
&c_propval, target, recurse?svn_depth_infinity:svn_depth_files,
skip_checks, base_revision_for_url,
NULL, revprops, client->client, temp_pool));
ret = py_commit_info_tuple(commit_info);
#else
if (revprops) {
PyErr_SetString(PyExc_NotImplementedError,
"revprops not supported with svn < 1.5");
apr_pool_destroy(temp_pool);
return NULL;
}
RUN_SVN_WITH_POOL(temp_pool, svn_client_propset2(propname, &c_propval,
target, recurse, skip_checks, client->client, temp_pool));
ret = Py_None;
Py_INCREF(ret);
#endif
apr_pool_destroy(temp_pool);
return ret;
}
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 = Py_None;
PyObject *revision;
ClientObject *client = (ClientObject *)self;
PyObject *ret;
if (!PyArg_ParseTuple(args, "ssO|Ob", &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(NULL);
if (temp_pool == NULL)
return NULL;
#if ONLY_SINCE_SVN(1, 5)
/* FIXME: Support changelists */
/* FIXME: Support actual_revnum */
/* FIXME: Support depth properly */
RUN_SVN_WITH_POOL(temp_pool,
svn_client_propget3(&hash_props, propname, target,
&c_peg_rev, &c_rev, NULL, recurse?svn_depth_infinity:svn_depth_files,
NULL, client->client, temp_pool));
#else
RUN_SVN_WITH_POOL(temp_pool,
svn_client_propget2(&hash_props, propname, target,
&c_peg_rev, &c_rev, recurse, client->client, temp_pool));
#endif
ret = prop_hash_to_dict(hash_props);
apr_pool_destroy(temp_pool);
return ret;
}
static PyObject *client_proplist(PyObject *self, PyObject *args,
PyObject *kwargs)
{
char *kwnames[] = { "target", "peg_revision", "depth", "revision", NULL };
svn_opt_revision_t c_peg_rev;
svn_opt_revision_t c_rev;
int depth;
apr_pool_t *temp_pool;
char *target;
PyObject *peg_revision = Py_None, *revision = Py_None;
ClientObject *client = (ClientObject *)self;
PyObject *prop_list;
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "sOi|O", kwnames,
&target, &peg_revision, &depth, &revision))
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(NULL);
if (temp_pool == NULL)
return NULL;
prop_list = PyList_New(0);
if (prop_list == NULL) {
apr_pool_destroy(temp_pool);
return NULL;
}
#if ONLY_SINCE_SVN(1, 5)
RUN_SVN_WITH_POOL(temp_pool,
svn_client_proplist3(target, &c_peg_rev, &c_rev,
depth, NULL,
proplist_receiver, prop_list,
client->client, temp_pool));
apr_pool_destroy(temp_pool);
#else
{
apr_array_header_t *props;
int i;
if (depth != svn_depth_infinity && depth != svn_depth_empty) {
PyErr_SetString(PyExc_NotImplementedError,
"depth can only be infinity or empty when built against svn < 1.5");
apr_pool_destroy(temp_pool);
return NULL;
}
RUN_SVN_WITH_POOL(temp_pool,
svn_client_proplist2(&props, target, &c_peg_rev, &c_rev,
(depth == svn_depth_infinity),
client->client, temp_pool));
for (i = 0; i < props->nelts; i++) {
svn_client_proplist_item_t *item;
PyObject *prop_dict, *value;
item = APR_ARRAY_IDX(props, i, svn_client_proplist_item_t *);
prop_dict = prop_hash_to_dict(item->prop_hash);
if (prop_dict == NULL) {
apr_pool_destroy(temp_pool);
Py_DECREF(prop_list);
return NULL;
}
value = Py_BuildValue("(sO)", item->node_name, prop_dict);
if (value == NULL) {
apr_pool_destroy(temp_pool);
Py_DECREF(prop_list);
Py_DECREF(prop_dict);
return NULL;
}
if (PyList_Append(prop_list, value) != 0) {
apr_pool_destroy(temp_pool);
Py_DECREF(prop_list);
Py_DECREF(prop_dict);
Py_DECREF(value);
return NULL;
}
Py_DECREF(value);
}
apr_pool_destroy(temp_pool);
}
#endif
return prop_list;
}
static PyObject *client_resolve(PyObject *self, PyObject *args)
{
#if ONLY_SINCE_SVN(1, 5)
svn_depth_t depth;
svn_wc_conflict_choice_t choice;
ClientObject *client = (ClientObject *)self;
apr_pool_t *temp_pool;
char *path;
if (!PyArg_ParseTuple(args, "sii", &path, &depth, &choice))
return NULL;
temp_pool = Pool(NULL);
if (temp_pool == NULL)
return NULL;
RUN_SVN_WITH_POOL(temp_pool, svn_client_resolve(path, depth, choice,
client->client, temp_pool));
apr_pool_destroy(temp_pool);
Py_RETURN_NONE;
#else
PyErr_SetString(PyExc_NotImplementedError,
"svn_client_resolve not available with Subversion < 1.5");
return NULL;
#endif
}
static PyObject *client_update(PyObject *self, PyObject *args, PyObject *kwargs)
{
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;
svn_boolean_t allow_unver_obstructions = FALSE,
depth_is_sticky = FALSE;
char *kwnames[] =
{ "path", "revision", "recurse", "ignore_externals", "depth_is_sticky",
"allow_unver_obstructions", NULL };
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|Obbbb", kwnames,
&paths, &rev, &recurse, &ignore_externals,
&depth_is_sticky, &allow_unver_obstructions))
return NULL;
if (!to_opt_revision(rev, &c_rev))
return NULL;
temp_pool = Pool(NULL);
if (temp_pool == NULL)
return NULL;
if (!path_list_to_apr_array(temp_pool, paths, &apr_paths)) {
apr_pool_destroy(temp_pool);
return NULL;
}
#if ONLY_SINCE_SVN(1, 5)
RUN_SVN_WITH_POOL(temp_pool, svn_client_update3(&result_revs,
apr_paths, &c_rev, recurse?svn_depth_infinity:svn_depth_files,
depth_is_sticky, ignore_externals, allow_unver_obstructions,
client->client, temp_pool));
#else
RUN_SVN_WITH_POOL(temp_pool, svn_client_update2(&result_revs,
apr_paths, &c_rev,
recurse, ignore_externals, client->client, temp_pool));
#endif
ret = PyList_New(result_revs->nelts);
if (ret == NULL) {
apr_pool_destroy(temp_pool);
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) {
Py_DECREF(ret);
return NULL;
}
}
apr_pool_destroy(temp_pool);
return ret;
}
static PyObject *client_list(PyObject *self, PyObject *args, PyObject *kwargs)
{
char *kwnames[] =
{ "path", "peg_revision", "depth", "dirents", "revision", NULL };
svn_opt_revision_t c_peg_rev;
svn_opt_revision_t c_rev;
int depth;
int dirents = SVN_DIRENT_ALL;
apr_pool_t *temp_pool;
char *path;
PyObject *peg_revision = Py_None, *revision = Py_None;
ClientObject *client = (ClientObject *)self;
PyObject *entry_dict;
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "sOi|iO", kwnames,
&path, &peg_revision, &depth, &dirents,
&revision))
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(NULL);
if (temp_pool == NULL)
return NULL;
entry_dict = PyDict_New();
if (entry_dict == NULL) {
apr_pool_destroy(temp_pool);
return NULL;
}
#if ONLY_SINCE_SVN(1, 5)
RUN_SVN_WITH_POOL(temp_pool,
svn_client_list2(path, &c_peg_rev, &c_rev,
depth, dirents, false,
list_receiver, entry_dict,
client->client, temp_pool));
#else
if (depth != svn_depth_infinity && depth != svn_depth_empty) {
PyErr_SetString(PyExc_NotImplementedError,
"depth can only be infinity or empty when built against svn < 1.5");
apr_pool_destroy(temp_pool);
return NULL;
}
RUN_SVN_WITH_POOL(temp_pool,
svn_client_list(path, &c_peg_rev, &c_rev,
(depth == svn_depth_infinity)?TRUE:FALSE,
dirents, false,
list_receiver, entry_dict,
client->client, temp_pool));
#endif
apr_pool_destroy(temp_pool);
return entry_dict;
}
static PyObject *client_diff(PyObject *self, PyObject *args, PyObject *kwargs)
{
#if ONLY_SINCE_SVN(1, 5)
char *kwnames[] = {
"rev1", "rev2", "path1", "path2",
"relative_to_dir", "diffopts", "encoding",
"ignore_ancestry", "no_diff_deleted", "ignore_content_type",
NULL,
};
apr_pool_t *temp_pool;
ClientObject *client = (ClientObject *)self;
svn_opt_revision_t c_rev1, c_rev2;
svn_depth_t depth = svn_depth_infinity;
char *path1 = NULL, *path2 = NULL, *relative_to_dir = NULL;
char *encoding = "utf-8";
PyObject *rev1 = Py_None, *rev2 = Py_None;
int ignore_ancestry = true, no_diff_deleted = true,
ignore_content_type = false;
PyObject *diffopts = Py_None;
apr_array_header_t *c_diffopts;
PyObject *outfile, *errfile;
apr_file_t *c_outfile, *c_errfile;
apr_off_t offset;
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OO|zzzOsbbb:diff", kwnames,
&rev1, &rev2, &path1, &path2,
&relative_to_dir, &diffopts, &encoding,
&ignore_ancestry, &no_diff_deleted,
&ignore_content_type))
return NULL;
if (!to_opt_revision(rev1, &c_rev1) || !to_opt_revision(rev2, &c_rev2))
return NULL;
temp_pool = Pool(NULL);
if (temp_pool == NULL)
return NULL;
if (diffopts == Py_None)
diffopts = PyList_New(0);
else
Py_INCREF(diffopts);
if (diffopts == NULL) {
apr_pool_destroy(temp_pool);
return NULL;
}
if (!string_list_to_apr_array(temp_pool, diffopts, &c_diffopts)) {
apr_pool_destroy(temp_pool);
Py_DECREF(diffopts);
return NULL;
}
Py_DECREF(diffopts);
outfile = PyOS_tmpfile();
if (outfile == NULL) {
apr_pool_destroy(temp_pool);
return NULL;
}
errfile = PyOS_tmpfile();
if (errfile == NULL) {
apr_pool_destroy(temp_pool);
Py_DECREF(outfile);
return NULL;
}
c_outfile = apr_file_from_object(outfile, temp_pool);
if (c_outfile == NULL) {
apr_pool_destroy(temp_pool);
Py_DECREF(outfile);
Py_DECREF(errfile);
return NULL;
}
c_errfile = apr_file_from_object(errfile, temp_pool);
if (c_errfile == NULL) {
apr_pool_destroy(temp_pool);
Py_DECREF(outfile);
Py_DECREF(errfile);
return NULL;
}
RUN_SVN_WITH_POOL(temp_pool,
svn_client_diff4(c_diffopts,
path1, &c_rev1, path2, &c_rev2,
relative_to_dir, depth,
ignore_ancestry, no_diff_deleted,
ignore_content_type, encoding,
c_outfile, c_errfile, NULL,
client->client, temp_pool));
offset = 0;
apr_file_seek(c_outfile, APR_SET, &offset);
offset = 0;
apr_file_seek(c_errfile, APR_SET, &offset);
apr_pool_destroy(temp_pool);
return Py_BuildValue("(NN)", outfile, errfile);
#else
PyErr_SetString(PyExc_NotImplementedError,
"svn_client_diff4 not available with Subversion < 1.5");
return NULL;
#endif
}
static PyObject *client_log(PyObject *self, PyObject *args, PyObject *kwargs)
{
char *kwnames[] = {
"callback", "paths", "start_rev", "end_rev", "limit", "peg_revision",
"discover_changed_paths", "strict_node_history",
"include_merged_revisions", "revprops",
NULL,
};
apr_pool_t *temp_pool;
ClientObject *client = (ClientObject *)self;
PyObject *callback, *paths, *start_rev = Py_None, *end_rev = Py_None,
*peg_revision = Py_None, *revprops = NULL;
int limit = 0;
svn_boolean_t discover_changed_paths = FALSE, strict_node_history = FALSE,
include_merged_revisions = FALSE;
apr_array_header_t *apr_paths, *apr_revprops = NULL;
svn_opt_revision_t c_peg_rev, c_start_rev, c_end_rev;
#if ONLY_SINCE_SVN(1, 6)
svn_opt_revision_range_t revision_range;
apr_array_header_t *revision_ranges;
#endif
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OO|OOiObbbO", kwnames,
&callback, &paths, &start_rev, &end_rev, &limit,
&peg_revision, &discover_changed_paths,
&strict_node_history, &include_merged_revisions,
&revprops))
return NULL;
if (!to_opt_revision(start_rev, &c_start_rev))
return NULL;
if (!to_opt_revision(end_rev, &c_end_rev))
return NULL;
if (!to_opt_revision(peg_revision, &c_peg_rev))
return NULL;
temp_pool = Pool(NULL);
if (temp_pool == NULL)
return NULL;
-#if ONLY_BEFORE_SVN(1, 4)
+#if ONLY_BEFORE_SVN(1, 5)
if (include_merged_revisions) {
- PyErr_SetString(PyExc_NotImplementedError,
- "include_merged_revisions not supported in svn < 1.4");
+ PyErr_SetString(PyExc_NotImplementedError,
+ "include_merged_revisions not supported in svn < 1.5");
apr_pool_destroy(temp_pool);
return NULL;
}
if (revprops) {
- PyErr_SetString(PyExc_NotImplementedError,
- "revprops not supported in svn < 1.4");
+ PyErr_SetString(PyExc_NotImplementedError,
+ "revprops not supported in svn < 1.5");
apr_pool_destroy(temp_pool);
return NULL;
}
#endif
if (!path_list_to_apr_array(temp_pool, paths, &apr_paths)) {
apr_pool_destroy(temp_pool);
return NULL;
}
if (revprops) {
if (!path_list_to_apr_array(temp_pool, revprops, &apr_revprops)) {
apr_pool_destroy(temp_pool);
return NULL;
}
}
#if ONLY_SINCE_SVN(1, 6)
revision_range.start = c_start_rev;
revision_range.end = c_end_rev;
revision_ranges = apr_array_make(temp_pool, 1, sizeof(svn_opt_revision_range_t *));
if (revision_ranges == NULL) {
apr_pool_destroy(temp_pool);
return NULL;
}
APR_ARRAY_PUSH(revision_ranges, svn_opt_revision_range_t *) = &revision_range;
RUN_SVN_WITH_POOL(temp_pool, svn_client_log5(apr_paths, &c_peg_rev,
revision_ranges, limit, discover_changed_paths,
strict_node_history, include_merged_revisions, apr_revprops,
py_svn_log_entry_receiver, (void*)callback,
client->client, temp_pool));
#elif ONLY_SINCE_SVN(1, 5)
RUN_SVN_WITH_POOL(temp_pool, svn_client_log4(apr_paths, &c_peg_rev,
&c_start_rev, &c_end_rev, limit, discover_changed_paths,
strict_node_history, include_merged_revisions, apr_revprops,
py_svn_log_entry_receiver, (void*)callback,
client->client, temp_pool));
#elif ONLY_SINCE_SVN(1, 4)
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,
(void*)callback, client->client, temp_pool));
#else
RUN_SVN_WITH_POOL(temp_pool, svn_client_log2(apr_paths, &c_start_rev,
&c_end_rev, limit, discover_changed_paths, strict_node_history,
py_svn_log_wrapper, (void*)callback,
client->client, temp_pool));
#endif
apr_pool_destroy(temp_pool);
Py_RETURN_NONE;
}
static PyObject *client_info(PyObject *self, PyObject *args, PyObject *kwargs)
{
char *kwnames[] = {
"path", "revision", "peg_revision", "depth",
NULL,
};
apr_pool_t *temp_pool;
ClientObject *client = (ClientObject *)self;
const char *path;
int depth;
PyObject *revision = Py_None, *peg_revision = Py_None;
svn_opt_revision_t c_peg_rev, c_rev;
PyObject *entry_dict;
svn_error_t *err;
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|OOi", kwnames,
&path, &revision,
&peg_revision, &depth))
return NULL;
if (!to_opt_revision(revision, &c_rev))
return NULL;
if (!to_opt_revision(peg_revision, &c_peg_rev))
return NULL;
#if ONLY_BEFORE_SVN(1, 5)
if (depth != svn_depth_infinity && depth != svn_depth_empty) {
PyErr_SetString(PyExc_NotImplementedError,
"depth can only be infinity or empty when built against svn < 1.5");
return NULL;
}
#endif
temp_pool = Pool(NULL);
if (temp_pool == NULL)
return NULL;
entry_dict = PyDict_New();
if (entry_dict == NULL) {
apr_pool_destroy(temp_pool);
return NULL;
}
Py_BEGIN_ALLOW_THREADS;
#if ONLY_SINCE_SVN(1, 5)
/* FIXME: Support changelists */
err = svn_client_info2(path, &c_peg_rev, &c_rev, info_receiver, entry_dict,
depth, NULL,
client->client, temp_pool);
#else
err = svn_client_info(path, &c_peg_rev, &c_rev,
info_receiver, entry_dict,
(depth == svn_depth_infinity),
client->client, temp_pool);
#endif
Py_END_ALLOW_THREADS;
if (err != NULL) {
handle_svn_error(err);
svn_error_clear(err);
apr_pool_destroy(temp_pool);
Py_DECREF(entry_dict);
return NULL;
}
apr_pool_destroy(temp_pool);
return entry_dict;
}
static PyMethodDef client_methods[] = {
{ "add", (PyCFunction)client_add, METH_VARARGS|METH_KEYWORDS,
"S.add(path, recursive=True, force=False, no_ignore=False)" },
{ "checkout", (PyCFunction)client_checkout, METH_VARARGS|METH_KEYWORDS,
"S.checkout(url, path, rev=None, peg_rev=None, recurse=True, ignore_externals=False, allow_unver_obstructions=False)" },
{ "export", (PyCFunction)client_export, METH_VARARGS|METH_KEYWORDS,
"S.export(from, to, rev=None, peg_rev=None, recurse=True, ignore_externals=False, overwrite=False, native_eol=None)" },
{ "cat", (PyCFunction)client_cat, METH_VARARGS|METH_KEYWORDS,
"S.cat(path, output_stream, revision=None, peg_revision=None)" },
{ "commit", (PyCFunction)client_commit, METH_VARARGS|METH_KEYWORDS, "S.commit(targets, recurse=True, keep_locks=True, revprops=None) -> (revnum, date, author)" },
{ "delete", client_delete, METH_VARARGS, "S.delete(paths, force=False)" },
{ "copy", (PyCFunction)client_copy, METH_VARARGS|METH_KEYWORDS, "S.copy(src_path, dest_path, srv_rev=None)" },
{ "propset", client_propset, METH_VARARGS, "S.propset(name, value, target, recurse=True, skip_checks=False)" },
{ "propget", client_propget, METH_VARARGS, "S.propget(name, target, peg_revision, revision=None, recurse=False) -> value" },
{ "proplist", (PyCFunction)client_proplist, METH_VARARGS|METH_KEYWORDS, "S.proplist(path, peg_revision, depth, revision=None)" },
{ "resolve", client_resolve, METH_VARARGS, "S.resolve(path, depth, choice)" },
{ "update", (PyCFunction)client_update, METH_VARARGS|METH_KEYWORDS, "S.update(path, rev=None, recurse=True, ignore_externals=False) -> list of revnums" },
{ "list", (PyCFunction)client_list, METH_VARARGS|METH_KEYWORDS, "S.list(path, peg_revision, depth, dirents=ra.DIRENT_ALL, revision=None) -> list of directory entries" },
{ "diff", (PyCFunction)client_diff, METH_VARARGS|METH_KEYWORDS, "S.diff(rev1, rev2, path1=None, path2=None, relative_to_dir=None, diffopts=[], encoding=\"utf-8\", ignore_ancestry=True, no_diff_deleted=True, ignore_content_type=False) -> unified diff as a string" },
{ "mkdir", (PyCFunction)client_mkdir, METH_VARARGS|METH_KEYWORDS, "S.mkdir(paths, make_parents=False, revprops=None) -> (revnum, date, author)" },
{ "log", (PyCFunction)client_log, METH_VARARGS|METH_KEYWORDS,
"S.log(callback, paths, start_rev=None, end_rev=None, limit=0, peg_revision=None, discover_changed_paths=False, strict_node_history=False, include_merged_revisions=False, revprops=None)" },
{ "info", (PyCFunction)client_info, METH_VARARGS|METH_KEYWORDS,
"S.info(path, revision=None, peg_revision=None, depth=DEPTH_EMPTY) -> dict of info entries" },
{ NULL, }
};
static PyGetSetDef client_getset[] = {
{ "log_msg_func", client_get_log_msg_func, client_set_log_msg_func, NULL },
{ "notify_func", client_get_notify_func, client_set_notify_func, NULL },
{ "auth", NULL, client_set_auth, NULL },
{ "config", NULL, client_set_config, NULL },
{ NULL, }
};
static PyObject *get_default_ignores(PyObject *self)
{
apr_array_header_t *patterns;
apr_pool_t *pool;
int i = 0;
ConfigObject *configobj = (ConfigObject *)self;
PyObject *ret;
pool = Pool(NULL);
if (pool == NULL)
return NULL;
RUN_SVN_WITH_POOL(pool, svn_wc_get_default_ignores(&patterns, configobj->config, pool));
ret = PyList_New(patterns->nelts);
for (i = 0; i < patterns->nelts; i++) {
PyObject *item = PyString_FromString(APR_ARRAY_IDX(patterns, i, char *));
if (item == NULL) {
apr_pool_destroy(pool);
Py_DECREF(item);
Py_DECREF(ret);
return NULL;
}
if (PyList_SetItem(ret, i, item) != 0) {
apr_pool_destroy(pool);
Py_DECREF(item);
Py_DECREF(ret);
return NULL;
}
}
apr_pool_destroy(pool);
return ret;
}
static PyMethodDef config_methods[] = {
{ "get_default_ignores", (PyCFunction)get_default_ignores, METH_NOARGS, NULL },
{ NULL }
};
static void config_dealloc(PyObject *obj)
{
apr_pool_t *pool = ((ConfigObject *)obj)->pool;
if (pool != NULL)
apr_pool_destroy(pool);
PyObject_Del(obj);
}
PyTypeObject Config_Type = {
PyObject_HEAD_INIT(NULL) 0,
"client.Config", /* const char *tp_name; For printing, in format "<module>.<name>" */
sizeof(ConfigObject), /* tp_basicsize */
0, /* tp_itemsize; For allocation */
/* Methods to implement standard operations */
(destructor)config_dealloc, /* destructor tp_dealloc; */
NULL, /* printfunc tp_print; */
NULL, /* getattrfunc tp_getattr; */
NULL, /* setattrfunc tp_setattr; */
NULL, /* cmpfunc tp_compare; */
NULL, /* reprfunc tp_repr; */
/* Method suites for standard classes */
NULL, /* PyNumberMethods *tp_as_number; */
NULL, /* PySequenceMethods *tp_as_sequence; */
NULL, /* PyMappingMethods *tp_as_mapping; */
/* More standard operations (here for binary compatibility) */
NULL, /* hashfunc tp_hash; */
NULL, /* ternaryfunc tp_call; */
NULL, /* reprfunc tp_str; */
NULL, /* getattrofunc tp_getattro; */
NULL, /* setattrofunc tp_setattro; */
/* Functions to access object as input/output buffer */
NULL, /* PyBufferProcs *tp_as_buffer; */
/* Flags to define presence of optional/expanded features */
0, /* long tp_flags; */
NULL, /* const char *tp_doc; Documentation string */
/* Assigned meaning in release 2.0 */
/* call function for all accessible objects */
NULL, /* traverseproc tp_traverse; */
/* delete references to contained objects */
NULL, /* inquiry tp_clear; */
/* Assigned meaning in release 2.1 */
/* rich comparisons */
NULL, /* richcmpfunc tp_richcompare; */
/* weak reference enabler */
0, /* Py_ssize_t tp_weaklistoffset; */
/* Added in release 2.2 */
/* Iterators */
NULL, /* getiterfunc tp_iter; */
NULL, /* iternextfunc tp_iternext; */
/* Attribute descriptor and subclassing stuff */
config_methods, /* struct PyMethodDef *tp_methods; */
};
static void configitem_dealloc(PyObject *self)
{
ConfigItemObject *item = (ConfigItemObject *)self;
Py_XDECREF(item->parent);
PyObject_Del(item);
}
PyTypeObject ConfigItem_Type = {
PyObject_HEAD_INIT(NULL) 0,
"client.ConfigItem", /* const char *tp_name; For printing, in format "<module>.<name>" */
sizeof(ConfigItemObject),
0,/* Py_ssize_t tp_basicsize, tp_itemsize; For allocation */
/* Methods to implement standard operations */
(destructor)configitem_dealloc, /* destructor tp_dealloc; */
};
static void info_dealloc(PyObject *self)
{
apr_pool_t *pool = ((InfoObject *)self)->pool;
if (pool != NULL)
apr_pool_destroy(pool);
PyObject_Del(self);
}
static PyMemberDef info_members[] = {
{ "url", T_STRING, offsetof(InfoObject, info.URL), READONLY,
"Where the item lives in the repository." },
{ "revision", T_LONG, offsetof(InfoObject, info.rev), READONLY,
"The revision of the object.", },
{ "kind", T_INT, offsetof(InfoObject, info.kind), READONLY,
"The node's kind.", },
{ "repos_root_url", T_STRING, offsetof(InfoObject, info.repos_root_URL), READONLY,
"The root URL of the repository." },
{ "repos_uuid", T_STRING, offsetof(InfoObject, info.repos_UUID), READONLY,
"The repository's UUID." },
{ "last_changed_revision", T_LONG, offsetof(InfoObject, info.last_changed_rev), READONLY,
"The last revision in which this object changed.", },
{ "last_changed_date", T_LONG, offsetof(InfoObject, info.last_changed_date), READONLY,
"The date of the last_changed_revision." },
{ "last_changed_author", T_STRING, offsetof(InfoObject, info.last_changed_author), READONLY,
"The author of the last_changed_revision." },
{ "wc_info", T_OBJECT, offsetof(InfoObject, wc_info), READONLY,
"Possible information about the working copy, None if not valid." },
{ NULL, }
};
static PyObject *info_get_size(PyObject *_self, void *closure)
{
InfoObject *self = (InfoObject *)_self;
if (self->info.size == SVN_WC_ENTRY_WORKING_SIZE_UNKNOWN)
Py_RETURN_NONE;
return PyLong_FromLong(self->info.size);
}
static PyGetSetDef info_getsetters[] = {
{ "size", info_get_size, NULL, "The size of the file in the repository.", },
{ NULL }
};
PyTypeObject Info_Type = {
PyObject_HEAD_INIT(NULL) 0,
"client.Info", /* const char *tp_name; For printing, in format "<module>.<name>" */
sizeof(InfoObject),
0,/* Py_ssize_t tp_basicsize, tp_itemsize; For allocation */
/* Methods to implement standard operations */
info_dealloc, /* destructor tp_dealloc; */
NULL, /* printfunc tp_print; */
NULL, /* getattrfunc tp_getattr; */
NULL, /* setattrfunc tp_setattr; */
NULL, /* cmpfunc tp_compare; */
NULL, /* reprfunc tp_repr; */
/* Method suites for standard classes */
NULL, /* PyNumberMethods *tp_as_number; */
NULL, /* PySequenceMethods *tp_as_sequence; */
NULL, /* PyMappingMethods *tp_as_mapping; */
/* More standard operations (here for binary compatibility) */
NULL, /* hashfunc tp_hash; */
NULL, /* ternaryfunc tp_call; */
NULL, /* reprfunc tp_str; */
NULL, /* getattrofunc tp_getattro; */
NULL, /* setattrofunc tp_setattro; */
/* Functions to access object as input/output buffer */
NULL, /* PyBufferProcs *tp_as_buffer; */
/* Flags to define presence of optional/expanded features */
0, /* long tp_flags; */
NULL, /* const char *tp_doc; Documentation string */
/* Assigned meaning in release 2.0 */
/* call function for all accessible objects */
NULL, /* traverseproc tp_traverse; */
/* delete references to contained objects */
NULL, /* inquiry tp_clear; */
/* Assigned meaning in release 2.1 */
/* rich comparisons */
NULL, /* richcmpfunc tp_richcompare; */
/* weak reference enabler */
0, /* Py_ssize_t tp_weaklistoffset; */
/* Added in release 2.2 */
/* Iterators */
NULL, /* getiterfunc tp_iter; */
NULL, /* iternextfunc tp_iternext; */
/* Attribute descriptor and subclassing stuff */
NULL, /* struct PyMethodDef *tp_methods; */
info_members, /* struct PyMemberDef *tp_members; */
info_getsetters, /* struct PyGetSetDef *tp_getsetters; */
};
static PyMemberDef wc_info_members[] = {
{ "schedule", T_INT, offsetof(WCInfoObject, info.schedule), READONLY,
"" },
{ "copyfrom_url", T_STRING, offsetof(WCInfoObject, info.copyfrom_url), READONLY,
"" },
{ "copyfrom_rev", T_LONG, offsetof(WCInfoObject, info.copyfrom_rev), READONLY,
"" },
/* TODO add support for checksum */
/* TODO add support for conflicts */
#if ONLY_SINCE_SVN(1, 7)
{ "changelist", T_STRING, offsetof(WCInfoObject, info.changelist), READONLY,
"" },
{ "recorded_size", T_PYSSIZET, offsetof(WCInfoObject, info.recorded_size), READONLY,
"" },
{ "recorded_time", T_LONG, offsetof(WCInfoObject, info.recorded_time), READONLY,
"" },
{ "wcroot_abspath", T_STRING, offsetof(WCInfoObject, info.recorded_time), READONLY,
"" },
#else
#if ONLY_SINCE_SVN(1, 5)
{ "depth", T_INT, offsetof(WCInfoObject, info.depth), READONLY,
"" },
#endif
{ "recorded_size", T_PYSSIZET, offsetof(InfoObject, info.WORKING_SIZE), READONLY,
"The size of the file in the repository.", },
{ "text_time", T_LONG, offsetof(WCInfoObject, info.text_time), READONLY,
"" },
{ "prop_time", T_LONG, offsetof(WCInfoObject, info.prop_time), READONLY,
"" },
#endif
{ NULL, }
};
PyTypeObject WCInfo_Type = {
PyObject_HEAD_INIT(NULL) 0,
"client.Info", /* const char *tp_name; For printing, in format "<module>.<name>" */
sizeof(WCInfoObject),
0,/* Py_ssize_t tp_basicsize, tp_itemsize; For allocation */
/* Methods to implement standard operations */
PyObject_Del, /* destructor tp_dealloc; */
NULL, /* printfunc tp_print; */
NULL, /* getattrfunc tp_getattr; */
NULL, /* setattrfunc tp_setattr; */
NULL, /* cmpfunc tp_compare; */
NULL, /* reprfunc tp_repr; */
/* Method suites for standard classes */
NULL, /* PyNumberMethods *tp_as_number; */
NULL, /* PySequenceMethods *tp_as_sequence; */
NULL, /* PyMappingMethods *tp_as_mapping; */
/* More standard operations (here for binary compatibility) */
NULL, /* hashfunc tp_hash; */
NULL, /* ternaryfunc tp_call; */
NULL, /* reprfunc tp_str; */
NULL, /* getattrofunc tp_getattro; */
NULL, /* setattrofunc tp_setattro; */
/* Functions to access object as input/output buffer */
NULL, /* PyBufferProcs *tp_as_buffer; */
/* Flags to define presence of optional/expanded features */
0, /* long tp_flags; */
NULL, /* const char *tp_doc; Documentation string */
/* Assigned meaning in release 2.0 */
/* call function for all accessible objects */
NULL, /* traverseproc tp_traverse; */
/* delete references to contained objects */
NULL, /* inquiry tp_clear; */
/* Assigned meaning in release 2.1 */
/* rich comparisons */
NULL, /* richcmpfunc tp_richcompare; */
/* weak reference enabler */
0, /* Py_ssize_t tp_weaklistoffset; */
/* Added in release 2.2 */
/* Iterators */
NULL, /* getiterfunc tp_iter; */
NULL, /* iternextfunc tp_iternext; */
/* Attribute descriptor and subclassing stuff */
NULL, /* struct PyMethodDef *tp_methods; */
wc_info_members, /* struct PyMemberDef *tp_members; */
};
PyTypeObject Client_Type = {
PyObject_HEAD_INIT(NULL) 0,
/* PyObject_VAR_HEAD */
"client.Client", /* const char *tp_name; For printing, in format "<module>.<name>" */
sizeof(ClientObject),
0,/* Py_ssize_t tp_basicsize, tp_itemsize; For allocation */
/* Methods to implement standard operations */
client_dealloc, /* destructor tp_dealloc; */
NULL, /* printfunc tp_print; */
NULL, /* getattrfunc tp_getattr; */
NULL, /* setattrfunc tp_setattr; */
NULL, /* cmpfunc tp_compare; */
NULL, /* reprfunc tp_repr; */
/* Method suites for standard classes */
NULL, /* PyNumberMethods *tp_as_number; */
NULL, /* PySequenceMethods *tp_as_sequence; */
NULL, /* PyMappingMethods *tp_as_mapping; */
/* More standard operations (here for binary compatibility) */
NULL, /* hashfunc tp_hash; */
NULL, /* ternaryfunc tp_call; */
NULL, /* reprfunc tp_str; */
NULL, /* getattrofunc tp_getattro; */
NULL, /* setattrofunc tp_setattro; */
/* Functions to access object as input/output buffer */
NULL, /* PyBufferProcs *tp_as_buffer; */
/* Flags to define presence of optional/expanded features */
0, /* long tp_flags; */
"Subversion client", /* const char *tp_doc; Documentation string */
/* Assigned meaning in release 2.0 */
/* call function for all accessible objects */
NULL, /* traverseproc tp_traverse; */
/* delete references to contained objects */
NULL, /* inquiry tp_clear; */
/* Assigned meaning in release 2.1 */
/* rich comparisons */
NULL, /* richcmpfunc tp_richcompare; */
/* weak reference enabler */
0, /* Py_ssize_t tp_weaklistoffset; */
/* Added in release 2.2 */
/* Iterators */
NULL, /* getiterfunc tp_iter; */
NULL, /* iternextfunc tp_iternext; */
/* Attribute descriptor and subclassing stuff */
client_methods, /* struct PyMethodDef *tp_methods; */
NULL, /* struct PyMemberDef *tp_members; */
client_getset, /* struct PyGetSetDef *tp_getset; */
NULL, /* struct _typeobject *tp_base; */
NULL, /* PyObject *tp_dict; */
NULL, /* descrgetfunc tp_descr_get; */
NULL, /* descrsetfunc tp_descr_set; */
0, /* Py_ssize_t tp_dictoffset; */
NULL, /* initproc tp_init; */
NULL, /* allocfunc tp_alloc; */
client_new, /* newfunc tp_new; */
};
static PyObject *get_config(PyObject *self, PyObject *args)
{
char *config_dir = NULL;
ConfigObject *data;
if (!PyArg_ParseTuple(args, "|z", &config_dir))
return NULL;
data = PyObject_New(ConfigObject, &Config_Type);
if (data == NULL)
return NULL;
data->pool = Pool(NULL);
if (data->pool == NULL) {
PyObject_Del(data);
return NULL;
}
RUN_SVN_WITH_POOL(data->pool,
svn_config_get_config(&data->config, config_dir, data->pool));
return (PyObject *)data;
}
/**
* Get runtime 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_client_version();
return Py_BuildValue("(iiis)", ver->major, ver->minor,
ver->patch, ver->tag);
}
SVN_VERSION_DEFINE(svn_api_version);
/**
* Get compile-time libsvn_wc version information.
*
* :return: tuple with major, minor, patch version number and tag.
*/
static PyObject *api_version(PyObject *self)
{
const svn_version_t *ver = &svn_api_version;
return Py_BuildValue("(iiis)", ver->major, ver->minor,
ver->patch, ver->tag);
}
static PyMethodDef client_mod_methods[] = {
{ "get_config", get_config, METH_VARARGS, "get_config(config_dir=None) -> config" },
{ "api_version", (PyCFunction)api_version, METH_NOARGS,
"api_version() -> (major, minor, patch, tag)\n\n"
"Version of libsvn_client Subvertpy was compiled against."
},
{ "version", (PyCFunction)version, METH_NOARGS,
"version() -> (major, minor, patch, tag)\n\n"
"Version of libsvn_wc currently used."
},
{ NULL }
};
void initclient(void)
{
PyObject *mod;
if (PyType_Ready(&Client_Type) < 0)
return;
if (PyType_Ready(&Config_Type) < 0)
return;
if (PyType_Ready(&ConfigItem_Type) < 0)
return;
if (PyType_Ready(&Info_Type) < 0)
return;
if (PyType_Ready(&WCInfo_Type) < 0)
return;
/* Make sure APR is initialized */
apr_initialize();
mod = Py_InitModule3("client", client_mod_methods, "Client methods");
if (mod == NULL)
return;
Py_INCREF(&Client_Type);
PyModule_AddObject(mod, "Client", (PyObject *)&Client_Type);
PyModule_AddObject(mod, "depth_empty",
(PyObject *)PyLong_FromLong(svn_depth_empty));
PyModule_AddObject(mod, "depth_files",
(PyObject *)PyLong_FromLong(svn_depth_files));
PyModule_AddObject(mod, "depth_immediates",
(PyObject *)PyLong_FromLong(svn_depth_immediates));
PyModule_AddObject(mod, "depth_infinity",
(PyObject *)PyLong_FromLong(svn_depth_infinity));
Py_INCREF(&Config_Type);
PyModule_AddObject(mod, "Config", (PyObject *)&Config_Type);
}
diff --git a/subvertpy/tests/test_client.py b/subvertpy/tests/test_client.py
index a5b04bd3..6af4a5a6 100644
--- a/subvertpy/tests/test_client.py
+++ b/subvertpy/tests/test_client.py
@@ -1,251 +1,258 @@
# 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 Lesser General Public License as published by
# the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
# You should have received a copy of the GNU Lesser General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""Subversion client library tests."""
from datetime import datetime, timedelta
import os
from StringIO import StringIO
import shutil
import tempfile
from subvertpy import (
SubversionException,
NODE_DIR, NODE_FILE,
client,
ra,
wc,
)
from subvertpy.tests import (
SubversionTestCase,
TestCase,
)
class VersionTest(TestCase):
def test_version_length(self):
self.assertEquals(4, len(client.version()))
def test_api_version_length(self):
self.assertEquals(4, len(client.api_version()))
def test_api_version_later_same(self):
self.assertTrue(client.api_version() <= client.version())
class TestClient(SubversionTestCase):
def setUp(self):
super(TestClient, self).setUp()
self.repos_url = self.make_client("d", "dc")
self.client = client.Client(auth=ra.Auth([ra.get_username_provider()]))
def tearDown(self):
del self.client
super(TestClient, self).tearDown()
def test_add(self):
self.build_tree({"dc/foo": None})
self.client.add("dc/foo")
def test_commit(self):
self.build_tree({"dc/foo": None})
self.client.add("dc/foo")
self.client.log_msg_func = lambda c: "Amessage"
self.client.commit(["dc"])
r = ra.RemoteAccess(self.repos_url)
revprops = r.rev_proplist(1)
self.assertEquals("Amessage", revprops["svn:log"])
def test_commit_start(self):
self.build_tree({"dc/foo": None})
self.client = client.Client(auth=ra.Auth([ra.get_username_provider()]),
log_msg_func=lambda c: "Bmessage")
self.client.add("dc/foo")
self.client.commit(["dc"])
r = ra.RemoteAccess(self.repos_url)
revprops = r.rev_proplist(1)
self.assertEquals("Bmessage", revprops["svn:log"])
def test_mkdir(self):
self.client.mkdir(["dc/foo"])
self.client.mkdir("dc/bar")
self.client.mkdir("dc/bla", revprops={"svn:log": "foo"})
def test_export(self):
self.build_tree({"dc/foo": "bla"})
self.client.add("dc/foo")
self.client.commit(["dc"])
self.client.export(self.repos_url, "de")
self.assertEquals(["foo"], os.listdir("de"))
def test_add_recursive(self):
self.build_tree({"dc/trunk/foo": 'bla', "dc/trunk": None})
self.client.add("dc/trunk")
adm = wc.WorkingCopy(None, os.path.join(os.getcwd(), "dc"))
e = adm.entry(os.path.join(os.getcwd(), "dc", "trunk"))
self.assertEquals(e.kind, NODE_DIR)
adm2 = wc.WorkingCopy(None, os.path.join(os.getcwd(), "dc", "trunk"))
e = adm2.entry(os.path.join(os.getcwd(), "dc", "trunk", "foo"))
self.assertEquals(e.kind, NODE_FILE)
self.assertEquals(e.revision, 0)
def test_get_config(self):
self.assertIsInstance(client.get_config(), client.Config)
try:
base_dir = tempfile.mkdtemp()
base_dir_basename = os.path.basename(base_dir)
svn_cfg_dir = os.path.join(base_dir, '.subversion')
os.mkdir(svn_cfg_dir)
with open(os.path.join(svn_cfg_dir, 'config'), 'w') as svn_cfg:
svn_cfg.write('[miscellany]\n')
svn_cfg.write('global-ignores = %s' % base_dir_basename)
config = client.get_config(svn_cfg_dir)
self.assertIsInstance(config, client.Config)
ignores = config.get_default_ignores()
self.assertTrue(base_dir_basename in ignores)
finally:
shutil.rmtree(base_dir)
def test_diff(self):
r = ra.RemoteAccess(self.repos_url,
auth=ra.Auth([ra.get_username_provider()]))
dc = self.get_commit_editor(self.repos_url)
f = dc.add_file("foo")
f.modify("foo1")
dc.close()
dc = self.get_commit_editor(self.repos_url)
f = dc.open_file("foo")
f.modify("foo2")
dc.close()
if client.api_version() < (1, 5):
self.assertRaises(NotImplementedError, self.client.diff, 1, 2,
self.repos_url, self.repos_url)
return # Skip test
(outf, errf) = self.client.diff(1, 2, self.repos_url, self.repos_url)
outf.seek(0)
errf.seek(0)
self.assertEquals("""Index: foo
===================================================================
--- foo\t(revision 1)
+++ foo\t(revision 2)
@@ -1 +1 @@
-foo1
\\ No newline at end of file
+foo2
\\ No newline at end of file
""", outf.read())
self.assertEquals("", errf.read())
def assertCatEquals(self, value, revision=None):
io = StringIO()
self.client.cat("dc/foo", io, revision)
self.assertEquals(value, io.getvalue())
def test_cat(self):
self.build_tree({"dc/foo": "bla"})
self.client.add("dc/foo")
self.client.log_msg_func = lambda c: "Commit"
self.client.commit(["dc"])
self.assertCatEquals("bla")
self.build_tree({"dc/foo": "blabla"})
self.client.commit(["dc"])
self.assertCatEquals("blabla")
self.assertCatEquals("bla", revision=1)
self.assertCatEquals("blabla", revision=2)
def assertLogEntryChangedPathsEquals(self, expected, entry):
changed_paths = entry["changed_paths"]
self.assertIsInstance(changed_paths, dict)
self.assertEquals(sorted(expected), sorted(changed_paths.keys()))
def assertLogEntryMessageEquals(self, expected, entry):
self.assertEquals(expected, entry["revprops"]["svn:log"])
def assertLogEntryDateAlmostEquals(self, expected, entry, delta):
actual = datetime.strptime(entry["revprops"]["svn:date"], "%Y-%m-%dT%H:%M:%S.%fZ")
self.assertTrue((actual - expected) < delta)
def test_log(self):
log_entries = []
commit_msg_1 = "Commit"
commit_msg_2 = "Commit 2"
delta = timedelta(hours=1)
def cb(changed_paths, revision, revprops, has_children=False):
log_entries.append({
'changed_paths': changed_paths,
'revision': revision,
'revprops': revprops,
'has_children': has_children,
})
self.build_tree({"dc/foo": "bla"})
self.client.add("dc/foo")
self.client.log_msg_func = lambda c: commit_msg_1
self.client.commit(["dc"])
commit_1_dt = datetime.utcnow()
self.client.log(cb, "dc/foo")
self.assertEquals(1, len(log_entries))
self.assertEquals(None, log_entries[0]["changed_paths"])
self.assertEquals(1, log_entries[0]["revision"])
self.assertLogEntryMessageEquals(commit_msg_1, log_entries[0])
self.assertLogEntryDateAlmostEquals(commit_1_dt, log_entries[0], delta)
self.build_tree({
"dc/foo": "blabla",
"dc/bar": "blablabla",
})
self.client.add("dc/bar")
self.client.log_msg_func = lambda c: commit_msg_2
self.client.commit(["dc"])
commit_2_dt = datetime.utcnow()
log_entries = []
self.client.log(cb, "dc/foo", discover_changed_paths=True)
self.assertEquals(2, len(log_entries))
self.assertLogEntryChangedPathsEquals(["/foo", "/bar"], log_entries[0])
self.assertEquals(2, log_entries[0]["revision"])
self.assertLogEntryMessageEquals(commit_msg_2, log_entries[0])
self.assertLogEntryDateAlmostEquals(commit_2_dt, log_entries[0], delta)
self.assertLogEntryChangedPathsEquals(["/foo"], log_entries[1])
self.assertEquals(1, log_entries[1]["revision"])
self.assertLogEntryMessageEquals(commit_msg_1, log_entries[1])
self.assertLogEntryDateAlmostEquals(commit_1_dt, log_entries[1], delta)
+ log_entries = []
+ self.client.log(cb, "dc/foo", start_rev=2, end_rev=2, discover_changed_paths=True)
+ self.assertEquals(1, len(log_entries))
+ self.assertLogEntryChangedPathsEquals(["/foo", "/bar"], log_entries[0])
+ self.assertEquals(2, log_entries[0]["revision"])
+ self.assertLogEntryMessageEquals(commit_msg_2, log_entries[0])
+ self.assertLogEntryDateAlmostEquals(commit_2_dt, log_entries[0], delta)
def test_info(self):
self.build_tree({"dc/foo": "bla"})
self.client.add("dc/foo")
self.client.log_msg_func = lambda c: "Commit"
self.client.commit(["dc"])
info = self.client.info("dc/foo")
self.assertEquals(["dc/foo"], info.keys())
self.assertEquals(1, info["dc/foo"].revision)
self.assertIs(None, info["dc/foo"].size)
self.assertEquals(wc.SCHEDULE_NORMAL, info["dc/foo"].wc_info.schedule)
self.build_tree({"dc/bar": "blablabla"})
self.client.add("dc/bar")
info = self.client.info("dc/bar")
self.assertEquals(["dc/bar"], info.keys())
self.assertEquals(0, info["dc/bar"].revision)
self.assertEquals(wc.SCHEDULE_ADD, info["dc/bar"].wc_info.schedule)
def test_info_nonexistant(self):
self.build_tree({"dc/foo": "bla"})
self.client.add("dc/foo")
self.client.log_msg_func = lambda c: "Commit"
self.client.commit(["dc"])
self.assertRaises(SubversionException, self.client.info, "dc/missing")

File Metadata

Mime Type
text/x-diff
Expires
Sat, Jun 21, 5:07 PM (1 w, 6 d ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
3212124

Event Timeline