diff options
-rw-r--r-- | NEWS | 6 | ||||
-rw-r--r-- | subvertpy/_ra.c | 16 | ||||
-rw-r--r-- | subvertpy/client.c | 80 | ||||
-rw-r--r-- | subvertpy/repos.c | 11 | ||||
-rw-r--r-- | subvertpy/tests/test_client.py | 22 | ||||
-rw-r--r-- | subvertpy/tests/test_ra.py | 9 | ||||
-rw-r--r-- | subvertpy/tests/test_wc.py | 6 | ||||
-rw-r--r-- | subvertpy/util.c | 77 | ||||
-rw-r--r-- | subvertpy/wc.c | 18 |
9 files changed, 162 insertions, 83 deletions
@@ -5,9 +5,13 @@ * Drop support for Python versions before 2.7, in preparation of Python3 support. (Jelmer Vernooij) + * subvertpy.client methods no longer canonicalize paths and URLs, instead + requiring the caller to do so. If uncanonicalized paths/URLs are passed in, + a ValueError will be raised. (Jelmer Vernooij) + IMPROVEMENTS - * Add Python3 support (Martin Panter, Yonggang Luo). + * Add Python3 support. (Martin Panter, Yonggang Luo, Jelmer Vernooij). * Add constant ERR_RA_CANNOT_CREATE_SESSION. (Jelmer Vernooij) diff --git a/subvertpy/_ra.c b/subvertpy/_ra.c index 90ee6b29..061d5973 100644 --- a/subvertpy/_ra.c +++ b/subvertpy/_ra.c @@ -1281,8 +1281,6 @@ static PyObject *ra_replay_range(PyObject *self, PyObject *args) #endif } - - static PyObject *ra_rev_proplist(PyObject *self, PyObject *args) { apr_pool_t *temp_pool; @@ -1568,6 +1566,7 @@ static PyObject *ra_get_file(PyObject *self, PyObject *args) svn_revnum_t fetch_rev; PyObject *py_stream, *py_props; apr_pool_t *temp_pool; + svn_stream_t *stream; if (!PyArg_ParseTuple(args, "OO|l:get_file", &py_path, &py_stream, &revision)) return NULL; @@ -1583,14 +1582,19 @@ static PyObject *ra_get_file(PyObject *self, PyObject *args) fetch_rev = revision; path = py_object_to_svn_relpath(py_path, temp_pool); - if (path == NULL) + if (path == NULL) { + apr_pool_destroy(temp_pool); return NULL; + } - /* Yuck. Subversion doesn't like leading slashes.. */ - while (*path == '/') path++; + stream = new_py_stream(temp_pool, py_stream); + if (stream == NULL) { + apr_pool_destroy(temp_pool); + return NULL; + } RUN_RA_WITH_POOL(temp_pool, ra, svn_ra_get_file(ra->ra, path, revision, - new_py_stream(temp_pool, py_stream), + stream, &fetch_rev, &props, temp_pool)); py_props = prop_hash_to_dict(props); diff --git a/subvertpy/client.c b/subvertpy/client.c index 26f96d07..788d186b 100644 --- a/subvertpy/client.c +++ b/subvertpy/client.c @@ -73,25 +73,40 @@ typedef struct { static int client_set_auth(PyObject *self, PyObject *auth, void *closure); static int client_set_config(PyObject *self, PyObject *auth, void *closure); +static bool client_check_path(const char *path, apr_pool_t *scratch_pool) +{ + return svn_path_is_canonical(path, scratch_pool); +} + static bool client_path_list_to_apr_array(apr_pool_t *pool, PyObject *l, apr_array_header_t **ret) { int i; + const char *path; if (l == Py_None) { *ret = NULL; return true; } - if (PyString_Check(l)) { + if (PyUnicode_Check(l) || PyBytes_Check(l)) { *ret = apr_array_make(pool, 1, sizeof(char *)); - APR_ARRAY_PUSH(*ret, const char *) = svn_path_canonicalize(PyString_AsString(l), pool); + path = py_object_to_svn_string(l, pool); + if (path == NULL) { + return false; + } + if (!client_check_path(path, pool)) { + PyErr_SetString(PyExc_ValueError, "Expected canonical path or URL"); + return false; + } + APR_ARRAY_PUSH(*ret, const char *) = path; } else if (PyList_Check(l)) { *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_Format(PyExc_TypeError, "Expected list of strings, item was %s", item->ob_type->tp_name); + path = py_object_to_svn_string(item, pool); + if (!client_check_path(path, pool)) { + PyErr_SetString(PyExc_ValueError, "Expected canonical path or URL"); return false; } - APR_ARRAY_PUSH(*ret, const char *) = svn_path_canonicalize(PyString_AsString(item), pool); + APR_ARRAY_PUSH(*ret, const char *) = path; } } else { PyErr_Format(PyExc_TypeError, "Expected list of strings, got: %s", @@ -820,34 +835,45 @@ static PyObject *client_export(PyObject *self, PyObject *args, PyObject *kwargs) 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; + 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, *py_path; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "sO|OO", kwnames, &path, &py_stream, &rev, &peg_rev)) - return NULL; + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OO|OO", kwnames, &py_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; + 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; + temp_pool = Pool(NULL); + if (temp_pool == NULL) { + return NULL; + } - stream = new_py_stream(temp_pool, py_stream); + path = py_object_to_svn_string(py_path, temp_pool); + if (path == NULL) { + apr_pool_destroy(temp_pool); + return NULL; + } + + stream = new_py_stream(temp_pool, py_stream); + if (stream == NULL) { + apr_pool_destroy(temp_pool); + return NULL; + } - RUN_SVN_WITH_POOL(temp_pool, svn_client_cat2(stream, path, - &c_peg_rev, &c_rev, client->client, temp_pool)); + 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; + apr_pool_destroy(temp_pool); + Py_RETURN_NONE; } static PyObject *client_delete(PyObject *self, PyObject *args) diff --git a/subvertpy/repos.c b/subvertpy/repos.c index eb40d756..e5015882 100644 --- a/subvertpy/repos.c +++ b/subvertpy/repos.c @@ -463,14 +463,21 @@ static PyObject *repos_verify(RepositoryObject *self, PyObject *args) apr_pool_t *temp_pool; PyObject *py_feedback_stream; svn_revnum_t start_rev, end_rev; + svn_stream_t *stream; if (!PyArg_ParseTuple(args, "Oll", &py_feedback_stream, &start_rev, &end_rev)) return NULL; temp_pool = Pool(NULL); - if (temp_pool == NULL) + if (temp_pool == NULL) { + return NULL; + } + stream = new_py_stream(temp_pool, py_feedback_stream); + if (stream == NULL) { + apr_pool_destroy(temp_pool); return NULL; + } RUN_SVN_WITH_POOL(temp_pool, svn_repos_verify_fs(self->repos, - new_py_stream(temp_pool, py_feedback_stream), start_rev, end_rev, + stream, start_rev, end_rev, py_cancel_check, NULL, temp_pool)); apr_pool_destroy(temp_pool); diff --git a/subvertpy/tests/test_client.py b/subvertpy/tests/test_client.py index b72ba546..f7566422 100644 --- a/subvertpy/tests/test_client.py +++ b/subvertpy/tests/test_client.py @@ -1,5 +1,5 @@ # Copyright (C) 2005-2007 Jelmer Vernooij <jelmer@jelmer.uk> - + # 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 @@ -70,7 +70,7 @@ class TestClient(SubversionTestCase): self.client.commit(["dc"]) r = ra.RemoteAccess(self.repos_url) revprops = r.rev_proplist(1) - self.assertEqual("Amessage", revprops["svn:log"]) + self.assertEqual(b"Amessage", revprops["svn:log"]) def test_commit_start(self): self.build_tree({"dc/foo": None}) @@ -80,7 +80,7 @@ class TestClient(SubversionTestCase): self.client.commit(["dc"]) r = ra.RemoteAccess(self.repos_url) revprops = r.rev_proplist(1) - self.assertEqual("Bmessage", revprops["svn:log"]) + self.assertEqual(b"Bmessage", revprops["svn:log"]) def test_mkdir(self): self.client.mkdir(["dc/foo"]) @@ -128,19 +128,21 @@ class TestClient(SubversionTestCase): config = client.get_config(svn_cfg_dir) self.assertIsInstance(config, client.Config) ignores = config.get_default_ignores() - self.assertTrue(base_dir_basename in ignores) + self.assertTrue( + base_dir_basename.encode('utf-8') in ignores, + "no %r in %r" % (base_dir_basename, 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) + dc = self.get_commit_editor(self.repos_url) f = dc.add_file("foo") f.modify(b"foo1") dc.close() - dc = self.get_commit_editor(self.repos_url) + dc = self.get_commit_editor(self.repos_url) f = dc.open_file("foo") f.modify(b"foo2") dc.close() @@ -191,13 +193,15 @@ class TestClient(SubversionTestCase): self.assertEqual(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") + actual = datetime.strptime( + entry["revprops"]["svn:date"].decode('utf-8'), + "%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" + commit_msg_1 = b"Commit" + commit_msg_2 = b"Commit 2" delta = timedelta(hours=1) def cb(changed_paths, revision, revprops, has_children=False): log_entries.append({ diff --git a/subvertpy/tests/test_ra.py b/subvertpy/tests/test_ra.py index 3de0a96f..5cbefcbb 100644 --- a/subvertpy/tests/test_ra.py +++ b/subvertpy/tests/test_ra.py @@ -77,7 +77,7 @@ class TestRemoteAccess(SubversionTestCase): self.assertEqual(1, self.ra.get_latest_revnum()) def test_get_uuid(self): - self.assertIsInstance(self.ra.get_uuid(), unicode) + self.assertEqual(36, len(self.ra.get_uuid())) def test_get_repos_root(self): self.assertEqual(self.repos_url, self.ra.get_repos_root()) @@ -254,7 +254,10 @@ class TestRemoteAccess(SubversionTestCase): root.close() editor.close() - self.assertEqual(set(['bar:foo', 'svn:author', 'svn:custom:blie', 'svn:date', 'svn:log']), set(self.ra.rev_proplist(1).keys())) + revprops = self.ra.rev_proplist(1) + self.assertEqual( + set(['bar:foo', 'svn:author', 'svn:custom:blie', 'svn:date', 'svn:log']), + set(revprops.keys()), "result: %r" % revprops) def test_get_commit_editor_context_manager(self): def mycb(paths, rev, revprops): @@ -292,7 +295,7 @@ class TestRemoteAccess(SubversionTestCase): stream = BytesIO() props = self.ra.get_file("bar", stream, 1)[1] - self.assertEqual("blie", props.get("bla:bar")) + self.assertEqual(b"blie", props.get("bla:bar")) stream = BytesIO() props = self.ra.get_file("bar", stream, 2)[1] self.assertIs(None, props.get("bla:bar")) diff --git a/subvertpy/tests/test_wc.py b/subvertpy/tests/test_wc.py index a65b054b..08dd9d51 100644 --- a/subvertpy/tests/test_wc.py +++ b/subvertpy/tests/test_wc.py @@ -44,13 +44,13 @@ class VersionTest(TestCase): class WorkingCopyTests(TestCase): def test_get_adm_dir(self): - self.assertEqual(".svn", wc.get_adm_dir()) + self.assertEqual(b".svn", wc.get_adm_dir()) def test_set_adm_dir(self): old_dir_name = wc.get_adm_dir() try: - wc.set_adm_dir("_svn") - self.assertEqual("_svn", wc.get_adm_dir()) + wc.set_adm_dir(b"_svn") + self.assertEqual(b"_svn", wc.get_adm_dir()) finally: wc.set_adm_dir(old_dir_name) diff --git a/subvertpy/util.c b/subvertpy/util.c index 568a69e6..94a04779 100644 --- a/subvertpy/util.c +++ b/subvertpy/util.c @@ -38,7 +38,7 @@ void PyErr_SetAprStatus(apr_status_t status) { char errmsg[1024]; - PyErr_SetString(PyExc_Exception, + PyErr_SetString(PyExc_Exception, apr_strerror(status, errmsg, sizeof(errmsg))); } @@ -119,23 +119,24 @@ const char *py_object_to_svn_uri(PyObject *obj, apr_pool_t *pool) const char *py_object_to_svn_relpath(PyObject *obj, apr_pool_t *pool) { const char *ret; - PyObject *bytes_obj = NULL; if (PyUnicode_Check(obj)) { - bytes_obj = obj = PyUnicode_AsUTF8String(obj); + obj = PyUnicode_AsUTF8String(obj); if (obj == NULL) { return NULL; } + } else { + Py_INCREF(obj); } if (PyBytes_Check(obj)) { ret = svn_relpath_canonicalize(PyBytes_AsString(obj), pool); - Py_XDECREF(bytes_obj); + Py_DECREF(obj); return ret; } else { - Py_XDECREF(bytes_obj); PyErr_SetString(PyExc_TypeError, "relative paths need to be UTF-8 bytestrings or unicode strings"); + Py_DECREF(obj); return NULL; } } @@ -239,7 +240,7 @@ void PyErr_SetSubversionException(svn_error_t *error) return; } - if (error->apr_err >= APR_OS_START_SYSERR && + if (error->apr_err >= APR_OS_START_SYSERR && error->apr_err < APR_OS_START_SYSERR + APR_OS_ERRSPACE_SIZE) { PyObject *excval = Py_BuildValue("(iz)", error->apr_err - APR_OS_START_SYSERR, error->message); PyErr_SetObject(PyExc_OSError, excval); @@ -303,10 +304,10 @@ void handle_svn_error(svn_error_t *error) return; /* Cancelled because of a Python exception, let Python deal with it. */ if (error->apr_err == SVN_ERR_RA_SVN_UNKNOWN_CMD) { - /* svnserve doesn't handle the 'failure' command sent back + /* svnserve doesn't handle the 'failure' command sent back * by the client if one of the editor commands failed. - * Rather than bouncing the error sent by the client - * (BZR_SVN_APR_ERROR_OFFSET for example), it will send + * Rather than bouncing the error sent by the client + * (BZR_SVN_APR_ERROR_OFFSET for example), it will send * SVN_ERR_RA_SVN_UNKNOWN_CMD. */ if (PyErr_Occurred() != NULL) return; @@ -351,22 +352,27 @@ bool string_list_to_apr_array(apr_pool_t *pool, PyObject *l, apr_array_header_t bool relpath_list_to_apr_array(apr_pool_t *pool, PyObject *l, apr_array_header_t **ret) { int i; + const char *relpath; if (l == Py_None) { *ret = NULL; return true; } - if (PyString_Check(l)) { + if (PyUnicode_Check(l) || PyBytes_Check(l)) { *ret = apr_array_make(pool, 1, sizeof(char *)); - APR_ARRAY_PUSH(*ret, const char *) = py_object_to_svn_relpath(l, pool); + relpath = py_object_to_svn_relpath(l, pool); + if (relpath == NULL) { + return false; + } + APR_ARRAY_PUSH(*ret, const char *) = relpath; } else if (PyList_Check(l)) { *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_Format(PyExc_TypeError, "Expected list of strings, item was %s", item->ob_type->tp_name); + relpath = py_object_to_svn_relpath(item, pool); + if (relpath == NULL) { return false; } - APR_ARRAY_PUSH(*ret, const char *) = py_object_to_svn_relpath(item, pool); + APR_ARRAY_PUSH(*ret, const char *) = relpath; } } else { PyErr_Format(PyExc_TypeError, "Expected list of strings, got: %s", @@ -394,7 +400,7 @@ PyObject *prop_hash_to_dict(apr_hash_t *props) if (py_props == NULL) { goto fail_props; } - for (idx = apr_hash_first(pool, props); idx != NULL; + for (idx = apr_hash_first(pool, props); idx != NULL; idx = apr_hash_next(idx)) { PyObject *py_key, *py_val; apr_hash_this(idx, (const void **)&key, &klen, (void **)&val); @@ -425,7 +431,7 @@ PyObject *prop_hash_to_dict(apr_hash_t *props) } apr_pool_destroy(pool); return py_props; - + fail_item: Py_DECREF(py_props); fail_props: @@ -460,16 +466,23 @@ apr_hash_t *prop_dict_to_hash(apr_pool_t *pool, PyObject *py_props) } if (!PyBytes_Check(k)) { - PyErr_SetString(PyExc_TypeError, + PyErr_SetString(PyExc_TypeError, "property name should be unicode or byte string"); Py_DECREF(k); return NULL; } + if (PyUnicode_Check(v)) { + v = PyUnicode_AsUTF8String(v); + } else { + Py_INCREF(v); + } + if (!PyBytes_Check(v)) { - PyErr_SetString(PyExc_TypeError, - "property value should be byte string"); + PyErr_SetString(PyExc_TypeError, + "property value should be unicode or byte string"); Py_DECREF(k); + Py_DECREF(v); return NULL; } @@ -478,6 +491,7 @@ apr_hash_t *prop_dict_to_hash(apr_pool_t *pool, PyObject *py_props) apr_hash_set(hash_props, PyBytes_AsString(k), PyBytes_Size(k), val_string); Py_DECREF(k); + Py_DECREF(v); } return hash_props; @@ -678,8 +692,8 @@ svn_error_t *py_svn_error() PyObject *wrap_lock(svn_lock_t *lock) { - return Py_BuildValue("(zzzbzz)", lock->path, lock->token, lock->owner, - lock->comment, lock->is_dav_comment, + return Py_BuildValue("(zzzbzz)", lock->path, lock->token, lock->owner, + lock->comment, lock->is_dav_comment, lock->creation_date, lock->expiration_date); } @@ -719,8 +733,8 @@ static svn_error_t *py_stream_read(void *baton, char *buffer, apr_size_t *length ret = PyObject_CallMethod(self, "read", "i", *length); CB_CHECK_PYRETVAL(ret); - if (!PyString_Check(ret)) { - PyErr_SetString(PyExc_TypeError, "Expected stream read function to return string"); + if (!PyBytes_Check(ret)) { + PyErr_SetString(PyExc_TypeError, "Expected stream read function to return bytes"); PyGILState_Release(state); return py_svn_error(); } @@ -733,10 +747,13 @@ static svn_error_t *py_stream_read(void *baton, char *buffer, apr_size_t *length static svn_error_t *py_stream_write(void *baton, const char *data, apr_size_t *len) { - PyObject *self = (PyObject *)baton, *ret; + PyObject *self = (PyObject *)baton, *ret, *py_data; PyGILState_STATE state = PyGILState_Ensure(); - ret = PyObject_CallMethod(self, "write", "s#", data, len[0]); + py_data = PyBytes_FromStringAndSize(data, *len); + CB_CHECK_PYRETVAL(py_data); + + ret = PyObject_CallMethod(self, "write", "O", py_data); CB_CHECK_PYRETVAL(ret); Py_DECREF(ret); PyGILState_Release(state); @@ -793,7 +810,7 @@ static apr_hash_t *get_default_config(void) if (!initialised) { pool = Pool(NULL); - RUN_SVN_WITH_POOL(pool, + RUN_SVN_WITH_POOL(pool, svn_config_get_config(&default_config, NULL, pool)); initialised = true; } @@ -961,7 +978,7 @@ static PyObject *stream_read_full(StreamObject *self, PyObject *args) } temp_pool = Pool(NULL); - if (temp_pool == NULL) + if (temp_pool == NULL) return NULL; if (len != -1) { char *buffer; @@ -983,7 +1000,7 @@ static PyObject *stream_read_full(StreamObject *self, PyObject *args) } else { #if ONLY_SINCE_SVN(1, 6) svn_string_t *result; - RUN_SVN_WITH_POOL(temp_pool, svn_string_from_stream(&result, + RUN_SVN_WITH_POOL(temp_pool, svn_string_from_stream(&result, self->stream, temp_pool, temp_pool)); @@ -992,7 +1009,7 @@ static PyObject *stream_read_full(StreamObject *self, PyObject *args) apr_pool_destroy(temp_pool); return ret; #else - PyErr_SetString(PyExc_NotImplementedError, + PyErr_SetString(PyExc_NotImplementedError, "Subversion 1.5 does not provide svn_string_from_stream()."); return NULL; #endif @@ -1010,7 +1027,7 @@ static PyMethodDef stream_methods[] = { PyTypeObject Stream_Type = { PyVarObject_HEAD_INIT(NULL, 0) "repos.Stream", /* const char *tp_name; For printing, in format "<module>.<name>" */ - sizeof(StreamObject), + sizeof(StreamObject), 0,/* Py_ssize_t tp_basicsize, tp_itemsize; For allocation */ /* Methods to implement standard operations */ diff --git a/subvertpy/wc.c b/subvertpy/wc.c index 24cbb815..be9a8d71 100644 --- a/subvertpy/wc.c +++ b/subvertpy/wc.c @@ -2445,13 +2445,19 @@ static PyObject *set_adm_dir(PyObject *self, PyObject *args) { apr_pool_t *temp_pool; char *name; + PyObject *py_name; - if (!PyArg_ParseTuple(args, "s", &name)) + if (!PyArg_ParseTuple(args, "O", &py_name)) return NULL; temp_pool = Pool(NULL); if (temp_pool == NULL) return NULL; + name = py_object_to_svn_string(py_name, temp_pool); + if (name == NULL) { + apr_pool_destroy(temp_pool); + return NULL; + } RUN_SVN_WITH_POOL(temp_pool, svn_wc_set_adm_dir(name, temp_pool)); apr_pool_destroy(temp_pool); Py_RETURN_NONE; @@ -2462,14 +2468,22 @@ static PyObject *get_pristine_copy_path(PyObject *self, PyObject *args) apr_pool_t *pool; const char *pristine_path; char *path; + PyObject *py_path; PyObject *ret; - if (!PyArg_ParseTuple(args, "s", &path)) + if (!PyArg_ParseTuple(args, "O", &py_path)) return NULL; pool = Pool(NULL); if (pool == NULL) return NULL; + + path = py_object_to_svn_string(py_path, pool); + if (path == NULL) { + apr_pool_destroy(pool); + return NULL; + } + PyErr_WarnEx(PyExc_DeprecationWarning, "get_pristine_copy_path is deprecated. Use get_pristine_contents instead.", 2); RUN_SVN_WITH_POOL(pool, svn_wc_get_pristine_copy_path(svn_dirent_canonicalize(path, pool), |