From 14a67349214393dbe3f4634af642abb522101b15 Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Fri, 15 Feb 2013 18:08:38 +0100 Subject: [PATCH] Added basic remote support * new Repository methods implemented: * remotes to list all existing remotes * remote_create to create a new remote entry * New Class Remote added --- include/pygit2/types.h | 1 + include/pygit2/utils.h | 2 + src/pygit2.c | 7 +++ src/remote.c | 130 ++++++++++++++++++++++++++++++++++++++++ src/repository.c | 50 ++++++++++++++++ test/test_repository.py | 20 +++++++ 6 files changed, 210 insertions(+) create mode 100644 src/remote.c diff --git a/include/pygit2/types.h b/include/pygit2/types.h index 97f83cf..834d6a8 100644 --- a/include/pygit2/types.h +++ b/include/pygit2/types.h @@ -58,6 +58,7 @@ OBJECT_STRUCT(Tag, git_tag, tag) OBJECT_STRUCT(Index, git_index, index) OBJECT_STRUCT(Walker, git_revwalk, walk) OBJECT_STRUCT(Config, git_config, config) +OBJECT_STRUCT(Remote, git_remote, remote) typedef struct { PyObject_HEAD diff --git a/include/pygit2/utils.h b/include/pygit2/utils.h index d6ffc81..d596b74 100644 --- a/include/pygit2/utils.h +++ b/include/pygit2/utils.h @@ -95,6 +95,8 @@ char * py_str_to_c_str(PyObject *value, const char *encoding); #define py_path_to_c_str(py_path) \ py_str_to_c_str(py_path, Py_FileSystemDefaultEncoding) +#define INSTANCIATE_CLASS(type, arglist) \ + PyObject_CallObject(PyType_GenericNew(&type, NULL, NULL), arglist); /* Helpers to make shorter PyMethodDef and PyGetSetDef blocks */ #define METHOD(type, name, args)\ diff --git a/src/pygit2.c b/src/pygit2.c index 07e6bc9..31d16ea 100644 --- a/src/pygit2.c +++ b/src/pygit2.c @@ -57,6 +57,7 @@ extern PyTypeObject ReferenceType; extern PyTypeObject RefLogIterType; extern PyTypeObject RefLogEntryType; extern PyTypeObject SignatureType; +extern PyTypeObject RemoteType; @@ -236,6 +237,9 @@ moduleinit(PyObject* m) if (PyType_Ready(&SignatureType) < 0) return NULL; + if (PyType_Ready(&RemoteType) < 0) + return NULL; + Py_INCREF(GitError); PyModule_AddObject(m, "GitError", GitError); @@ -278,6 +282,9 @@ moduleinit(PyObject* m) Py_INCREF(&SignatureType); PyModule_AddObject(m, "Signature", (PyObject *)&SignatureType); + Py_INCREF(&RemoteType); + PyModule_AddObject(m, "Remote", (PyObject *)&RemoteType); + PyModule_AddIntConstant(m, "GIT_OBJ_ANY", GIT_OBJ_ANY); PyModule_AddIntConstant(m, "GIT_OBJ_COMMIT", GIT_OBJ_COMMIT); PyModule_AddIntConstant(m, "GIT_OBJ_TREE", GIT_OBJ_TREE); diff --git a/src/remote.c b/src/remote.c new file mode 100644 index 0000000..43f61bd --- /dev/null +++ b/src/remote.c @@ -0,0 +1,130 @@ +/* + * Copyright 2010-2012 The pygit2 contributors + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, + * as published by the Free Software Foundation. + * + * In addition to the permissions in the GNU General Public License, + * the authors give you unlimited permission to link the compiled + * version of this file into combinations with other programs, + * and to distribute those combinations without any restriction + * coming from the use of this file. (The General Public License + * restrictions do apply in other respects; for example, they cover + * modification of the file, and distribution when not linked into + * a combined executable.) + * + * This file 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; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#define PY_SSIZE_T_CLEAN +#include +#include +#include +#include +#include + +extern PyObject *GitError; +extern PyTypeObject RepositoryType; + +PyObject * +Remote_call(Remote *self, PyObject *args, PyObject *kwds) +{ + Repository* py_repo = NULL; + char *name = NULL; + int err; + + if (!PyArg_ParseTuple(args, "O!s", &RepositoryType, &py_repo, &name)) + return NULL; + + self->repo = py_repo; + err = git_remote_load(&self->remote, py_repo->repo, name); + + if (err < 0) + return Error_set(err); + + return (PyObject*) self; +} + + +static void +Remote_dealloc(Remote *self) +{ + PyObject_Del(self); +} + + +PyDoc_STRVAR(Remote_name__doc__, "Name of the remote refspec"); + +PyObject * +Remote_name__get__(Remote *self) +{ + return PyUnicode_FromString(git_remote_name(self->remote)); +} + + +PyDoc_STRVAR(Remote_url__doc__, "Url of the remote refspec"); + +PyObject * +Remote_url__get__(Remote *self) +{ + return PyUnicode_FromString(git_remote_url(self->remote)); +} + + +PyGetSetDef Remote_getseters[] = { + GETTER(Remote, name), + GETTER(Remote, url), + {NULL} +}; + +PyDoc_STRVAR(Remote__doc__, "Remote object."); + +PyTypeObject RemoteType = { + PyVarObject_HEAD_INIT(NULL, 0) + "_pygit2.Remote", /* tp_name */ + sizeof(Remote), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)Remote_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + (ternaryfunc) Remote_call, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + Remote__doc__, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + 0, /* tp_members */ + Remote_getseters, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + 0, /* tp_new */ +}; diff --git a/src/repository.c b/src/repository.c index f8a5708..20b1048 100644 --- a/src/repository.c +++ b/src/repository.c @@ -44,6 +44,7 @@ extern PyTypeObject TreeType; extern PyTypeObject TreeBuilderType; extern PyTypeObject ConfigType; extern PyTypeObject DiffType; +extern PyTypeObject RemoteType; git_otype int_to_loose_object_type(int type_id) @@ -1013,6 +1014,53 @@ Repository_TreeBuilder(Repository *self, PyObject *args) return (PyObject*)builder; } + +PyDoc_STRVAR(Repository_remote_create__doc__, + "remote_create(name, url) -> Remote\n" + "\n" + "Creates a new remote."); + +PyObject * +Repository_remote_create(Repository *self, PyObject *args) +{ + git_remote *remote; + char *name = NULL, *url = NULL; + int err; + + if (!PyArg_ParseTuple(args, "ss", &name, &url)) + return NULL; + + err = git_remote_create(&remote, self->repo, name, url); + if (err < 0) + return Error_set(err); + + return INSTANCIATE_CLASS(RemoteType, Py_BuildValue("Os", self, name)); +} + + +PyDoc_STRVAR(Repository_remotes__doc__, "returns all configured remotes"); + +PyObject * +Repository_remotes__get__(Repository *self) +{ + git_strarray remotes; + PyObject* py_list = NULL, *py_tmp; + size_t i; + + git_remote_list(&remotes, self->repo); + + py_list = PyList_New(remotes.count); + for (i=0; i < remotes.count; ++i) { + py_tmp = INSTANCIATE_CLASS(RemoteType, Py_BuildValue("Os", self, remotes.strings[i])); + PyList_SetItem(py_list, i, py_tmp); + } + + git_strarray_free(&remotes); + + return py_list; +} + + PyMethodDef Repository_methods[] = { METHOD(Repository, create_blob, METH_VARARGS), METHOD(Repository, create_blob_fromfile, METH_VARARGS), @@ -1029,6 +1077,7 @@ PyMethodDef Repository_methods[] = { METHOD(Repository, revparse_single, METH_O), METHOD(Repository, status, METH_NOARGS), METHOD(Repository, status_file, METH_O), + METHOD(Repository, remote_create, METH_VARARGS), {NULL} }; @@ -1042,6 +1091,7 @@ PyGetSetDef Repository_getseters[] = { GETTER(Repository, is_bare), GETTER(Repository, config), GETTER(Repository, workdir), + GETTER(Repository, remotes), {NULL} }; diff --git a/test/test_repository.py b/test/test_repository.py index e16c842..f125672 100644 --- a/test/test_repository.py +++ b/test/test_repository.py @@ -184,6 +184,26 @@ class RepositoryTest_II(utils.RepoTestCase): expected = realpath(join(self._temp_dir, 'testrepo')) self.assertEqual(directory, expected) + def test_remote_create(self): + name = 'upstream' + url = 'git://github.com/libgit2/pygit2.git' + + remote = self.repo.remote_create(name, url); + + self.assertEqual(type(remote), pygit2.Remote) + self.assertEqual(name, remote.name) + self.assertEqual(url, remote.url) + + self.assertRaises(ValueError,self.repo.remote_create, *(name, url)) + + def test_remote_list(self): + self.assertEqual(0, len(self.repo.remotes)) + + name = 'upstream' + url = 'git://github.com/libgit2/pygit2.git' + remote = self.repo.remote_create(name, url); + self.assertTrue(remote.name in [x.name for x in self.repo.remotes]) + class NewRepositoryTest(utils.NoRepoTestCase): def test_new_repo(self):